retracesoftware-proxy 0.1.0__py3-none-any.whl → 0.1.2__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.
@@ -164,6 +164,9 @@
164
164
  "sys": {
165
165
  "with_state": {
166
166
  "disabled": ["excepthook"]
167
+ },
168
+ "with_state_recursive0": {
169
+ "disabled": ["settrace", "setprofile"]
167
170
  }
168
171
  },
169
172
 
@@ -293,6 +296,10 @@
293
296
  "_path_normpath"
294
297
  ],
295
298
 
299
+ "with_state_recursive0": {
300
+ "disabled": ["register_at_fork"]
301
+ },
302
+
296
303
  "wrappers": {
297
304
  "fork_exec": "retracesoftware.install.edgecases.fork_exec",
298
305
  "posix_spawn": "retracesoftware.install.edgecases.posix_spawn"
@@ -58,7 +58,7 @@ class MethodDescriptor:
58
58
  self.cls = descriptor.__objclass__
59
59
  self.name = descriptor.name
60
60
 
61
- def once(*args): return functional.memoize_one_arg(functional.compose(*args))
61
+ def once(*args): return functional.memoize_one_arg(functional.sequence(*args))
62
62
 
63
63
  any = functional.firstof
64
64
 
@@ -121,7 +121,7 @@ def replaying_proxy_factory(thread_state, is_immutable_type, tracer, next, bind,
121
121
  on_call = tracer('proxy.ext.call'),
122
122
  on_result = tracer('proxy.ext.result'),
123
123
  on_error = tracer('proxy.ext.error'),
124
- function = functional.compose(functional.always(next), create_stubs))
124
+ function = functional.sequence(functional.always(next), create_stubs))
125
125
 
