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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (35) hide show
  1. retracesoftware/__main__.py +285 -0
  2. retracesoftware/autoenable.py +53 -0
  3. retracesoftware/config.json +19 -233
  4. retracesoftware/config.yaml +0 -0
  5. retracesoftware/install/config.py +6 -0
  6. retracesoftware/install/edgecases.py +23 -1
  7. retracesoftware/install/patcher.py +98 -513
  8. retracesoftware/install/patchfindspec.py +117 -0
  9. retracesoftware/install/phases.py +338 -0
  10. retracesoftware/install/record.py +111 -40
  11. retracesoftware/install/replace.py +28 -0
  12. retracesoftware/install/replay.py +59 -11
  13. retracesoftware/install/tracer.py +171 -33
  14. retracesoftware/install/typeutils.py +20 -0
  15. retracesoftware/modules.toml +384 -0
  16. retracesoftware/preload.txt +216 -0
  17. retracesoftware/proxy/__init__.py +1 -1
  18. retracesoftware/proxy/globalref.py +31 -0
  19. retracesoftware/proxy/messagestream.py +204 -0
  20. retracesoftware/proxy/proxysystem.py +328 -71
  21. retracesoftware/proxy/proxytype.py +90 -38
  22. retracesoftware/proxy/record.py +109 -119
  23. retracesoftware/proxy/replay.py +94 -188
  24. retracesoftware/proxy/serializer.py +28 -0
  25. retracesoftware/proxy/startthread.py +40 -0
  26. retracesoftware/proxy/stubfactory.py +82 -27
  27. retracesoftware/proxy/thread.py +64 -4
  28. retracesoftware/replay.py +104 -0
  29. retracesoftware/run.py +378 -0
  30. retracesoftware/stackdifference.py +133 -0
  31. {retracesoftware_proxy-0.1.5.dist-info → retracesoftware_proxy-0.2.4.dist-info}/METADATA +2 -1
  32. retracesoftware_proxy-0.2.4.dist-info/RECORD +42 -0
  33. retracesoftware_proxy-0.1.5.dist-info/RECORD +0 -27
  34. {retracesoftware_proxy-0.1.5.dist-info → retracesoftware_proxy-0.2.4.dist-info}/WHEEL +0 -0
  35. {retracesoftware_proxy-0.1.5.dist-info → retracesoftware_proxy-0.2.4.dist-info}/top_level.txt +0 -0
@@ -1,86 +1,20 @@
1
1
  import retracesoftware.functional as functional
2
2
  import retracesoftware_utils as utils
3
- import retracesoftware.stream as stream
4
3
 
5
4
  from retracesoftware.install.tracer import Tracer
6
- from retracesoftware.proxy.thread import per_thread_messages
5
+ from retracesoftware.proxy.thread import per_thread_messages, thread_id
6
+ from retracesoftware.proxy.messagestream import *
7
7
  from retracesoftware.proxy.proxytype import *
8
8
  # from retracesoftware.proxy.gateway import gateway_pair
9
- from retracesoftware.proxy.record import StubRef, Placeholder
10
- from retracesoftware.proxy.proxysystem import ProxySystem, RetraceError
11
- from retracesoftware.proxy.stubfactory import StubFactory, StubFunction
9
+ from retracesoftware.proxy.record import StubRef
10
+ from retracesoftware.proxy.proxysystem import ProxySystem
11
+ from retracesoftware.proxy.stubfactory import StubFactory
12
+ from retracesoftware.proxy.globalref import GlobalRef
12
13
 
13
14
  import os
14
- import weakref
15
- import traceback
16
- # we can have a dummy method descriptor, its has a __name__ and when called, returns the next element
17
-
18
- # for types, we can patch the __new__ method
19
- # do it from C and immutable types can be patched too
20
- # patch the tp_new pointer?
21
-
22
- class ReplayError(RetraceError):
23
- pass
24
15
 
25
16
  class ReplayProxySystem(ProxySystem):
26
17
 
