Coverage for Users / vladimirpavlov / PycharmProjects / parameterizable / tests / test_cacheable_properties_mixin_wrapping.py: 94%
50 statements
« prev ^ index » next coverage.py v7.13.1, created at 2026-01-01 16:37 -0600
« prev ^ index » next coverage.py v7.13.1, created at 2026-01-01 16:37 -0600
1from functools import cached_property
2from mixinforge import CacheablePropertiesMixin
4def test_wrapped_descriptor():
5 """Test that cached_property discovery works through __wrapped__ chains."""
6 # Simulate a decorator that wraps the descriptor
7 def descriptor_wrapper(descriptor):
8 class Wrapper:
9 def __init__(self, wrapped):
10 self.__wrapped__ = wrapped
11 def __set_name__(self, owner, name):
12 if hasattr(self.__wrapped__, "__set_name__"):
13 self.__wrapped__.__set_name__(owner, name)
14 def __get__(self, instance, owner):
15 return self.__wrapped__.__get__(instance, owner)
16 return Wrapper(descriptor)
18 class Wrapped(CacheablePropertiesMixin):
19 @descriptor_wrapper
20 @cached_property
21 def wrapped_prop(self):
22 return "wrapped"
24 w = Wrapped()
25 # Check discovery
26 assert "wrapped_prop" in w._all_cached_properties_names
28 # Check functionality (invalidation relies on name only, so it should work
29 # as long as the property puts the value in __dict__ under the same name)
30 # Note: cached_property stores value in __dict__[name].
31 # Our wrapper delegates __get__, so cached_property.__get__ executes.
32 # cached_property.__get__ writes to __dict__['wrapped_prop'].
34 assert w.wrapped_prop == "wrapped"
35 assert "wrapped_prop" in w.__dict__
37 w._invalidate_cache()
38 assert "wrapped_prop" not in w.__dict__
39 assert w.wrapped_prop == "wrapped"
41def test_deep_wrapping_chain():
42 """Test that __wrapped__ unwrapping respects the depth limit."""
43 # Create a wrapper that creates a chain of given depth
44 def create_deep_wrapper(wrapped_obj, depth):
45 """Create a chain of wrappers with __wrapped__ attributes."""
46 current = wrapped_obj
47 for _ in range(depth):
48 class Wrapper:
49 def __init__(self, inner):
50 self.__wrapped__ = inner
51 def __get__(self, instance, owner):
52 if hasattr(self.__wrapped__, '__get__'):
53 return self.__wrapped__.__get__(instance, owner)
54 return self.__wrapped__
55 current = Wrapper(current)
56 return current
58 class DeepWrapped(CacheablePropertiesMixin):
59 pass
61 # Manually add wrapped properties to the class
62 prop_shallow = cached_property(lambda self: "shallow")
63 prop_shallow.__set_name__(DeepWrapped, "shallow_wrapped")
64 DeepWrapped.shallow_wrapped = create_deep_wrapper(prop_shallow, 50)
66 prop_deep = cached_property(lambda self: "deep")
67 prop_deep.__set_name__(DeepWrapped, "deep_wrapped")
68 DeepWrapped.deep_wrapped = create_deep_wrapper(prop_deep, 150)
70 d = DeepWrapped()
71 names = d._all_cached_properties_names
73 # shallow_wrapped should be discovered (within depth limit of 100)
74 assert "shallow_wrapped" in names
76 # deep_wrapped should NOT be discovered (exceeds depth limit of 100)
77 assert "deep_wrapped" not in names