11"""Variants of ObjectProxy for different use cases."""
22
3+ from collections .abc import Callable
4+ from types import ModuleType
5+
36from .__wrapt__ import BaseObjectProxy
47from .decorators import synchronized
58
@@ -30,7 +33,12 @@ def __iter__(self):
3033# object and add special dunder methods.
3134
3235
33- def __wrapper_call__ (self , * args , ** kwargs ):
36+ def __wrapper_call__ (* args , ** kwargs ):
37+ def _unpack_self (self , * args ):
38+ return self , args
39+
40+ self , args = _unpack_self (* args )
41+
3442 return self .__wrapped__ (* args , ** kwargs )
3543
3644
@@ -136,7 +144,7 @@ def __new__(cls, wrapped):
136144 if cls is AutoObjectProxy :
137145 name = BaseObjectProxy .__name__
138146
139- return super ().__new__ (type (name , (cls ,), namespace ))
147+ return super (AutoObjectProxy , cls ).__new__ (type (name , (cls ,), namespace ))
140148
141149 def __wrapped_setattr_fixups__ (self ):
142150 """Adjusts special dunder methods on the class as needed based on the
@@ -218,10 +226,64 @@ class LazyObjectProxy(AutoObjectProxy):
218226 when it is first needed.
219227 """
220228
221- def __new__ (cls , callback = None ):
222- return super ().__new__ (cls , None )
229+ def __new__ (cls , callback = None , * , interface = ...):
230+ """Injects special dunder methods into a dynamically created subclass
231+ as needed based on the wrapped object.
232+ """
233+
234+ if interface is ...:
235+ interface = type (None )
236+
237+ namespace = {}
238+
239+ interface_attrs = dir (interface )
240+ class_attrs = set (dir (cls ))
241+
242+ if "__call__" in interface_attrs and "__call__" not in class_attrs :
243+ namespace ["__call__" ] = __wrapper_call__
244+
245+ if "__iter__" in interface_attrs and "__iter__" not in class_attrs :
246+ namespace ["__iter__" ] = __wrapper_iter__
247+
248+ if "__next__" in interface_attrs and "__next__" not in class_attrs :
249+ namespace ["__next__" ] = __wrapper_next__
250+
251+ if "__aiter__" in interface_attrs and "__aiter__" not in class_attrs :
252+ namespace ["__aiter__" ] = __wrapper_aiter__
253+
254+ if "__anext__" in interface_attrs and "__anext__" not in class_attrs :
255+ namespace ["__anext__" ] = __wrapper_anext__
223256
224- def __init__ (self , callback = None ):
257+ if (
258+ "__length_hint__" in interface_attrs
259+ and "__length_hint__" not in class_attrs
260+ ):
261+ namespace ["__length_hint__" ] = __wrapper_length_hint__
262+
263+ # Note that not providing compatibility with generator-based coroutines
264+ # (PEP 342) here as they are removed in Python 3.11+ and were deprecated
265+ # in 3.8.
266+
267+ if "__await__" in interface_attrs and "__await__" not in class_attrs :
268+ namespace ["__await__" ] = __wrapper_await__
269+
270+ if "__get__" in interface_attrs and "__get__" not in class_attrs :
271+ namespace ["__get__" ] = __wrapper_get__
272+
273+ if "__set__" in interface_attrs and "__set__" not in class_attrs :
274+ namespace ["__set__" ] = __wrapper_set__
275+
276+ if "__delete__" in interface_attrs and "__delete__" not in class_attrs :
277+ namespace ["__delete__" ] = __wrapper_delete__
278+
279+ if "__set_name__" in interface_attrs and "__set_name__" not in class_attrs :
280+ namespace ["__set_name__" ] = __wrapper_set_name__
281+
282+ name = cls .__name__
283+
284+ return super (AutoObjectProxy , cls ).__new__ (type (name , (cls ,), namespace ))
285+
286+ def __init__ (self , callback = None , * , interface = ...):
225287 """Initialize the object proxy with wrapped object as `None` but due
226288 to presence of special `__wrapped_factory__` attribute addded first,
227289 this will actually trigger the deferred creation of the wrapped object
@@ -263,14 +325,21 @@ def __wrapped_get__(self):
263325 return self .__wrapped__
264326
265327
266- def lazy_import (name , attribute = None ):
328+ def lazy_import (name , attribute = None , * , interface = ... ):
267329 """Lazily imports the module `name`, returning a `LazyObjectProxy` which
268330 will import the module when it is first needed. When `name is a dotted name,
269331 then the full dotted name is imported and the last module is taken as the
270332 target. If `attribute` is provided then it is used to retrieve an attribute
271333 from the module.
272334 """
273335
336+ if attribute is not None :
337+ if interface is ...:
338+ interface = Callable
339+ else :
340+ if interface is ...:
341+ interface = ModuleType
342+
274343 def _import ():
275344 module = __import__ (name , fromlist = ["" ])
276345
@@ -279,4 +348,4 @@ def _import():
279348
280349 return module
281350
282- return LazyObjectProxy (_import )
351+ return LazyObjectProxy (_import , interface = interface )
0 commit comments