Skip to content

TypeError: cannot pickle '_abc._abc_data' object with python 3.14 and cloudpickle 3.1.2 #585

Description

@thomass-dev

Following #572.

This snippet raises cloudpicke exception:

# python==3.14
# skore==0.11.4
# scikit-learn==1.8.0
# cloudpickle==3.1.2

from cloudpickle import dumps
from sklearn.datasets import make_regression
from sklearn.linear_model import LinearRegression
from sklearn.model_selection import train_test_split
from skore import EstimatorReport, set_config
from skore._utils._parallel import _FuncWrapper

set_config(show_progress=False)

X, y = make_regression(random_state=42)
X_train, X_test, y_train, y_test = train_test_split(X, y, random_state=42)
report = EstimatorReport(
    LinearRegression(),
    X_train=X_train,
    y_train=y_train,
    X_test=X_test,
    y_test=y_test,
)

dumps(_FuncWrapper(report.metrics.summarize))
TypeError: cannot pickle '_abc._abc_data' object
when serializing dict item '_abc_impl'
when serializing tuple item 0
when serializing cell reconstructor arguments
when serializing cell object
when serializing tuple item 0
when serializing dict item '__closure__'
when serializing tuple item 1
when serializing function state
when serializing function object
when serializing dict item '__annotate__'
when serializing skore._utils._parallel._FuncWrapper state
when serializing skore._utils._parallel._FuncWrapper object

