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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (35) hide show
  1. retracesoftware/__main__.py +285 -0
  2. retracesoftware/autoenable.py +53 -0
  3. retracesoftware/config.json +19 -233
  4. retracesoftware/config.yaml +0 -0
  5. retracesoftware/install/config.py +6 -0
  6. retracesoftware/install/edgecases.py +23 -1
  7. retracesoftware/install/patcher.py +98 -513
  8. retracesoftware/install/patchfindspec.py +117 -0
  9. retracesoftware/install/phases.py +338 -0
  10. retracesoftware/install/record.py +111 -40
  11. retracesoftware/install/replace.py +28 -0
  12. retracesoftware/install/replay.py +59 -11
  13. retracesoftware/install/tracer.py +171 -33
  14. retracesoftware/install/typeutils.py +20 -0
  15. retracesoftware/modules.toml +384 -0
  16. retracesoftware/preload.txt +216 -0
  17. retracesoftware/proxy/__init__.py +1 -1
  18. retracesoftware/proxy/globalref.py +31 -0
  19. retracesoftware/proxy/messagestream.py +204 -0
  20. retracesoftware/proxy/proxysystem.py +328 -71
  21. retracesoftware/proxy/proxytype.py +90 -38
  22. retracesoftware/proxy/record.py +109 -119
  23. retracesoftware/proxy/replay.py +94 -188
  24. retracesoftware/proxy/serializer.py +28 -0
  25. retracesoftware/proxy/startthread.py +40 -0
  26. retracesoftware/proxy/stubfactory.py +82 -27
  27. retracesoftware/proxy/thread.py +64 -4
  28. retracesoftware/replay.py +104 -0
  29. retracesoftware/run.py +378 -0
  30. retracesoftware/stackdifference.py +133 -0
  31. {retracesoftware_proxy-0.1.5.dist-info → retracesoftware_proxy-0.2.4.dist-info}/METADATA +2 -1
  32. retracesoftware_proxy-0.2.4.dist-info/RECORD +42 -0
  33. retracesoftware_proxy-0.1.5.dist-info/RECORD +0 -27
  34. {retracesoftware_proxy-0.1.5.dist-info → retracesoftware_proxy-0.2.4.dist-info}/WHEEL +0 -0
  35. {retracesoftware_proxy-0.1.5.dist-info → retracesoftware_proxy-0.2.4.dist-info}/top_level.txt +0 -0
@@ -5,21 +5,29 @@ from retracesoftware.install import globals
5
5
  import os, json, re, glob
6
6
  from pathlib import Path
7
7
  from datetime import datetime
8
+ from retracesoftware.install.config import env_truthy
8
9
 
9
- def latest_from_pattern(pattern: str) -> str | None:
10
- """
11
- Given a strftime-style filename pattern (e.g. "recordings/%Y%m%d_%H%M%S_%f"),
12
- return the path to the most recent matching file, or None if no files exist.
13
- """
10
+ def recordings(pattern):
14
11
  # Turn strftime placeholders into '*' for globbing
15
12
  # (very simple replacement: %... -> *)
16
13
  glob_pattern = re.sub(r"%[a-zA-Z]", "*", pattern)
17
14
 
15
+ base_pattern = os.path.basename(pattern)
16
+
18
17
  # Find all matching files
19
- candidates = glob.glob(glob_pattern)
20
- if not candidates:
21
- return None
18
+ for path in glob.glob(glob_pattern):
19
+ try:
20
+ name = os.path.basename(path)
21
+ datetime.strptime(name, base_pattern)
22
+ yield path
23
+ except:
24
+ pass
22
25
 
26
+ def latest_from_pattern(pattern: str) -> str | None:
27
+ """
28
+ Given a strftime-style filename pattern (e.g. "recordings/%Y%m%d_%H%M%S_%f"),
29
+ return the path to the most recent matching file, or None if no files exist.
30
+ """
23
31
  # Derive the datetime format from the pattern (basename only)
24
32
  base_pattern = os.path.basename(pattern)
25
33
 
@@ -28,11 +36,11 @@ def latest_from_pattern(pattern: str) -> str | None:
28
36
  return datetime.strptime(name, base_pattern)
29
37
 
30
38
  # Find the latest by parsed timestamp
