retracesoftware-proxy 0.1.22__py3-none-any.whl → 0.2.0__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.
@@ -1,678 +1,122 @@
1
- import inspect
2
- # from retrace_utils import _intercept
3
- # import _intercept
4
- import types
5
- import re
6
- import sys
7
- import pdb
8
- import builtins
9
- import types
10
- import functools
11
- import traceback
12
- import functools
13
- import importlib
14
- import gc
15
- import os
16
- import atexit
17
- import threading
18
- from types import SimpleNamespace, MethodType
19
-
1
+ import enum
20
2
  from retracesoftware.install.typeutils import modify
21
- # from retracesoftware.proxy.immutabletypes import ImmutableTypes
22
- from retracesoftware.install.record import record_system
23
- from retracesoftware.install.replay import replay_system
24
- from retracesoftware.install.config import load_config
25
-
26
- from functools import wraps
27
-
28
- import retracesoftware.functional as functional
3
+ from retracesoftware.install.replace import update
4
+ import threading
5
+ import sys
29
6
  import retracesoftware.utils as utils
30
-
31
- from retracesoftware.install import edgecases
32
- from functools import partial
33
- from retracesoftware.proxy.thread import start_new_thread_wrapper
34
-
35
- # from retrace_utils.intercept import proxy
36
-
37
- # from retrace_utils.intercept.typeutils import *
38
- # from retrace_utils.intercept.proxytype import DynamicProxyFactory, proxytype
39
-
40
- from retracesoftware.install.predicate import PredicateBuilder
41
-
42
- def find_attr(mro, name):
43
- for cls in mro:
44
- if name in cls.__dict__:
45
- return cls.__dict__[name]
46
-
47
- def is_descriptor(obj):
48
- return hasattr(obj, '__get__') or hasattr(obj, '__set__') or hasattr(obj, '__delete__')
49
-
50
- # default_exclude = ['__class__', '__getattribute__', '__init_subclass__', '__dict__', '__del__', '__new__']
51
-
52
- def is_function_type(cls):
53
- return issubclass(cls, types.BuiltinFunctionType) or issubclass(cls, types.FunctionType)
54
-
55
- def select_keys(keys, dict):
56
- return {key: dict[key] for key in keys if key in dict}
57
-
58
- def map_values(f, dict):
59
- return {key: f(value) for key,value in dict.items()}
60
-
61
- def common_keys(dict, *dicts):
62
- common_keys = utils.set(dict)
63
- for d in dicts:
64
- common_keys &= d.keys()
65
-
66
- assert isinstance(common_keys, utils.set)
67
-
68
- return common_keys
69
-
70
- def intersection(*dicts):
71
- return { key: tuple(d[key] for d in dicts) for key in common_keys(*dicts) }
72
-
73
- def intersection_apply(f, *dicts):
74
- return map_values(lambda vals: f(*vals), intersection(*dicts))
7
+ import retracesoftware.functional as functional
8
+ import importlib
75
9
 
76
10
  def resolve(path):
77
11
  module, sep, name = path.rpartition('.')
78
- if module == None: module = 'builtins'
12
+
13
+ if module is None:
14
+ module = 'builtins'
79
15
 
80
16
  return getattr(importlib.import_module(module), name)
81
17
 
82
- # def sync(spec, module_dict):
83
- # for name,properties in spec.items():
84
-
85
- # cls = module_dict[name]
86
-
87
- # orig_init = cls.__init__
88
-
89
- # def __init__(inst, *args, **kwargs):
90
- # orig_init(inst, *args, **kwargs)
91
- # for prop in properties:
92
- # self.system.add_sync(getattr(inst, prop))
93
-
94
18
  def replace(replacements, coll):
95
19
  return map(lambda x: replacements.get(x, x), coll)
20
+
21
+ def patch_class(transforms, cls):
22
+ with modify(cls):
23
+ for attr,transform in transforms.items():
24
+ utils.update(cls, attr, resolve(transform))
96
25
 
