Skip to content
Draft
Show file tree
Hide file tree
Changes from 17 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
15 changes: 2 additions & 13 deletions .github/workflows/lint.yml
Original file line number Diff line number Diff line change
Expand Up @@ -29,22 +29,11 @@ jobs:
- name: Setup Python
uses: actions/setup-python@83679a892e2d95755f2dac6acb0bfd1e9ac5d548 # v6.1.0
with:
python-version: "3.12"
python-version: "3.13"
cache: pip

- name: Install dependencies
run: python -m pip install --upgrade pip pre-commit

- name: Lint
run: |
pre-commit run --all-files check-yaml
pre-commit run --all-files check-toml
pre-commit run --all-files check-added-large-files
pre-commit run --all-files check-merge-conflict
pre-commit run --all-files detect-private-key
pre-commit run --all-files end-of-file-fixer
pre-commit run --all-files trailing-whitespace
pre-commit run --all-files typos
pre-commit run --all-files nbstripout
pre-commit run --all-files ruff-check
pre-commit run --all-files mypy
run: pre-commit run --all-files
44 changes: 17 additions & 27 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -15,33 +15,25 @@ repos:
hooks:
- id: typos

- repo: https://github.com/kynan/nbstripout
rev: 0.9.0
hooks:
- id: nbstripout
args: [--keep-id]

- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.14.14
hooks:

- id: ruff-check
files: ^((skore|skore-hub-project|skore-local-project)/(hatch|src|tests))|(examples)/
args: [--fix, --extend-select, I]

- id: ruff-format
files: ^((skore|skore-hub-project|skore-local-project)/(hatch|src|tests))|(examples)/

- repo: https://github.com/pre-commit/mirrors-mypy
rev: v1.19.1
- repo: https://github.com/allganize/ty-pre-commit
rev: v0.0.17 # ty version
hooks:

- id: mypy
- id: ty-check
alias: ty-skore
name: ty skore/
language: python
args: [--project, skore]
pass_filenames: false
alias: mypy-skore
name: mypy skore/
files: ^skore/
args: [--config-file=skore/pyproject.toml, skore]
additional_dependencies:
# from dependencies
- IPython
Expand All @@ -61,18 +53,16 @@ repos:
- pyarrow
- scipy-stubs

- id: mypy
- id: ty-check
alias: ty-skore-local-project
name: ty skore-local-project/
args: [--project, skore-local-project]
pass_filenames: false
alias: mypy-skore-hub-project
name: mypy skore-hub-project/
files: ^skore-hub-project/
args: [--config-file=skore-hub-project/pyproject.toml, skore-hub-project]
additional_dependencies: [skore, blake3, httpx, orjson, pandas-stubs, pydantic]
additional_dependencies: [skore]

- id: mypy
- id: ty-check
alias: ty-skore-hub-project
name: ty skore-hub-project/
args: [--project, skore-hub-project]
pass_filenames: false
alias: mypy-skore-local-project
name: mypy skore-local-project/
files: ^skore-local-project/
args: [--config-file=skore-local-project/pyproject.toml, skore-local-project]
additional_dependencies: [skore]
additional_dependencies: [skore, blake3, httpx, orjson, pandas-stubs, pydantic]
18 changes: 4 additions & 14 deletions skore-hub-project/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -109,18 +109,8 @@ convention = "numpy"
[tool.ruff.lint.per-file-ignores]
"tests/*" = ["D"]

[tool.mypy]
exclude = ["hatch/", "tests/"]
strict = true
Copy link
Collaborator

@thomass-dev thomass-dev Feb 19, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This change has to be discussed, as ty doesn't have a strict mode for now and we would to be strict as much as possible in skore-hub-project and skore-local-project.

https://docs.astral.sh/ty/reference/typing-faq/#does-ty-have-a-strict-mode

Could you please list me the rules that will not be applied with this change?

[tool.ty.src]
exclude = ["hatch/**", "tests/**"]

[[tool.mypy.overrides]]
follow_untyped_imports = true
module = ["skore.*"]

[[tool.mypy.overrides]]
ignore_missing_imports = true
module = [
"joblib.*",
"scipy.*",
"sklearn.*",
]
[tool.ty.terminal]
error-on-warning = true
21 changes: 12 additions & 9 deletions skore-hub-project/src/skore_hub_project/artifact/artifact.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
from abc import ABC, abstractmethod
from contextlib import AbstractContextManager, nullcontext
from functools import cached_property
from typing import cast

from pydantic import BaseModel, ConfigDict, Field, computed_field

