]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
gh-103464: Add checks for arguments of pdb commands (GH-103465)
authorTian Gao <gaogaotiantian@hotmail.com>
Wed, 31 May 2023 20:51:46 +0000 (04:51 +0800)
committerGitHub <noreply@github.com>
Wed, 31 May 2023 20:51:46 +0000 (20:51 +0000)
Lib/pdb.py
Lib/test/test_pdb.py
Misc/NEWS.d/next/Library/2023-04-12-03-03-27.gh-issue-103464.Oa_8IW.rst [new file with mode: 0644]

index 6b6feac1ddead14ab2158b3236f750e445c65158..1af511788749db46b1032deba40cc7db12f3c59f 100755 (executable)
@@ -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:
index 83c7cdff87fd34524cef5f9ee4133397ae75d7b0..277400e152476feb062cd3fe8798b421584eca8b 100644 (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')
diff --git a/Misc/NEWS.d/next/Library/2023-04-12-03-03-27.gh-issue-103464.Oa_8IW.rst b/Misc/NEWS.d/next/Library/2023-04-12-03-03-27.gh-issue-103464.Oa_8IW.rst
new file mode 100644 (file)
index 0000000..6afaeb6
--- /dev/null
@@ -0,0 +1 @@
+Provide helpful usage messages when parsing incorrect :mod:`pdb` commands.