Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.8/site-packages/wrapt/patches.py: 22%
87 statements
« prev ^ index » next coverage.py v7.4.0, created at 2024-01-03 07:57 +0000
« prev ^ index » next coverage.py v7.4.0, created at 2024-01-03 07:57 +0000
1import inspect
2import sys
4PY2 = sys.version_info[0] == 2
6if PY2:
7 string_types = basestring,
8else:
9 string_types = str,
11from .__wrapt__ import FunctionWrapper
13# Helper functions for applying wrappers to existing functions.
15def resolve_path(module, name):
16 if isinstance(module, string_types):
17 __import__(module)
18 module = sys.modules[module]
20 parent = module
22 path = name.split('.')
23 attribute = path[0]
25 # We can't just always use getattr() because in doing
26 # that on a class it will cause binding to occur which
27 # will complicate things later and cause some things not
28 # to work. For the case of a class we therefore access
29 # the __dict__ directly. To cope though with the wrong
30 # class being given to us, or a method being moved into
31 # a base class, we need to walk the class hierarchy to
32 # work out exactly which __dict__ the method was defined
33 # in, as accessing it from __dict__ will fail if it was
34 # not actually on the class given. Fallback to using
35 # getattr() if we can't find it. If it truly doesn't
36 # exist, then that will fail.
38 def lookup_attribute(parent, attribute):
39 if inspect.isclass(parent):
40 for cls in inspect.getmro(parent):
41 if attribute in vars(cls):
42 return vars(cls)[attribute]
43 else:
44 return getattr(parent, attribute)
45 else:
46 return getattr(parent, attribute)
48 original = lookup_attribute(parent, attribute)
50 for attribute in path[1:]:
51 parent = original
52 original = lookup_attribute(parent, attribute)
54 return (parent, attribute, original)
56def apply_patch(parent, attribute, replacement):
57 setattr(parent, attribute, replacement)
59def wrap_object(module, name, factory, args=(), kwargs={}):
60 (parent, attribute, original) = resolve_path(module, name)
61 wrapper = factory(original, *args, **kwargs)
62 apply_patch(parent, attribute, wrapper)
63 return wrapper
65# Function for applying a proxy object to an attribute of a class
66# instance. The wrapper works by defining an attribute of the same name
67# on the class which is a descriptor and which intercepts access to the
68# instance attribute. Note that this cannot be used on attributes which
69# are themselves defined by a property object.
71class AttributeWrapper(object):
73 def __init__(self, attribute, factory, args, kwargs):
74 self.attribute = attribute
75 self.factory = factory
76 self.args = args
77 self.kwargs = kwargs
79 def __get__(self, instance, owner):
80 value = instance.__dict__[self.attribute]
81 return self.factory(value, *self.args, **self.kwargs)
83 def __set__(self, instance, value):
84 instance.__dict__[self.attribute] = value
86 def __delete__(self, instance):
87 del instance.__dict__[self.attribute]
89def wrap_object_attribute(module, name, factory, args=(), kwargs={}):
90 path, attribute = name.rsplit('.', 1)
91 parent = resolve_path(module, path)[2]
92 wrapper = AttributeWrapper(attribute, factory, args, kwargs)
93 apply_patch(parent, attribute, wrapper)
94 return wrapper
96# Functions for creating a simple decorator using a FunctionWrapper,
97# plus short cut functions for applying wrappers to functions. These are
98# for use when doing monkey patching. For a more featured way of
99# creating decorators see the decorator decorator instead.
101def function_wrapper(wrapper):
102 def _wrapper(wrapped, instance, args, kwargs):
103 target_wrapped = args[0]
104 if instance is None:
105 target_wrapper = wrapper
106 elif inspect.isclass(instance):
107 target_wrapper = wrapper.__get__(None, instance)
108 else:
109 target_wrapper = wrapper.__get__(instance, type(instance))
110 return FunctionWrapper(target_wrapped, target_wrapper)
111 return FunctionWrapper(wrapper, _wrapper)
113def wrap_function_wrapper(module, name, wrapper):
114 return wrap_object(module, name, FunctionWrapper, (wrapper,))
116def patch_function_wrapper(module, name, enabled=None):
117 def _wrapper(wrapper):
118 return wrap_object(module, name, FunctionWrapper, (wrapper, enabled))
119 return _wrapper
121def transient_function_wrapper(module, name):
122 def _decorator(wrapper):
123 def _wrapper(wrapped, instance, args, kwargs):
124 target_wrapped = args[0]
125 if instance is None:
126 target_wrapper = wrapper
127 elif inspect.isclass(instance):
128 target_wrapper = wrapper.__get__(None, instance)
129 else:
130 target_wrapper = wrapper.__get__(instance, type(instance))
131 def _execute(wrapped, instance, args, kwargs):
132 (parent, attribute, original) = resolve_path(module, name)
133 replacement = FunctionWrapper(original, target_wrapper)
134 setattr(parent, attribute, replacement)
135 try:
136 return wrapped(*args, **kwargs)
137 finally:
138 setattr(parent, attribute, original)
139 return FunctionWrapper(target_wrapped, _execute)
140 return FunctionWrapper(wrapper, _wrapper)
141 return _decorator