11import typing
2- import abc
32import collections
4- from untypy .error import UntypyError
53from untypy .util .debug import debug
6- import types
74
85def _f ():
96 yield 0
@@ -19,14 +16,6 @@ def __ne__(self, other):
1916 return not self .__eq__ (other )
2017 def __hash__ (self ):
2118 return hash (self .__wrapped__ )
22- def __patch__ (self , ms , name = None , extra = None ):
23- if extra is None :
24- extra = {}
25- cls = self .__class__
26- if name is None :
27- name = cls .__name__
28- ty = type (name , (cls ,), ms )
29- patch (self , ty , extra )
3019 def __repr__ (self ):
3120 return repr (self .__wrapped__ )
3221 def __str__ (self ):
@@ -171,40 +160,6 @@ def __new__(cls, content):
171160 self .__wrapped__ = content
172161 return self
173162
174- class WyppWrapError (Exception ):
175- pass
176-
177- def _readonly (self , * args , ** kwargs ):
178- raise RuntimeError ("Cannot modify ReadOnlyDict" )
179-
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- # FIXME: get rid off patch. Its evil to change the class of an object after its creation.
190- # Further, it causes issues with the GC of python 3.13.
191-
192- def patch (self , ty , extra ):
193- # SW (2024-10-18): With python 3.13 there is the behavior that extra is modified after patching
194- # the object. I never found out who is doing the modification. By wrapping extra with
195- # ReadOnlyDict, everything works. Strangely, no error occurs somewhere.
196- self .__extra__ = ReadOnlyDict (extra )
197- w = self .__wrapped__
198- m = None
199- if hasattr (w , '__module__' ):
200- m = getattr (w , '__module__' )
201- ty .__module__ = m
202- try :
203- self .__class__ = ty
204- except TypeError as e :
205- raise WyppWrapError (f'Cannot wrap { self .__wrapped__ } of type { type (self .__wrapped__ )} ' \
206- f'at type { ty } . Original error: { e } ' )
207-
208163# SimpleWrapper is a fallback for types that cannot be used as base types
209164class SimpleWrapper (WrapperBase ):
210165 def __init__ (self ):
@@ -252,8 +207,6 @@ class BaseWrapper(WrapperBase, wrapped.__class__):
252207 def __init__ (self ):
253208 self .__dict__ = wrapped .__dict__
254209 self .__wrapped__ = wrapped
255- def __patch__ (self , ms , name = None , extra = None ):
256- pass
257210 if name is None :
258211 name = 'ObjectWrapper'
259212 if extra is None :
@@ -271,6 +224,23 @@ def __patch__(self, ms, name=None, extra=None):
271224 w .__extra__ = extra
272225 return w
273226
227+ def wrapBuiltin (wrapped , methods , name , extra , cls ):
228+ if name is None :
229+ name = cls .__name__
230+ if extra is None :
231+ extra = {}
232+ # Dynamically create a new class:
233+ # type(class_name, base_classes, class_dict)
234+ WrapperClass = type (
235+ name ,
236+ (cls ,),
237+ methods
238+ )
239+ WrapperClass .__module__ = None
240+ w = WrapperClass (wrapped )
241+ w .__extra__ = extra
242+ return w
243+
274244def wrap (obj , methods , name = None , extra = None , simple = False ):
275245 if extra is None :
276246 extra = {}
@@ -279,19 +249,19 @@ def wrap(obj, methods, name=None, extra=None, simple=False):
279249 w = wrapSimple (obj , methods , name , extra )
280250 wrapper = 'SimpleWrapper'
281251 elif isinstance (obj , list ):
282- w = ListWrapper (obj )
252+ w = wrapBuiltin (obj , methods , name , extra , ListWrapper )
283253 wrapper = 'ListWrapper'
284254 elif isinstance (obj , tuple ):
285- w = TupleWrapper (obj )
255+ w = wrapBuiltin (obj , methods , name , extra , TupleWrapper )
286256 wrapper = 'TupleWrapper'
287257 elif isinstance (obj , dict ):
288- w = DictWrapper (obj )
258+ w = wrapBuiltin (obj , methods , name , extra , DictWrapper )
289259 wrapper = 'DictWrapper'
290260 elif isinstance (obj , str ):
291- w = StringWrapper (obj )
261+ w = wrapBuiltin (obj , methods , name , extra , StringWrapper )
292262 wrapper = 'StringWrapper'
293263 elif isinstance (obj , set ):
294- w = SetWrapper (obj )
264+ w = wrapBuiltin (obj , methods , name , extra , SetWrapper )
295265 wrapper = 'SetWrapper'
296266 elif isinstance (obj , collections .abc .ValuesView ):
297267 w = wrapSimple (obj , methods , name , extra , ValuesViewWrapper )
@@ -314,7 +284,6 @@ def wrap(obj, methods, name=None, extra=None, simple=False):
314284 else :
315285 w = wrapSimple (obj , methods , name , extra )
316286 wrapper = 'SimpleWrapper'
317- w .__patch__ (methods , name , extra )
318287 wname = name
319288 if wname is None :
320289 wname = str (type (w ))
0 commit comments