diff --git a/CHANGES.md b/CHANGES.md index 199326091..65f61d451 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -4,6 +4,9 @@ - Fix a bug affecting bound classmethod saving on Python 2. ([issue #288](https://github.com/cloudpipe/cloudpickle/issues/288)) +- Add support for pickling "getset" descriptors + ([issue #290](https://github.com/cloudpipe/cloudpickle/pull/290)) + 1.2.1 ===== diff --git a/cloudpickle/cloudpickle.py b/cloudpickle/cloudpickle.py index ad0f5bb58..c92b2eac4 100644 --- a/cloudpickle/cloudpickle.py +++ b/cloudpickle/cloudpickle.py @@ -848,6 +848,11 @@ def save_builtin_function_or_method(self, obj): method_descriptor = type(str.upper) dispatch[method_descriptor] = save_builtin_function_or_method + def save_getset_descriptor(self, obj): + return self.save_reduce(getattr, (obj.__objclass__, obj.__name__)) + + dispatch[types.GetSetDescriptorType] = save_getset_descriptor + def save_global(self, obj, name=None, pack=struct.pack): """ Save a "global". diff --git a/cloudpickle/cloudpickle_fast.py b/cloudpickle/cloudpickle_fast.py index 54421edb8..dfe554d62 100644 --- a/cloudpickle/cloudpickle_fast.py +++ b/cloudpickle/cloudpickle_fast.py @@ -260,6 +260,10 @@ def _file_reduce(obj): return _file_reconstructor, (retval,) +def _getset_descriptor_reduce(obj): + return getattr, (obj.__objclass__, obj.__name__) + + def _mappingproxy_reduce(obj): return types.MappingProxyType, (dict(obj),) @@ -405,6 +409,7 @@ class CloudPickler(Pickler): dispatch[staticmethod] = _classmethod_reduce dispatch[types.CellType] = _cell_reduce dispatch[types.CodeType] = _code_reduce + dispatch[types.GetSetDescriptorType] = _getset_descriptor_reduce dispatch[types.ModuleType] = _module_reduce dispatch[types.MethodType] = _method_reduce dispatch[types.MappingProxyType] = _mappingproxy_reduce diff --git a/tests/cloudpickle_test.py b/tests/cloudpickle_test.py index 75287f6e6..ec1d089bb 100644 --- a/tests/cloudpickle_test.py +++ b/tests/cloudpickle_test.py @@ -984,6 +984,11 @@ def test_logger(self): # logging.Logger object self.check_logger('cloudpickle.dummy_test_logger') + def test_getset_descriptor(self): + assert isinstance(float.real, types.GetSetDescriptorType) + depickled_descriptor = pickle_depickle(float.real) + self.assertIs(depickled_descriptor, float.real) + def test_abc(self): @abc.abstractmethod