97
- def container_replace(container, old, new):
98
- if isinstance(container, dict):
99
- if old in container:
100
- elem = container.pop(old)
101
- container[new] = elem
102
- container_replace(container, old, new)
103
- else:
104
- for key,value in container.items():
105
- if key != '__retrace_unproxied__' and value is old:
106
- container[key] = new
107
- return True
108
- elif isinstance(container, list):
109
- for i,value in enumerate(container):
110
- if value is old:
111
- container[i] = new
112
- return True
113
- elif isinstance(container, set):
114
- container.remove(old)
115
- container.add(new)
116
- return True
117
- else:
118
- return False
119
-
120
- def phase(func):
121
- func.is_phase = True # add marker attribute
122
- return func
123
-
124
- def patch(func):
125
- @wraps(func)
126
- def wrapper(self, spec, mod_dict):
127
- if isinstance(spec, str):
128
- return wrapper(self, [spec], mod_dict)
129
- elif isinstance(spec, list):
130
- res = {}
131
- for name in spec:
132
- if name in mod_dict:
133
- value = func(self, mod_dict[name])
134
- if value is not None:
135
- res[name] = value
136
- return res
137
- elif isinstance(spec, dict):
138
- # return {name: func(self, mod_dict[name], value) for name, value in spec.items() if name in mod_dict}
139
- res = {}
140
- for name,value in spec.items():
141
- if name in mod_dict:
142
- value = func(self, mod_dict[name], value)
143
- if value is not None:
144
- res[name] = value
145
- return res
146
- else:
147
- raise Exception('TODO')
148
-
149
- wrapper.is_phase = True
150
- return wrapper
151
-
152
- def superdict(cls):
153
- result = {}
154
- for cls in list(reversed(cls.__mro__))[1:]:
155
- result.update(cls.__dict__)
156
-
157
- return result
158
-
159
- # def is_method_descriptor(obj):
160
- # return isinstance(obj, types.FunctionType) or \
161
- # (isinstance(obj, (types.WrapperDescriptorType, types.MethodDescriptorType)) and obj.__objclass__ != object)
162
-
163
- def wrap_method_descriptors(wrapper, prefix, base):
164
- slots = {"__slots__": () }
165
-
166
- extended = type(f'{prefix}.{base.__module__}.{base.__name__}', (base,), {"__slots__": () })
167
-
168
- blacklist = ['__getattribute__', '__hash__', '__del__']
169
-
170
- for name,value in superdict(base).items():
171
- if name not in blacklist:
172
- if utils.is_method_descriptor(value):
173
- setattr(extended, name, wrapper(value))
174
-
175
- return extended
26
+ return cls
176
27
 
177
28
  class PerThread(threading.local):
178
29
  def __init__(self):
179
30
  self.internal = utils.counter()
180
- self.externak = utils.counter()
181
-
182
- class Patcher:
183
-
184
- def __init__(self, thread_state, config, system,
185
- immutable_types,
186
- on_function_proxy = None,
187
- debug_level = 0,
188
- post_commit = None):
189
-
190
- # validate(config)
191
- system.set_thread_id(0)
192
- # utils.set_thread_id(0)
193
- self.thread_counter = system.sync(utils.counter(1))
194
- # self.set_thread_number = set_thread_number
195
-
196
- self.thread_state = thread_state
197
- self.debug_level = debug_level
198
- self.on_function_proxy = on_function_proxy
199
- self.modules = config['modules']
200
- self.immutable_types_set = immutable_types
201
- self.predicate = PredicateBuilder()
202
- self.system = system
203
- self.type_attribute_filter = self.predicate(config['type_attribute_filter'])
204
- self.post_commit = post_commit
205
- self.exclude_paths = [re.compile(s) for s in config.get('exclude_paths', [])]
206
- self.typepatcher = {}
207
-
208
- per_thread = PerThread()
209
-
210
- self.hashfunc = self.thread_state.dispatch(
211
- functional.constantly(None),
212
- internal = functional.repeatedly(functional.partial(getattr, per_thread, 'internal')),
213
- external = functional.repeatedly(functional.partial(getattr, per_thread, 'external')))
214
-
215
- def is_phase(name): return getattr(getattr(self, name, None), "is_phase", False)
216
-
217
- self.phases = [(name, getattr(self, name)) for name in Patcher.__dict__.keys() if is_phase(name)]
218
-
219
- def log(self, *args):
220
- self.system.tracer.log(*args)
31
+ self.external = utils.counter()
221
32
 
