]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
gh-103068: Check condition expression of breakpoints for pdb (#103069)
authorgaogaotiantian <gaogaotiantian@hotmail.com>
Wed, 29 Mar 2023 10:09:12 +0000 (03:09 -0700)
committerGitHub <noreply@github.com>
Wed, 29 Mar 2023 10:09:12 +0000 (12:09 +0200)
Co-authored-by: Ɓukasz Langa <lukasz@langa.pl>
Co-authored-by: Artem Mukhin <ortem00@gmail.com>
Lib/pdb.py
Lib/test/test_pdb.py
Misc/NEWS.d/next/Library/2023-03-28-05-14-59.gh-issue-103068.YQTmrA.rst [new file with mode: 0644]

index d402de1192f9e2b7349aa51dd70af27b97e1d78c..3a06cd00ad2bf1eef1fac332b6fe07a9caf2ef05 100755 (executable)
@@ -377,8 +377,7 @@ class Pdb(bdb.Bdb, cmd.Cmd):
         # stop when the debuggee is returning from such generators.
         prefix = 'Internal ' if (not exc_traceback
                                     and exc_type is StopIteration) else ''
-        self.message('%s%s' % (prefix,
-            traceback.format_exception_only(exc_type, exc_value)[-1].strip()))
+        self.message('%s%s' % (prefix, self._format_exc(exc_value)))
         self.interaction(frame, exc_traceback)
 
     # General interaction function
@@ -399,7 +398,7 @@ class Pdb(bdb.Bdb, cmd.Cmd):
         displaying = self.displaying.get(self.curframe)
         if displaying:
             for expr, oldvalue in displaying.items():
-                newvalue, _ = self._getval_except(expr)
+                newvalue = self._getval_except(expr)
                 # check for identity first; this prevents custom __eq__ to
                 # be called at every loop, and also prevents instances whose
                 # fields are changed to be displayed
@@ -702,6 +701,9 @@ class Pdb(bdb.Bdb, cmd.Cmd):
         if comma > 0:
             # parse stuff after comma: "condition"
             cond = arg[comma+1:].lstrip()
+            if err := self._compile_error_message(cond):
+                self.error('Invalid condition %s: %r' % (cond, err))
+                return
             arg = arg[:comma].rstrip()
         # parse stuff before comma: [filename:]lineno | function
         colon = arg.rfind(':')
@@ -887,6 +889,9 @@ class Pdb(bdb.Bdb, cmd.Cmd):
         args = arg.split(' ', 1)
         try:
             cond = args[1]
+            if err := self._compile_error_message(cond):
+                self.error('Invalid condition %s: %r' % (cond, err))
+                return
         except IndexError:
             cond = None
         try:
@@ -1246,16 +1251,15 @@ class Pdb(bdb.Bdb, cmd.Cmd):
     def _getval_except(self, arg, frame=None):
         try:
             if frame is None:
-                return eval(arg, self.curframe.f_globals, self.curframe_locals), None
+                return eval(arg, self.curframe.f_globals, self.curframe_locals)
             else:
-                return eval(arg, frame.f_globals, frame.f_locals), None
+                return eval(arg, frame.f_globals, frame.f_locals)
         except BaseException as exc:
-            err = traceback.format_exception_only(exc)[-1].strip()
-            return _rstr('** raised %s **' % err), exc
+            return _rstr('** raised %s **' % self._format_exc(exc))
 
     def _error_exc(self):
-        exc_info = sys.exc_info()[:2]
-        self.error(traceback.format_exception_only(*exc_info)[-1].strip())
+        exc = sys.exc_info()[1]
+        self.error(self._format_exc(exc))
 
     def _msg_val_func(self, arg, func):
         try:
@@ -1443,10 +1447,10 @@ class Pdb(bdb.Bdb, cmd.Cmd):
             else:
                 self.message('No expression is being displayed')
         else:
-            val, exc = self._getval_except(arg)
-            if isinstance(exc, SyntaxError):
-                self.message('Unable to display %s: %r' % (arg, val))
+            if err := self._compile_error_message(arg):
+                self.error('Unable to display %s: %r' % (arg, err))
             else:
+                val = self._getval_except(arg)
                 self.displaying.setdefault(self.curframe, {})[arg] = val
                 self.message('display %s: %r' % (arg, val))
 
@@ -1647,6 +1651,16 @@ class Pdb(bdb.Bdb, cmd.Cmd):
 
         self.run(target.code)
 
+    def _format_exc(self, exc: BaseException):
+        return traceback.format_exception_only(exc)[-1].strip()
+
+    def _compile_error_message(self, expr):
+        """Return the error message as string if compiling `expr` fails."""
+        try:
+            compile(expr, "<stdin>", "eval")
+        except SyntaxError as exc:
+            return _rstr(self._format_exc(exc))
+        return ""
 
 # Collect all command help into docstring, if not run with -OO
 
index ae9c5d73e2daa7194fb32372cfb2f7502434d446..de2bab46495729df2a0d75120b323229bade1045 100644 (file)
@@ -240,9 +240,11 @@ def test_pdb_breakpoint_commands():
 
     >>> with PdbTestInput([  # doctest: +NORMALIZE_WHITESPACE
     ...     'break 3',
+    ...     'break 4, +',
     ...     'disable 1',
     ...     'ignore 1 10',
     ...     'condition 1 1 < 2',
+    ...     'condition 1 1 <',
     ...     'break 4',
     ...     'break 4',
     ...     'break',
@@ -264,6 +266,8 @@ def test_pdb_breakpoint_commands():
     ...     'commands 10',  # out of range
     ...     'commands a',   # display help
     ...     'commands 4',   # already deleted
+    ...     'break 6, undefined', # condition causing `NameError` during evaluation
+    ...     'continue', # will stop, ignoring runtime error
     ...     'continue',
     ... ]):
     ...    test_function()
@@ -271,12 +275,16 @@ def test_pdb_breakpoint_commands():
     -> print(1)
     (Pdb) break 3
     Breakpoint 1 at <doctest test.test_pdb.test_pdb_breakpoint_commands[0]>:3
+    (Pdb) break 4, +
+    *** Invalid condition +: SyntaxError: invalid syntax
     (Pdb) disable 1
     Disabled breakpoint 1 at <doctest test.test_pdb.test_pdb_breakpoint_commands[0]>:3
     (Pdb) ignore 1 10
     Will ignore next 10 crossings of breakpoint 1.
     (Pdb) condition 1 1 < 2
     New condition set for breakpoint 1.
+    (Pdb) condition 1 1 <
+    *** Invalid condition 1 <: SyntaxError: invalid syntax
     (Pdb) break 4
     Breakpoint 2 at <doctest test.test_pdb.test_pdb_breakpoint_commands[0]>:4
     (Pdb) break 4
@@ -331,8 +339,13 @@ def test_pdb_breakpoint_commands():
             end
     (Pdb) commands 4
     *** cannot set commands: Breakpoint 4 already deleted
+    (Pdb) break 6, undefined
+    Breakpoint 5 at <doctest test.test_pdb.test_pdb_breakpoint_commands[0]>:6
     (Pdb) continue
     3
+    > <doctest test.test_pdb.test_pdb_breakpoint_commands[0]>(6)test_function()
+    -> print(4)
+    (Pdb) continue
     4
     """
 
@@ -597,13 +610,14 @@ def test_pdb_display_command():
     ...     'undisplay',
     ...     'display a < 1',
     ...     'n',
+    ...     'display undefined',
     ...     'continue',
     ... ]):
     ...    test_function()
     > <doctest test.test_pdb.test_pdb_display_command[0]>(4)test_function()
     -> a = 1
     (Pdb) display +
-    Unable to display +: ** raised SyntaxError: invalid syntax **
+    *** Unable to display +: SyntaxError: invalid syntax
     (Pdb) display
     No expression is being displayed
     (Pdb) display a
@@ -627,6 +641,8 @@ def test_pdb_display_command():
     (Pdb) n
     > <doctest test.test_pdb.test_pdb_display_command[0]>(7)test_function()
     -> a = 4
+    (Pdb) display undefined
+    display undefined: ** raised NameError: name 'undefined' is not defined **
     (Pdb) continue
     """
 
diff --git a/Misc/NEWS.d/next/Library/2023-03-28-05-14-59.gh-issue-103068.YQTmrA.rst b/Misc/NEWS.d/next/Library/2023-03-28-05-14-59.gh-issue-103068.YQTmrA.rst
new file mode 100644 (file)
index 0000000..71c142c
--- /dev/null
@@ -0,0 +1,2 @@
+It's no longer possible to register conditional breakpoints in
+:class:`~pdb.Pdb` that raise :exc:`SyntaxError`. Patch by Tian Gao.