1 # Verify that gdb can pretty-print the various PyObject* types
3 # The code for testing gdb was adapted from similar work in Unladen Swallow's
4 # Lib/test/test_jit_gdb.py
12 from test
.test_support
import run_unittest
, findfile
15 gdb_version
, _
= subprocess
.Popen(["gdb", "--version"],
16 stdout
=subprocess
.PIPE
).communicate()
18 # This is what "no gdb" looks like. There may, however, be other
19 # errors that manifest this way too.
20 raise unittest
.SkipTest("Couldn't find gdb on the path")
21 gdb_version_number
= re
.search(r
"^GNU gdb [^\d]*(\d+)\.", gdb_version
)
22 if int(gdb_version_number
.group(1)) < 7:
23 raise unittest
.SkipTest("gdb versions before 7.0 didn't support python embedding"
24 " Saw:\n" + gdb_version
)
26 # Verify that "gdb" was built with the embedded python support enabled:
27 cmd
= "--eval-command=python import sys; print sys.version_info"
28 p
= subprocess
.Popen(["gdb", "--batch", cmd
],
29 stdout
=subprocess
.PIPE
)
30 gdbpy_version
, _
= p
.communicate()
31 if gdbpy_version
== '':
32 raise unittest
.SkipTest("gdb not built with embedded python support")
34 def gdb_has_frame_select():
35 # Does this build of gdb have gdb.Frame.select ?
36 cmd
= "--eval-command=python print(dir(gdb.Frame))"
37 p
= subprocess
.Popen(["gdb", "--batch", cmd
],
38 stdout
=subprocess
.PIPE
)
39 stdout
, _
= p
.communicate()
40 m
= re
.match(r
'.*\[(.*)\].*', stdout
)
42 raise unittest
.SkipTest("Unable to parse output from gdb.Frame.select test")
43 gdb_frame_dir
= m
.group(1).split(', ')
44 return "'select'" in gdb_frame_dir
46 HAS_PYUP_PYDOWN
= gdb_has_frame_select()
48 class DebuggerTests(unittest
.TestCase
):
50 """Test that the debugger can debug Python."""
52 def run_gdb(self
, *args
):
53 """Runs gdb with the command line given by *args.
55 Returns its stdout, stderr
57 out
, err
= subprocess
.Popen(
58 args
, stdout
=subprocess
.PIPE
, stderr
=subprocess
.PIPE
,
62 def get_stack_trace(self
, source
=None, script
=None,
63 breakpoint
='PyObject_Print',
64 cmds_after_breakpoint
=None,
67 Run 'python -c SOURCE' under gdb with a breakpoint.
69 Support injecting commands after the breakpoint is reached
71 Returns the stdout from gdb
73 cmds_after_breakpoint: if provided, a list of strings: gdb commands
75 # We use "set breakpoint pending yes" to avoid blocking with a:
76 # Function "foo" not defined.
77 # Make breakpoint pending on future shared library load? (y or [n])
78 # error, which typically happens python is dynamically linked (the
79 # breakpoints of interest are to be found in the shared library)
80 # When this happens, we still get:
81 # Function "PyObject_Print" not defined.
82 # emitted to stderr each time, alas.
84 # Initially I had "--eval-command=continue" here, but removed it to
85 # avoid repeated print breakpoints when traversing hierarchical data
88 # Generate a list of commands in gdb's language:
89 commands
= ['set breakpoint pending yes',
90 'break %s' % breakpoint
,
92 if cmds_after_breakpoint
:
93 commands
+= cmds_after_breakpoint
95 commands
+= ['backtrace']
99 # Use "commands" to generate the arguments with which to invoke "gdb":
100 args
= ["gdb", "--batch"]
101 args
+= ['--eval-command=%s' % cmd
for cmd
in commands
]
106 # -S suppresses the default 'import site'
110 args
+= ["-c", source
]
115 # print ' '.join(args)
117 # Use "args" to invoke gdb, capturing stdout, stderr:
118 out
, err
= self
.run_gdb(*args
)
120 # Ignore some noise on stderr due to the pending breakpoint:
121 err
= err
.replace('Function "%s" not defined.\n' % breakpoint
, '')
122 # Ignore some other noise on stderr (http://bugs.python.org/issue8600)
123 err
= err
.replace("warning: Unable to find libthread_db matching"
124 " inferior's thread library, thread debugging will"
125 " not be available.\n",
128 # Ensure no unexpected error messages:
129 self
.assertEquals(err
, '')
133 def get_gdb_repr(self
, source
,
134 cmds_after_breakpoint
=None,
136 # Given an input python source representation of data,
137 # run "python -c'print DATA'" under gdb with a breakpoint on
138 # PyObject_Print and scrape out gdb's representation of the "op"
139 # parameter, and verify that the gdb displays the same string
141 # For a nested structure, the first time we hit the breakpoint will
142 # give us the top-level structure
143 gdb_output
= self
.get_stack_trace(source
, breakpoint
='PyObject_Print',
144 cmds_after_breakpoint
=cmds_after_breakpoint
,
145 import_site
=import_site
)
146 # gdb can insert additional '\n' and space characters in various places
147 # in its output, depending on the width of the terminal it's connected
148 # to (using its "wrap_here" function)
149 m
= re
.match('.*#0\s+PyObject_Print\s+\(\s*op\=\s*(.*?),\s+fp=.*\).*',
150 gdb_output
, re
.DOTALL
)
152 self
.fail('Unexpected gdb output: %r\n%s' % (gdb_output
, gdb_output
))
153 return m
.group(1), gdb_output
155 def assertEndsWith(self
, actual
, exp_end
):
156 '''Ensure that the given "actual" string ends with "exp_end"'''
157 self
.assert_(actual
.endswith(exp_end
),
158 msg
='%r did not end with %r' % (actual
, exp_end
))
160 def assertMultilineMatches(self
, actual
, pattern
):
161 m
= re
.match(pattern
, actual
, re
.DOTALL
)
163 msg
='%r did not match %r' % (actual
, pattern
))
165 def get_sample_script(self
):
166 return findfile('gdb_sample.py')
168 class PrettyPrintTests(DebuggerTests
):
169 def test_getting_backtrace(self
):
170 gdb_output
= self
.get_stack_trace('print 42')
171 self
.assertTrue('PyObject_Print' in gdb_output
)
173 def assertGdbRepr(self
, val
, cmds_after_breakpoint
=None):
174 # Ensure that gdb's rendering of the value in a debugged process
175 # matches repr(value) in this process:
176 gdb_repr
, gdb_output
= self
.get_gdb_repr('print ' + repr(val
),
177 cmds_after_breakpoint
)
178 self
.assertEquals(gdb_repr
, repr(val
), gdb_output
)
181 'Verify the pretty-printing of various "int" values'
182 self
.assertGdbRepr(42)
183 self
.assertGdbRepr(0)
184 self
.assertGdbRepr(-7)
185 self
.assertGdbRepr(sys
.maxint
)
186 self
.assertGdbRepr(-sys
.maxint
)
189 'Verify the pretty-printing of various "long" values'
190 self
.assertGdbRepr(0L)
191 self
.assertGdbRepr(1000000000000L)
192 self
.assertGdbRepr(-1L)
193 self
.assertGdbRepr(-1000000000000000L)
195 def test_singletons(self
):
196 'Verify the pretty-printing of True, False and None'
197 self
.assertGdbRepr(True)
198 self
.assertGdbRepr(False)
199 self
.assertGdbRepr(None)
201 def test_dicts(self
):
202 'Verify the pretty-printing of dictionaries'
203 self
.assertGdbRepr({})
204 self
.assertGdbRepr({'foo': 'bar'})
205 self
.assertGdbRepr({'foo': 'bar', 'douglas':42})
207 def test_lists(self
):
208 'Verify the pretty-printing of lists'
209 self
.assertGdbRepr([])
210 self
.assertGdbRepr(range(5))
212 def test_strings(self
):
213 'Verify the pretty-printing of strings'
214 self
.assertGdbRepr('')
215 self
.assertGdbRepr('And now for something hopefully the same')
216 self
.assertGdbRepr('string with embedded NUL here \0 and then some more text')
217 self
.assertGdbRepr('this is byte 255:\xff and byte 128:\x80')
219 def test_tuples(self
):
220 'Verify the pretty-printing of tuples'
221 self
.assertGdbRepr(tuple())
222 self
.assertGdbRepr((1,))
223 self
.assertGdbRepr(('foo', 'bar', 'baz'))
225 def test_unicode(self
):
226 'Verify the pretty-printing of unicode values'
227 # Test the empty unicode string:
228 self
.assertGdbRepr(u
'')
230 self
.assertGdbRepr(u
'hello world')
232 # Test printing a single character:
233 # U+2620 SKULL AND CROSSBONES
234 self
.assertGdbRepr(u
'\u2620')
236 # Test printing a Japanese unicode string
237 # (I believe this reads "mojibake", using 3 characters from the CJK
238 # Unified Ideographs area, followed by U+3051 HIRAGANA LETTER KE)
239 self
.assertGdbRepr(u
'\u6587\u5b57\u5316\u3051')
241 # Test a character outside the BMP:
242 # U+1D121 MUSICAL SYMBOL C CLEF
244 # UTF-8: 0xF0 0x9D 0x84 0xA1
245 # UTF-16: 0xD834 0xDD21
246 # This will only work on wide-unicode builds:
247 self
.assertGdbRepr(u
"\U0001D121")
250 'Verify the pretty-printing of sets'
251 self
.assertGdbRepr(set())
252 self
.assertGdbRepr(set(['a', 'b']))
253 self
.assertGdbRepr(set([4, 5, 6]))
255 # Ensure that we handled sets containing the "dummy" key value,
256 # which happens on deletion:
257 gdb_repr
, gdb_output
= self
.get_gdb_repr('''s = set(['a','b'])
260 self
.assertEquals(gdb_repr
, "set(['b'])")
262 def test_frozensets(self
):
263 'Verify the pretty-printing of frozensets'
264 self
.assertGdbRepr(frozenset())
265 self
.assertGdbRepr(frozenset(['a', 'b']))
266 self
.assertGdbRepr(frozenset([4, 5, 6]))
268 def test_exceptions(self
):
269 # Test a RuntimeError
270 gdb_repr
, gdb_output
= self
.get_gdb_repr('''
272 raise RuntimeError("I am an error")
273 except RuntimeError, e:
276 self
.assertEquals(gdb_repr
,
277 "exceptions.RuntimeError('I am an error',)")
280 # Test division by zero:
281 gdb_repr
, gdb_output
= self
.get_gdb_repr('''
284 except ZeroDivisionError, e:
287 self
.assertEquals(gdb_repr
,
288 "exceptions.ZeroDivisionError('integer division or modulo by zero',)")
290 def test_classic_class(self
):
291 'Verify the pretty-printing of classic class instances'
292 gdb_repr
, gdb_output
= self
.get_gdb_repr('''
298 m
= re
.match(r
'<Foo\(an_int=42\) at remote 0x[0-9a-f]+>', gdb_repr
)
300 msg
='Unexpected classic-class rendering %r' % gdb_repr
)
302 def test_modern_class(self
):
303 'Verify the pretty-printing of new-style class instances'
304 gdb_repr
, gdb_output
= self
.get_gdb_repr('''
310 m
= re
.match(r
'<Foo\(an_int=42\) at remote 0x[0-9a-f]+>', gdb_repr
)
312 msg
='Unexpected new-style class rendering %r' % gdb_repr
)
314 def test_subclassing_list(self
):
315 'Verify the pretty-printing of an instance of a list subclass'
316 gdb_repr
, gdb_output
= self
.get_gdb_repr('''
323 m
= re
.match(r
'<Foo\(an_int=42\) at remote 0x[0-9a-f]+>', gdb_repr
)
325 msg
='Unexpected new-style class rendering %r' % gdb_repr
)
327 def test_subclassing_tuple(self
):
328 'Verify the pretty-printing of an instance of a tuple subclass'
329 # This should exercise the negative tp_dictoffset code in the
330 # new-style class support
331 gdb_repr
, gdb_output
= self
.get_gdb_repr('''
337 m
= re
.match(r
'<Foo\(an_int=42\) at remote 0x[0-9a-f]+>', gdb_repr
)
339 msg
='Unexpected new-style class rendering %r' % gdb_repr
)
341 def assertSane(self
, source
, corruption
, expvalue
=None, exptype
=None):
342 '''Run Python under gdb, corrupting variables in the inferior process
343 immediately before taking a backtrace.
345 Verify that the variable's representation is the expected failsafe
348 cmds_after_breakpoint
=[corruption
, 'backtrace']
350 cmds_after_breakpoint
=['backtrace']
352 gdb_repr
, gdb_output
= \
353 self
.get_gdb_repr(source
,
354 cmds_after_breakpoint
=cmds_after_breakpoint
)
357 if gdb_repr
== repr(expvalue
):
358 # gdb managed to print the value in spite of the corruption;
359 # this is good (see http://bugs.python.org/issue8330)
363 pattern
= '<' + exptype
+ ' at remote 0x[0-9a-f]+>'
365 # Match anything for the type name; 0xDEADBEEF could point to
366 # something arbitrary (see http://bugs.python.org/issue8330)
367 pattern
= '<.* at remote 0x[0-9a-f]+>'
369 m
= re
.match(pattern
, gdb_repr
)
371 self
.fail('Unexpected gdb representation: %r\n%s' % \
372 (gdb_repr
, gdb_output
))
374 def test_NULL_ptr(self
):
375 'Ensure that a NULL PyObject* is handled gracefully'
376 gdb_repr
, gdb_output
= (
377 self
.get_gdb_repr('print 42',
378 cmds_after_breakpoint
=['set variable op=0',
382 self
.assertEquals(gdb_repr
, '0x0')
384 def test_NULL_ob_type(self
):
385 'Ensure that a PyObject* with NULL ob_type is handled gracefully'
386 self
.assertSane('print 42',
389 def test_corrupt_ob_type(self
):
390 'Ensure that a PyObject* with a corrupt ob_type is handled gracefully'
391 self
.assertSane('print 42',
392 'set op->ob_type=0xDEADBEEF',
395 def test_corrupt_tp_flags(self
):
396 'Ensure that a PyObject* with a type with corrupt tp_flags is handled'
397 self
.assertSane('print 42',
398 'set op->ob_type->tp_flags=0x0',
401 def test_corrupt_tp_name(self
):
402 'Ensure that a PyObject* with a type with corrupt tp_name is handled'
403 self
.assertSane('print 42',
404 'set op->ob_type->tp_name=0xDEADBEEF',
407 def test_NULL_instance_dict(self
):
408 'Ensure that a PyInstanceObject with with a NULL in_dict is handled'
415 'set ((PyInstanceObject*)op)->in_dict = 0',
418 def test_builtins_help(self
):
419 'Ensure that the new-style class _Helper in site.py can be handled'
420 # (this was the issue causing tracebacks in
421 # http://bugs.python.org/issue8032#msg100537 )
423 gdb_repr
, gdb_output
= self
.get_gdb_repr('print __builtins__.help', import_site
=True)
424 m
= re
.match(r
'<_Helper at remote 0x[0-9a-f]+>', gdb_repr
)
426 msg
='Unexpected rendering %r' % gdb_repr
)
428 def test_selfreferential_list(self
):
429 '''Ensure that a reference loop involving a list doesn't lead proxyval
430 into an infinite loop:'''
431 gdb_repr
, gdb_output
= \
432 self
.get_gdb_repr("a = [3, 4, 5] ; a.append(a) ; print a")
434 self
.assertEquals(gdb_repr
, '[3, 4, 5, [...]]')
436 gdb_repr
, gdb_output
= \
437 self
.get_gdb_repr("a = [3, 4, 5] ; b = [a] ; a.append(b) ; print a")
439 self
.assertEquals(gdb_repr
, '[3, 4, 5, [[...]]]')
441 def test_selfreferential_dict(self
):
442 '''Ensure that a reference loop involving a dict doesn't lead proxyval
443 into an infinite loop:'''
444 gdb_repr
, gdb_output
= \
445 self
.get_gdb_repr("a = {} ; b = {'bar':a} ; a['foo'] = b ; print a")
447 self
.assertEquals(gdb_repr
, "{'foo': {'bar': {...}}}")
449 def test_selfreferential_old_style_instance(self
):
450 gdb_repr
, gdb_output
= \
451 self
.get_gdb_repr('''
457 self
.assertTrue(re
.match('<Foo\(an_attr=<\.\.\.>\) at remote 0x[0-9a-f]+>',
459 'Unexpected gdb representation: %r\n%s' % \
460 (gdb_repr
, gdb_output
))
462 def test_selfreferential_new_style_instance(self
):
463 gdb_repr
, gdb_output
= \
464 self
.get_gdb_repr('''
470 self
.assertTrue(re
.match('<Foo\(an_attr=<\.\.\.>\) at remote 0x[0-9a-f]+>',
472 'Unexpected gdb representation: %r\n%s' % \
473 (gdb_repr
, gdb_output
))
475 gdb_repr
, gdb_output
= \
476 self
.get_gdb_repr('''
484 self
.assertTrue(re
.match('<Foo\(an_attr=<Foo\(an_attr=<\.\.\.>\) at remote 0x[0-9a-f]+>\) at remote 0x[0-9a-f]+>',
486 'Unexpected gdb representation: %r\n%s' % \
487 (gdb_repr
, gdb_output
))
489 def test_truncation(self
):
490 'Verify that very long output is truncated'
491 gdb_repr
, gdb_output
= self
.get_gdb_repr('print range(1000)')
492 self
.assertEquals(gdb_repr
,
493 "[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, "
494 "14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, "
495 "27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, "
496 "40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, "
497 "53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, "
498 "66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, "
499 "79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, "
500 "92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, "
501 "104, 105, 106, 107, 108, 109, 110, 111, 112, 113, "
502 "114, 115, 116, 117, 118, 119, 120, 121, 122, 123, "
503 "124, 125, 126, 127, 128, 129, 130, 131, 132, 133, "
504 "134, 135, 136, 137, 138, 139, 140, 141, 142, 143, "
505 "144, 145, 146, 147, 148, 149, 150, 151, 152, 153, "
506 "154, 155, 156, 157, 158, 159, 160, 161, 162, 163, "
507 "164, 165, 166, 167, 168, 169, 170, 171, 172, 173, "
508 "174, 175, 176, 177, 178, 179, 180, 181, 182, 183, "
509 "184, 185, 186, 187, 188, 189, 190, 191, 192, 193, "
510 "194, 195, 196, 197, 198, 199, 200, 201, 202, 203, "
511 "204, 205, 206, 207, 208, 209, 210, 211, 212, 213, "
512 "214, 215, 216, 217, 218, 219, 220, 221, 222, 223, "
513 "224, 225, 226...(truncated)")
514 self
.assertEquals(len(gdb_repr
),
515 1024 + len('...(truncated)'))
517 def test_builtin_function(self
):
518 gdb_repr
, gdb_output
= self
.get_gdb_repr('print len')
519 self
.assertEquals(gdb_repr
, '<built-in function len>')
521 def test_builtin_method(self
):
522 gdb_repr
, gdb_output
= self
.get_gdb_repr('import sys; print sys.stdout.readlines')
523 self
.assertTrue(re
.match('<built-in method readlines of file object at remote 0x[0-9a-f]+>',
525 'Unexpected gdb representation: %r\n%s' % \
526 (gdb_repr
, gdb_output
))
528 def test_frames(self
):
529 gdb_output
= self
.get_stack_trace('''
534 print foo.__code__''',
535 breakpoint
='PyObject_Print',
536 cmds_after_breakpoint
=['print (PyFrameObject*)(((PyCodeObject*)op)->co_zombieframe)']
538 self
.assertTrue(re
.match(r
'.*\s+\$1 =\s+Frame 0x[0-9a-f]+, for file <string>, line 3, in foo \(\)\s+.*',
541 'Unexpected gdb representation: %r\n%s' % (gdb_output
, gdb_output
))
543 class PyListTests(DebuggerTests
):
544 def assertListing(self
, expected
, actual
):
545 self
.assertEndsWith(actual
, expected
)
547 def test_basic_command(self
):
548 'Verify that the "py-list" command works'
549 bt
= self
.get_stack_trace(script
=self
.get_sample_script(),
550 cmds_after_breakpoint
=['py-list'])
552 self
.assertListing(' 5 \n'
553 ' 6 def bar(a, b, c):\n'
556 ' 9 def baz(*args):\n'
559 ' 12 foo(1, 2, 3)\n',
562 def test_one_abs_arg(self
):
563 'Verify the "py-list" command with one absolute argument'
564 bt
= self
.get_stack_trace(script
=self
.get_sample_script(),
565 cmds_after_breakpoint
=['py-list 9'])
567 self
.assertListing(' 9 def baz(*args):\n'
570 ' 12 foo(1, 2, 3)\n',
573 def test_two_abs_args(self
):
574 'Verify the "py-list" command with two absolute arguments'
575 bt
= self
.get_stack_trace(script
=self
.get_sample_script(),
576 cmds_after_breakpoint
=['py-list 1,3'])
578 self
.assertListing(' 1 # Sample script for use by test_gdb.py\n'
580 ' 3 def foo(a, b, c):\n',
583 class StackNavigationTests(DebuggerTests
):
584 @unittest.skipUnless(HAS_PYUP_PYDOWN
, "test requires py-up/py-down commands")
585 def test_pyup_command(self
):
586 'Verify that the "py-up" command works'
587 bt
= self
.get_stack_trace(script
=self
.get_sample_script(),
588 cmds_after_breakpoint
=['py-up'])
589 self
.assertMultilineMatches(bt
,
591 #[0-9]+ Frame 0x[0-9a-f]+, for file .*gdb_sample.py, line 7, in bar \(a=1, b=2, c=3\)
595 @unittest.skipUnless(HAS_PYUP_PYDOWN
, "test requires py-up/py-down commands")
596 def test_down_at_bottom(self
):
597 'Verify handling of "py-down" at the bottom of the stack'
598 bt
= self
.get_stack_trace(script
=self
.get_sample_script(),
599 cmds_after_breakpoint
=['py-down'])
600 self
.assertEndsWith(bt
,
601 'Unable to find a newer python frame\n')
603 @unittest.skipUnless(HAS_PYUP_PYDOWN
, "test requires py-up/py-down commands")
604 def test_up_at_top(self
):
605 'Verify handling of "py-up" at the top of the stack'
606 bt
= self
.get_stack_trace(script
=self
.get_sample_script(),
607 cmds_after_breakpoint
=['py-up'] * 4)
608 self
.assertEndsWith(bt
,
609 'Unable to find an older python frame\n')
611 @unittest.skipUnless(HAS_PYUP_PYDOWN
, "test requires py-up/py-down commands")
612 def test_up_then_down(self
):
613 'Verify "py-up" followed by "py-down"'
614 bt
= self
.get_stack_trace(script
=self
.get_sample_script(),
615 cmds_after_breakpoint
=['py-up', 'py-down'])
616 self
.assertMultilineMatches(bt
,
618 #[0-9]+ Frame 0x[0-9a-f]+, for file .*gdb_sample.py, line 7, in bar \(a=1, b=2, c=3\)
620 #[0-9]+ Frame 0x[0-9a-f]+, for file .*gdb_sample.py, line 10, in baz \(args=\(1, 2, 3\)\)
624 class PyBtTests(DebuggerTests
):
625 def test_basic_command(self
):
626 'Verify that the "py-bt" command works'
627 bt
= self
.get_stack_trace(script
=self
.get_sample_script(),
628 cmds_after_breakpoint
=['py-bt'])
629 self
.assertMultilineMatches(bt
,
631 #[0-9]+ Frame 0x[0-9a-f]+, for file .*gdb_sample.py, line 7, in bar \(a=1, b=2, c=3\)
633 #[0-9]+ Frame 0x[0-9a-f]+, for file .*gdb_sample.py, line 4, in foo \(a=1, b=2, c=3\)
635 #[0-9]+ Frame 0x[0-9a-f]+, for file .*gdb_sample.py, line 12, in <module> \(\)
639 class PyPrintTests(DebuggerTests
):
640 def test_basic_command(self
):
641 'Verify that the "py-print" command works'
642 bt
= self
.get_stack_trace(script
=self
.get_sample_script(),
643 cmds_after_breakpoint
=['py-print args'])
644 self
.assertMultilineMatches(bt
,
645 r
".*\nlocal 'args' = \(1, 2, 3\)\n.*")
647 @unittest.skipUnless(HAS_PYUP_PYDOWN
, "test requires py-up/py-down commands")
648 def test_print_after_up(self
):
649 bt
= self
.get_stack_trace(script
=self
.get_sample_script(),
650 cmds_after_breakpoint
=['py-up', 'py-print c', 'py-print b', 'py-print a'])
651 self
.assertMultilineMatches(bt
,
652 r
".*\nlocal 'c' = 3\nlocal 'b' = 2\nlocal 'a' = 1\n.*")
654 def test_printing_global(self
):
655 bt
= self
.get_stack_trace(script
=self
.get_sample_script(),
656 cmds_after_breakpoint
=['py-print __name__'])
657 self
.assertMultilineMatches(bt
,
658 r
".*\nglobal '__name__' = '__main__'\n.*")
660 def test_printing_builtin(self
):
661 bt
= self
.get_stack_trace(script
=self
.get_sample_script(),
662 cmds_after_breakpoint
=['py-print len'])
663 self
.assertMultilineMatches(bt
,
664 r
".*\nbuiltin 'len' = <built-in function len>\n.*")
666 class PyLocalsTests(DebuggerTests
):
667 def test_basic_command(self
):
668 bt
= self
.get_stack_trace(script
=self
.get_sample_script(),
669 cmds_after_breakpoint
=['py-locals'])
670 self
.assertMultilineMatches(bt
,
671 r
".*\nargs = \(1, 2, 3\)\n.*")
673 @unittest.skipUnless(HAS_PYUP_PYDOWN
, "test requires py-up/py-down commands")
674 def test_locals_after_up(self
):
675 bt
= self
.get_stack_trace(script
=self
.get_sample_script(),
676 cmds_after_breakpoint
=['py-up', 'py-locals'])
677 self
.assertMultilineMatches(bt
,
678 r
".*\na = 1\nb = 2\nc = 3\n.*")
681 run_unittest(PrettyPrintTests
,
683 StackNavigationTests
,
689 if __name__
== "__main__":