222
- def path_predicate(self, path):
223
- for exclude in self.exclude_paths:
224
- if exclude.match(str(path)) is not None:
225
- # print(f'in path_predicate, excluding {path}')
226
- return False
227
- return True
33
+ def create_patcher(system):
228
34
 
229
- def on_proxytype(self, cls):
35
+ patcher = {}
230
36
 
231
- def patch(spec):
232
- for method, transform in spec.items():
233
- setattr(cls, method, resolve(transform)(getattr(cls, method)))
37
+ def foreach(func): return lambda config: {name: func for name in config}
38
+ def selector(func): return lambda config: {name: functional.partial(func, value) for name, value in config.items()}
234
39
 
235
- if cls.__module__ in self.modules:
236
- spec = self.modules[cls.__module__]
40
+ def simple_patcher(func): return foreach(functional.side_effect(func))
237
41
 
238
- if 'patchtype' in spec:
239
- patchtype = spec['patchtype']
240
- if cls.__name__ in patchtype:
241
- patch(patchtype[cls.__name__])
42
+ def type_attributes(transforms, cls):
43
+ with modify(cls):
44
+ for action, attrs in transforms.items():
45
+ for attr,func in patcher[action](attrs).items():
46
+ utils.update(cls, attr, func)
47
+ return cls
242
48
 
243
- @property
244
- def disable(self):
245
- return self.thread_state.select('disabled')
246
-
247
- def proxyable(self, name, obj):
248
- if name.startswith('__') and name.endswith('__'):
249
- return False
250
-
251
- if isinstance(obj, (str, int, dict, list, tuple)):
252
- return False
253
-
254
- if isinstance(obj, type):
255
- return not issubclass(obj, BaseException) and obj not in self.immutable_types_set
49
+ def bind(obj):
50
+ if issubclass(obj, enum.Enum):
51
+ for member in obj:
52
+ system.bind(member)
256
53
  else:
