summaryrefslogtreecommitdiff
path: root/decorators.py
diff options
context:
space:
mode:
authorAdrien Di Mascio <Adrien.DiMascio@logilab.fr>2011-10-10 15:14:47 +0200
committerAdrien Di Mascio <Adrien.DiMascio@logilab.fr>2011-10-10 15:14:47 +0200
commitdca0bc8ab45d0fba854e3e0cb103ee47aa1baef0 (patch)
tree3bae703bc59900c658515fd255dd235aba037e37 /decorators.py
parenta1af8ab0eed9033e955bf69564e0c82fbb3cb30b (diff)
downloadlogilab-common-dca0bc8ab45d0fba854e3e0cb103ee47aa1baef0.tar.gz
[decorators] provide a @cachedproperty decorator
Diffstat (limited to 'decorators.py')
-rw-r--r--decorators.py39
1 files changed, 39 insertions, 0 deletions
diff --git a/decorators.py b/decorators.py
index 91d7492..2bce3e8 100644
--- a/decorators.py
+++ b/decorators.py
@@ -116,6 +116,45 @@ def cached(callableobj=None, keyarg=None, **kwargs):
else:
return decorator(callableobj)
+
+class cachedproperty(object):
+ """ Provides a cached property equivalent to the stacking of
+ @cached and @property, but more efficient.
+
+ After first usage, the <property_name> becomes part of the object's
+ __dict__. Doing:
+
+ del obj.<property_name> empties the cache.
+
+ Idea taken from the pyramid_ framework and the mercurial_ project.
+
+ .. _pyramid: http://pypi.python.org/pypi/pyramid
+ .. _mercurial: http://pypi.python.org/pypi/Mercurial
+ """
+ __slots__ = ('wrapped',)
+
+ def __init__(self, wrapped):
+ try:
+ wrapped.__name__
+ except AttributeError:
+ raise TypeError('%s must have a __name__ attribute' %
+ wrapped)
+ self.wrapped = wrapped
+
+ @property
+ def __doc__(self):
+ doc = getattr(self.wrapped, '__doc__', None)
+ return ('<wrapped by the cachedproperty decorator>%s'
+ % ('\n%s' % doc if doc else ''))
+
+ def __get__(self, inst, objtype=None):
+ if inst is None:
+ return self
+ val = self.wrapped(inst)
+ setattr(inst, self.wrapped.__name__, val)
+ return val
+
+
def get_cache_impl(obj, funcname):
cls = obj.__class__
member = getattr(cls, funcname)