126
126
  return ProxyFactory(thread_state = thread_state,
127
127
  is_immutable_type = is_immutable_type,
@@ -131,30 +131,6 @@ def replaying_proxy_factory(thread_state, is_immutable_type, tracer, next, bind,
131
131
  wrap_int_call = wrap_int_call,
132
132
  wrap_ext_call = wrap_ext_call)
133
133
 
134
- def latest_from_pattern(pattern: str) -> str | None:
135
- """
136
- Given a strftime-style filename pattern (e.g. "recordings/%Y%m%d_%H%M%S_%f"),
137
- return the path to the most recent matching file, or None if no files exist.
138
- """
139
- # Turn strftime placeholders into '*' for globbing
140
- # (very simple replacement: %... -> *)
141
- glob_pattern = re.sub(r"%[a-zA-Z]", "*", pattern)
142
-
143
- # Find all matching files
144
- candidates = glob.glob(glob_pattern)
145
- if not candidates:
146
- return None
147
-
148
- # Derive the datetime format from the pattern (basename only)
149
- base_pattern = os.path.basename(pattern)
150
-
151
- def parse_time(path: str):
152
- name = os.path.basename(path)
153
- return datetime.strptime(name, base_pattern)
154
-
155
- # Find the latest by parsed timestamp
156
- latest = max(candidates, key=parse_time)
157
- return latest
158
134
 
159
135
  # class Reader:
160
136
 
@@ -24,7 +24,7 @@ from retracesoftware.install.config import load_config
24
24
 
25
25
  from functools import wraps
26
26
 
27
- import retracesoftware_functional as functional
27
+ import retracesoftware.functional as functional
28
28
  import retracesoftware_utils as utils
29
29
 
30
30
  from retracesoftware.install import edgecases
@@ -158,13 +158,14 @@ def wrap_method_descriptors(wrapper, prefix, base):
158
158
  class Patcher:
159
159
 
160
160
  def __init__(self, thread_state, config, system,
161
- immutable_types,
161
+ immutable_types,
162
162
  on_function_proxy = None,
163
163
  debug_level = 0,
164
164
  post_commit = None):
165
165
 
166
166
  # validate(config)
167
- utils.set_thread_id(0)
167
+ # system.set_thread_id(0)
168
+ # utils.set_thread_id(0)
168
169
  self.thread_counter = system.sync(utils.counter(1))
169
170
  # self.set_thread_number = set_thread_number
170
171
 
@@ -217,6 +218,7 @@ class Patcher:
217
218
  for name in spec:
218
219
  if name in mod_dict:
219
220
  if isinstance(mod_dict[name], type):
221
+ assert isinstance(mod_dict[name], type)
220
222
  self.immutable_types_set.add(mod_dict[name])
221
223
  else:
222
224
  raise Exception(f'Tried to add "{name}" - {mod_dict[name]} which isn\'t a type to immutable')
@@ -264,7 +266,7 @@ class Patcher:
264
266
 
265
267
  def threadrunner(*args, **kwargs):
266
268
  nonlocal thread_id
267
- utils.set_thread_id(thread_id)
269
+ self.system.set_thread_id(thread_id)
268
270
 
269
271
  with self.thread_state.select('internal'):
270
272
  # if self.tracing:
@@ -304,6 +306,23 @@ class Patcher:
304
306
  def sync_types(self, value):
305
307
  return wrap_method_descriptors(self.system.sync, "retrace", value)
306
308
 
309
+ @phase
310
+ def with_state_recursive(self, spec, mod_dict):
311
+
312
+ updates = {}
313
+
314
+ for state,elems in spec.items():
315
+
316
+ def wrap(obj):
317
+ return functional.recursive_wrap(
318
+ callable,
319
+ lambda f: self.thread_state.wrap(state, f),
320
+ obj)
321
+
322
+ updates.update(map_values(wrap, select_keys(elems, mod_dict)))
323
+
324
+ return updates
325
+
307
326
  @phase
308
327
  def with_state(self, spec, mod_dict):
309
328
 
@@ -321,7 +340,9 @@ class Patcher:
321
340
  def patch_extension_exec(self, exec):
322
341
 
323
342
  def wrapper(module):
324
- res = exec(module)
343
+ with self.thread_state.select('internal'):
344
+ res = exec(module)
345
+
325
346
  self(module)
326
347
  return res
327
348
 
@@ -342,7 +363,7 @@ class Patcher:
342
363
  assert callable(param)
343
364
 
344
365
  return functional.if_then_else(
345
- test = functional.compose(param, self.path_predicate),
366
+ test = functional.sequence(param, self.path_predicate),
346
367
  then = func,
347
368
  otherwise = self.thread_state.wrap('disabled', func))
348
369
 
@@ -356,13 +377,14 @@ class Patcher:
356
377
  name = parts[0]
357
378
  if name in mod_dict:
358
379
  value = mod_dict[name]
359
-
380
+ assert not isinstance(value, utils.wrapped_function), f"value for key: {name} is already wrapped"
360
381
  if len(parts) == 1:
361
382
  updates[name] = resolve(wrapper_name)(value)
362
383
  elif len(parts) == 2:
363
384
  member = getattr(value, parts[1], None)
364
385
  if member:
365
- setattr(value, parts[1], resolve(wrapper_name)(member))
386
+ new_value = resolve(wrapper_name)(member)
387
+ setattr(value, parts[1], new_value)
366
388
  else:
367
389
  raise Exception('TODO')
368
390
 
@@ -434,6 +456,23 @@ def env_truthy(key, default=False):
434
456
  return default
435
457
  return value.strip().lower() in ("1", "true", "yes", "on")
436
458
 
459
+ class ImmutableTypes(set):
460
+ def __init__(self, *args, **kwargs):
461
+ super().__init__(*args, **kwargs)
462
+
463
+ def __contains__(self, item):
464
+ assert isinstance(item, type)
465
+
466
+ if super().__contains__(item):
467
+ return True
468
+
469
+ for elem in self:
470
+ if issubclass(item, elem):
471
+ self.add(item)
472
+ return True
473
+
474
+ return False
475
+
437
476
  def install(mode):
438
477
 
439
478
  create_system = None
@@ -451,7 +490,7 @@ def install(mode):
451
490
 
452
491
  thread_state = utils.ThreadState(*states)
453
492
 
454
- immutable_types = set()
493
+ immutable_types = ImmutableTypes()
455
494
 
456
495
  if 'RETRACE_RECORDING_PATH' in os.environ:
457
496
  config['recording_path'] = os.environ['RETRACE_RECORDING_PATH']
@@ -463,6 +502,10 @@ def install(mode):
463
502
  immutable_types = immutable_types,
464
503
  config = config)
465
504
 
