retracesoftware-proxy 0.1.7__py3-none-any.whl → 0.1.9__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.
- retracesoftware/config.json +36 -12
- retracesoftware/install/patcher.py +64 -25
- retracesoftware/install/tracer.py +31 -2
- retracesoftware/proxy/proxysystem.py +67 -55
- retracesoftware/proxy/proxytype.py +31 -23
- retracesoftware/proxy/record.py +23 -99
- retracesoftware/proxy/replay.py +74 -152
- retracesoftware/proxy/stubfactory.py +20 -8
- {retracesoftware_proxy-0.1.7.dist-info → retracesoftware_proxy-0.1.9.dist-info}/METADATA +1 -1
- {retracesoftware_proxy-0.1.7.dist-info → retracesoftware_proxy-0.1.9.dist-info}/RECORD +12 -12
- {retracesoftware_proxy-0.1.7.dist-info → retracesoftware_proxy-0.1.9.dist-info}/WHEEL +0 -0
- {retracesoftware_proxy-0.1.7.dist-info → retracesoftware_proxy-0.1.9.dist-info}/top_level.txt +0 -0
retracesoftware/config.json
CHANGED
|
@@ -160,19 +160,33 @@
|
|
|
160
160
|
"_imp": {
|
|
161
161
|
"patch_extension_exec": ["exec_dynamic", "exec_builtin"]
|
|
162
162
|
},
|
|
163
|
+
"bdb": {
|
|
164
|
+
"methods_with_state": {
|
|
165
|
+
"disabled": {
|
|
166
|
+
"Bdb": ["trace_dispatch"]
|
|
167
|
+
}
|
|
168
|
+
},
|
|
169
|
+
"with_state": {
|
|
170
|
+
"disabled": ["Bdb.trace_dispatch"]
|
|
171
|
+
}
|
|
172
|
+
},
|
|
163
173
|
"sys": {
|
|
164
174
|
"with_state": {
|
|
165
175
|
"disabled": ["excepthook"]
|
|
166
176
|
},
|
|
167
|
-
"
|
|
168
|
-
"
|
|
177
|
+
"comment": {
|
|
178
|
+
"with_state_recursive": {
|
|
179
|
+
"disabled": ["settrace", "setprofile"]
|
|
180
|
+
}
|
|
169
181
|
}
|
|
170
182
|
},
|
|
171
183
|
|
|
172
184
|
"importlib._bootstrap": {
|
|
185
|
+
"comment": {
|
|
173
186
|
"with_state": {
|
|
174
187
|
"bootstrap": ["_load_unlocked", "_find_spec"]
|
|
175
188
|
}
|
|
189
|
+
}
|
|
176
190
|
},
|
|
177
191
|
|
|
178
192
|
"encodings": {
|
|
@@ -264,10 +278,16 @@
|
|
|
264
278
|
"open_code": "path",
|
|
265
279
|
"open": "file"
|
|
266
280
|
},
|
|
267
|
-
"
|
|
268
|
-
"FileIO
|
|
269
|
-
|
|
270
|
-
|
|
281
|
+
"patchtype": {
|
|
282
|
+
"FileIO": {
|
|
283
|
+
"readinfo": "retracesoftware.install.edgecases.readinto"
|
|
284
|
+
},
|
|
285
|
+
"BufferedReader": {
|
|
286
|
+
"readinfo": "retracesoftware.install.edgecases.readinto"
|
|
287
|
+
},
|
|
288
|
+
"BufferedRandom": {
|
|
289
|
+
"readinfo": "retracesoftware.install.edgecases.readinto"
|
|
290
|
+
}
|
|
271
291
|
}
|
|
272
292
|
},
|
|
273
293
|
"pathlib": {
|
|
@@ -323,10 +343,12 @@
|
|
|
323
343
|
"gaierror",
|
|
324
344
|
"timeout"
|
|
325
345
|
],
|
|
326
|
-
"
|
|
327
|
-
"socket
|
|
328
|
-
|
|
329
|
-
|
|
346
|
+
"patchtype": {
|
|
347
|
+
"socket": {
|
|
348
|
+
"recvfrom_into": "retracesoftware.install.edgecases.recvfrom_into",
|
|
349
|
+
"recv_into": "retracesoftware.install.edgecases.recv_into",
|
|
350
|
+
"recvmsg_into": "retracesoftware.install.edgecases.recvmsg_into"
|
|
351
|
+
}
|
|
330
352
|
}
|
|
331
353
|
},
|
|
332
354
|
"select": {
|
|
@@ -349,8 +371,10 @@
|
|
|
349
371
|
"_SSLSocket.write": "write"
|
|
350
372
|
}
|
|
351
373
|
},
|
|
352
|
-
"
|
|
353
|
-
"_SSLSocket
|
|
374
|
+
"patchtype": {
|
|
375
|
+
"_SSLSocket": {
|
|
376
|
+
"read": "retracesoftware.install.edgecases.read"
|
|
377
|
+
}
|
|
354
378
|
}
|
|
355
379
|
},
|
|
356
380
|
"_random": {
|
|
@@ -25,7 +25,7 @@ from retracesoftware.install.config import load_config
|
|
|
25
25
|
from functools import wraps
|
|
26
26
|
|
|
27
27
|
import retracesoftware.functional as functional
|
|
28
|
-
import
|
|
28
|
+
import retracesoftware.utils as utils
|
|
29
29
|
|
|
30
30
|
from retracesoftware.install import edgecases
|
|
31
31
|
from functools import partial
|
|
@@ -137,9 +137,9 @@ def superdict(cls):
|
|
|
137
137
|
|
|
138
138
|
return result
|
|
139
139
|
|
|
140
|
-
def is_method_descriptor(obj):
|
|
141
|
-
|
|
142
|
-
|
|
140
|
+
# def is_method_descriptor(obj):
|
|
141
|
+
# return isinstance(obj, types.FunctionType) or \
|
|
142
|
+
# (isinstance(obj, (types.WrapperDescriptorType, types.MethodDescriptorType)) and obj.__objclass__ != object)
|
|
143
143
|
|
|
144
144
|
def wrap_method_descriptors(wrapper, prefix, base):
|
|
145
145
|
slots = {"__slots__": () }
|
|
@@ -150,7 +150,7 @@ def wrap_method_descriptors(wrapper, prefix, base):
|
|
|
150
150
|
|
|
151
151
|
for name,value in superdict(base).items():
|
|
152
152
|
if name not in blacklist:
|
|
153
|
-
if is_method_descriptor(value):
|
|
153
|
+
if utils.is_method_descriptor(value):
|
|
154
154
|
setattr(extended, name, wrapper(value))
|
|
155
155
|
|
|
156
156
|
return extended
|
|
@@ -179,6 +179,7 @@ class Patcher:
|
|
|
179
179
|
self.type_attribute_filter = self.predicate(config['type_attribute_filter'])
|
|
180
180
|
self.post_commit = post_commit
|
|
181
181
|
self.exclude_paths = [re.compile(s) for s in config.get('exclude_paths', [])]
|
|
182
|
+
self.typepatcher = {}
|
|
182
183
|
|
|
183
184
|
def is_phase(name): return getattr(getattr(self, name, None), "is_phase", False)
|
|
184
185
|
|
|
@@ -193,7 +194,21 @@ class Patcher:
|
|
|
193
194
|
# print(f'in path_predicate, excluding {path}')
|
|
194
195
|
return False
|
|
195
196
|
return True
|
|
196
|
-
|
|
197
|
+
|
|
198
|
+
def on_proxytype(self, cls):
|
|
199
|
+
|
|
200
|
+
def patch(spec):
|
|
201
|
+
for method, transform in spec.items():
|
|
202
|
+
setattr(cls, method, resolve(transform)(getattr(cls, method)))
|
|
203
|
+
|
|
204
|
+
if cls.__module__ in self.modules:
|
|
205
|
+
spec = self.modules[cls.__module__]
|
|
206
|
+
|
|
207
|
+
if 'patchtype' in spec:
|
|
208
|
+
patchtype = spec['patchtype']
|
|
209
|
+
if cls.__name__ in patchtype:
|
|
210
|
+
patch(patchtype[cls.__name__])
|
|
211
|
+
|
|
197
212
|
@property
|
|
198
213
|
def disable(self):
|
|
199
214
|
return self.thread_state.select('disabled')
|
|
@@ -260,7 +275,7 @@ class Patcher:
|
|
|
260
275
|
|
|
261
276
|
@patch
|
|
262
277
|
def patch_start_new_thread(self, value):
|
|
263
|
-
def start_new_thread(
|
|
278
|
+
def start_new_thread(function, *args):
|
|
264
279
|
# synchronized, replay shoudl yield correct number
|
|
265
280
|
thread_id = self.thread_counter()
|
|
266
281
|
|
|
@@ -314,15 +329,35 @@ class Patcher:
|
|
|
314
329
|
for state,elems in spec.items():
|
|
315
330
|
|
|
316
331
|
def wrap(obj):
|
|
317
|
-
return functional.
|
|
318
|
-
|
|
319
|
-
lambda f: self.thread_state.wrap(state, f),
|
|
332
|
+
return functional.recurive_wrap_function(
|
|
333
|
+
functional.partial(self.thread_state.wrap, state),
|
|
320
334
|
obj)
|
|
321
335
|
|
|
322
336
|
updates.update(map_values(wrap, select_keys(elems, mod_dict)))
|
|
323
337
|
|
|
324
338
|
return updates
|
|
325
339
|
|
|
340
|
+
@phase
|
|
341
|
+
def methods_with_state(self, spec, mod_dict):
|
|
342
|
+
|
|
343
|
+
# updates = {}
|
|
344
|
+
|
|
345
|
+
def update(cls, name, f):
|
|
346
|
+
setattr(cls, name, f(getattr(cls, name)))
|
|
347
|
+
|
|
348
|
+
for state,cls_methods in spec.items():
|
|
349
|
+
def wrap(obj):
|
|
350
|
+
assert callable(obj)
|
|
351
|
+
return self.thread_state.wrap(desired_state = state, function = obj)
|
|
352
|
+
|
|
353
|
+
for typename,methodnames in cls_methods.items():
|
|
354
|
+
cls = mod_dict[typename]
|
|
355
|
+
|
|
356
|
+
for methodname in methodnames:
|
|
357
|
+
update(cls, methodname, wrap)
|
|
358
|
+
|
|
359
|
+
return {}
|
|
360
|
+
|
|
326
361
|
@phase
|
|
327
362
|
def with_state(self, spec, mod_dict):
|
|
328
363
|
|
|
@@ -330,7 +365,8 @@ class Patcher:
|
|
|
330
365
|
|
|
331
366
|
for state,elems in spec.items():
|
|
332
367
|
|
|
333
|
-
def wrap(obj):
|
|
368
|
+
def wrap(obj):
|
|
369
|
+
return self.thread_state.wrap(desired_state = state, function = obj)
|
|
334
370
|
|
|
335
371
|
updates.update(map_values(wrap, select_keys(elems, mod_dict)))
|
|
336
372
|
|
|
@@ -377,7 +413,9 @@ class Patcher:
|
|
|
377
413
|
name = parts[0]
|
|
378
414
|
if name in mod_dict:
|
|
379
415
|
value = mod_dict[name]
|
|
380
|
-
assert not isinstance(value, utils.wrapped_function),
|
|
416
|
+
assert not isinstance(value, utils.wrapped_function), \
|
|
417
|
+
f"value for key: {name} is already wrapped"
|
|
418
|
+
|
|
381
419
|
if len(parts) == 1:
|
|
382
420
|
updates[name] = resolve(wrapper_name)(value)
|
|
383
421
|
elif len(parts) == 2:
|
|
@@ -436,20 +474,19 @@ class Patcher:
|
|
|
436
474
|
|
|
437
475
|
def __call__(self, module):
|
|
438
476
|
|
|
439
|
-
|
|
440
|
-
|
|
477
|
+
if self.thread_state.value == 'internal' and not hasattr(module, '__retrace__'):
|
|
478
|
+
with self.thread_state.select('bootstrap'):
|
|
479
|
+
configs = list(self.configs(module))
|
|
441
480
|
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
except Exception as error:
|
|
452
|
-
raise Exception(f'Error patching module: {configs[0]}') from error
|
|
481
|
+
if len(configs) > 0:
|
|
482
|
+
if len(configs) > 1:
|
|
483
|
+
raise Exception(f'TODO')
|
|
484
|
+
else:
|
|
485
|
+
module.__retrace__ = None
|
|
486
|
+
try:
|
|
487
|
+
self.patch_module_with_name(configs[0], module)
|
|
488
|
+
except Exception as error:
|
|
489
|
+
raise Exception(f'Error patching module: {configs[0]}') from error
|
|
453
490
|
|
|
454
491
|
return module
|
|
455
492
|
|
|
@@ -518,6 +555,8 @@ def install(mode):
|
|
|
518
555
|
system = system,
|
|
519
556
|
post_commit = getattr(system, 'on_patched', None),
|
|
520
557
|
immutable_types = immutable_types)
|
|
558
|
+
|
|
559
|
+
system.on_proxytype = patcher.on_proxytype
|
|
521
560
|
|
|
522
561
|
def at_exit(): thread_state.value = 'disabled'
|
|
523
562
|
|
|
@@ -13,11 +13,34 @@ def format_kwargs(kwargs):
|
|
|
13
13
|
return tuple(result)
|
|
14
14
|
|
|
15
15
|
class Tracer:
|
|
16
|
+
|
|
16
17
|
def __init__(self, config, writer):
|
|
17
18
|
self.config = config
|
|
18
19
|
serialize = functional.walker(self.serialize)
|
|
19
20
|
self.writer = functional.mapargs(transform = serialize, function = writer)
|
|
20
21
|
|
|
22
|
+
def systrace(self, frame, event, arg):
|
|
23
|
+
try:
|
|
24
|
+
if event in ['line', 'call']:
|
|
25
|
+
print(f'In systrace: {event} {frame.f_code.co_filename}:{frame.f_lineno}')
|
|
26
|
+
self.writer(event, frame.f_code.co_filename, frame.f_code.co_name, frame.f_lineno)
|
|
27
|
+
|
|
28
|
+
elif event == 'return':
|
|
29
|
+
print(f'In systrace: {event}')
|
|
30
|
+
self.writer(event)
|
|
31
|
+
# self.writer(event, self.serialize(arg))
|
|
32
|
+
|
|
33
|
+
elif event == 'exception':
|
|
34
|
+
print(f'In systrace: {event}')
|
|
35
|
+
self.writer(event)
|
|
36
|
+
else:
|
|
37
|
+
print(f'In systrace: {event}')
|
|
38
|
+
|
|
39
|
+
return self.systrace
|
|
40
|
+
except:
|
|
41
|
+
print(f'systrace RAISED ERROR!')
|
|
42
|
+
raise
|
|
43
|
+
|
|
21
44
|
def serialize(self, obj):
|
|
22
45
|
try:
|
|
23
46
|
if obj is None: return None
|
|
@@ -56,6 +79,9 @@ class Tracer:
|
|
|
56
79
|
if name in self.config:
|
|
57
80
|
self.writer(name, message)
|
|
58
81
|
|
|
82
|
+
def checkpoint(self, obj):
|
|
83
|
+
self.writer(obj)
|
|
84
|
+
|
|
59
85
|
def stacktrace(self):
|
|
60
86
|
self.writer('stacktrace', utils.stacktrace())
|
|
61
87
|
|
|
@@ -63,8 +89,11 @@ class Tracer:
|
|
|
63
89
|
if name in self.config:
|
|
64
90
|
if name.endswith('.call'):
|
|
65
91
|
def write_call(*args, **kwargs):
|
|
66
|
-
|
|
67
|
-
|
|
92
|
+
self.stacktrace()
|
|
93
|
+
if len(args) > 0:
|
|
94
|
+
self.writer(name, args[0].__name__, args[1:], kwargs)
|
|
95
|
+
else:
|
|
96
|
+
breakpoint()
|
|
68
97
|
|
|
69
98
|
return functional.firstof(write_call, func) if func else write_call
|
|
70
99
|
|
|
@@ -36,13 +36,14 @@ def resolve(obj):
|
|
|
36
36
|
def is_function_type(cls):
|
|
37
37
|
return cls in [types.BuiltinFunctionType, types.FunctionType]
|
|
38
38
|
|
|
39
|
+
def is_instance_method(obj):
|
|
40
|
+
return isinstance(obj, types.MethodDescriptorType) or isinstance(obj, types.FunctionType)
|
|
41
|
+
|
|
39
42
|
class ProxySystem:
|
|
40
43
|
|
|
41
44
|
def bind(self, obj): pass
|
|
42
45
|
|
|
43
|
-
def wrap_int_to_ext(self, obj):
|
|
44
|
-
return obj
|
|
45
|
-
# return functional.sequence(functional.side_effect(functional.repeatedly(gc.collect)), obj)
|
|
46
|
+
def wrap_int_to_ext(self, obj): return obj
|
|
46
47
|
|
|
47
48
|
def wrap_ext_to_int(self, obj): return obj
|
|
48
49
|
|
|
@@ -54,10 +55,13 @@ class ProxySystem:
|
|
|
54
55
|
|
|
55
56
|
def on_ext_error(self, err_type, err_value, err_traceback):
|
|
56
57
|
pass
|
|
57
|
-
|
|
58
|
+
|
|
58
59
|
# def stacktrace(self):
|
|
59
60
|
# self.tracer.stacktrace()
|
|
60
61
|
|
|
62
|
+
def set_thread_id(self, id):
|
|
63
|
+
utils.set_thread_id(id)
|
|
64
|
+
|
|
61
65
|
def __init__(self, thread_state, immutable_types, tracer):
|
|
62
66
|
|
|
63
67
|
self.thread_state = thread_state
|
|
@@ -67,7 +71,7 @@ class ProxySystem:
|
|
|
67
71
|
self.on_proxytype = None
|
|
68
72
|
|
|
69
73
|
def is_immutable_type(cls):
|
|
70
|
-
return issubclass(cls, tuple(immutable_types))
|
|
74
|
+
return cls is object or issubclass(cls, tuple(immutable_types))
|
|
71
75
|
|
|
72
76
|
is_immutable = functional.sequence(functional.typeof, functional.memoize_one_arg(is_immutable_type))
|
|
73
77
|
|
|
@@ -84,7 +88,7 @@ class ProxySystem:
|
|
|
84
88
|
|
|
85
89
|
ext_spec = SimpleNamespace(
|
|
86
90
|
apply = thread_state.wrap('external', functional.apply),
|
|
87
|
-
proxy = proxyfactory(thread_state.wrap('disabled', self.
|
|
91
|
+
proxy = proxyfactory(thread_state.wrap('disabled', self.ext_proxytype)),
|
|
88
92
|
on_call = tracer('proxy.ext.call'),
|
|
89
93
|
on_result = self.on_ext_result,
|
|
90
94
|
on_error = self.on_ext_error,
|
|
@@ -102,6 +106,11 @@ class ProxySystem:
|
|
|
102
106
|
self.ext_dispatch = gateway('proxy.int.disabled.event', internal = self.ext_handler)
|
|
103
107
|
self.int_dispatch = gateway('proxy.ext.disabled.event', external = self.int_handler)
|
|
104
108
|
|
|
109
|
+
if 'systrace' in tracer.config:
|
|
110
|
+
func = thread_state.wrap(desired_state = 'disabled', function = tracer.systrace)
|
|
111
|
+
func = self.thread_state.dispatch(lambda *args: None, internal = func)
|
|
112
|
+
sys.settrace(func)
|
|
113
|
+
|
|
105
114
|
def new_child_path(self, path):
|
|
106
115
|
return path.parent / f'fork-{self.fork_counter}' / path.name
|
|
107
116
|
|
|
@@ -117,76 +126,78 @@ class ProxySystem:
|
|
|
117
126
|
self.thread_state.value = self.saved_thread_state
|
|
118
127
|
self.fork_counter += 1
|
|
119
128
|
|
|
120
|
-
def create_stub(self): return False
|
|
129
|
+
# def create_stub(self): return False
|
|
121
130
|
|
|
122
131
|
def int_proxytype(self, cls):
|
|
123
|
-
|
|
132
|
+
if cls is object:
|
|
133
|
+
breakpoint()
|
|
134
|
+
|
|
135
|
+
proxytype = dynamic_int_proxytype(
|
|
124
136
|
handler = self.int_dispatch,
|
|
125
137
|
cls = cls,
|
|
126
138
|
bind = self.bind)
|
|
127
|
-
|
|
128
|
-
def dynamic_ext_proxytype(self, cls):
|
|
129
|
-
|
|
130
|
-
proxytype = dynamic_proxytype(
|
|
131
|
-
handler = self.ext_dispatch,
|
|
132
|
-
cls = cls)
|
|
133
|
-
if self.on_proxytype:
|
|
134
|
-
self.on_proxytype(proxytype)
|
|
135
139
|
|
|
140
|
+
if self.on_proxytype: self.on_proxytype(proxytype)
|
|
136
141
|
return proxytype
|
|
137
|
-
|
|
138
|
-
# resolved = resolve(cls)
|
|
139
|
-
# if isinstance(resolved, ExtendingProxy):
|
|
140
|
-
# return dynamic_from_extended(resolved)
|
|
141
|
-
# elif isinstance(resolved, DynamicProxy):
|
|
142
|
-
# return resolved
|
|
143
|
-
# else:
|
|
144
|
-
# return dynamic_proxytype(handler = self.ext_dispatch, cls = cls)
|
|
145
142
|
|
|
146
143
|
def ext_proxytype(self, cls):
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
return self.proxy_function(cls)
|
|
144
|
+
if cls is object:
|
|
145
|
+
breakpoint()
|
|
146
|
+
|
|
147
|
+
proxytype = dynamic_proxytype(handler = self.ext_dispatch, cls = cls)
|
|
152
148
|
|
|
153
|
-
|
|
154
|
-
# handler = self.ext_dispatch,
|
|
155
|
-
# cls = cls,
|
|
156
|
-
# thread_state = self.thread_state,
|
|
157
|
-
# create_stub = self.create_stub())
|
|
158
|
-
|
|
159
|
-
def basetype(self, cls):
|
|
160
|
-
return cls
|
|
161
|
-
|
|
162
|
-
def extend_type(self, cls):
|
|
163
|
-
extended = extending_proxytype(
|
|
164
|
-
cls = cls,
|
|
165
|
-
base = self.basetype(cls),
|
|
166
|
-
thread_state = self.thread_state,
|
|
167
|
-
ext_handler = self.ext_dispatch,
|
|
168
|
-
int_handler = self.int_dispatch,
|
|
169
|
-
on_subclass_new = self.bind)
|
|
149
|
+
if self.on_proxytype: self.on_proxytype(proxytype)
|
|
170
150
|
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
return extended
|
|
174
|
-
|
|
151
|
+
return proxytype
|
|
152
|
+
|
|
175
153
|
def function_target(self, obj): return obj
|
|
176
154
|
|
|
177
155
|
def proxy_function(self, obj):
|
|
178
156
|
return utils.wrapped_function(handler = self.ext_handler, target = obj)
|
|
179
157
|
|
|
158
|
+
def patchtype(self, cls):
|
|
159
|
+
if cls in self.immutable_types or issubclass(cls, tuple):
|
|
160
|
+
return cls
|
|
161
|
+
|
|
162
|
+
def wrap(func):
|
|
163
|
+
return self.thread_state.dispatch(func, internal = self.proxy_function(func))
|
|
164
|
+
|
|
165
|
+
def wrap_new(func):
|
|
166
|
+
proxied = self.proxy_function(func)
|
|
167
|
+
|
|
168
|
+
def new(cls, *args, **kwargs):
|
|
169
|
+
instance = proxied(cls, *args, **kwargs)
|
|
170
|
+
instance.__init__(*args, **kwargs)
|
|
171
|
+
return instance
|
|
172
|
+
|
|
173
|
+
return self.thread_state.dispatch(func, internal = new)
|
|
174
|
+
|
|
175
|
+
if utils.is_extendable(cls):
|
|
176
|
+
slots = {'__module__': cls.__module__, '__slots__': []}
|
|
177
|
+
|
|
178
|
+
for name, value in superdict(cls).items():
|
|
179
|
+
if callable(value) and not is_instance_method(value):
|
|
180
|
+
slots[name] = wrap_new(value) if name == '__new__' else wrap(value)
|
|
181
|
+
|
|
182
|
+
return type(cls.__name__, (cls,), slots)
|
|
183
|
+
|
|
184
|
+
elif not utils.is_immutable(cls):
|
|
185
|
+
def update(name, f):
|
|
186
|
+
setattr(cls, name, f(getattr(cls, name)))
|
|
187
|
+
|
|
188
|
+
for name, value in superdict(cls).items():
|
|
189
|
+
if callable(value) and not is_instance_method(value):
|
|
190
|
+
update(name, wrap_new if name == '__new__' else wrap)
|
|
191
|
+
else:
|
|
192
|
+
return wrap(cls)
|
|
193
|
+
|
|
180
194
|
def __call__(self, obj):
|
|
181
195
|
assert not isinstance(obj, BaseException)
|
|
182
196
|
assert not isinstance(obj, Proxy)
|
|
183
197
|
assert not isinstance(obj, utils.wrapped_function)
|
|
184
198
|
|
|
185
|
-
if type(obj) == type:
|
|
186
|
-
|
|
187
|
-
return obj
|
|
188
|
-
|
|
189
|
-
return self.ext_proxytype(obj)
|
|
199
|
+
if type(obj) == type:
|
|
200
|
+
return self.patchtype(obj)
|
|
190
201
|
|
|
191
202
|
elif type(obj) in self.immutable_types:
|
|
192
203
|
return obj
|
|
@@ -199,6 +210,7 @@ class ProxySystem:
|
|
|
199
210
|
|
|
200
211
|
else:
|
|
201
212
|
proxytype = dynamic_proxytype(handler = self.ext_dispatch, cls = type(obj))
|
|
213
|
+
if self.on_proxytype: self.on_proxytype(proxytype)
|
|
202
214
|
|
|
203
215
|
return utils.create_wrapped(proxytype, obj)
|
|
204
216
|
# raise Exception(f'object {obj} was not proxied as its not a extensible type and is not callable')
|
|
@@ -163,7 +163,11 @@ class DescriptorProxy:
|
|
|
163
163
|
self.target = target
|
|
164
164
|
|
|
165
165
|
def __get__(self, obj, cls):
|
|
166
|
-
|
|
166
|
+
try:
|
|
167
|
+
return self.handler(self.target.__get__, obj, cls)
|
|
168
|
+
except:
|
|
169
|
+
print(f'error calling __get__')
|
|
170
|
+
raise
|
|
167
171
|
|
|
168
172
|
def __set__(self, obj, value):
|
|
169
173
|
return self.handler(self.target.__set__, obj, value)
|
|
@@ -171,27 +175,28 @@ class DescriptorProxy:
|
|
|
171
175
|
def __delete__(self, obj):
|
|
172
176
|
return self.handler(self.target.__delete__, obj)
|
|
173
177
|
|
|
174
|
-
class ExtendingDescriptorProxy:
|
|
178
|
+
# class ExtendingDescriptorProxy:
|
|
175
179
|
|
|
176
|
-
|
|
180
|
+
# __slots__ = ['handler', 'proxytype', 'name']
|
|
177
181
|
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
+
# def __init__(self, proxytype, handler, name):
|
|
183
|
+
# self.proxytype = proxytype
|
|
184
|
+
# self.handler = handler
|
|
185
|
+
# self.name = name
|
|
182
186
|
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
+
# def __get__(self, instance, owner):
|
|
188
|
+
# inst = owner if instance is None else instance
|
|
189
|
+
# getter = functional.partial(getattr, super(self.proxytype, inst))
|
|
190
|
+
# return self.handler(getter, self.name)
|
|
187
191
|
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
192
|
+
# def __set__(self, instance, value):
|
|
193
|
+
# breakpoint()
|
|
194
|
+
# setter = functional.partial(setattr, super(self.proxytype, instance))
|
|
195
|
+
# return self.handler(setter, self.name, value)
|
|
191
196
|
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
197
|
+
# def __delete__(self, instance):
|
|
198
|
+
# deleter = functional.partial(delattr, super(self.proxytype, instance))
|
|
199
|
+
# return self.handler(deleter, self.name)
|
|
195
200
|
|
|
196
201
|
|
|
197
202
|
def dynamic_proxytype(handler, cls):
|
|
@@ -203,14 +208,16 @@ def dynamic_proxytype(handler, cls):
|
|
|
203
208
|
|
|
204
209
|
spec = {}
|
|
205
210
|
|
|
211
|
+
def wrap(func): return utils.wrapped_function(handler = handler, target = func)
|
|
212
|
+
|
|
206
213
|
for name in superdict(cls).keys():
|
|
207
214
|
if name not in blacklist:
|
|
208
215
|
value = getattr(cls, name)
|
|
209
216
|
if is_descriptor(value):
|
|
210
217
|
if utils.is_method_descriptor(value):
|
|
211
|
-
spec[name] =
|
|
212
|
-
else:
|
|
213
|
-
|
|
218
|
+
spec[name] = wrap(value)
|
|
219
|
+
# else:
|
|
220
|
+
# spec[name] = DescriptorProxy(handler = handler, target = value)
|
|
214
221
|
|
|
215
222
|
# to_proxy = [m for m in methods(cls) if m not in blacklist]
|
|
216
223
|
|
|
@@ -218,8 +225,8 @@ def dynamic_proxytype(handler, cls):
|
|
|
218
225
|
|
|
219
226
|
# spec = { name: wrap(getattr(cls, name)) for name in to_proxy }
|
|
220
227
|
|
|
221
|
-
|
|
222
|
-
|
|
228
|
+
spec['__getattr__'] = wrap(getattr)
|
|
229
|
+
spec['__setattr__'] = wrap(setattr)
|
|
223
230
|
|
|
224
231
|
if utils.yields_callable_instances(cls):
|
|
225
232
|
spec['__call__'] = handler
|
|
@@ -362,7 +369,8 @@ def extending_proxytype(cls, base, thread_state, ext_handler, int_handler, on_su
|
|
|
362
369
|
extended = type(cls.__name__, (base, ExtendingProxy), slots)
|
|
363
370
|
|
|
364
371
|
for name in descriptors:
|
|
365
|
-
proxy = ExtendingDescriptorProxy(handler = ext_handler, name = name, proxytype = extended)
|
|
372
|
+
# proxy = ExtendingDescriptorProxy(handler = ext_handler, name = name, proxytype = extended)
|
|
373
|
+
proxy = DescriptorProxy(handler = ext_handler, target = getattr(cls, name))
|
|
366
374
|
setattr(extended, name, proxy)
|
|
367
375
|
|
|
368
376
|
def unproxied__new__(subclass, *args, **kwargs):
|
retracesoftware/proxy/record.py
CHANGED
|
@@ -70,6 +70,13 @@ class RecordProxySystem(ProxySystem):
|
|
|
70
70
|
self.thread_state.value = self.saved_thread_state
|
|
71
71
|
self.writer.reopen()
|
|
72
72
|
|
|
73
|
+
def sync(self, function):
|
|
74
|
+
write_sync = functional.always(self.writer.handle('SYNC'))
|
|
75
|
+
|
|
76
|
+
return utils.observer(
|
|
77
|
+
on_call = functional.firstof(self.thread_switch_monitor, write_sync),
|
|
78
|
+
function = function)
|
|
79
|
+
|
|
73
80
|
def __init__(self, thread_state,
|
|
74
81
|
immutable_types,
|
|
75
82
|
tracing_config,
|
|
@@ -95,132 +102,49 @@ class RecordProxySystem(ProxySystem):
|
|
|
95
102
|
|
|
96
103
|
serialize = functional.walker(self.bindings.get_else_key)
|
|
97
104
|
|
|
98
|
-
thread_switch_monitor = \
|
|
105
|
+
self.thread_switch_monitor = \
|
|
99
106
|
utils.thread_switch_monitor(
|
|
100
107
|
functional.repeatedly(functional.sequence(utils.thread_id, self.writer)))
|
|
101
108
|
|
|
102
|
-
self.sync = lambda function: functional.firstof(thread_switch_monitor, functional.always(self.writer.handle('SYNC')), function)
|
|
109
|
+
# self.sync = lambda function: functional.firstof(self.thread_switch_monitor, functional.always(self.writer.handle('SYNC')), function)
|
|
103
110
|
|
|
104
111
|
error = self.writer.handle('ERROR')
|
|
105
112
|
|
|
106
113
|
def write_error(cls, val, traceback):
|
|
107
114
|
error(cls, val)
|
|
108
115
|
|
|
109
|
-
self.set_thread_id = functional.partial(utils.set_thread_id, self.writer)
|
|
116
|
+
# self.set_thread_id = functional.partial(utils.set_thread_id, self.writer)
|
|
110
117
|
|
|
111
|
-
def watch(f): return functional.either(thread_switch_monitor, f)
|
|
118
|
+
def watch(f): return functional.either(self.thread_switch_monitor, f)
|
|
112
119
|
|
|
120
|
+
# w = self.writer.handle('TRACE')
|
|
121
|
+
# def foo(name, *args):
|
|
122
|
+
# print(f'writing: {self.writer.messages_written} {name} {args}')
|
|
123
|
+
# w(self.writer.messages_written, name, *args)
|
|
124
|
+
|
|
125
|
+
# tracer = Tracer(tracing_config, writer = foo)
|
|
113
126
|
tracer = Tracer(tracing_config, writer = self.writer.handle('TRACE'))
|
|
114
127
|
|
|
115
128
|
self.wrap_int_to_ext = watch
|
|
116
129
|
|
|
117
130
|
self.on_int_call = functional.mapargs(transform = serialize, function = self.writer.handle('CALL'))
|
|
118
131
|
|
|
119
|
-
|
|
120
|
-
# functional.isinstanceof(str),
|
|
121
|
-
# self.writer.handle('RESULT'),
|
|
122
|
-
# functional.sequence(serialize, self.writer))
|
|
123
|
-
|
|
124
|
-
self.on_ext_result = functional.sequence(serialize,
|
|
125
|
-
self.writer.handle('RESULT'))
|
|
132
|
+
self.on_ext_result = functional.sequence(serialize, self.writer.handle('RESULT'))
|
|
126
133
|
|
|
127
134
|
self.on_ext_error = write_error
|
|
128
135
|
|
|
129
136
|
self.ext_apply = self.int_apply = functional.apply
|
|
130
137
|
|
|
131
|
-
super().__init__(thread_state = thread_state,
|
|
138
|
+
super().__init__(thread_state = thread_state,
|
|
139
|
+
tracer = tracer,
|
|
140
|
+
immutable_types = immutable_types)
|
|
132
141
|
|
|
133
|
-
def
|
|
142
|
+
def ext_proxytype(self, cls):
|
|
134
143
|
|
|
135
|
-
proxytype = super().
|
|
144
|
+
proxytype = super().ext_proxytype(cls)
|
|
136
145
|
|
|
137
146
|
ref = self.writer.handle(StubRef(proxytype))
|
|
138
|
-
|
|
139
|
-
# blacklist = ['__class__', '__dict__', '__module__', '__doc__']
|
|
140
|
-
|
|
141
|
-
# methods = []
|
|
142
|
-
# members = []
|
|
143
|
-
|
|
144
|
-
# for key,value in proxytype.__dict__.items():
|
|
145
|
-
# if key not in blacklist:
|
|
146
|
-
# if utils.is_method_descriptor(value):
|
|
147
|
-
# methods.append(key)
|
|
148
|
-
# elif not key.startswith("__retrace"):
|
|
149
|
-
# members.append(key)
|
|
150
|
-
|
|
151
|
-
# ref = self.writer.handle(StubRef(
|
|
152
|
-
# name = cls.__name__,
|
|
153
|
-
# module = cls.__module__,
|
|
154
|
-
# methods = methods,
|
|
155
|
-
# members = members))
|
|
156
|
-
|
|
157
|
-
# list(proxytype.__dict__.keys())
|
|
158
|
-
|
|
159
|
-
# if resolveable_name(cls):
|
|
160
|
-
# module, name = resolveable_name(cls)
|
|
161
|
-
# ref = self.writer.handle(StubRef(type = 'dynamic', name = name, module = module))
|
|
162
|
-
# else:
|
|
163
|
-
# blacklist = ['__getattribute__']
|
|
164
|
-
# descriptors = {k: v for k,v in superdict(cls).items() if k not in blacklist and is_descriptor(v) }
|
|
165
|
-
|
|
166
|
-
# methods = [k for k, v in descriptors.items() if utils.is_method_descriptor(v)]
|
|
167
|
-
# members = [k for k, v in descriptors.items() if not utils.is_method_descriptor(v)]
|
|
168
|
-
|
|
169
|
-
# ref = self.writer.handle(ProxySpec(name = cls.__name__,
|
|
170
|
-
# module = cls.__module__,
|
|
171
|
-
# methods = methods,
|
|
172
|
-
# members = members))
|
|
173
147
|
|
|
174
148
|
self.writer.type_serializer[proxytype] = functional.constantly(ref)
|
|
175
149
|
|
|
176
150
|
return proxytype
|
|
177
|
-
|
|
178
|
-
def ext_proxytype(self, cls):
|
|
179
|
-
assert isinstance(cls, type), f"record.proxytype requires a type but was passed: {cls}"
|
|
180
|
-
|
|
181
|
-
# resolved = resolve(cls)
|
|
182
|
-
|
|
183
|
-
proxytype = super().ext_proxytype(cls)
|
|
184
|
-
|
|
185
|
-
assert resolve(cls) is cls
|
|
186
|
-
|
|
187
|
-
# ref = self.writer.handle(StubRef(name = cls.__name__,
|
|
188
|
-
# module = cls.__module__))
|
|
189
|
-
|
|
190
|
-
# if resolveable_name(cls):
|
|
191
|
-
# module, name = resolveable_name(cls)
|
|
192
|
-
# ref = self.writer.handle(StubRef(name = cls.__name__, module = cls.__module__))
|
|
193
|
-
# else:
|
|
194
|
-
# blacklist = ['__getattribute__']
|
|
195
|
-
# descriptors = {k: v for k,v in superdict(cls).items() if k not in blacklist and is_descriptor(v) }
|
|
196
|
-
|
|
197
|
-
# methods = [k for k, v in descriptors.items() if utils.is_method_descriptor(v)]
|
|
198
|
-
# members = [k for k, v in descriptors.items() if not utils.is_method_descriptor(v)]
|
|
199
|
-
|
|
200
|
-
# ref = self.writer.handle(ProxySpec(name = cls.__name__,
|
|
201
|
-
# module = cls.__module__,
|
|
202
|
-
# methods = methods,
|
|
203
|
-
# members = members))
|
|
204
|
-
|
|
205
|
-
# self.writer.type_serializer[proxytype] = functional.constantly(ref)
|
|
206
|
-
return proxytype
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
def extend_type(self, cls):
|
|
210
|
-
|
|
211
|
-
if cls in self.extended_types:
|
|
212
|
-
return self.extended_types[cls]
|
|
213
|
-
|
|
214
|
-
extended = super().extend_type(cls)
|
|
215
|
-
|
|
216
|
-
self.extended_types[cls] = extended
|
|
217
|
-
|
|
218
|
-
ref = self.writer.handle(ExtendedRef(name = cls.__name__,
|
|
219
|
-
module = cls.__module__))
|
|
220
|
-
|
|
221
|
-
# for binding?
|
|
222
|
-
# self.writer(ref)
|
|
223
|
-
|
|
224
|
-
self.writer.type_serializer[extended] = functional.constantly(ref)
|
|
225
|
-
|
|
226
|
-
return extended
|
retracesoftware/proxy/replay.py
CHANGED
|
@@ -78,22 +78,6 @@ def on_stack_mismatch(last_matching, record, replay):
|
|
|
78
78
|
|
|
79
79
|
class ReplayProxySystem(ProxySystem):
|
|
80
80
|
|
|
81
|
-
# def stubtype(self, cls):
|
|
82
|
-
# assert not issubclass(cls, Proxy)
|
|
83
|
-
|
|
84
|
-
# return dynamic_proxytype(handler = self.ext_handler, cls = cls)
|
|
85
|
-
|
|
86
|
-
def create_stub(self): return True
|
|
87
|
-
|
|
88
|
-
# def stubtype_from_spec(self, spec):
|
|
89
|
-
# print (f'FOOO!!! {spec}')
|
|
90
|
-
# return stubtype_from_spec(
|
|
91
|
-
# handler = self.ext_handler,
|
|
92
|
-
# module = spec.module,
|
|
93
|
-
# name = spec.name,
|
|
94
|
-
# methods = spec.methods,
|
|
95
|
-
# members = spec.members)
|
|
96
|
-
|
|
97
81
|
@utils.striptraceback
|
|
98
82
|
def next_result(self):
|
|
99
83
|
while True:
|
|
@@ -113,6 +97,7 @@ class ReplayProxySystem(ProxySystem):
|
|
|
113
97
|
return self.messages()
|
|
114
98
|
|
|
115
99
|
elif next == 'ERROR':
|
|
100
|
+
# breakpoint()
|
|
116
101
|
err_type = self.messages()
|
|
117
102
|
err_value = self.messages()
|
|
118
103
|
utils.raise_exception(err_type, err_value)
|
|
@@ -152,6 +137,66 @@ class ReplayProxySystem(ProxySystem):
|
|
|
152
137
|
def basetype(self, cls):
|
|
153
138
|
return self.stub_factory.create_stubtype(StubRef(cls))
|
|
154
139
|
|
|
140
|
+
|
|
141
|
+
def readnext(self):
|
|
142
|
+
with self.thread_state.select('disabled'):
|
|
143
|
+
try:
|
|
144
|
+
# obj = self.messages()
|
|
145
|
+
# print(f'read {obj}')
|
|
146
|
+
# return obj
|
|
147
|
+
return self.messages()
|
|
148
|
+
except Exception as error:
|
|
149
|
+
# print(f'Error reading stream: {error}')
|
|
150
|
+
traceback.print_exc()
|
|
151
|
+
os._exit(1)
|
|
152
|
+
|
|
153
|
+
def read_required(self, required):
|
|
154
|
+
obj = self.readnext()
|
|
155
|
+
if obj != required:
|
|
156
|
+
if self.last_matching_stack:
|
|
157
|
+
for line in self.last_matching_stack:
|
|
158
|
+
print(line)
|
|
159
|
+
|
|
160
|
+
print('---------------------------------')
|
|
161
|
+
print(f'Replay: {required}')
|
|
162
|
+
print('---------------------------------')
|
|
163
|
+
print(f'Record: {obj}')
|
|
164
|
+
print('---------------------------------')
|
|
165
|
+
for i in range(15):
|
|
166
|
+
print(self.readnext())
|
|
167
|
+
|
|
168
|
+
breakpoint()
|
|
169
|
+
os._exit(1)
|
|
170
|
+
raise Exception(f'Expected: {required} but got: {obj}')
|
|
171
|
+
|
|
172
|
+
self.last_matching_stack = utils.stacktrace()
|
|
173
|
+
|
|
174
|
+
def trace_writer(self, name, *args):
|
|
175
|
+
with self.thread_state.select('disabled'):
|
|
176
|
+
# read = self.messages_read
|
|
177
|
+
|
|
178
|
+
self.read_required('TRACE')
|
|
179
|
+
# self.read_required(read)
|
|
180
|
+
self.read_required(name)
|
|
181
|
+
|
|
182
|
+
if name == 'stacktrace':
|
|
183
|
+
record = self.readnext()
|
|
184
|
+
if args[0] == record:
|
|
185
|
+
self.last_matching_stack = args[0]
|
|
186
|
+
else:
|
|
187
|
+
on_stack_mismatch(
|
|
188
|
+
last_matching = self.last_matching_stack,
|
|
189
|
+
record = record,
|
|
190
|
+
replay = args[0])
|
|
191
|
+
os._exit(1)
|
|
192
|
+
else:
|
|
193
|
+
# print(f'Trace: {self.reader.messages_read} {name} {args}')
|
|
194
|
+
for arg in args:
|
|
195
|
+
self.read_required(arg)
|
|
196
|
+
|
|
197
|
+
def sync(self, function):
|
|
198
|
+
return utils.observer(on_call = functional.always(self.read_sync), function = function)
|
|
199
|
+
|
|
155
200
|
def __init__(self,
|
|
156
201
|
thread_state,
|
|
157
202
|
immutable_types,
|
|
@@ -159,152 +204,29 @@ class ReplayProxySystem(ProxySystem):
|
|
|
159
204
|
path,
|
|
160
205
|
fork_path = []):
|
|
161
206
|
|
|
162
|
-
# self.
|
|
163
|
-
|
|
164
|
-
reader = stream.reader(path)
|
|
207
|
+
# self.messages_read = 0
|
|
208
|
+
self.reader = stream.reader(path)
|
|
165
209
|
|
|
166
210
|
self.bindings = utils.id_dict()
|
|
167
211
|
self.set_thread_id = utils.set_thread_id
|
|
168
212
|
self.fork_path = fork_path
|
|
169
213
|
deserialize = functional.walker(self.bindings.get_else_key)
|
|
170
214
|
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
# messages = reader
|
|
176
|
-
|
|
177
|
-
def readnext():
|
|
178
|
-
with thread_state.select('disabled'):
|
|
179
|
-
try:
|
|
180
|
-
return self.messages()
|
|
181
|
-
except Exception as error:
|
|
182
|
-
# print(f'Error reading stream: {error}')
|
|
183
|
-
traceback.print_exc()
|
|
184
|
-
|
|
185
|
-
os._exit(1)
|
|
186
|
-
|
|
187
|
-
# print(f'read: {obj}')
|
|
188
|
-
# return obj
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
# lookup = weakref.WeakKeyDictionary()
|
|
215
|
+
# def count(res):
|
|
216
|
+
# self.messages_read += 1
|
|
217
|
+
# return res
|
|
192
218
|
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
# int_refs = {}
|
|
196
|
-
last_matching_stack = None
|
|
197
|
-
|
|
198
|
-
def read_required(required):
|
|
199
|
-
obj = readnext()
|
|
200
|
-
if obj != required:
|
|
201
|
-
if last_matching_stack:
|
|
202
|
-
for line in last_matching_stack:
|
|
203
|
-
print(line)
|
|
204
|
-
|
|
205
|
-
print('---------------------------------')
|
|
206
|
-
print(f'Replay: {required}')
|
|
207
|
-
print('---------------------------------')
|
|
208
|
-
print(f'Record: {obj}')
|
|
209
|
-
print('---------------------------------')
|
|
210
|
-
for i in range(5):
|
|
211
|
-
print(readnext())
|
|
212
|
-
|
|
213
|
-
utils.sigtrap(None)
|
|
214
|
-
os._exit(1)
|
|
215
|
-
raise Exception(f'Expected: {required} but got: {obj}')
|
|
216
|
-
|
|
217
|
-
def trace_writer(name, *args):
|
|
218
|
-
with thread_state.select('disabled'):
|
|
219
|
-
read_required('TRACE')
|
|
220
|
-
read_required(name)
|
|
221
|
-
|
|
222
|
-
if name == 'stacktrace':
|
|
223
|
-
record = readnext()
|
|
224
|
-
if args[0] == record:
|
|
225
|
-
nonlocal last_matching_stack
|
|
226
|
-
last_matching_stack = args[0]
|
|
227
|
-
else:
|
|
228
|
-
on_stack_mismatch(
|
|
229
|
-
last_matching= last_matching_stack,
|
|
230
|
-
record = record,
|
|
231
|
-
replay = args[0])
|
|
232
|
-
os._exit(1)
|
|
233
|
-
else:
|
|
234
|
-
print(f'Trace: {name} {args}')
|
|
235
|
-
for arg in args:
|
|
236
|
-
read_required(arg)
|
|
237
|
-
|
|
238
|
-
# self.tracer = Tracer(tracing_config, writer = trace_writer)
|
|
239
|
-
# self.immutable_types = immutable_types
|
|
240
|
-
|
|
241
|
-
self.reader = reader
|
|
242
|
-
|
|
243
|
-
# def foo(cls):
|
|
244
|
-
# print(cls)
|
|
245
|
-
# assert isinstance(cls, type)
|
|
246
|
-
# immutable_types.add(cls)
|
|
247
|
-
|
|
248
|
-
# add_stubtype = functional.side_effect(foo)
|
|
249
|
-
# add_stubtype = functional.side_effect(immutable_types.add)
|
|
250
|
-
|
|
251
|
-
# reader.type_deserializer[ProxyRef] = functional.sequence(lambda ref: ref.resolve(), self.stubtype, add_stubtype)
|
|
252
|
-
|
|
253
|
-
reader.type_deserializer[StubRef] = self.stub_factory
|
|
254
|
-
# reader.type_deserializer[ProxySpec] = functional.sequence(self.stubtype_from_spec, add_stubtype)
|
|
219
|
+
self.messages = functional.sequence(per_thread_messages(self.reader),
|
|
220
|
+
deserialize)
|
|
255
221
|
|
|
256
|
-
|
|
257
|
-
# functional.is_instanceof(str), writer.handle('RESULT'), writer)
|
|
258
|
-
|
|
259
|
-
# def int_proxytype(gateway):
|
|
260
|
-
# return lambda cls: dynamic_int_proxytype(handler = gateway, cls = cls, bind = self.bind)
|
|
261
|
-
|
|
262
|
-
# create_stubs = functional.walker(functional.when(is_stub_ref, lambda stub: stub.create()))
|
|
263
|
-
# create_stubs = functional.walker(functional.when(is_stub_type, lambda cls: cls()))
|
|
222
|
+
self.stub_factory = StubFactory(thread_state = thread_state, next_result = self.next_result)
|
|
264
223
|
|
|
265
|
-
|
|
266
|
-
# self.ext_apply = functional.repeatedly(self.next_result)
|
|
267
|
-
|
|
268
|
-
def read_sync(): read_required('SYNC')
|
|
224
|
+
self.last_matching_stack = None
|
|
269
225
|
|
|
270
|
-
self.
|
|
226
|
+
self.reader.type_deserializer[StubRef] = self.stub_factory
|
|
227
|
+
|
|
228
|
+
self.read_sync = functional.always(lambda: self.read_required('SYNC'))
|
|
271
229
|
|
|
272
230
|
super().__init__(thread_state = thread_state,
|
|
273
|
-
tracer = Tracer(tracing_config, writer = trace_writer),
|
|
231
|
+
tracer = Tracer(tracing_config, writer = self.trace_writer),
|
|
274
232
|
immutable_types = immutable_types)
|
|
275
|
-
|
|
276
|
-
# super().__init__(
|
|
277
|
-
# thread_state=thread_state,
|
|
278
|
-
# immutable_types= immutable_types,
|
|
279
|
-
# tracer=self.tracer,
|
|
280
|
-
# ext_apply = ext_apply)
|
|
281
|
-
|
|
282
|
-
# self.ext_handler, self.int_handler = gateway_pair(
|
|
283
|
-
# thread_state,
|
|
284
|
-
# self.tracer,
|
|
285
|
-
# immutable_types = immutable_types,
|
|
286
|
-
# ext_apply = ext_apply,
|
|
287
|
-
# int_proxytype = int_proxytype,
|
|
288
|
-
# ext_proxytype = functional.identity)
|
|
289
|
-
|
|
290
|
-
# def extend_type(self, base):
|
|
291
|
-
|
|
292
|
-
# # ok, how to provide __getattr__ style access,
|
|
293
|
-
|
|
294
|
-
# extended = extending_proxytype(
|
|
295
|
-
# cls = base,
|
|
296
|
-
# thread_state = self.thread_state,
|
|
297
|
-
# int_handler = self.int_handler,
|
|
298
|
-
# ext_handler = self.ext_handler,
|
|
299
|
-
# on_subclass_new = self.bind,
|
|
300
|
-
# is_stub = True)
|
|
301
|
-
|
|
302
|
-
# self.immutable_types.add(extended)
|
|
303
|
-
# # proxytype = extending_proxytype(base)
|
|
304
|
-
|
|
305
|
-
# # make_extensible(cls = extended,
|
|
306
|
-
# # int_handler = self.int_handler,
|
|
307
|
-
# # ext_handler = self.ext_handler,
|
|
308
|
-
# # on_new = self.reader.supply)
|
|
309
|
-
|
|
310
|
-
# return extended
|
|
@@ -17,19 +17,15 @@ class StubRef:
|
|
|
17
17
|
blacklist = ['__class__', '__dict__', '__module__', '__doc__', '__new__']
|
|
18
18
|
|
|
19
19
|
methods = []
|
|
20
|
-
members = []
|
|
21
20
|
|
|
22
21
|
for key,value in cls.__dict__.items():
|
|
23
22
|
if key not in blacklist:
|
|
24
23
|
if utils.is_method_descriptor(value):
|
|
25
24
|
methods.append(key)
|
|
26
|
-
elif not key.startswith("__retrace"):
|
|
27
|
-
members.append(key)
|
|
28
25
|
|
|
29
26
|
self.name = cls.__name__
|
|
30
27
|
self.module = cls.__module__
|
|
31
28
|
self.methods = methods
|
|
32
|
-
self.members = members
|
|
33
29
|
|
|
34
30
|
# def __init__(self, module, name, methods, members):
|
|
35
31
|
# self.name = name
|
|
@@ -38,7 +34,7 @@ class StubRef:
|
|
|
38
34
|
# self.members = members
|
|
39
35
|
|
|
40
36
|
def __str__(self):
|
|
41
|
-
return f'StubRef(module = {self.module}, name = {self.name}, methods = {self.methods}
|
|
37
|
+
return f'StubRef(module = {self.module}, name = {self.name}, methods = {self.methods})'
|
|
42
38
|
|
|
43
39
|
def resolve(module, name):
|
|
44
40
|
try:
|
|
@@ -103,7 +99,7 @@ class StubFactory:
|
|
|
103
99
|
next_result = self.thread_state.dispatch(disabled, external = self.next_result)
|
|
104
100
|
|
|
105
101
|
return StubMemberDescriptor(name = name, next_result = next_result)
|
|
106
|
-
|
|
102
|
+
|
|
107
103
|
def create_method(self, name):
|
|
108
104
|
|
|
109
105
|
def disabled(*args, **kwargs):
|
|
@@ -133,8 +129,24 @@ class StubFactory:
|
|
|
133
129
|
slots[method] = self.create_method(method)
|
|
134
130
|
assert utils.is_method_descriptor(slots[method])
|
|
135
131
|
|
|
136
|
-
|
|
137
|
-
|
|
132
|
+
def getattr(instance, name):
|
|
133
|
+
if self.thread_state.value == 'external':
|
|
134
|
+
return self.next_result()
|
|
135
|
+
else:
|
|
136
|
+
print(f'Error trying to get attribute: {name}, when retrace mode: {self.thread_state.value} was not external')
|
|
137
|
+
utils.sigtrap(None)
|
|
138
|
+
os._exit(1)
|
|
139
|
+
|
|
140
|
+
def setattr(instance, name, value):
|
|
141
|
+
if self.thread_state.value == 'external':
|
|
142
|
+
return self.next_result()
|
|
143
|
+
else:
|
|
144
|
+
print(f'Error trying to set attribute: {name}, to: {value} when retrace mode: {self.thread_state.value} was not external')
|
|
145
|
+
utils.sigtrap(None)
|
|
146
|
+
os._exit(1)
|
|
147
|
+
|
|
148
|
+
slots['__getattr__'] = getattr
|
|
149
|
+
slots['__setattr__'] = setattr
|
|
138
150
|
|
|
139
151
|
resolved = resolve(spec.module, spec.name)
|
|
140
152
|
|
|
@@ -1,27 +1,27 @@
|
|
|
1
1
|
retracesoftware/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
2
|
-
retracesoftware/config.json,sha256=
|
|
2
|
+
retracesoftware/config.json,sha256=c-bqr6SAuIGpnNRqZLPKW-0-VtG8VyGnqpQPhyc5CFY,8666
|
|
3
3
|
retracesoftware/install/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
4
4
|
retracesoftware/install/config.py,sha256=EzE5ifQF2lo--hu2njI4T0FJ-zlnWDJV6i7x0DMkVTw,1364
|
|
5
5
|
retracesoftware/install/edgecases.py,sha256=NR3lyvad9sRsyeDv_Ya8V4xMgPsMPOi9rMcnFOJGOEA,6330
|
|
6
6
|
retracesoftware/install/globals.py,sha256=F8XvIoZQQ10gSRalk30dvdKllxlwxkaggYY6FogLDxY,510
|
|
7
7
|
retracesoftware/install/install.py,sha256=HCD_ji8XCr96b5fNzNdL_8qcEp0Jf05Em7T6GA6u8HU,4969
|
|
8
|
-
retracesoftware/install/patcher.py,sha256=
|
|
8
|
+
retracesoftware/install/patcher.py,sha256=VXYzE01SyDDw3wQ-spZRb5HEcH3MOlod56039zhpJIE,18888
|
|
9
9
|
retracesoftware/install/predicate.py,sha256=tX7NQc0rGkyyHYO3mduYHcJHbw1wczT53m_Dpkzo6do,2679
|
|
10
10
|
retracesoftware/install/record.py,sha256=tseF_jV4k4HLPTgBPJdjcahl4EQqagoiisMAdGNC52Q,3257
|
|
11
11
|
retracesoftware/install/references.py,sha256=A-G651IDOfuo00MkbAdpbIQh_15ChvJ7uAVTSmE6zd4,1721
|
|
12
12
|
retracesoftware/install/replay.py,sha256=VUiHvQK3mgAJEGmtE2TFs9kXzxdWtsjibEcGkhZVCVE,1830
|
|
13
|
-
retracesoftware/install/tracer.py,sha256=
|
|
13
|
+
retracesoftware/install/tracer.py,sha256=cHEjiVxIp2iVTJEWndwSbMuiXVyGJQxJYZSGrfpSbCw,5723
|
|
14
14
|
retracesoftware/install/typeutils.py,sha256=_a1PuwdCsYjG1Nkd77V-flqYtwbD4RkJVKn6Z-xABL4,1813
|
|
15
15
|
retracesoftware/proxy/__init__.py,sha256=ZlDZIuUmKFsE9Tvfd2EKGabTepqv8nrbr5pQhCM3IKc,193
|
|
16
16
|
retracesoftware/proxy/gateway.py,sha256=xESohWXkiNm4ZutU0RgWUwxjxcBWRQ4rQyxIGQXv_F4,1590
|
|
17
17
|
retracesoftware/proxy/proxyfactory.py,sha256=qhOqDfMJnLDNkQs26JqDB431MwjjRhGQi8xupJ45asg,12272
|
|
18
|
-
retracesoftware/proxy/proxysystem.py,sha256=
|
|
19
|
-
retracesoftware/proxy/proxytype.py,sha256=
|
|
20
|
-
retracesoftware/proxy/record.py,sha256=
|
|
21
|
-
retracesoftware/proxy/replay.py,sha256=
|
|
22
|
-
retracesoftware/proxy/stubfactory.py,sha256=
|
|
18
|
+
retracesoftware/proxy/proxysystem.py,sha256=Nf_yKa4gMvKA2RWEUBmyH9m46lbsAdyPzj5jVDh4cs8,7427
|
|
19
|
+
retracesoftware/proxy/proxytype.py,sha256=crq7idJP77Magtv58l_uJCAlTnD2aDTT-Uz14raTSIo,12767
|
|
20
|
+
retracesoftware/proxy/record.py,sha256=vtNsoTHP5gsBVJ6dTGHR7QXHGbKm0eONfGcXOb9fhFk,4696
|
|
21
|
+
retracesoftware/proxy/replay.py,sha256=m4Buxlix09aUJX9JYWG1z4jW8Yvbo2zBV353hsZE5qY,7670
|
|
22
|
+
retracesoftware/proxy/stubfactory.py,sha256=CnP0Sm0tZNwyFIQIPkk7w_soNBDcflNKwICsAoZxSD0,5128
|
|
23
23
|
retracesoftware/proxy/thread.py,sha256=-SvnyVbANkmX2lLRpOvFtkpdpAoF6DhnnYdOOs7Q8vo,1379
|
|
24
|
-
retracesoftware_proxy-0.1.
|
|
25
|
-
retracesoftware_proxy-0.1.
|
|
26
|
-
retracesoftware_proxy-0.1.
|
|
27
|
-
retracesoftware_proxy-0.1.
|
|
24
|
+
retracesoftware_proxy-0.1.9.dist-info/METADATA,sha256=yiJDDJYQ4XQhjSaQq4PPZaW1dH7SHtn7m-Dfbk_CrUs,202
|
|
25
|
+
retracesoftware_proxy-0.1.9.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
26
|
+
retracesoftware_proxy-0.1.9.dist-info/top_level.txt,sha256=hYHsR6txLidmqvjBMITpIHvmJJbmoCAgr76-IpZPRz8,16
|
|
27
|
+
retracesoftware_proxy-0.1.9.dist-info/RECORD,,
|
|
File without changes
|
{retracesoftware_proxy-0.1.7.dist-info → retracesoftware_proxy-0.1.9.dist-info}/top_level.txt
RENAMED
|
File without changes
|