def test_tracing_vars(LineMatcher): lines = StringIO() with hunter.trace(actions=[ VarsPrinter('b', stream=lines), CodePrinter(stream=lines) ]): def a(): b = 1 b = 2 return 1 b = a() b = 2 try: raise Exception("BOOM!") except Exception: pass print(lines.getvalue()) lm = LineMatcher(lines.getvalue().splitlines()) lm.fnmatch_lines([ "*test_hunter.py* call def a():", "*test_hunter.py* line b = 1", "* vars b => 1", "*test_hunter.py* line b = 2", "* vars b => 2", "*test_hunter.py* line return 1", "* vars b => 2", "*test_hunter.py* return return 1", "* ... return value: 1", ])
def test_pid_prefix(LineMatcher, Action, force_pid, capfd): def main(): a = 1 pid = os.fork() if pid: os.waitpid(pid, 0) else: os._exit(0) # child with hunter.trace(actions=[ Action(force_pid=force_pid, stream=sys.stdout), VarsPrinter('a', force_pid=force_pid, stream=sys.stdout) ], stdlib=False, threading_support=True): main() out, err = capfd.readouterr() print('OUT', out) print('ERR', err) lm = LineMatcher(out.splitlines()) prefix = '[[]*[]] *' if force_pid else '' lm.fnmatch_lines_random([ prefix + "MainThread *test_hunter.py:* line * a = 1", prefix + "MainThread *test_hunter.py:* line * if pid:", prefix + "MainThread * * vars * a => 1", prefix + "MainThread *test_hunter.py:* line * os.waitpid(pid, 0)", "[[]*[]] *MainThread *test_hunter.py:* line * os._exit(0) # child", "[[]*[]] *MainThread * * vars * a => 1", ])
def run(): output = StringIO() with t.trace( Q(~Q(module_in=['re', 'sre', 'sre_parse']) & ~Q(module_startswith='namedtuple') & Q(kind="call"), actions=[ CodePrinter(stream=output), VarsPrinter('line', globals=True, stream=output) ])): _bulky_func_that_use_stdlib()
def test_tracing_printing_failures(LineMatcher): lines = StringIO() with trace(actions=[ CodePrinter(stream=lines), VarsPrinter("x", stream=lines) ]): class Bad(object): __slots__ = [] def __repr__(self): raise RuntimeError("I'm a bad class!") def a(): x = Bad() return x def b(): x = Bad() raise Exception(x) a() try: b() except Exception as exc: pass lm = LineMatcher(lines.getvalue().splitlines()) lm.fnmatch_lines([ """*tests*test_hunter.py:* call class Bad(object):""", """*tests*test_hunter.py:* line class Bad(object):""", """*tests*test_hunter.py:* line def __repr__(self):""", """*tests*test_hunter.py:* return def __repr__(self):""", """* ... return value: *""", """*tests*test_hunter.py:* call def a():""", """*tests*test_hunter.py:* line x = Bad()""", """*tests*test_hunter.py:* line return x""", """* vars x => !!! FAILED REPR: RuntimeError("I'm a bad class!"*)""", """*tests*test_hunter.py:* return return x""", """* ... return value: !!! FAILED REPR: RuntimeError("I'm a bad class!"*)""", """* vars x => !!! FAILED REPR: RuntimeError("I'm a bad class!"*)""", """*tests*test_hunter.py:* call def b():""", """*tests*test_hunter.py:* line x = Bad()""", """*tests*test_hunter.py:* line raise Exception(x)""", """* vars x => !!! FAILED REPR: RuntimeError("I'm a bad class!"*)""", """*tests*test_hunter.py:* exception raise Exception(x)""", """* ... exception value: !!! FAILED REPR: RuntimeError("I'm a bad class!"*)""", """* vars x => !!! FAILED REPR: RuntimeError("I'm a bad class!"*)""", """*tests*test_hunter.py:* return raise Exception(x)""", """* ... return value: None""", """* vars x => !!! FAILED REPR: RuntimeError("I'm a bad class!"*)""", ])
def test_trace_api_expansion(): # simple use with trace(function="foobar") as t: assert t.handler == When(Q(function="foobar"), CallPrinter) # "or" by expression with trace(module="foo", function="foobar") as t: assert t.handler == When(Q(module="foo", function="foobar"), CallPrinter) # pdb.set_trace with trace(function="foobar", action=Debugger) as t: assert str(t.handler) == str(When(Q(function="foobar"), Debugger)) # pdb.set_trace on any hits with trace(module="foo", function="foobar", action=Debugger) as t: assert str(t.handler) == str( When(Q(module="foo", function="foobar"), Debugger)) # pdb.set_trace when function is foobar, otherwise just print when module is foo with trace(Q(function="foobar", action=Debugger), module="foo") as t: assert str(t.handler) == str( When(And(When(Q(function="foobar"), Debugger), Q(module="foo")), CallPrinter)) # dumping variables from stack with trace(Q(function="foobar", action=VarsPrinter("foobar")), module="foo") as t: assert str(t.handler) == str( When( And( When(Q(function="foobar"), VarsPrinter("foobar")), Q(module="foo"), ), CallPrinter)) with trace(Q(function="foobar", action=VarsPrinter("foobar", "mumbojumbo")), module="foo") as t: assert str(t.handler) == str( When( And( When(Q(function="foobar"), VarsPrinter("foobar", "mumbojumbo")), Q(module="foo"), ), CallPrinter)) # multiple actions with trace(Q(function="foobar", actions=[VarsPrinter("foobar"), Debugger]), module="foo") as t: assert str(t.handler) == str( When( And( When(Q(function="foobar"), VarsPrinter("foobar"), Debugger), Q(module="foo"), ), CallPrinter))
def test_tracing_vars_expressions(LineMatcher): lines = StringIO() with hunter.trace(actions=[VarsPrinter('Foo.bar', 'vars(Foo)', 'len(range(2))', 'Foo.__dict__["bar"]', stream=lines)]): def main(): class Foo(object): bar = 1 main() print(lines.getvalue()) lm = LineMatcher(lines.getvalue().splitlines()) lm.fnmatch_lines_random([ '* [[]Foo.bar => 1[]]', '* [[]vars(Foo) => *[]]', '* [[]len(range(2)) => 2[]]', '* [[]Foo.__dict__[[]"bar"[]] => 1[]]', ])
def test_threading_support(LineMatcher): lines = StringIO() idents = set() names = set() started = threading.Event() def record(event): idents.add(event.threadid) names.add(event.threadname) return True with hunter.trace(record, actions=[ CodePrinter(stream=lines), VarsPrinter('a', stream=lines), CallPrinter(stream=lines) ], threading_support=True): def foo(a=1): started.set() print(a) def main(): foo() t = threading.Thread(target=foo) t.start() started.wait(10) main() lm = LineMatcher(lines.getvalue().splitlines()) assert idents - {t.ident} == {None} assert 'MainThread' in names assert any(name.startswith('Thread-') for name in names) lm.fnmatch_lines_random([ 'Thread-* *test_hunter.py:* call def foo(a=1):', 'Thread-* * vars a => 1', 'Thread-* *test_hunter.py:* call => foo(a=1)', 'Thread-* * vars a => 1', 'MainThread *test_hunter.py:* call def foo(a=1):', 'MainThread * vars a => 1', 'MainThread *test_hunter.py:* call => foo(a=1)', 'MainThread * vars a => 1', ])
def test_thread_filtering(LineMatcher, query): lines = StringIO() idents = set() names = set() started = threading.Event() def record(event): idents.add(event.threadid) names.add(event.threadname) return True with hunter.trace(~Q(**query), record, actions=[ CodePrinter(stream=lines), VarsPrinter('a', stream=lines), CallPrinter(stream=lines) ], threading_support=True): def foo(a=1): started.set() print(a) def main(): foo() t = threading.Thread(target=foo) t.start() started.wait(10) main() lm = LineMatcher(lines.getvalue().splitlines()) print(lines.getvalue()) assert None not in idents assert 'MainThread' not in names pprint(lm.lines) lm.fnmatch_lines_random([ 'Thread-* *test_hunter.py:* call def foo(a=1):', 'Thread-* * vars a => 1', 'Thread-* *test_hunter.py:* call => foo(a=1)', 'Thread-* * vars a => 1', ])
def test_debugger(LineMatcher): out = StringIO() calls = [] class FakePDB: def __init__(self, foobar=1): calls.append(foobar) def set_trace(self, frame): calls.append(frame.f_code.co_name) with hunter.trace(lambda event: event.locals.get("node") == "Foobar", module="test_hunter", function="foo", actions=[ CodePrinter, VarsPrinter("a", "node", "foo", "test_debugger", globals=True, stream=out), Debugger(klass=FakePDB, foobar=2) ]): def foo(): a = 1 node = "Foobar" node += "x" a += 2 return a foo() print(out.getvalue()) assert calls == [2, 'foo'] lm = LineMatcher(out.getvalue().splitlines()) pprint(lm.lines) lm.fnmatch_lines_random([ "* test_debugger => <function test_debugger at *", "* node => 'Foobar'", "* a => 1", ])
def test_debugger(LineMatcher): out = StringIO() calls = [] class FakePDB: def __init__(self, foobar=1): calls.append(foobar) def set_trace(self, frame): calls.append(frame.f_code.co_name) with hunter.trace(lambda event: event.locals.get('node') == 'Foobar', module='test_hunter', function='foo', actions=[ CodePrinter, VarsPrinter('a', 'node', 'foo', 'test_debugger', stream=out), Debugger(klass=FakePDB, foobar=2) ]): def foo(): a = 1 node = 'Foobar' node += 'x' a += 2 return a foo() print(out.getvalue()) assert calls == [2, 'foo'] lm = LineMatcher(out.getvalue().splitlines()) pprint(lm.lines) lm.fnmatch_lines_random([ "* [[]test_debugger => <function test_debugger at *[]]", "* [[]node => 'Foobar'[]]", "* [[]a => 1[]]", ])