| import dis | |
| import textwrap | |
| import unittest | |
| from test import support | |
| from test.support import os_helper | |
| from test.support.script_helper import assert_python_ok | |
| def example(): | |
| x = [] | |
| for i in range(0): | |
| x.append(i) | |
| x = "this is" | |
| y = "an example" | |
| print(x, y) | |
| @unittest.skipUnless(support.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 support.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("CALL_INTRINSIC_1", 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, adaptive=True)} | |
| 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() |