diff --git a/CHANGES.md b/CHANGES.md index 79043afe4..5a2fe96a5 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,6 +1,10 @@ 1.2.3 ===== +- Fix a regression in cloudpickle and python3.8 causing an error when trying to + pickle property objects. + ([PR #329](https://github.com/cloudpipe/cloudpickle/pull/329)). + - Fix a bug when a thread imports a module while cloudpickle iterates over the module list ([PR #322](https://github.com/cloudpipe/cloudpickle/pull/322)). diff --git a/cloudpickle/cloudpickle_fast.py b/cloudpickle/cloudpickle_fast.py index dfe554d62..a4a270385 100644 --- a/cloudpickle/cloudpickle_fast.py +++ b/cloudpickle/cloudpickle_fast.py @@ -291,6 +291,10 @@ def _root_logger_reduce(obj): return logging.getLogger, () +def _property_reduce(obj): + return property, (obj.fget, obj.fset, obj.fdel, obj.__doc__) + + def _weakset_reduce(obj): return weakref.WeakSet, (list(obj),) @@ -406,6 +410,7 @@ class CloudPickler(Pickler): dispatch[logging.Logger] = _logger_reduce dispatch[logging.RootLogger] = _root_logger_reduce dispatch[memoryview] = _memoryview_reduce + dispatch[property] = _property_reduce dispatch[staticmethod] = _classmethod_reduce dispatch[types.CellType] = _cell_reduce dispatch[types.CodeType] = _code_reduce diff --git a/tests/cloudpickle_test.py b/tests/cloudpickle_test.py index 8bd210abe..9c1f1c355 100644 --- a/tests/cloudpickle_test.py +++ b/tests/cloudpickle_test.py @@ -1119,6 +1119,50 @@ def func(x): cloned = pickle_depickle(func, protocol=self.protocol) self.assertEqual(cloned.__qualname__, func.__qualname__) + def test_property(self): + class MyObject: + _read_only_value = 1 + _read_write_value = 1 + + @property + def read_only_value(self): + "A read-only attribute" + return self._read_only_value + + @property + def read_write_value(self): + return self._read_write_value + + @read_write_value.setter + def read_write_value(self, value): + self._read_write_value = value + + + + my_object = MyObject() + + assert my_object.read_only_value == 1 + assert MyObject.read_only_value.__doc__ == "A read-only attribute" + + with pytest.raises(AttributeError): + my_object.read_only_value = 2 + my_object.read_write_value = 2 + + depickled_obj = pickle_depickle(my_object) + + assert depickled_obj.read_only_value == 1 + assert depickled_obj.read_write_value == 2 + + # make sure the depickled read_only_value attribute is still read-only + with pytest.raises(AttributeError): + my_object.read_only_value = 2 + + # make sure the depickled read_write_value attribute is writeable + depickled_obj.read_write_value = 3 + assert depickled_obj.read_write_value == 3 + type(depickled_obj).read_only_value.__doc__ == "A read-only attribute" + + def test_namedtuple(self): MyTuple = collections.namedtuple('MyTuple', ['a', 'b', 'c']) t1 = MyTuple(1, 2, 3)