retracesoftware-proxy 0.1.5__py3-none-any.whl → 0.2.4__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (35) hide show
  1. retracesoftware/__main__.py +285 -0
  2. retracesoftware/autoenable.py +53 -0
  3. retracesoftware/config.json +19 -233
  4. retracesoftware/config.yaml +0 -0
  5. retracesoftware/install/config.py +6 -0
  6. retracesoftware/install/edgecases.py +23 -1
  7. retracesoftware/install/patcher.py +98 -513
  8. retracesoftware/install/patchfindspec.py +117 -0
  9. retracesoftware/install/phases.py +338 -0
  10. retracesoftware/install/record.py +111 -40
  11. retracesoftware/install/replace.py +28 -0
  12. retracesoftware/install/replay.py +59 -11
  13. retracesoftware/install/tracer.py +171 -33
  14. retracesoftware/install/typeutils.py +20 -0
  15. retracesoftware/modules.toml +384 -0
  16. retracesoftware/preload.txt +216 -0
  17. retracesoftware/proxy/__init__.py +1 -1
  18. retracesoftware/proxy/globalref.py +31 -0
  19. retracesoftware/proxy/messagestream.py +204 -0
  20. retracesoftware/proxy/proxysystem.py +328 -71
  21. retracesoftware/proxy/proxytype.py +90 -38
  22. retracesoftware/proxy/record.py +109 -119
  23. retracesoftware/proxy/replay.py +94 -188
  24. retracesoftware/proxy/serializer.py +28 -0
  25. retracesoftware/proxy/startthread.py +40 -0
  26. retracesoftware/proxy/stubfactory.py +82 -27
  27. retracesoftware/proxy/thread.py +64 -4
  28. retracesoftware/replay.py +104 -0
  29. retracesoftware/run.py +378 -0
  30. retracesoftware/stackdifference.py +133 -0
  31. {retracesoftware_proxy-0.1.5.dist-info → retracesoftware_proxy-0.2.4.dist-info}/METADATA +2 -1
  32. retracesoftware_proxy-0.2.4.dist-info/RECORD +42 -0
  33. retracesoftware_proxy-0.1.5.dist-info/RECORD +0 -27
  34. {retracesoftware_proxy-0.1.5.dist-info → retracesoftware_proxy-0.2.4.dist-info}/WHEEL +0 -0
  35. {retracesoftware_proxy-0.1.5.dist-info → retracesoftware_proxy-0.2.4.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,204 @@
1
+ from ast import dump
2
+ from math import e
3
+ import retracesoftware.stream as stream
4
+ import retracesoftware_utils as utils
5
+ from retracesoftware.proxy.startthread import thread_id
6
+ import gc
7
+ import sys
8
+ import itertools
9
+ import threading
10
+
11
+ class ResultMessage:
12
+ def __init__(self, result):
13
+ self.result = result
14
+
15
+ def __str__(self):
16
+ return f'ResultMessage({self.result})'
17
+
18
+ def __repr__(self):
19
+ return f'ResultMessage({self.result})'
20
+
21
+ class ErrorMessage:
22
+ def __init__(self, type, value):
23
+ self.type = type
24
+ self.value = value
25
+
26
+ def __call__(self):
27
+ utils.raise_exception(self.type, self.value)
28
+
29
+ class CallMessage:
30
+ def __init__(self, func, args, kwargs):
31
+ self.func = func
32
+ self.args = args
33
+ self.kwargs = kwargs
34
+
35
+ def __call__(self):
36
+ try:
37
+ self.func(*self.args, **self.kwargs)
38
+ except BaseException:
39
+ pass
40
+ return None
41
+
42
+ class CheckpointMessage:
43
+ def __init__(self, value):
44
+ self.value = value
45
+
46
+ class GCStartMessage:
47
+ def __init__(self, value):
48
+ self.value = value
49
+
50
+ def __call__(self):
51
+ gc.collect(self.value)
52
+
53
+ def next_message(source):
54
+ message = source()
55
+
56
+ if message == 'CALL':
57
+ return CallMessage(source(), source(), source())
58
+ elif message == 'RESULT':
59
+ return ResultMessage(source())
60
+ elif message == 'ERROR':
61
+ return ErrorMessage(source(), source())
62
+ elif message == 'CHECKPOINT':
63
+ return CheckpointMessage(source())
64
+ elif message == 'ON_START_COLLECT':
65
+ return GCStartMessage(source())
66
+ else:
67
+ return message
68
+
69
+ def all_elements_same(t):
70
+ return len(set(t)) <= 1
71
+
72
+ def first(coll): return coll[0]
73
+
74
+ def common_prefix(*colls):
75
+ return list(map(first, itertools.takewhile(all_elements_same, zip(*colls))))
76
+
77
+ def print_stack(stack):
78
+ for filename, lineno in stack:
79
+ print(f'{filename}:{lineno}', file = sys.stderr)
80
+
81
+ def on_stack_difference(previous, record, replay):
82
+
83
+ common = common_prefix(previous, record, replay) if previous else common_prefix(record, replay)
84
+
85
+ if common:
86
+ print('Common root:')
87
+ print_stack(common)
88
+
89
+ if previous:
90
+ print('\nlast matching:')
91
+ print_stack(previous[len(common):])
92
+
93
+ print('\nrecord:')
94
+ print_stack(record[len(common):])
95
+
96
+ print('\nreplay:')
97
+ print_stack(replay[len(common):])
98
+
99
+ utils.sigtrap(None)
100
+
101
+ class MessageStream:
102
+
103
+ def __init__(self, thread_state, source, skip_weakref_callbacks, verbose):
104
+ self.source = stream.per_thread(source = source, thread = thread_id, timeout = 1000)
105
+ self.verbose = verbose
106
+
107
+ self.excludes = set([
108
+ MessageStream.__call__,
109
+ MessageStream.bind,
110
+ MessageStream.checkpoint,
111
+ MessageStream.on_stack,
112
+ MessageStream.read_required,
113
+ MessageStream.result.__wrapped__,
114
+ stream.stack,
115
+ ])
116
+
117
+ self.threadlocal = threading.local()
118
+ self.thread_state = thread_state
119
+ self.stack_messages = set([])
120
+ self.skip_weakref_callbacks = skip_weakref_callbacks
121
+
122
+ def on_stack(self, record):
123
+ replay = stream.stack(self.excludes)
124
+
125
+ if replay != record:
126
+ on_stack_difference(self.threadlocal.stacktrace, record, replay)
127
+
128
+ self.threadlocal.stacktrace = record
129
+
130
+ def dump_and_exit(self):
131
+ with self.thread_state.select('disabled'):
132
+ if hasattr(self.threadlocal, 'stacktrace'):
133
+ print('Last matching stacktrace:')
134
+ print_stack(self.threadlocal.stacktrace)
135
+
136
+ print('Current stacktrace:')
137
+ print_stack(stream.stack(set()))
138
+ utils.sigtrap(None)
139
+
140
+ def checkpoint(self, obj):
141
+ if self.verbose:
142
+ print(f'checkpointing: {obj}')
143
+
144
+ next = self()
145
+
146
+ if isinstance(next, CheckpointMessage):
147
+ if obj != next.value:
148
+ print(f'CHECKPOINT, expected: {obj} but got {next.value}', file=sys.stderr)
149
+ self.dump_and_exit()
150
+ else:
151
+ print(f'expected CHECKPOINT message but got {next}', file=sys.stderr)
152
+ self.dump_and_exit()
153
+
154
+ def bind(self, obj):
155
+
156
+ next = self()
157
+
158
+ if isinstance(next, stream.Bind):
159
+ next.value(obj)
160
+ else:
161
+ print(f'expected BIND message but got {next}', file=sys.stderr)
162
+ self.dump_and_exit()
163
+
164
+ def read_required(self, required):
165
+ next = self()
166
+
167
+ if next != required:
168
+ print(f'expected {required} message but got {next}', file=sys.stderr)
169
+ self.dump_and_exit()
170
+
171
+ def consume_to(self, to):
172
+ message = next_message(self.source)
173
+ while message != to:
174
+ message = next_message(self.source)
175
+
176
+ def __call__(self):
177
+ with self.thread_state.select('disabled'):
178
+ while True:
179
+ message = next_message(self.source)
180
+
181
+ if isinstance(message, CallMessage):
182
+ message()
183
+ elif isinstance(message, stream.Stack):
184
+ self.on_stack(message.value)
185
+ elif isinstance(message, GCStartMessage):
186
+ message()
187
+ self.read_required('ON_END_COLLECT')
188
+ elif self.skip_weakref_callbacks and message == 'ON_WEAKREF_CALLBACK_START':
189
+ self.consume_to('ON_WEAKREF_CALLBACK_END')
190
+ else:
191
+ return message
192
+
193
+ @utils.striptraceback
194
+ def result(self):
195
+
196
+ message = self()
197
+
198
+ if isinstance(message, ResultMessage):
199
+ return message.result
200
+ elif isinstance(message, ErrorMessage):
201
+ message()
202
+ else:
203
+ print(f'expected RESULT or ERROR message but got {message}', file=sys.stderr)
204
+ self.dump_and_exit()
@@ -5,8 +5,16 @@ from retracesoftware.proxy.gateway import adapter_pair
5
5
  from types import SimpleNamespace
6
6
  from retracesoftware.proxy.proxytype import *
7
7
  from retracesoftware.proxy.stubfactory import Stub
8
+ from retracesoftware.install.typeutils import modify, WithFlags, WithoutFlags
9
+
10
+ from retracesoftware.proxy.serializer import serializer
11
+ from retracesoftware.install.tracer import Tracer
8
12
  import sys
9
13
  import gc
14
+ import weakref
15
+ import enum
16
+ import functools
17
+ import re
10
18
 
11
19
  class RetraceError(Exception):
12
20
  pass
@@ -23,6 +31,12 @@ def maybe_proxy(proxytype):
23
31
  utils.unwrap,
24
32
  proxy(functional.memoize_one_arg(proxytype)))