27
- def stubtype(self, cls):
28
- assert not issubclass(cls, Proxy)
29
-
30
- return dynamic_proxytype(handler = self.ext_handler, cls = cls)
31
-
32
- def create_stub(self): return True
33
-
34
- # def stubtype_from_spec(self, spec):
35
- # print (f'FOOO!!! {spec}')
36
- # return stubtype_from_spec(
37
- # handler = self.ext_handler,
38
- # module = spec.module,
39
- # name = spec.name,
40
- # methods = spec.methods,
41
- # members = spec.members)
42
-
43
- @utils.striptraceback
44
- def next_result(self):
45
- while True:
46
- next = self.messages()
47
-
48
- if next == 'CALL':
49
- func = self.messages()
50
- args = self.messages()
51
- kwargs = self.messages()
52
-
53
- try:
54
- func(*args, **kwargs)
55
- except:
56
- pass
57
-
58
- elif next == 'RESULT':
59
- return self.messages()
60
- elif next == 'ERROR':
61
- err_type = self.messages()
62
- err_value = self.messages()
63
- utils.raise_exception(err_type, err_value)
64
- else:
65
- assert type(next) is not str
66
- return next
67
-
68
- def bind(self, obj):
69
- read = self.messages()
70
-
71
- assert isinstance(read, Placeholder)
72
-
73
- self.bindings[read] = obj
74
-
75
- # def dynamic_path(self):
76
- # if self.getpid() != self.pid:
77
- # self.pid = self.getpid()
78
- # # ok we are in child, calculate new path
79
- # self.path = self.path / f'fork-{self.fork_counter}'
80
- # self.fork_counter = 0
81
-
82
- # return self.path
83
-
84
18
  def after_fork_in_child(self):
85
19
  self.reader.path = self.new_child_path(self.reader.path)
86
20
  super().after_fork_in_child()
@@ -88,145 +22,117 @@ class ReplayProxySystem(ProxySystem):
88
22
  # def dynamic_ext_proxytype(self, cls):
89
23
  # raise Exception('dynamic_ext_proxytype should not be called in replay')
90
24
 
91
- def proxy_function(self, obj):
92
- func = functional.repeatedly(self.next_result)
93
- func.__name__ = obj.__name__
94
-
95
- return super().proxy_function(func)
25
+ @property
26
+ def ext_apply(self):
27
+ return functional.repeatedly(self.next_result)
96
28
 
97
- def __init__(self,
98
- thread_state,
99
- immutable_types,
100
- tracing_config,
101
- path,
102
- fork_path = []):
103
-
104
- # self.writer = writer
105
- # super().__init__(thread_state = thread_state)
106
- reader = stream.reader(path)
107
-
108
- self.bindings = utils.id_dict()
109
- self.set_thread_id = utils.set_thread_id
110
- self.fork_path = fork_path
111
- deserialize = functional.walker(self.bindings.get_else_key)
112
-
113
- self.messages = functional.sequence(per_thread_messages(reader), deserialize)
29
+ def proxy__new__(self, __new__, *args, **kwargs):
30
+ func = functional.repeatedly(self.next_result)
31
+ func.__name__ = '__new__'
32
+ return super().proxy__new__(func, *args, **kwargs)
114
33
 
115
- self.stub_factory = StubFactory(thread_state = thread_state, next_result = self.next_result)
34
+ def basetype(self, cls):
35
+ return self.stub_factory.create_stubtype(StubRef(cls))
116
36
 
117
- # messages = reader
37
+ def trace_writer(self, name, *args):
38
+ with self.thread_state.select('disabled'):
39
+ # read = self.messages_read
118
40
 
119
- def readnext():
120
- with thread_state.select('disabled'):
121
- try:
122
- return self.messages()
123
- except Exception as error:
124
- # print(f'Error reading stream: {error}')
125
- traceback.print_exc()
41
+ self.read_required('TRACE')
42
+ # self.read_required(read)
43
+ self.read_required(name)
126
44
 
45
+ if name == 'stacktrace':
46
+ print('FOOO!!!')
47
+ os._exit(1)
48
+ record = self.readnext()
49
+ if args[0] == record:
50
+ self.last_matching_stack = args[0]
51
+ else:
52
+ on_stack_mismatch(
53
+ last_matching = self.last_matching_stack,
54
+ record = record,
55
+ replay = args[0])
127
56
  os._exit(1)
57
+ else:
58
+ # print(f'Trace: {self.reader.messages_read} {name} {args}')
59
+ for arg in args:
60
+ self.read_required(arg)
128
61
 