31
- latest = max(candidates, key=parse_time)
32
- return latest
39
+ return max(recordings(pattern), key=parse_time)
33
40
 
34
41
  def replay_system(thread_state, immutable_types, config):
35
42
 
43
+ verbose = env_truthy('RETRACE_VERBOSE', False)
36
44
  recording_path = Path(latest_from_pattern(config['recording_path']))
37
45
 
38
46
  # print(f"replay running against path: {recording_path}")
@@ -48,7 +56,47 @@ def replay_system(thread_state, immutable_types, config):
48
56
  with open(recording_path / "tracing_config.json", "r", encoding="utf-8") as f:
49
57
  tracing_config = json.load(f)
50
58
 
59
+ with open(recording_path / "mainscript", "r", encoding="utf-8") as f:
60
+ mainscript = f.read()
61
+
62
+ call_trace_file = recording_path / "call_trace.txt"
63
+
64
+ if call_trace_file.exists():
65
+ class CallTracer:
66
+ def __init__(self):
67
+ self.file = open(call_trace_file, 'r')
68
+
69
+ def __call__(self, obj):
70
+ recording = json.loads(self.file.readline())
71
+ if obj != recording:
72
+ breakpoint()
73
+
74
+ def close(self):
75
+ self.file.close()
76
+
77
+ # def on_call(self, function_name):
78
+ # recording = self.file.readline()
79
+ # if f'call: {function_name}\n' != recording:
80
+ # breakpoint()
81
+
82
+ # assert f'call: {function_name}\n' == recording
83
+
84
+ # def on_return(self, function_name):
85
+ # recording = self.file.readline()
86
+ # if f'return: {function_name}\n' != recording:
87
+ # breakpoint()
88
+
89
+ # assert f'return: {function_name}\n' == recording
90
+
91
+ call_tracer = CallTracer()
92
+ else:
93
+ call_tracer = None
94
+
51
95
  return ReplayProxySystem(thread_state = thread_state,
52
96
  immutable_types = immutable_types,
53
97
  tracing_config = tracing_config,
54
- path = recording_path / 'trace.bin')
98
+ mainscript = mainscript,
99
+ path = recording_path / 'trace.bin',
100
+ tracecalls = env_truthy('RETRACE_ALL', False),
101
+ verbose = verbose,
102
+ magic_markers = env_truthy('RETRACE_MAGIC_MARKERS', False))
@@ -2,8 +2,12 @@ import retracesoftware.functional as functional
2
2
  import retracesoftware_utils as utils
3
3
  from retracesoftware.proxy.proxytype import Proxy
4
4
 
5
+ from retracesoftware.proxy.serializer import serializer
6
+
5
7
  import types
6
8
  import os
9
+ import sys
10
+ import functools
7
11
 
8
12
  def format_kwargs(kwargs):
9
13
  result = []
@@ -12,57 +16,191 @@ def format_kwargs(kwargs):
12
16
 
13
17
  return tuple(result)
14
18
 
19
+ class CallTracer:
20
+
21
+ ignore = set([('abc', '__subclasscheck__'),
22
+ ('threading', '_shutdown'),
23
+ ('typing', 'inner')])
24
+
25
+ def __init__(self, writer, thread_state):
26
+ self.writer = writer
27
+ self.thread_state = thread_state
28
+
29
+ def on_result(self, result):
30
+ if type(result) in [str]:
31
+ self.writer(result)
32
+ else:
33
+ self.writer(str(type(result)))
34
+
35
+ def on_error(self, exc_type, exc_value, exc_traceback):
36
+ pass
37
+
38
+ def __call__(self, frame):
39
+ if self.thread_state.value == 'internal':
40
+ with self.thread_state.select('disabled'):
41
+ func = frame.function
42
+
43
+ key = (func.__module__, func.__name__)
44
+
45
+ if key not in CallTracer.ignore and func.__name__ != '__hash__':
46
+ self.writer(f'{func.__module__}.{func.__name__}')
47
+ return self
48
+ else:
49
+ return lambda *args, **kwargs: self(*args, **kwargs)
50
+
15
51
  class Tracer:
52
+
16
53
  def __init__(self, config, writer):
17
54
  self.config = config
18
- serialize = functional.walker(self.serialize)
55
+ serialize = serializer
56
+ # functional.walker(self.serialize)
19
57
  self.writer = functional.mapargs(transform = serialize, function = writer)