25
33
 
34
+ class Patched:
35
+ __slots__ = ()
36
+
37
+ class RetraceBase:
38
+ pass
39
+
26
40
  unproxy_execute = functional.mapargs(starting = 1,
27
41
  transform = functional.walker(utils.try_unwrap),
28
42
  function = functional.apply)
@@ -36,13 +50,86 @@ def resolve(obj):
36
50
  def is_function_type(cls):
37
51
  return cls in [types.BuiltinFunctionType, types.FunctionType]
38
52
 
53
+ method_types = (types.MethodDescriptorType,
54
+ types.WrapperDescriptorType,
55
+ types.FunctionType)
56
+
57
+ def is_instance_method(obj):
58
+ return isinstance(obj, method_types)
59
+
60
+ def get_all_subtypes(cls):
61
+ """Recursively find all subtypes of a given class."""
62
+ subclasses = set(cls.__subclasses__())
63
+ for subclass in cls.__subclasses__():
64
+ subclasses.update(get_all_subtypes(subclass))
65
+ return subclasses
66
+
67
+ def cleanse(text):
68
+ pattern = r'0x[a-fA-F0-9]+'
69
+ return re.sub(pattern, '0x####', text)
70
+
71
+ excludes = [
72
+ "ABCMeta.__instancecheck__",
73
+ "ABCMeta.__subclasscheck__",
74
+ "Collection.__subclasshook__",
75
+ re.compile(r"WeakSet"),
76
+ ]
77
+
78
+ def exclude(text):
79
+ for elem in excludes:
80
+ if isinstance(elem, str):
81
+ if elem == text: return True
82
+ else:
83
+ # breakpoint()
84
+ # if text.contains('WeakSet'):
85
+
86
+ if re.match(elem, text):
87
+ return True
88
+ return False
89
+
90
+ def normalize_for_checkpoint(obj):
91
+ if isinstance(obj, types.FunctionType):
92
+ return obj.__qualname__
93
+ elif isinstance(obj, types.MethodType):
94
+ return obj.__qualname__
95
+ elif isinstance(obj, types.BuiltinFunctionType):
96
+ return obj.__name__
97
+ elif isinstance(obj, types.BuiltinMethodType):
98
+ return obj.__name__
99
+ elif isinstance(obj, str):
100
+ return obj
101
+ elif isinstance(obj, dict):
102
+ return {normalize_for_checkpoint(k): normalize_for_checkpoint(v) for k,v in obj.items()}
103
+ elif isinstance(obj, tuple):
104
+ return tuple(normalize_for_checkpoint(x) for x in obj)
105
+ elif isinstance(obj, list):
106
+ return [normalize_for_checkpoint(x) for x in obj]
107
+ elif isinstance(obj, int):
108
+ # try and filter out memory addresses
109
+ if obj > 1000000 or obj < -1000000:
110
+ return "XXXX"
111
+ else:
112
+ return int(obj)
113
+ elif isinstance(obj, float):
114
+ return obj
115
+ elif isinstance(obj, bool):
116
+ return obj
117
+ elif isinstance(obj, type):
118
+ return obj.__name__
119
+ elif isinstance(obj, enum.Enum):
120
+ return obj.name
121
+ elif isinstance(obj, enum.EnumMeta):
122
+ return obj.__name__
123
+ elif isinstance(obj, enum.EnumMember):
124
+ return obj.name
125
+ else:
126
+ return f"<object of type: {type(obj)}>"
127
+
39
128
  class ProxySystem:
