Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 838406b

Browse files
carljmcorona10
andauthored
gh-106292: restore checking __dict__ in cached_property.__get__ (#106380)
* gh-106292: restore checking __dict__ in cached_property.__get__ Co-authored-by: Dong-hee Na <[email protected]>
1 parent 217f47d commit 838406b

File tree

3 files changed

+36
-10
lines changed

3 files changed

+36
-10
lines changed

Lib/functools.py

Lines changed: 13 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -957,9 +957,10 @@ def __isabstractmethod__(self):
957957

958958

959959
################################################################################
960-
### cached_property() - computed once per instance, cached as attribute
960+
### cached_property() - property result cached as instance attribute
961961
################################################################################
962962

963+
_NOT_FOUND = object()
963964

964965
class cached_property:
965966
def __init__(self, func):
@@ -990,15 +991,17 @@ def __get__(self, instance, owner=None):
990991
f"instance to cache {self.attrname!r} property."
991992
)
992993
raise TypeError(msg) from None
993-
val = self.func(instance)
994-
try:
995-
cache[self.attrname] = val
996-
except TypeError:
997-
msg = (
998-
f"The '__dict__' attribute on {type(instance).__name__!r} instance "
999-
f"does not support item assignment for caching {self.attrname!r} property."
1000-
)
1001-
raise TypeError(msg) from None
994+
val = cache.get(self.attrname, _NOT_FOUND)
995+
if val is _NOT_FOUND:
996+
val = self.func(instance)
997+
try:
998+
cache[self.attrname] = val
999+
except TypeError:
1000+
msg = (
1001+
f"The '__dict__' attribute on {type(instance).__name__!r} instance "
1002+
f"does not support item assignment for caching {self.attrname!r} property."
1003+
)
1004+
raise TypeError(msg) from None
10021005
return val
10031006

10041007
__class_getitem__ = classmethod(GenericAlias)

Lib/test/test_functools.py

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3037,6 +3037,25 @@ def test_access_from_class(self):
30373037
def test_doc(self):
30383038
self.assertEqual(CachedCostItem.cost.__doc__, "The cost of the item.")
30393039

3040+
def test_subclass_with___set__(self):
3041+
"""Caching still works for a subclass defining __set__."""
3042+
class readonly_cached_property(py_functools.cached_property):
3043+
def __set__(self, obj, value):
3044+
raise AttributeError("read only property")
3045+
3046+
class Test:
3047+
def __init__(self, prop):
3048+
self._prop = prop
3049+
3050+
@readonly_cached_property
3051+
def prop(self):
3052+
return self._prop
3053+
3054+
t = Test(1)
3055+
self.assertEqual(t.prop, 1)
3056+
t._prop = 999
3057+
self.assertEqual(t.prop, 1)
3058+
30403059

30413060
if __name__ == '__main__':
30423061
unittest.main()
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
Check for an instance-dict cached value in the :meth:`__get__` method of
2+
:func:`functools.cached_property`. This better matches the pre-3.12 behavior
3+
and improves compatibility for users subclassing
4+
:func:`functools.cached_property` and adding a :meth:`__set__` method.

0 commit comments

Comments
 (0)