20
58
 
21
- def serialize(self, obj):
59
+ def systrace(self, frame, event, arg):
22
60
  try:
23
- if obj is None: return None
61
+ if event in ['line', 'call']:
62
+ print(f'In systrace: {event} {frame.f_code.co_filename}:{frame.f_lineno}')
63
+ self.writer(event, frame.f_code.co_filename, frame.f_code.co_name, frame.f_lineno)
64
+
65
+ elif event == 'return':
66
+ print(f'In systrace: {event}')
67
+ self.writer(event)
68
+ # self.writer(event, self.serialize(arg))
69
+
70
+ elif event == 'exception':
71
+ print(f'In systrace: {event}')
72
+ self.writer(event)
73
+ else:
74
+ print(f'In systrace: {event}')
75
+
76
+ return self.systrace
77
+ except:
78
+ print(f'systrace RAISED ERROR!')
79
+ raise
80
+
81
+ # def trace_calls(self, thread_state):
82
+ # if 'tracecalls' in self.config:
83
+ # # def on_call(frame):
84
+
85
+ # # func = frame.function
86
+
87
+ # # key = (func.__module__, func.__name__)
88
+
89
+ # # ignore = set([('abc', '__subclasscheck__'),
90
+ # # ('threading', '_shutdown')])
91
+
92
+ # # if key in ignore:
93
+ # # print('About to throw!!!')
94
+ # # raise Exception()
95
+
96
+ # # objtype = None
97
+
98
+ # # if 'self' in frame.locals:
99
+ # # this = frame.locals['self']
100
+ # # objtype = type(this)
101
+
102
+ # # # if objtype:
103
+ # # # print(f'Called!!!!: {func.__module__}.{objtype.__name__}.{func.__name__}')
104
+ # # # else:
105
+ # # # print(f'Called!!!!: {func.__module__}.{func.__name__}')
24
106
 
25
- cls = functional.typeof(obj)
107
+ # # qualname = (func.__module__, objtype, func.__name__)
26
108
 
27
- if issubclass(cls, Proxy):
28
- return str(cls)
109
+ # # self.writer(f'{func.__module__}.{func.__name__}')
110
+
111
+ # # # if func.__name__ == '_path_stat':
112
+ # # # utils.sigtrap('_path_stat start')
113
+
114
+ # # return qualname
115
+
116
+ # # def on_result(qualname, res):
117
+ # # mod, obj, func = qualname
118
+
119
+ # # # if obj:
120
+ # # # print(f'Returning!!!!: {mod}.{obj}.{func}')
121
+ # # # else:
122
+ # # # print(f'Returning!!!!: {mod}.{func}')
123
+
124
+ # # # if func == '_path_stat':
125
+ # # # utils.sigtrap('_path_stat res')
126
+
127
+ # # if func != '__hash__':
128
+ # # ignore = set(('typing', 'inner'))
129
+
130
+ # # if qualname not in ignore:
131
+ # # if type(res) in [str, int]:
132
+ # # self.writer(res)
133
+ # # else:
134
+ # # self.writer(str(type(res)))
135
+
136
+ # # def on_call_disabled(frame):
137
+ # # func = frame.function
138
+ # # print(f'disabled: {func.__module__}.{func.__name__}')
139
+
140
+ # callback = CallTracer(writer = self.writer, thread_state = thread_state)
141
+ # utils.intercept_frame_eval(callback)
142
+
143
+ # def serialize(self, obj):
144
+ # try:
145
+ # if obj is None: return None
146
+
147
+ # cls = functional.typeof(obj)
148
+
149
+ # if issubclass(cls, Proxy):
150
+ # return str(cls)
29
151
 
30
- if issubclass(cls, (int, str)):
31
- return obj
152
+ # if issubclass(cls, (int, str)):
153
+ # return obj
32
154
 
33
- return str(cls)
155
+ # return str(cls)
34
156
 
35
- # if issubclass(cls, Proxy):
36
- # return f'<Proxy>'
157
+ # # if issubclass(cls, Proxy):
158
+ # # return f'<Proxy>'
37
159
 
38
- # if issubclass(cls, types.TracebackType):
39
- # return '<traceback>'
160
+ # # if issubclass(cls, types.TracebackType):
161
+ # # return '<traceback>'
40
162
 
