import re import unittest from test.support import python_is_optimized from .util import run_gdb, setup_module, DebuggerTests, SAMPLE_SCRIPT def setUpModule(): setup_module() def gdb_has_frame_select(): # Does this build of gdb have gdb.Frame.select ? stdout, stderr = run_gdb("--eval-command=python print(dir(gdb.Frame))") m = re.match(r'.*\[(.*)\].*', stdout) if not m: raise unittest.SkipTest( f"Unable to parse output from gdb.Frame.select test\n" f"stdout={stdout!r}\n" f"stderr={stderr!r}\n") gdb_frame_dir = m.group(1).split(', ') return "'select'" in gdb_frame_dir HAS_PYUP_PYDOWN = gdb_has_frame_select() @unittest.skipIf(python_is_optimized(), "Python was compiled with optimizations") class PyListTests(DebuggerTests): def assertListing(self, expected, actual): self.assertEndsWith(actual, expected) def test_basic_command(self): 'Verify that the "py-list" command works' bt = self.get_stack_trace(script=SAMPLE_SCRIPT, cmds_after_breakpoint=['py-list']) self.assertListing(' 5 \n' ' 6 def bar(a, b, c):\n' ' 7 baz(a, b, c)\n' ' 8 \n' ' 9 def baz(*args):\n' ' >10 id(42)\n' ' 11 \n' ' 12 foo(1, 2, 3)\n', bt) def test_one_abs_arg(self): 'Verify the "py-list" command with one absolute argument' bt = self.get_stack_trace(script=SAMPLE_SCRIPT, cmds_after_breakpoint=['py-list 9']) self.assertListing(' 9 def baz(*args):\n' ' >10 id(42)\n' ' 11 \n' ' 12 foo(1, 2, 3)\n', bt) def test_two_abs_args(self): 'Verify the "py-list" command with two absolute arguments' bt = self.get_stack_trace(script=SAMPLE_SCRIPT, cmds_after_breakpoint=['py-list 1,3']) self.assertListing(' 1 # Sample script for use by test_gdb\n' ' 2 \n' ' 3 def foo(a, b, c):\n', bt) SAMPLE_WITH_C_CALL = """ from _testcapi import pyobject_vectorcall def foo(a, b, c): bar(a, b, c) def bar(a, b, c): pyobject_vectorcall(baz, (a, b, c), None) def baz(*args): id(42) foo(1, 2, 3) """ class StackNavigationTests(DebuggerTests): @unittest.skipUnless(HAS_PYUP_PYDOWN, "test requires py-up/py-down commands") @unittest.skipIf(python_is_optimized(), "Python was compiled with optimizations") def test_pyup_command(self): 'Verify that the "py-up" command works' bt = self.get_stack_trace(source=SAMPLE_WITH_C_CALL, cmds_after_breakpoint=['py-up', 'py-up']) self.assertMultilineMatches(bt, r'''^.* #[0-9]+ Frame 0x-?[0-9a-f]+, for file , line 12, in baz \(args=\(1, 2, 3\)\) #[0-9]+ $''') @unittest.skipUnless(HAS_PYUP_PYDOWN, "test requires py-up/py-down commands") def test_down_at_bottom(self): 'Verify handling of "py-down" at the bottom of the stack' bt = self.get_stack_trace(script=SAMPLE_SCRIPT, cmds_after_breakpoint=['py-down']) self.assertEndsWith(bt, 'Unable to find a newer python frame\n') @unittest.skipUnless(HAS_PYUP_PYDOWN, "test requires py-up/py-down commands") def test_up_at_top(self): 'Verify handling of "py-up" at the top of the stack' bt = self.get_stack_trace(script=SAMPLE_SCRIPT, cmds_after_breakpoint=['py-up'] * 5) self.assertEndsWith(bt, 'Unable to find an older python frame\n') @unittest.skipUnless(HAS_PYUP_PYDOWN, "test requires py-up/py-down commands") @unittest.skipIf(python_is_optimized(), "Python was compiled with optimizations") def test_up_then_down(self): 'Verify "py-up" followed by "py-down"' bt = self.get_stack_trace(source=SAMPLE_WITH_C_CALL, cmds_after_breakpoint=['py-up', 'py-up', 'py-down']) self.assertMultilineMatches(bt, r'''^.* #[0-9]+ Frame 0x-?[0-9a-f]+, for file , line 12, in baz \(args=\(1, 2, 3\)\) #[0-9]+ #[0-9]+ Frame 0x-?[0-9a-f]+, for file , line 12, in baz \(args=\(1, 2, 3\)\) $''') class PyPrintTests(DebuggerTests): @unittest.skipIf(python_is_optimized(), "Python was compiled with optimizations") def test_basic_command(self): 'Verify that the "py-print" command works' bt = self.get_stack_trace(source=SAMPLE_WITH_C_CALL, cmds_after_breakpoint=['py-up', 'py-print args']) self.assertMultilineMatches(bt, r".*\nlocal 'args' = \(1, 2, 3\)\n.*") @unittest.skipIf(python_is_optimized(), "Python was compiled with optimizations") @unittest.skipUnless(HAS_PYUP_PYDOWN, "test requires py-up/py-down commands") def test_print_after_up(self): bt = self.get_stack_trace(source=SAMPLE_WITH_C_CALL, cmds_after_breakpoint=['py-up', 'py-up', 'py-print c', 'py-print b', 'py-print a']) self.assertMultilineMatches(bt, r".*\nlocal 'c' = 3\nlocal 'b' = 2\nlocal 'a' = 1\n.*") @unittest.skipIf(python_is_optimized(), "Python was compiled with optimizations") def test_printing_global(self): bt = self.get_stack_trace(script=SAMPLE_SCRIPT, cmds_after_breakpoint=['py-up', 'py-print __name__']) self.assertMultilineMatches(bt, r".*\nglobal '__name__' = '__main__'\n.*") @unittest.skipIf(python_is_optimized(), "Python was compiled with optimizations") def test_printing_builtin(self): bt = self.get_stack_trace(script=SAMPLE_SCRIPT, cmds_after_breakpoint=['py-up', 'py-print len']) self.assertMultilineMatches(bt, r".*\nbuiltin 'len' = \n.*") class PyLocalsTests(DebuggerTests): @unittest.skipIf(python_is_optimized(), "Python was compiled with optimizations") def test_basic_command(self): bt = self.get_stack_trace(script=SAMPLE_SCRIPT, cmds_after_breakpoint=['py-up', 'py-locals']) self.assertMultilineMatches(bt, r".*\nargs = \(1, 2, 3\)\n.*") @unittest.skipUnless(HAS_PYUP_PYDOWN, "test requires py-up/py-down commands") @unittest.skipIf(python_is_optimized(), "Python was compiled with optimizations") def test_locals_after_up(self): bt = self.get_stack_trace(script=SAMPLE_SCRIPT, cmds_after_breakpoint=['py-up', 'py-up', 'py-locals']) self.assertMultilineMatches(bt, r'''^.* Locals for foo a = 1 b = 2 c = 3 Locals for .*$''')