gh-125631: Enable setting persistent_id and persistent_load of pickler and unpickler (GH-125752)

pickle.Pickler.persistent_id and pickle.Unpickler.persistent_load can
again be overridden as instance attributes.
This commit is contained in:
Serhiy Storchaka 2024-11-07 08:53:02 +02:00 committed by GitHub
parent 2a6b6b33df
commit 223d3dc554
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 146 additions and 2 deletions

View file

@ -250,6 +250,84 @@ class PyIdPersPicklerTests(AbstractIdentityPersistentPicklerTests,
self.assertEqual(unpickler.load(), 'abc')
self.assertEqual(called, ['abc'])
def test_pickler_instance_attribute(self):
def persistent_id(obj):
called.append(obj)
return obj
for proto in range(pickle.HIGHEST_PROTOCOL + 1):
f = io.BytesIO()
pickler = self.pickler(f, proto)
called = []
old_persistent_id = pickler.persistent_id
pickler.persistent_id = persistent_id
self.assertEqual(pickler.persistent_id, persistent_id)
pickler.dump('abc')
self.assertEqual(called, ['abc'])
self.assertEqual(self.loads(f.getvalue()), 'abc')
del pickler.persistent_id
self.assertEqual(pickler.persistent_id, old_persistent_id)
def test_unpickler_instance_attribute(self):
def persistent_load(pid):
called.append(pid)
return pid
for proto in range(pickle.HIGHEST_PROTOCOL + 1):
unpickler = self.unpickler(io.BytesIO(self.dumps('abc', proto)))
called = []
old_persistent_load = unpickler.persistent_load
unpickler.persistent_load = persistent_load
self.assertEqual(unpickler.persistent_load, persistent_load)
self.assertEqual(unpickler.load(), 'abc')
self.assertEqual(called, ['abc'])
del unpickler.persistent_load
self.assertEqual(unpickler.persistent_load, old_persistent_load)
def test_pickler_super_instance_attribute(self):
class PersPickler(self.pickler):
def persistent_id(subself, obj):
raise AssertionError('should never be called')
def _persistent_id(subself, obj):
called.append(obj)
self.assertIsNone(super().persistent_id(obj))
return obj
for proto in range(pickle.HIGHEST_PROTOCOL + 1):
f = io.BytesIO()
pickler = PersPickler(f, proto)
called = []
old_persistent_id = pickler.persistent_id
pickler.persistent_id = pickler._persistent_id
self.assertEqual(pickler.persistent_id, pickler._persistent_id)
pickler.dump('abc')
self.assertEqual(called, ['abc'])
self.assertEqual(self.loads(f.getvalue()), 'abc')
del pickler.persistent_id
self.assertEqual(pickler.persistent_id, old_persistent_id)
def test_unpickler_super_instance_attribute(self):
class PersUnpickler(self.unpickler):
def persistent_load(subself, pid):
raise AssertionError('should never be called')
def _persistent_load(subself, pid):
called.append(pid)
with self.assertRaises(self.persistent_load_error):
super().persistent_load(pid)
return pid
for proto in range(pickle.HIGHEST_PROTOCOL + 1):
unpickler = PersUnpickler(io.BytesIO(self.dumps('abc', proto)))
called = []
old_persistent_load = unpickler.persistent_load
unpickler.persistent_load = unpickler._persistent_load
self.assertEqual(unpickler.persistent_load, unpickler._persistent_load)
self.assertEqual(unpickler.load(), 'abc')
self.assertEqual(called, ['abc'])
del unpickler.persistent_load
self.assertEqual(unpickler.persistent_load, old_persistent_load)
class PyPicklerUnpicklerObjectTests(AbstractPicklerUnpicklerObjectTests, unittest.TestCase):
pickler_class = pickle._Pickler
@ -373,7 +451,7 @@ if has_c_implementation:
check_sizeof = support.check_sizeof
def test_pickler(self):
basesize = support.calcobjsize('6P2n3i2n3i2P')
basesize = support.calcobjsize('7P2n3i2n3i2P')
p = _pickle.Pickler(io.BytesIO())
self.assertEqual(object.__sizeof__(p), basesize)
MT_size = struct.calcsize('3nP0n')
@ -390,7 +468,7 @@ if has_c_implementation:
0) # Write buffer is cleared after every dump().
def test_unpickler(self):
basesize = support.calcobjsize('2P2nP 2P2n2i5P 2P3n8P2n2i')
basesize = support.calcobjsize('2P2n2P 2P2n2i5P 2P3n8P2n2i')
unpickler = _pickle.Unpickler
P = struct.calcsize('P') # Size of memo table entry.
n = struct.calcsize('n') # Size of mark table entry.