1 # -*- coding: utf-8 -*-
3 # Copyright (C) 2013 LEAP
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU General Public License as published by
7 # the Free Software Foundation, either version 3 of the License, or
8 # (at your option) any later version.
10 # This program is distributed in the hope that it will be useful,
11 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 # GNU General Public License for more details.
15 # You should have received a copy of the GNU General Public License
16 # along with this program. If not, see <http://www.gnu.org/licenses/>.
24 logger = logging.getLogger(__name__)
27 class _memoized(object):
31 Caches a function's return value each time it is called.
32 If called later with the same arguments, the cached value is returned
35 def __init__(self, func, ignore_kwargs=None, is_method=False):
37 :param ignore_kwargs: If True, ignore all kwargs.
38 If tuple, ignore those kwargs.
39 :type ignore_kwargs: bool, tuple or None
40 :param is_method: whether the decorated function is a method.
41 (ignores the self argument if so).
44 self.ignore_kwargs = ignore_kwargs if ignore_kwargs else []
45 self.is_method = is_method
48 # TODO should put bounds to the cache dict so we do not
49 # consume a huge amount of memory.
52 def __call__(self, *args, **kwargs):
59 def ret_or_raise(value):
61 Returns the value except if it is an exception,
62 in which case it's raised.
64 if isinstance(value, Exception):
69 # forget about `self` as key
71 if self.ignore_kwargs is True:
74 key = (key_args, frozenset(
75 [(k, v) for k, v in kwargs.items()
76 if k not in self.ignore_kwargs]))
78 if not isinstance(key, collections.Hashable):
79 # uncacheable. a list, for instance.
80 # better to not cache than blow up.
81 logger.warning("Key is not hashable, bailing out!")
82 return self.func(*args, **kwargs)
85 logger.debug("Got value from cache...")
86 value = self.cache[key]
87 return ret_or_raise(value)
90 value = self.func(*args, **kwargs)
91 except Exception as exc:
92 logger.error("Exception while calling function: %r" % (exc,))
94 self.cache[key] = value
95 return ret_or_raise(value)
99 Return the function's docstring.
101 return self.func.__doc__
103 def __get__(self, obj, objtype):
105 Support instance methods.
107 return functools.partial(self.__call__, obj)
110 def memoized_method(function=None, ignore_kwargs=None):
112 Wrap _memoized to allow for deferred calling
114 :type function: callable, or None.
115 :type ignore_kwargs: None, True or tuple.
118 return _memoized(function, is_method=True)
120 def wrapper(function):
122 function, ignore_kwargs=ignore_kwargs, is_method=True)