Activity

  1. thomass-dev commented on Dec 16, 2025

    @thomass-dev
    Author

    I'm trying to reduce the reproducer, to be used without skore.

  2. changed the title [-]Python==3.14 / cloudpickle==3.1.2 : unable to pickle `TypeError: cannot pickle '_abc._abc_data' object`[/-] [+]Python==3.14 / cloudpickle==3.1.2 : unable to pickle[/+] on Dec 16, 2025
  3. changed the title [-]Python==3.14 / cloudpickle==3.1.2 : unable to pickle[/-] [+]Python==3.14 / cloudpickle==3.1.2: unable to pickle[/+] on Dec 16, 2025
  4. changed the title [-]Python==3.14 / cloudpickle==3.1.2: unable to pickle[/-] [+]python==3.14/cloudpickle==3.1.2: unable to pickle[/+] on Dec 16, 2025
  5. lesteve commented on Dec 16, 2025

    @lesteve
    Contributor

    Thanks I can reproduce with skore, my guess is that this is a variation of #572 indeed. Compare to #572 the function mentioned in the error message is __annotate__ rather than __annotate_func__, not sure what to make of this right now.

    #578 may have some relevant information as well.

  6. lesteve commented on Dec 16, 2025

    @lesteve
    Contributor

    I had a closer look, you can reproduce with a simpler _FuncWrapper:

    from functools import update_wrapper
    
    class _FuncWrapper:
        def __init__(self, function):
            self.function = function
            # you need the following line with update_wrapper to trigger the issue
            update_wrapper(self, self.function)

    I am guessing the issue is a combination of the _FuncWrapper + report.metrics.summarize side (without _FuncWrapper report.metrics.summarize works fine, trying _FuncWrapper with a simple function with type annotations doesn't reproduce the issue).

    Looking at the skore code it seems to mix descriptors + advanced typing, so the key for a reproducer without skore lies in understanding what combinations of advanced functionalities create the issue. I tried a few things but could not find the exact combination ...

  7. changed the title [-]python==3.14/cloudpickle==3.1.2: unable to pickle[/-] [+]`TypeError: cannot pickle '_abc._abc_data' object` with python 3.14 and cloudpickle 3.1.2[/+] on Dec 22, 2025
  8. ogrisel commented on Dec 22, 2025

    @ogrisel
    Contributor

    Thanks for the skore-based reproducer and the initial investigation. However, I wonder if the cause of the problem comes from cloudpickle itself: I don't see any dynamic class or function in the reproducer, so I don't see why cloudpickle-specific features would be needed for that case.

    Indeed, I can reproduce a similar problem with the dumps function of the pickle module of CPython 3.14:

    Details
    from pickle import dumps
    from sklearn.datasets import make_regression
    from sklearn.linear_model import LinearRegression
    from sklearn.model_selection import train_test_split
    from skore import EstimatorReport, set_config
    from skore._utils._parallel import _FuncWrapper
    
    set_config(show_progress=False)
    
    X, y = make_regression(random_state=42)
    X_train, X_test, y_train, y_test = train_test_split(X, y, random_state=42)
    report = EstimatorReport(
        LinearRegression(),
        X_train=X_train,
        y_train=y_train,
        X_test=X_test,
        y_test=y_test,
    )
    
    dumps(_FuncWrapper(report.metrics.summarize))
    ---------------------------------------------------------------------------
    PicklingError                             Traceback (most recent call last)
    Cell In[4], line 22
         13 X_train, X_test, y_train, y_test = train_test_split(X, y, random_state=42)
         14 report = EstimatorReport(
         15     LinearRegression(),
         16     X_train=X_train,
       (...)     19     y_test=y_test,
         20 )
    ---> 22 dumps(_FuncWrapper(report.metrics.summarize))
    
    PicklingError: Can't pickle <function _MetricsAccessor.__annotate__ at 0x30a1a8880>: it's not the same object as skore._sklearn._estimator.metrics_accessor._MetricsAccessor.__annotate__
    when serializing dict item '__annotate__'
    when serializing skore._utils._parallel._FuncWrapper state
    when serializing skore._utils._parallel._FuncWrapper object

    I checked, and that same code works with Python 3.13.

    So maybe there is something wrong with either the _MetricsAccessor class definition, or _FuncWrapper or CPython 3.14 annotations.

  9. ogrisel commented on Dec 22, 2025

    @ogrisel
    Contributor

    I would be great to craft a reproducer that only depends on CPython and its standard library.

  10. ogrisel commented on Dec 22, 2025

    @ogrisel
    Contributor

    Here is a more minimal reproducer:

    # %%
    %%writefile helper_module.py
    from functools import update_wrapper
    
    class _FuncWrapper:
        def __init__(self, function):
            self.function = function
            update_wrapper(self, self.function)
    
    
    def some_function(x: str) -> str:
        return x
    
    # %%
    from helper_module import some_function, _FuncWrapper
    from pickle import dumps
    
    dumps(some_function)
    dumps(_FuncWrapper(some_function))
    ---------------------------------------------------------------------------
    PicklingError                             Traceback (most recent call last)
    Cell In[4], line 6
          3 from pickle import dumps
          5 dumps(some_function)
    ----> 6 dumps(_FuncWrapper(some_function))
    
    PicklingError: Can't pickle <function __annotate__ at 0x121cade80>: it's not the same object as helper_module.__annotate__
    when serializing dict item '__annotate__'
    when serializing helper_module._FuncWrapper state
    when serializing helper_module._FuncWrapper object

    The first call to dumps works fine but the second fails. So I suspect that the wrapper needs to do something special to be picklable to deal with annotated callables on Python 3.14.

  11. ogrisel commented on Dec 22, 2025

    @ogrisel
    Contributor

    I reported a variant of the above minimal reproducer upstream: python/cpython#143067

  12. lesteve commented on Dec 22, 2025

    @lesteve
    Contributor

    The same snippet as python/cpython#143067 works fine with cloudpickle.dumps though, right?

    from functools import update_wrapper
    import pickle
    import cloudpickle
    
    class _FuncWrapper:
        def __init__(self, function):
            self.function = function
            update_wrapper(self, self.function)
    
    
    def some_function_without_annotations(x):
        return x
    
    
    def some_function_with_annotations(x: str) -> str:
        return x
    
    
    pickle.dumps(some_function_with_annotations)  # ok
    pickle.dumps(_FuncWrapper(some_function_without_annotations))  # ok
    cloudpickle.dumps(_FuncWrapper(some_function_with_annotations))  # ok
    pickle.dumps(_FuncWrapper(some_function_with_annotations))  # error
  13. ogrisel commented on Dec 22, 2025

    @ogrisel
    Contributor

    Indeed. Things might be more complicated then...

    Still I think it's worth investigating what's needed to restore regular picklability for the minimal reproducer with CPython 3.14 before debugging cloudpickle specific things on the more complex setting. Maybe solving the simple case will also solve the complex case.

  14. auguste-probabl commented on Apr 16, 2026

    @auguste-probabl

    May be closed by python/cpython#137814

  15. ogrisel commented on Apr 16, 2026

    @ogrisel
    Contributor

    We need to test against a local buid or nightly build of both the Python 3.14 and the 3.15 branches.

  16. thomass-dev commented on Apr 16, 2026

    @thomass-dev
    Author

    @ogrisel sounds good on our side with python==3.14.4 probabl-ai/skore#2771.

    Edit: but the minimal reproducer continues to fail.

  17. BioGeek commented on Jun 8, 2026

    @BioGeek

    I created a PR with a fix: #594

  18. ogrisel commented on Jul 2, 2026

    @ogrisel
    Contributor

    I built Python's main branch and the reproducer of #585 (comment) no longer fails.

  19. ogrisel commented on Jul 2, 2026

    @ogrisel
    Contributor

    I also tried with Python 3.14.6 and the reproducer passes as well (while it fails on 3.14.4).

  20. ogrisel commented on Jul 2, 2026

    @ogrisel
    Contributor

    The test case of #594 fails with 3.14.6 though:

    import abc
    import functools
    import cloudpickle
    
    class FuncWrapper:
        def __init__(self, function):
            self.function = function
            functools.update_wrapper(self, self.function)
    
        def __call__(self, *args, **kwargs):
            return self.function(*args, **kwargs)
    
    class AbstractClass(abc.ABC):
        a: int
    
        def method(self, arg: str) -> str:
            return arg.upper()
    
    wrapped = FuncWrapper(AbstractClass().method)
    cloudpickle.dumps(wrapped)
    TypeError: cannot pickle '_abc._abc_data' object
    when serializing dict item '_abc_impl'
    when serializing tuple item 0
    when serializing cell reconstructor arguments
    when serializing cell object
    when serializing tuple item 0
    when serializing dict item '__closure__'
    when serializing tuple item 1
    when serializing function state
    when serializing function object
    when serializing dict item '__annotate__'
    when serializing FuncWrapper state
    when serializing FuncWrapper object
    
  21. BioGeek commented on Jul 2, 2026

    @BioGeek

    @ogrisel I updated my PR #594 and validated that it now works on Python 3.14.4, 3.14.6 and 3.15.0a7.

  22. ogrisel commented on Jul 3, 2026

    @ogrisel
    Contributor

    Thanks @BioGeek. Before proceeding with the review, I am pondering whether this can still be considered by a regression in CPython or not. If so we should try to report a minimal reproducer upstream and maybe consider #594 as a temporary workaround rather than a definitive fix.

  23. ogrisel commented on Jul 3, 2026

    @ogrisel
    Contributor

    After taking a deeper look, I don't think that the problem that remains can be considered's a regression in CPython but I am not sure what's the cleanest way to deal with it either.

  24. j-adamczyk commented on Sep 18, 2026

    @j-adamczyk

    @ogrisel could you please consider merging this temporary fix PR? It would enable us to dump scikit-fingerprints to support Python 3.14, as we depend on cloudpickle. Python 3.15 will be released soon, and being 2 versions behind starts to become inconvenient.

  25. BioGeek commented on Sep 23, 2026

    @BioGeek

    @ogrisel I opened #603 as an alternative fix for this issue.

    Part of this is already fixed upstream. The root cause is CPython gh-137814. Fixed in 3.14.5 by gh-148221 and on main by gh-137842. On 3.14.5+ the module-level reproducer of this issue already works with cloudpickle master. @j-adamczyk that may well unblock scikit-fingerprints today, without waiting for a cloudpickle release. What still fails on every 3.14+ build is the case where the annotated class itself is pickled by value (defined in __main__, in a notebook, or otherwise dynamic).

    Test suite is green on 3.12, 3.14.4, 3.14.6 and 3.15.0b3

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions