From 7ed9a024c452c0bbcb19642f238257c48b472059 Mon Sep 17 00:00:00 2001 From: Pierre Glaser Date: Sat, 26 Oct 2019 17:14:19 +0200 Subject: [PATCH 1/4] TST add a test pickling class with inherited slots --- tests/cloudpickle_test.py | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/tests/cloudpickle_test.py b/tests/cloudpickle_test.py index 8bd210abe..64d3a9613 100644 --- a/tests/cloudpickle_test.py +++ b/tests/cloudpickle_test.py @@ -1666,6 +1666,17 @@ def __init__(self): with pytest.raises(AttributeError): obj.non_registered_attribute = 1 + class SubclassWithSlots(ClassWithSlots): + def __init__(self): + self.unregistered_attribute = 1 + + obj = SubclassWithSlots() + s = cloudpickle.dumps(obj, protocol=self.protocol) + del SubclassWithSlots + depickled_obj = cloudpickle.loads(s) + assert depickled_obj.unregistered_attribute == 1 + + @unittest.skipIf(not hasattr(types, "MappingProxyType"), "Old versions of Python do not have this type.") def test_mappingproxy(self): From 3a1c4a73171d132ba3d100af6a1a00e8cb48c5a2 Mon Sep 17 00:00:00 2001 From: Pierre Glaser Date: Sat, 26 Oct 2019 17:19:17 +0200 Subject: [PATCH 2/4] FIX dont pickle parent's class __slots__ --- cloudpickle/cloudpickle.py | 2 +- cloudpickle/cloudpickle_fast.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/cloudpickle/cloudpickle.py b/cloudpickle/cloudpickle.py index 1d4c85cbe..76e373c67 100644 --- a/cloudpickle/cloudpickle.py +++ b/cloudpickle/cloudpickle.py @@ -631,7 +631,7 @@ def save_dynamic_class(self, obj): # doc can't participate in a cycle with the original class. type_kwargs = {'__doc__': clsdict.pop('__doc__', None)} - if hasattr(obj, "__slots__"): + if "__slots__" in clsdict: type_kwargs['__slots__'] = obj.__slots__ # pickle string length optimization: member descriptors of obj are # created automatically from obj's __slots__ attribute, no need to diff --git a/cloudpickle/cloudpickle_fast.py b/cloudpickle/cloudpickle_fast.py index dfe554d62..58e0c8f37 100644 --- a/cloudpickle/cloudpickle_fast.py +++ b/cloudpickle/cloudpickle_fast.py @@ -68,7 +68,7 @@ def dumps(obj, protocol=None): def _class_getnewargs(obj): type_kwargs = {} - if hasattr(obj, "__slots__"): + if "__slots__" in obj.__dict__: type_kwargs["__slots__"] = obj.__slots__ __dict__ = obj.__dict__.get('__dict__', None) From 871f832143b4c4438afddbb467a66f63eb755dae Mon Sep 17 00:00:00 2001 From: Pierre Glaser Date: Sat, 26 Oct 2019 17:21:14 +0200 Subject: [PATCH 3/4] MNT update changelog --- CHANGES.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGES.md b/CHANGES.md index 94dcc014f..fddd98147 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,6 +1,9 @@ 1.2.3 ===== +- Fix a bug affecting subclasses of slotted classes. + ([issue #311](https://github.com/cloudpipe/cloudpickle/issues/311)) + 1.2.2 ===== From 649fc5941fbb9ba747de712cbdfd7d333a3ab557 Mon Sep 17 00:00:00 2001 From: Pierre Glaser Date: Sat, 26 Oct 2019 19:44:46 +0200 Subject: [PATCH 4/4] FIX also update class_getstate --- cloudpickle/cloudpickle_fast.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cloudpickle/cloudpickle_fast.py b/cloudpickle/cloudpickle_fast.py index 58e0c8f37..60678a434 100644 --- a/cloudpickle/cloudpickle_fast.py +++ b/cloudpickle/cloudpickle_fast.py @@ -143,7 +143,7 @@ def _class_getstate(obj): (registry, _, _, _) = abc._get_dump(obj) clsdict["_abc_impl"] = [subclass_weakref() for subclass_weakref in registry] - if hasattr(obj, "__slots__"): + if "__slots__" in clsdict: # pickle string length optimization: member descriptors of obj are # created automatically from obj's __slots__ attribute, no need to # save them in obj's state