257
- return type(obj) not in self.immutable_types_set
258
-
259
- @phase
260
- def immutable_types(self, spec, mod_dict):
261
- if isinstance(spec, str):
262
- return self.immutable_types([spec], mod_dict)
263
-
264
- for name in spec:
265
- if name in mod_dict:
266
- if isinstance(mod_dict[name], type):
267
- assert isinstance(mod_dict[name], type)
268
- self.immutable_types_set.add(mod_dict[name])
269
- else:
270
- raise Exception(f'Tried to add "{name}" - {mod_dict[name]} which isn\'t a type to immutable')
271
-
272
- @patch
273
- def proxy(self, value):
274
- return self.system(value)
275
-
276
- @phase
277
- def proxy_all_except(self, spec, mod_dict):
278
-
279
- all_except = set(spec)
280
-
281
- def proxyable(name, value):
282
- return name not in all_except and self.proxyable(name, value)
283
-
284
- return {key: self.system(value) for key,value in mod_dict.items() if proxyable(key, value)}
285
-
286
- @phase
287
- def proxy_type_attributes(self, spec, mod_dict):
288
- for classname, attributes in spec.items():
289
- if classname in mod_dict:
290
- cls = mod_dict[classname]
291
- if isinstance(cls, type):
292
- for name in attributes:
293
- attr = find_attr(cls.__mro__, name)
294
- if attr is not None and (callable(attr) or is_descriptor(attr)):
295
- proxied = self.system(attr)
296
- # proxied = self.proxy(attr)
297
-
298
- with modify(cls):
299
- setattr(cls, name, proxied)
300
- else:
301
- raise Exception(f"Cannot patch attributes for {cls.__module__}.{cls.__name__} as object is: {cls} and not a type")
302
-
303
- @phase
304
- def replace(self, spec, mod_dict):
305
- return {key: resolve(value) for key,value in spec.items()}
306
-
307
- @patch
308
- def patch_start_new_thread(self, value):
309
- return start_new_thread_wrapper(thread_state = self.thread_state,
310
- on_exit = self.system.on_thread_exit,
311
- start_new_thread = value)
312
-
313
- # def start_new_thread(function, *args):
314
- # # synchronized, replay shoudl yield correct number
315
- # thread_id = self.thread_counter()
316
-
317
- # def threadrunner(*args, **kwargs):
318
- # nonlocal thread_id
319
- # self.system.set_thread_id(thread_id)
320
-
321
- # with self.thread_state.select('internal'):
322
- # try:
323
- # # if self.tracing:
324
- # # FrameTracer.install(self.thread_state.dispatch(noop, internal = self.checkpoint))
325
- # return function(*args, **kwargs)
326
- # finally:
327
- # print(f'exiting: {thread_id}')
328
-
329
- # return value(threadrunner, *args)
330
-
331
- # return self.thread_state.dispatch(value, internal = start_new_thread)
332
-
333
- @phase
334
- def wrappers(self, spec, mod_dict):
335
- return intersection_apply(lambda path, value: resolve(path)(value), spec, mod_dict)
336
-
337
- @patch
338
- def patch_exec(self, exec):
339
-
340
- def is_module(source, *args):
341
- return isinstance(source, types.CodeType) and source.co_name == '<module>'
342
-
343
- def after_exec(source, globals = None, locals = None):
344
- self(sys.modules[globals['__name__']])
345
-
346
- def first(x): return x[0]
347
-
348
- def disable(func): return self.thread_state.wrap('disabled', func)
349
-
350
- return self.thread_state.dispatch(
351
- exec,
352
- internal = functional.sequence(
353
- functional.vector(exec, functional.when(is_module, disable(after_exec))), first))
354
-
355
- # self.thread_state.wrap(desired_state = 'disabled', function = exec_wrapper)
356
-
357
- @patch
358
- def sync_types(self, value):
359
- return wrap_method_descriptors(self.system.sync, "retrace", value)
360
-
361
- @phase
362
- def with_state_recursive(self, spec, mod_dict):
363
-
364
- updates = {}
365
-
366
- for state,elems in spec.items():
367
-
368
- def wrap(obj):
369
- return functional.recurive_wrap_function(
370
- functional.partial(self.thread_state.wrap, state),
371
- obj)
372
-
373
- updates.update(map_values(wrap, select_keys(elems, mod_dict)))
374
-
375
- return updates
376
-
377
- @phase
378
- def methods_with_state(self, spec, mod_dict):
379
-
380
- # updates = {}
381
-
382
- def update(cls, name, f):
383
- setattr(cls, name, f(getattr(cls, name)))
384
-
385
- for state,cls_methods in spec.items():
386
- def wrap(obj):
387
- assert callable(obj)
388
- return self.thread_state.wrap(desired_state = state, function = obj)
389
-
390
- for typename,methodnames in cls_methods.items():
391
- cls = mod_dict[typename]
392
-
393
- for methodname in methodnames:
394
- update(cls, methodname, wrap)
395
-
396
- return {}
397
-
398
- @phase
399
- def with_state(self, spec, mod_dict):
400
-
401
- updates = {}
402
-
403
- for state,elems in spec.items():
54
+ system.bind(obj)
404
55
 
