Skip to content

Commit

Permalink
pythongh-65052: Prevent pdb from crashing when trying to display obje…
Browse files Browse the repository at this point in the history
…cts (pythonGH-110578)

(cherry picked from commit c523ce0)

Co-authored-by: Tian Gao <gaogaotiantian@hotmail.com>
  • Loading branch information
gaogaotiantian authored and miss-islington committed Oct 11, 2023
1 parent c06ac1b commit c1f8fec
Show file tree
Hide file tree
Showing 3 changed files with 62 additions and 7 deletions.
21 changes: 14 additions & 7 deletions Lib/pdb.py
Expand Up @@ -410,8 +410,9 @@ def preloop(self):
# fields are changed to be displayed
if newvalue is not oldvalue and newvalue != oldvalue:
displaying[expr] = newvalue
self.message('display %s: %r [old: %r]' %
(expr, newvalue, oldvalue))
self.message('display %s: %s [old: %s]' %
(expr, self._safe_repr(newvalue, expr),
self._safe_repr(oldvalue, expr)))

def interaction(self, frame, traceback):
# Restore the previous signal handler at the Pdb prompt.
Expand Down Expand Up @@ -1264,7 +1265,7 @@ 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]))
self.message('%s = %s' % (name, self._safe_repr(dict[name], name)))
else:
self.message('%s = *** undefined ***' % (name,))
do_a = do_args
Expand All @@ -1275,7 +1276,7 @@ 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__']))
self.message(self._safe_repr(self.curframe_locals['__return__'], "retval"))
else:
self.error('Not yet returned!')
do_rv = do_retval
Expand Down Expand Up @@ -1310,6 +1311,12 @@ def _msg_val_func(self, arg, func):
except:
self._error_exc()

def _safe_repr(self, obj, expr):
try:
return repr(obj)
except Exception as e:
return _rstr(f"*** repr({expr}) failed: {self._format_exc(e)} ***")

def do_p(self, arg):
"""p expression
Expand Down Expand Up @@ -1486,8 +1493,8 @@ def do_display(self, arg):
if not arg:
if self.displaying:
self.message('Currently displaying:')
for item in self.displaying.get(self.curframe, {}).items():
self.message('%s: %r' % item)
for key, val in self.displaying.get(self.curframe, {}).items():
self.message('%s: %s' % (key, self._safe_repr(val, key)))
else:
self.message('No expression is being displayed')
else:
Expand All @@ -1496,7 +1503,7 @@ def do_display(self, arg):
else:
val = self._getval_except(arg)
self.displaying.setdefault(self.curframe, {})[arg] = val
self.message('display %s: %r' % (arg, val))
self.message('display %s: %s' % (arg, self._safe_repr(val, arg)))

complete_display = _complete_expression

Expand Down
47 changes: 47 additions & 0 deletions Lib/test/test_pdb.py
Expand Up @@ -1846,6 +1846,53 @@ def test_pdb_ambiguous_statements():
(Pdb) continue
"""

def test_pdb_issue_gh_65052():
"""See GH-65052
args, retval and display should not crash if the object is not displayable
>>> class A:
... def __new__(cls):
... import pdb; pdb.Pdb(nosigint=True, readrc=False).set_trace()
... return object.__new__(cls)
... def __init__(self):
... import pdb; pdb.Pdb(nosigint=True, readrc=False).set_trace()
... self.a = 1
... def __repr__(self):
... return self.a
>>> def test_function():
... A()
>>> with PdbTestInput([ # doctest: +ELLIPSIS +NORMALIZE_WHITESPACE
... 's',
... 'retval',
... 'continue',
... 'args',
... 'display self',
... 'display',
... 'continue',
... ]):
... test_function()
> <doctest test.test_pdb.test_pdb_issue_gh_65052[0]>(4)__new__()
-> return object.__new__(cls)
(Pdb) s
--Return--
> <doctest test.test_pdb.test_pdb_issue_gh_65052[0]>(4)__new__()-><A instance at ...>
-> return object.__new__(cls)
(Pdb) retval
*** repr(retval) failed: AttributeError: 'A' object has no attribute 'a' ***
(Pdb) continue
> <doctest test.test_pdb.test_pdb_issue_gh_65052[0]>(7)__init__()
-> self.a = 1
(Pdb) args
self = *** repr(self) failed: AttributeError: 'A' object has no attribute 'a' ***
(Pdb) display self
display self: *** repr(self) failed: AttributeError: 'A' object has no attribute 'a' ***
(Pdb) display
Currently displaying:
self: *** repr(self) failed: AttributeError: 'A' object has no attribute 'a' ***
(Pdb) continue
"""


@support.requires_subprocess()
class PdbTestCase(unittest.TestCase):
Expand Down
@@ -0,0 +1 @@
Prevent :mod:`pdb` from crashing when trying to display undisplayable objects

0 comments on commit c1f8fec

Please sign in to comment.