1 # -*- coding: utf-8 -*-
3 __author__
= 'Daniel Greenfeld'
4 __email__
= 'pydanny@gmail.com'
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
17 Source: https://github.com/bottlepy/bottle/commit/fa7733e075da0d790d809aa3d2f53071897e6f76
20 def __init__(self
, ttl
=None):
23 if callable(ttl_or_func
):
24 self
.prepare_func(ttl_or_func
)
26 self
.ttl
= ttl_or_func
28 def prepare_func(self
, func
, doc
=None):
29 '''Prepare to cache object method.'''
31 self
.__doc
__ = doc
or func
.__doc
__
32 self
.__name
__ = func
.__name
__
33 self
.__module
__ = func
.__module
__
35 def __call__(self
, func
, doc
=None):
36 self
.prepare_func(func
, doc
)
39 def __get__(self
, obj
, cls
):
45 value
, last_update
= obj
._cache
[self
.__name
__]
46 if self
.ttl
and self
.ttl
> 0 and now
- last_update
> self
.ttl
:
48 except (KeyError, AttributeError):
49 value
= self
.func(obj
)
52 except AttributeError:
53 cache
= obj
._cache
= {}
54 cache
[self
.__name
__] = (value
, now
)
58 def __delattr__(self
, name
):
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.
66 def __init__(self
, ttl
=None):
67 super(threaded_cached_property
, self
).__init
__(ttl
)
68 self
.lock
= threading
.RLock()
70 def __get__(self
, obj
, cls
):
72 # Double check if the value was computed before the lock was
74 prop_name
= self
.__name
__
75 if hasattr(obj
, '_cache') and prop_name
in obj
._cache
:
76 return obj
._cache
[prop_name
][0]
78 # If not, do the calculation and release the lock.
79 return super(threaded_cached_property
, self
).__get
__(obj
, cls
)