Skip to content

Commit c90a27b

Browse files
committed
Drop the wrapper instead of chaining it
The prefix repeats the class name that the constructor TypeError already carries, and the C unpickler never wrapped the error, so letting the original propagate removes the traceback-in-args bug and the divergence at once.
1 parent 19ac51f commit c90a27b

3 files changed

Lines changed: 9 additions & 23 deletions

File tree

Lib/pickle.py

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1623,11 +1623,7 @@ def load_dict(self):
16231623
def _instantiate(self, klass, args):
16241624
if (args or not isinstance(klass, type) or
16251625
hasattr(klass, "__getinitargs__")):
1626-
try:
1627-
value = klass(*args)
1628-
except TypeError as err:
1629-
raise TypeError("in constructor for %s: %s" %
1630-
(klass.__name__, str(err))) from err
1626+
value = klass(*args)
16311627
else:
16321628
value = klass.__new__(klass)
16331629
self.append(value)

Lib/test/pickletester.py

Lines changed: 4 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -848,27 +848,17 @@ def test_load_classic_instance(self):
848848

849849
def test_load_bad_constructor(self):
850850
# gh-154002: a TypeError raised by an old-style instance constructor
851-
# during INST/OBJ unpickling must be reported with proper exception
852-
# chaining, not with the traceback object stored in the exception's
853-
# args (which used to happen in the pure-Python unpickler).
851+
# during INST/OBJ unpickling propagates unchanged. The pure-Python
852+
# unpickler used to replace it with one that carried the traceback
853+
# object in its args.
854854
# 0: ( MARK
855855
# 1: I INT 1
856856
# 4: i INST '__main__ BadConstructor' (MARK at 0)
857857
# 28: . STOP
858858
data = b'(I1\ni__main__\nBadConstructor\n.'
859859
with self.assertRaises(TypeError) as cm:
860860
self.loads(data)
861-
exc = cm.exception
862-
# A traceback object must never leak into the exception's args.
863-
self.assertNotIn(types.TracebackType, [type(a) for a in exc.args])
864-
# Only the pure-Python unpickler wraps the failure; the C one lets the
865-
# original TypeError propagate. When it wraps, it must chain the cause.
866-
if str(exc).startswith("in constructor for "):
867-
self.assertEqual(
868-
exc.args,
869-
("in constructor for BadConstructor: bad constructor",))
870-
self.assertIsInstance(exc.__cause__, TypeError)
871-
self.assertEqual(str(exc.__cause__), "bad constructor")
861+
self.assertEqual(cm.exception.args, ("bad constructor",))
872862

873863
def test_maxint64(self):
874864
maxint64 = (1 << 63) - 1
Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
Fix exception handling in the pure-Python :mod:`pickle` unpickler when a
2-
class constructor raises :exc:`TypeError` while unpickling an old-style
3-
instance. The original error is now chained with ``from`` instead of having
4-
its traceback object stored in the raised exception's ``args``.
1+
The pure-Python :mod:`pickle` unpickler no longer replaces a :exc:`TypeError`
2+
raised by an old-style instance constructor with a new one carrying the
3+
traceback object in its ``args``. The original error now propagates, as it
4+
already did in the C implementation.

0 commit comments

Comments
 (0)