129
- # print(f'read: {obj}')
130
- # return obj
131
-
62
+ def on_thread_exit(self, thread_id):
63
+ # print(f'on_thread_exit!!!!')
64
+ self.reader.wake_pending()
132
65
 
133
- # lookup = weakref.WeakKeyDictionary()
134
-
135
- # debug = debug_level(config)
136
-
137
- # int_refs = {}
138
-
139
- def read_required(required):
140
- obj = readnext()
141
- if obj != required:
142
- print(f'Replay: {required} Record: {obj}')
143
- for i in range(5):
144
- print(readnext())
145
-
146
- utils.sigtrap(None)
147
- os._exit(1)
148
- raise Exception(f'Expected: {required} but got: {obj}')
66
+ def __init__(self,
67
+ reader,
68
+ thread_state,
69
+ immutable_types,
70
+ tracing_config,
71
+ traceargs,
72
+ verbose = False,
73
+ fork_path = [],
74
+ skip_weakref_callbacks = False):
149
75
 
150
- def trace_writer(name, *args):
151
- with thread_state.select('disabled'):
152
- print(f'Trace: {name} {args}')
153
-
154
- read_required('TRACE')
155
- read_required(name)
76
+ self.reader = reader
77
+ # self.skip_weakref_callbacks = skip_weakref_callbacks
156
78
 
157
- for arg in args:
158
- read_required(arg)
79
+ self.fork_path = fork_path
159
80
 
160
- # self.tracer = Tracer(tracing_config, writer = trace_writer)
161
- # self.immutable_types = immutable_types
81
+ self.messages = MessageStream(
82
+ thread_state = thread_state,
83
+ source = reader,
84
+ skip_weakref_callbacks = skip_weakref_callbacks,
85
+ verbose = verbose)
162
86
 
163
- self.reader = reader
87
+ self.checkpoint = self.messages.checkpoint
88
+ self.bind = self.messages.bind
89
+ self.next_result = self.messages.result
90
+ self.exclude_from_stacktrace = self.messages.excludes.add
164
91
 
165
- # def foo(cls):
166
- # print(cls)
167
- # assert isinstance(cls, type)
168
- # immutable_types.add(cls)
92
+ self.stub_factory = StubFactory(thread_state = thread_state, next_result = self.next_result)
169
93
 
170
- # add_stubtype = functional.side_effect(foo)
171
- # add_stubtype = functional.side_effect(immutable_types.add)
94
+ self.last_matching_stack = None
172
95
 
173
- # reader.type_deserializer[ProxyRef] = functional.sequence(lambda ref: ref.resolve(), self.stubtype, add_stubtype)
96
+ def run_ref(ref):
97
+ print(f'run_ref!!!! {ref}')
98
+ return ref()
174
99
 
175
- reader.type_deserializer[StubRef] = self.stub_factory
176
- # reader.type_deserializer[ProxySpec] = functional.sequence(self.stubtype_from_spec, add_stubtype)
100
+ self.reader.type_deserializer[StubRef] = self.stub_factory
101
+ # self.reader.type_deserializer[GlobalRef] = lambda ref: ref()
102
+ self.reader.type_deserializer[GlobalRef] = run_ref
177
103
 
178
- # on_ext_result = functional.if_then_else(
179
- # functional.is_instanceof(str), writer.handle('RESULT'), writer)
104
+ excludes = [ReplayProxySystem.trace_writer]
105
+
106
+ for exclude in excludes:
107
+ self.messages.excludes.add(exclude)
180
108
 
181
- # def int_proxytype(gateway):
182
- # return lambda cls: dynamic_int_proxytype(handler = gateway, cls = cls, bind = self.bind)
109
+ sync = functional.lazy(self.messages.read_required, 'SYNC')
183
110
 
184
- # create_stubs = functional.walker(functional.when(is_stub_ref, lambda stub: stub.create()))
185
- # create_stubs = functional.walker(functional.when(is_stub_type, lambda cls: cls()))
111
+ read_sync = thread_state.dispatch(utils.noop, internal = sync)
186
112
 
187
- # self.ext_apply = functional.repeatedly(functional.sequence(self.next_result, create_stubs))
188
- # self.ext_apply = functional.repeatedly(self.next_result)
189
-
190
- def read_sync(): read_required('SYNC')
113
+ self.on_ext_call = sync
191
114
 
