Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.11/site-packages/wrapt/proxies.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
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
1"""Variants of ObjectProxy for different use cases."""
3from collections.abc import Callable
4from types import ModuleType
6from .__wrapt__ import BaseObjectProxy
7from .synchronization import synchronized
9# Define ObjectProxy which for compatibility adds `__iter__()` support which
10# has been removed from `BaseObjectProxy`.
13class ObjectProxy(BaseObjectProxy):
14 """A generic object proxy which forwards special methods as needed.
15 For backwards compatibility this class adds support for `__iter__()`. If
16 you don't need backward compatibility for `__iter__()` support then it is
17 preferable to use `BaseObjectProxy` directly. If you want automatic
18 support for special dunder methods for callables, iterators, and async,
19 then use `AutoObjectProxy`."""
21 @property
22 def __object_proxy__(self):
23 return ObjectProxy
25 def __new__(cls, *args, **kwargs):
26 return super().__new__(cls)
28 def __iter__(self):
29 return iter(self.__wrapped__)
32# Define variant of ObjectProxy which can automatically adjust to the wrapped
33# object and add special dunder methods.
36def __wrapper_call__(*args, **kwargs):
37 def _unpack_self(self, *args):
38 return self, args
40 self, args = _unpack_self(*args)
42 return self.__wrapped__(*args, **kwargs)
45def __wrapper_iter__(self):
46 return iter(self.__wrapped__)
49def __wrapper_next__(self):
50 return self.__wrapped__.__next__()
53def __wrapper_aiter__(self):
54 return self.__wrapped__.__aiter__()
57async def __wrapper_anext__(self):
58 return await self.__wrapped__.__anext__()
61def __wrapper_length_hint__(self):
62 return self.__wrapped__.__length_hint__()
65def __wrapper_await__(self):
66 return (yield from self.__wrapped__.__await__())
69def __wrapper_get__(self, instance, owner):
70 return self.__wrapped__.__get__(instance, owner)
73def __wrapper_set__(self, instance, value):
74 return self.__wrapped__.__set__(instance, value)
77def __wrapper_delete__(self, instance):
78 return self.__wrapped__.__delete__(instance)
81def __wrapper_set_name__(self, owner, name):
82 return self.__wrapped__.__set_name__(owner, name)
85class AutoObjectProxy(BaseObjectProxy):
86 """An object proxy which can automatically adjust to the wrapped object
87 and add special dunder methods as needed. Note that this creates a new
88 class for each instance, so it has much higher memory overhead than using
89 `BaseObjectProxy` directly. If you know what special dunder methods you need
90 then it is preferable to use `BaseObjectProxy` directly and add them to a
91 subclass as needed. If you only need `__iter__()` support for backwards
92 compatibility then use `ObjectProxy` instead.
93 """
95 def __new__(cls, wrapped):
96 """Injects special dunder methods into a dynamically created subclass
97 as needed based on the wrapped object.
98 """
100 namespace = {}
102 wrapped_attrs = dir(wrapped)
103 class_attrs = set(dir(cls))
105 if callable(wrapped) and "__call__" not in class_attrs:
106 namespace["__call__"] = __wrapper_call__
108 if "__iter__" in wrapped_attrs and "__iter__" not in class_attrs:
109 namespace["__iter__"] = __wrapper_iter__
111 if "__next__" in wrapped_attrs and "__next__" not in class_attrs:
112 namespace["__next__"] = __wrapper_next__
114 if "__aiter__" in wrapped_attrs and "__aiter__" not in class_attrs:
115 namespace["__aiter__"] = __wrapper_aiter__
117 if "__anext__" in wrapped_attrs and "__anext__" not in class_attrs:
118 namespace["__anext__"] = __wrapper_anext__
120 if "__length_hint__" in wrapped_attrs and "__length_hint__" not in class_attrs:
121 namespace["__length_hint__"] = __wrapper_length_hint__
123 # Note that not providing compatibility with generator-based coroutines
124 # (PEP 342) here as they are removed in Python 3.11+ and were deprecated
125 # in 3.8.
127 if "__await__" in wrapped_attrs and "__await__" not in class_attrs:
128 namespace["__await__"] = __wrapper_await__
130 if "__get__" in wrapped_attrs and "__get__" not in class_attrs:
131 namespace["__get__"] = __wrapper_get__
133 if "__set__" in wrapped_attrs and "__set__" not in class_attrs:
134 namespace["__set__"] = __wrapper_set__
136 if "__delete__" in wrapped_attrs and "__delete__" not in class_attrs:
137 namespace["__delete__"] = __wrapper_delete__
139 if "__set_name__" in wrapped_attrs and "__set_name__" not in class_attrs:
140 namespace["__set_name__"] = __wrapper_set_name__
142 name = cls.__name__
144 if cls is AutoObjectProxy:
145 name = BaseObjectProxy.__name__
147 # Explicit class in super() is required here to ensure __new__
148 # is called on the parent of AutoObjectProxy, not the dynamically
149 # created subclass.
150 return super(AutoObjectProxy, cls).__new__(type(name, (cls,), namespace))
152 def __wrapped_setattr_fixups__(self):
153 """Adjusts special dunder methods on the class as needed based on the
154 wrapped object, when `__wrapped__` is changed.
155 """
157 cls = type(self)
158 class_attrs = set(dir(cls))
160 if callable(self.__wrapped__):
161 if "__call__" not in class_attrs:
162 cls.__call__ = __wrapper_call__
163 elif getattr(cls, "__call__", None) is __wrapper_call__:
164 delattr(cls, "__call__")
166 if hasattr(self.__wrapped__, "__iter__"):
167 if "__iter__" not in class_attrs:
168 cls.__iter__ = __wrapper_iter__
169 elif getattr(cls, "__iter__", None) is __wrapper_iter__:
170 delattr(cls, "__iter__")
172 if hasattr(self.__wrapped__, "__next__"):
173 if "__next__" not in class_attrs:
174 cls.__next__ = __wrapper_next__
175 elif getattr(cls, "__next__", None) is __wrapper_next__:
176 delattr(cls, "__next__")
178 if hasattr(self.__wrapped__, "__aiter__"):
179 if "__aiter__" not in class_attrs:
180 cls.__aiter__ = __wrapper_aiter__
181 elif getattr(cls, "__aiter__", None) is __wrapper_aiter__:
182 delattr(cls, "__aiter__")
184 if hasattr(self.__wrapped__, "__anext__"):
185 if "__anext__" not in class_attrs:
186 cls.__anext__ = __wrapper_anext__
187 elif getattr(cls, "__anext__", None) is __wrapper_anext__:
188 delattr(cls, "__anext__")
190 if hasattr(self.__wrapped__, "__length_hint__"):
191 if "__length_hint__" not in class_attrs:
192 cls.__length_hint__ = __wrapper_length_hint__
193 elif getattr(cls, "__length_hint__", None) is __wrapper_length_hint__:
194 delattr(cls, "__length_hint__")
196 if hasattr(self.__wrapped__, "__await__"):
197 if "__await__" not in class_attrs:
198 cls.__await__ = __wrapper_await__
199 elif getattr(cls, "__await__", None) is __wrapper_await__:
200 delattr(cls, "__await__")
202 if hasattr(self.__wrapped__, "__get__"):
203 if "__get__" not in class_attrs:
204 cls.__get__ = __wrapper_get__
205 elif getattr(cls, "__get__", None) is __wrapper_get__:
206 delattr(cls, "__get__")
208 if hasattr(self.__wrapped__, "__set__"):
209 if "__set__" not in class_attrs:
210 cls.__set__ = __wrapper_set__
211 elif getattr(cls, "__set__", None) is __wrapper_set__:
212 delattr(cls, "__set__")
214 if hasattr(self.__wrapped__, "__delete__"):
215 if "__delete__" not in class_attrs:
216 cls.__delete__ = __wrapper_delete__
217 elif getattr(cls, "__delete__", None) is __wrapper_delete__:
218 delattr(cls, "__delete__")
220 if hasattr(self.__wrapped__, "__set_name__"):
221 if "__set_name__" not in class_attrs:
222 cls.__set_name__ = __wrapper_set_name__
223 elif getattr(cls, "__set_name__", None) is __wrapper_set_name__:
224 delattr(cls, "__set_name__")
227class LazyObjectProxy(AutoObjectProxy):
228 """An object proxy which can generate/create the wrapped object on demand
229 when it is first needed.
230 """
232 def __new__(cls, callback=None, *, interface=...):
233 """Injects special dunder methods into a dynamically created subclass
234 as needed based on the wrapped object.
235 """
237 if interface is ...:
238 interface = type(None)
240 namespace = {}
242 interface_attrs = dir(interface)
243 class_attrs = set(dir(cls))
245 if "__call__" in interface_attrs and "__call__" not in class_attrs:
246 namespace["__call__"] = __wrapper_call__
248 if "__iter__" in interface_attrs and "__iter__" not in class_attrs:
249 namespace["__iter__"] = __wrapper_iter__
251 if "__next__" in interface_attrs and "__next__" not in class_attrs:
252 namespace["__next__"] = __wrapper_next__
254 if "__aiter__" in interface_attrs and "__aiter__" not in class_attrs:
255 namespace["__aiter__"] = __wrapper_aiter__
257 if "__anext__" in interface_attrs and "__anext__" not in class_attrs:
258 namespace["__anext__"] = __wrapper_anext__
260 if (
261 "__length_hint__" in interface_attrs
262 and "__length_hint__" not in class_attrs
263 ):
264 namespace["__length_hint__"] = __wrapper_length_hint__
266 # Note that not providing compatibility with generator-based coroutines
267 # (PEP 342) here as they are removed in Python 3.11+ and were deprecated
268 # in 3.8.
270 if "__await__" in interface_attrs and "__await__" not in class_attrs:
271 namespace["__await__"] = __wrapper_await__
273 if "__get__" in interface_attrs and "__get__" not in class_attrs:
274 namespace["__get__"] = __wrapper_get__
276 if "__set__" in interface_attrs and "__set__" not in class_attrs:
277 namespace["__set__"] = __wrapper_set__
279 if "__delete__" in interface_attrs and "__delete__" not in class_attrs:
280 namespace["__delete__"] = __wrapper_delete__
282 if "__set_name__" in interface_attrs and "__set_name__" not in class_attrs:
283 namespace["__set_name__"] = __wrapper_set_name__
285 name = cls.__name__
287 # Explicit class in super() is required here to ensure __new__
288 # is called on the parent of AutoObjectProxy, not the dynamically
289 # created subclass.
290 return super(AutoObjectProxy, cls).__new__(type(name, (cls,), namespace))
292 def __init__(self, callback=None, *, interface=...):
293 """Initialize the object proxy with wrapped object as `None` but due
294 to presence of special `__wrapped_factory__` attribute addded first,
295 this will actually trigger the deferred creation of the wrapped object
296 when first needed.
297 """
299 if callback is not None:
300 self.__wrapped_factory__ = callback
302 super().__init__(None)
304 __wrapped_get_called__ = False
306 def __wrapped_factory__(self):
307 return None
309 def __wrapped_get__(self):
310 """Gets the wrapped object, creating it if necessary."""
312 # We synchronize on the class type, which will be unique to this instance
313 # since we inherit from `AutoObjectProxy` which creates a new class
314 # for each instance. If we synchronize on `self` or the method then
315 # we can end up in infinite recursion via `__getattr__()`.
317 with synchronized(type(self)):
318 # We were called because `__wrapped__` was not set, but because of
319 # multiple threads we may find that it has been set by the time
320 # we get the lock. So check again now whether `__wrapped__` is set.
321 # If it is then just return it, otherwise call the factory to
322 # create it.
324 if self.__wrapped_get_called__:
325 return self.__wrapped__
327 self.__wrapped__ = self.__wrapped_factory__()
329 self.__wrapped_get_called__ = True
331 return self.__wrapped__
334def lazy_import(name, attribute=None, *, interface=...):
335 """Lazily imports the module `name`, returning a `LazyObjectProxy` which
336 will import the module when it is first needed. When `name is a dotted name,
337 then the full dotted name is imported and the last module is taken as the
338 target. If `attribute` is provided then it is used to retrieve an attribute
339 from the module.
340 """
342 if attribute is not None:
343 if interface is ...:
344 interface = Callable
345 else:
346 if interface is ...:
347 interface = ModuleType
349 def _import():
350 module = __import__(name, fromlist=[""])
352 if attribute is not None:
353 return getattr(module, attribute)
355 return module
357 return LazyObjectProxy(_import, interface=interface)