Skip to content

Commit

Permalink
pythongh-119824: Print stack entry when user input is needed (python#…
Browse files Browse the repository at this point in the history
…119882)

Co-authored-by: Irit Katriel <[email protected]>
  • Loading branch information
2 people authored and noahbkim committed Jul 11, 2024
1 parent 0a69022 commit d5f6cf2
Show file tree
Hide file tree
Showing 4 changed files with 90 additions and 21 deletions.
10 changes: 8 additions & 2 deletions Doc/library/pdb.rst
Original file line number Diff line number Diff line change
Expand Up @@ -321,11 +321,17 @@ can be overridden by the local file.
argument must be an identifier, ``help exec`` must be entered to get help on
the ``!`` command.

.. pdbcommand:: w(here)
.. pdbcommand:: w(here) [count]

Print a stack trace, with the most recent frame at the bottom. An arrow (``>``)
Print a stack trace, with the most recent frame at the bottom. if *count*
is 0, print the current frame entry. If *count* is negative, print the least
recent - *count* frames. If *count* is positive, print the most recent
*count* frames. An arrow (``>``)
indicates the current frame, which determines the context of most commands.

.. versionchanged:: 3.14
*count* argument is added.

.. pdbcommand:: d(own) [count]

Move the current frame *count* (default one) levels down in the stack trace
Expand Down
50 changes: 39 additions & 11 deletions Lib/pdb.py
Original file line number Diff line number Diff line change
Expand Up @@ -603,10 +603,18 @@ def interaction(self, frame, tb_or_exc):
assert tb is not None, "main exception must have a traceback"
with self._hold_exceptions(_chained_exceptions):
self.setup(frame, tb)
# if we have more commands to process, do not show the stack entry
if not self.cmdqueue:
# We should print the stack entry if and only if the user input
# is expected, and we should print it right before the user input.
# If self.cmdqueue is not empty, we append a "w 0" command to the
# queue, which is equivalent to print_stack_entry
if self.cmdqueue:
self.cmdqueue.append('w 0')
else:
self.print_stack_entry(self.stack[self.curindex])
self._cmdloop()
# If "w 0" is not used, pop it out
if self.cmdqueue and self.cmdqueue[-1] == 'w 0':
self.cmdqueue.pop()
self.forget()

def displayhook(self, obj):
Expand Down Expand Up @@ -1401,16 +1409,24 @@ def do_clear(self, arg):
complete_cl = _complete_location

