Skip to content

Commit 6544b25

Browse files
authored
bpo-37022: Fix bug where pdb's do_p/do_pp commands swallow exceptions from repr (GH-18180)
1 parent 8a4f085 commit 6544b25

File tree

3 files changed

+48
-14
lines changed

3 files changed

+48
-14
lines changed

Lib/pdb.py

+19-14
Original file line numberDiff line numberDiff line change
@@ -384,8 +384,7 @@ def default(self, line):
384384
sys.stdin = save_stdin
385385
sys.displayhook = save_displayhook
386386
except:
387-
exc_info = sys.exc_info()[:2]
388-
self.error(traceback.format_exception_only(*exc_info)[-1].strip())
387+
self._error_exc()
389388

390389
def precmd(self, line):
391390
"""Handle alias expansion and ';;' separator."""
@@ -1104,8 +1103,7 @@ def do_debug(self, arg):
11041103
try:
11051104
sys.call_tracing(p.run, (arg, globals, locals))
11061105
except Exception:
1107-
exc_info = sys.exc_info()[:2]
1108-
self.error(traceback.format_exception_only(*exc_info)[-1].strip())
1106+
self._error_exc()
11091107
self.message("LEAVING RECURSIVE DEBUGGER")
11101108
sys.settrace(self.trace_dispatch)
11111109
self.lastcmd = p.lastcmd
@@ -1163,8 +1161,7 @@ def _getval(self, arg):
11631161
try:
11641162
return eval(arg, self.curframe.f_globals, self.curframe_locals)
11651163
except:
1166-
exc_info = sys.exc_info()[:2]
1167-
self.error(traceback.format_exception_only(*exc_info)[-1].strip())
1164+
self._error_exc()
11681165
raise
11691166

11701167
def _getval_except(self, arg, frame=None):
@@ -1178,23 +1175,31 @@ def _getval_except(self, arg, frame=None):
11781175
err = traceback.format_exception_only(*exc_info)[-1].strip()
11791176
return _rstr('** raised %s **' % err)
11801177

1178+
def _error_exc(self):
1179+
exc_info = sys.exc_info()[:2]
1180+
self.error(traceback.format_exception_only(*exc_info)[-1].strip())
1181+
1182+
def _msg_val_func(self, arg, func):
1183+
try:
1184+
val = self._getval(arg)
1185+
except:
1186+
return # _getval() has displayed the error
1187+
try:
1188+
self.message(func(val))
1189+
except:
1190+
self._error_exc()
1191+
11811192
def do_p(self, arg):
11821193
"""p expression
11831194
Print the value of the expression.
11841195
"""
1185-
try:
1186-
self.message(repr(self._getval(arg)))
1187-
except:
1188-
pass
1196+
self._msg_val_func(arg, repr)
11891197

11901198
def do_pp(self, arg):
11911199
"""pp expression
11921200
Pretty-print the value of the expression.
11931201
"""
1194-
try:
1195-
self.message(pprint.pformat(self._getval(arg)))
1196-
except:
1197-
pass
1202+
self._msg_val_func(arg, pprint.pformat)
11981203

11991204
complete_print = _complete_expression
12001205
complete_p = _complete_expression

Lib/test/test_pdb.py

+28
Original file line numberDiff line numberDiff line change
@@ -391,6 +391,34 @@ def test_pdb_breakpoints_preserved_across_interactive_sessions():
391391
(Pdb) continue
392392
"""
393393

394+
def test_pdb_pp_repr_exc():
395+
"""Test that do_p/do_pp do not swallow exceptions.
396+
397+
>>> class BadRepr:
398+
... def __repr__(self):
399+
... raise Exception('repr_exc')
400+
>>> obj = BadRepr()
401+
402+
>>> def test_function():
403+
... import pdb; pdb.Pdb(nosigint=True, readrc=False).set_trace()
404+
405+
>>> with PdbTestInput([ # doctest: +NORMALIZE_WHITESPACE
406+
... 'p obj',
407+
... 'pp obj',
408+
... 'continue',
409+
... ]):
410+
... test_function()
411+
--Return--
412+
> <doctest test.test_pdb.test_pdb_pp_repr_exc[2]>(2)test_function()->None
413+
-> import pdb; pdb.Pdb(nosigint=True, readrc=False).set_trace()
414+
(Pdb) p obj
415+
*** Exception: repr_exc
416+
(Pdb) pp obj
417+
*** Exception: repr_exc
418+
(Pdb) continue
419+
"""
420+
421+
394422
def do_nothing():
395423
pass
396424

Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
:mod:`pdb` now displays exceptions from ``repr()`` with its ``p`` and ``pp`` commands.

0 commit comments

Comments
 (0)