diff --git a/CHANGES.md b/CHANGES.md index 65f61d451..5eb9a3350 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,6 +1,12 @@ 1.2.2 ===== +- Revert the change introduced in + ([issue #276](https://github.com/cloudpipe/cloudpickle/pull/276)) + attempting to pickle functions annotations for Python 3.4 to 3.6. It is not + possible to pickle complex typing constructs for those versions (see + [issue #193]( https://github.com/cloudpipe/cloudpickle/issues/193)) + - Fix a bug affecting bound classmethod saving on Python 2. ([issue #288](https://github.com/cloudpipe/cloudpickle/issues/288)) diff --git a/README.md b/README.md index 2eed9717e..7c9e0dd48 100644 --- a/README.md +++ b/README.md @@ -85,6 +85,25 @@ Running the tests PYTHONPATH='.:tests' py.test +Note about function Annotations +------------------------------- + +Note that because of design issues `Python`'s `typing` module, `cloudpickle` +supports pickling type annotations of dynamic functions for `Python` 3.7 and +later. On `Python` 3.4, 3.5 and 3.6, those type annotations will be dropped +silently during pickling (example below): + +```python +>>> import typing +>>> import cloudpickle +>>> def f(x: typing.Union[list, int]): +... return x +>>> f + +>>> cloudpickle.loads(cloudpickle.dumps(f)) # drops f's annotations + +``` + History ------- diff --git a/cloudpickle/cloudpickle.py b/cloudpickle/cloudpickle.py index c92b2eac4..1d4c85cbe 100644 --- a/cloudpickle/cloudpickle.py +++ b/cloudpickle/cloudpickle.py @@ -747,7 +747,9 @@ def save_function_tuple(self, func): 'doc': func.__doc__, '_cloudpickle_submodules': submodules } - if hasattr(func, '__annotations__') and sys.version_info >= (3, 4): + if hasattr(func, '__annotations__') and sys.version_info >= (3, 7): + # Although annotations were added in Python3.4, It is not possible + # to properly pickle them until Python3.7. (See #193) state['annotations'] = func.__annotations__ if hasattr(func, '__qualname__'): state['qualname'] = func.__qualname__ diff --git a/tests/cloudpickle_test.py b/tests/cloudpickle_test.py index ec1d089bb..144635930 100644 --- a/tests/cloudpickle_test.py +++ b/tests/cloudpickle_test.py @@ -1621,9 +1621,9 @@ def g(): self.assertEqual(f2.__doc__, f.__doc__) - @unittest.skipIf(sys.version_info < (3, 4), - """This syntax won't work on py2 and pickling annotations - isn't supported for py34 and below.""") + @unittest.skipIf(sys.version_info < (3, 7), + "This syntax won't work on py2 and pickling annotations " + "isn't supported for py37 and below.") def test_wraps_preserves_function_annotations(self): from functools import wraps @@ -1640,6 +1640,16 @@ def g(x): self.assertEqual(f2.__annotations__, f.__annotations__) + @unittest.skipIf(sys.version_info < (3, 7), + """This syntax won't work on py2 and pickling annotations + isn't supported for py37 and below.""") + def test_type_hint(self): + # Try to pickle compound typing constructs. This would typically fail + # on Python < 3.7 (See #193) + import typing + t = typing.Union[list, int] + assert pickle_depickle(t) == t + def test_instance_with_slots(self): for slots in [["registered_attribute"], "registered_attribute"]: class ClassWithSlots(object):