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

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

83 statements  

1import inspect 

2import sys 

3 

4from .__wrapt__ import FunctionWrapper 

5 

6# Helper functions for applying wrappers to existing functions. 

7 

8 

9def resolve_path(target, name): 

10 """ 

11 Resolves the dotted path supplied as `name` to an attribute on a target 

12 object. The `target` can be a module, class, or instance of a class. If the 

13 `target` argument is a string, it is assumed to be the name of a module, 

14 which will be imported if necessary and then used as the target object. 

15 Returns a tuple containing the parent object holding the attribute lookup 

16 resolved to, the attribute name (path prefix removed if present), and the 

17 original attribute value. 

18 """ 

19 

20 if isinstance(target, str): 

21 __import__(target) 

22 target = sys.modules[target] 

23 

24 parent = target 

25 

26 path = name.split(".") 

27 attribute = path[0] 

28 

29 # We can't just always use getattr() because in doing 

30 # that on a class it will cause binding to occur which 

31 # will complicate things later and cause some things not 

32 # to work. For the case of a class we therefore access 

33 # the __dict__ directly. To cope though with the wrong 

34 # class being given to us, or a method being moved into 

35 # a base class, we need to walk the class hierarchy to 

36 # work out exactly which __dict__ the method was defined 

37 # in, as accessing it from __dict__ will fail if it was 

38 # not actually on the class given. Fallback to using 

39 # getattr() if we can't find it. If it truly doesn't 

40 # exist, then that will fail. 

41 

42 def lookup_attribute(parent, attribute): 

43 if inspect.isclass(parent): 

44 for cls in inspect.getmro(parent): 

45 if attribute in vars(cls): 

46 return vars(cls)[attribute] 

47 else: 

48 return getattr(parent, attribute) 

49 else: 

50 return getattr(parent, attribute) 

51 

52 original = lookup_attribute(parent, attribute) 

53 

54 for attribute in path[1:]: 

55 parent = original 

56 original = lookup_attribute(parent, attribute) 

57 

58 return (parent, attribute, original) 

59 

60 

61def apply_patch(parent, attribute, replacement): 

62 """ 

63 Convenience function for applying a patch to an attribute. Currently this 

64 maps to the standard setattr() function, but in the future may be extended 

65 to support more complex patching strategies. 

66 """ 

67 

68 setattr(parent, attribute, replacement) 

69 

70 

71def wrap_object(target, name, factory, args=(), kwargs={}): 

72 """ 

73 Wraps an object which is the attribute of a target object with a wrapper 

74 object created by the `factory` function. The `target` can be a module, 

75 class, or instance of a class. In the special case of `target` being a 

76 string, it is assumed to be the name of a module, with the module being 

77 imported if necessary and then used as the target object. The `name` is a 

78 string representing the dotted path to the attribute. The `factory` function 

79 should accept the original object and may accept additional positional and 

80 keyword arguments which will be set by unpacking input arguments using 

81 `*args` and `**kwargs` calling conventions. The factory function should 

82 return a new object that will replace the original object. 

83 """ 

84 

85 (parent, attribute, original) = resolve_path(target, name) 

86 wrapper = factory(original, *args, **kwargs) 

87 apply_patch(parent, attribute, wrapper) 

88 

89 return wrapper 

90 

91 

92# Function for applying a proxy object to an attribute of a class 

93# instance. The wrapper works by defining an attribute of the same name 

94# on the class which is a descriptor and which intercepts access to the 

95# instance attribute. Note that this cannot be used on attributes which 

96# are themselves defined by a property object. 

97 

98 

99class AttributeWrapper: 

100 

101 def __init__(self, attribute, factory, args, kwargs): 

102 self.attribute = attribute 

103 self.factory = factory 

104 self.args = args 

105 self.kwargs = kwargs 

106 

107 def __get__(self, instance, owner): 

108 value = instance.__dict__[self.attribute] 

109 return self.factory(value, *self.args, **self.kwargs) 

110 

111 def __set__(self, instance, value): 

112 instance.__dict__[self.attribute] = value 

113 

114 def __delete__(self, instance): 

115 del instance.__dict__[self.attribute] 

116 

117 

118def wrap_object_attribute(module, name, factory, args=(), kwargs={}): 

119 """ 

120 Wraps an object which is the attribute of a class instance with a wrapper 

121 object created by the `factory` function. It does this by patching the 

122 class, not the instance, with a descriptor that intercepts access to the 

123 instance attribute. The `module` can be a module, class, or instance of a 

124 class. In the special case of `module` being a string, it is assumed to be 

125 the name of a module, with the module being imported if necessary and then 

126 used as the target object. The `name` is a string representing the dotted 

127 path to the attribute. The `factory` function should accept the original 

128 object and may accept additional positional and keyword arguments which will 

129 be set by unpacking input arguments using `*args` and `**kwargs` calling 

130 conventions. The factory function should return a new object that will 

131 replace the original object. 

132 """ 

