bpo-37022: Fix bug where pdb's do_p/do_pp commands swallow exceptions from repr (GH-18180)

This commit is contained in:
Daniel Hahler 2021-06-10 22:32:04 +02:00 committed by GitHub
parent 8a4f0850d7
commit 6544b2532d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 48 additions and 14 deletions

View File

@ -384,8 +384,7 @@ class Pdb(bdb.Bdb, cmd.Cmd):
sys.stdin = save_stdin sys.stdin = save_stdin
sys.displayhook = save_displayhook sys.displayhook = save_displayhook
except: except:
exc_info = sys.exc_info()[:2] self._error_exc()
self.error(traceback.format_exception_only(*exc_info)[-1].strip())
def precmd(self, line): def precmd(self, line):
"""Handle alias expansion and ';;' separator.""" """Handle alias expansion and ';;' separator."""
@ -1104,8 +1103,7 @@ class Pdb(bdb.Bdb, cmd.Cmd):
try: try:
sys.call_tracing(p.run, (arg, globals, locals)) sys.call_tracing(p.run, (arg, globals, locals))
except Exception: except Exception:
exc_info = sys.exc_info()[:2] self._error_exc()
self.error(traceback.format_exception_only(*exc_info)[-1].strip())
self.message("LEAVING RECURSIVE DEBUGGER") self.message("LEAVING RECURSIVE DEBUGGER")
sys.settrace(self.trace_dispatch) sys.settrace(self.trace_dispatch)
self.lastcmd = p.lastcmd self.lastcmd = p.lastcmd
@ -1163,8 +1161,7 @@ class Pdb(bdb.Bdb, cmd.Cmd):
try: try:
return eval(arg, self.curframe.f_globals, self.curframe_locals) return eval(arg, self.curframe.f_globals, self.curframe_locals)
except: except:
exc_info = sys.exc_info()[:2] self._error_exc()
self.error(traceback.format_exception_only(*exc_info)[-1].strip())
raise raise
def _getval_except(self, arg, frame=None): def _getval_except(self, arg, frame=None):
@ -1178,23 +1175,31 @@ class Pdb(bdb.Bdb, cmd.Cmd):
err = traceback.format_exception_only(*exc_info)[-1].strip() err = traceback.format_exception_only(*exc_info)[-1].strip()
return _rstr('** raised %s **' % err) return _rstr('** raised %s **' % err)
def _error_exc(self):
exc_info = sys.exc_info()[:2]
self.error(traceback.format_exception_only(*exc_info)[-1].strip())
def _msg_val_func(self, arg, func):
try:
val = self._getval(arg)
except:
return # _getval() has displayed the error
try:
self.message(func(val))
except:
self._error_exc()
def do_p(self, arg): def do_p(self, arg):
"""p expression """p expression
Print the value of the expression. Print the value of the expression.
""" """
try: self._msg_val_func(arg, repr)
self.message(repr(self._getval(arg)))
except:
pass
def do_pp(self, arg): def do_pp(self, arg):
"""pp expression """pp expression
Pretty-print the value of the expression. Pretty-print the value of the expression.
""" """
try: self._msg_val_func(arg, pprint.pformat)
self.message(pprint.pformat(self._getval(arg)))
except:
pass
complete_print = _complete_expression complete_print = _complete_expression
complete_p = _complete_expression complete_p = _complete_expression

View File

@ -391,6 +391,34 @@ def test_pdb_breakpoints_preserved_across_interactive_sessions():
(Pdb) continue (Pdb) continue
""" """
def test_pdb_pp_repr_exc():
"""Test that do_p/do_pp do not swallow exceptions.
>>> class BadRepr:
... def __repr__(self):
... raise Exception('repr_exc')
>>> obj = BadRepr()
>>> def test_function():
... import pdb; pdb.Pdb(nosigint=True, readrc=False).set_trace()
>>> with PdbTestInput([ # doctest: +NORMALIZE_WHITESPACE
... 'p obj',
... 'pp obj',
... 'continue',
... ]):
... test_function()
--Return--
> <doctest test.test_pdb.test_pdb_pp_repr_exc[2]>(2)test_function()->None
-> import pdb; pdb.Pdb(nosigint=True, readrc=False).set_trace()
(Pdb) p obj
*** Exception: repr_exc
(Pdb) pp obj
*** Exception: repr_exc
(Pdb) continue
"""
def do_nothing(): def do_nothing():
pass pass

View File

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