Skip to content

Commit ed60ab5

Browse files
gh-119824: Print stack entry when user input is needed (#119882)
Co-authored-by: Irit Katriel <[email protected]>
1 parent 7fadfd8 commit ed60ab5

File tree

4 files changed

+90
-21
lines changed

4 files changed

+90
-21
lines changed

Doc/library/pdb.rst

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -321,11 +321,17 @@ can be overridden by the local file.
321321
argument must be an identifier, ``help exec`` must be entered to get help on
322322
the ``!`` command.
323323

324-
.. pdbcommand:: w(here)
324+
.. pdbcommand:: w(here) [count]
325325

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

332+
.. versionchanged:: 3.14
333+
*count* argument is added.
334+
329335
.. pdbcommand:: d(own) [count]
330336

331337
Move the current frame *count* (default one) levels down in the stack trace

Lib/pdb.py

Lines changed: 39 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -603,10 +603,18 @@ def interaction(self, frame, tb_or_exc):
603603
assert tb is not None, "main exception must have a traceback"
604604
with self._hold_exceptions(_chained_exceptions):
605605
self.setup(frame, tb)
606-
# if we have more commands to process, do not show the stack entry
607-
if not self.cmdqueue:
606+
# We should print the stack entry if and only if the user input
607+
# is expected, and we should print it right before the user input.
608+
# If self.cmdqueue is not empty, we append a "w 0" command to the
609+
# queue, which is equivalent to print_stack_entry
610+
if self.cmdqueue:
611+
self.cmdqueue.append('w 0')
612+
else:
608613
self.print_stack_entry(self.stack[self.curindex])
609614
self._cmdloop()
615+
# If "w 0" is not used, pop it out
616+
if self.cmdqueue and self.cmdqueue[-1] == 'w 0':
617+
self.cmdqueue.pop()
610618
self.forget()
611619

612620
def displayhook(self, obj):
@@ -1401,16 +1409,24 @@ def do_clear(self, arg):
14011409
complete_cl = _complete_location
14021410

14031411
def do_where(self, arg):
1404-
"""w(here)
1412+
"""w(here) [count]
14051413
1406-
Print a stack trace, with the most recent frame at the bottom.
1414+
Print a stack trace. If count is not specified, print the full stack.
1415+
If count is 0, print the current frame entry. If count is positive,
1416+
print count entries from the most recent frame. If count is negative,
1417+
print -count entries from the least recent frame.
14071418
An arrow indicates the "current frame", which determines the
14081419
context of most commands. 'bt' is an alias for this command.
14091420
"""
1410-
if arg:
1411-
self._print_invalid_arg(arg)
1412-
return
1413-
self.print_stack_trace()
1421+
if not arg:
1422+
count = None
1423+
else:
1424+
try:
1425+
count = int(arg)
1426+
except ValueError:
1427+
self.error('Invalid count (%s)' % arg)
1428+
return
1429+
self.print_stack_trace(count)
14141430
do_w = do_where
14151431
do_bt = do_where
14161432

@@ -2065,10 +2081,22 @@ def complete_unalias(self, text, line, begidx, endidx):
20652081
# It is also consistent with the up/down commands (which are
20662082
# compatible with dbx and gdb: up moves towards 'main()'
20672083
# and down moves towards the most recent stack frame).
2068-
2069-
def print_stack_trace(self):
2084+
# * if count is None, prints the full stack
2085+
# * if count = 0, prints the current frame entry
2086+
# * if count < 0, prints -count least recent frame entries
2087+
# * if count > 0, prints count most recent frame entries
2088+
2089+
def print_stack_trace(self, count=None):
2090+
if count is None:
2091+
stack_to_print = self.stack
2092+
elif count == 0:
2093+
stack_to_print = [self.stack[self.curindex]]
2094+
elif count < 0:
2095+
stack_to_print = self.stack[:-count]
2096+
else:
2097+
stack_to_print = self.stack[-count:]
20702098
try:
2071-
for frame_lineno in self.stack:
2099+
for frame_lineno in stack_to_print:
20722100
self.print_stack_entry(frame_lineno)
20732101
except KeyboardInterrupt:
20742102
pass

Lib/test/test_pdb.py

Lines changed: 42 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -781,52 +781,85 @@ def test_pdb_where_command():
781781
... import pdb; pdb.Pdb(nosigint=True, readrc=False).set_trace()
782782
783783
>>> def f():
784-
... g();
784+
... g()
785785
786786
>>> def test_function():
787787
... f()
788788
789789
>>> with PdbTestInput([ # doctest: +ELLIPSIS
790790
... 'w',
791791
... 'where',
792+
... 'w 1',
793+
... 'w invalid',
792794
... 'u',
793795
... 'w',
796+
... 'w 0',
797+
... 'w 100',
798+
... 'w -100',
794799
... 'continue',
795800
... ]):
796801
... test_function()
797802
> <doctest test.test_pdb.test_pdb_where_command[0]>(2)g()
798803
-> import pdb; pdb.Pdb(nosigint=True, readrc=False).set_trace()
799804
(Pdb) w
800805
...
801-
<doctest test.test_pdb.test_pdb_where_command[3]>(8)<module>()
806+
<doctest test.test_pdb.test_pdb_where_command[3]>(13)<module>()
802807
-> test_function()
803808
<doctest test.test_pdb.test_pdb_where_command[2]>(2)test_function()
804809
-> f()
805810
<doctest test.test_pdb.test_pdb_where_command[1]>(2)f()
806-
-> g();
811+
-> g()
807812
> <doctest test.test_pdb.test_pdb_where_command[0]>(2)g()
808813
-> import pdb; pdb.Pdb(nosigint=True, readrc=False).set_trace()
809814
(Pdb) where
810815
...
811-
<doctest test.test_pdb.test_pdb_where_command[3]>(8)<module>()
816+
<doctest test.test_pdb.test_pdb_where_command[3]>(13)<module>()
812817
-> test_function()
813818
<doctest test.test_pdb.test_pdb_where_command[2]>(2)test_function()
814819
-> f()
815820
<doctest test.test_pdb.test_pdb_where_command[1]>(2)f()
816-
-> g();
821+
-> g()
817822
> <doctest test.test_pdb.test_pdb_where_command[0]>(2)g()
818823
-> import pdb; pdb.Pdb(nosigint=True, readrc=False).set_trace()
824+
(Pdb) w 1
825+
> <doctest test.test_pdb.test_pdb_where_command[0]>(2)g()
826+
-> import pdb; pdb.Pdb(nosigint=True, readrc=False).set_trace()
827+
(Pdb) w invalid
828+
*** Invalid count (invalid)
819829
(Pdb) u
820830
> <doctest test.test_pdb.test_pdb_where_command[1]>(2)f()
821-
-> g();
831+
-> g()
822832
(Pdb) w
823833
...
824-
<doctest test.test_pdb.test_pdb_where_command[3]>(8)<module>()
834+
<doctest test.test_pdb.test_pdb_where_command[3]>(13)<module>()
835+
-> test_function()
836+
<doctest test.test_pdb.test_pdb_where_command[2]>(2)test_function()
837+
-> f()
838+
> <doctest test.test_pdb.test_pdb_where_command[1]>(2)f()
839+
-> g()
840+
<doctest test.test_pdb.test_pdb_where_command[0]>(2)g()
841+
-> import pdb; pdb.Pdb(nosigint=True, readrc=False).set_trace()
842+
(Pdb) w 0
843+
> <doctest test.test_pdb.test_pdb_where_command[1]>(2)f()
844+
-> g()
845+
(Pdb) w 100
846+
...
847+
<doctest test.test_pdb.test_pdb_where_command[3]>(13)<module>()
825848
-> test_function()
826849
<doctest test.test_pdb.test_pdb_where_command[2]>(2)test_function()
827850
-> f()
828851
> <doctest test.test_pdb.test_pdb_where_command[1]>(2)f()
829-
-> g();
852+
-> g()
853+
<doctest test.test_pdb.test_pdb_where_command[0]>(2)g()
854+
-> import pdb; pdb.Pdb(nosigint=True, readrc=False).set_trace()
855+
(Pdb) w -100
856+
...
857+
<doctest test.test_pdb.test_pdb_where_command[3]>(13)<module>()
858+
-> test_function()
859+
<doctest test.test_pdb.test_pdb_where_command[2]>(2)test_function()
860+
-> f()
861+
> <doctest test.test_pdb.test_pdb_where_command[1]>(2)f()
862+
-> g()
830863
<doctest test.test_pdb.test_pdb_where_command[0]>(2)g()
831864
-> import pdb; pdb.Pdb(nosigint=True, readrc=False).set_trace()
832865
(Pdb) continue
@@ -3179,6 +3212,7 @@ def test_pdbrc_basic(self):
31793212
stdout, stderr = self.run_pdb_script(script, 'q\n', pdbrc=pdbrc, remove_home=True)
31803213
self.assertNotIn("SyntaxError", stdout)
31813214
self.assertIn("a+8=9", stdout)
3215+
self.assertIn("-> b = 2", stdout)
31823216

31833217
def test_pdbrc_empty_line(self):
31843218
"""Test that empty lines in .pdbrc are ignored."""
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Print stack entry in :mod:`pdb` when and only when user input is needed.

0 commit comments

Comments
 (0)