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

gh-103693: Add convenience variable feature to pdb #103694

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 7 commits into from
May 3, 2023
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Next Next commit
Add convenience variable feature
  • Loading branch information
gaogaotiantian committed Apr 22, 2023
commit 7db9d37f9e2916af7b9b9954529da83cedc93eac
13 changes: 13 additions & 0 deletions 13 Doc/library/pdb.rst
Original file line number Diff line number Diff line change
Expand Up @@ -263,6 +263,19 @@ the commands; the input is split at the first ``;;`` pair, even if it is in the
middle of a quoted string. A workaround for strings with double semicolons
is to use implicit string concatenation ``';'';'`` or ``";"";"``.

To set temporary global variables, use *convenience variable*. A *convenience
iritkatriel marked this conversation as resolved.
Show resolved Hide resolved
variable* is a variable whose name starts with ``$``. For example, ``$foo = 1``
set a global variable ``$foo`` which you can use in the debugger session. The
gaogaotiantian marked this conversation as resolved.
Show resolved Hide resolved
*convenience variables* are cleared when the program resumes execution so it's
less likely to interfere with your program compared to using normal variables
iritkatriel marked this conversation as resolved.
Show resolved Hide resolved
like ``foo = 1``.

There are three preset *convenience variables*:

* ``$_frame``: the current frame you are debugging
* ``$_return``: the return value if the frame is returning
* ``$_exception``: the ``(exc_type, exc_value)`` tuple if the frame is raising an exception

iritkatriel marked this conversation as resolved.
Show resolved Hide resolved
.. index::
pair: .pdbrc; file
triple: debugger; configuration; file
Expand Down
21 changes: 21 additions & 0 deletions 21 Lib/pdb.py
Original file line number Diff line number Diff line change
Expand Up @@ -270,6 +270,8 @@ def forget(self):
self.lineno = None
self.stack = []
self.curindex = 0
if hasattr(self, 'curframe') and self.curframe:
self.curframe.f_globals.pop('__pdb_convenience_variables', None)
self.curframe = None
self.tb_lineno.clear()

Expand All @@ -288,6 +290,11 @@ def setup(self, f, tb):
# locals whenever the .f_locals accessor is called, so we
# cache it here to ensure that modifications are not overwritten.
self.curframe_locals = self.curframe.f_locals
self.set_convenience_variable('_frame', self.curframe)
if '__return__' in self.curframe_locals:
self.set_convenience_variable('_return', self.curframe_locals['__return__'])
if '__exception__' in self.curframe_locals:
self.set_convenience_variable('_exception', self.curframe_locals['__exception__'])
return self.execRcLines()

# Can be executed earlier than 'setup' if desired
Expand Down Expand Up @@ -394,6 +401,7 @@ def _cmdloop(self):
self.message('--KeyboardInterrupt--')

# Called before loop, handles display expressions
# Set up convenience variable containers
def preloop(self):
displaying = self.displaying.get(self.curframe)
if displaying:
Expand Down Expand Up @@ -477,6 +485,9 @@ def precmd(self, line):
next = line[marker+2:].lstrip()
self.cmdqueue.append(next)
line = line[:marker].rstrip()

# Replace all the convenience variables
iritkatriel marked this conversation as resolved.
Show resolved Hide resolved
line = re.sub(r'\$([a-zA-Z_][a-zA-Z0-9_]*)', r'__pdb_convenience_variables["\1"]', line)
return line

def onecmd(self, line):
Expand Down Expand Up @@ -527,6 +538,15 @@ def message(self, msg):
def error(self, msg):
print('***', msg, file=self.stdout)

# convenience variables

def set_convenience_variable(self, name, value):
if not hasattr(self, 'curframe') or not self.curframe:
return
if '__pdb_convenience_variables' not in self.curframe.f_globals:
self.curframe.f_globals['__pdb_convenience_variables'] = {}
self.curframe.f_globals['__pdb_convenience_variables'][name] = value

# Generic completion functions. Individual complete_foo methods can be
# assigned below to one of these functions.

Expand Down Expand Up @@ -1018,6 +1038,7 @@ def _select_frame(self, number):
self.curindex = number
self.curframe = self.stack[self.curindex][0]
self.curframe_locals = self.curframe.f_locals
self.set_convenience_variable('_frame', self.curframe)
self.print_stack_entry(self.stack[self.curindex])
self.lineno = None

Expand Down
78 changes: 78 additions & 0 deletions 78 Lib/test/test_pdb.py
Original file line number Diff line number Diff line change
Expand Up @@ -746,6 +746,84 @@ def test_pdb_where_command():
(Pdb) continue
"""

def test_convenience_variables():
"""Test convenience variables

>>> def util_function():
... import pdb; pdb.Pdb(nosigint=True, readrc=False).set_trace()
... try:
... raise Exception('test')
... except:
... pass
... return 1

>>> def test_function():
... util_function()

>>> with PdbTestInput([ # doctest: +ELLIPSIS, +NORMALIZE_WHITESPACE
... '$_frame.f_lineno', # Check frame convenience variable
... '$a = 10', # Set a convenience variable
... '$a', # Print its value
... 'p $a + 2', # Do some calculation
... 'u', # Switch frame
... '$_frame.f_lineno', # Make sure the frame changed
... '$a', # Make sure the value persists
... 'd', # Go back to the original frame
... 'next',
... '$a', # The value should be gone
... 'next',
... '$_exception[1]', # Check exception convenience variable
... 'next',
... '$_exception', # Exception should be gone
... 'return',
... '$_return', # Check return convenience variable
... 'continue',
... ]):
... test_function()
> <doctest test.test_pdb.test_convenience_variables[0]>(3)util_function()
-> try:
(Pdb) $_frame.f_lineno
3
(Pdb) $a = 10
(Pdb) $a
10
(Pdb) p $a + 2
12
(Pdb) u
> <doctest test.test_pdb.test_convenience_variables[1]>(2)test_function()
-> util_function()
(Pdb) $_frame.f_lineno
2
(Pdb) $a
10
(Pdb) d
> <doctest test.test_pdb.test_convenience_variables[0]>(3)util_function()
-> try:
(Pdb) next
> <doctest test.test_pdb.test_convenience_variables[0]>(4)util_function()
-> raise Exception('test')
(Pdb) $a
*** KeyError: 'a'
(Pdb) next
Exception: test
> <doctest test.test_pdb.test_convenience_variables[0]>(4)util_function()
-> raise Exception('test')
(Pdb) $_exception[1]
Exception('test')
(Pdb) next
> <doctest test.test_pdb.test_convenience_variables[0]>(5)util_function()
-> except:
(Pdb) $_exception
(<class 'Exception'>, Exception('test'))
(Pdb) return
--Return--
> <doctest test.test_pdb.test_convenience_variables[0]>(7)util_function()->1
-> return 1
(Pdb) $_return
1
(Pdb) continue
"""

def test_post_mortem():
"""Test post mortem traceback debugging.

Expand Down
Morty Proxy This is a proxified and sanitized view of the page, visit original site.