Expand Down Expand Up @@ -55,7 +56,7 @@ def content_to_upload(self) -> Generator[str, None, None]:
yield "<str>"
"""

@computed_field # type: ignore[prop-decorator]
@computed_field
@cached_property
def checksum(self) -> str | None:
"""Checksum used to identify the content of the artifact."""
Expand All @@ -65,11 +66,13 @@ def checksum(self) -> str | None:
contextmanager = nullcontext(contextmanager)

with contextmanager as content:
if content is not None:
return upload(
project=self.project,
content=content,
content_type=self.content_type,
)

return None
if content is None:
return None

content = cast(str | bytes, content)

return upload(
project=self.project,
content=content,
content_type=self.content_type,
)
2 changes: 1 addition & 1 deletion skore-hub-project/src/skore_hub_project/metric/metric.py
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ class Metric(BaseModel, ABC, Generic[Report]):
greater_is_better: bool | None = Field(init=False)
position: int | None = Field(init=False)

@computed_field # type: ignore[prop-decorator]
@computed_field
@property
def value(self) -> float | None:
"""The value of the metric."""
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -158,13 +158,13 @@ def model_post_init(self, _: Any) -> None: # noqa: D102
self.__sample_to_class_index = None
self.__classes = None

@computed_field # type: ignore[prop-decorator]
@computed_field
@cached_property
def dataset_size(self) -> int:
"""Size of the dataset."""
return len(self.report.X)

@computed_field # type: ignore[prop-decorator]
@computed_field
@cached_property
def splitting_strategy(self) -> dict[str, Any]:
"""
Expand Down Expand Up @@ -240,21 +240,21 @@ def splitting_strategy(self) -> dict[str, Any]:

groups: list[int] | None = None

@computed_field # type: ignore[prop-decorator]
@computed_field
@property
def class_names(self) -> list[str] | None:
"""In classification, the class names of the dataset used in the report."""
return self.__classes

@computed_field # type: ignore[prop-decorator]
@computed_field
@cached_property
def target_range(self) -> list[float] | None:
"""The range of the target values of the dataset used in the report."""
if self.__classes:
return None
return [float(self.report.y.min()), float(self.report.y.max())]

@computed_field # type: ignore[prop-decorator]
@computed_field
@cached_property
def estimators(self) -> list[EstimatorReportPayload]:
"""The estimators used in each split by the report in a payload format."""
Expand Down
16 changes: 8 additions & 8 deletions skore-hub-project/src/skore_hub_project/report/report.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,20 +53,20 @@ class ReportPayload(BaseModel, ABC, Generic[Report]):

model_config = ConfigDict(frozen=True, arbitrary_types_allowed=True)

METRICS: ClassVar[tuple[type[Metric[Report]], ...]]
MEDIAS: ClassVar[tuple[type[Media[Report]], ...]]
METRICS: ClassVar[tuple[type[Metric], ...]]
MEDIAS: ClassVar[tuple[type[Media], ...]]

project: Project = Field(repr=False, exclude=True)
report: Report = Field(repr=False, exclude=True)
key: str

@computed_field # type: ignore[prop-decorator]
@computed_field
@property
def estimator_class_name(self) -> str:
"""The name of the report's estimator."""
return self.report.estimator_name_

@computed_field # type: ignore[prop-decorator]
@computed_field
@cached_property
def dataset_fingerprint(self) -> str:
"""The hash of the targets in the test-set."""
Expand All @@ -81,13 +81,13 @@ def dataset_fingerprint(self) -> str:
),
)

@computed_field # type: ignore[prop-decorator]
@computed_field
@property
def ml_task(self) -> str:
"""The type of ML task covered by the report."""
return self.report.ml_task

@computed_field # type: ignore[prop-decorator]
@computed_field
@cached_property
def metrics(self) -> list[Metric[Report]]:
"""
Expand Down Expand Up @@ -128,7 +128,7 @@ def metrics(self) -> list[Metric[Report]]:

return [metric for metric in metrics if metric.value is not None]

@computed_field # type: ignore[prop-decorator]
@computed_field
@cached_property
def medias(self) -> list[Media[Report]]:
"""
Expand All @@ -151,7 +151,7 @@ def medias(self) -> list[Media[Report]]:

return payloads

