retracesoftware-proxy 0.2.18__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,36 +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
 
93
146
  if options.verbose:
94
147
  print(f"Retrace enabled, recording to {options.recording}", file=sys.stderr)
95
148
 
96
149
  path = Path(expand_recording_path(options.recording))
97
- # ensure the path exists
98
- 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)
99
156
 
100
157
  from retracesoftware.install import edgecases
101
158
  edgecases.recording_path = path
102
159
 
103
160
  # write various recording files to directory
161
+ path_info = stream.get_path_info()
104
162
  dump_as_json(path / 'settings.json', {
105
163
  'argv': args,
106
164
  'executable': sys.executable,
107
165
  'magic_markers': options.magic_markers,
108
166
  'trace_inputs': options.trace_inputs,
109
167
  'trace_shutdown': options.trace_shutdown,
110
- 'env': dict(os.environ),
111
168
  'python_version': sys.version,
112
- 'md5_checksums': checksums(),
169
+ 'cwd': path_info['cwd'],
170
+ 'sys_path': path_info['sys_path'],
113
171
  })
114
-
115
- rundir = path / 'run'
116
- rundir.mkdir(exist_ok=True)
117
-
118
- script = Path(scriptname(args))
119
- if script.exists():
120
- 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')
121
180
 
122
181
  # create a .venv directory in the recording path
123
182
  # venv_path = path / '.venv'
@@ -178,24 +237,32 @@ def record(options, args):
178
237
  gc.callbacks.remove(system.on_gc_event)
179
238
 
180
239
  def replay(args):
181
- path = Path(args.recording)
240
+ # Resolve path before any chdir
241
+ path = Path(args.recording).resolve()
182
242
 
183
243
  if not path.exists():
184
244
  raise RecordingNotFoundError(f"Recording path: {path} does not exist")
185
245
 
186
246
  settings = load_json(path / "settings.json")
247
+ recorded_checksums = load_json(path / "md5_checksums.json")
187
248
 
188
- if settings['md5_checksums'] != checksums():
189
- 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}")
190
254
 
191
255
  if settings['python_version'] != sys.version:
192
256
  raise VersionMismatchError("Python version does not match, cannot run replay with different version of Python to record")
193
257
 
194
- os.environ.update(settings['env'])
258
+ os.environ.update(load_env(path / '.env'))
195
259
 
196
260
  if sys.executable != settings['executable']:
197
261
  raise ConfigurationError(f"Stopping replay as current python executable: {sys.executable} is not what was used for record: {settings['executable']}")
198
262
 
263
+ # Change to recorded cwd - replay from same directory as recording
264
+ os.chdir(settings['cwd'])
265
+
199
266
  thread_state = utils.ThreadState(*thread_states)
200
267
 
201
268
  # with stream.reader(path = path / 'trace.bin',
@@ -226,6 +293,7 @@ def replay(args):
226
293
  gc.collect()
227
294
  gc.disable()
228
295
 
296
+ # Use original argv - scripts are now relative to cwd (recording/run)
229
297
  run_with_retrace(system, settings['argv'], settings['trace_shutdown'])
230
298
 
231
299
  def main():
@@ -272,6 +340,13 @@ def main():
272
340
  help='Whether to write call parameters, used for debugging'
273
341
  )
274
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
+
275
350
  parser.add_argument('rest', nargs = argparse.REMAINDER, help='target application and arguments')
276
351
 
277
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
 
@@ -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.18
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=FwxTltjG1CnARYD4bdPLNiTTc8cF7WryLdBSDSg0zF0,9871
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
16
  retracesoftware/install/patcher.py,sha256=Z98ux8SDw53wyRKCevOT-tPOFcwcM3KH1dyR7Tg_TJ4,5859
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
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.18.dist-info/METADATA,sha256=KZeS6LA5Mqcp9H-ofq3949xU3ad9DmQdhp7sDmuw1k0,227
43
- retracesoftware_proxy-0.2.18.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
44
- retracesoftware_proxy-0.2.18.dist-info/top_level.txt,sha256=hYHsR6txLidmqvjBMITpIHvmJJbmoCAgr76-IpZPRz8,16
45
- retracesoftware_proxy-0.2.18.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)