405
- def wrap(obj):
406
- return self.thread_state.wrap(desired_state = state, function = obj)
56
+ def add_immutable_type(obj):
57
+ if not isinstance(obj, type):
58
+ raise Exception("TODO")
59
+ system.immutable_types.add(obj)
60
+ return obj
407
61
 
408
- updates.update(map_values(wrap, select_keys(elems, mod_dict)))
62
+ per_thread = PerThread()
409
63
 
410
- return updates
64
+ hashfunc = system.thread_state.dispatch(
65
+ functional.constantly(None),
66
+ internal = functional.repeatedly(functional.partial(getattr, per_thread, 'internal')),
67
+ external = functional.repeatedly(functional.partial(getattr, per_thread, 'external')))
411
68
 
412
- @patch
413
- def patch_hash(self, cls):
414
- utils.patch_hash(cls = cls, hashfunc = self.hashfunc)
69
+ def patch_hash(obj):
70
+ if not isinstance(obj, type):
71
+ raise Exception("TODO")
415
72
 
416
- @patch
417
- def patch_extension_exec(self, exec):
418
-
419
- def first(x): return x[0]
420
-
421
- def disable(func): return self.thread_state.wrap('disabled', func)
73
+ utils.patch_hash(cls = obj, hashfunc = hashfunc)
74
+ return obj
422
75
 
