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
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
1import inspect
2import sys
4from .__wrapt__ import FunctionWrapper
6# Helper functions for applying wrappers to existing functions.
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 """
20 if isinstance(target, str):
21 __import__(target)
22 target = sys.modules[target]
24 parent = target
26 path = name.split(".")
27 attribute = path[0]
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.
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)
52 original = lookup_attribute(parent, attribute)
54 for attribute in path[1:]:
55 parent = original
56 original = lookup_attribute(parent, attribute)
58 return (parent, attribute, original)
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 """
68 setattr(parent, attribute, replacement)
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 """
85 (parent, attribute, original) = resolve_path(target, name)
86 wrapper = factory(original, *args, **kwargs)
87 apply_patch(parent, attribute, wrapper)
89 return wrapper
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.
99class AttributeWrapper:
101 def __init__(self, attribute, factory, args, kwargs):
102 self.attribute = attribute
103 self.factory = factory
104 self.args = args
105 self.kwargs = kwargs
107 def __get__(self, instance, owner):
108 value = instance.__dict__[self.attribute]
109 return self.factory(value, *self.args, **self.kwargs)
111 def __set__(self, instance, value):
112 instance.__dict__[self.attribute] = value
114 def __delete__(self, instance):
115 del instance.__dict__[self.attribute]
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 """
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
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.
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 """
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)
168 return FunctionWrapper(wrapper, _wrapper)
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 """
183 return wrap_object(target, name, FunctionWrapper, (wrapper,))
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 """
201 def _wrapper(wrapper):
202 return wrap_object(target, name, FunctionWrapper, (wrapper, enabled))
204 return _wrapper
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 """
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))
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)
235 return FunctionWrapper(target_wrapped, _execute)
237 return FunctionWrapper(wrapper, _wrapper)
239 return _decorator