Skip to content

Commit e375bff

Browse files
gaogaotiantianambvartemmukhin
authored
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>
1 parent d835b3f commit e375bff

File tree

3 files changed

+45
-13
lines changed

3 files changed

+45
-13
lines changed

Lib/pdb.py

+26-12
Original file line numberDiff line numberDiff line change
@@ -377,8 +377,7 @@ def user_exception(self, frame, exc_info):
377377
# stop when the debuggee is returning from such generators.
378378
prefix = 'Internal ' if (not exc_traceback
379379
and exc_type is StopIteration) else ''
380-
self.message('%s%s' % (prefix,
381-
traceback.format_exception_only(exc_type, exc_value)[-1].strip()))
380+
self.message('%s%s' % (prefix, self._format_exc(exc_value)))
382381
self.interaction(frame, exc_traceback)
383382

384383
# General interaction function
@@ -399,7 +398,7 @@ def preloop(self):
399398
displaying = self.displaying.get(self.curframe)
400399
if displaying:
401400
for expr, oldvalue in displaying.items():
402-
newvalue, _ = self._getval_except(expr)
401+
newvalue = self._getval_except(expr)
403402
# check for identity first; this prevents custom __eq__ to
404403
# be called at every loop, and also prevents instances whose
405404
# fields are changed to be displayed
@@ -702,6 +701,9 @@ def do_break(self, arg, temporary = 0):
702701
if comma > 0:
703702
# parse stuff after comma: "condition"
704703
cond = arg[comma+1:].lstrip()
704+
if err := self._compile_error_message(cond):
705+
self.error('Invalid condition %s: %r' % (cond, err))
706+
return
705707
arg = arg[:comma].rstrip()
706708
# parse stuff before comma: [filename:]lineno | function
707709
colon = arg.rfind(':')
@@ -887,6 +889,9 @@ def do_condition(self, arg):
887889
args = arg.split(' ', 1)
888890
try:
889891
cond = args[1]
892+
if err := self._compile_error_message(cond):
893+
self.error('Invalid condition %s: %r' % (cond, err))
894+
return
890895
except IndexError:
891896
cond = None
892897
try:
@@ -1246,16 +1251,15 @@ def _getval(self, arg):
12461251
def _getval_except(self, arg, frame=None):
12471252
try:
12481253
if frame is None:
1249-
return eval(arg, self.curframe.f_globals, self.curframe_locals), None
1254+
return eval(arg, self.curframe.f_globals, self.curframe_locals)
12501255
else:
1251-
return eval(arg, frame.f_globals, frame.f_locals), None
1256+
return eval(arg, frame.f_globals, frame.f_locals)
12521257
except BaseException as exc:
1253-
err = traceback.format_exception_only(exc)[-1].strip()
1254-
return _rstr('** raised %s **' % err), exc
1258+
return _rstr('** raised %s **' % self._format_exc(exc))
12551259

12561260
def _error_exc(self):
1257-
exc_info = sys.exc_info()[:2]
1258-
self.error(traceback.format_exception_only(*exc_info)[-1].strip())
1261+
exc = sys.exc_info()[1]
1262+
self.error(self._format_exc(exc))
12591263

12601264
def _msg_val_func(self, arg, func):
12611265
try:
@@ -1443,10 +1447,10 @@ def do_display(self, arg):
14431447
else:
14441448
self.message('No expression is being displayed')
14451449
else:
1446-
val, exc = self._getval_except(arg)
1447-
if isinstance(exc, SyntaxError):
1448-
self.message('Unable to display %s: %r' % (arg, val))
1450+
if err := self._compile_error_message(arg):
1451+
self.error('Unable to display %s: %r' % (arg, err))
14491452
else:
1453+
val = self._getval_except(arg)
14501454
self.displaying.setdefault(self.curframe, {})[arg] = val
14511455
self.message('display %s: %r' % (arg, val))
14521456

@@ -1647,6 +1651,16 @@ def _run(self, target: Union[_ModuleTarget, _ScriptTarget]):
16471651

16481652
self.run(target.code)
16491653