41
- # elif issubclass(cls, utils.wrapped_function):
42
- # return utils.unwrap(obj).__name__
163
+ # # elif issubclass(cls, utils.wrapped_function):
164
+ # # return utils.unwrap(obj).__name__
43
165
 
44
- # elif hasattr(obj, '__module__') and hasattr(obj, '__name__'):
45
- # return f'{obj.__module__}.{obj.__name__}'
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)))}'
51
- except:
52
- print("ERROR in tracer serialize!!!!")
53
- os._exit(1)
166
+ # # elif hasattr(obj, '__module__') and hasattr(obj, '__name__'):
167
+ # # return f'{obj.__module__}.{obj.__name__}'
168
+ # # # elif isinstance(obj, type):
169
+ # # # return f'{obj.__module__}.{obj.__name__}'
170
+ # # else:
171
+ # # return '<other>'
172
+ # # # return f'instance type: {str(self.serialize(type(obj)))}'
173
+ # except:
174
+ # print("ERROR in tracer serialize!!!!")
175
+ # os._exit(1)
54
176
 
55
177
  def log(self, name, message):
56
178
  if name in self.config:
57
179
  self.writer(name, message)
58
180
 
181
+ def checkpoint(self, obj):
182
+ self.writer(obj)
183
+
184
+ # def stacktrace(self):
185
+ # self.writer('stacktrace', utils.stacktrace())
186
+
187
+ def _write_call(self, name, *args, **kwargs):
188
+ # self.stacktrace()
189
+ if len(args) > 0:
190
+ funcname = args[0].__name__
191
+ # if funcname == '__del__':
192
+ # utils.sigtrap('FOO')
193
+
194
+ self.writer(name, funcname, args[1:], kwargs)
195
+ else:
196
+ utils.sigtrap('FOO')
197
+ breakpoint()
198
+
59
199
  def __call__(self, name, func = None):
60
200
  if name in self.config:
61
201
  if name.endswith('.call'):
62
- def write_call(*args, **kwargs):
63
- self.writer(name, args[0].__name__, args[1:], kwargs)
64
-
65
- return functional.firstof(write_call, func) if func else write_call
202
+ f = functools.partial(self._write_call, name)
203
+ return functional.firstof(f, func) if func else f
66
204
 
67
205
  elif name.endswith('.result'):
68
206
  def write_result(obj):
@@ -93,11 +231,11 @@ class Tracer:
93
231
 
94
232
  return wrapper
95
233
 
96
- elif name.endswith('.stack'):
97
- def write_event(*args, **kwargs):
98
- self.writer(name, utils.stacktrace())
234
+ # elif name.endswith('.stack'):
235
+ # def write_event(*args, **kwargs):
236
+ # self.writer(name, utils.stacktrace())
99
237
 
100
- return functional.firstof(write_event, func) if func else write_event
238
+ # return functional.firstof(write_event, func) if func else write_event
101
239
 
102
240
 
103
241
  return func
@@ -35,6 +35,25 @@ class WithoutFlags:
35
35
  def __exit__(self, *args):
36
36
  utils.set_type_flags(self.cls, self.saved)
37
37
 
38
+ class WithFlags:
39
+
40
+ def __init__(self, cls , *flags):
41
+ self.cls = cls
42
+ self.flags = flags
43
+
44
+ def __enter__(self):
45
+ self.saved = utils.type_flags(self.cls)
46
+ flags = self.saved
47
+
48
+ for flag in self.flags:
49
+ flags |= utils.TypeFlags[flag]
50
+
51
+ utils.set_type_flags(self.cls, flags)
52
+ return self.cls
53
+
54
+ def __exit__(self, *args):
55
+ utils.set_type_flags(self.cls, self.saved)
56
+
38
57
  def add_flag(cls : type, flag):
39
58
  if isinstance(flag, str):
40
59
  flag = utils.TypeFlags[flag]
@@ -45,6 +64,7 @@ def add_flag(cls : type, flag):
45
64
  def modify(cls):
46
65
  return WithoutFlags(cls, "Py_TPFLAGS_IMMUTABLETYPE")
47
66
 
67
+
48
68
  def type_has_feature(cls, flag):
49
69
  if isinstance(flag, str):
50
70
  flag = utils.TypeFlags[flag]