Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,12 @@
In development
==============

3.1.2
=====

- Fix pickling of classes containing type annotations for Python 3.14
([PR#578](https://github.com/cloudpipe/cloudpickle/pull/578))

3.1.1
=====

Expand Down
10 changes: 10 additions & 0 deletions cloudpickle/cloudpickle.py
Original file line number Diff line number Diff line change
Expand Up @@ -783,6 +783,12 @@ def _class_getstate(obj):

clsdict.pop("__dict__", None) # unpicklable property object

if sys.version_info >= (3, 14):
# PEP-649/749: __annotate_func__ contains a closure that references the class
# dict. We need to exclude it from pickling. Python will recreate it when
# __annotations__ is accessed at unpickling time.
clsdict.pop("__annotate_func__", None)

return (clsdict, {})


Expand Down Expand Up @@ -1190,6 +1196,10 @@ def _class_setstate(obj, state):
for subclass in registry:
obj.register(subclass)

# PEP-649/749: During pickling, we excluded the __annotate_func__ attribute but it
# will be created by Python. Subsequently, annotations will be recreated when
# __annotations__ is accessed.

return obj


Expand Down
16 changes: 16 additions & 0 deletions tests/cloudpickle_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -2672,6 +2672,22 @@ class C:
C1 = pickle_depickle(C, protocol=self.protocol)
assert C1.__annotations__ == C.__annotations__

def test_class_annotations_abstractclass(self):
# see https://github.com/cloudpipe/cloudpickle/issues/572

class C(abc.ABC):
a: int

C1 = pickle_depickle(C, protocol=self.protocol)
assert C1.__annotations__ == C.__annotations__
C2 = pickle_depickle(C1, protocol=self.protocol)
if sys.version_info >= (3, 14):
# check that __annotate_func__ is created by Python
assert hasattr(C2, "__annotate_func__")
assert C2.__annotations__ == C1.__annotations__
c2 = C2()
assert isinstance(c2, C2)

def test_function_annotations(self):
def f(a: int) -> str:
pass
Expand Down