gh-103068: Check condition expression of breakpoints for pdb (#103069)

Co-authored-by: Łukasz Langa <lukasz@langa.pl>
Co-authored-by: Artem Mukhin <ortem00@gmail.com>
This commit is contained in:
gaogaotiantian 2023-03-29 03:09:12 -07:00 committed by GitHub
parent d835b3f05d
commit e375bff037
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 45 additions and 13 deletions

View File

@ -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

View 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
"""

View File

@ -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.