133 

134 path, attribute = name.rsplit(".", 1) 

135 parent = resolve_path(module, path)[2] 

136 wrapper = AttributeWrapper(attribute, factory, args, kwargs) 

137 apply_patch(parent, attribute, wrapper) 

138 return wrapper 

139 

140 

141# Functions for creating a simple decorator using a FunctionWrapper, 

142# plus short cut functions for applying wrappers to functions. These are 

143# for use when doing monkey patching. For a more featured way of 

144# creating decorators see the decorator decorator instead. 

145 

146 

147def function_wrapper(wrapper): 

148 """ 

149 Creates a decorator for wrapping a function with a `wrapper` function. 

150 The decorator which is returned may also be applied to any other callable 

151 objects such as lambda functions, methods, classmethods, and staticmethods, 

152 or objects which implement the `__call__()` method. The `wrapper` function 

153 should accept the `wrapped` function, `instance`, `args`, and `kwargs`, 

154 arguments and return the result of calling the wrapped function or some 

155 other appropriate value. 

156 """ 

157 

158 def _wrapper(wrapped, instance, args, kwargs): 

159 target_wrapped = args[0] 

160 if instance is None: 

161 target_wrapper = wrapper 

162 elif inspect.isclass(instance): 

163 target_wrapper = wrapper.__get__(None, instance) 

164 else: 

165 target_wrapper = wrapper.__get__(instance, type(instance)) 

166 return FunctionWrapper(target_wrapped, target_wrapper) 

167 

168 return FunctionWrapper(wrapper, _wrapper) 

169 

170 

171def wrap_function_wrapper(target, name, wrapper): 

172 """ 

173 Wraps a function which is the attribute of a target object with a `wrapper` 

174 function. The `target` can be a module, class, or instance of a class. In 

175 the special case of `target` being a string, it is assumed to be the name 

176 of a module, with the module being imported if necessary. The `name` is a 

177 string representing the dotted path to the attribute. The `wrapper` function 

178 should accept the `wrapped` function, `instance`, `args`, and `kwargs` 

179 arguments, and would return the result of calling the wrapped attribute or 

180 some other appropriate value. 

181 """ 

182 

183 return wrap_object(target, name, FunctionWrapper, (wrapper,)) 

184 

185 

186def patch_function_wrapper(target, name, enabled=None): 

187 """ 

188 Creates a decorator which can be applied to a wrapper function, where the 

189 wrapper function will be used to wrap a function which is the attribute of 

190 a target object. The `target` can be a module, class, or instance of a class. 

191 In the special case of `target` being a string, it is assumed to be the name 

192 of a module, with the module being imported if necessary. The `name` is a 

193 string representing the dotted path to the attribute. The `enabled` 

194 argument can be a boolean or a callable that returns a boolean. When a 

195 callable is provided, it will be called each time the wrapper is invoked to 

196 determine if the wrapper function should be executed or whether the wrapped 

197 function should be called directly. If `enabled` is not provided, the 

198 wrapper is enabled by default. 

199 """ 

200 

201 def _wrapper(wrapper): 

202 return wrap_object(target, name, FunctionWrapper, (wrapper, enabled)) 

203 

204 return _wrapper 

205 

206 

207def transient_function_wrapper(target, name): 

208 """Creates a decorator that patches a target function with a wrapper 

209 function, but only for the duration of the call that the decorator was 

210 applied to. The `target` can be a module, class, or instance of a class. 

211 In the special case of `target` being a string, it is assumed to be the name 

212 of a module, with the module being imported if necessary. The `name` is a 

213 string representing the dotted path to the attribute. 

214 """ 

215 

216 def _decorator(wrapper): 

217 def _wrapper(wrapped, instance, args, kwargs): 

218 target_wrapped = args[0] 

219 if instance is None: 

220 target_wrapper = wrapper 

221 elif inspect.isclass(instance): 

222 target_wrapper = wrapper.__get__(None, instance) 

223 else: 

224 target_wrapper = wrapper.__get__(instance, type(instance)) 

225 

226 def _execute(wrapped, instance, args, kwargs): 

227 (parent, attribute, original) = resolve_path(target, name) 

228 replacement = FunctionWrapper(original, target_wrapper) 

229 setattr(parent, attribute, replacement) 

230 try: 

231 return wrapped(*args, **kwargs) 

232 finally: 

233 setattr(parent, attribute, original) 

234 

235 return FunctionWrapper(target_wrapped, _execute) 

236 

237 return FunctionWrapper(wrapper, _wrapper) 

238 

239 return _decorator