1 # -*- coding: utf-8 -*-
3 __author__
= 'Daniel Greenfeld'
4 __email__
= 'pydanny@gmail.com'
12 class cached_property(object):
14 A property that is only computed once per instance and then replaces itself
15 with an ordinary attribute. Deleting the attribute resets the property.
16 Source: https://github.com/bottlepy/bottle/commit/fa7733e075da0d790d809aa3d2f53071897e6f76
19 def __init__(self
, func
):
20 self
.__doc
__ = getattr(func
, '__doc__')
23 def __get__(self
, obj
, cls
):
26 return obj
.__dict
__.setdefault(self
.func
.__name
__, self
.func(obj
))
29 # Leave for backwards compatibility
30 threaded_cached_property
= cached_property
33 class cached_property_with_ttl(object):
35 A property that is only computed once per instance and then replaces itself
36 with an ordinary attribute. Setting the ttl to a number expresses how long
37 the property will last before being timed out.
40 def __init__(self
, ttl
=None):
43 if callable(ttl_or_func
):
44 self
.prepare_func(ttl_or_func
)
46 self
.ttl
= ttl_or_func
48 def prepare_func(self
, func
, doc
=None):
49 '''Prepare to cache object method.'''
51 self
.__doc
__ = doc
or func
.__doc
__
52 self
.__name
__ = func
.__name
__
53 self
.__module
__ = func
.__module
__
55 def __call__(self
, func
, doc
=None):
56 self
.prepare_func(func
, doc
)
59 def __get__(self
, obj
, cls
):
65 value
, last_update
= obj
._cache
[self
.__name
__]
66 if self
.ttl
and self
.ttl
> 0 and now
- last_update
> self
.ttl
:
68 except (KeyError, AttributeError):
69 value
= self
.func(obj
)
72 except AttributeError:
73 cache
= obj
._cache
= {}
74 cache
[self
.__name
__] = (value
, now
)
78 def __delete__(self
, obj
):
80 del obj
._cache
[self
.__name
__]
81 except (KeyError, AttributeError):
84 # Aliases to make cached_property_with_ttl easier to use
85 cached_property_ttl
= cached_property_with_ttl
86 timed_cached_property
= cached_property_with_ttl
89 class threaded_cached_property_with_ttl(cached_property_with_ttl
):
91 A cached_property version for use in environments where multiple threads
92 might concurrently try to access the property.
95 def __init__(self
, ttl
=None):
96 super(threaded_cached_property_with_ttl
, self
).__init
__(ttl
)
97 self
.lock
= threading
.RLock()
99 def __get__(self
, obj
, cls
):
101 # Double check if the value was computed before the lock was
103 prop_name
= self
.__name
__
104 if hasattr(obj
, '_cache') and prop_name
in obj
._cache
:
105 return obj
._cache
[prop_name
][0]
107 # If not, do the calculation and release the lock.
108 return super(threaded_cached_property_with_ttl
, self
).__get
__(obj
,
111 # Alias to make threaded_cached_property_with_ttl easier to use
112 threaded_cached_property_ttl
= threaded_cached_property_with_ttl
113 timed_threaded_cached_property
= threaded_cached_property_with_ttl