192
- self.sync = lambda function: utils.observer(on_call = functional.always(read_sync), function = function)
193
-
194
- super().__init__(thread_state = thread_state,
195
- tracer = Tracer(tracing_config, writer = trace_writer),
196
- immutable_types = immutable_types)
197
-
198
- # super().__init__(
199
- # thread_state=thread_state,
200
- # immutable_types= immutable_types,
201
- # tracer=self.tracer,
202
- # ext_apply = ext_apply)
115
+ self.sync = lambda function: utils.observer(on_call = read_sync, function = function)
203
116
 
204
- # self.ext_handler, self.int_handler = gateway_pair(
205
- # thread_state,
206
- # self.tracer,
207
- # immutable_types = immutable_types,
208
- # ext_apply = ext_apply,
209
- # int_proxytype = int_proxytype,
210
- # ext_proxytype = functional.identity)
211
-
212
- # def extend_type(self, base):
213
-
214
- # # ok, how to provide __getattr__ style access,
117
+ self.create_from_external = utils.noop
215
118
 
216
- # extended = extending_proxytype(
217
- # cls = base,
218
- # thread_state = self.thread_state,
219
- # int_handler = self.int_handler,
220
- # ext_handler = self.ext_handler,
221
- # on_subclass_new = self.bind,
222
- # is_stub = True)
119
+ if skip_weakref_callbacks:
120
+ self.wrap_weakref_callback = \
121
+ lambda callback: \
122
+ thread_state.dispatch(
123
+ callback, internal = self.disable_for(callback))
124
+ else:
125
+ self.on_weakref_callback_start = functional.lazy(self.messages.read_required, 'ON_WEAKREF_CALLBACK_START')
126
+ self.on_weakref_callback_end = functional.lazy(self.messages.read_required, 'ON_WEAKREF_CALLBACK_END')
223
127
 
224
- # self.immutable_types.add(extended)
225
- # # proxytype = extending_proxytype(base)
128
+ super().__init__(thread_state = thread_state,
129
+ tracer = Tracer(tracing_config, writer = self.trace_writer),
130
+ immutable_types = immutable_types,
131
+ traceargs = traceargs)
226
132
 
227
- # # make_extensible(cls = extended,
228
- # # int_handler = self.int_handler,
229
- # # ext_handler = self.ext_handler,
230
- # # on_new = self.reader.supply)
133
+ def write_trace(self, obj):
134
+ if 'TRACER' != self.messages():
135
+ utils.sigtrap(obj)
231
136
 
232
- # return extended
137
+ # self.read_required ('TRACER')
138
+ self.read_required(obj)
@@ -0,0 +1,28 @@
1
+ def serializer(obj):
2
+ cls = type(obj)
3
+
4
+ try:
5
+ if issubclass(cls, Enum):
6
+ return str(obj)
7
+
8
+ if issubclass(cls, (tuple, list)):
9
+ return list(map(serializer, obj))
10
+ elif issubclass(cls, dict):
11
+ return {str(serializer(k)):serializer(v) for k,v in obj.items()}
12
+ elif issubclass(cls, int):
13
+ if obj > 1000000 or obj < -1000000:
14
+ return "XXXX"
15
+ else:
16
+ return int(obj)
17
+ elif issubclass(cls, (bool, str, types.NoneType)):
18
+ return obj
19
+ elif issubclass(cls, types.FunctionType):
20
+ return obj.__qualname__
21
+ elif issubclass(cls, types.CellType):
22
+ return getattr(obj, '__qualname__', 'CellType')
23
+ elif issubclass(cls, types.GeneratorType):
24
+ return obj.__qualname__
25
+ else:
26
+ return cleanse(str(obj))
27
+ except:
28
+ return f'Unserializable object of type: {cls}'
@@ -0,0 +1,40 @@
1
+ from retracesoftware import functional
2
+ from retracesoftware import utils
3
+
4
+ import _thread
5
+
6
+ # _thread.start_new_thread(function, args[, kwargs])
7
+
8
+ # push a per thread executor function, as a context manager
9
+
10
+ counters = _thread._local()
11
+ counters.id = ()
12
+ counters.counter = 0
13
+
14
+ def with_thread_id(thread_id, function, *args, **kwargs):
15
+ counters.id = thread_id
16
+ counters.counter = 0
17
+ return function(*args, **kwargs)
18
+
19
+ thread_id = functional.lazy(getattr, counters, 'id')
20
+
21
+ def start_new_thread(original, wrapper, function, *args):
22
+ return original(wrapper(function), *args)
23
+
24
+ def wrap_thread_function(thread_state, function):
25
+ if thread_state.value == 'internal':
26
+ next_id = counters.id + (counters.counter,)
27
+ counters.counter += 1
28
+ return functional.partial(with_thread_id, next_id, thread_state.wrap('internal', function))
29
+ else:
30
+ return function
31
+
32
+ def patch_thread_start(thread_state):
33
+
34
+ wrapper = functional.partial(wrap_thread_function, thread_state)
35
+
36
+ _thread.start_new = functional.partial(start_new_thread, _thread.start_new, wrapper)
37
+ _thread.start_new_thread = functional.partial(start_new_thread, _thread.start_new_thread, wrapper)
38
+
39
+ import threading
40
+ threading._start_new_thread = _thread.start_new_thread
@@ -13,14 +13,33 @@ class ExtendedRef:
13
13
  self.module = module
