Skip to content

bug(skore-hub-project): Error when using pydantic 2.12 and Python 3.14 #2115

@glemaitre

Description

@glemaitre

What would you like to say?

While trying to push an EstimatorReport on the development hub I got some issues.

I think that it should be a small reproducer:

from sklearn.datasets import fetch_openml
from sklearn.preprocessing import LabelEncoder
from skrub import tabular_pipeline
from skore import train_test_split

X, y = fetch_openml("adult", version=2, as_frame=True, return_X_y=True)
y_encoded = LabelEncoder().fit_transform(y)
split_data = train_test_split(X, y_encoded, random_state=1, as_dict=True)
project.put("report", EstimatorReport(tabular_pipeline("classifier"), **split_data))

The traceback was something like:

---------------------------------------------------------------------------
PydanticUserError                         Traceback (most recent call last)
Cell In[20], line 1
----> 1 project.put("lr", lr_report)
      2 project.put("rf", rf_report)
      3 project.put("hgbdt", hgbdt_report)

File ~/Documents/packages/python-stack/skore/skore/skore/src/skore/project/project.py:236, in Project.put(self, key, report)
    230 if not isinstance(report, EstimatorReport | CrossValidationReport):
    231     raise TypeError(
    232         f"Report must be `EstimatorReport` or `CrossValidationReport` "
    233         f"(found '{type(report)}')"
    234     )
--> 236 return self.__project.put(key=key, report=report)

File ~/Documents/packages/python-stack/skore-hub-project/skore-hub-project/skore-hub-project/src/skore_hub_project/project/project.py:56, in ensure_project_is_created.<locals>.wrapper(project, *args, **kwargs)
     50         hub_client.post(
     51             f"projects/{project.quoted_tenant}/{project.quoted_name}"
     52         )
     54     project.created = True
---> 56 return method(project, *args, **kwargs)

File ~/Documents/packages/python-stack/skore-hub-project/skore-hub-project/skore-hub-project/src/skore_hub_project/project/project.py:162, in Project.put(self, key, report)
    142 @ensure_project_is_created
    143 def put(self, key: str, report: EstimatorReport | CrossValidationReport):
    144     """
    145     Put a key-report pair to the hub project.
    146 
   (...)    160         If the combination of parameters are not valid.
    161     """
--> 162     from ..report import CrossValidationReportPayload, EstimatorReportPayload
    164     if not isinstance(key, str):
    165         raise TypeError(f"Key must be a string (found '{type(key)}')")

File ~/Documents/packages/python-stack/skore-hub-project/skore-hub-project/skore-hub-project/src/skore_hub_project/report/__init__.py:3
      1 """Class definitions of the payloads used to send a report to ``hub``."""
----> 3 from .cross_validation_report import CrossValidationReportPayload
      4 from .estimator_report import EstimatorReportPayload
      6 __all__ = [
      7     "CrossValidationReportPayload",
      8     "EstimatorReportPayload",
      9 ]

File ~/Documents/packages/python-stack/skore-hub-project/skore-hub-project/skore-hub-project/src/skore_hub_project/report/cross_validation_report.py:14
     12 from skore_hub_project.artifact.media.data import TableReport
     13 from skore_hub_project.artifact.media.media import Media
---> 14 from skore_hub_project.metric import (
     15     AccuracyTestMean,
     16     AccuracyTestStd,
     17     AccuracyTrainMean,
     18     AccuracyTrainStd,
     19     BrierScoreTestMean,
     20     BrierScoreTestStd,
     21     BrierScoreTrainMean,
     22     BrierScoreTrainStd,
     23     FitTimeMean,
     24     FitTimeStd,
     25     LogLossTestMean,
     26     LogLossTestStd,
     27     LogLossTrainMean,
     28     LogLossTrainStd,
     29     PrecisionTestMean,
     30     PrecisionTestStd,
     31     PrecisionTrainMean,
     32     PrecisionTrainStd,
     33     PredictTimeTestMean,
     34     PredictTimeTestStd,
     35     PredictTimeTrainMean,
     36     PredictTimeTrainStd,
     37     R2TestMean,
     38     R2TestStd,
     39     R2TrainMean,
     40     R2TrainStd,
     41     RecallTestMean,
     42     RecallTestStd,
     43     RecallTrainMean,
     44     RecallTrainStd,
     45     RmseTestMean,
     46     RmseTestStd,
     47     RmseTrainMean,
     48     RmseTrainStd,
     49     RocAucTestMean,
     50     RocAucTestStd,
     51     RocAucTrainMean,
     52     RocAucTrainStd,
     53 )
     54 from skore_hub_project.metric.metric import CrossValidationReportMetric
     55 from skore_hub_project.metric.timing import FitTimeAggregate, PredictTimeAggregate

File ~/Documents/packages/python-stack/skore-hub-project/skore-hub-project/skore-hub-project/src/skore_hub_project/metric/__init__.py:3
      1 """Class definitions of the payloads used to send a metric to ``hub``."""
----> 3 from .accuracy import (
      4     AccuracyTest,
      5     AccuracyTestMean,
      6     AccuracyTestStd,
      7     AccuracyTrain,
      8     AccuracyTrainMean,
      9     AccuracyTrainStd,
     10 )
     11 from .brier_score import (
     12     BrierScoreTest,
     13     BrierScoreTestMean,
   (...)     17     BrierScoreTrainStd,
     18 )
     19 from .log_loss import (
     20     LogLossTest,
     21     LogLossTestMean,
   (...)     25     LogLossTrainStd,
     26 )

