From 714f506e58504adae1c73a5608a1afefa93f166c Mon Sep 17 00:00:00 2001 From: Tian Gao Date: Tue, 11 Apr 2023 19:50:33 -0700 Subject: [PATCH 1/4] Add checks for command arguments --- Lib/pdb.py | 64 ++++++++++++++++++++++++++++++++++++++------ Lib/test/test_pdb.py | 36 ++++++++++++++++++++++--- 2 files changed, 89 insertions(+), 11 deletions(-) diff --git a/Lib/pdb.py b/Lib/pdb.py index a3553b345a8dd3..6a69e477199882 100755 --- a/Lib/pdb.py +++ b/Lib/pdb.py @@ -628,7 +628,7 @@ def do_commands(self, arg): try: bnum = int(arg) except: - self.error("Usage: commands [bnum]\n ...\n end") + self._print_invalid_arg(arg) return try: self.get_bpbynumber(bnum) @@ -925,14 +925,22 @@ def do_ignore(self, arg): 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: @@ -1009,6 +1017,9 @@ def do_where(self, arg): 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 @@ -1095,6 +1106,9 @@ def do_step(self, arg): (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 @@ -1105,6 +1119,9 @@ def do_next(self, arg): 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 @@ -1136,6 +1153,9 @@ def do_return(self, arg): 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 @@ -1145,6 +1165,9 @@ def do_continue(self, arg): 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 = \ @@ -1239,6 +1262,9 @@ def do_args(self, arg): 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 @@ -1257,6 +1283,9 @@ def do_retval(self, arg): 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: @@ -1373,6 +1402,9 @@ def do_longlist(self, arg): 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: @@ -1553,7 +1585,9 @@ def do_unalias(self, arg): 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]] @@ -1703,7 +1737,7 @@ def _getsourcelines(self, obj): 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." @@ -1719,10 +1753,24 @@ def _help_message_from_doc(self, doc): 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().f_back.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: diff --git a/Lib/test/test_pdb.py b/Lib/test/test_pdb.py index 9ad9a1c52ac102..113c89d00365af 100644 --- a/Lib/test/test_pdb.py +++ b/Lib/test/test_pdb.py @@ -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 @@ -830,6 +832,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() + > (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') From d31568a859399250cddd1450e6cc1321821c5078 Mon Sep 17 00:00:00 2001 From: "blurb-it[bot]" <43283697+blurb-it[bot]@users.noreply.github.com> Date: Wed, 12 Apr 2023 03:03:28 +0000 Subject: [PATCH 2/4] =?UTF-8?q?=F0=9F=93=9C=F0=9F=A4=96=20Added=20by=20blu?= =?UTF-8?q?rb=5Fit.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../next/Library/2023-04-12-03-03-27.gh-issue-103464.Oa_8IW.rst | 1 + 1 file changed, 1 insertion(+) create mode 100644 Misc/NEWS.d/next/Library/2023-04-12-03-03-27.gh-issue-103464.Oa_8IW.rst 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 index 00000000000000..31671a58834525 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2023-04-12-03-03-27.gh-issue-103464.Oa_8IW.rst @@ -0,0 +1 @@ +Add arguments checking for :mod:`pdb` commands From 1e541f877c0a1723b38be78b0513c307c752bda8 Mon Sep 17 00:00:00 2001 From: Tian Gao Date: Thu, 11 May 2023 08:58:37 +0800 Subject: [PATCH 3/4] Use argument in sys._getframe to avoid f_back Co-authored-by: Brandt Bucher --- Lib/pdb.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Lib/pdb.py b/Lib/pdb.py index 6a69e477199882..8c0ea42a9c914f 100755 --- a/Lib/pdb.py +++ b/Lib/pdb.py @@ -1767,7 +1767,7 @@ def _print_invalid_arg(self, 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().f_back.f_code.co_name)) + 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)) From 095150d0d036d281ef5533b290001a75ffbb5b9a Mon Sep 17 00:00:00 2001 From: Brandt Bucher Date: Mon, 22 May 2023 15:50:22 -0700 Subject: [PATCH 4/4] Update NEWS entry --- .../next/Library/2023-04-12-03-03-27.gh-issue-103464.Oa_8IW.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 index 31671a58834525..6afaeb6485ee40 100644 --- 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 @@ -1 +1 @@ -Add arguments checking for :mod:`pdb` commands +Provide helpful usage messages when parsing incorrect :mod:`pdb` commands.