summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorOmer Katz <omer.drow@gmail.com>2021-04-01 13:48:00 +0300
committerGitHub <noreply@github.com>2021-04-01 13:48:00 +0300
commit560518287ab672fff3ca98107fb6bded456f6a01 (patch)
tree35d79e238d5572cef04445962d6dd6fc75ad5a34
parent8474df91301cf4341d9baed0b2d6df2b4f3d17aa (diff)
downloadkombu-560518287ab672fff3ca98107fb6bded456f6a01.tar.gz
Use a thread-safe implementation of cached_property (#1316)
* Use a thread-safe implementation of cached_property. * Restore setter and deleter. * Restore tests. * Fix __get__ signature for backport. * Cleanup. Co-authored-by: Asif Saif Uddin <auvipy@gmail.com>
-rw-r--r--kombu/utils/objects.py84
-rw-r--r--requirements/default.txt1
-rw-r--r--t/unit/utils/test_objects.py6
3 files changed, 41 insertions, 50 deletions
diff --git a/kombu/utils/objects.py b/kombu/utils/objects.py
index 4f220439..faddb932 100644
--- a/kombu/utils/objects.py
+++ b/kombu/utils/objects.py
@@ -1,64 +1,54 @@
"""Object Utilities."""
+__all__ = ('cached_property',)
-class cached_property:
- """Cached property descriptor.
+try:
+ from functools import _NOT_FOUND, cached_property as _cached_property
+except ImportError:
+ # TODO: Remove this fallback once we drop support for Python < 3.8
+ from cached_property import threaded_cached_property as _cached_property
- Caches the return value of the get method on first call.
+ _NOT_FOUND = object()
- Examples:
- .. code-block:: python
- @cached_property
- def connection(self):
- return Connection()
-
- @connection.setter # Prepares stored value
- def connection(self, value):
- if value is None:
- raise TypeError('Connection must be a connection')
- return value
-
- @connection.deleter
- def connection(self, value):
- # Additional action to do at del(self.attr)
- if value is not None:
- print('Connection {0!r} deleted'.format(value)
- """
-
- def __init__(self, fget=None, fset=None, fdel=None, doc=None):
- self.__get = fget
+class cached_property(_cached_property):
+ def __init__(self, fget=None, fset=None, fdel=None):
+ super().__init__(fget)
self.__set = fset
self.__del = fdel
- self.__doc__ = doc or fget.__doc__
- self.__name__ = fget.__name__
- self.__module__ = fget.__module__
- def __get__(self, obj, type=None):
- if obj is None:
- return self
- try:
- return obj.__dict__[self.__name__]
- except KeyError:
- value = obj.__dict__[self.__name__] = self.__get(obj)
- return value
+ if not hasattr(self, 'attrname'):
+ # This is a backport so we set this ourselves.
+ self.attrname = self.func.__name__
+
+ def __get__(self, instance, owner=None):
+ # TODO: Remove this after we drop support for Python<3.8
+ # or fix the signature in the cached_property package
+ return super().__get__(instance, owner)
- def __set__(self, obj, value):
- if obj is None:
+ def __set__(self, instance, value):
+ if instance is None:
return self
- if self.__set is not None:
- value = self.__set(obj, value)
- obj.__dict__[self.__name__] = value
- def __delete__(self, obj, _sentinel=object()):
- if obj is None:
+ with self.lock:
+ if self.__set is not None:
+ value = self.__set(instance, value)
+
+ cache = instance.__dict__
+ cache[self.attrname] = value
+
+ def __delete__(self, instance):
+ if instance is None:
return self
- value = obj.__dict__.pop(self.__name__, _sentinel)
- if self.__del is not None and value is not _sentinel:
- self.__del(obj, value)
+
+ with self.lock:
+ value = instance.__dict__.pop(self.attrname, _NOT_FOUND)
+
+ if self.__del and value is not _NOT_FOUND:
+ self.__del(instance, value)
def setter(self, fset):
- return self.__class__(self.__get, fset, self.__del)
+ return self.__class__(self.func, fset, self.__del)
def deleter(self, fdel):
- return self.__class__(self.__get, self.__set, fdel)
+ return self.__class__(self.func, self.__set, fdel)
diff --git a/requirements/default.txt b/requirements/default.txt
index eac21d82..4d27a499 100644
--- a/requirements/default.txt
+++ b/requirements/default.txt
@@ -1,3 +1,4 @@
importlib-metadata>=0.18; python_version<"3.8"
+cached_property; python_version<"3.8"
amqp>=5.0.6,<6.0.0
vine
diff --git a/t/unit/utils/test_objects.py b/t/unit/utils/test_objects.py
index a1ad2000..330a7db5 100644
--- a/t/unit/utils/test_objects.py
+++ b/t/unit/utils/test_objects.py
@@ -17,10 +17,10 @@ class test_cached_property:
self.xx = value
x = X()
- del(x.foo)
+ del x.foo
assert not x.xx
x.__dict__['foo'] = 'here'
- del(x.foo)
+ del x.foo
assert x.xx == 'here'
def test_when_access_from_class(self):
@@ -48,4 +48,4 @@ class test_cached_property:
x.foo = 30
assert x.xx == 10
- del(x.foo)
+ del x.foo