1 import dis
2 import textwrap
3 import unittest
4
5 from test import support
6 from test.support import os_helper
7 from test.support.script_helper import assert_python_ok
8
9 def example():
10 x = []
11 for i in range(0):
12 x.append(i)
13 x = "this is"
14 y = "an example"
15 print(x, y)
16
17
18 @unittest.skipUnless(support.Py_DEBUG, "lltrace requires Py_DEBUG")
19 class ESC[4;38;5;81mTestLLTrace(ESC[4;38;5;149munittestESC[4;38;5;149m.ESC[4;38;5;149mTestCase):
20
21 def run_code(self, code):
22 code = textwrap.dedent(code).strip()
23 with open(os_helper.TESTFN, 'w', encoding='utf-8') as fd:
24 self.addCleanup(os_helper.unlink, os_helper.TESTFN)
25 fd.write(code)
26 status, stdout, stderr = assert_python_ok(os_helper.TESTFN)
27 self.assertEqual(stderr, b"")
28 self.assertEqual(status, 0)
29 result = stdout.decode('utf-8')
30 if support.verbose:
31 print("\n\n--- code ---")
32 print(code)
33 print("\n--- stdout ---")
34 print(result)
35 print()
36 return result
37
38 def test_lltrace(self):
39 stdout = self.run_code("""
40 def dont_trace_1():
41 a = "a"
42 a = 10 * a
43 def trace_me():
44 for i in range(3):
45 +i
46 def dont_trace_2():
47 x = 42
48 y = -x
49 dont_trace_1()
50 __lltrace__ = 1
51 trace_me()
52 del __lltrace__
53 dont_trace_2()
54 """)
55 self.assertIn("GET_ITER", stdout)
56 self.assertIn("FOR_ITER", stdout)
57 self.assertIn("CALL_INTRINSIC_1", stdout)
58 self.assertIn("POP_TOP", stdout)
59 self.assertNotIn("BINARY_OP", stdout)
60 self.assertNotIn("UNARY_NEGATIVE", stdout)
61
62 self.assertIn("'trace_me' in module '__main__'", stdout)
63 self.assertNotIn("dont_trace_1", stdout)
64 self.assertNotIn("'dont_trace_2' in module", stdout)
65
66 def test_lltrace_different_module(self):
67 stdout = self.run_code("""
68 from test import test_lltrace
69 test_lltrace.__lltrace__ = 1
70 test_lltrace.example()
71 """)
72 self.assertIn("'example' in module 'test.test_lltrace'", stdout)
73 self.assertIn('LOAD_CONST', stdout)
74 self.assertIn('FOR_ITER', stdout)
75 self.assertIn('this is an example', stdout)
76
77 # check that offsets match the output of dis.dis()
78 instr_map = {i.offset: i for i in dis.get_instructions(example, adaptive=True)}
79 for line in stdout.splitlines():
80 offset, colon, opname_oparg = line.partition(":")
81 if not colon:
82 continue
83 offset = int(offset)
84 opname_oparg = opname_oparg.split()
85 if len(opname_oparg) == 2:
86 opname, oparg = opname_oparg
87 oparg = int(oparg)
88 else:
89 (opname,) = opname_oparg
90 oparg = None
91 self.assertEqual(instr_map[offset].opname, opname)
92 self.assertEqual(instr_map[offset].arg, oparg)
93
94 def test_lltrace_does_not_crash_on_subscript_operator(self):
95 # If this test fails, it will reproduce a crash reported as
96 # bpo-34113. The crash happened at the command line console of
97 # debug Python builds with __lltrace__ enabled (only possible in console),
98 # when the internal Python stack was negatively adjusted
99 stdout = self.run_code("""
100 import code
101
102 console = code.InteractiveConsole()
103 console.push('__lltrace__ = 1')
104 console.push('a = [1, 2, 3]')
105 console.push('a[0] = 1')
106 print('unreachable if bug exists')
107 """)
108 self.assertIn("unreachable if bug exists", stdout)
109
110 if __name__ == "__main__":
111 unittest.main()