Source code for PyExpLabSys.thirdparty.cached_property
# -*- coding: utf-8 -*-
"""Copy of the cached property project from
https://github.com/pydanny/cached-property
There is a copy in PyExpLabSys only to not introduce a dependency for such a
small functionality
All credits goes to the original author
"""
__author__ = 'Daniel Greenfeld'
__email__ = 'pydanny@gmail.com'
__version__ = '0.1.5'
__license__ = 'BSD'
import threading
# pylint: disable=invalid-name,too-few-public-methods
class cached_property(object):
"""A property that is only computed once per instance and then replaces
itself with an ordinary attribute. Deleting the attribute resets the
property.
Source: https://github.com/bottlepy/bottle/commit/
fa7733e075da0d790d809aa3d2f53071897e6f76
"""
def __init__(self, func):
self.__doc__ = getattr(func, '__doc__')
self.func = func
def __get__(self, obj, cls):
if obj is None:
return self
value = obj.__dict__[self.func.__name__] = self.func(obj)
return value
# pylint: disable=invalid-name,too-few-public-methods
class threaded_cached_property(cached_property):
"""A cached_property version for use in environments where multiple
threads might concurrently try to access the property.
"""
def __init__(self, func):
super(threaded_cached_property, self).__init__(func)
self.lock = threading.RLock()
def __get__(self, obj, cls):
with self.lock:
# Double check if the value was computed before the lock was
# acquired.
prop_name = self.func.__name__
if prop_name in obj.__dict__:
return obj.__dict__[prop_name]
# If not, do the calculation and release the lock.
return super(threaded_cached_property, self).__get__(obj, cls)