Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit d944d87

Browse files
gh-103464: Add checks for arguments of pdb commands (GH-103465)
1 parent ed86e14 commit d944d87

File tree

3 files changed

+90
-11
lines changed

3 files changed

+90
-11
lines changed

Lib/pdb.py

Lines changed: 56 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -644,7 +644,7 @@ def do_commands(self, arg):
644644
try:
645645
bnum = int(arg)
646646
except:
647-
self.error("Usage: commands [bnum]\n ...\n end")
647+
self._print_invalid_arg(arg)
648648
return
649649
try:
650650
self.get_bpbynumber(bnum)
@@ -941,14 +941,22 @@ def do_ignore(self, arg):
941941
condition evaluates to true.
942942
"""
943943
args = arg.split()
944-
try:
945-
count = int(args[1].strip())
946-
except:
944+
if not args:
945+
self.error('Breakpoint number expected')
946+
return
947+
if len(args) == 1:
947948
count = 0
949+
elif len(args) == 2:
950+
try:
951+
count = int(args[1])
952+
except ValueError:
953+
self._print_invalid_arg(arg)
954+
return
955+
else:
956+
self._print_invalid_arg(arg)
957+
return
948958
try:
949959
bp = self.get_bpbynumber(args[0].strip())
950-
except IndexError:
951-
self.error('Breakpoint number expected')
952960
except ValueError as err:
953961
self.error(err)
954962
else:
@@ -1025,6 +1033,9 @@ def do_where(self, arg):
10251033
An arrow indicates the "current frame", which determines the
10261034
context of most commands. 'bt' is an alias for this command.
10271035
"""
1036+
if arg:
1037+
self._print_invalid_arg(arg)
1038+
return
10281039
self.print_stack_trace()
10291040
do_w = do_where
10301041
do_bt = do_where
@@ -1112,6 +1123,9 @@ def do_step(self, arg):
11121123
(either in a function that is called or in the current
11131124
function).
11141125
"""
1126+
if arg:
1127+
self._print_invalid_arg(arg)
1128+
return
11151129
self.set_step()
11161130
return 1
11171131
do_s = do_step
@@ -1122,6 +1136,9 @@ def do_next(self, arg):
11221136
Continue execution until the next line in the current function
11231137
is reached or it returns.
11241138
"""
1139+
if arg:
1140+
self._print_invalid_arg(arg)
1141+
return
11251142
self.set_next(self.curframe)
11261143
return 1
11271144
do_n = do_next
@@ -1153,6 +1170,9 @@ def do_return(self, arg):
11531170
11541171
Continue execution until the current function returns.
11551172
"""
1173+
if arg:
1174+
self._print_invalid_arg(arg)
1175+
return
11561176
self.set_return(self.curframe)
11571177
return 1
11581178
do_r = do_return
@@ -1162,6 +1182,9 @@ def do_continue(self, arg):
11621182
11631183
Continue execution, only stop when a breakpoint is encountered.
11641184
"""
1185+
if arg:
1186+
self._print_invalid_arg(arg)
1187+
return
11651188
if not self.nosigint:
11661189
try:
11671190
Pdb._previous_sigint_handler = \
@@ -1256,6 +1279,9 @@ def do_args(self, arg):
12561279
12571280
Print the argument list of the current function.
12581281
"""
1282+
if arg:
1283+
self._print_invalid_arg(arg)
1284+
return
12591285
co = self.curframe.f_code
12601286
dict = self.curframe_locals
12611287
n = co.co_argcount + co.co_kwonlyargcount
@@ -1274,6 +1300,9 @@ def do_retval(self, arg):
12741300
12751301
Print the return value for the last return of a function.
12761302
"""
1303+
if arg:
1304+
self._print_invalid_arg(arg)
1305+
return
12771306
if '__return__' in self.curframe_locals:
12781307
self.message(repr(self.curframe_locals['__return__']))
12791308
else:
@@ -1390,6 +1419,9 @@ def do_longlist(self, arg):
13901419
13911420
List the whole source code for the current function or frame.
13921421
"""
1422+
if arg:
1423+
self._print_invalid_arg(arg)
1424+
return
13931425
filename = self.curframe.f_code.co_filename
13941426
breaklist = self.get_file_breaks(filename)
13951427
try:
@@ -1570,7 +1602,9 @@ def do_unalias(self, arg):
15701602
Delete the specified alias.
15711603
"""
15721604
args = arg.split()
1573-
if len(args) == 0: return
1605+
if len(args) == 0:
1606+
self._print_invalid_arg(arg)
1607+
return
15741608
if args[0] in self.aliases:
15751609
del self.aliases[args[0]]
15761610

