diff --git a/cloudpickle/cloudpickle.py b/cloudpickle/cloudpickle.py index 4f4d857a..5a74cce5 100644 --- a/cloudpickle/cloudpickle.py +++ b/cloudpickle/cloudpickle.py @@ -751,6 +751,14 @@ def _class_getstate(obj): clsdict = _extract_class_dict(obj) clsdict.pop("__weakref__", None) + # In Python 3.13+, classes with annotations have an __annotate__ function + # that may have closures referencing unpicklable objects in the class + # namespace. We don't need to pickle it as Python will regenerate it from + # __annotations__ when the class is reconstructed. + clsdict.pop("__annotate__", None) + # Also handle __annotate_func__ which may appear in some Python 3.14 builds + clsdict.pop("__annotate_func__", None) + if issubclass(type(obj), abc.ABCMeta): # If obj is an instance of an ABCMeta subclass, don't pickle the # cache/negative caches populated during isinstance/issubclass diff --git a/tests/cloudpickle_test.py b/tests/cloudpickle_test.py index 45e8c592..7640cf4e 100644 --- a/tests/cloudpickle_test.py +++ b/tests/cloudpickle_test.py @@ -1429,6 +1429,33 @@ def some_method(self): self.assertRaises(TypeError, IncompleteBaseSubclass) + def test_abc_with_annotations(self): + # Test pickling abstract classes with type annotations. + # This is a regression test for Python 3.13+ (PEP 649) where __annotate__ + # functions can have closures that reference _abc_impl. + + def class_factory(): + class Model(abc.ABC): + field1: int + field2: str + field3: float + + return Model + + Model = class_factory() + + # This should not raise TypeError about unpicklable _abc._abc_data + depickled = pickle_depickle(Model, protocol=self.protocol) + + # Verify the annotations are preserved + self.assertEqual( + depickled.__annotations__, + {"field1": int, "field2": str, "field3": float}, + ) + + # Verify it's still an ABC + self.assertTrue(issubclass(depickled, abc.ABC)) + def test_weakset_identity_preservation(self): # Test that weaksets don't lose all their inhabitants if they're # pickled in a larger data structure that includes other references to