40
129
 
41
- def bind(self, obj): pass
130
+ # def bind(self, obj): pass
42
131
 
43
- def wrap_int_to_ext(self, obj):
44
- return obj
45
- # return functional.sequence(functional.side_effect(functional.repeatedly(gc.collect)), obj)
132
+ def wrap_int_to_ext(self, obj): return obj
46
133
 
47
134
  def wrap_ext_to_int(self, obj): return obj
48
135
 
@@ -54,35 +141,68 @@ class ProxySystem:
54
141
 
55
142
  def on_ext_error(self, err_type, err_value, err_traceback):
56
143
  pass
144
+
145
+ def on_ext_call(self, func, *args, **kwargs):
146
+ pass
147
+
148
+ # def stacktrace(self):
149
+ # self.tracer.stacktrace()
150
+
151
+ def set_thread_id(self, id):
152
+ utils.set_thread_id(id)
153
+
154
+ @property
155
+ def ext_apply(self): return functional.apply
57
156
 
58
- def __init__(self, thread_state, immutable_types, tracer):
157
+ @property
158
+ def int_apply(self): return functional.apply
159
+
160
+ def wrap_weakref_callback(self, callback):
161
+ def when_internal(f):
162
+ return self.thread_state.dispatch(utils.noop, internal = f)
163
+
164
+ return utils.observer(
165
+ on_call = when_internal(self.on_weakref_callback_start),
166
+ on_result = when_internal(self.on_weakref_callback_end),
167
+ on_error = when_internal(self.on_weakref_callback_end),
168
+ function = callback)
169
+
170
+ def __init__(self, thread_state, immutable_types, tracer, traceargs):
59
171
 
