Skip to content

Commit 6ff1cc2

Browse files
authored
FIX remove __annotate_func__ that return __dict__ within a closure Python 3.14 (#578)
1 parent e8bbede commit 6ff1cc2

File tree

3 files changed

+32
-0
lines changed

3 files changed

+32
-0
lines changed

CHANGES.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,12 @@
11
In development
22
==============
33

4+
3.1.2
5+
=====
6+
7+
- Fix pickling of classes containing type annotations for Python 3.14
8+
([PR#578](https://github.com/cloudpipe/cloudpickle/pull/578))
9+
410
3.1.1
511
=====
612

cloudpickle/cloudpickle.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -783,6 +783,12 @@ def _class_getstate(obj):
783783

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

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

788794

@@ -1190,6 +1196,10 @@ def _class_setstate(obj, state):
11901196
for subclass in registry:
11911197
obj.register(subclass)
11921198

1199+
# PEP-649/749: During pickling, we excluded the __annotate_func__ attribute but it
1200+
# will be created by Python. Subsequently, annotations will be recreated when
1201+
# __annotations__ is accessed.
1202+
11931203
return obj
11941204

11951205

tests/cloudpickle_test.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2672,6 +2672,22 @@ class C:
26722672
C1 = pickle_depickle(C, protocol=self.protocol)
26732673
assert C1.__annotations__ == C.__annotations__
26742674

2675+
def test_class_annotations_abstractclass(self):
2676+
# see https://github.com/cloudpipe/cloudpickle/issues/572
2677+
2678+
class C(abc.ABC):
2679+
a: int
2680+
2681+
C1 = pickle_depickle(C, protocol=self.protocol)
2682+
assert C1.__annotations__ == C.__annotations__
2683+
C2 = pickle_depickle(C1, protocol=self.protocol)
2684+
if sys.version_info >= (3, 14):
2685+
# check that __annotate_func__ is created by Python
2686+
assert hasattr(C2, "__annotate_func__")
2687+
assert C2.__annotations__ == C1.__annotations__
2688+
c2 = C2()
2689+
assert isinstance(c2, C2)
2690+
26752691
def test_function_annotations(self):
26762692
def f(a: int) -> str:
26772693
pass

0 commit comments

Comments
 (0)