@@ -1723,7 +1757,7 @@ def _getsourcelines(self, obj):
17231757
lineno = max(1, lineno)
17241758
return lines, lineno
17251759

1726-
def _help_message_from_doc(self, doc):
1760+
def _help_message_from_doc(self, doc, usage_only=False):
17271761
lines = [line.strip() for line in doc.rstrip().splitlines()]
17281762
if not lines:
17291763
return "No help message found."
@@ -1739,10 +1773,24 @@ def _help_message_from_doc(self, doc):
17391773
elif i < usage_end:
17401774
prefix = " "
17411775
else:
1776+
if usage_only:
1777+
break
17421778
prefix = ""
17431779
formatted.append(indent + prefix + line)
17441780
return "\n".join(formatted)
17451781

1782+
def _print_invalid_arg(self, arg):
1783+
"""Return the usage string for a function."""
1784+
1785+
self.error(f"Invalid argument: {arg}")
1786+
1787+
# Yes it's a bit hacky. Get the caller name, get the method based on
1788+
# that name, and get the docstring from that method.
1789+
# This should NOT fail if the caller is a method of this class.
1790+
doc = inspect.getdoc(getattr(self, sys._getframe(1).f_code.co_name))
1791+
if doc is not None:
1792+
self.message(self._help_message_from_doc(doc, usage_only=True))
1793+
17461794
# Collect all command help into docstring, if not run with -OO
17471795

17481796
if __doc__ is not None:

Lib/test/test_pdb.py

Lines changed: 33 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -334,9 +334,11 @@ def test_pdb_breakpoint_commands():
334334
(Pdb) commands 10
335335
*** cannot set commands: Breakpoint number 10 out of range
336336
(Pdb) commands a
337-
*** Usage: commands [bnum]
338-
...
339-
end
337+
*** Invalid argument: a
338+
Usage: (Pdb) commands [bpnumber]
339+
(com) ...
340+
(com) end
341+
(Pdb)
340342
(Pdb) commands 4
341343
*** cannot set commands: Breakpoint 4 already deleted
342344
(Pdb) break 6, undefined
@@ -908,6 +910,34 @@ def test_pdb_skip_modules():
908910
(Pdb) continue
909911
"""
910912

913+
def test_pdb_invalid_arg():
914+
"""This tests pdb commands that have invalid arguments
915+
916+
>>> def test_function():
917+
... import pdb; pdb.Pdb(nosigint=True, readrc=False).set_trace()
918+
... pass
919+
920+
>>> with PdbTestInput([
921+
... 'a = 3',
922+
... 'll 4',
923+
... 'step 1',
924+
... 'continue'
925+
... ]):
926+
... test_function()
927+
> <doctest test.test_pdb.test_pdb_invalid_arg[0]>(3)test_function()
928+
-> pass
929+
(Pdb) a = 3
930+
*** Invalid argument: = 3
931+
Usage: a(rgs)
932+
(Pdb) ll 4
933+
*** Invalid argument: 4
934+
Usage: ll | longlist
935+
(Pdb) step 1
936+
*** Invalid argument: 1
937+
Usage: s(tep)
938+
(Pdb) continue
939+
"""
940+
911941

912942
# Module for testing skipping of module that makes a callback
913943
mod = types.ModuleType('module_to_skip')
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Provide helpful usage messages when parsing incorrect :mod:`pdb` commands.

0 commit comments

Comments
 (0)