mirror of https://github.com/python/cpython
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:
parent
d835b3f05d
commit
e375bff037
38
Lib/pdb.py
38
Lib/pdb.py
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
"""
|
||||
|
||||
|
|
|
@ -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.
|
Loading…
Reference in New Issue