Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.8/site-packages/blinker/_saferef.py: 26%

77 statements  

« prev     ^ index     » next       coverage.py v7.2.7, created at 2023-06-07 06:35 +0000

1# extracted from Louie, http://pylouie.org/ 

2# updated for Python 3 

3# 

4# Copyright (c) 2006 Patrick K. O'Brien, Mike C. Fletcher, 

5# Matthew R. Scott 

6# 

7# Redistribution and use in source and binary forms, with or without 

8# modification, are permitted provided that the following conditions are 

9# met: 

10# 

11# * Redistributions of source code must retain the above copyright 

12# notice, this list of conditions and the following disclaimer. 

13# 

14# * Redistributions in binary form must reproduce the above 

15# copyright notice, this list of conditions and the following 

16# disclaimer in the documentation and/or other materials provided 

17# with the distribution. 

18# 

19# * Neither the name of the <ORGANIZATION> nor the names of its 

20# contributors may be used to endorse or promote products derived 

21# from this software without specific prior written permission. 

22# 

23# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 

24# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 

25# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 

26# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 

27# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 

28# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 

29# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 

30# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 

31# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 

32# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 

33# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 

34# 

35"""Refactored 'safe reference from dispatcher.py""" 

36import operator 

37import sys 

38import traceback 

39import weakref 

40 

41 

42get_self = operator.attrgetter("__self__") 

43get_func = operator.attrgetter("__func__") 

44 

45 

46def safe_ref(target, on_delete=None): 

47 """Return a *safe* weak reference to a callable target. 

48 

49 - ``target``: The object to be weakly referenced, if it's a bound 

50 method reference, will create a BoundMethodWeakref, otherwise 

51 creates a simple weakref. 

52 

53 - ``on_delete``: If provided, will have a hard reference stored to 

54 the callable to be called after the safe reference goes out of 

55 scope with the reference object, (either a weakref or a 

56 BoundMethodWeakref) as argument. 

57 """ 

58 try: 

59 im_self = get_self(target) 

60 except AttributeError: 

61 if callable(on_delete): 

62 return weakref.ref(target, on_delete) 

63 else: 

64 return weakref.ref(target) 

65 else: 

66 if im_self is not None: 

67 # Turn a bound method into a BoundMethodWeakref instance. 

68 # Keep track of these instances for lookup by disconnect(). 

69 assert hasattr(target, "im_func") or hasattr(target, "__func__"), ( 

70 f"safe_ref target {target!r} has im_self, but no im_func, " 

71 "don't know how to create reference" 

72 ) 

73 reference = BoundMethodWeakref(target=target, on_delete=on_delete) 

74 return reference 

75 

76 

77class BoundMethodWeakref: 

78 """'Safe' and reusable weak references to instance methods. 

79 

80 BoundMethodWeakref objects provide a mechanism for referencing a 

81 bound method without requiring that the method object itself 

82 (which is normally a transient object) is kept alive. Instead, 

83 the BoundMethodWeakref object keeps weak references to both the 

84 object and the function which together define the instance method. 

85 

86 Attributes: 

87 

88 - ``key``: The identity key for the reference, calculated by the 

89 class's calculate_key method applied to the target instance method. 

90 

91 - ``deletion_methods``: Sequence of callable objects taking single 

92 argument, a reference to this object which will be called when 

93 *either* the target object or target function is garbage 

94 collected (i.e. when this object becomes invalid). These are 

95 specified as the on_delete parameters of safe_ref calls. 

96 

97 - ``weak_self``: Weak reference to the target object. 

98 

99 - ``weak_func``: Weak reference to the target function. 

100 

101 Class Attributes: 

102 

103 - ``_all_instances``: Class attribute pointing to all live 

104 BoundMethodWeakref objects indexed by the class's 

105 calculate_key(target) method applied to the target objects. 

106 This weak value dictionary is used to short-circuit creation so 

107 that multiple references to the same (object, function) pair 

108 produce the same BoundMethodWeakref instance. 

109 """ 

110 