@computed_field # type: ignore[prop-decorator]
@computed_field
@cached_property
def pickle(self) -> Pickle:
"""
Expand Down
17 changes: 4 additions & 13 deletions skore-local-project/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -99,17 +99,8 @@ convention = "numpy"
[tool.ruff.lint.per-file-ignores]
"tests/*" = ["D"]

[tool.mypy]
exclude = ["hatch/", "tests/"]
strict = true
[tool.ty.src]
exclude = ["hatch/**", "tests/**"]

[[tool.mypy.overrides]]
follow_untyped_imports = true
module = ["skore.*"]

[[tool.mypy.overrides]]
ignore_missing_imports = true
module = [
"diskcache.*",
"joblib.*",
]
[tool.ty.terminal]
error-on-warning = true
15 changes: 11 additions & 4 deletions skore-local-project/src/skore_local_project/metadata.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,12 @@
from dataclasses import InitVar, dataclass, field, fields
from datetime import datetime, timezone
from math import isfinite
from typing import TYPE_CHECKING, Literal, cast
from typing import TYPE_CHECKING, Any, Literal, cast

from joblib import hash

if TYPE_CHECKING:
from collections.abc import Generator
from typing import Any

from skore import CrossValidationReport, EstimatorReport

Expand Down Expand Up @@ -118,10 +117,14 @@ def metric(report: EstimatorReport, name: str) -> float | None:

return cast_to_float(getattr(report.metrics, name)(data_source="test"))

def __post_init__(self, report: EstimatorReport) -> None: # type: ignore[override]
def __post_init__(self, report: EstimatorReport | CrossValidationReport) -> None:
Copy link
Collaborator

@thomass-dev thomass-dev Feb 19, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I know it violates the Liskov substitution principle, but your change is not true. Please revert.

Copy link
Collaborator

@thomass-dev thomass-dev Feb 19, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can fix the violation in a dedicated issue as we have done in https://github.com/probabl-ai/skore/blob/main/skore-hub-project/src/skore_hub_project/report/estimator_report.py, but it is not important here.

"""Initialize dynamic fields."""
super().__post_init__(report)

from skore import EstimatorReport
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is redundant with the import in TYPE_CHECKING, isn't?

Copy link
Collaborator

@auguste-probabl auguste-probabl Feb 19, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, I got test failures when this wasn't imported out of TYPE_CHECKING. So it looks like cast statements are not simply removed at runtime...


report = cast(EstimatorReport, report)

self.rmse = self.metric(report, "rmse")
self.log_loss = self.metric(report, "log_loss")
self.roc_auc = self.metric(report, "roc_auc")
Expand Down Expand Up @@ -164,10 +167,14 @@ def timing(report: CrossValidationReport, label: str) -> float | None:

return cast_to_float(series.iloc[0])

def __post_init__(self, report: CrossValidationReport) -> None: # type: ignore[override]
def __post_init__(self, report) -> None:
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please revert.

"""Initialize dynamic fields."""
super().__post_init__(report)

from skore import CrossValidationReport

report = cast(CrossValidationReport, report)

self.rmse_mean = self.metric(report, "rmse")
self.log_loss_mean = self.metric(report, "log_loss")
self.roc_auc_mean = self.metric(report, "roc_auc")
Expand Down
4 changes: 2 additions & 2 deletions skore-local-project/src/skore_local_project/storage.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@
from pathlib import Path
from typing import Any

from diskcache import Cache
import diskcache

Cache = partial(Cache, size_limit=float("inf"), cull_limit=0, eviction=None)
Cache = partial(diskcache.Cache, size_limit=float("inf"), cull_limit=0, eviction=None)


class DirectoryDoesNotExist(Exception):
Expand Down
18 changes: 4 additions & 14 deletions skore/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -162,18 +162,8 @@ convention = "numpy"
[tool.ruff.lint.per-file-ignores]
"tests/*" = ["D"]

[tool.mypy]
exclude = ["src/skore/_externals/", "hatch/", "tests/"]
[tool.ty.src]
exclude = ["hatch/**", "src/skore/_externals/**", "tests/**"]

[[tool.mypy.overrides]]
ignore_missing_imports = true
module = [
"ipywidgets.*",
"jinja2.*",
"joblib.*",
"pandas.*",
"plotly.*",
"seaborn.*",
"sklearn.*",
"skrub.*",
]
[tool.ty.terminal]
error-on-warning = true
6 changes: 4 additions & 2 deletions skore/src/skore/_project/_summary.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
if TYPE_CHECKING:
from typing import Literal

from skore import ComparisonReport, CrossValidationReport, EstimatorReport
from skore import ComparisonReport, CrossValidationReport, EstimatorReport, Project


class Summary(DataFrame):
Expand All @@ -38,6 +38,8 @@ class Summary(DataFrame):

_metadata = ["project"]

project: Project

@staticmethod
def factory(project, /):
"""
Expand Down Expand Up @@ -158,7 +160,7 @@ def _query_string_selection(self) -> str | None:
>>> query_string = df._query_string_selection()
>>> df_filtered = df.query(query_string)
"""
if not hasattr(self, "_plot_widget"):
if not hasattr(self, "_plot_widget") or self._plot_widget.current_fig is None:
return None

self._plot_widget.update_selection()
Expand Down
Loading