def __init__(self, callback): self.callback = callback self.btracer = BytecodeTracer() self.top_level_function = None self.sys_modules = None
class TestBytecodeTracer: def setup(self): self._traces = [] self.btracer = BytecodeTracer() def _trace(self, frame, event, arg): try: if arg is not sys.settrace: for ret in self.btracer.trace(frame, event): if ret[0] is not None: self._traces.append(ret) except TypeError: pass return self._trace def assert_trace(self, *traces): assert_equal(self._traces, list(traces)) def assert_trace_slice(self, start, end, *traces): assert_equal(self._traces[start:end], list(traces)) def trace_function(self, fun): dis.dis(fun.func_code) rewrite_function(fun) self.btracer.setup() sys.settrace(self._trace) try: fun() finally: sys.settrace(None) self.btracer.teardown()
class TestBytecodeTracer: def setup(self): self._traces = [] self._ignored_events = ['load_global', 'store_global'] self.btracer = BytecodeTracer() def _trace(self, frame, event, arg): try: if arg is not sys.settrace: for ret in self.btracer.trace(frame, event): if ret[0] is not None and ret[ 0] not in self._ignored_events: self._traces.append(ret) except TypeError: pass return self._trace def assert_trace(self, *traces): assert_equal(self._traces, list(traces)) def assert_trace_slice(self, start, end, *traces): assert_equal(self._traces[start:end], list(traces)) def trace_function(self, fun): dis.dis(fun.func_code) rewrite_function(fun) self.btracer.setup() sys.settrace(self._trace) try: fun() finally: sys.settrace(None) self.btracer.teardown()
class StandardTracer(object): """Wrapper around basic C{sys.settrace} mechanism that maps 'call', 'return' and 'exception' events into more meaningful callbacks. See L{ICallback} for details on events that tracer reports. """ def __init__(self, callback): self.callback = callback self.btracer = BytecodeTracer() self.top_level_function = None self.sys_modules = None # :: function | str -> None def trace(self, code): """Trace execution of given code. Code may be either a function or a string with Python code. This method may be invoked many times for a single tracer instance. """ self.setup(code) self.btracer.setup() rewrite_function(self.top_level_function) sys.settrace(self.tracer) try: self.top_level_function() finally: sys.settrace(None) self.teardown() self.btracer.teardown() def setup(self, code): self.top_level_function = make_callable(code) self.sys_modules = sys.modules.keys() def teardown(self): # Revert any changes to sys.modules. # This unfortunatelly doesn't include changes to the modules' state itself. # Replaced module instances in sys.modules are also not reverted. modnames = [m for m in sys.modules.keys() if m not in self.sys_modules] for modname in modnames: del sys.modules[modname] self.top_level_function = None self.sys_modules = None def tracer(self, frame, event, arg): # Bytecode tracing is unreliable without the rewrite step, so we have # to ignore all interactions inside that code. That usually concerns # modules that were imported before the tracer started. if not has_been_rewritten(frame.f_code): return # We don't want to trace our own internals. if is_called_from_code_rewriting_importer(frame): return bytecode_events = list(self.btracer.trace(frame, event)) if bytecode_events: for ev, args in bytecode_events: # Exceptions originating in C code are reported only after # execution goes back to the Python level. To regain # consistency with other exception events, we simulate # an exception raised inside C code just before its return. if ev == 'c_return' and event == 'exception': self.handle_standard_tracer_event(frame, event, arg) self.handle_bytecode_tracer_event(ev, args) return self.handle_standard_tracer_event(frame, event, arg) def handle_bytecode_tracer_event(self, event, args): if event == 'c_call': self.record_c_call(*args) elif event == 'c_return': self.callback.c_returned(args) elif event == 'print': pass # TODO elif event == 'print_to': value, output = args pass # TODO elif event == 'store_attr': obj, name, value = args self.callback.attribute_rebound(obj, name, value) elif event == 'delete_attr': obj, name = args pass # TODO elif event == 'load_global': module, name, value = args if name not in builtins_names: self.callback.global_read(module, name, value) elif event == 'store_global': module, name, value = args self.callback.global_rebound(module, name, value) elif event == 'delete_global': pass # TODO def handle_standard_tracer_event(self, frame, event, arg): if event == 'call': if not self.should_ignore_frame(frame): if self.record_call(frame): return self.tracer elif event == 'return': self.callback.returned(arg) elif event == 'exception': if not is_generator_exit(arg[0]): # There are three cases here, each requiring different handling # of values in arg[0] and arg[1]. First, we may get a regular # exception generated by the `raise` statement. Second, we may # get an exception generated inside the interpreter, like an # IndexError or NameError. Finally, code in Python < 2.6 can # raise a string exception. # # In each case, arg[0] and arg[1] have different values, # described in the table below. # # +------------------+---------------------------+ # | arg[0] | arg[1] | # +-------------+------------------+---------------------------+ # | regular | exception type | exception instance | # | exceptions | (e.g. TypeError)| | # +-------------+------------------+---------------------------+ # | interpreter | exception type | message (a string) or | # | exceptions | (e.g. NameError)| exception initialization | # | | | arguments (a tuple) | # +-------------+------------------+---------------------------+ # | string | string itself | value or None | # | exceptions | (e.g. "Error") | | # +-------------+------------------+---------------------------+ # # arg[2] in all cases contains an exception traceback. if isinstance(arg[0], str): # Return the string itself as an exception and ignore # the value, as it's not used during test generation, # at least for now. exception = arg[0] elif isinstance(arg[1], str): # Recreate instance of a single-argument interpreter # exception. exception = arg[0](arg[1]) elif isinstance(arg[1], tuple): # Recreate instance of a multi-argument interpreter # exception. exception = arg[0](*arg[1]) else: exception = arg[1] self.callback.raised(exception, arg[2]) def should_ignore_frame(self, frame): return is_class_definition(frame) or self.is_ignored_code(frame.f_code) def is_ignored_code(self, code): if code.co_name in IGNORED_NAMES: return True if self.top_level_function is not None \ and code is self.top_level_function.func_code: return True return False def record_call(self, frame): code = frame.f_code name = code.co_name try: obj, input = get_method_information(frame) return self.callback.method_called(name, obj, input, code, frame) except NotMethodFrame: input = input_from_argvalues(*inspect.getargvalues(frame)) return self.callback.function_called(name, input, code, frame) def record_c_call(self, func, pargs, kargs): func_name = func.__name__ obj = get_self_from_method(func) if obj is not None: klass = type(obj) self.callback.c_method_called(obj, klass, func_name, pargs) else: self.callback.c_function_called(func_name, pargs)
def setup(self): self._traces = [] self._ignored_events = ['load_global', 'store_global'] self.btracer = BytecodeTracer()
def setup(self): self._traces = [] self.btracer = BytecodeTracer()
import dis import inspect import sys from bytecode_tracer import BytecodeTracer from bytecode_tracer import rewrite_function btracer = BytecodeTracer() def trace(frame, event, arg): bytecode_events = list(btracer.trace(frame, event)) if bytecode_events: for ev, rest in bytecode_events: if ev == 'c_call': func, pargs, kargs = rest print "C_CALL", func.__name__, repr(pargs), repr(kargs) elif ev == 'c_return': print "C_RETURN", repr(rest) elif ev == 'print': print "PRINT", repr(rest) elif ev == 'print_to': value, output = rest print "PRINT_TO", repr(value), repr(output) else: if event == 'call': args = inspect.getargvalues(frame) try: args = str(args) except Exception: args = "<unknown>" print "CALL", frame.f_code.co_name, args
class StandardTracer(object): """Wrapper around basic C{sys.settrace} mechanism that maps 'call', 'return' and 'exception' events into more meaningful callbacks. See L{ICallback} for details on events that tracer reports. """ def __init__(self, callback): self.callback = callback self.btracer = BytecodeTracer() self.top_level_function = None self.sys_modules = None # :: function | str -> None def trace(self, code): """Trace execution of given code. Code may be either a function or a string with Python code. This method may be invoked many times for a single tracer instance. """ self.setup(code) self.btracer.setup() rewrite_function(self.top_level_function) sys.settrace(self.tracer) try: self.top_level_function() finally: sys.settrace(None) self.teardown() self.btracer.teardown() def setup(self, code): self.top_level_function = make_callable(code) self.sys_modules = sys.modules.keys() def teardown(self): # Revert any changes to sys.modules. # This unfortunatelly doesn't include changes to the modules' state itself. # Replaced module instances in sys.modules are also not reverted. modnames = [m for m in sys.modules.keys() if m not in self.sys_modules] for modname in modnames: del sys.modules[modname] self.top_level_function = None self.sys_modules = None def tracer(self, frame, event, arg): # Bytecode tracing is unreliable without the rewrite step, so we have # to ignore all interactions inside that code. That usually concerns # modules that were imported before the tracer started. if not has_been_rewritten(frame.f_code): return # We don't want to trace our own internals. if is_called_from_code_rewriting_importer(frame): return bytecode_events = list(self.btracer.trace(frame, event)) if bytecode_events: for ev, args in bytecode_events: # Exceptions originating in C code are reported only after # execution goes back to the Python level. To regain # consistency with other exception events, we simulate # an exception raised inside C code just before its return. if ev == 'c_return' and event == 'exception': self.handle_standard_tracer_event(frame, event, arg) self.handle_bytecode_tracer_event(ev, args) return self.handle_standard_tracer_event(frame, event, arg) def handle_bytecode_tracer_event(self, event, args): if event == 'c_call': self.record_c_call(*args) elif event == 'c_return': self.callback.c_returned(args) elif event == 'print': pass # TODO elif event == 'print_to': #value, output = args pass # TODO elif event == 'store_attr': obj, name, value = args self.callback.attribute_rebound(obj, name, value) elif event == 'delete_attr': obj, name = args pass # TODO elif event == 'load_global': module, name, value = args if name not in builtins_names: self.callback.global_read(module, name, value) elif event == 'store_global': module, name, value = args self.callback.global_rebound(module, name, value) elif event == 'delete_global': pass # TODO def handle_standard_tracer_event(self, frame, event, arg): if event == 'call': if not self.should_ignore_frame(frame): if self.record_call(frame): return self.tracer elif event == 'return': self.callback.returned(arg) elif event == 'exception': if not is_generator_exit(arg[0]): # There are three cases here, each requiring different handling # of values in arg[0] and arg[1]. First, we may get a regular # exception generated by the `raise` statement. Second, we may # get an exception generated inside the interpreter, like an # IndexError or NameError. Finally, code in Python < 2.6 can # raise a string exception. # # In each case, arg[0] and arg[1] have different values, # described in the table below. # # +------------------+---------------------------+ # | arg[0] | arg[1] | # +-------------+------------------+---------------------------+ # | regular | exception type | exception instance | # | exceptions | (e.g. TypeError)| | # +-------------+------------------+---------------------------+ # | interpreter | exception type | message (a string) or | # | exceptions | (e.g. NameError)| exception initialization | # | | | arguments (a tuple) | # +-------------+------------------+---------------------------+ # | string | string itself | value or None | # | exceptions | (e.g. "Error") | | # +-------------+------------------+---------------------------+ # # arg[2] in all cases contains an exception traceback. if isinstance(arg[0], str): # Return the string itself as an exception and ignore # the value, as it's not used during test generation, # at least for now. exception = arg[0] elif isinstance(arg[1], str): # Recreate instance of a single-argument interpreter # exception. exception = arg[0](arg[1]) elif isinstance(arg[1], tuple): # Recreate instance of a multi-argument interpreter # exception. exception = arg[0](*arg[1]) else: exception = arg[1] self.callback.raised(exception, arg[2]) def should_ignore_frame(self, frame): return is_class_definition(frame) or self.is_ignored_code(frame.f_code) def is_ignored_code(self, code): if code.co_name in IGNORED_NAMES: return True if self.top_level_function is not None \ and code is self.top_level_function.func_code: return True return False def record_call(self, frame): code = frame.f_code name = code.co_name try: obj, input = get_method_information(frame) return self.callback.method_called(name, obj, input, code, frame) except NotMethodFrame: input = input_from_argvalues(*inspect.getargvalues(frame)) return self.callback.function_called(name, input, code, frame) def record_c_call(self, func, pargs, kargs): func_name = func.__name__ obj = get_self_from_method(func) print ("TODO: CHECK c_call:%s" % kargs) if obj is not None: klass = type(obj) self.callback.c_method_called(obj, klass, func_name, pargs) else: self.callback.c_function_called(func_name, pargs)
import dis import inspect import sys from bytecode_tracer import BytecodeTracer from bytecode_tracer import rewrite_function btracer = BytecodeTracer() def trace(frame, event, arg): bytecode_events = list(btracer.trace(frame, event)) if bytecode_events: for ev, rest in bytecode_events: if ev == 'c_call': func, pargs, kargs = rest print "C_CALL", func.__name__, repr(pargs), repr(kargs) elif ev == 'c_return': print "C_RETURN", repr(rest) elif ev == 'print': print "PRINT", repr(rest) elif ev == 'print_to': value, output = rest print "PRINT_TO", repr(value), repr(output) else: if event == 'call': args = inspect.getargvalues(frame) try: args = str(args) except Exception: args = "<unknown>"