gh-103464: Add checks for arguments of pdb commands (GH-103465)

This commit is contained in:
Tian Gao 2023-06-01 04:51:46 +08:00 committed by GitHub
parent ed86e14b16
commit d944d873b2
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 90 additions and 11 deletions

View file

@ -644,7 +644,7 @@ class Pdb(bdb.Bdb, cmd.Cmd):
try:
bnum = int(arg)
except:
self.error("Usage: commands [bnum]\n ...\n end")
self._print_invalid_arg(arg)
return
try:
self.get_bpbynumber(bnum)
@ -941,14 +941,22 @@ class Pdb(bdb.Bdb, cmd.Cmd):
condition evaluates to true.
"""
args = arg.split()
try:
count = int(args[1].strip())
except:
if not args:
self.error('Breakpoint number expected')
return
if len(args) == 1:
count = 0
elif len(args) == 2:
try:
count = int(args[1])
except ValueError:
self._print_invalid_arg(arg)
return
else:
self._print_invalid_arg(arg)
return
try:
bp = self.get_bpbynumber(args[0].strip())
except IndexError:
self.error('Breakpoint number expected')
except ValueError as err:
self.error(err)
else:
@ -1025,6 +1033,9 @@ class Pdb(bdb.Bdb, cmd.Cmd):
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()
do_w = do_where
do_bt = do_where
@ -1112,6 +1123,9 @@ class Pdb(bdb.Bdb, cmd.Cmd):
(either in a function that is called or in the current
function).
"""
if arg:
self._print_invalid_arg(arg)
return
self.set_step()
return 1
do_s = do_step
@ -1122,6 +1136,9 @@ class Pdb(bdb.Bdb, cmd.Cmd):
Continue execution until the next line in the current function
is reached or it returns.
"""
if arg:
self._print_invalid_arg(arg)
return
self.set_next(self.curframe)
return 1
do_n = do_next
@ -1153,6 +1170,9 @@ class Pdb(bdb.Bdb, cmd.Cmd):
Continue execution until the current function returns.
"""
if arg:
self._print_invalid_arg(arg)
return
self.set_return(self.curframe)
return 1
do_r = do_return
@ -1162,6 +1182,9 @@ class Pdb(bdb.Bdb, cmd.Cmd):
Continue execution, only stop when a breakpoint is encountered.
"""
if arg:
self._print_invalid_arg(arg)
return
if not self.nosigint:
try:
Pdb._previous_sigint_handler = \
@ -1256,6 +1279,9 @@ class Pdb(bdb.Bdb, cmd.Cmd):
Print the argument list of the current function.
"""
if arg:
self._print_invalid_arg(arg)
return
co = self.curframe.f_code
dict = self.curframe_locals
n = co.co_argcount + co.co_kwonlyargcount
@ -1274,6 +1300,9 @@ class Pdb(bdb.Bdb, cmd.Cmd):
Print the return value for the last return of a function.
"""
if arg:
self._print_invalid_arg(arg)
return
if '__return__' in self.curframe_locals:
self.message(repr(self.curframe_locals['__return__']))
else:
@ -1390,6 +1419,9 @@ class Pdb(bdb.Bdb, cmd.Cmd):
List the whole source code for the current function or frame.
"""
if arg:
self._print_invalid_arg(arg)
return
filename = self.curframe.f_code.co_filename
breaklist = self.get_file_breaks(filename)
try:
@ -1570,7 +1602,9 @@ class Pdb(bdb.Bdb, cmd.Cmd):
Delete the specified alias.
"""
args = arg.split()
if len(args) == 0: return
if len(args) == 0:
self._print_invalid_arg(arg)
return
if args[0] in self.aliases:
del self.aliases[args[0]]
@ -1723,7 +1757,7 @@ class Pdb(bdb.Bdb, cmd.Cmd):
lineno = max(1, lineno)
return lines, lineno
def _help_message_from_doc(self, doc):
def _help_message_from_doc(self, doc, usage_only=False):
lines = [line.strip() for line in doc.rstrip().splitlines()]
if not lines:
return "No help message found."
@ -1739,10 +1773,24 @@ class Pdb(bdb.Bdb, cmd.Cmd):
elif i < usage_end:
prefix = " "
else:
if usage_only:
break
prefix = ""
formatted.append(indent + prefix + line)
return "\n".join(formatted)
def _print_invalid_arg(self, arg):
"""Return the usage string for a function."""
self.error(f"Invalid argument: {arg}")
# Yes it's a bit hacky. Get the caller name, get the method based on
# that name, and get the docstring from that method.
# This should NOT fail if the caller is a method of this class.
doc = inspect.getdoc(getattr(self, sys._getframe(1).f_code.co_name))
if doc is not None:
self.message(self._help_message_from_doc(doc, usage_only=True))
# Collect all command help into docstring, if not run with -OO
if __doc__ is not None:

View file

@ -334,9 +334,11 @@ def test_pdb_breakpoint_commands():
(Pdb) commands 10
*** cannot set commands: Breakpoint number 10 out of range
(Pdb) commands a
*** Usage: commands [bnum]
...
end
*** Invalid argument: a
Usage: (Pdb) commands [bpnumber]
(com) ...
(com) end
(Pdb)
(Pdb) commands 4
*** cannot set commands: Breakpoint 4 already deleted
(Pdb) break 6, undefined
@ -908,6 +910,34 @@ def test_pdb_skip_modules():
(Pdb) continue
"""
def test_pdb_invalid_arg():
"""This tests pdb commands that have invalid arguments
>>> def test_function():
... import pdb; pdb.Pdb(nosigint=True, readrc=False).set_trace()
... pass
>>> with PdbTestInput([
... 'a = 3',
... 'll 4',
... 'step 1',
... 'continue'
... ]):
... test_function()
> <doctest test.test_pdb.test_pdb_invalid_arg[0]>(3)test_function()
-> pass
(Pdb) a = 3
*** Invalid argument: = 3
Usage: a(rgs)
(Pdb) ll 4
*** Invalid argument: 4
Usage: ll | longlist
(Pdb) step 1
*** Invalid argument: 1
Usage: s(tep)
(Pdb) continue
"""
# Module for testing skipping of module that makes a callback
mod = types.ModuleType('module_to_skip')

View file

@ -0,0 +1 @@
Provide helpful usage messages when parsing incorrect :mod:`pdb` commands.