]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
[3.12] gh-65052: Prevent pdb from crashing when trying to display objects (GH-110578...
authorMiss Islington (bot) <31488909+miss-islington@users.noreply.github.com>
Wed, 11 Oct 2023 18:59:11 +0000 (20:59 +0200)
committerGitHub <noreply@github.com>
Wed, 11 Oct 2023 18:59:11 +0000 (18:59 +0000)
gh-65052: Prevent pdb from crashing when trying to display objects (GH-110578)
(cherry picked from commit c523ce0f434582580a3721e15cb7dd6b56ad0236)

Co-authored-by: Tian Gao <gaogaotiantian@hotmail.com>
Lib/pdb.py
Lib/test/test_pdb.py
Misc/NEWS.d/next/Library/2023-10-09-19-09-32.gh-issue-65052.C2mRlo.rst [new file with mode: 0644]

index 646695ccd1f4dd8fcfa425ce0675a668f2cb26b6..df01829b48460e8d41eb253c9bbdee05729111c0 100755 (executable)
@@ -410,8 +410,9 @@ class Pdb(bdb.Bdb, cmd.Cmd):
                 # 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.
@@ -1264,7 +1265,7 @@ class Pdb(bdb.Bdb, cmd.Cmd):
         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
@@ -1275,7 +1276,7 @@ class Pdb(bdb.Bdb, cmd.Cmd):
         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
@@ -1310,6 +1311,12 @@ class Pdb(bdb.Bdb, cmd.Cmd):
         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
 
@@ -1486,8 +1493,8 @@ class Pdb(bdb.Bdb, cmd.Cmd):
         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:
@@ -1496,7 +1503,7 @@ class Pdb(bdb.Bdb, cmd.Cmd):
             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
 
index c0fb61253be5ad75f53ea7dbe3dcc05f60269ffe..46f9d6ef8a1d44cefc79b58bc1355cdf10e6331e 100644 (file)
@@ -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):
diff --git a/Misc/NEWS.d/next/Library/2023-10-09-19-09-32.gh-issue-65052.C2mRlo.rst b/Misc/NEWS.d/next/Library/2023-10-09-19-09-32.gh-issue-65052.C2mRlo.rst
new file mode 100644 (file)
index 0000000..4739c63
--- /dev/null
@@ -0,0 +1 @@
+Prevent :mod:`pdb` from crashing when trying to display undisplayable objects