import dis import sys import textwrap import unittest from test.support import os_helper, verbose from test.support.script_helper import assert_python_ok def example(): x = [] for i in range(1): x.append(i) x = "this is" y = "an example" print(x, y) Py_DEBUG = hasattr(sys, 'gettotalrefcount') @unittest.skipUnless(Py_DEBUG, "lltrace requires Py_DEBUG") class TestLLTrace(unittest.TestCase): def run_code(self, code): code = textwrap.dedent(code).strip() with open(os_helper.TESTFN, 'w', encoding='utf-8') as fd: self.addCleanup(os_helper.unlink, os_helper.TESTFN) fd.write(code) status, stdout, stderr = assert_python_ok(os_helper.TESTFN) self.assertEqual(stderr, b"") self.assertEqual(status, 0) result = stdout.decode('utf-8') if verbose: print("\n\n--- code ---") print(code) print("\n--- stdout ---") print(result) print() return result def test_lltrace(self): stdout = self.run_code(""" def dont_trace_1(): a = "a" a = 10 * a def trace_me(): for i in range(3): +i def dont_trace_2(): x = 42 y = -x dont_trace_1() __lltrace__ = 1 trace_me() del __lltrace__ dont_trace_2() """) self.assertIn("GET_ITER", stdout) self.assertIn("FOR_ITER", stdout) self.assertIn("UNARY_POSITIVE", stdout) self.assertIn("POP_TOP", stdout) self.assertNotIn("BINARY_OP", stdout) self.assertNotIn("UNARY_NEGATIVE", stdout) self.assertIn("'trace_me' in module '__main__'", stdout) self.assertNotIn("dont_trace_1", stdout) self.assertNotIn("'dont_trace_2' in module", stdout) def test_lltrace_different_module(self): stdout = self.run_code(""" from test import test_lltrace test_lltrace.__lltrace__ = 1 test_lltrace.example() """) self.assertIn("'example' in module 'test.test_lltrace'", stdout) self.assertIn('LOAD_CONST', stdout) self.assertIn('FOR_ITER', stdout) self.assertIn('this is an example', stdout) # check that offsets match the output of dis.dis() instr_map = {i.offset: i for i in dis.get_instructions(example)} for line in stdout.splitlines(): offset, colon, opname_oparg = line.partition(":") if not colon: continue offset = int(offset) opname_oparg = opname_oparg.split() if len(opname_oparg) == 2: opname, oparg = opname_oparg oparg = int(oparg) else: (opname,) = opname_oparg oparg = None self.assertEqual(instr_map[offset].opname, opname) self.assertEqual(instr_map[offset].arg, oparg) def test_lltrace_does_not_crash_on_subscript_operator(self): # If this test fails, it will reproduce a crash reported as # bpo-34113. The crash happened at the command line console of # debug Python builds with __lltrace__ enabled (only possible in console), # when the internal Python stack was negatively adjusted stdout = self.run_code(""" import code console = code.InteractiveConsole() console.push('__lltrace__ = 1') console.push('a = [1, 2, 3]') console.push('a[0] = 1') print('unreachable if bug exists') """) self.assertIn("unreachable if bug exists", stdout) if __name__ == "__main__": unittest.main()