172
+ self.patched_types = set()
60
173
  self.thread_state = thread_state
61
174
  self.fork_counter = 0
62
175
  self.tracer = tracer
63
176
  self.immutable_types = immutable_types
64
- self.on_proxytype = None
65
-
66
- def is_immutable_type(cls):
67
- return issubclass(cls, tuple(immutable_types))
177
+ self.base_to_patched = {}
178
+
179
+ def should_proxy_type(cls):
180
+ return cls is not object and \
181
+ not issubclass(cls, tuple(immutable_types)) and \
182
+ cls not in self.patched_types
68
183
 
69
- is_immutable = functional.sequence(functional.typeof, functional.memoize_one_arg(is_immutable_type))
184
+ should_proxy = functional.sequence(functional.typeof, functional.memoize_one_arg(should_proxy_type))
70
185
 
71
186
  def proxyfactory(proxytype):
72
- return functional.walker(functional.when_not(is_immutable, maybe_proxy(proxytype)))
187
+ return functional.walker(functional.when(should_proxy, maybe_proxy(proxytype)))
73
188
 
74
189
  int_spec = SimpleNamespace(
75
- apply = thread_state.wrap('internal', functional.apply),
190
+ apply = thread_state.wrap('internal', self.int_apply),
76
191
  proxy = proxyfactory(thread_state.wrap('disabled', self.int_proxytype)),
77
192
  on_call = tracer('proxy.int.call', self.on_int_call),
78
193
  on_result = tracer('proxy.int.result'),
79
194
  on_error = tracer('proxy.int.error'),
80
195
  )
