Coverage for Users/vladimirpavlov/PycharmProjects/parameterizable/tests/test_cacheable_properties_mixin_wrapping.py: 94%

1 from functools import cached_property

2 from mixinforge import CacheablePropertiesMixin

3

4 def 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)

17

18 class Wrapped(CacheablePropertiesMixin):

19 @descriptor_wrapper

20 @cached_property

21 def wrapped_prop(self):

22 return "wrapped"

23

24 w = Wrapped()

25 # Check discovery

26 assert "wrapped_prop" in w._all_cached_properties_names

27

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'].

33

34 assert w.wrapped_prop == "wrapped"

35 assert "wrapped_prop" in w.__dict__

36

37 w._invalidate_cache()

38 assert "wrapped_prop" not in w.__dict__

39 assert w.wrapped_prop == "wrapped"

40

41 def 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

57

58 class DeepWrapped(CacheablePropertiesMixin):

59 pass

60

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)

65

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)

69

70 d = DeepWrapped()

71 names = d._all_cached_properties_names

72

73 # shallow_wrapped should be discovered (within depth limit of 100)

74 assert "shallow_wrapped" in names

75

76 # deep_wrapped should NOT be discovered (exceeds depth limit of 100)

77 assert "deep_wrapped" not in names