1654+
def _format_exc(self, exc: BaseException):
1655+
return traceback.format_exception_only(exc)[-1].strip()
1656+
1657+
def _compile_error_message(self, expr):
1658+
"""Return the error message as string if compiling `expr` fails."""
1659+
try:
1660+
compile(expr, "<stdin>", "eval")
1661+
except SyntaxError as exc:
1662+
return _rstr(self._format_exc(exc))
1663+
return ""
16501664

16511665
# Collect all command help into docstring, if not run with -OO
16521666

Lib/test/test_pdb.py

+17-1
Original file line numberDiff line numberDiff line change
@@ -240,9 +240,11 @@ def test_pdb_breakpoint_commands():
240240
241241
>>> with PdbTestInput([ # doctest: +NORMALIZE_WHITESPACE
242242
... 'break 3',
243+
... 'break 4, +',
243244
... 'disable 1',
244245
... 'ignore 1 10',
245246
... 'condition 1 1 < 2',
247+
... 'condition 1 1 <',
246248
... 'break 4',
247249
... 'break 4',
248250
... 'break',
@@ -264,19 +266,25 @@ def test_pdb_breakpoint_commands():
264266
... 'commands 10', # out of range
265267
... 'commands a', # display help
266268
... 'commands 4', # already deleted
269+
... 'break 6, undefined', # condition causing `NameError` during evaluation
270+
... 'continue', # will stop, ignoring runtime error
267271
... 'continue',
268272
... ]):
269273
... test_function()
270274
> <doctest test.test_pdb.test_pdb_breakpoint_commands[0]>(3)test_function()
271275
-> print(1)
272276
(Pdb) break 3
273277
Breakpoint 1 at <doctest test.test_pdb.test_pdb_breakpoint_commands[0]>:3
278+
(Pdb) break 4, +
279+
*** Invalid condition +: SyntaxError: invalid syntax
274280
(Pdb) disable 1
275281
Disabled breakpoint 1 at <doctest test.test_pdb.test_pdb_breakpoint_commands[0]>:3
276282
(Pdb) ignore 1 10
277283
Will ignore next 10 crossings of breakpoint 1.
278284
(Pdb) condition 1 1 < 2
279285
New condition set for breakpoint 1.
286+
(Pdb) condition 1 1 <
287+
*** Invalid condition 1 <: SyntaxError: invalid syntax
280288
(Pdb) break 4
281289
Breakpoint 2 at <doctest test.test_pdb.test_pdb_breakpoint_commands[0]>:4
282290
(Pdb) break 4
@@ -331,8 +339,13 @@ def test_pdb_breakpoint_commands():
331339
end
332340
(Pdb) commands 4
333341
*** cannot set commands: Breakpoint 4 already deleted
342+
(Pdb) break 6, undefined
343+
Breakpoint 5 at <doctest test.test_pdb.test_pdb_breakpoint_commands[0]>:6
334344
(Pdb) continue
335345
3
346+
> <doctest test.test_pdb.test_pdb_breakpoint_commands[0]>(6)test_function()
347+
-> print(4)
348+
(Pdb) continue
336349
4
337350
"""
338351

@@ -597,13 +610,14 @@ def test_pdb_display_command():
597610
... 'undisplay',
598611
... 'display a < 1',
599612
... 'n',
613+
... 'display undefined',
600614
... 'continue',
601615
... ]):
602616
... test_function()
603617
> <doctest test.test_pdb.test_pdb_display_command[0]>(4)test_function()
604618
-> a = 1
605619
(Pdb) display +
606-
Unable to display +: ** raised SyntaxError: invalid syntax **
620+
*** Unable to display +: SyntaxError: invalid syntax
607621
(Pdb) display
608622
No expression is being displayed
609623
(Pdb) display a
@@ -627,6 +641,8 @@ def test_pdb_display_command():
627641
(Pdb) n
628642
> <doctest test.test_pdb.test_pdb_display_command[0]>(7)test_function()
629643
-> a = 4
644+
(Pdb) display undefined
645+
display undefined: ** raised NameError: name 'undefined' is not defined **
630646
(Pdb) continue
631647
"""
632648

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
It's no longer possible to register conditional breakpoints in
2+
:class:`~pdb.Pdb` that raise :exc:`SyntaxError`. Patch by Tian Gao.

0 commit comments

Comments
 (0)