196
+
197
+ def trace_ext_call(func, *args, **kwargs):
198
+ self.on_ext_call(func, *args, **kwargs)
199
+ self.checkpoint(self.normalize_for_checkpoint({'function': func, 'args': args, 'kwargs': kwargs}))
81
200
 
82
201
  ext_spec = SimpleNamespace(
83
- apply = thread_state.wrap('external', functional.apply),
84
- proxy = proxyfactory(thread_state.wrap('disabled', self.dynamic_ext_proxytype)),
85
- on_call = tracer('proxy.ext.call'),
202
+ apply = thread_state.wrap('external', self.ext_apply),
203
+ proxy = proxyfactory(thread_state.wrap('disabled', self.ext_proxytype)),
204
+
205
+ on_call = trace_ext_call if traceargs else self.on_ext_call,
86
206
  on_result = self.on_ext_result,
87
207
  on_error = self.on_ext_error,
88
208
  )
@@ -92,13 +212,26 @@ class ProxySystem:
92
212
  def gateway(name, internal = functional.apply, external = functional.apply):
93
213
  default = tracer(name, unproxy_execute)
94
214
  return thread_state.dispatch(default, internal = internal, external = external)
95
-
96
- self.ext_handler = self.wrap_int_to_ext(int2ext)
97
- self.int_handler = self.wrap_ext_to_int(ext2int)
215
+
216
+ self.ext_handler = thread_state.wrap('retrace', self.wrap_int_to_ext(int2ext))
217
+ self.int_handler = thread_state.wrap('retrace', self.wrap_ext_to_int(ext2int))
98
218
 
99
219
  self.ext_dispatch = gateway('proxy.int.disabled.event', internal = self.ext_handler)
100
220
  self.int_dispatch = gateway('proxy.ext.disabled.event', external = self.int_handler)
101
221
 
222
+ self.exclude_from_stacktrace(Tracer._write_call)
223
+
224
+ # if 'systrace' in tracer.config:
225
+ # func = thread_state.wrap(desired_state = 'disabled', function = tracer.systrace)
226
+ # func = self.thread_state.dispatch(lambda *args: None, internal = func)
227
+ # sys.settrace(func)
228
+ # self.on_new_patched = self.thread_state.dispatch(utils.noop,
229
+ # internal = self.on_new_ext_patched, external = self.on_new_int_patched)
230
+ # tracer.trace_calls(thread_state)
231
+
232
+ def disable_for(self, func):
233
+ return self.thread_state.wrap('disabled', func)
234
+
102
235
  def new_child_path(self, path):
103
236
  return path.parent / f'fork-{self.fork_counter}' / path.name
104
237
 
@@ -114,84 +247,208 @@ class ProxySystem:
114
247
  self.thread_state.value = self.saved_thread_state
115
248
  self.fork_counter += 1
116
249
 
117
- def create_stub(self): return False
250
+ def on_thread_exit(self, thread_id):
251
+ pass
252
+
253
+ # def create_stub(self): return False
118
254
 
119
255
  def int_proxytype(self, cls):
256
+ if cls is object:
257
+ breakpoint()
258
+
120
259
  return dynamic_int_proxytype(
121
260
  handler = self.int_dispatch,
122
261
  cls = cls,
123
262
  bind = self.bind)
263
+
264
+ def ext_proxytype(self, cls):
124
265
 
125
- def dynamic_ext_proxytype(self, cls):
266
+ proxytype = dynamic_proxytype(handler = self.ext_dispatch, cls = cls)
267
+ proxytype.__retrace_source__ = 'external'
268
+
269
+ if issubclass(cls, Patched):
270
+ patched = cls
271
+ elif cls in self.base_to_patched:
272
+ patched = self.base_to_patched[cls]
273
+ else:
274
+ patched = None
275
+
276
+ assert patched == None or patched.__base__ is not object
277
+
278
+ if patched:
279
+ # breakpoint()
280
+
281
+ patcher = getattr(patched, '__retrace_patch_proxy__', None)
282
+ if patcher: patcher(proxytype)
283
+
284
+ # for key,value in patched.__dict__.items():
285
+ # if callable(value) and key not in ['__new__'] and not hasattr(proxytype, key):
286
+ # setattr(proxytype, key, value)
126
287
 