def do_where(self, arg):
"""w(here)
"""w(here) [count]
Print a stack trace, with the most recent frame at the bottom.
Print a stack trace. If count is not specified, print the full stack.
If count is 0, print the current frame entry. If count is positive,
print count entries from the most recent frame. If count is negative,
print -count entries from the least recent frame.
An arrow indicates the "current frame", which determines the
context of most commands. 'bt' is an alias for this command.
"""
if arg:
self._print_invalid_arg(arg)
return
self.print_stack_trace()
if not arg:
count = None
else:
try:
count = int(arg)
except ValueError:
self.error('Invalid count (%s)' % arg)
return
self.print_stack_trace(count)
do_w = do_where
do_bt = do_where

Expand Down Expand Up @@ -2065,10 +2081,22 @@ def complete_unalias(self, text, line, begidx, endidx):
# It is also consistent with the up/down commands (which are
# compatible with dbx and gdb: up moves towards 'main()'
# and down moves towards the most recent stack frame).

def print_stack_trace(self):
# * if count is None, prints the full stack
# * if count = 0, prints the current frame entry
# * if count < 0, prints -count least recent frame entries
# * if count > 0, prints count most recent frame entries

def print_stack_trace(self, count=None):
if count is None:
stack_to_print = self.stack
elif count == 0:
stack_to_print = [self.stack[self.curindex]]
elif count < 0:
stack_to_print = self.stack[:-count]
else:
stack_to_print = self.stack[-count:]
try:
for frame_lineno in self.stack:
for frame_lineno in stack_to_print:
self.print_stack_entry(frame_lineno)
except KeyboardInterrupt:
pass
Expand Down
50 changes: 42 additions & 8 deletions Lib/test/test_pdb.py
Original file line number Diff line number Diff line change
Expand Up @@ -781,52 +781,85 @@ def test_pdb_where_command():
... import pdb; pdb.Pdb(nosigint=True, readrc=False).set_trace()
>>> def f():
... g();
... g()
>>> def test_function():
... f()
>>> with PdbTestInput([ # doctest: +ELLIPSIS
... 'w',
... 'where',
... 'w 1',
... 'w invalid',
... 'u',
... 'w',
... 'w 0',
... 'w 100',
... 'w -100',
... 'continue',
... ]):
... test_function()
> <doctest test.test_pdb.test_pdb_where_command[0]>(2)g()
-> import pdb; pdb.Pdb(nosigint=True, readrc=False).set_trace()
(Pdb) w
...
<doctest test.test_pdb.test_pdb_where_command[3]>(8)<module>()
<doctest test.test_pdb.test_pdb_where_command[3]>(13)<module>()
-> test_function()
<doctest test.test_pdb.test_pdb_where_command[2]>(2)test_function()
-> f()
<doctest test.test_pdb.test_pdb_where_command[1]>(2)f()
-> g();
-> g()
> <doctest test.test_pdb.test_pdb_where_command[0]>(2)g()
-> import pdb; pdb.Pdb(nosigint=True, readrc=False).set_trace()
(Pdb) where
...
<doctest test.test_pdb.test_pdb_where_command[3]>(8)<module>()
<doctest test.test_pdb.test_pdb_where_command[3]>(13)<module>()
-> test_function()
<doctest test.test_pdb.test_pdb_where_command[2]>(2)test_function()
-> f()
<doctest test.test_pdb.test_pdb_where_command[1]>(2)f()
-> g();
-> g()
> <doctest test.test_pdb.test_pdb_where_command[0]>(2)g()
-> import pdb; pdb.Pdb(nosigint=True, readrc=False).set_trace()
(Pdb) w 1
> <doctest test.test_pdb.test_pdb_where_command[0]>(2)g()
-> import pdb; pdb.Pdb(nosigint=True, readrc=False).set_trace()
(Pdb) w invalid
*** Invalid count (invalid)
(Pdb) u
> <doctest test.test_pdb.test_pdb_where_command[1]>(2)f()
-> g();
-> g()
(Pdb) w
...
<doctest test.test_pdb.test_pdb_where_command[3]>(8)<module>()
<doctest test.test_pdb.test_pdb_where_command[3]>(13)<module>()
-> test_function()
<doctest test.test_pdb.test_pdb_where_command[2]>(2)test_function()
-> f()
> <doctest test.test_pdb.test_pdb_where_command[1]>(2)f()
-> g()
<doctest test.test_pdb.test_pdb_where_command[0]>(2)g()
-> import pdb; pdb.Pdb(nosigint=True, readrc=False).set_trace()
(Pdb) w 0
> <doctest test.test_pdb.test_pdb_where_command[1]>(2)f()
-> g()
(Pdb) w 100
...
<doctest test.test_pdb.test_pdb_where_command[3]>(13)<module>()
-> test_function()
<doctest test.test_pdb.test_pdb_where_command[2]>(2)test_function()
-> f()
> <doctest test.test_pdb.test_pdb_where_command[1]>(2)f()
-> g();
-> g()
<doctest test.test_pdb.test_pdb_where_command[0]>(2)g()
-> import pdb; pdb.Pdb(nosigint=True, readrc=False).set_trace()
(Pdb) w -100
...
<doctest test.test_pdb.test_pdb_where_command[3]>(13)<module>()
-> test_function()
<doctest test.test_pdb.test_pdb_where_command[2]>(2)test_function()
-> f()
> <doctest test.test_pdb.test_pdb_where_command[1]>(2)f()
-> g()
<doctest test.test_pdb.test_pdb_where_command[0]>(2)g()
-> import pdb; pdb.Pdb(nosigint=True, readrc=False).set_trace()
(Pdb) continue
Expand Down Expand Up @@ -3179,6 +3212,7 @@ def test_pdbrc_basic(self):
stdout, stderr = self.run_pdb_script(script, 'q\n', pdbrc=pdbrc, remove_home=True)
self.assertNotIn("SyntaxError", stdout)
self.assertIn("a+8=9", stdout)
self.assertIn("-> b = 2", stdout)

def test_pdbrc_empty_line(self):
"""Test that empty lines in .pdbrc are ignored."""
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Print stack entry in :mod:`pdb` when and only when user input is needed.

0 comments on commit d5f6cf2

Please sign in to comment.