diff --git a/Doc/library/pdb.rst b/Doc/library/pdb.rst index 6d1dba1bf2eb01..6dbd3e35e78685 100644 --- a/Doc/library/pdb.rst +++ b/Doc/library/pdb.rst @@ -543,7 +543,9 @@ can be overridden by the local file. .. pdbcommand:: retval - Print the return value for the last return of a function. + Print the return value for the last return of a function. This command must + be used immediately after the return line is executed; *return* command can + be used to jump to the location where *retval* can be used. .. rubric:: Footnotes diff --git a/Lib/pdb.py b/Lib/pdb.py index d7110074538ace..7ba2bf6fc2e124 100755 --- a/Lib/pdb.py +++ b/Lib/pdb.py @@ -1230,7 +1230,12 @@ def do_args(self, arg): for i in range(n): name = co.co_varnames[i] if name in dict: - self.message('%s = %r' % (name, dict[name])) + try: + r = dict[name] + repr(r) + except Exception as e: + r = e + self.message('%s = %r' % (name, r)) else: self.message('%s = *** undefined ***' % (name,)) do_a = do_args @@ -1240,7 +1245,10 @@ def do_retval(self, arg): Print the return value for the last return of a function. """ if '__return__' in self.curframe_locals: - self.message(repr(self.curframe_locals['__return__'])) + try: + self.message(repr(self.curframe_locals['__return__'])) + except: + self._error_exc() else: self.error('Not yet returned!') do_rv = do_retval diff --git a/Lib/test/test_pdb.py b/Lib/test/test_pdb.py index 01263db28f18cd..2c6abc094384f2 100644 --- a/Lib/test/test_pdb.py +++ b/Lib/test/test_pdb.py @@ -429,6 +429,42 @@ def test_pdb_pp_repr_exc(): (Pdb) continue """ +def test_pdb_bad_repr(): + # Note that return of test_function() needs to be assigned to a value; + # otherwise doctest tries to run repr() on it and fails. + """Test that args/retval commands handle bad repr objects. + + >>> class BadRepr: + ... def __repr__(self): + ... raise AttributeError("'BadRepr' object has no attribute 'foo'") + ... def __str__(self): + ... return '' + >>> obj = BadRepr() + + >>> def test_function(x): + ... import pdb; pdb.Pdb(nosigint=True, readrc=False).set_trace() + ... return x + + >>> with PdbTestInput([ # doctest: +NORMALIZE_WHITESPACE, +ELLIPSIS + ... 'args', + ... 'return', + ... 'retval', + ... 'continue', + ... ]): + ... x = test_function(obj) + > (3)test_function() + -> return x + (Pdb) args + x = AttributeError("'BadRepr' object has no attribute 'foo'") + (Pdb) return + --Return-- + > (3)test_function()... + -> return x + (Pdb) retval + *** AttributeError: 'BadRepr' object has no attribute 'foo' + (Pdb) continue + """ + def do_nothing(): pass diff --git a/Misc/NEWS.d/next/Library/2021-09-16-22-38-58.bpo-20853.J3kJay.rst b/Misc/NEWS.d/next/Library/2021-09-16-22-38-58.bpo-20853.J3kJay.rst new file mode 100644 index 00000000000000..8fd6ee6ba1bf8b --- /dev/null +++ b/Misc/NEWS.d/next/Library/2021-09-16-22-38-58.bpo-20853.J3kJay.rst @@ -0,0 +1,2 @@ +In :mod:`pdb`, *args* and *retval* commands were fixed to properly handle +objects for which :func:`repr` raises an exception.