14
14
 
15
15
  class StubRef:
16
- def __init__(self, module, name, methods, members):
17
- self.name = name
18
- self.module = module
19
- self.methods = methods
20
- self.members = members
16
+ def __init__(self, cls):
17
+ blacklist = ['__class__', '__dict__', '__module__', '__doc__', '__new__']
18
+
19
+ self.methods = []
20
+ self.static_methods = []
21
+ self.class_methods = []
22
+
23
+ for key,value in cls.__dict__.items():
24
+ if key not in blacklist:
25
+ if isinstance(value, classmethod):
26
+ self.class_methods.append(key)
27
+ elif isinstance(value, staticmethod):
28
+ self.class_methods.append(key)
29
+ elif utils.is_method_descriptor(value):
30
+ self.methods.append(key)
31
+
32
+ self.name = cls.__name__
33
+ self.module = cls.__module__
34
+
35
+ # def __init__(self, module, name, methods, members):
36
+ # self.name = name
37
+ # self.module = module
38
+ # self.methods = methods
39
+ # self.members = members
21
40
 
22
41
  def __str__(self):
23
- return f'StubRef(module = {self.module}, name = {self.name}, methods = {self.methods}, members = {self.members})'
42
+ return f'StubRef(module = {self.module}, name = {self.name}, methods = {self.methods})'
24
43
 
25
44
  def resolve(module, name):
26
45
  try:
@@ -33,6 +52,11 @@ class StubMethodDescriptor(functional.repeatedly):
33
52
  super().__init__(next_result)
34
53
  self.__name__ = name
35
54
 
55
+ # @utils.striptraceback
56
+ # def __call__(self, *args, **kwargs):
57
+ # super().__call__(*args, **kwargs)
58
+
59
+ #@utils.striptraceback
36
60
  def __str__(self):
37
61
  return f"stub - {__name__}"
38
62
 
@@ -41,29 +65,25 @@ class StubMemberDescriptor:
41
65
  self.next_result = next_result
42
66
  self.__name__ = name
43
67
 
68
+ #@utils.striptraceback
44
69
  def __get__(self, instance, owner):
45
70
  if instance is None:
46
71
  return self
47
72
 
48
73
  return self.next_result()
49
74
 
75
+ #@utils.striptraceback
50
76
  def __set__(self, instance, value):
51
77
  return self.next_result()
52
78
 
79
+ #@utils.striptraceback
53
80
  def __delete__(self, instance):
54
81
  return self.next_result()
55
-
82
+
83
+ #@utils.striptraceback
56
84
  def __str__(self):
57
85
  return f"stub member - {__name__}"
58
86
 
59
- class StubFunction(functional.repeatedly):
60
- def __init__(self, name, next_result):
61
- super().__init__(next_result)
62
- self.__name__ = name
63
-
64
- def __str__(self):
65
- return f"stub function - {__name__}"
66
-
67
87
  class StubFactory:
68
88
 
69
89
  __slots__ = ['next_result', 'thread_state', 'cache']
