Skip to content

Commit 1c41aa7

Browse files
[3.13] gh-119824: Print stack entry when user input is needed (GH-119882) (#120533)
Co-authored-by: Irit Katriel <[email protected]>
1 parent 6ee68fb commit 1c41aa7

File tree

3 files changed

+82
-19
lines changed

3 files changed

+82
-19
lines changed

Lib/pdb.py

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

614622
def displayhook(self, obj):
@@ -1403,16 +1411,24 @@ def do_clear(self, arg):
14031411
complete_cl = _complete_location
14041412

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

@@ -2067,10 +2083,22 @@ def complete_unalias(self, text, line, begidx, endidx):
20672083
# It is also consistent with the up/down commands (which are
20682084
# compatible with dbx and gdb: up moves towards 'main()'
20692085
# and down moves towards the most recent stack frame).
2070-
2071-
def print_stack_trace(self):
2086+
# * if count is None, prints the full stack
2087+
# * if count = 0, prints the current frame entry
2088+
# * if count < 0, prints -count least recent frame entries
2089+
# * if count > 0, prints count most recent frame entries
2090+
2091+
def print_stack_trace(self, count=None):
2092+
if count is None:
2093+
stack_to_print = self.stack
2094+
elif count == 0:
2095+
stack_to_print = [self.stack[self.curindex]]
2096+
elif count < 0:
2097+
stack_to_print = self.stack[:-count]
2098+
else:
2099+
stack_to_print = self.stack[-count:]
20722100
try:
2073-
for frame_lineno in self.stack:
2101+
for frame_lineno in stack_to_print:
20742102
self.print_stack_entry(frame_lineno)
20752103
except KeyboardInterrupt:
20762104
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
@@ -3165,6 +3198,7 @@ def test_pdbrc_basic(self):
31653198
stdout, stderr = self.run_pdb_script(script, 'q\n', pdbrc=pdbrc, remove_home=True)
31663199
self.assertNotIn("SyntaxError", stdout)
31673200
self.assertIn("a+8=9", stdout)
3201+
self.assertIn("-> b = 2", stdout)
31683202

31693203
def test_pdbrc_empty_line(self):
31703204
"""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)