https://github.com/python/cpython/commit/76ac2dec7991fdd6b9365689a36922d1c1ae0a38
commit: 76ac2dec7991fdd6b9365689a36922d1c1ae0a38
branch: main
author: Vyron Vasileiadis <[email protected]>
committer: serhiy-storchaka <[email protected]>
date: 2026-08-16T08:34:21+03:00
summary:
gh-154002: Do not wrap a constructor TypeError in pickle._Unpickler (GH-154003)
It passed the traceback as the second argument to a new TypeError, so that
the traceback object ended up in args and the original error was not
chained. The original error now propagates, as in the C implementation.
files:
A Misc/NEWS.d/next/Library/2026-07-18-18-12-44.gh-issue-154002.Qw9zTn.rst
M Lib/pickle.py
M Lib/test/picklecommon.py
M Lib/test/pickletester.py
diff --git a/Lib/pickle.py b/Lib/pickle.py
index f92b1fde768fc7d..b475d96dd6576d9 100644
--- a/Lib/pickle.py
+++ b/Lib/pickle.py
@@ -1623,11 +1623,7 @@ def load_dict(self):
def _instantiate(self, klass, args):
if (args or not isinstance(klass, type) or
hasattr(klass, "__getinitargs__")):
- try:
- value = klass(*args)
- except TypeError as err:
- raise TypeError("in constructor for %s: %s" %
- (klass.__name__, str(err)), err.__traceback__)
+ value = klass(*args)
else:
value = klass.__new__(klass)
self.append(value)
diff --git a/Lib/test/picklecommon.py b/Lib/test/picklecommon.py
index bb8e41b01492ead..5dd56c4fbf9ec81 100644
--- a/Lib/test/picklecommon.py
+++ b/Lib/test/picklecommon.py
@@ -17,6 +17,11 @@ class E(C):
def __getinitargs__(self):
return ()
+# For test_load_bad_constructor
+class BadConstructor:
+ def __init__(self, *args):
+ raise TypeError("bad constructor")
+
import __main__
__main__.C = C
C.__module__ = "__main__"
@@ -24,6 +29,8 @@ def __getinitargs__(self):
D.__module__ = "__main__"
__main__.E = E
E.__module__ = "__main__"
+__main__.BadConstructor = BadConstructor
+BadConstructor.__module__ = "__main__"
# Simple mutable object.
class Object(object):
diff --git a/Lib/test/pickletester.py b/Lib/test/pickletester.py
index 83a71b7efedd379..c53262e358b48ea 100644
--- a/Lib/test/pickletester.py
+++ b/Lib/test/pickletester.py
@@ -846,6 +846,20 @@ def test_load_classic_instance(self):
b'q\x00oq\x01}q\x02b.').replace(b'X', xname)
self.assert_is_copy(X(*args), self.loads(pickle2))
+ def test_load_bad_constructor(self):
+ # gh-154002: a TypeError raised by an old-style instance constructor
+ # during INST/OBJ unpickling propagates unchanged. The pure-Python
+ # unpickler used to replace it with one that carried the traceback
+ # object in its args.
+ # 0: ( MARK
+ # 1: I INT 1
+ # 4: i INST '__main__ BadConstructor' (MARK at 0)
+ # 28: . STOP
+ data = b'(I1\ni__main__\nBadConstructor\n.'
+ with self.assertRaises(TypeError) as cm:
+ self.loads(data)
+ self.assertEqual(cm.exception.args, ("bad constructor",))
+
def test_maxint64(self):
maxint64 = (1 << 63) - 1
data = b'I' + str(maxint64).encode("ascii") + b'\n.'
diff --git
a/Misc/NEWS.d/next/Library/2026-07-18-18-12-44.gh-issue-154002.Qw9zTn.rst
b/Misc/NEWS.d/next/Library/2026-07-18-18-12-44.gh-issue-154002.Qw9zTn.rst
new file mode 100644
index 000000000000000..0475ce7330b6fa8
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2026-07-18-18-12-44.gh-issue-154002.Qw9zTn.rst
@@ -0,0 +1,4 @@
+The pure-Python :mod:`pickle` unpickler no longer replaces a :exc:`TypeError`
+raised by an old-style instance constructor with a new one carrying the
+traceback object in its ``args``. The original error now propagates, as it
+already did in the C implementation.
_______________________________________________
Python-checkins mailing list -- [email protected]
To unsubscribe send an email to [email protected]
https://mail.python.org/mailman3//lists/python-checkins.python.org
Member address: [email protected]