127
- proxytype = dynamic_proxytype(
128
- handler = self.ext_dispatch,
129
- cls = cls)
130
- if self.on_proxytype:
131
- self.on_proxytype(proxytype)
132
-
133
288
  return proxytype
134
-
135
- # resolved = resolve(cls)
136
- # if isinstance(resolved, ExtendingProxy):
137
- # return dynamic_from_extended(resolved)
138
- # elif isinstance(resolved, DynamicProxy):
139
- # return resolved
140
- # else:
141
- # return dynamic_proxytype(handler = self.ext_dispatch, cls = cls)
289
+
290
+ def function_target(self, obj): return obj
291
+
292
+ def proxy_function(self, func, **kwargs):
293
+ if is_instance_method(func):
294
+ return self.thread_state.method_dispatch(func, **kwargs)
295
+ else:
296
+ f = self.thread_state.dispatch(func, **kwargs)
297
+
298
+ if isinstance(func, staticmethod):
299
+ return staticmethod(f)
300
+ elif isinstance(func, classmethod):
301
+ return classmethod(f)
302
+ else:
303
+ return f
304
+
305
+ def proxy_ext_function(self, func):
306
+ proxied = utils.wrapped_function(handler = self.ext_handler, target = func)
307
+ return self.proxy_function(func = func, internal = proxied)
308
+
309
+ def proxy_int_function(self, func):
310
+ proxied = utils.wrapped_function(handler = self.int_handler, target = func)
311
+ return self.proxy_function(func = func, external = proxied)
312
+
313
+ def proxy_ext_member(self, member):
314
+ return utils.wrapped_member(handler = self.ext_dispatch, target = member)
315
+
316
+ def proxy_int_member(self, member):
317
+ return utils.wrapped_member(handler = self.int_dispatch, target = member)
318
+
319
+ # def proxy__new__(self, *args, **kwargs):
320
+ # return self.ext_handler(*args, **kwargs)
321
+
322
+ # def on_new_ext_patched(self, obj):
323
+ # print(f'HWE!!!!!!!!!!! 2')
324
+ # print(f'HWE!!!!!!!!!!! 3')
325
+ # return id(obj)
326
+
327
+ # def on_new_int_patched(self, obj):
328
+ # return id(obj)
329
+
330
+ # def on_del_patched(self, ref):
331
+ # pass
332
+
333
+ # def create_from_external(self, obj):
334
+ # pass
335
+ # breakpoint()
336
+
337
+ def patch_type(self, cls):
338
+
339
+ # breakpoint()
142
340
 
143
- def ext_proxytype(self, cls):
144
341
  assert isinstance(cls, type)
145
- if utils.is_extendable(cls):
146
- return self.extend_type(cls)
147
- else:
148
- return instantiable_dynamic_proxytype(
149
- handler = self.ext_dispatch,
150
- cls = cls,
151
- thread_state = self.thread_state,
152
- create_stub = self.create_stub())
153
-
154
- def extend_type(self, cls):
155
-
156
- extended = extending_proxytype(
157
- cls = cls,
158
- base = Stub if self.create_stub() else cls,
159
- thread_state = self.thread_state,
160
- ext_handler = self.ext_dispatch,
161
- int_handler = self.int_dispatch,
162
- on_subclass_new = self.bind)
163
-
164
- self.immutable_types.add(extended)
165
342
 
166
- return extended
343
+ if issubclass(cls, BaseException): breakpoint()
344
+
345
+ assert not issubclass(cls, BaseException)
167
346
 
168
- def function_target(self, obj): return obj
347
+ assert cls not in self.patched_types
169
348
 
170
- def proxy_function(self, obj):
171
- return utils.wrapped_function(handler = self.ext_handler, target = obj)
349
+ self.patched_types.add(cls)
350
+
351
+ # if a method returns a patched object... what to do
352
+ # well if its a subclass, may need to return id as may have local state
353
+ # a subclass will have an associated id, should have been created via __new__
354
+ # if not a subclass, create empty proxy, what about object identity?
355
+
356
+ # track ALL creations ? override tp_alloc ?
357
+ # patch tp_alloc and tp_dealloc to call lifecycle object
358
+ # given a lifecycle, can attach to a class
359
+ # have one function attach_lifecycle, on_new returns token passed to on_del function
172
360
 