111 _all_instances = weakref.WeakValueDictionary() # type: ignore[var-annotated] 

112 

113 def __new__(cls, target, on_delete=None, *arguments, **named): 

114 """Create new instance or return current instance. 

115 

116 Basically this method of construction allows us to 

117 short-circuit creation of references to already-referenced 

118 instance methods. The key corresponding to the target is 

119 calculated, and if there is already an existing reference, 

120 that is returned, with its deletion_methods attribute updated. 

121 Otherwise the new instance is created and registered in the 

122 table of already-referenced methods. 

123 """ 

124 key = cls.calculate_key(target) 

125 current = cls._all_instances.get(key) 

126 if current is not None: 

127 current.deletion_methods.append(on_delete) 

128 return current 

129 else: 

130 base = super().__new__(cls) 

131 cls._all_instances[key] = base 

132 base.__init__(target, on_delete, *arguments, **named) 

133 return base 

134 

135 def __init__(self, target, on_delete=None): 

136 """Return a weak-reference-like instance for a bound method. 

137 

138 - ``target``: The instance-method target for the weak reference, 

139 must have im_self and im_func attributes and be 

140 reconstructable via the following, which is true of built-in 

141 instance methods:: 

142 

143 target.im_func.__get__( target.im_self ) 

144 

145 - ``on_delete``: Optional callback which will be called when 

146 this weak reference ceases to be valid (i.e. either the 

147 object or the function is garbage collected). Should take a 

148 single argument, which will be passed a pointer to this 

149 object. 

150 """ 

151 

152 def remove(weak, self=self): 

153 """Set self.isDead to True when method or instance is destroyed.""" 

154 methods = self.deletion_methods[:] 

155 del self.deletion_methods[:] 

156 try: 

157 del self.__class__._all_instances[self.key] 

158 except KeyError: 

159 pass 

160 for function in methods: 

161 try: 

162 if callable(function): 

163 function(self) 

164 except Exception: 

165 try: 

166 traceback.print_exc() 

167 except AttributeError: 

168 e = sys.exc_info()[1] 

169 print( 

170 f"Exception during saferef {self} " 

171 f"cleanup function {function}: {e}" 

172 ) 

173 

174 self.deletion_methods = [on_delete] 

175 self.key = self.calculate_key(target) 

176 im_self = get_self(target) 

177 im_func = get_func(target) 

178 self.weak_self = weakref.ref(im_self, remove) 

179 self.weak_func = weakref.ref(im_func, remove) 

180 self.self_name = str(im_self) 

181 self.func_name = str(im_func.__name__) 

182 

183 @classmethod 

184 def calculate_key(cls, target): 

185 """Calculate the reference key for this reference. 

186 

187 Currently this is a two-tuple of the id()'s of the target 

188 object and the target function respectively. 

189 """ 

190 return (id(get_self(target)), id(get_func(target))) 

191 

192 def __str__(self): 

193 """Give a friendly representation of the object.""" 

194 return "{}({}.{})".format( 

195 self.__class__.__name__, 

196 self.self_name, 

197 self.func_name, 

198 ) 

199 

200 __repr__ = __str__ 

201 

202 def __hash__(self): 

203 return hash((self.self_name, self.key)) 

204 

205 def __nonzero__(self): 

206 """Whether we are still a valid reference.""" 

207 return self() is not None 

208 

209 def __eq__(self, other): 

210 """Compare with another reference.""" 

211 if not isinstance(other, self.__class__): 

212 return operator.eq(self.__class__, type(other)) 

213 return operator.eq(self.key, other.key) 

214 

215 def __call__(self): 

216 """Return a strong reference to the bound method. 

217 

218 If the target cannot be retrieved, then will return None, 

219 otherwise returns a bound instance method for our object and 

220 function. 

221 

222 Note: You may call this method any number of times, as it does 

223 not invalidate the reference. 

224 """ 

225 target = self.weak_self() 

226 if target is not None: 

227 function = self.weak_func() 

228 if function is not None: 

229 return function.__get__(target) 

230 return None