File ~/Documents/packages/python-stack/skore-hub-project/skore-hub-project/skore-hub-project/src/skore_hub_project/metric/accuracy.py:10
      5 from typing import ClassVar, Literal
      7 from .metric import CrossValidationReportMetric, EstimatorReportMetric
---> 10 class Accuracy(EstimatorReportMetric):  # noqa: D101
     11     accessor: ClassVar[Literal["metrics.accuracy"]] = "metrics.accuracy"
     12     name: Literal["accuracy"] = "accuracy"

File ~/Documents/packages/python-stack/skore/.pixi/envs/dev/lib/python3.14/site-packages/pydantic/_internal/_model_construction.py:242, in ModelMetaclass.__new__(mcs, cls_name, bases, namespace, __pydantic_generic_metadata__, __pydantic_reset_parent_namespace__, _create_model_module, **kwargs)
    238     parent_namespace = unpack_lenient_weakvaluedict(parent_namespace)
    240 ns_resolver = NsResolver(parent_namespace=parent_namespace)
--> 242 set_model_fields(cls, config_wrapper=config_wrapper, ns_resolver=ns_resolver)
    244 # This is also set in `complete_model_class()`, after schema gen because they are recreated.
    245 # We set them here as well for backwards compatibility:
    246 cls.__pydantic_computed_fields__ = {
    247     k: v.info for k, v in cls.__pydantic_decorators__.computed_fields.items()
    248 }

File ~/Documents/packages/python-stack/skore/.pixi/envs/dev/lib/python3.14/site-packages/pydantic/_internal/_model_construction.py:566, in set_model_fields(cls, config_wrapper, ns_resolver)
    558 """Collect and set `cls.__pydantic_fields__` and `cls.__class_vars__`.
    559 
    560 Args:
   (...)    563     ns_resolver: Namespace resolver to use when getting model annotations.
    564 """
    565 typevars_map = get_model_typevars_map(cls)
--> 566 fields, class_vars = collect_model_fields(cls, config_wrapper, ns_resolver, typevars_map=typevars_map)
    568 cls.__pydantic_fields__ = fields
    569 cls.__class_vars__.update(class_vars)

File ~/Documents/packages/python-stack/skore/.pixi/envs/dev/lib/python3.14/site-packages/pydantic/_internal/_fields.py:364, in collect_model_fields(cls, config_wrapper, ns_resolver, typevars_map)
    361     assigned_value.default = default
    362     assigned_value._attributes_set['default'] = default
--> 364 field_info = FieldInfo_.from_annotated_attribute(ann_type, assigned_value, _source=AnnotationSource.CLASS)
    365 # Store the original annotation and assignment value that should be used to rebuild the field info later.
    366 # Note that the assignment is always stored as the annotation might contain a type var that is later
    367 #  parameterized with an unknown forward reference (and we'll need it to rebuild the field info):
    368 field_info._original_assignment = assigned_value

File ~/Documents/packages/python-stack/skore/.pixi/envs/dev/lib/python3.14/site-packages/pydantic/fields.py:382, in FieldInfo.from_annotated_attribute(annotation, default, _source)
    357 """Create `FieldInfo` from an annotation with a default value.
    358 
    359 This is used in cases like the following:
   (...)    379     A field object with the passed values.
    380 """
    381 if annotation is not MISSING and annotation is default:
--> 382     raise PydanticUserError(
    383         'Error when building FieldInfo from annotated attribute. '
    384         "Make sure you don't have any field name clashing with a type annotation.",
    385         code='unevaluable-type-annotation',
    386     )
    388 try:
    389     inspected_ann = inspect_annotation(
    390         annotation,
    391         annotation_source=_source,
    392         unpack_type_aliases='skip',
    393     )

PydanticUserError: Error when building FieldInfo from annotated attribute. Make sure you don't have any field name clashing with a type annotation.

By downgrading to pydantic 2.11 as we have in the CI, I don't have the issue anymore. I was using pydantic 2.12 with Python 3.14.

System:
    python: 3.14.0 | packaged by conda-forge | (main, Oct  7 2025, 20:20:06) [Clang 20.1.8 ]
executable: /Users/glemaitre/Documents/packages/python-stack/skore/.pixi/envs/dev/bin/python
   machine: macOS-15.6.1-arm64-arm-64bit-Mach-O

Python dependencies:
        skore: 0.0.0+unknown
          pip: None
    anywidget: 0.9.18
      ipython: 9.6.0
   ipywidgets: 8.1.7
       joblib: 1.5.2
   matplotlib: 3.10.6
        numpy: 2.3.3
       pandas: 2.3.3
       plotly: 6.3.1
         rich: 14.1.0
 scikit-learn: 1.8.dev0
      seaborn: 0.13.2
 skore[local]: None
        skrub: 0.7.dev0
skore-hub-project: 0.0.0+unknown

It could be cool to investigate the failure.

Metadata

Metadata

Assignees

Labels

bug 🐛Something isn't workingneeds Investigation 🔎Requires investigating the issue to know if we should go further with the idea

Type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions