retracesoftware-proxy 0.2.17__py3-none-any.whl → 0.2.19__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.
@@ -33,6 +33,24 @@ def dump_as_json(path, obj):
33
33
  with open(path, 'w') as f:
34
34
  json.dump(obj, f, indent=2)
35
35
 
36
+ def load_env(file):
37
+ """Load a .env file into a dict."""
38
+ env = {}
39
+ with open(file, 'r') as f:
40
+ for line in f:
41
+ line = line.strip()
42
+ if not line or line.startswith('#'):
43
+ continue
44
+ if '=' in line:
45
+ key, value = line.split('=', 1)
46
+ # Remove surrounding quotes if present
47
+ if value.startswith('"') and value.endswith('"'):
48
+ value = value[1:-1]
49
+ # Unescape
50
+ value = value.replace('\\n', '\n').replace('\\"', '"').replace('\\\\', '\\')
51
+ env[key] = value
52
+ return env
53
+
36
54
  vscode_workspace = {
37
55
  "folders": [{ 'path': '.' }],
38
56
  "settings": {
@@ -74,6 +92,28 @@ def file_md5(path):
74
92
  def checksum(path):
75
93
  return file_md5(path) if path.is_file() else {entry.name: checksum(entry) for entry in path.iterdir() if entry.name != '__pycache__'}
76
94
 
95
+ def diff_dicts(recorded, current, path=""):
96
+ """Recursively diff two dicts, returning list of differences."""
97
+ diffs = []
98
+ all_keys = set(recorded.keys()) | set(current.keys())
99
+
100
+ for key in sorted(all_keys):
101
+ key_path = f"{path}.{key}" if path else key
102
+
103
+ if key not in recorded:
104
+ diffs.append(f" + {key_path}: (new in current)")
105
+ elif key not in current:
106
+ diffs.append(f" - {key_path}: (missing in current)")
107
+ elif recorded[key] != current[key]:
108
+ if isinstance(recorded[key], dict) and isinstance(current[key], dict):
109
+ diffs.extend(diff_dicts(recorded[key], current[key], key_path))
110
+ else:
111
+ diffs.append(f" ! {key_path}:")
112
+ diffs.append(f" recorded: {recorded[key][:16]}..." if isinstance(recorded[key], str) and len(recorded[key]) > 16 else f" recorded: {recorded[key]}")
113
+ diffs.append(f" current: {current[key][:16]}..." if isinstance(current[key], str) and len(current[key]) > 16 else f" current: {current[key]}")
114
+
115
+ return diffs
116
+
77
117
  def retrace_extension_paths():
78
118
  names = ['_retracesoftware_utils_release', '_retracesoftware_utils_debug',
79
119
  '_retracesoftware_functional_release', '_retracesoftware_functional_debug',
@@ -88,33 +128,55 @@ def retrace_module_paths():
88
128
  def checksums():
89
129
  return {name: checksum(path) for name, path in retrace_module_paths().items()}
90
130
 
131
+ def run_create_tracedir_cmd(create_tracedir_cmd, path):
132
+ import subprocess
133
+ result = subprocess.run([create_tracedir_cmd, str(path)], capture_output=True, text=True)
134
+ if result.returncode != 0:
135
+ msg = f"create_tracedir_cmd '{create_tracedir_cmd}' failed with exit code {result.returncode}"
136
+ if result.stdout:
137
+ msg += f"\nstdout: {result.stdout}"
138
+ if result.stderr:
139
+ msg += f"\nstderr: {result.stderr}"
140
+ raise ConfigurationError(msg)
141
+ if not path.exists():
142
+ raise ConfigurationError(f"create_tracedir_cmd '{create_tracedir_cmd}' exited successfully but directory '{path}' does not exist")
143
+
91
144
  def record(options, args):
92
145
 
146
+ if options.verbose:
147
+ print(f"Retrace enabled, recording to {options.recording}", file=sys.stderr)
148
+
93
149
  path = Path(expand_recording_path(options.recording))
94
- # ensure the path exists
95
- path.mkdir(parents=True, exist_ok=True)
150
+
151
+ # Create trace directory via custom command or default mkdir
152
+ if options.create_tracedir_cmd:
153
+ run_create_tracedir_cmd(options.create_tracedir_cmd, path)
154
+ else:
155
+ path.mkdir(parents=True, exist_ok=True)
96
156
 
97
157
  from retracesoftware.install import edgecases
98
158
  edgecases.recording_path = path
99
159
 
100
160
  # write various recording files to directory
161
+ path_info = stream.get_path_info()
101
162
  dump_as_json(path / 'settings.json', {
102
163
  'argv': args,
103
164
  'executable': sys.executable,
104
165
  'magic_markers': options.magic_markers,
105
166
  'trace_inputs': options.trace_inputs,
106
167
  'trace_shutdown': options.trace_shutdown,
107
- 'env': dict(os.environ),
108
168
  'python_version': sys.version,
109
- 'md5_checksums': checksums(),
169
+ 'cwd': path_info['cwd'],
170
+ 'sys_path': path_info['sys_path'],
110
171
  })
111
-
112
- rundir = path / 'run'
113
- rundir.mkdir(exist_ok=True)
114
-
115
- script = Path(scriptname(args))
116
- if script.exists():
117
- copy2(script, rundir)
172
+ dump_as_json(path / 'md5_checksums.json', checksums())
173
+
174
+ # Write env to standard .env file
175
+ with open(path / '.env', 'w') as f:
176
+ for key, value in os.environ.items():
177
+ # Escape newlines and quotes for .env format
178
+ escaped = value.replace('\\', '\\\\').replace('\n', '\\n').replace('"', '\\"')
179
+ f.write(f'{key}="{escaped}"\n')
118
180
 
119
181
  # create a .venv directory in the recording path
120
182
  # venv_path = path / '.venv'
@@ -175,24 +237,32 @@ def record(options, args):
175
237
  gc.callbacks.remove(system.on_gc_event)
176
238
 
177
239
  def replay(args):
178
- path = Path(args.recording)
240
+ # Resolve path before any chdir
241
+ path = Path(args.recording).resolve()
179
242
 
180
243
  if not path.exists():
181
244
  raise RecordingNotFoundError(f"Recording path: {path} does not exist")
182
245
 
183
246
  settings = load_json(path / "settings.json")
247
+ recorded_checksums = load_json(path / "md5_checksums.json")
184
248
 
185
- if settings['md5_checksums'] != checksums():
186
- raise VersionMismatchError("Checksums for Retrace do not match, cannot run replay with different version of retrace to record")
249
+ current_checksums = checksums()
250
+ if recorded_checksums != current_checksums:
251
+ diffs = diff_dicts(recorded_checksums, current_checksums)
252
+ diff_str = "\n".join(diffs) if diffs else "(no differences found in structure)"
253
+ raise VersionMismatchError(f"Checksums for Retrace do not match:\n{diff_str}")
187
254
 
188
255
  if settings['python_version'] != sys.version:
189
256
  raise VersionMismatchError("Python version does not match, cannot run replay with different version of Python to record")
190
257
 
191
- os.environ.update(settings['env'])
258
+ os.environ.update(load_env(path / '.env'))
192
259
 
193
260
  if sys.executable != settings['executable']:
194
261
  raise ConfigurationError(f"Stopping replay as current python executable: {sys.executable} is not what was used for record: {settings['executable']}")
195
262
 
263
+ # Change to recorded cwd - replay from same directory as recording
264
+ os.chdir(settings['cwd'])
265
+
196
266
  thread_state = utils.ThreadState(*thread_states)
197
267
 
198
268
  # with stream.reader(path = path / 'trace.bin',
@@ -223,6 +293,7 @@ def replay(args):
223
293
  gc.collect()
224
294
  gc.disable()
225
295
 
296
+ # Use original argv - scripts are now relative to cwd (recording/run)
226
297
  run_with_retrace(system, settings['argv'], settings['trace_shutdown'])
227
298
 
228
299
  def main():
@@ -269,6 +340,13 @@ def main():
269
340
  help='Whether to write call parameters, used for debugging'
270
341
  )
271
342
 
343
+ parser.add_argument(
344
+ '--create_tracedir_cmd',
345
+ type=str,
346
+ default=None,
347
+ help='Command to create trace directory (receives directory path as argument)'
348
+ )
349
+
272
350
  parser.add_argument('rest', nargs = argparse.REMAINDER, help='target application and arguments')
273
351
 
274
352
  args = parser.parse_args()
@@ -51,6 +51,10 @@ else:
51
51
  if is_true('RETRACE_TRACE_INPUTS'):
52
52
  new_argv.append('--trace_inputs')
53
53
 
54
+ if 'RETRACE_CREATE_TRACEDIR_CMD' in os.environ:
55
+ new_argv.append('--create_tracedir_cmd')
56
+ new_argv.append(os.environ['RETRACE_CREATE_TRACEDIR_CMD'])
57
+
54
58
  new_argv.append('--')
55
59
  new_argv.extend(sys.orig_argv[1:])
56
60
 
@@ -61,12 +61,39 @@ def create_patcher(system):
61
61
 
62
62
  per_thread = PerThread()
63
63
 
64
+ # Hash patching for deterministic record/replay
65
+ #
66
+ # Why we patch __hash__:
67
+ # Python's default object hashes are based on memory addresses, which vary
68
+ # between runs. Sets iterate in hash order, so iteration order is
69
+ # non-deterministic. For record/replay to work correctly, we need identical
70
+ # set iteration order during both phases, so we replace __hash__ with a
71
+ # deterministic counter-based hash that returns stable values.
72
+ # (Note: dicts maintain insertion order since Python 3.7, so they're stable.)
73
+ #
74
+ # The hashfunc dispatches based on thread state:
75
+ # - disabled: returns None (unhashable, triggers fallback)
76
+ # - internal: counter for when thread is inside the sandbox (proxied code)
77
+ # - external: counter for when thread is outside the sandbox (user code)
78
+ #
79
+ # IMPORTANT - Python 3.12 compatibility:
80
+ # Python 3.12 changed typing._tp_cache to use a global _caches dict with
81
+ # functions as keys (see bpo GH-98253). If typing is imported BEFORE we
82
+ # patch FunctionType.__hash__, those functions won't be in our internal
83
+ # hash cache, and lookups will fail. utils.patch_hash handles this by
84
+ # pre-populating the cache with all existing instances of the patched type.
85
+ #
64
86
  hashfunc = system.thread_state.dispatch(
65
87
  functional.constantly(None),
66
88
  internal = functional.repeatedly(functional.partial(getattr, per_thread, 'internal')),
67
89
  external = functional.repeatedly(functional.partial(getattr, per_thread, 'external')))
68
90
 
69
91
  def patch_hash(obj):
92
+ """Patch __hash__ on a type to use deterministic counter-based hashing.
93
+
94
+ This ensures stable set iteration order for instances of this type,
95
+ which is required for record/replay consistency.
96
+ """
70
97
  if not isinstance(obj, type):
71
98
  raise Exception("TODO")
72
99
 
@@ -1,117 +1,23 @@
1
- from functools import wraps
2
1
  import os
3
2
  from pathlib import Path
4
- import shutil
5
- import sys
6
3
 
7
- # Set to track copied modules
8
- copied_modules = set()
9
-
10
- def get_relative_path(module_path: Path, sys_path: list) -> Path:
11
- """Compute the relative path of module_path relative to sys.path entries."""
12
- module_path = module_path.resolve()
13
- for base in sys_path:
14
- base_path = Path(base).resolve()
15
- try:
16
- if module_path.is_relative_to(base_path):
17
- return module_path.relative_to(base_path)
18
- except ValueError:
19
- continue
20
- return Path(module_path.name)
4
+ # Set to track logged modules (avoid duplicates)
5
+ logged_modules = set()
21
6
 
22
7
  class patch_find_spec:
23
- def __init__(self, cwd, run_path, python_path):
24
- self.cwd = cwd
25
- self.run_path = run_path
26
- self.python_path = python_path
8
+ """
9
+ Hook that logs accessed module file paths to a file.
10
+ One absolute path per line.
11
+ """
12
+ def __init__(self, output_file):
13
+ self.output_file = output_file
27
14
 
28
15
  def __call__(self, spec):
29
16
  if spec is not None and spec.origin and spec.origin != "built-in" and os.path.isfile(spec.origin):
30
17
  module_name = spec.name
31
- if module_name not in copied_modules:
32
- module_path = Path(spec.origin)
33
-
34
- dest_path = None
35
-
36
- if module_path.is_relative_to(self.cwd):
37
- dest_path = self.run_path / module_path.relative_to(self.cwd)
38
- elif self.python_path:
39
- relative_path = get_relative_path(module_path, sys.path)
40
- dest_path = self.python_path / relative_path
41
-
42
- if dest_path:
43
- dest_path.parent.mkdir(parents=True, exist_ok=True)
44
- shutil.copy2(module_path, dest_path)
45
- copied_modules.add(module_name)
46
-
47
- # def __call__(self, fullname, path, target=None):
48
- # spec = self.original_find_spec(fullname, path, target)
49
-
50
- # if spec is not None and spec.origin and spec.origin != "built-in" and os.path.isfile(spec.origin):
51
- # module_name = spec.name
52
- # if module_name not in copied_modules:
53
- # module_path = Path(spec.origin)
54
-
55
- # dest_path = None
56
-
57
- # if module_path.is_relative_to(self.cwd):
58
- # dest_path = self.run_path / module_path.relative_to(self.cwd)
59
- # elif self.python_path:
60
- # relative_path = get_relative_path(module_path, sys.path)
61
- # dest_path = self.python_path / relative_path
62
-
63
- # if dest_path:
64
- # dest_path.parent.mkdir(parents=True, exist_ok=True)
65
- # shutil.copy2(module_path, dest_path)
66
- # copied_modules.add(module_name)
67
-
68
- # return spec
69
-
70
- # def patch_find_spec(cwd, run_path, python_path, original_find_spec):
71
- # """Create a patched version of find_spec that copies .py files."""
72
- # @wraps(original_find_spec)
73
- # def patched_find_spec(fullname, path, target=None):
74
- # spec = original_find_spec(fullname, path, target)
75
-
76
- # if spec is not None and spec.origin and spec.origin != "built-in" and os.path.isfile(spec.origin):
77
- # module_name = spec.name
78
- # if module_name not in copied_modules:
79
- # module_path = Path(spec.origin)
80
-
81
- # dest_path = None
82
-
83
- # if module_path.is_relative_to(cwd):
84
- # dest_path = run_path / module_path.relative_to(cwd)
85
- # elif python_path:
86
- # relative_path = get_relative_path(module_path, sys.path)
87
- # dest_path = python_path / relative_path
88
-
89
- # if dest_path:
90
- # dest_path.parent.mkdir(parents=True, exist_ok=True)
91
- # shutil.copy2(module_path, dest_path)
92
- # copied_modules.add(module_name)
93
-
94
- # # elif python_path:
95
- # # relative_path = get_relative_path(module_path, sys.path)
96
- # # dest_path = python_path / relative_path
97
- # # dest_path.parent.mkdir(parents=True, exist_ok=True)
98
-
99
- # # shutil.copy2(module_path, dest_path)
100
- # # copied_modules.add(module_name)
101
-
102
- # # if module_path.suffix == '.py':
103
- # # elif module_path.suffix == '.py':
104
- # # relative_path = get_relative_path(module_path, sys.path)
105
- # # dest_path = path / relative_path
106
- # # dest_path.parent.mkdir(parents=True, exist_ok=True)
107
- # # try:
108
- # # shutil.copy2(module_path, dest_path)
109
- # # print(f"Copied {module_path} to {dest_path} (relative: {relative_path})")
110
- # # copied_modules.add(module_name)
111
- # # except Exception as e:
112
- # # print(f"Failed to copy {module_path}: {e}")
113
- # # else:
114
- # # print(f"Skipped copying {module_path} (not a .py file)")
115
-
116
- # return spec
117
- # return patched_find_spec
18
+ if module_name not in logged_modules:
19
+ # Write absolute path to output file
20
+ abs_path = os.path.realpath(spec.origin)
21
+ self.output_file.write(abs_path + '\n')
22
+ self.output_file.flush()
23
+ logged_modules.add(module_name)
retracesoftware/run.py CHANGED
@@ -133,13 +133,11 @@ def run_python_command(argv):
133
133
  print(f"Error: Not a Python script: {script_path}", file=sys.stderr)
134
134
  return 1
135
135
 
136
- full_path = str(path.resolve())
137
- sys.argv = [full_path] + script_args
136
+ # Use the path as given (relative or absolute) to preserve cwd-relative paths
137
+ # for stack trace normalization during record/replay
138
+ sys.argv = [script_path] + script_args
138
139
 
139
- # Change to script's directory
140
- os.chdir(path.parent)
141
-
142
- runpy.run_path(full_path, run_name="__main__")
140
+ runpy.run_path(script_path, run_name="__main__")
143
141
  return 0
144
142
 
145
143
  except ModuleNotFoundError:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: retracesoftware_proxy
3
- Version: 0.2.17
3
+ Version: 0.2.19
4
4
  License: Apache-2.0
5
5
  Requires-Python: >=3.11
6
6
  Requires-Dist: retracesoftware_utils
@@ -1,29 +1,21 @@
1
1
  retracesoftware/__init__.py,sha256=jQw15XBREFZX93BHpvREfc-hQmMUHYtO6gO-qZmdW1o,482
2
- retracesoftware/__main__.py,sha256=tJR_vKz6-WRCXlioto2PP92SC_K-GPAKoT05fsA8Px0,9761
3
- retracesoftware/autoenable.py,sha256=6KxpaPpeleSN7-qmekA4w2HJQaet_9Flt9WikQvyN04,1808
2
+ retracesoftware/__main__.py,sha256=oTpF0ev2yJrrhROIJroT5UqJPRQWdBYrfUWwctOywrY,13375
3
+ retracesoftware/autoenable.py,sha256=LVt6KfMIIScV5b4dsXP_DHlrRm1i5iIGfVauup61YtE,2001
4
4
  retracesoftware/config.json,sha256=Cw5YZCfTo8GLmtbTH2LYwST7wNe7925DAwsTtRIGhBE,3817
5
5
  retracesoftware/config.yaml,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
6
6
  retracesoftware/exceptions.py,sha256=8jT4tJrRRRh3nRERrsqaKgIJ3itb-VYumpa9UcoxF9o,647
7
7
  retracesoftware/modules.toml,sha256=Xu8B6H9HMM4ZaxiEPy9FGcMHBHsyBD1qR0weUXgau4Y,14613
8
8
  retracesoftware/preload.txt,sha256=8Wm8OWnDfEp9X4FN-yNQYLfZk_h36kMgHA_QUw_7Hbs,2370
9
- retracesoftware/replay.py,sha256=FEEenvRUeULmv-Zzb6g7Ub85oGonmvMT4ATE4q0yWqI,3676
10
9
  retracesoftware/retrace.pth,sha256=umCRQITpnqAp1N-svTCi4lJxOo7GihDrLtO3pA2wwgM,35
11
- retracesoftware/run.py,sha256=Gi1TpMF30QLmiziCU7hu3zR_8Hr1R7Qx0TsiQOjLmzA,12216
10
+ retracesoftware/run.py,sha256=h0t4ZO9-JHXZ4mjgwXNGSGL1UFxh7pKdr_wvokkJ-PY,12253
12
11
  retracesoftware/stackdifference.py,sha256=nM9r5YHMCNQcrWozMjUX4ZTrZL5U0rQChSGzpiXy_DU,4466
13
12
  retracesoftware/install/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
14
13
  retracesoftware/install/config.py,sha256=AnSlHI3lWQrPUg0fOS26PqXiq1hJSy56SwrNL8mbAG8,1546
15
14
  retracesoftware/install/edgecases.py,sha256=Ry3BETJ4Hmqpw9xXTlNeUgBL9q-sumaazqjmzZBRtD4,7265
16
15
  retracesoftware/install/globals.py,sha256=F8XvIoZQQ10gSRalk30dvdKllxlwxkaggYY6FogLDxY,510
17
- retracesoftware/install/install.py,sha256=fP7E9OWiQd1Dgi4jL8rL9T5SXW5AL62BM3NEW61fisw,4969
18
- retracesoftware/install/modulepatcher.py,sha256=yBXrbfr5J9JQXCVbcNpDm8H17bsPbL6DY54bImFjBKM,16715
19
- retracesoftware/install/patcher.py,sha256=DL8GKQzxCSVdTXeexgRuPaKL51SsymcPpP6lUJ1Ezo8,4364
20
- retracesoftware/install/patchfindspec.py,sha256=uQkfdOQOY20hFAcwDAqx7m8rbtGM2rBKOfC3Szb1zhw,4942
21
- retracesoftware/install/phases.py,sha256=OWaw86xzC924tdIA9C-12Qdr3sCx67hEOn-Ih9FM--4,9359
22
- retracesoftware/install/predicate.py,sha256=tX7NQc0rGkyyHYO3mduYHcJHbw1wczT53m_Dpkzo6do,2679
23
- retracesoftware/install/record.py,sha256=0iV9zMnO7aMUx8HDkni1eeopC3lWR5J6sDwxY6bgeX0,5861
24
- retracesoftware/install/references.py,sha256=A-G651IDOfuo00MkbAdpbIQh_15ChvJ7uAVTSmE6zd4,1721
16
+ retracesoftware/install/patcher.py,sha256=Z98ux8SDw53wyRKCevOT-tPOFcwcM3KH1dyR7Tg_TJ4,5859
17
+ retracesoftware/install/patchfindspec.py,sha256=oOjmsVsTSfex3ined3kcZ4iLh0emsx2Q5dB2_YVd62I,792
25
18
  retracesoftware/install/replace.py,sha256=FYiSJtNrXEhl-H6M5tJm0kbliBA0sZdxE0306pr-YQg,872
26
- retracesoftware/install/replay.py,sha256=bB2eMpIP2vXZzeJ98jtlkGSE1DEYWk3olUbiBQcdVj0,3539
27
19
  retracesoftware/install/tracer.py,sha256=VfjghYepk2M2DLy1nEYgHcaCXo0616OVMxxx3A3dNhM,9363
28
20
  retracesoftware/install/typeutils.py,sha256=GiYDPsKGBtp0_1E71AJm16u03L8eSoR1piz3yvOgAhk,2253
29
21
  retracesoftware/proxy/__init__.py,sha256=ntIyqKhBRkKEkcW_oOPodikh-mxYl8OXRnSaj-9-Xwc,178
@@ -39,7 +31,7 @@ retracesoftware/proxy/serializer.py,sha256=S5yhHoP2iYaXBY7Jr8Ei630Z31521x0IO6Deu
39
31
  retracesoftware/proxy/startthread.py,sha256=YKUcOSjA-9_5ZsDHhyP0fXJSP3RWDKV-ajX-I0SI-bU,1270
40
32
  retracesoftware/proxy/stubfactory.py,sha256=fyQtCZnkXvLaTlGcxE0Lx8od09ZOgnPWPn0YvNFfVeQ,6219
41
33
  retracesoftware/proxy/thread.py,sha256=AAj6BL4kw-KMHX4fV66LuePQbTYvPmI3IY8ZwV069nI,3073
42
- retracesoftware_proxy-0.2.17.dist-info/METADATA,sha256=_t4czKiwu0bwCYVOr24gXZS_QlH4cufaqEIn5f97asM,227
43
- retracesoftware_proxy-0.2.17.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
44
- retracesoftware_proxy-0.2.17.dist-info/top_level.txt,sha256=hYHsR6txLidmqvjBMITpIHvmJJbmoCAgr76-IpZPRz8,16
45
- retracesoftware_proxy-0.2.17.dist-info/RECORD,,
34
+ retracesoftware_proxy-0.2.19.dist-info/METADATA,sha256=sUiA5tyrwKB5INk9509ZpTYDjFSbR_2x60bPC9cIOpY,227
35
+ retracesoftware_proxy-0.2.19.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
36
+ retracesoftware_proxy-0.2.19.dist-info/top_level.txt,sha256=hYHsR6txLidmqvjBMITpIHvmJJbmoCAgr76-IpZPRz8,16
37
+ retracesoftware_proxy-0.2.19.dist-info/RECORD,,
@@ -1,142 +0,0 @@
1
- from __future__ import annotations
2
-
3
- import retracesoftware.functional as functional
4
- import retracesoftware.utils as utils
5
-
6
- # from retracesoftware_functional import mapargs, walker, first_arg, if_then_else, compose, observer, anyargs, memoize_one_arg, side_effect, partial, threadwatcher, firstof, notinstance_test, instance_test,typeof, isinstanceof, always
7
- # from retracesoftware.proxy.proxy import ProxyFactory, InternalProxy, ProxySpec, WrappingProxySpec, ExtendingProxySpec, ExtendingProxy
8
- from retracesoftware_proxy import thread_id
9
- # from retracesoftware_stream import ObjectWriter, ObjectReader
10
- import retracesoftware.stream as stream
11
-
12
- # from retracesoftware_utils import visitor
13
- from retracesoftware.install.tracer import Tracer
14
- from retracesoftware.proxy.record import RecordProxySystem
15
- from retracesoftware.proxy.replay import ReplayProxySystem
16
- from datetime import datetime
17
-
18
- import os
19
- import sys
20
- import json
21
- import enum
22
- import _thread
23
- import pickle
24
- import weakref
25
- import types
26
- from pathlib import Path
27
- import glob, os
28
- import re
29
-
30
- # from retracesoftware_proxy import *
31
- # from retracesoftware_utils import *
32
- # from retracesoftware.proxy import references
33
- from retracesoftware.install import patcher
34
- from retracesoftware.install.config import load_config
35
- # from retracesoftware.proxy.immutabletypes import ImmutableTypes
36
- # from retracesoftware.proxy import edgecases
37
- from retracesoftware.install import globals
38
- # from retracesoftware.proxy.record import RecordProxyFactory
39
-
40
-
41
- class DebugWriter:
42
- __slot__ = ['checkpoint']
43
-
44
- def __init__(self, checkpoint):
45
- self.checkpoint = checkpoint
46
-
47
- def write_call(self, func, *args, **kwargs):
48
- self.checkpoint({'type': 'call', 'func': ...})
49
-
50
- def write_result(self, res):
51
- self.checkpoint({'type': 'result', 'result': res})
52
-
53
- def write_error(self, *args):
54
- self.checkpoint({'type': 'error', 'error': tuple(args)})
55
-
56
- class MethodDescriptor:
57
- def __init__(self, descriptor):
58
- self.cls = descriptor.__objclass__
59
- self.name = descriptor.name
60
-
61
- def once(*args): return functional.memoize_one_arg(functional.sequence(*args))
62
-
63
- any = functional.firstof
64
-
65
-
66
- def compose(*args):
67
- new_args = [item for item in args if item is not None]
68
- if len(new_args) == 0:
69
- raise Exception('TODO')
70
- elif len(new_args) == 1:
71
- return new_args[0]
72
- else:
73
- return functional.compose(*new_args)
74
-
75
- class SerializedWrappedFunction:
76
- def __init__(self, func):
77
- if hasattr(func, '__objclass__'):
78
- self.cls = func.__objclass__
79
- elif hasattr(func, '__module__'):
80
- self.module = func.__module__
81
-
82
- if hasattr(func, '__name__'):
83
- self.name = func.__name__
84
-
85
-
86
- def replaying_proxy_factory(thread_state, is_immutable_type, tracer, next, bind, checkpoint):
87
-
88
- # def on_new_ext_proxytype(proxytype):
89
- # assert not issubclass(proxytype, DynamicProxy)
90
- # bind(proxytype)
91
- # writer.add_type_serializer(cls = proxytype, serializer = functional.typeof)
92
-
93
- # bind_new_int_proxy = functional.if_then_else(functional.isinstanceof(InternalProxy), functional.memoize_one_arg(bind), None)
94
-
95
- # on_ext_call = utils.visitor(from_arg = 1, function = bind_new_int_proxy)
96
-
97
- def wrap_int_call(handler):
98
- return functional.observer(
99
- on_call = tracer('proxy.int.call'),
100
- on_result = tracer('proxy.int.result'),
101
- on_error = tracer('proxy.int.error'),
102
- function = handler)
103
-
104
- # def is_stub_type(obj):
105
- # return type(obj) == type and issubclass(obj, (WrappingProxy, ExtendingProxy))
106
-
107
- def is_stub_type(obj):
108
- return type(obj) == type
109
-
110
- create_stubs = functional.walker(functional.when(is_stub_type, utils.create_stub_object))
111
- # create_stubs = functional.walker(functional.when(is_stub_type, utils.create_stub_object))
112
-
113
- def wrap_ext_call(handler):
114
- return functional.observer(
115
- on_call = tracer('proxy.ext.call'),
116
- on_result = tracer('proxy.ext.result'),
117
- on_error = tracer('proxy.ext.error'),
118
- function = functional.sequence(functional.always(next), create_stubs))
119
-
120
- return ProxyFactory(thread_state = thread_state,
121
- is_immutable_type = is_immutable_type,
122
- tracer = tracer,
123
- on_new_int_proxy = bind,
124
- # on_new_ext_proxytype = on_new_ext_proxytype,
125
- wrap_int_call = wrap_int_call,
126
- wrap_ext_call = wrap_ext_call)
127
-
128
-
129
- # class Reader:
130
-
131
- # def __init__(self, objectreader):
132
- # self.objectreader = objectreader
133
-
134
- # self.objectreader()
135
-
136
- def tracing_config(config):
137
- level = os.environ.get('RETRACE_DEBUG', config['default_tracing_level'])
138
- return config['tracing_levels'].get(level, {})
139
-
140
-
141
- def install(create_system):
142
- return patcher.install(config = load_config('config.json'), create_system = create_system)