]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
[3.13] gh-77188: Add pickle tests for objects with slots (GH-144116) (GH-144120)
authorMiss Islington (bot) <31488909+miss-islington@users.noreply.github.com>
Thu, 22 Jan 2026 11:26:48 +0000 (12:26 +0100)
committerGitHub <noreply@github.com>
Thu, 22 Jan 2026 11:26:48 +0000 (11:26 +0000)
(cherry picked from commit cf71e34940e2314ee7ca00961d86a7172286eeea)

Co-authored-by: Serhiy Storchaka <storchaka@gmail.com>
Lib/test/picklecommon.py
Lib/test/pickletester.py

index fece50f17feccad73512dccc130e95c8283b3582..4c19b6c421fc612ea1173b85ade2e4031d5f43ec 100644 (file)
@@ -26,7 +26,7 @@ __main__.E = E
 E.__module__ = "__main__"
 
 # Simple mutable object.
-class Object:
+class Object(object):
     pass
 
 # Hashable immutable key object containing unheshable mutable data.
@@ -38,6 +38,43 @@ class K:
         # Shouldn't support the recursion itself
         return K, (self.value,)
 
+class WithSlots(object):
+    __slots__ = ('a', 'b')
+
+class WithSlotsSubclass(WithSlots):
+    __slots__ = ('c',)
+
+class WithSlotsAndDict(object):
+    __slots__ = ('a', '__dict__')
+
+class WithPrivateAttrs(object):
+    def __init__(self, a):
+        self.__private = a
+    def get(self):
+        return self.__private
+
+class WithPrivateAttrsSubclass(WithPrivateAttrs):
+    def __init__(self, a, b):
+        super().__init__(a)
+        self.__private = b
+    def get2(self):
+        return self.__private
+
+class WithPrivateSlots(object):
+    __slots__ = ('__private',)
+    def __init__(self, a):
+        self.__private = a
+    def get(self):
+        return self.__private
+
+class WithPrivateSlotsSubclass(WithPrivateSlots):
+    __slots__ = ('__private',)
+    def __init__(self, a, b):
+        super().__init__(a)
+        self.__private = b
+    def get2(self):
+        return self.__private
+
 # For test_misc
 class myint(int):
     def __init__(self, x):
index 99ae5f9ad64b4f065e0cce899f284ff9152cb4f5..ae1c8c8aa330154cd742dae2fbba238cd3515990 100644 (file)
@@ -33,6 +33,7 @@ from test.support import (
 from test.support.import_helper import forget
 from test.support.os_helper import TESTFN
 from test.support import threading_helper
+from test.support.testcase import ExtraAssertions
 from test.support.warnings_helper import save_restore_warnings_filters
 from test import picklecommon
 from test.picklecommon import *
@@ -2023,7 +2024,7 @@ class AbstractPicklingErrorTests:
         check(-2**1000, (OverflowError, struct.error))
 
 
-class AbstractPickleTests:
+class AbstractPickleTests(ExtraAssertions):
     # Subclass must define self.dumps, self.loads.
 
     py_version = sys.version_info  # for test_xpickle
@@ -3617,6 +3618,82 @@ class AbstractPickleTests:
                 with self.subTest(proto=proto, descr=descr):
                     self.assertRaises(TypeError, self.dumps, descr, proto)
 
+    def test_object_with_attrs(self):
+        obj = Object()
+        obj.a = 1
+        for proto in protocols:
+            with self.subTest(proto=proto):
+                unpickled = self.loads(self.dumps(obj, proto))
+                self.assertEqual(unpickled.a, obj.a)
+
+    def test_object_with_slots(self):
+        obj = WithSlots()
+        obj.a = 1
+        self.assertRaises(TypeError, self.dumps, obj, 0)
+        self.assertRaises(TypeError, self.dumps, obj, 1)
+        for proto in protocols[2:]:
+            with self.subTest(proto=proto):
+                unpickled = self.loads(self.dumps(obj, proto))
+                self.assertEqual(unpickled.a, obj.a)
+                self.assertNotHasAttr(unpickled, 'b')
+
+        obj = WithSlotsSubclass()
+        obj.a = 1
+        obj.c = 2
+        self.assertRaises(TypeError, self.dumps, obj, 0)
+        self.assertRaises(TypeError, self.dumps, obj, 1)
+        for proto in protocols[2:]:
+            with self.subTest(proto=proto):
+                unpickled = self.loads(self.dumps(obj, proto))
+                self.assertEqual(unpickled.a, obj.a)
+                self.assertEqual(unpickled.c, obj.c)
+                self.assertNotHasAttr(unpickled, 'b')
+
+        obj = WithSlotsAndDict()
+        obj.a = 1
+        obj.c = 2
+        self.assertRaises(TypeError, self.dumps, obj, 0)
+        self.assertRaises(TypeError, self.dumps, obj, 1)
+        for proto in protocols[2:]:
+            with self.subTest(proto=proto):
+                unpickled = self.loads(self.dumps(obj, proto))
+                self.assertEqual(unpickled.a, obj.a)
+                self.assertEqual(unpickled.c, obj.c)
+                self.assertEqual(unpickled.__dict__, obj.__dict__)
+                self.assertNotHasAttr(unpickled, 'b')
+
+    def test_object_with_private_attrs(self):
+        obj = WithPrivateAttrs(1)
+        for proto in protocols:
+            with self.subTest(proto=proto):
+                unpickled = self.loads(self.dumps(obj, proto))
+                self.assertEqual(unpickled.get(), obj.get())
+
+        obj = WithPrivateAttrsSubclass(1, 2)
+        for proto in protocols:
+            with self.subTest(proto=proto):
+                unpickled = self.loads(self.dumps(obj, proto))
+                self.assertEqual(unpickled.get(), obj.get())
+                self.assertEqual(unpickled.get2(), obj.get2())
+
+    def test_object_with_private_slots(self):
+        obj = WithPrivateSlots(1)
+        self.assertRaises(TypeError, self.dumps, obj, 0)
+        self.assertRaises(TypeError, self.dumps, obj, 1)
+        for proto in protocols[2:]:
+            with self.subTest(proto=proto):
+                unpickled = self.loads(self.dumps(obj, proto))
+                self.assertEqual(unpickled.get(), obj.get())
+
+        obj = WithPrivateSlotsSubclass(1, 2)
+        self.assertRaises(TypeError, self.dumps, obj, 0)
+        self.assertRaises(TypeError, self.dumps, obj, 1)
+        for proto in protocols[2:]:
+            with self.subTest(proto=proto):
+                unpickled = self.loads(self.dumps(obj, proto))
+                self.assertEqual(unpickled.get(), obj.get())
+                self.assertEqual(unpickled.get2(), obj.get2())
+
     def test_compat_pickle(self):
         if self.py_version < (3, 4):
             self.skipTest("doesn't work in Python < 3.4'")