Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 0fc58c6

Browse files
gh-103693: Add convenience variable feature to pdb (#103694)
1 parent 524a7f7 commit 0fc58c6

File tree

5 files changed

+119
-0
lines changed

5 files changed

+119
-0
lines changed

Doc/library/pdb.rst

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -263,6 +263,21 @@ the commands; the input is split at the first ``;;`` pair, even if it is in the
263263
middle of a quoted string. A workaround for strings with double semicolons
264264
is to use implicit string concatenation ``';'';'`` or ``";"";"``.
265265

266+
To set a temporary global variable, use a *convenience variable*. A *convenience
267+
variable* is a variable whose name starts with ``$``. For example, ``$foo = 1``
268+
sets a global variable ``$foo`` which you can use in the debugger session. The
269+
*convenience variables* are cleared when the program resumes execution so it's
270+
less likely to interfere with your program compared to using normal variables
271+
like ``foo = 1``.
272+
273+
There are three preset *convenience variables*:
274+
275+
* ``$_frame``: the current frame you are debugging
276+
* ``$_retval``: the return value if the frame is returning
277+
* ``$_exception``: the exception if the frame is raising an exception
278+
279+
.. versionadded:: 3.12
280+
266281
.. index::
267282
pair: .pdbrc; file
268283
triple: debugger; configuration; file

Doc/whatsnew/3.12.rst

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -408,6 +408,14 @@ os.path
408408
* Add :func:`os.path.splitroot` to split a path into a triad
409409
``(drive, root, tail)``. (Contributed by Barney Gale in :gh:`101000`.)
410410

411+
pdb
412+
---
413+
414+
* Add convenience variables to hold values temporarily for debug session
415+
and provide quick access to values like the current frame or the return
416+
value.
417+
(Contributed by Tian Gao in :gh:`103693`.)
418+
411419
shutil
412420
------
413421

Lib/pdb.py

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -270,6 +270,8 @@ def forget(self):
270270
self.lineno = None
271271
self.stack = []
272272
self.curindex = 0
273+
if hasattr(self, 'curframe') and self.curframe:
274+
self.curframe.f_globals.pop('__pdb_convenience_variables', None)
273275
self.curframe = None
274276
self.tb_lineno.clear()
275277

@@ -288,6 +290,7 @@ def setup(self, f, tb):
288290
# locals whenever the .f_locals accessor is called, so we
289291
# cache it here to ensure that modifications are not overwritten.
290292
self.curframe_locals = self.curframe.f_locals
293+
self.set_convenience_variable(self.curframe, '_frame', self.curframe)
291294
return self.execRcLines()
292295

293296
# Can be executed earlier than 'setup' if desired
@@ -359,6 +362,7 @@ def user_return(self, frame, return_value):
359362
if self._wait_for_mainpyfile:
360363
return
361364
frame.f_locals['__return__'] = return_value
365+
self.set_convenience_variable(frame, '_retval', return_value)
362366
self.message('--Return--')
363367
self.interaction(frame, None)
364368

@@ -369,6 +373,7 @@ def user_exception(self, frame, exc_info):
369373
return
370374
exc_type, exc_value, exc_traceback = exc_info
371375
frame.f_locals['__exception__'] = exc_type, exc_value
376+
self.set_convenience_variable(frame, '_exception', exc_value)
372377

373378
# An 'Internal StopIteration' exception is an exception debug event
374379
# issued by the interpreter when handling a subgenerator run with
@@ -394,6 +399,7 @@ def _cmdloop(self):
394399
self.message('--KeyboardInterrupt--')
395400

396401
# Called before loop, handles display expressions
402+
# Set up convenience variable containers
397403
def preloop(self):
398404
displaying = self.displaying.get(self.curframe)
399405
if displaying:
@@ -477,6 +483,9 @@ def precmd(self, line):
477483
next = line[marker+2:].lstrip()
478484
self.cmdqueue.append(next)
479485
line = line[:marker].rstrip()
486+
487+
# Replace all the convenience variables
488+
line = re.sub(r'\$([a-zA-Z_][a-zA-Z0-9_]*)', r'__pdb_convenience_variables["\1"]', line)
480489
return line
481490

482491
def onecmd(self, line):
@@ -527,6 +536,13 @@ def message(self, msg):
527536
def error(self, msg):
528537
print('***', msg, file=self.stdout)
529538

539+
# convenience variables
540+
541+
def set_convenience_variable(self, frame, name, value):
542+
if '__pdb_convenience_variables' not in frame.f_globals:
543+
frame.f_globals['__pdb_convenience_variables'] = {}
544+
frame.f_globals['__pdb_convenience_variables'][name] = value
545+
530546
# Generic completion functions. Individual complete_foo methods can be
531547
# assigned below to one of these functions.
532548

@@ -1018,6 +1034,7 @@ def _select_frame(self, number):
10181034
self.curindex = number
10191035
self.curframe = self.stack[self.curindex][0]
10201036
self.curframe_locals = self.curframe.f_locals
1037+
self.set_convenience_variable(self.curframe, '_frame', self.curframe)
10211038
self.print_stack_entry(self.stack[self.curindex])
10221039
self.lineno = None
10231040

Lib/test/test_pdb.py

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -746,6 +746,84 @@ def test_pdb_where_command():
746746
(Pdb) continue
747747
"""
748748

749+
def test_convenience_variables():
750+
"""Test convenience variables
751+
752+
>>> def util_function():
753+
... import pdb; pdb.Pdb(nosigint=True, readrc=False).set_trace()
754+
... try:
755+
... raise Exception('test')
756+
... except:
757+
... pass
758+
... return 1
759+
760+
>>> def test_function():
761+
... util_function()
762+
763+
>>> with PdbTestInput([ # doctest: +ELLIPSIS, +NORMALIZE_WHITESPACE
764+
... '$_frame.f_lineno', # Check frame convenience variable
765+
... '$a = 10', # Set a convenience variable
766+
... '$a', # Print its value
767+
... 'p $a + 2', # Do some calculation
768+
... 'u', # Switch frame
769+
... '$_frame.f_lineno', # Make sure the frame changed
770+
... '$a', # Make sure the value persists
771+
... 'd', # Go back to the original frame
772+
... 'next',
773+
... '$a', # The value should be gone
774+
... 'next',
775+
... '$_exception', # Check exception convenience variable
776+
... 'next',
777+
... '$_exception', # Exception should be gone
778+
... 'return',
779+
... '$_retval', # Check return convenience variable
780+
... 'continue',
781+
... ]):
782+
... test_function()
783+
> <doctest test.test_pdb.test_convenience_variables[0]>(3)util_function()
784+
-> try:
785+
(Pdb) $_frame.f_lineno
786+
3
787+
(Pdb) $a = 10
788+
(Pdb) $a
789+
10
790+
(Pdb) p $a + 2
791+
12
792+
(Pdb) u
793+
> <doctest test.test_pdb.test_convenience_variables[1]>(2)test_function()
794+
-> util_function()
795+
(Pdb) $_frame.f_lineno
796+
2
797+
(Pdb) $a
798+
10
799+
(Pdb) d
800+
> <doctest test.test_pdb.test_convenience_variables[0]>(3)util_function()
801+
-> try:
802+
(Pdb) next
803+
> <doctest test.test_pdb.test_convenience_variables[0]>(4)util_function()
804+
-> raise Exception('test')
805+
(Pdb) $a
806+
*** KeyError: 'a'
807+
(Pdb) next
808+
Exception: test
809+
> <doctest test.test_pdb.test_convenience_variables[0]>(4)util_function()
810+
-> raise Exception('test')
811+
(Pdb) $_exception
812+
Exception('test')
813+
(Pdb) next
814+
> <doctest test.test_pdb.test_convenience_variables[0]>(5)util_function()
815+
-> except:
816+
(Pdb) $_exception
817+
*** KeyError: '_exception'
818+
(Pdb) return
819+
--Return--
820+
> <doctest test.test_pdb.test_convenience_variables[0]>(7)util_function()->1
821+
-> return 1
822+
(Pdb) $_retval
823+
1
824+
(Pdb) continue
825+
"""
826+
749827
def test_post_mortem():
750828
"""Test post mortem traceback debugging.
751829
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Add convenience variable feature to :mod:`pdb`

0 commit comments

Comments
 (0)