def test_mutation(tracer): text = "AAAA" tracer.start() lower_text = text.lower( ) # Test this line does not emit a mutation of `text`. tracer.stop() assert tracer.events == [ InitialValue( lineno=-1, target=Symbol("text"), value='"AAAA"', repr='"AAAA"', ), Binding( lineno=9, target=Symbol("lower_text"), value='"aaaa"', repr='"aaaa"', sources={Symbol("text")}, ), ]
def test_decorator_multiple_times(trace, rpc_stub): @trace def func(b): a = b return a func(1) # Test that server wait does not block user code execution. trace._wait_for_termination() assert func(2) == 2 from cyberbrain import pprint pprint(trace.events) assert trace.events == [ InitialValue( lineno=21, target=Symbol("b"), value="1", ), Binding(lineno=21, target=Symbol("a"), value="1", sources={Symbol("b")}), Return(lineno=22, value="1", sources={Symbol("a")}), ] trace.server.stop()
def test_closure(tracer, mocked_responses): tracer.start() a = 1 # LOAD_CLASSDEREF class Foo: print(a) # LOAD_CLOSURE class Bar( Foo ): # LOAD_CLOSURE. If we remove super(Bar, self) it becomes LOAD_CONST def __init__(self): super(Bar, self).__init__() tracer.stop() assert tracer.events == [ Binding(lineno=28, target=Symbol("a"), value="1"), Binding( lineno=30, target=Symbol("Foo"), value='{"py/type":"test_cellvar.test_closure.<locals>.Foo"}', sources={Symbol("a")}, ), Binding( lineno=33, target=Symbol("Bar"), value='{"py/type":"test_cellvar.test_closure.<locals>.Bar"}', repr="<class 'test_cellvar.test_closure.<locals>.Bar'>", sources={Symbol("Foo")}, ), ]
def test_continue_in_finally(tracer, test_server): tracer.start() for x in range(2): try: pass finally: continue # 3.8: POP_FINALLY, >= 3.9: POP_EXCEPT, RERAISE tracer.stop() assert tracer.events == [ Binding(target=Symbol("x"), value="0", lineno=22), JumpBackToLoopStart(lineno=26, jump_target=16), Binding(target=Symbol("x"), value="1", lineno=22), JumpBackToLoopStart(lineno=26, jump_target=16), ] assert tracer.loops == [ Loop( start_offset=16, end_offset=get_value({"py38": 32, "default": 24}), start_lineno=22, ) ] test_server.assert_frame_sent("test_continue_in_finally")
def test_continue_in_finally_with_exception(tracer, test_server): """Tests POP_FINALLY when tos is an exception.""" tracer.start() # If the finally clause executes a return, break or continue statement, the saved # exception is discarded. for x in range(2): try: raise IndexError finally: continue # BREAK_LOOP (3.7) POP_FINALLY (3.8) tracer.stop() assert tracer.events == [ Binding(target=Symbol("x"), value="0", lineno=58), JumpBackToLoopStart(lineno=62, jump_target=16), Binding(target=Symbol("x"), value="1", lineno=58), JumpBackToLoopStart(lineno=62, jump_target=16), ] assert tracer.loops == [ Loop( start_offset=16, end_offset=get_value({"py38": 36, "default": 40}), start_lineno=58, ) ] test_server.assert_frame_sent("test_continue_in_finally_with_exception")
def test_continue_in_finally_with_exception(tracer, rpc_stub): """Tests POP_FINALLY when tos is an exception.""" tracer.start() # If the finally clause executes a return, break or continue statement, the saved # exception is discarded. for x in range(2): try: raise IndexError finally: continue # BREAK_LOOP (3.7) POP_FINALLY (3.8) tracer.stop() assert tracer.events == [ Binding(target=Symbol("x"), value=0, lineno=52), JumpBackToLoopStart(lineno=56, jump_target=16), Binding(target=Symbol("x"), value=1, lineno=52), JumpBackToLoopStart(lineno=56, jump_target=16), ] assert tracer.loops == [ Loop(start_offset=16, end_offset=36, start_lineno=52) ] assert_GetFrame(rpc_stub, "test_continue_in_finally_with_exception")
def test_telephone(trace, check_golden_file): @trace def test_telephone_inner(): args = argparse.Namespace( mutations=0.6, seed=2, text="The quick brown fox jumps over the lazy dog.") text = args.text random.seed(args.seed) alpha = "".join(sorted(string.ascii_letters + string.punctuation)) len_text = len(text) num_mutations = round(args.mutations * len_text) new_text = text for i in random.sample(range(len_text), num_mutations): new_char = random.choice(alpha.replace(new_text[i], "")) new_text = new_text[:i] + new_char + new_text[i + 1:] print(f'You said: "{text}"\nI heard : "{new_text}"') test_telephone_inner() # Events should exclude modules. assert_that(trace.events, not_(has_item(has_property("target", Symbol("random"))))) assert_that( trace.events, has_item( has_properties({ "target": Symbol("i"), "sources": {Symbol("len_text"), Symbol("num_mutations")}, })), )
def test_recursion_decorator(trace): @trace def fib(n): if n <= 1: return n else: return fib(n - 1) + fib(n - 2) print(fib(3)) assert trace.events == [ InitialValue( lineno=7, target=Symbol("n"), value="3", repr="3", ), InitialValue( lineno=10, target=Symbol("fib"), value= '{"repr": "<function test_recursion_decorator.<locals>.fib>"}', repr="<function test_recursion_decorator.<locals>.fib>", ), Return( lineno=10, value="2", repr="2", sources={Symbol("fib"), Symbol("n")}, ), ]
def test_unary_operations(tracer, mocked_responses): a = 1 tracer.start() b = +a # UNARY_POSITIVE b = -a # UNARY_NEGATIVE b = not a # UNARY_NOT b = ~a # UNARY_INVERT tracer.stop() assert tracer.events == [ InitialValue(target=Symbol("a"), value="1", lineno=-1), Binding(target=Symbol("b"), value="1", sources={Symbol("a")}, lineno=9), Binding(target=Symbol("b"), value="-1", sources={Symbol("a")}, lineno=10), Binding(target=Symbol("b"), value="false", sources={Symbol("a")}, lineno=11), Binding(target=Symbol("b"), value="-2", sources={Symbol("a")}, lineno=12), ]
def test_unary_operations(tracer, rpc_stub): a = 1 tracer.start() b = +a # UNARY_POSITIVE b = -a # UNARY_NEGATIVE b = not a # UNARY_NOT b = ~a # UNARY_INVERT tracer.stop() assert tracer.events == [ InitialValue(target=Symbol("a"), value=1, lineno=10), Binding(target=Symbol("b"), value=1, sources={Symbol("a")}, lineno=10), Binding(target=Symbol("b"), value=-1, sources={Symbol("a")}, lineno=11), Binding(target=Symbol("b"), value=False, sources={Symbol("a")}, lineno=12), Binding(target=Symbol("b"), value=-2, sources={Symbol("a")}, lineno=13), ] assert_GetFrame(rpc_stub, "test_unary_operations")
def test_ref_outside(trace, mocked_responses): @trace def test_ref_outside_inner(): a = f() test_ref_outside_inner() assert trace.events == [ InitialValue(lineno=-1, target=Symbol("f"), value='{"repr": "<function f>"}'), Binding(lineno=11, target=Symbol("a"), value="1", sources={Symbol("f")}), Return(lineno=11, value="null", sources=set()), ]
def test_existing_variable_emit_initial_value(tracer, test_server): x = "foo" tracer.start() y = x tracer.stop() assert tracer.events == [ InitialValue(target=Symbol("x"), value='"foo"', lineno=7), Binding(target=Symbol("y"), value='"foo"', sources={Symbol("x")}, lineno=7), ] test_server.assert_frame_sent("test_existing_variable_emit_initial_value")
def test_module(): assert tracer.events == [ InitialValue(target=Symbol("x"), value="1", lineno=-1), Deletion(target=Symbol("x"), lineno=8), InitialValue(target=Symbol("__annotations__"), value="{}", lineno=-1), Mutation( target=Symbol("__annotations__"), value='{"y":{"py/type":"builtins.int"}}', sources={Symbol("__annotations__") }, # `int` is a built-in so is excluded from sources. lineno=9, ), ]
def test_existing_variable_emit_initial_value(tracer, mocked_responses): x = "foo" tracer.start() y = x tracer.stop() assert tracer.events == [ InitialValue(target=Symbol("x"), value='"foo"', lineno=7), Binding(target=Symbol("y"), value='"foo"', sources={Symbol("x")}, lineno=7), ]
def test_import(tracer): tracer.start() import os # IMPORT_NAME from os import path # IMPORT_FROM, loads module from sys import settrace # IMPORT_FROM, loads function tracer.stop() print(os, path, settrace) # Prevent PyCharm from removing unused imports. assert_that( tracer.events, contains_exactly( all_of( instance_of(Binding), has_properties( { "target": Symbol("os"), "value": contains_string("<module 'os'"), "sources": set(), "lineno": 9, } ), ), all_of( instance_of(Binding), has_properties( { "target": Symbol("path"), "value": all_of( contains_string("<module"), contains_string("path") ), "sources": set(), "lineno": 10, } ), ), all_of( instance_of(Binding), has_properties( { "target": Symbol("settrace"), "value": '{"repr": "<built-in function settrace>"}', "sources": set(), "lineno": 11, } ), ), ), )
def test_ref_outside(trace, test_server): @trace def test_ref_outside_inner(): a = f() test_ref_outside_inner() assert trace.events == [ InitialValue(lineno=11, target=Symbol("f"), value='{"repr": "<function f>"}'), Binding(lineno=11, target=Symbol("a"), value="1", sources={Symbol("f")}), Return(lineno=11, value="null", sources=set()), ] test_server.assert_frame_sent("test_ref_outside_inner")
def test_attribute(tracer, rpc_stub): class A: pass a1 = A() a2 = A() a2.y = 1 tracer.start() a1.x = a2 # STORE_ATTR a1.x.y = 2 # LOAD_ATTR, STORE_ATTR del a1.x # DELETE_ATTR tracer.stop() assert tracer.events == [ InitialValue( lineno=14, target=Symbol("a2"), value='{"y": 1}', repr="<test_attribute.test_attribute.<locals>.A object>", ), InitialValue( lineno=14, target=Symbol("a1"), value="{}", repr="<test_attribute.test_attribute.<locals>.A object>", ), Mutation( lineno=14, target=Symbol("a1"), sources={Symbol("a2"), Symbol("a1")}, value='{"x": {"y": 1}}', repr="<test_attribute.test_attribute.<locals>.A object>", ), Mutation( lineno=15, target=Symbol("a1"), sources={Symbol("a1")}, value='{"x": {"y": 2}}', repr="<test_attribute.test_attribute.<locals>.A object>", ), Mutation( lineno=16, target=Symbol("a1"), sources={Symbol("a1")}, value="{}", repr="<test_attribute.test_attribute.<locals>.A object>", ), ] from utils import assert_GetFrame assert_GetFrame(rpc_stub, "test_attribute")
def test_existing_variable_emit_initial_value(tracer, rpc_stub): x = "foo" tracer.start() y = x tracer.stop() assert tracer.events == [ InitialValue(target=Symbol("x"), value="foo", lineno=8), Binding(target=Symbol("y"), value="foo", sources={Symbol("x")}, lineno=8), ] assert_GetFrame(rpc_stub, "test_existing_variable_emit_initial_value")
def test_module(rpc_stub): assert tracer.events == [ InitialValue(target=Symbol("x"), value=1, lineno=10), Deletion(target=Symbol("x"), lineno=10), InitialValue(target=Symbol("__annotations__"), value={}, lineno=11), Mutation( target=Symbol("__annotations__"), value={"y": int}, sources={Symbol("__annotations__") }, # `int` is a built-in so is excluded from sources. lineno=11, ), ] assert_GetFrame(rpc_stub, "test_outside_func")
def test_numpy(tracer, test_server): tracer.start() x = np.array([6, 7, 8]) tracer.stop() from utils import get_os_type, get_value int_type = get_value({ "windows": "int32", "linux": "int64", "mac": "int64" }) assert tracer.events == [ Binding( lineno=8, target=Symbol("x"), value=f'{{"dtype":"{int_type}","values":[6,7,8]}}', repr="array([6,7,8])", sources=set(), ) ] if get_os_type() != "windows": test_server.assert_frame_sent("test_numpy")
def test_ref_outside(trace, rpc_stub): @trace def test_ref_outside_inner(): a = f() test_ref_outside_inner() assert trace.events == [ InitialValue(lineno=11, target=Symbol("f"), value=f), Binding(lineno=11, target=Symbol("a"), value=1, sources={Symbol("f")}), Return(lineno=11, value=None, sources=set()), ] from utils import assert_GetFrame assert_GetFrame(rpc_stub, "test_ref_outside_inner")
def test_trace_decorated_function(trace): def my_decorator(f): def inner(*args): a = 1 f(*args) b = a return inner @my_decorator @trace def original_func(): a = [1, 2, 3] original_func() assert trace.events == [ Binding( lineno=16, target=Symbol("a"), value="[1,2,3]", repr="[1, 2, 3]", sources=set(), ), Return( lineno=16, value="null", repr="None", sources=set(), ), ]
def test_yield_from(trace, mocked_responses): def inner(): for i in range(2): yield i @trace def yield_from_function(): yield from inner() # CALL_FUNCTION, GET_YIELD_FROM_ITER, LOAD_CONST # The above line triggers two return events for sys.settrace for output in yield_from_function(): print(output) trace.stop() assert trace.events == [ InitialValue( lineno=75, target=Symbol("inner"), value='{"repr": "<function test_yield_from.<locals>.inner>"}', repr="<function test_yield_from.<locals>.inner>", ), Return( lineno=75, value="null", repr="None", sources=set(), ), ]
def test_while_jump_to_zero(trace): @trace def while_jump_to_zero(count): while count > 0: count -= 1 while_jump_to_zero(2) assert trace.events == [ InitialValue( lineno=103, target=Symbol("count"), value="2", repr="2", ), Binding( lineno=104, target=Symbol("count"), value="1", repr="1", sources={Symbol("count")}, ), JumpBackToLoopStart(lineno=104, jump_target=get_value({ "py37": 2, "default": 0 })), Binding( lineno=104, target=Symbol("count"), value="0", repr="0", sources={Symbol("count")}, ), JumpBackToLoopStart(lineno=104, jump_target=get_value({ "py37": 2, "default": 0 })), Return( lineno=104, value="null", repr="None", sources=set(), ), ]
def test_api_tracer(tracer, mocked_responses): tracer.start() a = 1 tracer.stop() assert tracer.events == [ Binding(lineno=6, target=Symbol("a"), value="1", sources=set()) ]
def test_attribute(tracer, mocked_responses): class A: pass a1 = A() a2 = A() a2.y = 1 tracer.start() a1.x = a2 # STORE_ATTR a1.x.y = 2 # LOAD_ATTR, STORE_ATTR del a1.x # DELETE_ATTR tracer.stop() assert tracer.events == [ InitialValue( lineno=-1, target=Symbol("a2"), value='{"y":1}', repr="<test_attribute.test_attribute.<locals>.A object>", ), InitialValue( lineno=-1, target=Symbol("a1"), value="{}", repr="<test_attribute.test_attribute.<locals>.A object>", ), Mutation( lineno=14, target=Symbol("a1"), sources={Symbol("a2"), Symbol("a1")}, value='{"x":{"y":1}}', repr="<test_attribute.test_attribute.<locals>.A object>", ), Mutation( lineno=15, target=Symbol("a1"), sources={Symbol("a1")}, value='{"x":{"y":2}}', repr="<test_attribute.test_attribute.<locals>.A object>", ), Mutation( lineno=16, target=Symbol("a1"), sources={Symbol("a1")}, value="{}", repr="<test_attribute.test_attribute.<locals>.A object>", ), ]
def test_pandas(tracer, mocked_responses): tracer.start() baby_data_set = [ ("Bob", 968), ("Jessica", 155), ("Mary", 77), ("John", 578), ("Mel", 973), ] df = pd.DataFrame(data=baby_data_set, columns=["Names", "Births"]) tracer.stop() from utils import get_value eol = get_value({"windows": r"\r\n", "linux": r"\n", "mac": "\\n"}) assert tracer.events == [ Binding( lineno=get_value({"py37": 13, "default": 8}), target=Symbol("baby_data_set"), value='[["Bob",968],["Jessica",155],["Mary",77],["John",578],["Mel",973]]', repr=( "[('Bob', 968), ('Jessica', 155), " "('Mary', 77), ('John', 578), ('Mel', 973)]" ), sources=set(), ), Binding( lineno=15, target=Symbol("df"), value=( f'{{"values":"Names,Births{eol}Bob,968{eol}Jessica,155{eol}' f'Mary,77{eol}John,578{eol}Mel,973{eol}"' ',"txt":true,"meta":{"dtypes":{"Names":"object","Births":"int64"},' '"index":"{\\"py/object\\":\\"pandas.core.indexes.range.RangeIndex\\"' ',\\"values\\":\\"[0,1,2,3,4]\\",\\"txt\\":true,\\"meta\\":{\\"dtype\\"' ':\\"int64\\",\\"name\\":null}}",' '"column_level_names":[null],"header":[0]}}' ), repr=( " Names Births\n0 Bob 968\n1 Jessica 155\n2" " Mary 77\n3 John 578\n4 Mel 973" ), sources={Symbol("baby_data_set")}, ), ]
def test_hello(tracer, rpc_stub): tracer.start() x = "hello world" # LOAD_CONST, STORE_FAST y = x # LOAD_FAST, STORE_FAST x, y = y, x # ROT_TWO, STORE_FAST tracer.stop() assert tracer.events == [ Binding(target=Symbol("x"), value="hello world", lineno=6), Binding(target=Symbol("y"), value="hello world", sources={Symbol("x")}, lineno=7), Binding(target=Symbol("x"), value="hello world", sources={Symbol("y")}, lineno=8), Binding(target=Symbol("y"), value="hello world", sources={Symbol("x")}, lineno=8), ] from utils import assert_GetFrame assert_GetFrame(rpc_stub, "test_hello")
def test_jump(tracer, rpc_stub): a = [] b = "b" c = "c" tracer.start() if a: # POP_JUMP_IF_FALSE pass # JUMP_FORWARD else: x = 1 if not a: # POP_JUMP_IF_TRUE x = 2 x = a != b != c # JUMP_IF_FALSE_OR_POP x = a == b or c # JUMP_IF_TRUE_OR_POP # TODO: Test JUMP_ABSOLUTE. This requires loop instructions to be Implemented. tracer.stop() assert tracer.events == [ InitialValue(target=Symbol("a"), value=[], lineno=12), Binding(target=Symbol("x"), value=1, lineno=15), Binding(target=Symbol("x"), value=2, lineno=18), InitialValue(target=Symbol("b"), value="b", lineno=20), InitialValue(target=Symbol("c"), value="c", lineno=20), # This is a known defect. We have no way to know `x` comes from `a`, because # the result of `a != b` only determines whether to jump to execute `b != c` # I think it's fine though. Binding( target=Symbol("x"), value=True, sources={Symbol("b"), Symbol("c")}, lineno=20, ), # Same defect here. Binding(target=Symbol("x"), value="c", sources={Symbol("c")}, lineno=21), ] assert_GetFrame(rpc_stub, "test_jump")
def test_decorator_multiple_times(trace, mocked_responses): @trace def func(b): a = b return a func(1) assert func(2) == 2 assert trace.events == [ InitialValue( lineno=21, target=Symbol("b"), value="1", ), Binding(lineno=21, target=Symbol("a"), value="1", sources={Symbol("b")}), Return(lineno=22, value="1", sources={Symbol("a")}), ]