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 9ba498ce8f575de..c9b72cf5a1ca837 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.