@@ -85,16 +105,21 @@ class StubFactory:
85
105
  next_result = self.thread_state.dispatch(disabled, external = self.next_result)
86
106
 
87
107
  return StubMemberDescriptor(name = name, next_result = next_result)
88
-
108
+
89
109
  def create_method(self, name):
90
110
 
91
111
  def disabled(*args, **kwargs):
92
112
  if self.thread_state.value == 'disabled' and name == '__repr__':
93
113
  return f"stub - {name}"
94
114
  else:
95
- print(f'Error trying to call descriptor: {name} {args} {kwargs}, retrace mode: {self.thread_state.value}')
96
- utils.sigtrap(None)
97
- os._exit(1)
115
+ return None
116
+ # print(f'Error trying to call descriptor: {name} {args} {kwargs}, retrace mode: {self.thread_state.value}')
117
+
118
+ # import traceback
119
+ # traceback.print_stack()
120
+
121
+ # utils.sigtrap(None)
122
+ # os._exit(1)
98
123
 
99
124
  next_result = self.thread_state.dispatch(disabled, external = self.next_result)
100
125
 
@@ -105,36 +130,66 @@ class StubFactory:
105
130
 
106
131
  def create_stubtype(self, spec):
107
132
 
133
+ assert self.thread_state.value == 'disabled'
134
+
108
135
  slots = {
109
136
  '__module__': spec.module,
110
137
  '__qualname__': spec.name,
138
+ '__name__': spec.name,
111
139
  }
112
140
 
113
141
  for method in spec.methods:
114
142
  slots[method] = self.create_method(method)
115
143
  assert utils.is_method_descriptor(slots[method])
116
144
 
117
- for member in spec.members:
118
- slots[member] = self.create_member(member)
145
+ def on_disabled(name):
146
+ print(f'Error trying to get/set attribute: {name}, when retrace mode: {self.thread_state.value} was not external')
147
+ utils.sigtrap(None)
148
+ os._exit(1)
149
+
150
+ #@utils.striptraceback
151
+ def getattr(instance, name):
152
+ print('In stub getattr!!!')
153
+ if self.thread_state.value == 'external':
154
+ return self.next_result()
155
+ else:
156
+ print(f'Error trying to get attribute: {name}, when retrace mode: {self.thread_state.value} was not external')
157
+ utils.sigtrap(None)
158
+ os._exit(1)
159
+
160
+ #@utils.striptraceback
161
+ def setattr(instance, name, value):
162
+ if self.thread_state.value == 'external':
163
+ return self.next_result()
164
+ else:
165
+ print(f'Error trying to set attribute: {name}, to: {value} when retrace mode: {self.thread_state.value} was not external')
166
+ utils.sigtrap(None)
167
+ os._exit(1)
168
+
169
+ slots['__getattr__'] = self.thread_state.method_dispatch(on_disabled, external = self.next_result)
170
+ # slots['__getattr__'] = getattr
171
+ slots['__setattr__'] = self.thread_state.method_dispatch(on_disabled, external = self.next_result)
119
172
 
120
173
  resolved = resolve(spec.module, spec.name)
121
174
 
122
- if isinstance(resolved, type):
123
- slots['__class__'] = property(functional.repeatedly(resolved))
175
+ # if isinstance(resolved, type):
176
+ # slots['__class__'] = property(functional.repeatedly(resolved))
177
+
178
+ # else:
179
+ # utils.sigtrap(f'{spec.module}.{spec.name}')
124
180
 
125
181
  stubtype = type(spec.name, (Stub, ), slots)
126
182
 
127
183
  for method in spec.methods:
128
184
  slots[method].__objclass__ = stubtype
129
185
 
186
+ stubtype.__retrace_target_type__ = resolved
187
+
130
188
  return stubtype
131
189
 
132
190
  def __call__(self, spec):
133
- print(f'In stubFactory.__call__ {spec}')
134
191
  if spec not in self.cache:
135
192
  self.cache[spec] = self.create_stubtype(spec)
136
193
 
137
194
  stubtype = self.cache[spec]
138
- stub = stubtype.__new__(stubtype)
139
- print(f'In stubFactory created new')
140
- return stub
195
+ return stubtype.__new__(stubtype)