33import collections
44from untypy .error import UntypyError
55from untypy .util .debug import debug
6+ import types
67
78def _f ():
89 yield 0
910generatorType = type (_f ())
1011
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-
4212class WrapperBase :
4313 def __eq__ (self , other ):
4414 if hasattr (other , '__wrapped__' ):
@@ -58,8 +28,6 @@ def __patch__(self, ms, name=None, extra=None):
5828 ty = type (name , (cls ,), ms )
5929 patch (self , ty , extra )
6030 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)}"
6331 return repr (self .__wrapped__ )
6432 def __str__ (self ):
6533 return str (self .__wrapped__ )
@@ -72,27 +40,6 @@ def __reduce__(self): return self.__wrapped__.__reduce__()
7240 def __reduce_ex__ (self ): return self .__wrapped__ .__reduce_ex__ ()
7341 def __sizeof__ (self ): return self .__wrapped__ .__sizeof__ ()
7442
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-
9643# A wrapper for list such that the class is a subclass of the builtin list class.
9744class ListWrapper (WrapperBase , list ): # important: inherit from WrapperBase first
9845 def __new__ (cls , content ):
@@ -224,33 +171,43 @@ def __new__(cls, content):
224171 self .__wrapped__ = content
225172 return self
226173
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__' ]
174+ class WyppWrapError (Exception ):
175+ pass
176+
177+ def _readonly (self , * args , ** kwargs ):
178+ raise RuntimeError ("Cannot modify ReadOnlyDict" )
235179
236- _extra = ['__next__' ]
180+ class ReadOnlyDict (dict ):
181+ __setitem__ = _readonly
182+ __delitem__ = _readonly
183+ pop = _readonly
184+ popitem = _readonly
185+ clear = _readonly
186+ update = _readonly
187+ setdefault = _readonly
188+
189+ def patch (self , ty , extra ):
190+ # SW (2024-10-18): With python 3.13 there is the behavior that extra is modified after patching
191+ # the object. I never found out who is doing the modification. By wrapping extra with
192+ # ReadOnlyDict, everything works. Strangely, no error occurs somewhere.
193+ self .__extra__ = ReadOnlyDict (extra )
194+ w = self .__wrapped__
195+ m = None
196+ if hasattr (w , '__module__' ):
197+ m = getattr (w , '__module__' )
198+ ty .__module__ = m
199+ try :
200+ self .__class__ = ty
201+ except TypeError as e :
202+ raise WyppWrapError (f'Cannot wrap { self .__wrapped__ } of type { type (self .__wrapped__ )} ' \
203+ f'at type { ty } . Original error: { e } ' )
237204
238205# SimpleWrapper is a fallback for types that cannot be used as base types
239206class SimpleWrapper (WrapperBase ):
240- def __init__ (self , baseObject ):
241- self . __wrapped__ = baseObject
207+ def __init__ (self ):
208+ pass
242209 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 )
210+ pass
254211
255212class ValuesViewWrapper (SimpleWrapper ):
256213 pass
@@ -264,46 +221,99 @@ class KeysViewWrapper(SimpleWrapper):
264221 pass
265222collections .abc .KeysView .register (KeysViewWrapper )
266223
224+ def wrapSimple (wrapped , methods , name , extra , cls = SimpleWrapper ):
225+ if name is None :
226+ name = cls .__name__
227+ if extra is None :
228+ extra = {}
229+ for x in ['__next__' , '__iter__' ]:
230+ if x not in methods and hasattr (wrapped , x ):
231+ attr = getattr (wrapped , x )
232+ methods [x ] = attr
233+ # Dynamically create a new class:
234+ # type(class_name, base_classes, class_dict)
235+ WrapperClass = type (
236+ name ,
237+ (cls ,),
238+ methods
239+ )
240+ if not name .startswith ('WyppTypeCheck()' ) and hasattr (wrapped , '__module__' ):
241+ WrapperClass .__module__ = getattr (wrapped , '__module__' )
242+ w = WrapperClass ()
243+ w .__wrapped__ = wrapped
244+ w .__extra__ = extra
245+ return w
246+
247+ def wrapObj (wrapped , methods , name , extra ):
248+ class BaseWrapper (WrapperBase , wrapped .__class__ ):
249+ def __init__ (self ):
250+ self .__dict__ = wrapped .__dict__
251+ self .__wrapped__ = wrapped
252+ def __patch__ (self , ms , name = None , extra = None ):
253+ pass
254+ if name is None :
255+ name = 'ObjectWrapper'
256+ if extra is None :
257+ extra = {}
258+ # Dynamically create a new class:
259+ # type(class_name, base_classes, class_dict)
260+ WrapperClass = type (
261+ name ,
262+ (BaseWrapper ,),
263+ methods
264+ )
265+ if hasattr (wrapped , '__module__' ):
266+ WrapperClass .__module__ = getattr (wrapped , '__module__' )
267+ w = WrapperClass ()
268+ w .__extra__ = extra
269+ return w
270+
267271def wrap (obj , methods , name = None , extra = None , simple = False ):
268272 if extra is None :
269273 extra = {}
274+ wrapper = None
270275 if simple :
271- w = SimpleWrapper (obj )
276+ w = wrapSimple (obj , methods , name , extra )
277+ wrapper = 'SimpleWrapper'
272278 elif isinstance (obj , list ):
273279 w = ListWrapper (obj )
280+ wrapper = 'ListWrapper'
274281 elif isinstance (obj , tuple ):
275282 w = TupleWrapper (obj )
283+ wrapper = 'TupleWrapper'
276284 elif isinstance (obj , dict ):
277285 w = DictWrapper (obj )
286+ wrapper = 'DictWrapper'
278287 elif isinstance (obj , str ):
279288 w = StringWrapper (obj )
289+ wrapper = 'StringWrapper'
280290 elif isinstance (obj , set ):
281291 w = SetWrapper (obj )
292+ wrapper = 'SetWrapper'
282293 elif isinstance (obj , collections .abc .ValuesView ):
283- w = ValuesViewWrapper (obj )
294+ w = wrapSimple (obj , methods , name , extra , ValuesViewWrapper )
295+ wrapper = 'ValuesViewWrapper'
284296 elif isinstance (obj , collections .abc .KeysView ):
285- w = KeysViewWrapper (obj )
297+ w = wrapSimple (obj , methods , name , extra , KeysViewWrapper )
298+ wrapper = 'KeysViewWrapper'
286299 elif isinstance (obj , collections .abc .ItemsView ):
287- w = ItemsViewWrapper (obj )
300+ w = wrapSimple (obj , methods , name , extra , ItemsViewWrapper )
301+ wrapper = 'ItemsViewWrapper'
288302 elif isinstance (obj , typing .Generic ):
289- w = SimpleWrapper (obj )
303+ w = wrapSimple (obj , methods , name , extra )
304+ wrapper = 'SimpleWrapper'
290305 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 )
306+ w = wrapSimple (obj , methods , name , extra )
307+ wrapper = 'SimpleWrapper'
300308 elif hasattr (obj , '__dict__' ):
301- w = ObjectWrapper (obj )
309+ w = wrapObj (obj , methods , name , extra )
310+ wrapper = 'ObjectWrapper'
302311 else :
303- w = SimpleWrapper (obj )
312+ w = wrapSimple (obj , methods , name , extra )
313+ wrapper = 'SimpleWrapper'
304314 w .__patch__ (methods , name , extra )
305315 wname = name
306316 if wname is None :
307317 wname = str (type (w ))
308- debug (f"Wrapping { obj } at 0x{ id (obj ):09x} as { wname } , simple={ simple } , wrapper=0x{ id (w ):09x} " )
318+ debug (f"Wrapping { obj } at 0x{ id (obj ):09x} as { wname } , simple={ simple } , wrapper=0x{ id (w ):09x} ( { wrapper } ) " )
309319 return w
0 commit comments