retracesoftware-proxy 0.2.11__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 (42) hide show
  1. retracesoftware/__init__.py +0 -0
  2. retracesoftware/__main__.py +288 -0
  3. retracesoftware/autoenable.py +53 -0
  4. retracesoftware/config.json +175 -0
  5. retracesoftware/config.yaml +0 -0
  6. retracesoftware/install/__init__.py +0 -0
  7. retracesoftware/install/config.py +59 -0
  8. retracesoftware/install/edgecases.py +250 -0
  9. retracesoftware/install/globals.py +17 -0
  10. retracesoftware/install/install.py +142 -0
  11. retracesoftware/install/patcher.py +122 -0
  12. retracesoftware/install/patchfindspec.py +117 -0
  13. retracesoftware/install/phases.py +338 -0
  14. retracesoftware/install/predicate.py +92 -0
  15. retracesoftware/install/record.py +174 -0
  16. retracesoftware/install/references.py +66 -0
  17. retracesoftware/install/replace.py +28 -0
  18. retracesoftware/install/replay.py +102 -0
  19. retracesoftware/install/tracer.py +284 -0
  20. retracesoftware/install/typeutils.py +92 -0
  21. retracesoftware/modules.toml +388 -0
  22. retracesoftware/preload.txt +218 -0
  23. retracesoftware/proxy/__init__.py +3 -0
  24. retracesoftware/proxy/gateway.py +49 -0
  25. retracesoftware/proxy/globalref.py +31 -0
  26. retracesoftware/proxy/messagestream.py +204 -0
  27. retracesoftware/proxy/proxyfactory.py +357 -0
  28. retracesoftware/proxy/proxysystem.py +454 -0
  29. retracesoftware/proxy/proxytype.py +424 -0
  30. retracesoftware/proxy/record.py +211 -0
  31. retracesoftware/proxy/replay.py +138 -0
  32. retracesoftware/proxy/serializer.py +28 -0
  33. retracesoftware/proxy/startthread.py +40 -0
  34. retracesoftware/proxy/stubfactory.py +195 -0
  35. retracesoftware/proxy/thread.py +106 -0
  36. retracesoftware/replay.py +104 -0
  37. retracesoftware/run.py +378 -0
  38. retracesoftware/stackdifference.py +133 -0
  39. retracesoftware_proxy-0.2.11.dist-info/METADATA +8 -0
  40. retracesoftware_proxy-0.2.11.dist-info/RECORD +42 -0
  41. retracesoftware_proxy-0.2.11.dist-info/WHEEL +5 -0
  42. retracesoftware_proxy-0.2.11.dist-info/top_level.txt +1 -0
