Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit 0fc58c6

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

File tree

Expand file treeCollapse file tree

5 files changed

+119
-0
lines changed
Filter options
Expand file treeCollapse file tree

5 files changed

+119
-0
lines changed

‎Doc/library/pdb.rst

Copy file name to clipboardExpand all lines: Doc/library/pdb.rst
+15Lines 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

Copy file name to clipboardExpand all lines: Doc/whatsnew/3.12.rst
+8Lines 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

Copy file name to clipboardExpand all lines: Lib/pdb.py
+17Lines 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

Copy file name to clipboardExpand all lines: Lib/test/test_pdb.py
+78Lines 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
+1Lines 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 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.