[Python-checkins] [3.12] gh-106292: restore checking __dict__ in cached_property.__get__ (GH-106380) (#106469)
carljm
webhook-mailer at python.org
Wed Jul 5 19:31:57 EDT 2023
https://github.com/python/cpython/commit/7b615a1573b8a119e8ad27915912da745756a547
commit: 7b615a1573b8a119e8ad27915912da745756a547
branch: 3.12
author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com>
committer: carljm <carl at oddbird.net>
date: 2023-07-05T23:31:54Z
summary:
[3.12] gh-106292: restore checking __dict__ in cached_property.__get__ (GH-106380) (#106469)
gh-106292: restore checking __dict__ in cached_property.__get__ (GH-106380)
* gh-106292: restore checking __dict__ in cached_property.__get__
(cherry picked from commit 838406b4fc044c0b2f397c23275c69f16a76205b)
Co-authored-by: Carl Meyer <carl at oddbird.net>
Co-authored-by: Dong-hee Na <donghee.na92 at gmail.com>
files:
A Misc/NEWS.d/next/Library/2023-07-03-15-09-44.gh-issue-106292.3npldV.rst
M Lib/functools.py
M Lib/test/test_functools.py
diff --git a/Lib/functools.py b/Lib/functools.py
index 72b2103e7a554..2ae4290f98398 100644
--- a/Lib/functools.py
+++ b/Lib/functools.py
@@ -956,9 +956,10 @@ def __isabstractmethod__(self):
################################################################################
-### cached_property() - computed once per instance, cached as attribute
+### cached_property() - property result cached as instance attribute
################################################################################
+_NOT_FOUND = object()
class cached_property:
def __init__(self, func):
@@ -989,15 +990,17 @@ def __get__(self, instance, owner=None):
f"instance to cache {self.attrname!r} property."
)
raise TypeError(msg) from None
- val = self.func(instance)
- try:
- cache[self.attrname] = val
- except TypeError:
- msg = (
- f"The '__dict__' attribute on {type(instance).__name__!r} instance "
- f"does not support item assignment for caching {self.attrname!r} property."
- )
- raise TypeError(msg) from None
+ val = cache.get(self.attrname, _NOT_FOUND)
+ if val is _NOT_FOUND:
+ val = self.func(instance)
+ try:
+ cache[self.attrname] = val
+ except TypeError:
+ msg = (
+ f"The '__dict__' attribute on {type(instance).__name__!r} instance "
+ f"does not support item assignment for caching {self.attrname!r} property."
+ )
+ raise TypeError(msg) from None
return val
__class_getitem__ = classmethod(GenericAlias)
diff --git a/Lib/test/test_functools.py b/Lib/test/test_functools.py
index d668fa4c3adf5..c4eca0f5b7951 100644
--- a/Lib/test/test_functools.py
+++ b/Lib/test/test_functools.py
@@ -3037,6 +3037,25 @@ def test_access_from_class(self):
def test_doc(self):
self.assertEqual(CachedCostItem.cost.__doc__, "The cost of the item.")
+ def test_subclass_with___set__(self):
+ """Caching still works for a subclass defining __set__."""
+ class readonly_cached_property(py_functools.cached_property):
+ def __set__(self, obj, value):
+ raise AttributeError("read only property")
+
+ class Test:
+ def __init__(self, prop):
+ self._prop = prop
+
+ @readonly_cached_property
+ def prop(self):
+ return self._prop
+
+ t = Test(1)
+ self.assertEqual(t.prop, 1)
+ t._prop = 999
+ self.assertEqual(t.prop, 1)
+
if __name__ == '__main__':
unittest.main()
diff --git a/Misc/NEWS.d/next/Library/2023-07-03-15-09-44.gh-issue-106292.3npldV.rst b/Misc/NEWS.d/next/Library/2023-07-03-15-09-44.gh-issue-106292.3npldV.rst
new file mode 100644
index 0000000000000..233509344d509
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2023-07-03-15-09-44.gh-issue-106292.3npldV.rst
@@ -0,0 +1,4 @@
+Check for an instance-dict cached value in the :meth:`__get__` method of
+:func:`functools.cached_property`. This better matches the pre-3.12 behavior
+and improves compatibility for users subclassing
+:func:`functools.cached_property` and adding a :meth:`__set__` method.
More information about the Python-checkins
mailing list