Make ``ttl`` argument optional.
[cached-property.git] / cached_property.py
1 # -*- coding: utf-8 -*-
2
3 __author__ = 'Daniel Greenfeld'
4 __email__ = 'pydanny@gmail.com'
5 __version__ = '0.1.5'
6 __license__ = 'BSD'
7
8 from time import time
9 import threading
10
11
12 class cached_property(object):
13 """ A property that is only computed once per instance and then replaces
14 itself with an ordinary attribute. Deleting the attribute resets the
15 property.
16
17 Source: https://github.com/bottlepy/bottle/commit/fa7733e075da0d790d809aa3d2f53071897e6f76
18 """ # noqa
19
20 def __init__(self, ttl=None):
21 ttl_or_func = ttl
22 self.ttl = None
23 if callable(ttl_or_func):
24 self.prepare_func(ttl_or_func)
25 else:
26 self.ttl = ttl_or_func
27
28 def prepare_func(self, func, doc=None):
29 '''Prepare to cache object method.'''
30 self.func = func
31 self.__doc__ = doc or func.__doc__
32 self.__name__ = func.__name__
33 self.__module__ = func.__module__
34
35 def __call__(self, func, doc=None):
36 self.prepare_func(func, doc)
37 return self
38
39 def __get__(self, obj, cls):
40 if obj is None:
41 return self
42
43 now = time()
44 try:
45 value, last_update = obj._cache[self.__name__]
46 if self.ttl and self.ttl > 0 and now - last_update > self.ttl:
47 raise AttributeError
48 except (KeyError, AttributeError):
49 value = self.func(obj)
50 try:
51 cache = obj._cache
52 except AttributeError:
53 cache = obj._cache = {}
54 cache[self.__name__] = (value, now)
55
56 return value
57
58 def __delattr__(self, name):
59 print(name)
60
61
62 class threaded_cached_property(cached_property):
63 """ A cached_property version for use in environments where multiple
64 threads might concurrently try to access the property.
65 """
66 def __init__(self, ttl=None):
67 super(threaded_cached_property, self).__init__(ttl)
68 self.lock = threading.RLock()
69
70 def __get__(self, obj, cls):
71 with self.lock:
72 # Double check if the value was computed before the lock was
73 # acquired.
74 prop_name = self.__name__
75 if hasattr(obj, '_cache') and prop_name in obj._cache:
76 return obj._cache[prop_name][0]
77
78 # If not, do the calculation and release the lock.
79 return super(threaded_cached_property, self).__get__(obj, cls)