| import opcode |
| import re |
| import sys |
| import textwrap |
| import unittest |
| |
| from test.support import os_helper, verbose |
| from test.support.script_helper import assert_python_ok |
| |
| |
| Py_DEBUG = hasattr(sys, 'gettotalrefcount') |
| |
| @unittest.skipUnless(Py_DEBUG, "lltrace requires Py_DEBUG") |
| class TestLLTrace(unittest.TestCase): |
| |
| 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 __ltrace__ enabled (only possible in console), |
| # when the internal Python stack was negatively adjusted |
| with open(os_helper.TESTFN, 'w', encoding='utf-8') as fd: |
| self.addCleanup(os_helper.unlink, os_helper.TESTFN) |
| fd.write(textwrap.dedent("""\ |
| import code |
| |
| console = code.InteractiveConsole() |
| console.push('__ltrace__ = 1') |
| console.push('a = [1, 2, 3]') |
| console.push('a[0] = 1') |
| print('unreachable if bug exists') |
| """)) |
| |
| assert_python_ok(os_helper.TESTFN) |
| |
| 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 check_op(self, op, stdout, present): |
| op = opcode.opmap[op] |
| regex = re.compile(f': {op}($|, )', re.MULTILINE) |
| if present: |
| self.assertTrue(regex.search(stdout), |
| f'": {op}" not found in: {stdout}') |
| else: |
| self.assertFalse(regex.search(stdout), |
| f'": {op}" found in: {stdout}') |
| |
| def check_op_in(self, op, stdout): |
| self.check_op(op, stdout, True) |
| |
| def check_op_not_in(self, op, stdout): |
| self.check_op(op, stdout, False) |
| |
| 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() |
| __ltrace__ = 1 |
| trace_me() |
| del __ltrace__ |
| dont_trace_2() |
| """) |
| self.check_op_in("GET_ITER", stdout) |
| self.check_op_in("FOR_ITER", stdout) |
| self.check_op_in("UNARY_POSITIVE", stdout) |
| self.check_op_in("POP_TOP", stdout) |
| |
| # before: dont_trace_1() is not traced |
| self.check_op_not_in("BINARY_MULTIPLY", stdout) |
| |
| # after: dont_trace_2() is not traced |
| self.check_op_not_in("UNARY_NEGATIVE", stdout) |
| |
| |
| if __name__ == "__main__": |
| unittest.main() |