retracesoftware-proxy 0.0.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.
Files changed (41) hide show
  1. retracesoftware/__init__.py +0 -0
  2. retracesoftware/__main__.py +266 -0
  3. retracesoftware/autoenable.py +53 -0
  4. retracesoftware/config.json +175 -0
  5. retracesoftware/config.yaml +0 -0
  6. retracesoftware/install/__init__.py +0 -0
  7. retracesoftware/install/config.py +59 -0
  8. retracesoftware/install/edgecases.py +242 -0
  9. retracesoftware/install/globals.py +17 -0
  10. retracesoftware/install/install.py +142 -0
  11. retracesoftware/install/patcher.py +122 -0
  12. retracesoftware/install/patchfindspec.py +117 -0
  13. retracesoftware/install/phases.py +338 -0
  14. retracesoftware/install/predicate.py +92 -0
  15. retracesoftware/install/record.py +174 -0
  16. retracesoftware/install/references.py +66 -0
  17. retracesoftware/install/replace.py +28 -0
  18. retracesoftware/install/replay.py +102 -0
  19. retracesoftware/install/tracer.py +284 -0
  20. retracesoftware/install/typeutils.py +92 -0
  21. retracesoftware/modules.toml +384 -0
  22. retracesoftware/proxy/__init__.py +3 -0
  23. retracesoftware/proxy/gateway.py +49 -0
  24. retracesoftware/proxy/globalref.py +31 -0
  25. retracesoftware/proxy/messagestream.py +204 -0
  26. retracesoftware/proxy/proxyfactory.py +357 -0
  27. retracesoftware/proxy/proxysystem.py +454 -0
  28. retracesoftware/proxy/proxytype.py +424 -0
  29. retracesoftware/proxy/record.py +211 -0
  30. retracesoftware/proxy/replay.py +138 -0
  31. retracesoftware/proxy/serializer.py +28 -0
  32. retracesoftware/proxy/startthread.py +40 -0
  33. retracesoftware/proxy/stubfactory.py +195 -0
  34. retracesoftware/proxy/thread.py +106 -0
  35. retracesoftware/replay.py +104 -0
  36. retracesoftware/run.py +373 -0
  37. retracesoftware/stackdifference.py +133 -0
  38. retracesoftware_proxy-0.0.0.dist-info/METADATA +8 -0
  39. retracesoftware_proxy-0.0.0.dist-info/RECORD +41 -0
  40. retracesoftware_proxy-0.0.0.dist-info/WHEEL +5 -0
  41. retracesoftware_proxy-0.0.0.dist-info/top_level.txt +1 -0
