def test_data_dependency_3(self): # Transitive explicit (full cover) dependencies def func() -> int: foo = 1 result = 1 + foo return result expected_instructions = [ # foo = 1 Instr("LOAD_CONST", arg=1), Instr("STORE_FAST", arg="foo"), # result = 1 + foo Instr("LOAD_CONST", arg=1), Instr("LOAD_FAST", arg="foo"), Instr("BINARY_ADD"), Instr("STORE_FAST", arg="result"), # return result Instr("LOAD_FAST", arg="result"), Instr("RETURN_VALUE"), ] dynamic_slice = slice_function_at_return(func.__code__) self.assertEqual(len(dynamic_slice.sliced_instructions), len(expected_instructions)) self.assertTrue( compare(dynamic_slice.sliced_instructions, expected_instructions))
def test_simple_control_dependency_2(self): # If condition evaluated to false, with two relevant variables (but no influence on result) def func() -> int: foo = 1 bar = 2 result = 3 if foo == bar: result = 1 return result init_basic_block = BasicBlock([ # result = 3 Instr("LOAD_CONST", arg=3), Instr("STORE_FAST", arg="result"), ]) return_basic_block = BasicBlock([ # return result Instr("LOAD_FAST", arg="result"), Instr("RETURN_VALUE") ]) expected_instructions = [] expected_instructions.extend(init_basic_block) expected_instructions.extend(return_basic_block) dynamic_slice = slice_function_at_return(func.__code__) self.assertEqual(len(dynamic_slice.sliced_instructions), len(expected_instructions)) self.assertTrue( compare(dynamic_slice.sliced_instructions, expected_instructions))
def test_simple_control_dependency_4(self): # If-elif-else with else branch true def func() -> int: foo = 1 bar = 2 if foo == bar: result = 1 elif foo > bar: result = 2 else: result = 3 return result return_block = BasicBlock([ # return result Instr("LOAD_FAST", arg="result"), Instr("RETURN_VALUE") ]) else_block = BasicBlock([ # result = 3 Instr("LOAD_CONST", arg=3), Instr("STORE_FAST", arg="result") ]) elif_cond = BasicBlock([ # elif foo == 1: Instr("LOAD_FAST", arg="foo"), Instr("LOAD_FAST", arg="bar"), Instr("COMPARE_OP", arg=Compare.GT), Instr("POP_JUMP_IF_FALSE", arg=else_block), ]) if_cond = BasicBlock([ # if foo == bar Instr("LOAD_FAST", arg="foo"), Instr("LOAD_FAST", arg="bar"), Instr("COMPARE_OP", arg=Compare.EQ), Instr("POP_JUMP_IF_FALSE", arg=elif_cond), ]) init_block = BasicBlock([ # foo = 1 Instr("LOAD_CONST", arg=1), Instr("STORE_FAST", arg="foo"), # bar = 2 Instr("LOAD_CONST", arg=2), Instr("STORE_FAST", arg="bar"), ]) expected_instructions = [] expected_instructions.extend(init_block) expected_instructions.extend(if_cond) expected_instructions.extend(elif_cond) expected_instructions.extend(else_block) expected_instructions.extend(return_block) dynamic_slice = slice_function_at_return(func.__code__) self.assertEqual(len(dynamic_slice.sliced_instructions), len(expected_instructions)) self.assertTrue( compare(dynamic_slice.sliced_instructions, expected_instructions))
def test_builtin_addresses(self): def func(): test_dict = {1: "one", 2: "two"} # noinspection PyListCreation test_list = [1, 2] test_list.append(3) result = test_dict.get(1) return result function_block = BasicBlock([ # test_dict = {1: "one", 2: "two"} Instr("LOAD_CONST", arg="one"), Instr("LOAD_CONST", arg="two"), Instr("LOAD_CONST", arg=(1, 2)), Instr("BUILD_CONST_KEY_MAP", arg=2), Instr("STORE_FAST", arg="test_dict"), # result = test_dict.get(1) Instr("LOAD_FAST", arg="test_dict"), Instr("LOAD_METHOD", arg="get"), Instr("LOAD_CONST", arg=1), Instr("CALL_METHOD", arg=1), Instr("STORE_FAST", arg="result"), # return result Instr("LOAD_FAST", arg="result"), Instr("RETURN_VALUE"), ]) expected_instructions = [] expected_instructions.extend(function_block) dynamic_slice = slice_function_at_return(func.__code__, test_name="test_builtin_addresses") self.assertEqual(len(dynamic_slice.sliced_instructions), len(expected_instructions)) self.assertTrue(compare(dynamic_slice.sliced_instructions, expected_instructions))
def test_nested_class(self): def func(): # STORE_DEREF, LOAD_CLOSURE, LOAD_CLASSDEREF x = [] class NestedClass: y = x class_attr = NestedClass.y result = class_attr return result freevar_x = FreeVar("x") cellvar_x = CellVar("x") function_block = BasicBlock([ # x = [] Instr("BUILD_LIST", arg=0), Instr("STORE_DEREF", arg=cellvar_x), # class NestedClass: Instr("LOAD_BUILD_CLASS"), Instr("LOAD_CLOSURE", arg=cellvar_x), Instr("BUILD_TUPLE", arg=1), Instr("LOAD_CONST", arg=dummy_code_object), Instr("LOAD_CONST", arg="NestedClass"), Instr("MAKE_FUNCTION", arg=8), Instr("LOAD_CONST", arg="NestedClass"), Instr("CALL_FUNCTION", arg=2), Instr("STORE_FAST", arg="NestedClass"), # class_attr = NestedClass.y Instr("LOAD_FAST", arg="NestedClass"), Instr("LOAD_ATTR", arg="y"), Instr("STORE_FAST", arg="class_attr"), # result = class_attr Instr("LOAD_FAST", arg="class_attr"), Instr("STORE_FAST", arg="result"), # return result Instr("LOAD_FAST", arg="result"), Instr("RETURN_VALUE"), ]) nested_class_block = BasicBlock([ # y = x Instr("LOAD_CLASSDEREF", arg=freevar_x), Instr("STORE_NAME", arg="y"), Instr("LOAD_CONST", arg=None), Instr("RETURN_VALUE"), ]) expected_instructions = [] expected_instructions.extend(function_block) expected_instructions.extend(nested_class_block) dynamic_slice = slice_function_at_return(func.__code__, test_name="test_nested_class") self.assertEqual(len(dynamic_slice.sliced_instructions), len(expected_instructions)) self.assertTrue(compare(dynamic_slice.sliced_instructions, expected_instructions))
def test_simple_loop(self): def func(): result = 0 for i in range(0, 3): result += i return result return_block = BasicBlock([ # return result Instr("LOAD_FAST", arg="result"), Instr("RETURN_VALUE") ]) loop_header = BasicBlock([ Instr("FOR_ITER", arg=return_block), ]) loop_block = BasicBlock([ # result += i Instr("STORE_FAST", arg="i"), Instr("LOAD_FAST", arg="result"), Instr("LOAD_FAST", arg="i"), Instr("INPLACE_ADD"), Instr("STORE_FAST", arg="result"), Instr("JUMP_ABSOLUTE", arg=loop_header), ]) loop_setup = BasicBlock([ # for i in range(0, 3): Instr("LOAD_GLOBAL", arg="range"), Instr("LOAD_CONST", arg=0), Instr("LOAD_CONST", arg=3), Instr("CALL_FUNCTION", arg=2), Instr("GET_ITER"), ]) init_block = BasicBlock([ Instr("LOAD_CONST", arg=0), Instr("STORE_FAST", arg="result"), ]) expected_instructions = [] expected_instructions.extend(init_block) expected_instructions.extend(loop_setup) expected_instructions.extend(loop_header) expected_instructions.extend(loop_block) expected_instructions.extend(return_block) dynamic_slice = slice_function_at_return(func.__code__, test_name="test_simple_loop") self.assertEqual(len(dynamic_slice.sliced_instructions), len(expected_instructions)) self.assertTrue(compare(dynamic_slice.sliced_instructions, expected_instructions))
def test_simple_control_dependency_1(self): # If condition evaluated to true, with relevant variable foo def func() -> int: foo = 1 result = 3 if foo == 1: result = 1 return result return_basic_block = BasicBlock([ # return result Instr("LOAD_FAST", arg="result"), Instr("RETURN_VALUE") ]) if_basic_block = BasicBlock([ # result = 1 Instr("LOAD_CONST", arg=1), Instr("STORE_FAST", arg="result"), ]) init_basic_block = BasicBlock([ # foo = 1 Instr("LOAD_CONST", arg=1), Instr("STORE_FAST", arg="foo"), # if foo == 1 Instr("LOAD_FAST", arg="foo"), Instr("LOAD_CONST", arg=1), Instr("COMPARE_OP", arg=Compare.EQ), Instr("POP_JUMP_IF_FALSE", arg=return_basic_block), ]) expected_instructions = [] expected_instructions.extend(init_basic_block) expected_instructions.extend(if_basic_block) expected_instructions.extend(return_basic_block) dynamic_slice = slice_function_at_return(func.__code__) self.assertEqual(len(dynamic_slice.sliced_instructions), len(expected_instructions)) self.assertTrue( compare(dynamic_slice.sliced_instructions, expected_instructions))
def test_with_extended_arg(self): def func(): p = [1, 2, 3, 4, 5, 6] # noinspection PyUnusedLocal unused = p q, r, *s, t = p # With extended argument result = q, r return result module_block = BasicBlock([ # p = [1, 2, 3, 4, 5, 6] Instr("LOAD_CONST", arg=1), Instr("LOAD_CONST", arg=2), Instr("LOAD_CONST", arg=3), Instr("LOAD_CONST", arg=4), Instr("LOAD_CONST", arg=5), Instr("LOAD_CONST", arg=6), Instr("BUILD_LIST", arg=6), Instr("STORE_FAST", arg="p"), # q, r, *s, t = p Instr("LOAD_FAST", arg="p"), # Instr("EXTENDED_ARG", arg=1), # EXTENDED_ARG can not be in a slice Instr("UNPACK_EX", arg=258), Instr("STORE_FAST", arg="q"), Instr("STORE_FAST", arg="r"), # result = q Instr("LOAD_FAST", arg="q"), Instr("LOAD_FAST", arg="r"), Instr("BUILD_TUPLE", arg=2), Instr("STORE_FAST", arg="result"), # return result Instr("LOAD_FAST", arg="result"), Instr("RETURN_VALUE"), ]) expected_instructions = [] expected_instructions.extend(module_block) dynamic_slice = slice_function_at_return(func.__code__, test_name="test_with_extended_arg") self.assertEqual(len(dynamic_slice.sliced_instructions), len(expected_instructions)) self.assertTrue(compare(dynamic_slice.sliced_instructions, expected_instructions))
def test_data_dependency_1(self): # Implicit data dependency at return, explicit (full cover) for result def func() -> int: result = 1 return result expected_instructions = [ # result = 1 Instr("LOAD_CONST", arg=1), Instr("STORE_FAST", arg="result"), # return result Instr("LOAD_FAST", arg="result"), Instr("RETURN_VALUE"), ] dynamic_slice = slice_function_at_return(func.__code__) self.assertEqual(len(dynamic_slice.sliced_instructions), len(expected_instructions)) self.assertTrue( compare(dynamic_slice.sliced_instructions, expected_instructions))
def test_mod_untraced_object(self): def func(): lst = [('foo', '3'), ('bar', '1'), ('foobar', '2')] lst.sort( ) # This is incorrectly excluded, since it is not known that the method modifies the list result = lst return result function_block = BasicBlock([ # lst = [('foo', '3'), ('bar', '1'), ('foobar', '2')] Instr("LOAD_CONST", arg=('foo', '3')), Instr("LOAD_CONST", arg=('bar', '1')), Instr("LOAD_CONST", arg=('foobar', '2')), Instr("BUILD_LIST", arg=3), Instr("STORE_FAST", arg="lst"), # lst.sort() # This is incorrectly excluded, since it is not known that the method modifies the list Instr("LOAD_FAST", arg="lst"), Instr("LOAD_METHOD", arg="sort"), Instr("CALL_METHOD", arg=0), Instr("POP_TOP"), # result = lst Instr("LOAD_FAST", arg="lst"), Instr("STORE_FAST", arg="result"), # return result Instr("LOAD_FAST", arg="result"), Instr("RETURN_VALUE"), ]) expected_instructions = [] expected_instructions.extend(function_block) dynamic_slice = slice_function_at_return( func.__code__, test_name="test_mod_untraced_object") self.assertEqual(len(dynamic_slice.sliced_instructions), len(expected_instructions)) self.assertTrue( compare(dynamic_slice.sliced_instructions, expected_instructions))
def test_data_dependency_composite(self): # Composite type dependencies, which are way to broad def func(): # noinspection PyListCreation foo_list = [1, 2, 3] # the only list operation which should be included foo_list.append( 4 ) # should no be included, and is not included (good and limitation at the same time) foo_list += [5] # should no be included, but is foo_list[2:3] = [0, 0] # should no be included, but is result = foo_list[0] # correctly included return result # correctly included expected_instructions = [ # foo_list = [1, 2, 3] Instr("LOAD_CONST", arg=1), Instr("LOAD_CONST", arg=2), Instr("LOAD_CONST", arg=3), Instr("BUILD_LIST", arg=3), Instr("STORE_FAST", arg="foo_list"), # result = foo_list[0] Instr("LOAD_FAST", arg="foo_list"), Instr("LOAD_CONST", arg=0), Instr("BINARY_SUBSCR"), Instr("STORE_FAST", arg="result"), # return result Instr("LOAD_FAST", arg="result"), Instr("RETURN_VALUE"), ] dynamic_slice = slice_function_at_return(func.__code__) self.assertEqual(len(dynamic_slice.sliced_instructions), len(expected_instructions)) self.assertTrue( compare(dynamic_slice.sliced_instructions, expected_instructions))
def test_lambda(self): def func(): x = lambda a: a + 10 result = x(1) return result function_block = BasicBlock([ # x = lambda a: a + 10 Instr("LOAD_CONST", arg=dummy_code_object), Instr("LOAD_CONST", arg="IntegrationTestLanguageFeatures.test_lambda.<locals>.func.<locals>.<lambda>"), Instr("MAKE_FUNCTION", arg=0), Instr("STORE_FAST", arg="x"), # result = x(1) Instr("LOAD_FAST", arg="x"), Instr("LOAD_CONST", arg=1), Instr("CALL_FUNCTION", arg=1), Instr("STORE_FAST", arg="result"), # return result Instr("LOAD_FAST", arg="result"), Instr("RETURN_VALUE"), ]) lambda_block = BasicBlock([ # lambda a: a + 10 Instr("LOAD_FAST", arg="a"), Instr("LOAD_CONST", arg=10), Instr("BINARY_ADD"), Instr("RETURN_VALUE"), ]) expected_instructions = [] expected_instructions.extend(function_block) expected_instructions.extend(lambda_block) dynamic_slice = slice_function_at_return(func.__code__, test_name="test_lambda") self.assertEqual(len(dynamic_slice.sliced_instructions), len(expected_instructions)) self.assertTrue(compare(dynamic_slice.sliced_instructions, expected_instructions))
def test_dunder_definition(self): def func(): class NestedClass: def __init__( self ): # Definition of dunder methods wrongly excluded, these are not explicitly loaded self.x = 1 result = NestedClass() return result function_block = BasicBlock([ # class NestedClass: Instr("LOAD_BUILD_CLASS"), Instr("LOAD_CONST", arg=dummy_code_object), Instr("LOAD_CONST", arg="NestedClass"), Instr("MAKE_FUNCTION", arg=0), Instr("LOAD_CONST", arg="NestedClass"), Instr("CALL_FUNCTION", arg=2), Instr("STORE_FAST", arg="NestedClass"), # result = NestedClass() Instr("LOAD_FAST", arg="NestedClass"), Instr("CALL_FUNCTION", arg=0), Instr("STORE_FAST", arg="result"), # return result Instr("LOAD_FAST", arg="result"), Instr("RETURN_VALUE"), ]) nested_class_block = BasicBlock([ # Definition of dunder methods are wrongly excluded, since these are not explicitly loaded # def __init__(self): Instr("LOAD_CONST", arg=dummy_code_object), Instr( "LOAD_CONST", arg= "IntegrationTestLanguageFeatures.test_object_modification_call.<locals>." "func.<locals>.NestedClass.__init__"), Instr("MAKE_FUNCTION", arg=0), Instr("STORE_NAME", arg="__init__"), Instr("LOAD_CONST", arg=None), Instr("RETURN_VALUE"), ]) init_block = BasicBlock([ # self.x = 1 Instr("LOAD_CONST", arg=1), Instr("LOAD_FAST", arg="self"), Instr("STORE_ATTR", arg="x"), Instr("LOAD_CONST", arg=None), Instr("RETURN_VALUE"), ]) expected_instructions = [] expected_instructions.extend(function_block) expected_instructions.extend(nested_class_block) expected_instructions.extend(init_block) dynamic_slice = slice_function_at_return( func.__code__, test_name="test_dunder_definition") self.assertEqual(len(dynamic_slice.sliced_instructions), len(expected_instructions)) self.assertTrue( compare(dynamic_slice.sliced_instructions, expected_instructions))
def test_exception(self): # Exception def func(): foo = 1 bar = 0 try: result = 0 / 0 except ZeroDivisionError: result = foo + bar return result self.assertEqual(func(), 1) dummy_block = BasicBlock([]) return_block = BasicBlock([ # return result Instr("LOAD_FAST", arg="result"), Instr("RETURN_VALUE") ]) try_block = BasicBlock([ # result = foo / bar <- did somehow effect slicing criterion... Instr("LOAD_FAST", arg="foo"), Instr("LOAD_FAST", arg="bar"), Instr("BINARY_TRUE_DIVIDE"), # except ZeroDivisionError: Instr("DUP_TOP"), Instr("LOAD_GLOBAL", arg="ZeroDivisionError"), Instr("COMPARE_OP", arg=Compare.EXC_MATCH), Instr("POP_JUMP_IF_FALSE", arg=dummy_block), ]) except_block = BasicBlock([ # result = foo + bar Instr("LOAD_FAST", arg="foo"), Instr("LOAD_FAST", arg="bar"), Instr("BINARY_ADD"), Instr("STORE_FAST", arg="result"), Instr("JUMP_FORWARD", arg=dummy_block), ]) function_block = BasicBlock([ # foo = 1 Instr("LOAD_CONST", arg=1), # <- excluded because no stack simulation Instr("STORE_FAST", arg="foo"), # bar = 0 Instr("LOAD_CONST", arg=0), # <- excluded because no stack simulation Instr("STORE_FAST", arg="bar"), # try: # Instr("SETUP_FINALLY", arg=try_block), ]) expected_instructions = [] expected_instructions.extend(return_block) expected_instructions.extend(except_block) expected_instructions.extend(function_block) expected_instructions.extend(try_block) dynamic_slice = slice_function_at_return(func.__code__) self.assertEqual(len(dynamic_slice.sliced_instructions), len(expected_instructions)) self.assertTrue( compare(dynamic_slice.sliced_instructions, expected_instructions))
def test_object_modification_call(self): def func(): class NestedClass: def __init__(self): self.x = 1 def inc_x(self): self.x = self.x + 1 ob = NestedClass() ob.inc_x() result = ob.x return result function_block = BasicBlock([ # class NestedClass: Instr("LOAD_BUILD_CLASS"), Instr("LOAD_CONST", arg=dummy_code_object), Instr("LOAD_CONST", arg="NestedClass"), Instr("MAKE_FUNCTION", arg=0), Instr("LOAD_CONST", arg="NestedClass"), Instr("CALL_FUNCTION", arg=2), Instr("STORE_FAST", arg="NestedClass"), # ob = NestedClass() Instr("LOAD_FAST", arg="NestedClass"), Instr("CALL_FUNCTION", arg=0), Instr("STORE_FAST", arg="ob"), # ob.inc_x() Instr("LOAD_FAST", arg="ob"), Instr("LOAD_METHOD", arg="inc_x"), Instr("CALL_METHOD", arg=0), # result = ob.x Instr("LOAD_FAST", arg="ob"), Instr("LOAD_ATTR", arg="x"), Instr("STORE_FAST", arg="result"), # return result Instr("LOAD_FAST", arg="result"), Instr("RETURN_VALUE"), ]) nested_class_block = BasicBlock([ # Definition of dunder methods are wrongly excluded, since these are not explicitly loaded # def __init__(self): # Instr("LOAD_CONST", arg=dummy_code_object), # Instr("LOAD_CONST", arg="IntegrationTestLanguageFeatures.test_object_modification_call.<locals>." # "func.<locals>.NestedClass.__init__"), # Instr("MAKE_FUNCTION", arg=0), # Instr("STORE_NAME", arg="__init__"), # def inc_x(self): Instr("LOAD_CONST", arg=dummy_code_object), Instr("LOAD_CONST", arg="IntegrationTestLanguageFeatures.test_object_modification_call.<locals>." "func.<locals>.NestedClass.inc_x"), Instr("MAKE_FUNCTION", arg=0), Instr("STORE_NAME", arg="inc_x"), Instr("LOAD_CONST", arg=None), Instr("RETURN_VALUE"), ]) init_block = BasicBlock([ # self.x = 1 Instr("LOAD_CONST", arg=1), Instr("LOAD_FAST", arg="self"), Instr("STORE_ATTR", arg="x"), Instr("LOAD_CONST", arg=None), Instr("RETURN_VALUE"), ]) inc_x_block = BasicBlock([ # self.x = self.x + 1 Instr("LOAD_FAST", arg="self"), Instr("LOAD_ATTR", arg="x"), Instr("LOAD_CONST", arg=1), Instr("BINARY_ADD"), Instr("LOAD_FAST", arg="self"), Instr("STORE_ATTR", arg="x"), # This "None return" is not included, because the return value is not used # Instr("LOAD_CONST", arg=None), # Instr("RETURN_VALUE"), ]) expected_instructions = [] expected_instructions.extend(function_block) expected_instructions.extend(nested_class_block) expected_instructions.extend(init_block) expected_instructions.extend(inc_x_block) dynamic_slice = slice_function_at_return(func.__code__, test_name="test_object_modification_call") self.assertEqual(len(dynamic_slice.sliced_instructions), len(expected_instructions)) self.assertTrue(compare(dynamic_slice.sliced_instructions, expected_instructions))
def test_nested_class_2(self): # Critical test to ensure that the attributes converted to variables # are taken from the correct scope. def func(): # STORE_DEREF, LOAD_CLOSURE, LOAD_CLASSDEREF x1 = [1] x2 = [2] class Bar: foo = x1 # included! class Foo: foo = x2 # NOT included y = x2 # included y = Foo.y # NOT included class_attr = Bar.foo class_attr2 = Bar.Foo.y result = class_attr + class_attr2 return result freevar_x1 = FreeVar("x1") cellvar_x1 = CellVar("x1") freevar_x2 = FreeVar("x2") cellvar_x2 = CellVar("x2") function_block = BasicBlock([ # x1 = [1] Instr("LOAD_CONST", arg=1), Instr("BUILD_LIST", arg=1), Instr("STORE_DEREF", arg=cellvar_x1), # x2 = [2] Instr("LOAD_CONST", arg=2), Instr("BUILD_LIST", arg=1), Instr("STORE_DEREF", arg=cellvar_x2), # class Bar: Instr("LOAD_BUILD_CLASS"), Instr("LOAD_CLOSURE", arg=cellvar_x1), Instr("LOAD_CLOSURE", arg=freevar_x2), Instr("BUILD_TUPLE", arg=2), Instr("LOAD_CONST", arg=dummy_code_object), Instr("LOAD_CONST", arg="Bar"), Instr("MAKE_FUNCTION", arg=8), Instr("LOAD_CONST", arg="Bar"), Instr("CALL_FUNCTION", arg=2), Instr("STORE_FAST", arg="Bar"), # class_attr = Bar.y Instr("LOAD_FAST", arg="Bar"), Instr("LOAD_ATTR", arg="foo"), Instr("STORE_FAST", arg="class_attr"), # class_attr2 = Bar.Foo.y Instr("LOAD_FAST", arg="Bar"), Instr("LOAD_ATTR", arg="Foo"), Instr("LOAD_ATTR", arg="y"), Instr("STORE_FAST", arg="class_attr2"), # result = class_attr + class_attr2 Instr("LOAD_FAST", arg="class_attr"), Instr("LOAD_FAST", arg="class_attr2"), Instr("BINARY_ADD"), Instr("STORE_FAST", arg="result"), # return result Instr("LOAD_FAST", arg="result"), Instr("RETURN_VALUE"), ]) bar_block = BasicBlock([ # class Foo: Instr("LOAD_BUILD_CLASS"), Instr("LOAD_CLOSURE", arg=cellvar_x2), Instr("BUILD_TUPLE", arg=1), Instr("LOAD_CONST", arg=dummy_code_object), Instr("LOAD_CONST", arg="Foo"), Instr("MAKE_FUNCTION", arg=8), Instr("LOAD_CONST", arg="Foo"), Instr("CALL_FUNCTION", arg=2), Instr("STORE_NAME", arg="Foo"), # foo = x1 Instr("LOAD_CLASSDEREF", arg=freevar_x1), Instr("STORE_NAME", arg="foo"), Instr("LOAD_CONST", arg=None), Instr("RETURN_VALUE"), ]) foo_block = BasicBlock([ # y = x2 Instr("LOAD_CLASSDEREF", arg=freevar_x2), Instr("STORE_NAME", arg="y"), Instr("LOAD_CONST", arg=None), Instr("RETURN_VALUE"), ]) expected_instructions = [] expected_instructions.extend(function_block) expected_instructions.extend(foo_block) expected_instructions.extend(bar_block) dynamic_slice = slice_function_at_return(func.__code__, test_name="test_nested_class_2") self.assertEqual(func(), [1, 2]) self.assertEqual(len(dynamic_slice.sliced_instructions), len(expected_instructions)) self.assertTrue(compare(dynamic_slice.sliced_instructions, expected_instructions))