505
+ os.register_at_fork(before = system.before_fork,
506
+ after_in_parent = system.after_fork_in_parent,
507
+ after_in_child = system.after_fork_in_child)
508
+
466
509
  patcher = Patcher(thread_state = thread_state,
467
510
  config = config,
468
511
  system = system,
@@ -13,6 +13,10 @@ from datetime import datetime
13
13
  import json
14
14
  from pathlib import Path
15
15
 
16
+ class ThreadSwitch:
17
+ def __init__(self, id):
18
+ self.id = id
19
+
16
20
  def write_files(recording_path):
17
21
  with open(recording_path / 'env', 'w') as f:
18
22
  json.dump(dict(os.environ), f, indent=2)
@@ -38,9 +42,12 @@ def tracing_level(config):
38
42
  # level = os.environ.get('RETRACE_DEBUG', config['default_tracing_level'])
39
43
  # return config['tracing_levels'].get(level, {})
40
44
 
41
- def thread_aware_writer(writer):
42
- on_thread_switch = functional.sequence(utils.thread_id(), writer.handle('THREAD_SWITCH'))
43
- return utils.threadawareproxy(on_thread_switch = on_thread_switch, target = writer)
45
+ def merge_config(base, override):
46
+ if isinstance(base, dict) and isinstance(override, dict):
47
+ ...
48
+ else:
49
+ return override
50
+
44
51
 
45
52
  def record_system(thread_state, immutable_types, config):
46
53
 
@@ -57,7 +64,7 @@ def record_system(thread_state, immutable_types, config):
57
64
  with open(recording_path / 'tracing_config.json', 'w') as f:
58
65
  json.dump(tracing_config, f, indent=2)
59
66
 
60
- writer = thread_aware_writer(stream.writer(path = recording_path / 'trace.bin'))
67
+ # writer = stream.writer(path = recording_path / 'trace.bin')
61
68
 
62
69
  # os.register_at_fork(
63
70
  # # before = self.thread_state.wrap('disabled', self.before_fork),
@@ -86,43 +93,11 @@ def record_system(thread_state, immutable_types, config):
86
93
  # self.gc_end()
87
94
  # gc.callbacks.append(self.gc_hook)
88
95
 
89
- w = writer.handle('TRACE')
90
- def trace_writer(*args):
91
- print(f'Trace: {args}')
92
- w(*args)
93
-
94
96
  # print(f'Tracing config: {tracing_config(config)}')
95
97
 
96
- tracer = Tracer(tracing_config, writer = trace_writer)
97
98
  # tracer = Tracer(config = tracing_config(config), writer = writer.handle('TRACE'))
98
99
 
99
- factory = RecordProxySystem(thread_state = thread_state,
100
- immutable_types = immutable_types,
101
- tracer = tracer,
102
- writer = writer)
103
-
104
- # factory.proxy_type = compose(factory.proxy_type, side_effect(...))
105
-
106
- def on_patched(module_name, updates):
107
- ...
108
- # for name, value in updates.items():
109
- # if isinstance(value, type) and \
110
- # issubclass(value, ExtendingProxy) and \
111
- # not issubclass(value, InternalProxy):
112
-
113
- # ref = writer.handle(ExtendingProxySpec(module = module_name, name = name))
114
- # try:
115
- # writer.add_type_serializer(cls = value, serializer = functional.constantly(ref))
116
- # except:
117
- # pass
118
-
119
- factory.on_patched = on_patched
120
-
121
- factory.sync = lambda function: functional.observer(on_call = functional.always(writer.handle('SYNC')), function = function)
122
-
123
- # factory.set_thread_number = writer.thread_number
124
- factory.tracer = tracer
125
-
126
- factory.log = functional.partial(writer, 'LOG')
127
-
128
- return factory
100
+ return RecordProxySystem(thread_state = thread_state,
101
+ immutable_types = immutable_types,
102
+ tracing_config = tracing_config,
103
+ path = recording_path / 'trace.bin')
@@ -1,12 +1,8 @@
1
1
  from retracesoftware.proxy import *
2
2
 
3
- import retracesoftware.functional as functional
4
- import retracesoftware_utils as utils
5
- import retracesoftware.stream as stream
6
- from retracesoftware.install.tracer import Tracer
7
3
  from retracesoftware.install import globals
8
4
 
9
- import os, json, re, glob, weakref
5
+ import os, json, re, glob
10
6
  from pathlib import Path
11
7
  from datetime import datetime
12
8
 
@@ -35,15 +31,6 @@ def latest_from_pattern(pattern: str) -> str | None:
35
31
  latest = max(candidates, key=parse_time)
36
32
  return latest
37
33
 
38
-
39
-
40
- def thread_aware_reader(reader):
41
- def on_thread_switch():
42
- ...
43
-
44
- return utils.threadawareproxy(on_thread_switch = on_thread_switch, target = reader)
45
-
46
-
47
34
  def replay_system(thread_state, immutable_types, config):
48
35
 
49
36
  recording_path = Path(latest_from_pattern(config['recording_path']))
@@ -61,91 +48,7 @@ def replay_system(thread_state, immutable_types, config):
61
48
  with open(recording_path / "tracing_config.json", "r", encoding="utf-8") as f:
62
49
  tracing_config = json.load(f)
63
50
 
64
- handler = None
65
-
66
- # def create_stub_type(proxyspec):
67
- # if isinstance(proxyspec, ExtendingProxySpec):
68
- # mod = sys.modules[proxyspec.module]
69
- # resolved = getattr(mod, proxyspec.name)
70
- # assert issubclass(resolved, ExtendingProxy)
71
-
72
- # return resolved
73
- # # unproxied = resolved.__base__
74
- # # print(f'base: {unproxied} has_generic_new: {utils.has_generic_new(unproxied)}')
75
- # # print(f'base: {unproxied} has_generic_alloc: {utils.has_generic_alloc(unproxied)}')
76
- # # utils.create_stub_object(resolved)
77
- # # os._exit(1)
78
- # # return resolved
79
-
80
- # elif isinstance(proxyspec, WrappingProxySpec):
81
- # nonlocal handler
82
- # return proxyspec.create_type(handler)
83
- # else:
84
- # print(f'In create_stub_type!!!! {proxyspec}')
85
- # os._exit(1)
86
-
87
- # deserializer = functional.compose(pickle.loads, functional.when_instanceof(ProxySpec, create_stub_type))
88
-
89
- reader = thread_aware_reader(stream.reader(path = recording_path / 'trace.bin'))
90
-
91
- # reader = utils.threadawareproxy(on_thread_switch = ..., target = reader)
92
-
93
- def readnext():
94
- return reader()
95
- # print(f'read: {obj}')
96
- # return obj
97
-
98
- lookup = weakref.WeakKeyDictionary()
99
-
100
- # debug = debug_level(config)
101
-
102
- # int_refs = {}
103
-
104
- def checkpoint(replay):
105
- ...
106
-
107
- def read_required(required):
108
- obj = readnext()
109
- if obj != required:
110
- print(f'Expected: {required} but got: {obj}')
111
- for i in range(5):
112
- readnext()
113
-
114
- utils.sigtrap(None)
115
- os._exit(1)
116
- raise Exception(f'Expected: {required} but got: {obj}')
117
-
118
- def trace_writer(name, *args):
119
- print(f'Trace: {name} {args}')
120
-
121
- read_required('TRACE')
122
- read_required(name)
123
-
124
- for arg in args:
125
- read_required(arg)
126
-
127
- tracer = Tracer(tracing_config, writer = trace_writer)
128
-
129
- factory = ReplayProxySystem(thread_state = thread_state,
51
+ return ReplayProxySystem(thread_state = thread_state,
130
52
  immutable_types = immutable_types,
131
- tracer = tracer,
132
- reader = reader)
133
-
134
- # factory = replaying_proxy_factory(thread_state = thread_state,
135
- # is_immutable_type = is_immutable_type,
136
- # tracer = tracer,
137
- # bind = reader.supply,
138
- # next = next_result,
139
- # checkpoint = checkpoint)
140
-
141
- factory.tracer = tracer
142
-
143
- def read_sync(): read_required('SYNC')
144
-
145
- factory.sync = lambda function: functional.observer(on_call = functional.always(read_sync), function = function)
146
-
147
- # factory.set_thread_number = writer.thread_number
148
-
149
- factory.log = lambda message: checkpoint({'type': 'log_message', 'message': message})
150
-
151
- return factory
53
+ tracing_config = tracing_config,
54
+ path = recording_path / 'trace.bin')
@@ -24,28 +24,30 @@ class Tracer:
24
24
 
25
25
  cls = functional.typeof(obj)
26
26
 
27
+ if issubclass(cls, Proxy):
28
+ return str(cls)
29
+
27
30
  if issubclass(cls, (int, str)):
28
31
  return obj
29
32
 
30
- else:
31
- return 'FOO!!!!'
33
+ return str(cls)
32
34
 
33
- if issubclass(cls, Proxy):
34
- return f'<Proxy>'
35
+ # if issubclass(cls, Proxy):
36
+ # return f'<Proxy>'
35
37
 
36
- if issubclass(cls, types.TracebackType):
37
- return '<traceback>'
38
+ # if issubclass(cls, types.TracebackType):
39
+ # return '<traceback>'
38
40
 
39
- elif issubclass(cls, utils.wrapped_function):
40
- return utils.unwrap(obj).__name__
41
+ # elif issubclass(cls, utils.wrapped_function):
42
+ # return utils.unwrap(obj).__name__
41
43
 
42
- elif hasattr(obj, '__module__') and hasattr(obj, '__name__'):
43
- return f'{obj.__module__}.{obj.__name__}'
44
- # elif isinstance(obj, type):
44
+ # elif hasattr(obj, '__module__') and hasattr(obj, '__name__'):
45
45
  # return f'{obj.__module__}.{obj.__name__}'
46
- else:
47
- return '<other>'
48
- # return f'instance type: {str(self.serialize(type(obj)))}'
46
+ # # elif isinstance(obj, type):
47
+ # # return f'{obj.__module__}.{obj.__name__}'
48
+ # else:
49
+ # return '<other>'
50
+ # # return f'instance type: {str(self.serialize(type(obj)))}'
49
51
  except:
50
52
  print("ERROR in tracer serialize!!!!")
51
53
  os._exit(1)
@@ -120,7 +122,7 @@ class Tracer:
120
122
  writer = functional.partial(self.writer, name)
121
123
 
122
124
  if name in self.config:
123
- return functional.compose(func, functional.side_effect(writer))
125
+ return functional.sequence(func, functional.side_effect(writer))
124
126
  else:
125
127
  return func
126
128
 
@@ -139,6 +141,6 @@ class Tracer:
139
141
 
140
142
  def event_after(self, name, func):
141
143
  if name in self.config:
142
- return functional.compose(func, functional.side_effect(self.event(name)))
144
+ return functional.sequence(func, functional.side_effect(self.event(name)))
143
145
  else:
144
146
  return func
@@ -1,3 +1,3 @@
1
1
  from retracesoftware.proxy.record import RecordProxySystem
2
2
  from retracesoftware.proxy.replay import ReplayProxySystem
3
-
3
+ from retracesoftware.proxy.thread import set_thread_id, per_thread_messages
@@ -1,100 +1,49 @@
1
1
  import retracesoftware.functional as functional
2
2
  import retracesoftware_utils as utils
3
3
 
4
+ from retracesoftware.proxy.proxytype import ExtendingProxy
5
+
4
6
  # from retracesoftware.proxy.proxytype import ExtendingProxy
5
7
 
6
- unproxy_execute = functional.mapargs(starting = 1,
7
- transform = functional.walker(utils.try_unwrap),
8
- function = functional.apply)
8
+ # unproxy_execute = functional.mapargs(starting = 1,
9
+ # transform = functional.walker(utils.try_unwrap),
10
+ # function = functional.apply)
9
11
 
10
- def adapter(proxy_input,
12
+ def adapter(function,
13
+ proxy_input,
11
14
  proxy_output,
12
- function,
13
15
  on_call = None,
14
16
  on_result = None,
15
17
  on_error = None):
16
18
 
17
19
  # function = functional.apply
18
20
 
19
- if on_call: function = functional.observer(on_call = on_call, function = function)
21
+ if on_call: function = utils.observer(on_call = on_call, function = function)
22
+
23
+ #functional.observer(on_call = on_call, function = function)
20
24
 
21
25
  function = functional.mapargs(starting = 1, transform = proxy_input, function = function)
22
26
 
23
- function = functional.compose(function, proxy_output)
27
+ function = functional.sequence(function, proxy_output)
24
28
 
25
29
  if on_result or on_error:
26
- function = functional.observer(on_result = on_result, on_error = on_error, function = function)
30
+ function = utils.observer(on_result = on_result, on_error = on_error, function = function)
27
31
 
28
32
  return function
29
33
 
30
- def adapter_pair(proxy_int,
31
- proxy_ext,
32
- int_apply,
33
- ext_apply,
34
- tracer,
35
- on_int_call,
36
- on_ext_result,
37
- on_ext_error):
34
+ def adapter_pair(int, ext):
38
35
  return (
39
36
  adapter(
40
- function = ext_apply,
41
- proxy_input = proxy_int,
42
- proxy_output = proxy_ext,
43
- on_call = tracer('proxy.ext.call'),
44
- on_result = on_ext_result,
45
- on_error = on_ext_error),
46
- # on_result = tracer('proxy.ext.result', on_ext_result),
47
- # on_error = tracer('proxy.ext.error', on_ext_error)),
37
+ function = ext.apply,
38
+ proxy_input = int.proxy,
39
+ proxy_output = ext.proxy,
40
+ on_call = ext.on_call,
41
+ on_result = ext.on_result,
42
+ on_error = ext.on_error),
48
43
  adapter(
49
- function = int_apply,
50
- proxy_input = proxy_ext,
51
- proxy_output = proxy_int,
52
- on_call = tracer('proxy.int.call', on_int_call),
53
- on_result = tracer('proxy.int.result'),
54
- on_error = tracer('proxy.int.error')))
55
-
56
- def proxy(proxytype):
57
- return functional.selfapply(functional.compose(functional.typeof, proxytype))
58
-
59
- def maybe_proxy(proxytype):
60
- return functional.if_then_else(
61
- functional.isinstanceof(utils.Wrapped),
62
- utils.unwrap,
63
- proxy(functional.memoize_one_arg(proxytype)))
64
-
65
- def gateway_pair(thread_state,
66
- tracer,
67
- immutable_types,
68
- int_proxytype,
69
- ext_proxytype,
70
- int_apply = functional.apply,
71
- ext_apply = functional.apply,
72
- on_int_call = None,
73
- on_ext_result = None,
74
- on_ext_error = None):
75
-
76
- def is_immutable_type(cls):
77
- return issubclass(cls, tuple(immutable_types))
78
-
79
- is_immutable = functional.sequence(functional.typeof, functional.memoize_one_arg(is_immutable_type))
80
-
81
- def create_proxier(proxytype):
82
- return functional.walker(functional.when_not(is_immutable, maybe_proxy(proxytype)))
83
-
84
- int_to_ext_dispatch = thread_state.dispatch(tracer('proxy.int.disabled.event', unproxy_execute))
85
- ext_to_int_dispatch = thread_state.dispatch(tracer('proxy.ext.disabled.event', unproxy_execute))
86
-
87
- int_to_ext, ext_to_int = adapter_pair(
88
- proxy_int = create_proxier(int_proxytype(ext_to_int_dispatch)),
89
- proxy_ext = create_proxier(ext_proxytype(int_to_ext_dispatch)),
90
- int_apply = thread_state.wrap(desired_state = 'internal', function = int_apply),
91
- ext_apply = thread_state.wrap(desired_state = 'external', function = ext_apply),
92
- tracer = tracer,
93
- on_int_call = on_int_call,
94
- on_ext_result = on_ext_result,
95
- on_ext_error = on_ext_error)
96
-
97
- thread_state.set_dispatch(int_to_ext_dispatch, external = functional.apply, internal = tracer('proxy.int_to_ext.stack', int_to_ext))
98
- thread_state.set_dispatch(ext_to_int_dispatch, internal = functional.apply, external = tracer('proxy.ext_to_int.wrap', ext_to_int))
99
-
100
- return (int_to_ext_dispatch, ext_to_int_dispatch)
44
+ function = int.apply,
45
+ proxy_input = ext.proxy,
46
+ proxy_output = int.proxy,
47
+ on_call = int.on_call,
48
+ on_result = int.on_result,
49
+ on_error = int.on_error))