retracesoftware/run.py ADDED
@@ -0,0 +1,373 @@
1
+ import sys
2
+ import os
3
+ import runpy
4
+ from pathlib import Path
5
+ # from retracesoftware.install.phases import *
6
+ import pkgutil
7
+ import tomllib
8
+ import retracesoftware.functional as functional
9
+ import builtins
10
+ import importlib
11
+ import _imp
12
+ import importlib._bootstrap_external as _bootstrap_external
13
+ import atexit
14
+ import threading
15
+ import _signal
16
+ import retracesoftware.utils as utils
17
+ from retracesoftware.install.patcher import patch_module, create_patcher, patch_imported_module
18
+ from retracesoftware.proxy.startthread import patch_thread_start
19
+ from retracesoftware.install.replace import update
20
+
21
+ thread_states = [
22
+ "disabled", # Default state when retrace is disabled for a thread
23
+ "internal", # Default state when retrace is disabled for a thread
24
+ "external", # When target thread is running outside the python system to be recorded
25
+ "retrace", # When target thread is running outside the retrace system
26
+ "importing", # When target thread is running outside the retrace system
27
+ "gc", # When the target thread is running inside the pyton garbage collector
28
+ ]
29
+
30
+ class ImmutableTypes(set):
31
+ def __init__(self, *args, **kwargs):
32
+ super().__init__(*args, **kwargs)
33
+
34
+ def __contains__(self, item):
35
+ assert isinstance(item, type)
36
+
37
+ if super().__contains__(item):
38
+ return True
39
+
40
+ for elem in self:
41
+ if issubclass(item, elem):
42
+ self.add(item)
43
+ return True
44
+
45
+ return False
46
+
47
+ def load_module_config(filename):
48
+ data = pkgutil.get_data("retracesoftware", filename)
49
+ assert data is not None
50
+ return tomllib.loads(data.decode("utf-8"))
51
+
52
+ def wait_for_non_daemon_threads(timeout=None):
53
+ """
54
+ Wait for all non-daemon threads to finish, just like Python does on exit.
55
+
56
+ Args:
57
+ timeout (float, optional): Max seconds to wait. None = wait forever.
58
+
59
+ Returns:
60
+ bool: True if all threads finished, False if timeout exceeded.
61
+ """
62
+ import threading
63
+ import time
64
+
65
+ start_time = time.time()
66
+ main_thread = threading.main_thread()
67
+
68
+ while True:
69
+ # Get all active threads
70
+ active = threading.enumerate()
71
+
72
+ # Filter: non-daemon and not the main thread
73
+ non_daemon_threads = [
74
+ t for t in active
75
+ if t is not main_thread and not t.daemon
76
+ ]
77
+
78
+ if not non_daemon_threads:
79
+ return True # All done!
80
+
81
+ # Check timeout
82
+ if timeout is not None:
83
+ elapsed = time.time() - start_time
84
+ if elapsed >= timeout:
85
+ print(f"Timeout: {len(non_daemon_threads)} thread(s) still alive")
86
+ return False
87
+
88
+ # Sleep briefly to avoid busy-wait
89
+ time.sleep(0.1)
90
+
91
+ def run_python_command(argv):
92
+ """
93
+ Run a Python app from a command list using runpy.
94
+
95
+ Supports:
96
+ ['-m', 'module', 'arg1', ...] → like `python -m module arg1 ...`
97
+ ['script.py', 'arg1', ...] → like `python script.py arg1 ...`
98
+
99
+ Args:
100
+ argv: List of command-line arguments (first item is either '-m' or script path)
101
+
102
+ Returns:
103
+ Exit code (0 on success, 1+ on error)
104
+ """
105
+ if not argv:
106
+ print("Error: No command provided", file=sys.stderr)
107
+ return 1
108
+
109
+ original_argv = sys.argv[:]
110
+ original_cwd = os.getcwd()
111
+
112
+ try:
113
+ if argv[0] == '-m':
114
+ if len(argv) < 2:
115
+ print("Error: -m requires a module name", file=sys.stderr)
116
+ return 1
117
+ module_name = argv[1]
118
+ module_args = argv[2:]
119
+ sys.argv = ['-m', module_name] + module_args
120
+ runpy.run_module(module_name, run_name="__main__")
121
+ return 0
122
+
123
+ else:
124
+ script_path = argv[0]
125
+ script_args = argv[1:]
126
+ path = Path(script_path)
127
+
128
+ if not path.exists():
129
+ print(f"Error: Script not found: {script_path}", file=sys.stderr)
130
+ return 1
131
+
132
+ if path.suffix != ".py":
133
+ print(f"Error: Not a Python script: {script_path}", file=sys.stderr)
134
+ return 1
135
+
136
+ full_path = str(path.resolve())
137
+ sys.argv = [full_path] + script_args
138
+
139
+ # Change to script's directory
140
+ os.chdir(path.parent)
141
+
142
+ runpy.run_path(full_path, run_name="__main__")
143
+ return 0
144
+
145
+ except ModuleNotFoundError:
146
+ print(f"Error: No module named '{argv[1]}'", file=sys.stderr)
147
+ return 1
148
+ except Exception as e:
149
+ print(f"Error: {e}", file=sys.stderr)
150
+ return 1
151
+ finally:
152
+ sys.argv = original_argv
153
+ os.chdir(original_cwd)
154
+
155
+ def patch_import(thread_state, patcher, sync, checkpoint):
156
+ # builtins.__import__ = thread_state.dispatch(builtins.__import__, internal = sync(thread_state.wrap('importing', builtins.__import__)))
157
+ # bi = builtins.__import__
158
+ # def foo(*args, **kwargs):
159
+ # print(f'in patched __import__: {thread_state.value} {args[0]}')
160
+ # if thread_state.value == 'internal':
161
+ # with thread_state.select('importing'):
162
+ # return bi(*args, **kwargs)
163
+ # else:
164
+ # return bi(*args, **kwargs)
165
+
166
+ # builtins.__import__ = foo
167
+
168
+ builtins.__import__ = thread_state.dispatch(builtins.__import__, internal = thread_state.wrap('importing', builtins.__import__))
169
+
170
+ def exec(source, globals = None, locals = None):
171
+ checkpoint(f"exec module: {globals.get('__name__', 'unknown')}")
172
+ res = builtins.exec(source, globals, locals)
173
+ patcher(globals, False)
174
+ return res
175
+
176
+ def patch(module):
177
+ patcher(module.__dict__, False)
178
+ return module
179
+
180
+ _imp.exec_dynamic = thread_state.dispatch(_imp.exec_dynamic,
181
+ importing = functional.juxt(
182
+ thread_state.wrap('internal', _imp.exec_dynamic),
183
+ patch))
184
+ # _imp.exec_dynamic = lambda mod: utils.sigtrap(f'{thread_state.value} - {mod}')
185
+ # _imp.exec_builtin = lambda mod: utils.sigtrap(f'{thread_state.value} - {mod}')
186
+
187
+ _imp.exec_builtin = thread_state.dispatch(_imp.exec_builtin,
188
+ importing = functional.juxt(
189
+ thread_state.wrap('internal', _imp.exec_builtin),
190
+ patch))
191
+
192
+ # def runpy_exec(source, globals = None, locals = None):
193
+ # print(f'In runpy exec!!!!!')
194
+ # return builtins.exec(source, globals, locals)
195
+
196
+ # utils.update(runpy, "_run_code",
197
+ # utils.wrap_func_with_overrides,
198
+ # exec = runpy_exec)
199
+
200
+ utils.update(_bootstrap_external._LoaderBasics, "exec_module",
201
+ utils.wrap_func_with_overrides,
202
+ exec = thread_state.dispatch(builtins.exec, importing = thread_state.wrap('internal', sync(exec))))
203
+
204
+ preload = [
205
+ "logging",
206
+ "pathlib",
207
+ "_signal",
208
+ "_posixsubprocess",
209
+ "socket",
210
+ "select",
211
+ "ssl",
212
+ "random",
213
+ "email",
214
+ "email.errors",
215
+ "http.client",
216
+
217
+ "json",
218
+ "typing",
219
+ "queue",
220
+ "mimetypes",
221
+ "tempfile",
222
+ "zipfile",
223
+ "importlib.resources",
224
+ "importlib.metadata",
225
+ "encodings.idna"
226
+
227
+ # "http.client",
228
+ # "queue",
229
+ # "mimetypes",
230
+ # "encodings.idna",
231
+ # "hmac",
232
+ # "ipaddress",
233
+ # "tempfile",
234
+ # "zipfile",
235
+ # "importlib.resources",
236
+ # "importlib.metadata",
237
+ # "atexit",
238
+ # "weakref"
239
+ ]
240
+
241
+ # def debugger_is_active():
242
+ # return sys.gettrace() and 'debugpy' in sys.modules
243
+
244
+ def init_weakref():
245
+ import weakref
246
+ def dummy_callback():
247
+ pass
248
+
249
+ class DummyTarget:
250
+ pass
251
+
252
+ f = weakref.finalize(DummyTarget(), dummy_callback)
253
+ f.detach()
254
+
255
+ def wrapped_weakref(ref, thread_state, wrap_callback):
256
+ orig_new = ref.__new__
257
+
258
+ def __new__(cls, ob, callback=None, **kwargs):
259
+ return orig_new(cls, ob, wrap_callback(callback) if callback else None)
260
+
261
+ return type('ref', (ref, ), {'__new__': thread_state.dispatch(orig_new, internal = __new__)})
262
+
263
+ def patch_weakref(thread_state, wrap_callback):
264
+ import _weakref
265
+
266
+ update(_weakref.ref, wrapped_weakref(_weakref.ref, thread_state, wrap_callback))
267
+ # _weakref.ref = wrapped_weakref(_weakref.ref)
268
+
269
+ # def patch_signal(thread_state, wrap_callback):
270
+
271
+ # def wrap_handler(handler):
272
+ # return utils.observer(on_call = ..., function = handler)
273
+
274
+ # _signal.signal =
275
+ # update(_signal.signal, thread_state.dispatch(_signal.signal, internal = thread_state.wrap('internal', _signal.signal)))
276
+
277
+
278
+
279
+ def install(system):
280
+
281
+ patch_weakref(thread_state = system.thread_state, wrap_callback = system.wrap_weakref_callback)
282
+
283
+ init_weakref()
284
+
285
+ for name in preload:
286
+ importlib.import_module(name)
287
+
288
+ # if 'pydevd' in sys.modules:
289
+ # utils.update(sys.modules['pydevd'].PyDB, 'enable_tracing', system.disable_for)
290
+ # utils.update(sys.modules['pydevd'].PyDB, 'set_suspend', system.disable_for)
291
+ # utils.update(sys.modules['pydevd'].PyDB, 'do_wait_suspend', system.disable_for)
292
+
293
+ # if '_pydevd_bundle.pydevd_trace_dispatch_regular' in sys.modules:
294
+ # mod = sys.modules['_pydevd_bundle.pydevd_trace_dispatch_regular']
295
+ # utils.update(mod.ThreadTracer, '__call__', system.disable_for)
296
+
297
+ for function in utils.stack_functions():
298
+ system.exclude_from_stacktrace(function)
299
+
300
+ def recursive_disable(func):
301
+ if not callable(func):
302
+ return func
303
+
304
+ def wrapped(*args, **kwargs):
305
+ with system.thread_state.select('disabled'):
306
+ return recursive_disable(func(*args, **kwargs))
307
+
308
+ return wrapped
309
+
310
+ sys.settrace = functional.sequence(recursive_disable, sys.settrace)
311
+ sys.setprofile = functional.sequence(recursive_disable, sys.setprofile)
312
+ threading.settrace = functional.sequence(recursive_disable, threading.settrace)
313
+
314
+ sys.settrace(sys.gettrace())
315
+ sys.setprofile(sys.getprofile())
316
+
317
+ system.checkpoint('About to install retrace system')
318
+
319
+ module_config = load_module_config('modules.toml')
320
+
321
+ patch_loaded = functional.partial(patch_module, create_patcher(system), module_config)
322
+ patch_imported = functional.partial(patch_imported_module, create_patcher(system), system.checkpoint, module_config)
323
+
324
+ system.checkpoint('Started installing system 1')
325
+
326
+ for modname in module_config.keys():
327
+ if modname in sys.modules:
328
+ patch_loaded(sys.modules[modname].__dict__, True)
329
+
330
+ system.checkpoint('About to patch threading')
331
+
332
+ patch_thread_start(system.thread_state)
333
+ threading.current_thread().__retrace__ = system
334
+
335
+ system.checkpoint('About to patch import')
336
+ patch_import(thread_state = system.thread_state,
337
+ patcher = patch_imported,
338
+ sync = system.sync,
339
+ checkpoint = system.checkpoint)
340
+
341
+ # print(f'MODULES: {list(sys.modules.keys())}')
342
+
343
+ importlib.import_module = \
344
+ system.thread_state.dispatch(system.disable_for(importlib.import_module),
345
+ internal = system.thread_state.wrap('importing', importlib.import_module))
346
+
347
+
348
+ system.checkpoint('About to patch preload libraries')
349
+
350
+ system.checkpoint('system patched...')
351
+
352
+ def run_with_retrace(system, argv, trace_shutdown = False):
353
+
354
+ def runpy_exec(source, globals = None, locals = None):
355
+ with system.thread_state.select('internal'):
356
+ return builtins.exec(source, globals, locals)
357
+
358
+ utils.update(runpy, "_run_code",
359
+ utils.wrap_func_with_overrides,
360
+ exec = runpy_exec)
361
+
362
+ try:
363
+ run_python_command(argv)
364
+ finally:
365
+ wait_for_non_daemon_threads()
366
+ try:
367
+ if trace_shutdown:
368
+ with system.thread_state.select('internal'):
369
+ atexit._run_exitfuncs()
370
+ else:
371
+ atexit._run_exitfuncs()
372
+ except Exception as e:
373
+ print(f"Error in atexit hook: {e}", file=sys.stderr)
@@ -0,0 +1,133 @@
1
+ import itertools
2
+ import inspect
3
+ import ast
4
+ from typing import Optional
5
+ import importlib
6
+ import retracesoftware.utils as utils
7
+
8
+ def frozen_to_module(name: str):
9
+ """
10
+ Given a frozen filename like "<frozen importlib._bootstrap>",
11
+ return the actual module object (or None if not frozen).
12
+ """
13
+ if not name.startswith("<frozen ") or not name.endswith(">"):
14
+ return None
15
+
16
+ # Extract the part between <frozen ...>
17
+ module_name = name[len("<frozen "):-1]
18
+
19
+ # Special case: the stdlib ships some as _frozen_importlib and _frozen_importlib_external
20
+ if module_name == "importlib._bootstrap":
21
+ return sys.modules.get("_frozen_importlib") or importlib._bootstrap
22
+ if module_name == "importlib._bootstrap_external":
23
+ return sys.modules.get("_frozen_importlib_external") or importlib._bootstrap_external
24
+
25
+ # Everything else is directly importable
26
+ return importlib.import_module(module_name)
27
+
28
+ def get_function_at_line(filename: str, source: str, lineno: int) -> Optional[str]:
29
+ """
30
+ Return the name of the function (or class/method) that contains the given line number.
31
+
32
+ Returns None if:
33
+ - file not found
34
+ - syntax error
35
+ - line is outside any function (module level)
36
+ """
37
+ # except FileNotFoundError:
38
+ # return None
39
+ # except UnicodeDecodeError:
40
+ # return None
41
+
42
+ try:
43
+ tree = ast.parse(source, filename=filename)
44
+ except SyntaxError:
45
+ return None
46
+
47
+ class FunctionFinder(ast.NodeVisitor):
48
+ def __init__(self, target_lineno: int):
49
+ self.target_lineno = target_lineno
50
+ self.current: Optional[str] = None
51
+ self.stack: list[str] = []
52
+
53
+ def visit_FunctionDef(self, node):
54
+ if node.lineno <= self.target_lineno <= node.end_lineno:
55
+ # breakpoint()
56
+ self.stack.append(node.name)
57
+ self.current = node.name
58
+ self.generic_visit(node)
59
+ self.stack.pop()
60
+ elif self.stack:
61
+ # We're inside a nested function/class, keep traversing
62
+ self.generic_visit(node)
63
+
64
+ def visit_AsyncFunctionDef(self, node):
65
+ self.visit_FunctionDef(node) # same logic
66
+
67
+ def visit_ClassDef(self, node):
68
+ if node.lineno <= self.target_lineno <= node.end_lineno:
69
+ self.stack.append(node.name)
70
+ # Don't set current here — we prefer method names
71
+ self.generic_visit(node)
72
+ self.stack.pop()
73
+
74
+ # def get_result(self) -> Optional[str]:
75
+ # if not self.stack:
76
+ # return None
77
+ # # Return the deepest (most specific) function/method name
78
+ # return self.current
79
+ # # return self.stack[-1]
80
+
81
+ finder = FunctionFinder(lineno)
82
+ finder.visit(tree)
83
+ # print(f'foind: {finder.current}')
84
+ return finder.current
85
+
86
+ def get_source(filename):
87
+ try:
88
+ with open(filename, "r", encoding="utf-8") as f:
89
+ return f.read()
90
+ except FileNotFoundError:
91
+ return inspect.getsource(frozen_to_module(filename))
92
+
93
+ def all_elements_same(t):
94
+ return len(set(t)) <= 1
95
+
96
+ def first(coll): return coll[0]
97
+
98
+ def common_prefix(*colls):
99
+ return list(map(first, itertools.takewhile(all_elements_same, zip(*colls))))
100
+
101
+ def render_stack(frames):
102
+ for filename,lineno in frames:
103
+ try:
104
+ source = get_source(filename)
105
+ print(f'File "{filename}", line {lineno}, in {get_function_at_line(filename, source, lineno)}')
106
+ print(f' {source.splitlines()[lineno - 1].lstrip()}')
107
+ except Exception:
108
+ print(f'File not found: "{filename}", line {lineno}')
109
+
110
+ def on_stack_difference(previous, record, replay):
111
+
112
+ # previous = [x for x in previous if x not in excludes]
113
+ # record = [x for x in record if x not in excludes]
114
+ # replay = [x for x in replay if x not in excludes]
115
+
116
+ if record != replay:
117
+ common = common_prefix(previous, record, replay) if previous else common_prefix(record, replay)
118
+
119
+ if common:
120
+ print('Common root:')
121
+ render_stack(common)
122
+
123
+ if previous:
124
+ print('\nlast matching:')
125
+ render_stack(previous[len(common):])
126
+
127
+ print('\nrecord:')
128
+ render_stack(record[len(common):])
129
+
130
+ print('\nreplay:')
131
+ render_stack(replay[len(common):])
132
+
133
+ utils.sigtrap('on_stack_difference!!!!!')
@@ -0,0 +1,8 @@
1
+ Metadata-Version: 2.4
2
+ Name: retracesoftware_proxy
3
+ Version: 0.0.0
4
+ License: Apache-2.0
5
+ Requires-Python: >=3.11
6
+ Requires-Dist: retracesoftware_utils
7
+ Requires-Dist: retracesoftware_functional
8
+ Requires-Dist: retracesoftware_stream
@@ -0,0 +1,41 @@
1
+ retracesoftware/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
2
+ retracesoftware/__main__.py,sha256=legPSoOYPrfnB_t2v5NsdGuoBXjFRd8qub98QRXCtFM,8408
3
+ retracesoftware/autoenable.py,sha256=s7dSykM0y3xVqRzrWouGgoed4-lMNRhdA7Gnm1f04NA,1772
4
+ retracesoftware/config.json,sha256=Cw5YZCfTo8GLmtbTH2LYwST7wNe7925DAwsTtRIGhBE,3817
5
+ retracesoftware/config.yaml,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
6
+ retracesoftware/modules.toml,sha256=C3O4LnHwWInRT9ZSB-crSM8Q98vYCZSFJB3akGJLfVM,14536
7
+ retracesoftware/replay.py,sha256=rKuhen_MDDi_5WVDmQgchxJ0ZrCbYL1yZZboZsu_q54,3601
8
+ retracesoftware/run.py,sha256=ovunjJjDAe23ik-KbYrLUseb9u2bZ26uZNg-QIPENMc,11966
9
+ retracesoftware/stackdifference.py,sha256=nM9r5YHMCNQcrWozMjUX4ZTrZL5U0rQChSGzpiXy_DU,4466
10
+ retracesoftware/install/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
11
+ retracesoftware/install/config.py,sha256=AnSlHI3lWQrPUg0fOS26PqXiq1hJSy56SwrNL8mbAG8,1546
12
+ retracesoftware/install/edgecases.py,sha256=zv6xjUFoiuNWeBhIDIKb7kN6DVv8gxoG797sNV2ak8Y,7099
13
+ retracesoftware/install/globals.py,sha256=F8XvIoZQQ10gSRalk30dvdKllxlwxkaggYY6FogLDxY,510
14
+ retracesoftware/install/install.py,sha256=HCD_ji8XCr96b5fNzNdL_8qcEp0Jf05Em7T6GA6u8HU,4969
15
+ retracesoftware/install/patcher.py,sha256=sHgsKvdvjMJSVl8lOa3YiCjGf6zYhxgkWp67-ptJvlk,4157
16
+ retracesoftware/install/patchfindspec.py,sha256=uQkfdOQOY20hFAcwDAqx7m8rbtGM2rBKOfC3Szb1zhw,4942
17
+ retracesoftware/install/phases.py,sha256=OWaw86xzC924tdIA9C-12Qdr3sCx67hEOn-Ih9FM--4,9359
18
+ retracesoftware/install/predicate.py,sha256=tX7NQc0rGkyyHYO3mduYHcJHbw1wczT53m_Dpkzo6do,2679
19
+ retracesoftware/install/record.py,sha256=08q7zc9cfyCyhLsh15q1d_5vFIhKbs9ln6VvL6kED5o,5861
20
+ retracesoftware/install/references.py,sha256=A-G651IDOfuo00MkbAdpbIQh_15ChvJ7uAVTSmE6zd4,1721
21
+ retracesoftware/install/replace.py,sha256=FYiSJtNrXEhl-H6M5tJm0kbliBA0sZdxE0306pr-YQg,872
22
+ retracesoftware/install/replay.py,sha256=bB2eMpIP2vXZzeJ98jtlkGSE1DEYWk3olUbiBQcdVj0,3539
23
+ retracesoftware/install/tracer.py,sha256=GO3Nnzd0ylxM7-A7RN0LzLCFdouJIiHtIcJ2zjFx0Q0,9363
24
+ retracesoftware/install/typeutils.py,sha256=-oFzgUfq_nHeOkj3YKZiMLlMzQhCedg3qymLiEJNkVE,2253
25
+ retracesoftware/proxy/__init__.py,sha256=ntIyqKhBRkKEkcW_oOPodikh-mxYl8OXRnSaj-9-Xwc,178
26
+ retracesoftware/proxy/gateway.py,sha256=xESohWXkiNm4ZutU0RgWUwxjxcBWRQ4rQyxIGQXv_F4,1590
27
+ retracesoftware/proxy/globalref.py,sha256=yXtJsOeBHN9xoEgJWA3MJco-jD2SQUef_fDatA4A6rg,803
28
+ retracesoftware/proxy/messagestream.py,sha256=kjxIugCSr7YSJbIqsqqXeW7dyE2SzWow2E-UJnkAflk,5890
29
+ retracesoftware/proxy/proxyfactory.py,sha256=qhOqDfMJnLDNkQs26JqDB431MwjjRhGQi8xupJ45asg,12272
30
+ retracesoftware/proxy/proxysystem.py,sha256=Pj9sk5FE3Mfyf4-PAtXSz2Pvr_GwXcpfhUSJvkz00dg,15076
31
+ retracesoftware/proxy/proxytype.py,sha256=LZ1rNMsxXRUMdI4uTzDvGkUyPOYxCFroT_ZAtOGTbBk,14012
32
+ retracesoftware/proxy/record.py,sha256=JTmeZ13ISbsVal1WOtae425DP0iA-QiAv0C6azPpV8k,6680
33
+ retracesoftware/proxy/replay.py,sha256=Kx4XrsP0T35IvTzf3pwhR24Lotoz43p7E8LBevEQHpE,4948
34
+ retracesoftware/proxy/serializer.py,sha256=S5yhHoP2iYaXBY7Jr8Ei630Z31521x0IO6DeuClOD9s,958
35
+ retracesoftware/proxy/startthread.py,sha256=YKUcOSjA-9_5ZsDHhyP0fXJSP3RWDKV-ajX-I0SI-bU,1270
36
+ retracesoftware/proxy/stubfactory.py,sha256=fyQtCZnkXvLaTlGcxE0Lx8od09ZOgnPWPn0YvNFfVeQ,6219
37
+ retracesoftware/proxy/thread.py,sha256=T1ME6DHB8O0xVnX3Rt1lMl7oCJ2Y0aoFT91D76yNICk,3073
38
+ retracesoftware_proxy-0.0.0.dist-info/METADATA,sha256=mJIUCRnUE3D2fGhntlAEyiUYTf22UT94U4KleWa9i3s,226
39
+ retracesoftware_proxy-0.0.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
40
+ retracesoftware_proxy-0.0.0.dist-info/top_level.txt,sha256=hYHsR6txLidmqvjBMITpIHvmJJbmoCAgr76-IpZPRz8,16
41
+ retracesoftware_proxy-0.0.0.dist-info/RECORD,,
@@ -0,0 +1,5 @@
1
+ Wheel-Version: 1.0
2
+ Generator: setuptools (80.9.0)
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
5
+
@@ -0,0 +1 @@
1
+ retracesoftware