@@ -0,0 +1,454 @@
1
+ import retracesoftware.functional as functional
2
+ import retracesoftware_utils as utils
3
+ import types
4
+ from retracesoftware.proxy.gateway import adapter_pair
5
+ from types import SimpleNamespace
6
+ from retracesoftware.proxy.proxytype import *
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
12
+ import sys
13
+ import gc
14
+ import weakref
15
+ import enum
16
+ import functools
17
+ import re
18
+
19
+ class RetraceError(Exception):
20
+ pass
21
+
22
+ def proxy(proxytype):
23
+ return functional.spread(
24
+ utils.create_wrapped,
25
+ functional.sequence(functional.typeof, proxytype),
26
+ None)
27
+
28
+ def maybe_proxy(proxytype):
29
+ return functional.if_then_else(
30
+ functional.isinstanceof(utils.Wrapped),
31
+ utils.unwrap,
32
+ proxy(functional.memoize_one_arg(proxytype)))
33
+
34
+ class Patched:
35
+ __slots__ = ()
36
+
37
+ class RetraceBase:
38
+ pass
39
+
40
+ unproxy_execute = functional.mapargs(starting = 1,
41
+ transform = functional.walker(utils.try_unwrap),
42
+ function = functional.apply)
43
+
44
+ def resolve(obj):
45
+ try:
46
+ return getattr(sys.modules[obj.__module__], obj.__name__)
47
+ except:
48
+ return None
49
+
50
+ def is_function_type(cls):
51
+ return cls in [types.BuiltinFunctionType, types.FunctionType]
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
+
128
+ class ProxySystem:
129
+
130
+ # def bind(self, obj): pass
131
+
132
+ def wrap_int_to_ext(self, obj): return obj
133
+
134
+ def wrap_ext_to_int(self, obj): return obj
135
+
136
+ def on_int_call(self, func, *args, **kwargs):
137
+ pass
138
+
139
+ def on_ext_result(self, result):
140
+ pass
141
+
142
+ def on_ext_error(self, err_type, err_value, err_traceback):
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
156
+
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):
171
+
172
+ self.patched_types = set()
173
+ self.thread_state = thread_state
174
+ self.fork_counter = 0
175
+ self.tracer = tracer
176
+ self.immutable_types = 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
183
+
184
+ should_proxy = functional.sequence(functional.typeof, functional.memoize_one_arg(should_proxy_type))
185
+
186
+ def proxyfactory(proxytype):
187
+ return functional.walker(functional.when(should_proxy, maybe_proxy(proxytype)))
188
+
189
+ int_spec = SimpleNamespace(
190
+ apply = thread_state.wrap('internal', self.int_apply),
191
+ proxy = proxyfactory(thread_state.wrap('disabled', self.int_proxytype)),
192
+ on_call = tracer('proxy.int.call', self.on_int_call),
193
+ on_result = tracer('proxy.int.result'),
194
+ on_error = tracer('proxy.int.error'),
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}))
200
+
201
+ ext_spec = SimpleNamespace(
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,
206
+ on_result = self.on_ext_result,
207
+ on_error = self.on_ext_error,
208
+ )
209
+
210
+ int2ext, ext2int = adapter_pair(int_spec, ext_spec)
211
+
212
+ def gateway(name, internal = functional.apply, external = functional.apply):
213
+ default = tracer(name, unproxy_execute)
214
+ return thread_state.dispatch(default, internal = internal, external = external)
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))
218
+
219
+ self.ext_dispatch = gateway('proxy.int.disabled.event', internal = self.ext_handler)
220
+ self.int_dispatch = gateway('proxy.ext.disabled.event', external = self.int_handler)
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
+
235
+ def new_child_path(self, path):
236
+ return path.parent / f'fork-{self.fork_counter}' / path.name
237
+
238
+ def before_fork(self):
239
+ self.saved_thread_state = self.thread_state.value
240
+ self.thread_state.value = 'disabled'
241
+
242
+ def after_fork_in_child(self):
243
+ self.thread_state.value = self.saved_thread_state
244
+ self.fork_counter = 0
245
+
246
+ def after_fork_in_parent(self):
247
+ self.thread_state.value = self.saved_thread_state
248
+ self.fork_counter += 1
249
+
250
+ def on_thread_exit(self, thread_id):
251
+ pass
252
+
253
+ # def create_stub(self): return False
254
+
255
+ def int_proxytype(self, cls):
256
+ if cls is object:
257
+ breakpoint()
258
+
259
+ return dynamic_int_proxytype(
260
+ handler = self.int_dispatch,
261
+ cls = cls,
262
+ bind = self.bind)
263
+
264
+ def ext_proxytype(self, cls):
265
+
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)
287
+
288
+ return proxytype
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()
340
+
341
+ assert isinstance(cls, type)
342
+
343
+ if issubclass(cls, BaseException): breakpoint()
344
+
345
+ assert not issubclass(cls, BaseException)
346
+
347
+ assert cls not in self.patched_types
348
+
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
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
+
426
+ def __call__(self, obj):
427
+ assert not isinstance(obj, BaseException)
428
+ assert not isinstance(obj, Proxy)
429
+ assert not isinstance(obj, utils.wrapped_function)
430
+
431
+ if type(obj) == type:
432
+ return self.patch_type(obj)
433
+
434
+ elif type(obj) in self.immutable_types:
435
+ return obj
436
+
437
+ elif is_function_type(type(obj)):
438
+ return self.thread_state.dispatch(obj, internal = self.proxy_ext_function(obj))
439
+
440
+ elif type(obj) == types.ClassMethodDescriptorType:
441
+ func = self.thread_state.dispatch(obj, internal = self.proxy_ext_function(obj))
442
+ return classmethod(func)
443
+ else:
444
+ return self.proxy_value(obj)
445
+
446
+ # def write_trace(self, obj):
447
+
448
+ # exclude = set([
449
+ # "ABCMeta.__subclasscheck__"
450
+ # # "__subclasscheck__"
451
+ # ])
452
+
453
+
454
+