361
+ def proxy_attrs(cls, dict, proxy_function, proxy_member):
362
+
363
+ blacklist = ['__new__', '__getattribute__', '__del__', '__dict__']
364
+
365
+ for name, value in dict.items():
366
+ if name not in blacklist:
367
+ if type(value) in [types.MemberDescriptorType, types.GetSetDescriptorType]:
368
+ setattr(cls, name, proxy_member(value))
369
+ elif callable(value):
370
+ setattr(cls, name, proxy_function(value))
371
+
372
+ with WithoutFlags(cls, "Py_TPFLAGS_IMMUTABLETYPE"):
373
+
374
+ proxy_attrs(
375
+ cls,
376
+ dict = superdict(cls),
377
+ proxy_function = self.proxy_ext_function,
378
+ proxy_member = self.proxy_ext_member)
379
+
380
+ on_alloc = self.thread_state.dispatch(
381
+ utils.noop,
382
+ internal = self.bind,
383
+ external = self.create_from_external)
384
+
385
+ utils.set_on_alloc(cls, on_alloc)
386
+
387
+ cls.__retrace_system__ = self
388
+
389
+ if utils.is_extendable(cls):
390
+ def init_subclass(cls, **kwargs):
391
+
392
+ self.patched_types.add(cls)
393
+
394
+ def proxy_function(obj):
395
+ return utils.wrapped_function(handler = self.int_handler, target = obj)
396
+
397
+ proxy_attrs(
398
+ cls,
399
+ dict = cls.__dict__,
400
+ proxy_function = self.proxy_int_function,
401
+ proxy_member = self.proxy_int_member)
402
+
403
+ cls.__init_subclass__ = classmethod(init_subclass)
404
+
405
+ for subtype in get_all_subtypes(cls):
406
+ init_subclass(subtype)
407
+ utils.set_on_alloc(subtype, on_alloc)
408
+
409
+ cls.__retrace__ = self
410
+
411
+ self.bind(cls)
412
+
413
+ return cls
414
+
415
+ # def is_entry_frame(self, frame):
416
+ # return frame.globals.get("__name__", None) == "__main__"
417
+
418
+ def proxy_value(self, obj):
419
+ utils.sigtrap('proxy_value')
420
+
421
+ proxytype = dynamic_proxytype(handler = self.ext_dispatch, cls = type(obj))
422
+ proxytype.__retrace_source__ = 'external'
423
+
424
+ return utils.create_wrapped(proxytype, obj)
425
+
173
426
  def __call__(self, obj):
174
427
  assert not isinstance(obj, BaseException)
175
428
  assert not isinstance(obj, Proxy)
176
429
  assert not isinstance(obj, utils.wrapped_function)
177
430
 
178
431
  if type(obj) == type:
179
- if obj in self.immutable_types or issubclass(obj, tuple):
180
- return obj
181
-
182
- return self.ext_proxytype(obj)
432
+ return self.patch_type(obj)
183
433
 
184
434
  elif type(obj) in self.immutable_types:
185
435
  return obj
186
436
 
187
- elif is_function_type(type(obj)):
188
-
189
- return self.thread_state.dispatch(
190
- obj,
191
- internal = self.proxy_function(obj))
437
+ elif is_function_type(type(obj)):
438
+ return self.thread_state.dispatch(obj, internal = self.proxy_ext_function(obj))
192
439
 
440
+ elif type(obj) == types.ClassMethodDescriptorType:
441
+ func = self.thread_state.dispatch(obj, internal = self.proxy_ext_function(obj))
442
+ return classmethod(func)
193
443
  else:
194
- proxytype = dynamic_proxytype(handler = self.ext_dispatch, cls = type(obj))
444
+ return self.proxy_value(obj)
445
+
446
+ # def write_trace(self, obj):
447
+
448
+ # exclude = set([
449
+ # "ABCMeta.__subclasscheck__"
450
+ # # "__subclasscheck__"
451
+ # ])
452
+
195
453
 
196
- return utils.create_wrapped(proxytype, obj)
197
- # raise Exception(f'object {obj} was not proxied as its not a extensible type and is not callable')
454
+