11import typing
2- import abc
32import collections
4- from untypy .error import UntypyError
53from untypy .util .debug import debug
64
75def _f ():
86 yield 0
97generatorType = type (_f ())
108
11- class WyppWrapError (Exception ):
12- pass
13-
14- def _readonly (self , * args , ** kwargs ):
15- raise RuntimeError ("Cannot modify ReadOnlyDict" )
16-
17- class ReadOnlyDict (dict ):
18- __setitem__ = _readonly
19- __delitem__ = _readonly
20- pop = _readonly
21- popitem = _readonly
22- clear = _readonly
23- update = _readonly
24- setdefault = _readonly
25-
26- def patch (self , ty , extra ):
27- # SW (2024-10-18): With python 3.13 there is the behavior that extra is modified after patching
28- # the object. I never found out who is doing the modification. By wrapping extra with
29- # ReadOnlyDict, everything works. Strangely, no error occurs somewhere.
30- self .__extra__ = ReadOnlyDict (extra )
31- w = self .__wrapped__
32- m = None
33- if hasattr (w , '__module__' ):
34- m = getattr (w , '__module__' )
35- ty .__module__ = m
36- try :
37- self .__class__ = ty
38- except TypeError as e :
39- raise WyppWrapError (f'Cannot wrap { self .__wrapped__ } of type { type (self .__wrapped__ )} ' \
40- f'at type { ty } . Original error: { e } ' )
41-
429class WrapperBase :
4310 def __eq__ (self , other ):
4411 if hasattr (other , '__wrapped__' ):
@@ -49,17 +16,7 @@ def __ne__(self, other):
4916 return not self .__eq__ (other )
5017 def __hash__ (self ):
5118 return hash (self .__wrapped__ )
52- def __patch__ (self , ms , name = None , extra = None ):
53- if extra is None :
54- extra = {}
55- cls = self .__class__
56- if name is None :
57- name = cls .__name__
58- ty = type (name , (cls ,), ms )
59- patch (self , ty , extra )
6019 def __repr__ (self ):
61- #w = self.__wrapped__
62- #return f"Wrapper(addr=0x{id(self):09x}, wrapped_addr=0x{id(w):09x}, wrapped={repr(w)}"
6320 return repr (self .__wrapped__ )
6421 def __str__ (self ):
6522 return str (self .__wrapped__ )
@@ -72,27 +29,6 @@ def __reduce__(self): return self.__wrapped__.__reduce__()
7229 def __reduce_ex__ (self ): return self .__wrapped__ .__reduce_ex__ ()
7330 def __sizeof__ (self ): return self .__wrapped__ .__sizeof__ ()
7431
75- class ObjectWrapper (WrapperBase ):
76- def __init__ (self , baseObject ):
77- self .__dict__ = baseObject .__dict__
78- self .__wrapped__ = baseObject
79- def __patch__ (self , ms , name = None , extra = None ):
80- if extra is None :
81- extra = {}
82- cls = self .__class__
83- if name is None :
84- name = cls .__name__
85- wrappedCls = type (self .__wrapped__ )
86- ty = type (name , (wrappedCls , cls ), ms )
87- patch (self , ty , extra )
88-
89- class ABCObjectWrapper (abc .ABC , ObjectWrapper ):
90- pass
91-
92- # Superclasses in reverse order.
93- class ABCObjectWrapperRev (ObjectWrapper , abc .ABC ):
94- pass
95-
9632# A wrapper for list such that the class is a subclass of the builtin list class.
9733class ListWrapper (WrapperBase , list ): # important: inherit from WrapperBase first
9834 def __new__ (cls , content ):
@@ -224,33 +160,10 @@ def __new__(cls, content):
224160 self .__wrapped__ = content
225161 return self
226162
227- # These methods are not delegated to the wrapped object
228- _blacklist = [
229- '__class__' , '__delattr__' , '__dict__' , '__dir__' , '__doc__' ,
230- '__getattribute__' , '__get_attr_' , '__init_subclass__'
231- '__init__' , '__new__' , '__del__' , '__repr__' , '__setattr__' , '__str__' ,
232- '__hash__' , '__eq__' , '__patch__' ,
233- '__class_getitem__' , '__subclasshook__' ,
234- '__firstlineno__' , '__static_attributes__' ]
235-
236- _extra = ['__next__' ]
237-
238163# SimpleWrapper is a fallback for types that cannot be used as base types
239164class SimpleWrapper (WrapperBase ):
240- def __init__ (self , baseObject ):
241- self .__wrapped__ = baseObject
242- def __patch__ (self , ms , name = None , extra = None ):
243- if extra is None :
244- extra = {}
245- cls = self .__class__
246- if name is None :
247- name = cls .__name__
248- baseObject = self .__wrapped__
249- for x in dir (baseObject ) + _extra :
250- if x not in ms and x not in _blacklist and hasattr (baseObject , x ):
251- ms [x ] = getattr (baseObject , x )
252- ty = type (name , (cls ,), ms ) #
253- patch (self , ty , extra )
165+ def __init__ (self , wrapped ):
166+ self .__wrapped__ = wrapped
254167
255168class ValuesViewWrapper (SimpleWrapper ):
256169 pass
@@ -264,46 +177,99 @@ class KeysViewWrapper(SimpleWrapper):
264177 pass
265178collections .abc .KeysView .register (KeysViewWrapper )
266179
180+ def _wrap (wrapped , methods , mod , name , extra , cls ):
181+ if extra is None :
182+ extra = {}
183+ # Dynamically create a new class:
184+ # type(class_name, base_classes, class_dict)
185+ WrapperClass = type (
186+ name ,
187+ (cls ,),
188+ methods
189+ )
190+ WrapperClass .__module__ = mod
191+ w = WrapperClass (wrapped )
192+ w .__extra__ = extra
193+ return w
194+
195+ def wrapSimple (wrapped , methods , name , extra , cls = SimpleWrapper ):
196+ if name is None :
197+ name = cls .__name__
198+ mod = None
199+ else :
200+ if hasattr (wrapped , '__module__' ):
201+ mod = wrapped .__module__
202+ else :
203+ mod = None
204+ for x in ['__next__' , '__iter__' ]:
205+ if x not in methods and hasattr (wrapped , x ):
206+ attr = getattr (wrapped , x )
207+ methods [x ] = attr
208+ return _wrap (wrapped , methods , mod , name , extra , cls )
209+
210+ def wrapObj (wrapped , methods , name , extra ):
211+ class BaseWrapper (WrapperBase , wrapped .__class__ ):
212+ def __init__ (self , wrapped ):
213+ self .__dict__ = wrapped .__dict__
214+ self .__wrapped__ = wrapped
215+ if name is None :
216+ name = 'ObjectWrapper'
217+ if hasattr (wrapped , '__module__' ):
218+ mod = getattr (wrapped , '__module__' )
219+ else :
220+ mod = None
221+ return _wrap (wrapped , methods , mod , name , extra , BaseWrapper )
222+
223+ def wrapBuiltin (wrapped , methods , name , extra , cls ):
224+ if name is None :
225+ name = cls .__name__
226+ return _wrap (wrapped , methods , None , name , extra , cls )
227+
267228def wrap (obj , methods , name = None , extra = None , simple = False ):
268229 if extra is None :
269230 extra = {}
231+ wrapper = None
270232 if simple :
271- w = SimpleWrapper (obj )
233+ w = wrapSimple (obj , methods , name , extra )
234+ wrapper = 'SimpleWrapper'
272235 elif isinstance (obj , list ):
273- w = ListWrapper (obj )
236+ w = wrapBuiltin (obj , methods , name , extra , ListWrapper )
237+ wrapper = 'ListWrapper'
274238 elif isinstance (obj , tuple ):
275- w = TupleWrapper (obj )
239+ w = wrapBuiltin (obj , methods , name , extra , TupleWrapper )
240+ wrapper = 'TupleWrapper'
276241 elif isinstance (obj , dict ):
277- w = DictWrapper (obj )
242+ w = wrapBuiltin (obj , methods , name , extra , DictWrapper )
243+ wrapper = 'DictWrapper'
278244 elif isinstance (obj , str ):
279- w = StringWrapper (obj )
245+ w = wrapBuiltin (obj , methods , name , extra , StringWrapper )
246+ wrapper = 'StringWrapper'
280247 elif isinstance (obj , set ):
281- w = SetWrapper (obj )
248+ w = wrapBuiltin (obj , methods , name , extra , SetWrapper )
249+ wrapper = 'SetWrapper'
282250 elif isinstance (obj , collections .abc .ValuesView ):
283- w = ValuesViewWrapper (obj )
251+ w = wrapSimple (obj , methods , name , extra , ValuesViewWrapper )
252+ wrapper = 'ValuesViewWrapper'
284253 elif isinstance (obj , collections .abc .KeysView ):
285- w = KeysViewWrapper (obj )
254+ w = wrapSimple (obj , methods , name , extra , KeysViewWrapper )
255+ wrapper = 'KeysViewWrapper'
286256 elif isinstance (obj , collections .abc .ItemsView ):
287- w = ItemsViewWrapper (obj )
257+ w = wrapSimple (obj , methods , name , extra , ItemsViewWrapper )
258+ wrapper = 'ItemsViewWrapper'
288259 elif isinstance (obj , typing .Generic ):
289- w = SimpleWrapper (obj )
260+ w = wrapSimple (obj , methods , name , extra )
261+ wrapper = 'SimpleWrapper'
290262 elif isinstance (obj , generatorType ):
291- w = SimpleWrapper (obj )
292- elif isinstance (obj , abc .ABC ) and hasattr (obj , '__dict__' ):
293- try :
294- w = ABCObjectWrapper (obj )
295- except WyppWrapError :
296- try :
297- w = ABCObjectWrapperRev (obj )
298- except WyppWrapError :
299- w = SimpleWrapper (obj )
263+ w = wrapSimple (obj , methods , name , extra )
264+ wrapper = 'SimpleWrapper'
300265 elif hasattr (obj , '__dict__' ):
301- w = ObjectWrapper (obj )
266+ w = wrapObj (obj , methods , name , extra )
267+ wrapper = 'ObjectWrapper'
302268 else :
303- w = SimpleWrapper (obj )
304- w . __patch__ ( methods , name , extra )
269+ w = wrapSimple (obj , methods , name , extra )
270+ wrapper = 'SimpleWrapper'
305271 wname = name
306272 if wname is None :
307273 wname = str (type (w ))
308- debug (f"Wrapping { obj } at 0x{ id (obj ):09x} as { wname } , simple={ simple } , wrapper=0x{ id (w ):09x} " )
274+ debug (f"Wrapping { obj } at 0x{ id (obj ):09x} as { wname } , simple={ simple } , wrapper=0x{ id (w ):09x} ( { wrapper } ) " )
309275 return w
0 commit comments