423
- return self.thread_state.dispatch(exec,
424
- internal = functional.sequence(functional.vector(exec, disable(self)), first))
425
-
426
- # def wrapper(module):
427
- # with self.thread_state.select('internal'):
428
- # res = exec(module)
429
-
430
- # self(module)
431
- # return res
432
-
433
- # return wrapper
434
-
435
- @patch
436
- def path_predicates(self, func, param):
437
- signature = inspect.signature(func).parameters
438
-
439
- try:
440
- index = list(signature.keys()).index(param)
441
- except ValueError:
442
- print(f'parameter {param} not in: {signature.keys()} {type(func)} {func}')
443
- raise
444
-
445
- param = functional.param(name = param, index = index)
446
-
447
- assert callable(param)
448
-
449
- return functional.if_then_else(
450
- test = functional.sequence(param, self.path_predicate),
451
- then = func,
452
- otherwise = self.thread_state.wrap('disabled', func))
453
-
454
- @phase
455
- def wrap(self, spec, mod_dict):
456
- updates = {}
457
-
458
- for path, wrapper_name in spec.items():
459
-
460
- parts = path.split('.')
461
- name = parts[0]
462
- if name in mod_dict:
463
- value = mod_dict[name]
464
- assert not isinstance(value, utils.wrapped_function), \
465
- f"value for key: {name} is already wrapped"
466
-
467
- if len(parts) == 1:
468
- updates[name] = resolve(wrapper_name)(value)
469
- elif len(parts) == 2:
470
- member = getattr(value, parts[1], None)
471
- if member:
472
- new_value = resolve(wrapper_name)(member)
473
- setattr(value, parts[1], new_value)
474
- else:
475
- raise Exception('TODO')
476
-
477
- return updates
478
-
479
- def updates(self, spec, mod_dict):
480
- updates = {}
481
-
482
- for phase,func in self.phases:
483
- if phase in spec:
484
- self.log('install.module.phase', phase)
485
-
486
- phase_updates = func(spec[phase], mod_dict | updates)
487
-
488
- if phase_updates:
489
- self.log('install.module.phase.results', list(phase_updates.keys()))
490
- for name,value in phase_updates.items():
491
- if value is not None:
492
- updates[name] = value
493
- # updates |= phase_updates
494
-
495
- return updates
496
-
497
- def configs(self, module):
498
- for name,value in sys.modules.items():
499
- if value is module and name in self.modules:
500
- yield name
501
-
502
- def patch_module_with_name(self, mod_name, module):
503
- with self.disable:
504
-
505
- assert self.thread_state.value == 'disabled'
506
-
507
- self.log('install.module', mod_name)
508
-
509
- # self.system.log(f'patching module: {mod_name}')
510
-
511
- spec = self.modules.get(mod_name)
512
-
513
- updates = self.updates(spec = spec, mod_dict = module.__dict__)
514
-
515
- originals = select_keys(updates.keys(), module.__dict__)
516
-
517
- module.__dict__.update(updates)
518
-
519
- for name, value in originals.items():
520
- for ref in gc.get_referrers(value):
521
- if ref is not originals:
522
- container_replace(container = ref, old = value, new = updates[name])
523
- if isinstance(updates[name], type) and isinstance(value, type) and issubclass(updates[name], value):
524
- for subclass in value.__subclasses__():
525
- if subclass not in updates.values():
526
- subclass.__bases__ = tuple(replace({value: updates[name]}, subclass.__bases__))
527
-
528
- module.__retrace__ = originals
529
-
530
- if self.post_commit:
531
- self.post_commit(mod_name, updates)
532
-
533
- def __call__(self, module):
534
-
535
- if not hasattr(module, '__retrace__'):
536
-
537
- configs = list(self.configs(module))
538
-
539
- if len(configs) > 0:
540
- if len(configs) > 1:
541
- raise Exception(f'TODO')
542
- else:
543
- module.__retrace__ = None
544
- try:
545
- self.patch_module_with_name(configs[0], module)
546
- except Exception as error:
547
- raise Exception(f'Error patching module: {configs[0]}') from error
548
-
549
- return module
550
-
551
- def env_truthy(key, default=False):
552
- value = os.getenv(key)
553
- if value is None:
554
- return default
555
- return value.strip().lower() in ("1", "true", "yes", "on")
556
-
557
- class ImmutableTypes(set):
558
- def __init__(self, *args, **kwargs):
559
- super().__init__(*args, **kwargs)
560
-
561
- def __contains__(self, item):
562
- assert isinstance(item, type)
563
-
564
- if super().__contains__(item):
565
- return True
566
-
567
- for elem in self:
568
- if issubclass(item, elem):
569
- self.add(item)
570
- return True
571
-
572
- return False
573
-
574
- def install(mode):
575
-
576
- create_system = None
577
-
578
- if mode == 'record':
579
- create_system = record_system
580
- elif mode == 'replay':
581
- create_system = replay_system
582
- else:
583
- raise Exception(f'mode: {mode} unsupported')
584
-
585
- importlib.import_module('locale')
586
- importlib.import_module('calendar')
587
- importlib.import_module('_strptime')
588
-
589
- config = load_config('config.json')
590
-
591
- states = [x for x in config['states'] if isinstance(x, str)]
592
-
593
- thread_state = utils.ThreadState(*states)
594
-
595
- immutable_types = ImmutableTypes()
596
-
597
- if 'RETRACE_RECORDING_PATH' in os.environ:
598
- config['recording_path'] = os.environ['RETRACE_RECORDING_PATH']
599
-
600
- # immutable_types = ImmutableTypes(config)
601
- config['verbose'] = env_truthy('RETRACE_VERBOSE')
602
-
603
- system = create_system(thread_state = thread_state,
604
- immutable_types = immutable_types,
605
- config = config)
606
-
607
- os.register_at_fork(before = system.before_fork,
608
- after_in_parent = system.after_fork_in_parent,
609
- after_in_child = system.after_fork_in_child)
610
-
611
- patcher = Patcher(thread_state = thread_state,
612
- config = config,
613
- system = system,
614
- post_commit = getattr(system, 'on_patched', None),
615
- immutable_types = immutable_types)
616
-
617
- system.on_proxytype = patcher.on_proxytype
618
-
619
- def at_exit(): thread_state.value = 'disabled'
620
-
621
- # print(f'MODULES: {list(sys.modules.keys())}')
622
-
623
- importlib.invalidate_caches()
624
-
625
- # with thread_state.select('internal'):
626
-
627
- atexit.register(lambda: at_exit)
628
-
629
- for key, module in list(sys.modules.items()):
630
- if not key.startswith('retracesoftware'):
631
- patcher(module)
632
-
633
- for library in config.get('preload', []):
634
- with thread_state.select('internal'):
635
- importlib.import_module(library)
636
-
637
- importlib.invalidate_caches()
638
- # # if env_truthy('RETRACE_TRACE_CALLS'):
639
- # # trace_calls(system = retracesystem)
640
-
641
- # thread_state.value = 'internal'
642
-
643
- # import threading
644
- # threading.current_thread().retrace = system
645
-
646
- threading.current_thread().__retrace__ = system
647
-
648
- # original = atexit.register
649
-
650
- # def atexit_register(function):
651
- # return original(thread_state.wrap('internal', function))
652
-
653
- def wrap_internal(func): return thread_state.wrap('internal', func)
654
-
655
- atexit.register = \
656
- thread_state.dispatch(atexit.register, internal = functional.sequence(wrap_internal, atexit.register))
657
-
658
- def disable_after_main(frame):
659
- if system.is_entry_frame(frame):
660
- print('enabling retrace!!!!')
661
- # utils.intercept_frame_eval(None)
662
- thread_state.value = 'internal'
663
-
664
- def on_return():
665
- thread_state.value = 'disabled'
666
-
667
- obj = SimpleNamespace()
668
- obj.on_return = on_return
669
-
670
- return obj
76
+ patcher.update({
77
+ 'type_attributes': selector(type_attributes),
78
+ 'patch_class': selector(patch_class),
79
+ 'disable': foreach(system.disable_for),
80
+ 'patch_types': simple_patcher(system.patch_type),
81
+ 'proxy': foreach(system),
82
+ 'bind': simple_patcher(bind),
83
+ 'wrap': lambda config: {name: resolve(action) for name,action in config.items() },
84
+ 'immutable': simple_patcher(add_immutable_type),
85
+ 'patch_hash': simple_patcher(patch_hash),
86
+ })
87
+ return patcher
88
+
89
+ def patch_namespace(patcher, config, namespace, update_refs):
90
+ for phase_name, phase_config in config.items():
91
+ if phase_name in patcher:
92
+ for name,func in patcher[phase_name](phase_config).items():
93
+ if name in namespace:
94
+ # print(f"patching: {name}")
95
+
96
+ value = namespace[name]
97
+ new_value = func(value)
98
+
99
+ if value is not new_value:
100
+ namespace[name] = new_value
101
+
102
+ if update_refs:
103
+ update(value, new_value)
671
104
  else:
672
- return disable_after_main
673
-
674
- utils.intercept_frame_eval(disable_after_main)
675
-
676
- # utils.sigtrap(None)
677
- print(f'retrace installed: {thread_state}')
678
- return system
105
+ print(phase_name)
106
+ utils.sigtrap('FOO1')
107
+
108
+ def patch_module(patcher, config, namespace, update_refs):
109
+ if '__name__' in namespace:
110
+ name = namespace['__name__']
111
+ if name in config:
112
+ patch_namespace(patcher, config = config[name], namespace=namespace, update_refs=update_refs)
113
+
114
+ def patch_imported_module(patcher, checkpoint, config, namespace, update_refs):
115
+ if '__name__' in namespace:
116
+ name = namespace['__name__']
117
+ checkpoint(f"importing module: {name}")
118
+
119
+ if name in config:
120
+ # print(f"Patching imported module: {name}")
121
+ # checkpoint(f"Patching imported module: {name}")
122
+ patch_namespace(patcher, config = config[name], namespace=namespace, update_refs=update_refs)