retracesoftware-proxy 0.2.12__py3-none-any.whl → 0.2.15__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.
- retracesoftware/__init__.py +20 -0
- retracesoftware/__main__.py +6 -5
- retracesoftware/autoenable.py +9 -4
- retracesoftware/exceptions.py +34 -0
- retracesoftware/install/install.py +1 -1
- retracesoftware/install/modulepatcher.py +506 -0
- retracesoftware/install/patcher.py +6 -1
- retracesoftware/install/record.py +1 -1
- retracesoftware/install/tracer.py +1 -1
- retracesoftware/install/typeutils.py +1 -1
- retracesoftware/modules.toml +2 -3
- retracesoftware/proxy/gateway.py +1 -1
- retracesoftware/proxy/messagestream.py +1 -1
- retracesoftware/proxy/proxyfactory.py +2 -2
- retracesoftware/proxy/proxysystem.py +5 -5
- retracesoftware/proxy/record.py +15 -0
- retracesoftware/proxy/thread.py +1 -1
- retracesoftware/replay.py +2 -1
- retracesoftware/retrace.pth +1 -0
- retracesoftware/run.py +3 -3
- {retracesoftware_proxy-0.2.12.dist-info → retracesoftware_proxy-0.2.15.dist-info}/METADATA +1 -1
- retracesoftware_proxy-0.2.15.dist-info/RECORD +45 -0
- {retracesoftware_proxy-0.2.12.dist-info → retracesoftware_proxy-0.2.15.dist-info}/WHEEL +1 -1
- retracesoftware_proxy-0.2.12.dist-info/RECORD +0 -42
- {retracesoftware_proxy-0.2.12.dist-info → retracesoftware_proxy-0.2.15.dist-info}/top_level.txt +0 -0
retracesoftware/__init__.py
CHANGED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
# Extend namespace to include retracesoftware.utils, retracesoftware.functional, etc.
|
|
2
|
+
__path__ = __import__('pkgutil').extend_path(__path__, __name__)
|
|
3
|
+
|
|
4
|
+
from retracesoftware.exceptions import (
|
|
5
|
+
RetraceError,
|
|
6
|
+
RecordError,
|
|
7
|
+
ReplayError,
|
|
8
|
+
ConfigurationError,
|
|
9
|
+
VersionMismatchError,
|
|
10
|
+
RecordingNotFoundError,
|
|
11
|
+
)
|
|
12
|
+
|
|
13
|
+
__all__ = [
|
|
14
|
+
'RetraceError',
|
|
15
|
+
'RecordError',
|
|
16
|
+
'ReplayError',
|
|
17
|
+
'ConfigurationError',
|
|
18
|
+
'VersionMismatchError',
|
|
19
|
+
'RecordingNotFoundError',
|
|
20
|
+
]
|
retracesoftware/__main__.py
CHANGED
|
@@ -20,6 +20,7 @@ import gc
|
|
|
20
20
|
import hashlib
|
|
21
21
|
|
|
22
22
|
from retracesoftware.run import install, run_with_retrace, ImmutableTypes, thread_states
|
|
23
|
+
from retracesoftware.exceptions import RecordingNotFoundError, VersionMismatchError, ConfigurationError
|
|
23
24
|
|
|
24
25
|
def expand_recording_path(path):
|
|
25
26
|
return datetime.datetime.now().strftime(path.format(pid = os.getpid()))
|
|
@@ -74,7 +75,7 @@ def checksum(path):
|
|
|
74
75
|
return file_md5(path) if path.is_file() else {entry.name: checksum(entry) for entry in path.iterdir() if entry.name != '__pycache__'}
|
|
75
76
|
|
|
76
77
|
def retrace_extension_paths():
|
|
77
|
-
names = ['retracesoftware_utils', '
|
|
78
|
+
names = ['retracesoftware_utils', '_retracesoftware_functional', 'retracesoftware_stream']
|
|
78
79
|
return {name: Path(sys.modules[name].__file__) for name in names}
|
|
79
80
|
|
|
80
81
|
def retrace_module_paths():
|
|
@@ -175,20 +176,20 @@ def replay(args):
|
|
|
175
176
|
path = Path(args.recording)
|
|
176
177
|
|
|
177
178
|
if not path.exists():
|
|
178
|
-
raise
|
|
179
|
+
raise RecordingNotFoundError(f"Recording path: {path} does not exist")
|
|
179
180
|
|
|
180
181
|
settings = load_json(path / "settings.json")
|
|
181
182
|
|
|
182
183
|
if settings['md5_checksums'] != checksums():
|
|
183
|
-
raise
|
|
184
|
+
raise VersionMismatchError("Checksums for Retrace do not match, cannot run replay with different version of retrace to record")
|
|
184
185
|
|
|
185
186
|
if settings['python_version'] != sys.version:
|
|
186
|
-
raise
|
|
187
|
+
raise VersionMismatchError("Python version does not match, cannot run replay with different version of Python to record")
|
|
187
188
|
|
|
188
189
|
os.environ.update(settings['env'])
|
|
189
190
|
|
|
190
191
|
if sys.executable != settings['executable']:
|
|
191
|
-
raise
|
|
192
|
+
raise ConfigurationError(f"Stopping replay as current python executable: {sys.executable} is not what was used for record: {settings['executable']}")
|
|
192
193
|
|
|
193
194
|
thread_state = utils.ThreadState(*thread_states)
|
|
194
195
|
|
retracesoftware/autoenable.py
CHANGED
|
@@ -1,12 +1,17 @@
|
|
|
1
1
|
if __name__ == "__main__":
|
|
2
2
|
import sysconfig
|
|
3
3
|
import pathlib
|
|
4
|
+
import shutil
|
|
4
5
|
|
|
5
|
-
#
|
|
6
|
-
|
|
7
|
-
|
|
6
|
+
# Source: retrace.pth bundled with this package
|
|
7
|
+
source = pathlib.Path(__file__).parent / 'retrace.pth'
|
|
8
|
+
|
|
9
|
+
# Target: site-packages root
|
|
10
|
+
target = pathlib.Path(sysconfig.get_paths()["purelib"]) / 'retrace.pth'
|
|
11
|
+
|
|
12
|
+
shutil.copy(source, target)
|
|
8
13
|
|
|
9
|
-
print(f'Retrace autoinstall enabled
|
|
14
|
+
print(f'Retrace autoinstall enabled: {source} -> {target}')
|
|
10
15
|
else:
|
|
11
16
|
import os
|
|
12
17
|
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Retrace exception hierarchy.
|
|
3
|
+
|
|
4
|
+
All retrace-specific exceptions inherit from RetraceError.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
class RetraceError(Exception):
|
|
8
|
+
"""Base exception for all retrace errors."""
|
|
9
|
+
pass
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class RecordError(RetraceError):
|
|
13
|
+
"""Error during recording phase."""
|
|
14
|
+
pass
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class ReplayError(RetraceError):
|
|
18
|
+
"""Error during replay phase."""
|
|
19
|
+
pass
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class ConfigurationError(RetraceError):
|
|
23
|
+
"""Error in retrace configuration."""
|
|
24
|
+
pass
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
class VersionMismatchError(RetraceError):
|
|
28
|
+
"""Recording and replay versions don't match."""
|
|
29
|
+
pass
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
class RecordingNotFoundError(RetraceError):
|
|
33
|
+
"""Recording path does not exist."""
|
|
34
|
+
pass
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
3
|
import retracesoftware.functional as functional
|
|
4
|
-
import
|
|
4
|
+
import retracesoftware.utils as utils
|
|
5
5
|
|
|
6
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
7
|
# from retracesoftware.proxy.proxy import ProxyFactory, InternalProxy, ProxySpec, WrappingProxySpec, ExtendingProxySpec, ExtendingProxy
|
|
@@ -0,0 +1,506 @@
|
|
|
1
|
+
import retracesoftware.utils as utils
|
|
2
|
+
import retracesoftware.functional as functional
|
|
3
|
+
from functools import wraps
|
|
4
|
+
import inspect
|
|
5
|
+
import gc
|
|
6
|
+
import importlib
|
|
7
|
+
import types
|
|
8
|
+
import sys
|
|
9
|
+
|
|
10
|
+
from retracesoftware.proxy.thread import start_new_thread_wrapper, counters
|
|
11
|
+
from retracesoftware.install.typeutils import modify
|
|
12
|
+
|
|
13
|
+
def find_attr(mro, name):
|
|
14
|
+
for cls in mro:
|
|
15
|
+
if name in cls.__dict__:
|
|
16
|
+
return cls.__dict__[name]
|
|
17
|
+
|
|
18
|
+
def is_descriptor(obj):
|
|
19
|
+
return hasattr(obj, '__get__') or hasattr(obj, '__set__') or hasattr(obj, '__delete__')
|
|
20
|
+
|
|
21
|
+
def resolve(path):
|
|
22
|
+
module, sep, name = path.rpartition('.')
|
|
23
|
+
if module == None: module = 'builtins'
|
|
24
|
+
|
|
25
|
+
return getattr(importlib.import_module(module), name)
|
|
26
|
+
|
|
27
|
+
def phase(func):
|
|
28
|
+
func.is_phase = True # add marker attribute
|
|
29
|
+
return func
|
|
30
|
+
|
|
31
|
+
def replace(replacements, coll):
|
|
32
|
+
return map(lambda x: replacements.get(x, x), coll)
|
|
33
|
+
|
|
34
|
+
def container_replace(container, old, new):
|
|
35
|
+
if isinstance(container, dict):
|
|
36
|
+
if old in container:
|
|
37
|
+
elem = container.pop(old)
|
|
38
|
+
container[new] = elem
|
|
39
|
+
container_replace(container, old, new)
|
|
40
|
+
else:
|
|
41
|
+
for key,value in container.items():
|
|
42
|
+
if key != '__retrace_unproxied__' and value is old:
|
|
43
|
+
container[key] = new
|
|
44
|
+
return True
|
|
45
|
+
elif isinstance(container, list):
|
|
46
|
+
for i,value in enumerate(container):
|
|
47
|
+
if value is old:
|
|
48
|
+
container[i] = new
|
|
49
|
+
return True
|
|
50
|
+
elif isinstance(container, set):
|
|
51
|
+
container.remove(old)
|
|
52
|
+
container.add(new)
|
|
53
|
+
return True
|
|
54
|
+
else:
|
|
55
|
+
return False
|
|
56
|
+
|
|
57
|
+
def select_keys(keys, dict):
|
|
58
|
+
return {key: dict[key] for key in keys if key in dict}
|
|
59
|
+
|
|
60
|
+
def map_values(f, dict):
|
|
61
|
+
return {key: f(value) for key,value in dict.items()}
|
|
62
|
+
|
|
63
|
+
def common_keys(dict, *dicts):
|
|
64
|
+
common_keys = utils.set(dict)
|
|
65
|
+
for d in dicts:
|
|
66
|
+
common_keys &= d.keys()
|
|
67
|
+
|
|
68
|
+
assert isinstance(common_keys, utils.set)
|
|
69
|
+
|
|
70
|
+
return common_keys
|
|
71
|
+
|
|
72
|
+
def intersection(*dicts):
|
|
73
|
+
return { key: tuple(d[key] for d in dicts) for key in common_keys(*dicts) }
|
|
74
|
+
|
|
75
|
+
def intersection_apply(f, *dicts):
|
|
76
|
+
return map_values(lambda vals: f(*vals), intersection(*dicts))
|
|
77
|
+
|
|
78
|
+
def patch(func):
|
|
79
|
+
@wraps(func)
|
|
80
|
+
def wrapper(self, spec, mod_dict):
|
|
81
|
+
if isinstance(spec, str):
|
|
82
|
+
return wrapper(self, [spec], mod_dict)
|
|
83
|
+
elif isinstance(spec, list):
|
|
84
|
+
res = {}
|
|
85
|
+
for name in spec:
|
|
86
|
+
if name in mod_dict:
|
|
87
|
+
value = func(self, mod_dict[name])
|
|
88
|
+
if value is not None:
|
|
89
|
+
res[name] = value
|
|
90
|
+
return res
|
|
91
|
+
elif isinstance(spec, dict):
|
|
92
|
+
# return {name: func(self, mod_dict[name], value) for name, value in spec.items() if name in mod_dict}
|
|
93
|
+
res = {}
|
|
94
|
+
for name,value in spec.items():
|
|
95
|
+
if name in mod_dict:
|
|
96
|
+
value = func(self, mod_dict[name], value)
|
|
97
|
+
if value is not None:
|
|
98
|
+
res[name] = value
|
|
99
|
+
return res
|
|
100
|
+
else:
|
|
101
|
+
raise Exception('TODO')
|
|
102
|
+
|
|
103
|
+
wrapper.is_phase = True
|
|
104
|
+
return wrapper
|
|
105
|
+
|
|
106
|
+
def is_special(name):
|
|
107
|
+
return len(name) > 4 and name.startswith('__') and name.endswith('__')
|
|
108
|
+
|
|
109
|
+
def superdict(cls):
|
|
110
|
+
result = {}
|
|
111
|
+
for cls in list(reversed(cls.__mro__))[1:]:
|
|
112
|
+
result.update(cls.__dict__)
|
|
113
|
+
|
|
114
|
+
return result
|
|
115
|
+
|
|
116
|
+
def wrap_method_descriptors(wrapper, prefix, base):
|
|
117
|
+
slots = {"__slots__": () }
|
|
118
|
+
|
|
119
|
+
extended = type(f'{prefix}.{base.__module__}.{base.__name__}', (base,), {"__slots__": () })
|
|
120
|
+
|
|
121
|
+
blacklist = ['__getattribute__', '__hash__', '__del__']
|
|
122
|
+
|
|
123
|
+
for name,value in superdict(base).items():
|
|
124
|
+
if name not in blacklist:
|
|
125
|
+
if utils.is_method_descriptor(value):
|
|
126
|
+
setattr(extended, name, wrapper(value))
|
|
127
|
+
|
|
128
|
+
return extended
|
|
129
|
+
|
|
130
|
+
class ElementPatcher:
|
|
131
|
+
|
|
132
|
+
def __init__(self, config, phases):
|
|
133
|
+
funcs = {}
|
|
134
|
+
|
|
135
|
+
for phase in phases:
|
|
136
|
+
if phase.name in config:
|
|
137
|
+
for key,func in phase(config[phase.name]).items():
|
|
138
|
+
val = funcs.get(key, [])
|
|
139
|
+
val.append(func)
|
|
140
|
+
funcs[key] = val
|
|
141
|
+
|
|
142
|
+
self.funcs = utils.map_values(lambda funcs: functional.sequence(*funcs), funcs)
|
|
143
|
+
self.fallback = None
|
|
144
|
+
|
|
145
|
+
if 'default' in config:
|
|
146
|
+
default_name = config['default']
|
|
147
|
+
for phase in phases:
|
|
148
|
+
if phase.name == default_name:
|
|
149
|
+
self.fallback = phase.patch
|
|
150
|
+
|
|
151
|
+
def __call__(self, name, value):
|
|
152
|
+
if name in self.funcs:
|
|
153
|
+
return self.funcs[name](value)
|
|
154
|
+
elif not is_special(name) and self.fallback:
|
|
155
|
+
return self.fallback(value)
|
|
156
|
+
else:
|
|
157
|
+
return value
|
|
158
|
+
|
|
159
|
+
# class Patcher:
|
|
160
|
+
|
|
161
|
+
# def __init__(self, config):
|
|
162
|
+
|
|
163
|
+
# self.config = config
|
|
164
|
+
# self.phases = []
|
|
165
|
+
# self.patched = {}
|
|
166
|
+
|
|
167
|
+
# # system.set_thread_id(0)
|
|
168
|
+
# # self.thread_counter = system.sync(utils.counter(1))
|
|
169
|
+
# # self.module_config = module_config
|
|
170
|
+
|
|
171
|
+
# # self.thread_state = thread_state
|
|
172
|
+
# # self.debug_level = debug_level
|
|
173
|
+
# # self.on_function_proxy = on_function_proxy
|
|
174
|
+
# # self.modules = config['modules']
|
|
175
|
+
# # self.immutable_types_set = immutable_types
|
|
176
|
+
# # self.predicate = PredicateBuilder()
|
|
177
|
+
# # self.system = system
|
|
178
|
+
# # self.type_attribute_filter = self.predicate(config['type_attribute_filter'])
|
|
179
|
+
# # self.post_commit = post_commit
|
|
180
|
+
# # self.exclude_paths = [re.compile(s) for s in config.get('exclude_paths', [])]
|
|
181
|
+
# # self.typepatcher = {}
|
|
182
|
+
# # self.originals = {}
|
|
183
|
+
|
|
184
|
+
# # def is_phase(name): return getattr(getattr(self, name, None), "is_phase", False)
|
|
185
|
+
|
|
186
|
+
# # self.phases = [(name, getattr(self, name)) for name in Patcher.__dict__.keys() if is_phase(name)]
|
|
187
|
+
|
|
188
|
+
# def add_phase(self, phase):
|
|
189
|
+
# self.phases.append(phase)
|
|
190
|
+
|
|
191
|
+
# # def log(self, *args):
|
|
192
|
+
# # self.system.tracer.log(*args)
|
|
193
|
+
|
|
194
|
+
# def path_predicate(self, path):
|
|
195
|
+
# for exclude in self.exclude_paths:
|
|
196
|
+
# if exclude.match(str(path)) is not None:
|
|
197
|
+
# # print(f'in path_predicate, excluding {path}')
|
|
198
|
+
# return False
|
|
199
|
+
# return True
|
|
200
|
+
|
|
201
|
+
# # def on_proxytype(self, cls):
|
|
202
|
+
|
|
203
|
+
# # def patch(spec):
|
|
204
|
+
# # for method, transform in spec.items():
|
|
205
|
+
# # setattr(cls, method, resolve(transform)(getattr(cls, method)))
|
|
206
|
+
|
|
207
|
+
# # if cls.__module__ in self.modules:
|
|
208
|
+
# # spec = self.modules[cls.__module__]
|
|
209
|
+
|
|
210
|
+
# # if 'patchtype' in spec:
|
|
211
|
+
# # patchtype = spec['patchtype']
|
|
212
|
+
# # if cls.__name__ in patchtype:
|
|
213
|
+
# # patch(patchtype[cls.__name__])
|
|
214
|
+
|
|
215
|
+
# @property
|
|
216
|
+
# def disable(self):
|
|
217
|
+
# return self.thread_state.select('disabled')
|
|
218
|
+
|
|
219
|
+
# def proxyable(self, name, obj):
|
|
220
|
+
# if name.startswith('__') and name.endswith('__'):
|
|
221
|
+
# return False
|
|
222
|
+
|
|
223
|
+
# if isinstance(obj, (str, int, dict, list, tuple)):
|
|
224
|
+
# return False
|
|
225
|
+
|
|
226
|
+
# if isinstance(obj, type):
|
|
227
|
+
# return not issubclass(obj, BaseException) and obj not in self.immutable_types_set
|
|
228
|
+
|
|
229
|
+
class Patcher:
|
|
230
|
+
|
|
231
|
+
def __init__(self, config):
|
|
232
|
+
|
|
233
|
+
self.config = config
|
|
234
|
+
self.phases = []
|
|
235
|
+
self.patched = {}
|
|
236
|
+
|
|
237
|
+
def add_phase(self, phase):
|
|
238
|
+
self.phases.append(phase)
|
|
239
|
+
|
|
240
|
+
def path_predicate(self, path):
|
|
241
|
+
for exclude in self.exclude_paths:
|
|
242
|
+
if exclude.match(str(path)) is not None:
|
|
243
|
+
# print(f'in path_predicate, excluding {path}')
|
|
244
|
+
return False
|
|
245
|
+
return True
|
|
246
|
+
|
|
247
|
+
@property
|
|
248
|
+
def disable(self):
|
|
249
|
+
return self.thread_state.select('disabled')
|
|
250
|
+
|
|
251
|
+
def proxyable(self, name, obj):
|
|
252
|
+
if name.startswith('__') and name.endswith('__'):
|
|
253
|
+
return False
|
|
254
|
+
|
|
255
|
+
if isinstance(obj, (str, int, dict, list, tuple)):
|
|
256
|
+
return False
|
|
257
|
+
|
|
258
|
+
if isinstance(obj, type):
|
|
259
|
+
return not issubclass(obj, BaseException) and obj not in self.immutable_types_set
|
|
260
|
+
else:
|
|
261
|
+
return type(obj) not in self.immutable_types_set
|
|
262
|
+
|
|
263
|
+
@phase
|
|
264
|
+
def proxy_type_attributes(self, spec, mod_dict):
|
|
265
|
+
for classname, attributes in spec.items():
|
|
266
|
+
if classname in mod_dict:
|
|
267
|
+
cls = mod_dict[classname]
|
|
268
|
+
if isinstance(cls, type):
|
|
269
|
+
for name in attributes:
|
|
270
|
+
attr = find_attr(cls.__mro__, name)
|
|
271
|
+
if attr is not None and (callable(attr) or is_descriptor(attr)):
|
|
272
|
+
proxied = self.system(attr)
|
|
273
|
+
# proxied = self.proxy(attr)
|
|
274
|
+
|
|
275
|
+
with modify(cls):
|
|
276
|
+
setattr(cls, name, proxied)
|
|
277
|
+
else:
|
|
278
|
+
raise Exception(f"Cannot patch attributes for {cls.__module__}.{cls.__name__} as object is: {cls} and not a type")
|
|
279
|
+
|
|
280
|
+
@phase
|
|
281
|
+
def replace(self, spec, mod_dict):
|
|
282
|
+
return {key: resolve(value) for key,value in spec.items()}
|
|
283
|
+
|
|
284
|
+
@patch
|
|
285
|
+
def patch_start_new_thread(self, value):
|
|
286
|
+
return start_new_thread_wrapper(thread_state = self.thread_state,
|
|
287
|
+
on_exit = self.system.on_thread_exit,
|
|
288
|
+
start_new_thread = value)
|
|
289
|
+
|
|
290
|
+
# def start_new_thread(function, *args):
|
|
291
|
+
# # synchronized, replay shoudl yield correct number
|
|
292
|
+
# thread_id = self.thread_counter()
|
|
293
|
+
|
|
294
|
+
# def threadrunner(*args, **kwargs):
|
|
295
|
+
# nonlocal thread_id
|
|
296
|
+
# self.system.set_thread_id(thread_id)
|
|
297
|
+
|
|
298
|
+
# with self.thread_state.select('internal'):
|
|
299
|
+
# try:
|
|
300
|
+
# # if self.tracing:
|
|
301
|
+
# # FrameTracer.install(self.thread_state.dispatch(noop, internal = self.checkpoint))
|
|
302
|
+
# return function(*args, **kwargs)
|
|
303
|
+
# finally:
|
|
304
|
+
# print(f'exiting: {thread_id}')
|
|
305
|
+
|
|
306
|
+
# return value(threadrunner, *args)
|
|
307
|
+
|
|
308
|
+
# return self.thread_state.dispatch(value, internal = start_new_thread)
|
|
309
|
+
|
|
310
|
+
@phase
|
|
311
|
+
def wrappers(self, spec, mod_dict):
|
|
312
|
+
return intersection_apply(lambda path, value: resolve(path)(value), spec, mod_dict)
|
|
313
|
+
|
|
314
|
+
@patch
|
|
315
|
+
def patch_exec(self, exec):
|
|
316
|
+
|
|
317
|
+
def is_module(source, *args):
|
|
318
|
+
return isinstance(source, types.CodeType) and source.co_name == '<module>'
|
|
319
|
+
|
|
320
|
+
def after_exec(source, globals = None, locals = None):
|
|
321
|
+
if isinstance(source, types.CodeType) and source.co_name == '<module>' and '__name__' in globals:
|
|
322
|
+
self(sys.modules[globals['__name__']])
|
|
323
|
+
|
|
324
|
+
def first(x): return x[0]
|
|
325
|
+
|
|
326
|
+
def disable(func): return self.thread_state.wrap('disabled', func)
|
|
327
|
+
|
|
328
|
+
return self.thread_state.dispatch(
|
|
329
|
+
exec,
|
|
330
|
+
internal = functional.sequence(
|
|
331
|
+
functional.juxt(exec, functional.when(is_module, disable(after_exec))), first))
|
|
332
|
+
|
|
333
|
+
# self.thread_state.wrap(desired_state = 'disabled', function = exec_wrapper)
|
|
334
|
+
|
|
335
|
+
@patch
|
|
336
|
+
def sync_types(self, value):
|
|
337
|
+
return wrap_method_descriptors(self.system.sync, "retrace", value)
|
|
338
|
+
|
|
339
|
+
@phase
|
|
340
|
+
def with_state_recursive(self, spec, mod_dict):
|
|
341
|
+
|
|
342
|
+
updates = {}
|
|
343
|
+
|
|
344
|
+
for state,elems in spec.items():
|
|
345
|
+
|
|
346
|
+
def wrap(obj):
|
|
347
|
+
return functional.recurive_wrap_function(
|
|
348
|
+
functional.partial(self.thread_state.wrap, state),
|
|
349
|
+
obj)
|
|
350
|
+
|
|
351
|
+
updates.update(map_values(wrap, select_keys(elems, mod_dict)))
|
|
352
|
+
|
|
353
|
+
return updates
|
|
354
|
+
|
|
355
|
+
@phase
|
|
356
|
+
def methods_with_state(self, spec, mod_dict):
|
|
357
|
+
|
|
358
|
+
# updates = {}
|
|
359
|
+
|
|
360
|
+
def update(cls, name, f):
|
|
361
|
+
setattr(cls, name, f(getattr(cls, name)))
|
|
362
|
+
|
|
363
|
+
for state,cls_methods in spec.items():
|
|
364
|
+
def wrap(obj):
|
|
365
|
+
assert callable(obj)
|
|
366
|
+
return self.thread_state.wrap(desired_state = state, function = obj)
|
|
367
|
+
|
|
368
|
+
for typename,methodnames in cls_methods.items():
|
|
369
|
+
cls = mod_dict[typename]
|
|
370
|
+
|
|
371
|
+
for methodname in methodnames:
|
|
372
|
+
update(cls, methodname, wrap)
|
|
373
|
+
|
|
374
|
+
return {}
|
|
375
|
+
|
|
376
|
+
@phase
|
|
377
|
+
def with_state(self, spec, mod_dict):
|
|
378
|
+
|
|
379
|
+
updates = {}
|
|
380
|
+
|
|
381
|
+
for state,elems in spec.items():
|
|
382
|
+
|
|
383
|
+
def wrap(obj):
|
|
384
|
+
return self.thread_state.wrap(desired_state = state, function = obj)
|
|
385
|
+
|
|
386
|
+
updates.update(map_values(wrap, select_keys(elems, mod_dict)))
|
|
387
|
+
|
|
388
|
+
return updates
|
|
389
|
+
|
|
390
|
+
@patch
|
|
391
|
+
def patch_extension_exec(self, exec):
|
|
392
|
+
|
|
393
|
+
def first(x): return x[0]
|
|
394
|
+
|
|
395
|
+
def disable(func): return self.thread_state.wrap('disabled', func)
|
|
396
|
+
|
|
397
|
+
return self.thread_state.dispatch(exec,
|
|
398
|
+
internal = functional.sequence(functional.juxt(exec, disable(self)), first))
|
|
399
|
+
|
|
400
|
+
# def wrapper(module):
|
|
401
|
+
# with self.thread_state.select('internal'):
|
|
402
|
+
# res = exec(module)
|
|
403
|
+
|
|
404
|
+
# self(module)
|
|
405
|
+
# return res
|
|
406
|
+
|
|
407
|
+
# return wrapper
|
|
408
|
+
|
|
409
|
+
@patch
|
|
410
|
+
def path_predicates(self, func, param):
|
|
411
|
+
signature = inspect.signature(func).parameters
|
|
412
|
+
|
|
413
|
+
try:
|
|
414
|
+
index = list(signature.keys()).index(param)
|
|
415
|
+
except ValueError:
|
|
416
|
+
print(f'parameter {param} not in: {signature.keys()} {type(func)} {func}')
|
|
417
|
+
raise
|
|
418
|
+
|
|
419
|
+
param = functional.param(name = param, index = index)
|
|
420
|
+
|
|
421
|
+
assert callable(param)
|
|
422
|
+
|
|
423
|
+
return functional.if_then_else(
|
|
424
|
+
test = functional.sequence(param, self.path_predicate),
|
|
425
|
+
then = func,
|
|
426
|
+
otherwise = self.thread_state.wrap('disabled', func))
|
|
427
|
+
|
|
428
|
+
@phase
|
|
429
|
+
def wrap(self, spec, mod_dict):
|
|
430
|
+
updates = {}
|
|
431
|
+
|
|
432
|
+
for path, wrapper_name in spec.items():
|
|
433
|
+
|
|
434
|
+
parts = path.split('.')
|
|
435
|
+
name = parts[0]
|
|
436
|
+
if name in mod_dict:
|
|
437
|
+
value = mod_dict[name]
|
|
438
|
+
assert not isinstance(value, utils.wrapped_function), \
|
|
439
|
+
f"value for key: {name} is already wrapped"
|
|
440
|
+
|
|
441
|
+
if len(parts) == 1:
|
|
442
|
+
updates[name] = resolve(wrapper_name)(value)
|
|
443
|
+
elif len(parts) == 2:
|
|
444
|
+
member = getattr(value, parts[1], None)
|
|
445
|
+
if member:
|
|
446
|
+
new_value = resolve(wrapper_name)(member)
|
|
447
|
+
setattr(value, parts[1], new_value)
|
|
448
|
+
else:
|
|
449
|
+
raise Exception('TODO')
|
|
450
|
+
|
|
451
|
+
return updates
|
|
452
|
+
|
|
453
|
+
def find_phase(self, name):
|
|
454
|
+
for phase in self.phases:
|
|
455
|
+
if phase.name == name:
|
|
456
|
+
return phase
|
|
457
|
+
|
|
458
|
+
raise Exception(f'Phase: {name} not found')
|
|
459
|
+
|
|
460
|
+
# def run_transforms(self, config, obj):
|
|
461
|
+
# if isinstance(config, str):
|
|
462
|
+
# return find_phase(config).patch(obj)
|
|
463
|
+
# else:
|
|
464
|
+
# for name in config
|
|
465
|
+
|
|
466
|
+
def element_patcher(self, name):
|
|
467
|
+
if name in self.config:
|
|
468
|
+
return ElementPatcher(config = self.config[name],
|
|
469
|
+
phases = self.phases)
|
|
470
|
+
|
|
471
|
+
def wrap_namespace(self, ns):
|
|
472
|
+
name = ns.get('__name__', None)
|
|
473
|
+
patcher = self.element_patcher(name)
|
|
474
|
+
print(f'wrap_namespace {name} {patcher}')
|
|
475
|
+
return utils.InterceptDict(ns, patcher) if patcher else ns
|
|
476
|
+
|
|
477
|
+
def update(self, old, new):
|
|
478
|
+
if isinstance(new, type) and isinstance(old, type) and issubclass(new, old):
|
|
479
|
+
for subclass in old.__subclasses__():
|
|
480
|
+
if subclass is not new:
|
|
481
|
+
subclass.__bases__ = tuple(replace({old: new}, subclass.__bases__))
|
|
482
|
+
|
|
483
|
+
for ref in gc.get_referrers(old):
|
|
484
|
+
container_replace(container = ref, old = old, new = new)
|
|
485
|
+
|
|
486
|
+
def patch_loaded_module(self, mod_dict):
|
|
487
|
+
modname = mod_dict.get('__name__', None)
|
|
488
|
+
|
|
489
|
+
print(f'Patching loaded module: {modname}')
|
|
490
|
+
|
|
491
|
+
if modname in self.config:
|
|
492
|
+
|
|
493
|
+
|
|
494
|
+
originals = {}
|
|
495
|
+
self.patched[modname] = originals
|
|
496
|
+
|
|
497
|
+
patcher = self.element_patcher(modname)
|
|
498
|
+
|
|
499
|
+
for key in list(mod_dict.keys()):
|
|
500
|
+
original = mod_dict[key]
|
|
501
|
+
patched = patcher(key, original)
|
|
502
|
+
|
|
503
|
+
if patched is not original:
|
|
504
|
+
self.update(old = original, new = patched)
|
|
505
|
+
originals[key] = original
|
|
506
|
+
assert mod_dict[key] is patched
|
|
@@ -94,8 +94,13 @@ def patch_namespace(patcher, config, namespace, update_refs):
|
|
|
94
94
|
# print(f"patching: {name}")
|
|
95
95
|
|
|
96
96
|
value = namespace[name]
|
|
97
|
-
new_value = func(value)
|
|
98
97
|
|
|
98
|
+
try:
|
|
99
|
+
new_value = func(value)
|
|
100
|
+
except Exception as e:
|
|
101
|
+
print(f"Error patching {name}, phase: {phase_name}: {e}")
|
|
102
|
+
raise e
|
|
103
|
+
|
|
99
104
|
if value is not new_value:
|
|
100
105
|
namespace[name] = new_value
|
|
101
106
|
|
retracesoftware/modules.toml
CHANGED
|
@@ -366,9 +366,8 @@ proxy = ["SemLock", "sem_unlink"]
|
|
|
366
366
|
# "map_buffer": "ignore",
|
|
367
367
|
# "*": "proxy_if_function"
|
|
368
368
|
# },
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
# },
|
|
369
|
+
["psycopg2._psycopg"]
|
|
370
|
+
proxy = ["connect", "cursor", "connection"]
|
|
372
371
|
|
|
373
372
|
# "_weakrefset": {
|
|
374
373
|
# "WeakSet": "sync_type",
|
retracesoftware/proxy/gateway.py
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import retracesoftware.functional as functional
|
|
2
|
-
import
|
|
2
|
+
import retracesoftware.utils as utils
|
|
3
3
|
import types
|
|
4
4
|
from retracesoftware.proxy.gateway import adapter_pair
|
|
5
5
|
from types import SimpleNamespace
|
|
@@ -16,8 +16,7 @@ import enum
|
|
|
16
16
|
import functools
|
|
17
17
|
import re
|
|
18
18
|
|
|
19
|
-
|
|
20
|
-
pass
|
|
19
|
+
from retracesoftware.exceptions import RetraceError
|
|
21
20
|
|
|
22
21
|
def proxy(proxytype):
|
|
23
22
|
return functional.spread(
|
|
@@ -403,8 +402,9 @@ class ProxySystem:
|
|
|
403
402
|
cls.__init_subclass__ = classmethod(init_subclass)
|
|
404
403
|
|
|
405
404
|
for subtype in get_all_subtypes(cls):
|
|
406
|
-
|
|
407
|
-
|
|
405
|
+
with WithoutFlags(subtype, "Py_TPFLAGS_IMMUTABLETYPE"):
|
|
406
|
+
init_subclass(subtype)
|
|
407
|
+
utils.set_on_alloc(subtype, on_alloc)
|
|
408
408
|
|
|
409
409
|
cls.__retrace__ = self
|
|
410
410
|
|
retracesoftware/proxy/record.py
CHANGED
|
@@ -162,6 +162,21 @@ class RecordProxySystem(ProxySystem):
|
|
|
162
162
|
|
|
163
163
|
def write_error(cls, val, traceback):
|
|
164
164
|
assert isinstance(val, BaseException)
|
|
165
|
+
# if not isinstance(val, BaseException):
|
|
166
|
+
# # Debug: something is passing wrong value type
|
|
167
|
+
# import sys
|
|
168
|
+
# print(f"DEBUG write_error called with:")
|
|
169
|
+
# print(f" cls={cls}, type={type(cls)}")
|
|
170
|
+
# print(f" val={val!r}, type={type(val)}")
|
|
171
|
+
# print(f" traceback={traceback}")
|
|
172
|
+
# print(f" sys.exc_info()={sys.exc_info()}")
|
|
173
|
+
|
|
174
|
+
# # Some C extensions (e.g. psycopg2) return plain strings as errors
|
|
175
|
+
# # Wrap them in the exception class if possible
|
|
176
|
+
# if isinstance(val, str) and isinstance(cls, type) and issubclass(cls, BaseException):
|
|
177
|
+
# val = cls(val)
|
|
178
|
+
# else:
|
|
179
|
+
# val = RuntimeError(f"Non-exception error: {val}")
|
|
165
180
|
error(cls, val)
|
|
166
181
|
|
|
167
182
|
tracer = Tracer(tracing_config, writer = self.writer.handle('TRACE'))
|
retracesoftware/proxy/thread.py
CHANGED
retracesoftware/replay.py
CHANGED
|
@@ -8,6 +8,7 @@ from retracesoftware.proxy.replay import ReplayProxySystem
|
|
|
8
8
|
import retracesoftware.utils as utils
|
|
9
9
|
import json
|
|
10
10
|
from retracesoftware.stackdifference import on_stack_difference
|
|
11
|
+
from retracesoftware.exceptions import RecordingNotFoundError
|
|
11
12
|
|
|
12
13
|
def parse_args():
|
|
13
14
|
parser = argparse.ArgumentParser(
|
|
@@ -68,7 +69,7 @@ def main():
|
|
|
68
69
|
path = Path(args.recording)
|
|
69
70
|
|
|
70
71
|
if not path.exists():
|
|
71
|
-
raise
|
|
72
|
+
raise RecordingNotFoundError(f"Recording path: {path} does not exist")
|
|
72
73
|
|
|
73
74
|
settings = load_json(path / "settings.json")
|
|
74
75
|
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import retracesoftware.autoenable;
|
retracesoftware/run.py
CHANGED
|
@@ -145,9 +145,9 @@ def run_python_command(argv):
|
|
|
145
145
|
except ModuleNotFoundError:
|
|
146
146
|
print(f"Error: No module named '{argv[1]}'", file=sys.stderr)
|
|
147
147
|
return 1
|
|
148
|
-
except Exception as e:
|
|
149
|
-
|
|
150
|
-
|
|
148
|
+
# except Exception as e:
|
|
149
|
+
# print(f"Error1234: {e}", file=sys.stderr)
|
|
150
|
+
# raise e
|
|
151
151
|
finally:
|
|
152
152
|
sys.argv = original_argv
|
|
153
153
|
os.chdir(original_cwd)
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
retracesoftware/__init__.py,sha256=jQw15XBREFZX93BHpvREfc-hQmMUHYtO6gO-qZmdW1o,482
|
|
2
|
+
retracesoftware/__main__.py,sha256=DGCvchaF_wW1j_cirC1tu1QM1gBHE3i-69okn3LKEXM,9582
|
|
3
|
+
retracesoftware/autoenable.py,sha256=6KxpaPpeleSN7-qmekA4w2HJQaet_9Flt9WikQvyN04,1808
|
|
4
|
+
retracesoftware/config.json,sha256=Cw5YZCfTo8GLmtbTH2LYwST7wNe7925DAwsTtRIGhBE,3817
|
|
5
|
+
retracesoftware/config.yaml,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
6
|
+
retracesoftware/exceptions.py,sha256=8jT4tJrRRRh3nRERrsqaKgIJ3itb-VYumpa9UcoxF9o,647
|
|
7
|
+
retracesoftware/modules.toml,sha256=Xu8B6H9HMM4ZaxiEPy9FGcMHBHsyBD1qR0weUXgau4Y,14613
|
|
8
|
+
retracesoftware/preload.txt,sha256=8Wm8OWnDfEp9X4FN-yNQYLfZk_h36kMgHA_QUw_7Hbs,2370
|
|
9
|
+
retracesoftware/replay.py,sha256=FEEenvRUeULmv-Zzb6g7Ub85oGonmvMT4ATE4q0yWqI,3676
|
|
10
|
+
retracesoftware/retrace.pth,sha256=umCRQITpnqAp1N-svTCi4lJxOo7GihDrLtO3pA2wwgM,35
|
|
11
|
+
retracesoftware/run.py,sha256=Gi1TpMF30QLmiziCU7hu3zR_8Hr1R7Qx0TsiQOjLmzA,12216
|
|
12
|
+
retracesoftware/stackdifference.py,sha256=nM9r5YHMCNQcrWozMjUX4ZTrZL5U0rQChSGzpiXy_DU,4466
|
|
13
|
+
retracesoftware/install/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
14
|
+
retracesoftware/install/config.py,sha256=AnSlHI3lWQrPUg0fOS26PqXiq1hJSy56SwrNL8mbAG8,1546
|
|
15
|
+
retracesoftware/install/edgecases.py,sha256=Ry3BETJ4Hmqpw9xXTlNeUgBL9q-sumaazqjmzZBRtD4,7265
|
|
16
|
+
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
|
|
25
|
+
retracesoftware/install/replace.py,sha256=FYiSJtNrXEhl-H6M5tJm0kbliBA0sZdxE0306pr-YQg,872
|
|
26
|
+
retracesoftware/install/replay.py,sha256=bB2eMpIP2vXZzeJ98jtlkGSE1DEYWk3olUbiBQcdVj0,3539
|
|
27
|
+
retracesoftware/install/tracer.py,sha256=VfjghYepk2M2DLy1nEYgHcaCXo0616OVMxxx3A3dNhM,9363
|
|
28
|
+
retracesoftware/install/typeutils.py,sha256=GiYDPsKGBtp0_1E71AJm16u03L8eSoR1piz3yvOgAhk,2253
|
|
29
|
+
retracesoftware/proxy/__init__.py,sha256=ntIyqKhBRkKEkcW_oOPodikh-mxYl8OXRnSaj-9-Xwc,178
|
|
30
|
+
retracesoftware/proxy/gateway.py,sha256=-U0WobF8oFku5ncgO9avXfqTV4t8urMtIQ1FUaHD2Wg,1590
|
|
31
|
+
retracesoftware/proxy/globalref.py,sha256=yXtJsOeBHN9xoEgJWA3MJco-jD2SQUef_fDatA4A6rg,803
|
|
32
|
+
retracesoftware/proxy/messagestream.py,sha256=lp0_Stqn1Ju3tgHrt1E5pcH02RXjyd6rvkuHQnhGRlk,5890
|
|
33
|
+
retracesoftware/proxy/proxyfactory.py,sha256=xssL3rPtou9QurBrQkQNXfJszXkF90V8w6OuKdd02o8,12272
|
|
34
|
+
retracesoftware/proxy/proxysystem.py,sha256=F-NWRMIww1KNw_Bk0GTr2DBVxl0D5sb3EKSf3Zap3LU,15172
|
|
35
|
+
retracesoftware/proxy/proxytype.py,sha256=LZ1rNMsxXRUMdI4uTzDvGkUyPOYxCFroT_ZAtOGTbBk,14012
|
|
36
|
+
retracesoftware/proxy/record.py,sha256=BfZap06nz2cr6eGgf3baMyyilqWipTXCjMB5_1ZIlSA,7522
|
|
37
|
+
retracesoftware/proxy/replay.py,sha256=Kx4XrsP0T35IvTzf3pwhR24Lotoz43p7E8LBevEQHpE,4948
|
|
38
|
+
retracesoftware/proxy/serializer.py,sha256=S5yhHoP2iYaXBY7Jr8Ei630Z31521x0IO6DeuClOD9s,958
|
|
39
|
+
retracesoftware/proxy/startthread.py,sha256=YKUcOSjA-9_5ZsDHhyP0fXJSP3RWDKV-ajX-I0SI-bU,1270
|
|
40
|
+
retracesoftware/proxy/stubfactory.py,sha256=fyQtCZnkXvLaTlGcxE0Lx8od09ZOgnPWPn0YvNFfVeQ,6219
|
|
41
|
+
retracesoftware/proxy/thread.py,sha256=AAj6BL4kw-KMHX4fV66LuePQbTYvPmI3IY8ZwV069nI,3073
|
|
42
|
+
retracesoftware_proxy-0.2.15.dist-info/METADATA,sha256=KjgPEEtawqKuayhjhSq_s0lFFWw4qZrb4zHZurIB4X0,227
|
|
43
|
+
retracesoftware_proxy-0.2.15.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
|
|
44
|
+
retracesoftware_proxy-0.2.15.dist-info/top_level.txt,sha256=hYHsR6txLidmqvjBMITpIHvmJJbmoCAgr76-IpZPRz8,16
|
|
45
|
+
retracesoftware_proxy-0.2.15.dist-info/RECORD,,
|
|
@@ -1,42 +0,0 @@
|
|
|
1
|
-
retracesoftware/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
2
|
-
retracesoftware/__main__.py,sha256=ckCG50cNKujXnQSNezgzSXnY0Ke-xi9v1-6NM3SSIWE,9433
|
|
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=rQkTbb8eU88JxHdht1sDV3vZvXQAUYBOuyG5xrIaFJw,14623
|
|
7
|
-
retracesoftware/preload.txt,sha256=8Wm8OWnDfEp9X4FN-yNQYLfZk_h36kMgHA_QUw_7Hbs,2370
|
|
8
|
-
retracesoftware/replay.py,sha256=rKuhen_MDDi_5WVDmQgchxJ0ZrCbYL1yZZboZsu_q54,3601
|
|
9
|
-
retracesoftware/run.py,sha256=dQ_yM5f7Mlb0NyZf0CysBX6d_j_IHtIB_NG1W6FGIuY,12207
|
|
10
|
-
retracesoftware/stackdifference.py,sha256=nM9r5YHMCNQcrWozMjUX4ZTrZL5U0rQChSGzpiXy_DU,4466
|
|
11
|
-
retracesoftware/install/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
12
|
-
retracesoftware/install/config.py,sha256=AnSlHI3lWQrPUg0fOS26PqXiq1hJSy56SwrNL8mbAG8,1546
|
|
13
|
-
retracesoftware/install/edgecases.py,sha256=Ry3BETJ4Hmqpw9xXTlNeUgBL9q-sumaazqjmzZBRtD4,7265
|
|
14
|
-
retracesoftware/install/globals.py,sha256=F8XvIoZQQ10gSRalk30dvdKllxlwxkaggYY6FogLDxY,510
|
|
15
|
-
retracesoftware/install/install.py,sha256=HCD_ji8XCr96b5fNzNdL_8qcEp0Jf05Em7T6GA6u8HU,4969
|
|
16
|
-
retracesoftware/install/patcher.py,sha256=sHgsKvdvjMJSVl8lOa3YiCjGf6zYhxgkWp67-ptJvlk,4157
|
|
17
|
-
retracesoftware/install/patchfindspec.py,sha256=uQkfdOQOY20hFAcwDAqx7m8rbtGM2rBKOfC3Szb1zhw,4942
|
|
18
|
-
retracesoftware/install/phases.py,sha256=OWaw86xzC924tdIA9C-12Qdr3sCx67hEOn-Ih9FM--4,9359
|
|
19
|
-
retracesoftware/install/predicate.py,sha256=tX7NQc0rGkyyHYO3mduYHcJHbw1wczT53m_Dpkzo6do,2679
|
|
20
|
-
retracesoftware/install/record.py,sha256=08q7zc9cfyCyhLsh15q1d_5vFIhKbs9ln6VvL6kED5o,5861
|
|
21
|
-
retracesoftware/install/references.py,sha256=A-G651IDOfuo00MkbAdpbIQh_15ChvJ7uAVTSmE6zd4,1721
|
|
22
|
-
retracesoftware/install/replace.py,sha256=FYiSJtNrXEhl-H6M5tJm0kbliBA0sZdxE0306pr-YQg,872
|
|
23
|
-
retracesoftware/install/replay.py,sha256=bB2eMpIP2vXZzeJ98jtlkGSE1DEYWk3olUbiBQcdVj0,3539
|
|
24
|
-
retracesoftware/install/tracer.py,sha256=GO3Nnzd0ylxM7-A7RN0LzLCFdouJIiHtIcJ2zjFx0Q0,9363
|
|
25
|
-
retracesoftware/install/typeutils.py,sha256=-oFzgUfq_nHeOkj3YKZiMLlMzQhCedg3qymLiEJNkVE,2253
|
|
26
|
-
retracesoftware/proxy/__init__.py,sha256=ntIyqKhBRkKEkcW_oOPodikh-mxYl8OXRnSaj-9-Xwc,178
|
|
27
|
-
retracesoftware/proxy/gateway.py,sha256=xESohWXkiNm4ZutU0RgWUwxjxcBWRQ4rQyxIGQXv_F4,1590
|
|
28
|
-
retracesoftware/proxy/globalref.py,sha256=yXtJsOeBHN9xoEgJWA3MJco-jD2SQUef_fDatA4A6rg,803
|
|
29
|
-
retracesoftware/proxy/messagestream.py,sha256=kjxIugCSr7YSJbIqsqqXeW7dyE2SzWow2E-UJnkAflk,5890
|
|
30
|
-
retracesoftware/proxy/proxyfactory.py,sha256=qhOqDfMJnLDNkQs26JqDB431MwjjRhGQi8xupJ45asg,12272
|
|
31
|
-
retracesoftware/proxy/proxysystem.py,sha256=Pj9sk5FE3Mfyf4-PAtXSz2Pvr_GwXcpfhUSJvkz00dg,15076
|
|
32
|
-
retracesoftware/proxy/proxytype.py,sha256=LZ1rNMsxXRUMdI4uTzDvGkUyPOYxCFroT_ZAtOGTbBk,14012
|
|
33
|
-
retracesoftware/proxy/record.py,sha256=JTmeZ13ISbsVal1WOtae425DP0iA-QiAv0C6azPpV8k,6680
|
|
34
|
-
retracesoftware/proxy/replay.py,sha256=Kx4XrsP0T35IvTzf3pwhR24Lotoz43p7E8LBevEQHpE,4948
|
|
35
|
-
retracesoftware/proxy/serializer.py,sha256=S5yhHoP2iYaXBY7Jr8Ei630Z31521x0IO6DeuClOD9s,958
|
|
36
|
-
retracesoftware/proxy/startthread.py,sha256=YKUcOSjA-9_5ZsDHhyP0fXJSP3RWDKV-ajX-I0SI-bU,1270
|
|
37
|
-
retracesoftware/proxy/stubfactory.py,sha256=fyQtCZnkXvLaTlGcxE0Lx8od09ZOgnPWPn0YvNFfVeQ,6219
|
|
38
|
-
retracesoftware/proxy/thread.py,sha256=T1ME6DHB8O0xVnX3Rt1lMl7oCJ2Y0aoFT91D76yNICk,3073
|
|
39
|
-
retracesoftware_proxy-0.2.12.dist-info/METADATA,sha256=zCFbVvn3SSkfMEVfReu6UG8lmz0lVRQGblAQ1RKz8BE,227
|
|
40
|
-
retracesoftware_proxy-0.2.12.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
41
|
-
retracesoftware_proxy-0.2.12.dist-info/top_level.txt,sha256=hYHsR6txLidmqvjBMITpIHvmJJbmoCAgr76-IpZPRz8,16
|
|
42
|
-
retracesoftware_proxy-0.2.12.dist-info/RECORD,,
|
{retracesoftware_proxy-0.2.12.dist-info → retracesoftware_proxy-0.2.15.dist-info}/top_level.txt
RENAMED
|
File without changes
|