Skip to content

Commit e30d405

Browse files
committed
get rid off patching completely
1 parent d25b63f commit e30d405

2 files changed

Lines changed: 29 additions & 59 deletions

File tree

python/deps/untypy/test/util_test/test_wrapper.py

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -77,12 +77,13 @@ def _test_api_complete(self, obj, ignore=[]):
7777
'__weakref__', '__wrapped__', '_DictWrapper__marker', '__setstate__',
7878
'__getstate__', '__firstlineno__', '__static_attributes__'
7979
] + ignore
80-
for x in dir(wrapped):
81-
if x in blacklist: continue
82-
m = getattr(wrapped, x)
83-
if not hasattr(m, '__module__'):
84-
self.fail(f'Attribute {x} not defined')
85-
elif m.__module__ != expectedModule:
80+
for x in dir(obj):
81+
if x in blacklist:
82+
continue
83+
a = getattr(wrapped, x)
84+
if not hasattr(a, '__module__'):
85+
self.fail(f'Attribute {x} not defined. obj={obj}, a={a}, wrapped={wrapped}')
86+
elif a.__module__ != expectedModule:
8687
self.fail(f'Attrribute {x} not defined in {expectedModule}')
8788

8889
def test_list_api_complete(self):

python/deps/untypy/untypy/util/wrapper.py

Lines changed: 22 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,6 @@
11
import typing
2-
import abc
32
import collections
4-
from untypy.error import UntypyError
53
from untypy.util.debug import debug
6-
import types
74

85
def _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
209164
class 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+
274244
def 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

Comments
 (0)