Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.11/site-packages/wrapt/weakrefs.py: 17%

Shortcuts on this page

r m x   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

47 statements  

1"""Weak reference proxy for functions and bound methods.""" 

2 

3import functools 

4import weakref 

5 

6from .__wrapt__ import BaseObjectProxy, _FunctionWrapperBase 

7 

8# A weak function proxy. This will work on instance methods, class 

9# methods, static methods and regular functions. Special treatment is 

10# needed for the method types because the bound method is effectively a 

11# transient object and applying a weak reference to one will immediately 

12# result in it being destroyed and the weakref callback called. The weak 

13# reference is therefore applied to the instance the method is bound to 

14# and the original function. The function is then rebound at the point 

15# of a call via the weak function proxy. 

16# 

17# Where the function is a wrapt function wrapper, such as results from 

18# applying a decorator, the same applies but there may be no instance, 

19# either because the wrapper was never bound, as for a decorated free 

20# function, or because the method was accessed via the class rather 

21# than an instance. In that case a weak reference to the class the 

22# wrapper was accessed through, the owner, is retained as well so that 

23# the function can still be rebound correctly at the point of a call. 

24 

25 

26def _weak_function_proxy_callback(ref, proxy, callback): 

27 if proxy._self_expired: 

28 return 

29 

30 proxy._self_expired = True 

31 

32 # This could raise an exception. We let it propagate back and let 

33 # the weakref.proxy() deal with it, at which point it generally 

34 # prints out a short error message direct to stderr and keeps going. 

35 

36 if callback is not None: 

37 callback(proxy) 

38 

39 

40class WeakFunctionProxy(BaseObjectProxy): 

41 """A weak function proxy.""" 

42 

43 def __init__(self, wrapped, callback=None): 

44 """Create a proxy to object which uses a weak reference. This is 

45 similar to the `weakref.proxy` but is designed to work with functions 

46 and methods. It will automatically rebind the function to the instance 

47 when called if the function was originally a bound method. This is 

48 necessary because bound methods are transient objects and applying a 

49 weak reference to one will immediately result in it being destroyed 

50 and the weakref callback called. The weak reference is therefore 

51 applied to the instance the method is bound to and the original 

52 function. The function is then rebound at the point of a call via the 

53 weak function proxy. 

54 """ 

55 

56 # We need to determine if the wrapped function is actually a 

57 # bound method. In the case of a bound method, we need to keep a 

58 # reference to the original unbound function and the instance. 

59 # This is necessary because if we hold a reference to the bound 

60 # function, it will be the only reference and given it is a 

61 # temporary object, it will almost immediately expire and 

62 # the weakref callback triggered. So what is done is that we 

63 # hold a reference to the instance and unbound function and 

64 # when called bind the function to the instance once again and 

65 # then call it. Note that we avoid using a nested function for 

66 # the callback here so as not to cause any odd reference cycles. 

67 

68 _callback = callback and functools.partial( 

69 _weak_function_proxy_callback, proxy=self, callback=callback 

70 ) 

71 

72 self._self_expired = False 

73 self._self_owner = None 

74 

75 if isinstance(wrapped, _FunctionWrapperBase): 

76 # A function wrapper may have no instance, either because it 

77 # was never bound, as for a decorated free function, or 

78 # because the method was accessed via the class rather than 

79 # an instance. Only take a weak reference to the instance 

80 # where there is one. The owner is the class the wrapper was 

81 # accessed through, and is retained so the function can be 

82 # rebound with it when called. Without it a classmethod 

83 # accessed via the class could not be rebound at all, and an 

84 # instance method accessed via the class would not be able 

85 # to identify an instance passed as the first argument. 

86 

87 instance = wrapped._self_instance 

88 self._self_instance = ( 

89 weakref.ref(instance, _callback) if instance is not None else None 

90 ) 

91 owner = wrapped._self_owner 

92 if owner is not None: 

93 self._self_owner = weakref.ref(owner, _callback) 

94 

95 if wrapped._self_parent is not None: 

96 # Explicit class in super() is used because the proxy 

97 # overrides __class__ and MRO-related methods to delegate 

98 # to the wrapped object, which can interfere with bare 

99 # super(). 

100 super(WeakFunctionProxy, self).__init__( 

101 weakref.proxy(wrapped._self_parent, _callback) 

102 ) 

103 

104 else: 

105 super(WeakFunctionProxy, self).__init__( 

106 weakref.proxy(wrapped, _callback) 

107 ) 

108 

109 return 

110 

111 try: 

112 self._self_instance = weakref.ref(wrapped.__self__, _callback) 

113 

114 super(WeakFunctionProxy, self).__init__( 

115 weakref.proxy(wrapped.__func__, _callback) 

116 ) 

117 

118 except AttributeError: 

119 self._self_instance = None 

120 

121 super(WeakFunctionProxy, self).__init__(weakref.proxy(wrapped, _callback)) 

122 

123 def __call__(*args, **kwargs): 

124 def _unpack_self(self, *args): 

125 return self, args 

126 

127 self, args = _unpack_self(*args) 

128 

129 # We perform a boolean check here on the instance and wrapped 

130 # function as that will trigger the reference error prior to 

131 # calling if the reference had expired. 

132 

133 instance = self._self_instance and self._self_instance() 

134 function = self.__wrapped__ and self.__wrapped__ 

135 

136 # If the wrapped function was originally a bound method but the 

137 # instance it was bound to has been garbage collected, raise a 

138 # ReferenceError rather than silently calling it as unbound. 

139 

140 if self._self_instance is not None and instance is None: 

141 raise ReferenceError("weakly-referenced object no longer exists") 

142 

143 # If the wrapped function was a function wrapper for which the 

144 # owner was retained, rebind the function against the instance, 

145 # which may be None, and that owner. This is what a classmethod 

146 # accessed via the class needs to be rebound at all, and what an 

147 # instance method accessed via the class needs to recognise an 

148 # instance passed as the first argument. If the owner has been 

149 # garbage collected, raise a ReferenceError as for the instance. 

150 

151 if self._self_owner is not None: 

152 owner = self._self_owner() 

153 if owner is None: 

154 raise ReferenceError("weakly-referenced object no longer exists") 

155 return function.__get__(instance, owner)(*args, **kwargs) 

156 

157 # Otherwise, if the wrapped function was originally a bound 

158 # function, for which we retained a reference to the instance and 

159 # the unbound function, we need to rebind the function and then 

160 # call it. If not just call the wrapped function. 

161 

162 if instance is None: 

163 return self.__wrapped__(*args, **kwargs) 

164 

165 return function.__get__(instance, type(instance))(*args, **kwargs)