diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index f7d25d9..ff255c5 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -23,7 +23,7 @@ jobs: - uses: actions/setup-python@v6 with: python-version: 3.12 - - uses: pre-commit/action@v3.0.0 + - uses: pre-commit/action@v3.0.1 with: extra_args: --hook-stage manual --all-files @@ -33,10 +33,10 @@ jobs: strategy: fail-fast: false matrix: - python-version: ["3.9", "3.13", "3.14", "3.14t"] + python-version: ["3.10", "3.13", "3.14", "3.14t"] runs-on: [ubuntu-latest, macos-latest, windows-latest, windows-11-arm] exclude: - - python-version: "3.9" + - python-version: "3.10" runs-on: windows-11-arm steps: diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index c4e88f2..93e6851 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -19,7 +19,7 @@ jobs: - uses: actions/setup-python@v6 with: - python-version: 3.12 + python-version: 3.13 - name: Install pandoc uses: r-lib/actions/setup-pandoc@v2 diff --git a/.github/workflows/wheels.yml b/.github/workflows/wheels.yml index edec887..e4a9a3e 100644 --- a/.github/workflows/wheels.yml +++ b/.github/workflows/wheels.yml @@ -24,7 +24,7 @@ jobs: - name: Build SDist run: pipx run build --sdist - - uses: actions/upload-artifact@v6 + - uses: actions/upload-artifact@v7 with: name: artifact-sdist path: dist/*.tar.gz @@ -61,7 +61,7 @@ jobs: MACOSX_DEPLOYMENT_TARGET: 11.0 - name: Upload wheels - uses: actions/upload-artifact@v6 + uses: actions/upload-artifact@v7 with: name: artifact-wheel-${{ matrix.os }}-${{ matrix.arch }} path: wheelhouse/*.whl @@ -74,7 +74,7 @@ jobs: permissions: id-token: write steps: - - uses: actions/download-artifact@v7 + - uses: actions/download-artifact@v8 with: path: dist pattern: artifact-* diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 5c3c19b..febf323 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -40,7 +40,7 @@ repos: exclude: binder/mycorrections.json - repo: https://github.com/astral-sh/ruff-pre-commit - rev: "v0.14.13" + rev: "v0.15.4" hooks: - id: ruff args: ["--fix", "--show-fixes"] @@ -52,13 +52,13 @@ repos: - id: codespell - repo: https://github.com/abravalheri/validate-pyproject - rev: "v0.24.1" + rev: "v0.25" hooks: - id: validate-pyproject additional_dependencies: ["validate-pyproject-schema-store[all]"] - repo: https://github.com/python-jsonschema/check-jsonschema - rev: "0.36.0" + rev: "0.37.0" hooks: - id: check-dependabot - id: check-github-workflows @@ -68,7 +68,7 @@ repos: rev: v3.21.2 hooks: - id: pyupgrade - args: ["--py39-plus"] + args: ["--py310-plus"] - repo: https://github.com/pre-commit/mirrors-mypy rev: v1.19.1 diff --git a/pyproject.toml b/pyproject.toml index ee1dfbe..e19a444 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -27,15 +27,16 @@ classifiers = [ "License :: OSI Approved :: BSD License", "Programming Language :: Python", "Programming Language :: Python :: 3", - "Programming Language :: Python :: 3.9", "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", "Programming Language :: Python :: 3.13", "Programming Language :: Python :: 3.14", + "Programming Language :: Python :: Free Threading", + "Programming Language :: Python :: Free Threading :: 3 - Stable", "Development Status :: 5 - Production/Stable", ] -requires-python = ">=3.9" +requires-python = ">=3.10" dependencies = [ "numpy >=1.13.3", "pydantic >=2", @@ -105,7 +106,7 @@ ignore = [ ] [tool.mypy] -python_version = "3.9" +python_version = "3.10" files = ["src"] [[tool.mypy.overrides]] @@ -144,7 +145,7 @@ BUILD_DEMO = "OFF" write_to = "src/correctionlib/version.py" [tool.cibuildwheel] -skip = ["cp3{9,10}*-win_arm64"] +skip = ["cp310*-win_arm64"] test-groups = ["test"] test-command = "python -m pytest {package}/tests" test-skip = ["*-musllinux_*", "cp3{10,11,12}-win32", "cp*-win_arm64"] diff --git a/src/correctionlib/convert.py b/src/correctionlib/convert.py index 7ee34d6..fb26341 100644 --- a/src/correctionlib/convert.py +++ b/src/correctionlib/convert.py @@ -19,15 +19,16 @@ ) if TYPE_CHECKING: + from typing import Literal + from numpy import ndarray - from typing_extensions import Literal from uhi.typing.plottable import PlottableAxis, PlottableHistogram else: # py3.8+: no longer necessary try: from typing import Literal except ImportError: - from typing_extensions import Literal + from typing import Literal def from_uproot_THx( diff --git a/src/correctionlib/highlevel.py b/src/correctionlib/highlevel.py index c46af16..72267ed 100644 --- a/src/correctionlib/highlevel.py +++ b/src/correctionlib/highlevel.py @@ -3,9 +3,9 @@ from __future__ import annotations import json -from collections.abc import Iterator, Mapping +from collections.abc import Callable, Iterator, Mapping from numbers import Integral -from typing import TYPE_CHECKING, Any, Callable +from typing import TYPE_CHECKING, Any import numpy from packaging import version diff --git a/src/correctionlib/schemav1.py b/src/correctionlib/schemav1.py index 9ad8069..6b081b1 100644 --- a/src/correctionlib/schemav1.py +++ b/src/correctionlib/schemav1.py @@ -1,4 +1,4 @@ -from typing import Literal, Optional, Union +from typing import Literal, Union from pydantic import BaseModel, ConfigDict @@ -13,7 +13,7 @@ class Variable(Model): name: str type: Literal["string", "int", "real"] "Implicitly 64 bit integer and double-precision floating point?" - description: Optional[str] = None + description: str | None = None # TODO: clamping behavior for out of range? @@ -52,7 +52,7 @@ class MultiBinning(Model): class Category(Model): nodetype: Literal["category"] # TODO: should be Union[List[str], List[int]] - keys: list[Union[str, int]] + keys: list[str | int] content: list[Content] @@ -64,7 +64,7 @@ class Category(Model): class Correction(Model): name: str "A useful name" - description: Optional[str] = None + description: str | None = None "Detailed description of the correction" version: int "Version" diff --git a/src/correctionlib/schemav2.py b/src/correctionlib/schemav2.py index 039f22b..fe41986 100644 --- a/src/correctionlib/schemav2.py +++ b/src/correctionlib/schemav2.py @@ -1,8 +1,9 @@ import math import warnings from collections import Counter +from collections.abc import Callable from functools import partial -from typing import Annotated, Callable, Literal, Optional, Union +from typing import Annotated, Literal, Union from pydantic import ( AfterValidator, @@ -39,7 +40,7 @@ class Variable(Model): type: Literal["string", "int", "real"] = Field( description="A string, a 64 bit integer, or a double-precision floating point value" ) - description: Optional[str] = Field( + description: str | None = Field( description="A nice description of what this variable means", default=None, ) @@ -77,7 +78,7 @@ class Formula(Model): variables: list[str] = Field( description="The names of the correction input variables this formula applies to" ) - parameters: Optional[list[float]] = Field( + parameters: list[float] | None = Field( description="Parameters, if the parser supports them (e.g. [0] for TFormula)", default=None, ) @@ -195,11 +196,11 @@ class Binning(Model): input: str = Field( description="The name of the correction input variable this binning applies to" ) - edges: Union[NonUniformBinning, UniformBinning] = Field( + edges: NonUniformBinning | UniformBinning = Field( description="Edges of the binning, either as a list of monotonically increasing floats or as an instance of UniformBinning. edges[i] <= x < edges[i+1] => f(x, ...) = content[i](...)" ) content: list[Content] - flow: Union[Content, Literal["clamp", "error", "wrap"]] = Field( + flow: Content | Literal["clamp", "error", "wrap"] = Field( description="Overflow behavior for out-of-bounds values" ) @@ -228,7 +229,7 @@ class MultiBinning(Model): description="The names of the correction input variables this binning applies to", min_length=1, ) - edges: list[Union[NonUniformBinning, UniformBinning]] = Field( + edges: list[NonUniformBinning | UniformBinning] = Field( description="Bin edges for each input" ) content: list[Content] = Field( @@ -237,7 +238,7 @@ class MultiBinning(Model): to the element at i0 in dimension 0, i1 in dimension 1, etc. and d0 = len(edges[0])-1, etc. """ ) - flow: Union[Content, Literal["clamp", "error", "wrap"]] = Field( + flow: Content | Literal["clamp", "error", "wrap"] = Field( description="Overflow behavior for out-of-bounds values" ) @@ -266,7 +267,7 @@ class CategoryItem(Model): The key type must match the type of the Category input variable """ - key: Union[StrictInt, StrictStr] + key: StrictInt | StrictStr value: Content @@ -278,7 +279,7 @@ class Category(Model): description="The name of the correction input variable this category node applies to" ) content: list[CategoryItem] - default: Optional[Content] = None + default: Content | None = None @field_validator("content") @classmethod @@ -345,7 +346,7 @@ def _validate_input(allowed_names: set[str], node: Content) -> None: def _binning_range( - edges: Union[NonUniformBinning, UniformBinning], + edges: NonUniformBinning | UniformBinning, ) -> tuple[float, float]: if isinstance(edges, list): low = float(edges[0]) @@ -358,7 +359,7 @@ def _binning_range( class _SummaryInfo: def __init__(self) -> None: - self.values: set[Union[str, int]] = set() + self.values: set[str | int] = set() self.default: bool = False self.overflow: bool = True self.transform: bool = False @@ -393,7 +394,7 @@ def _summarize( class Correction(Model): name: str - description: Optional[str] = Field( + description: str | None = Field( description="Detailed description of the correction", default=None, ) @@ -404,7 +405,7 @@ class Correction(Model): description="The function signature of the correction" ) output: Variable = Field(description="Output type for this correction") - generic_formulas: Optional[list[Formula]] = Field( + generic_formulas: list[Formula] | None = Field( description="""A list of common formulas that may be used For corrections with many parameterized formulas that follow a regular pattern, @@ -499,7 +500,7 @@ class CompoundCorrection(Model): """ name: str - description: Optional[str] = Field( + description: str | None = Field( description="Detailed description of the correction stack", default=None, ) @@ -550,12 +551,12 @@ def fmt_input(var: Variable) -> str: class CorrectionSet(Model): schema_version: Literal[2] = Field(description="The overall schema version") - description: Optional[str] = Field( + description: str | None = Field( description="A nice description of what is in this CorrectionSet means", default=None, ) corrections: list[Correction] - compound_corrections: Optional[list[CompoundCorrection]] = None + compound_corrections: list[CompoundCorrection] | None = None @field_validator("corrections") @classmethod @@ -575,8 +576,8 @@ def validate_corrections(cls, items: list[Correction]) -> list[Correction]: @field_validator("compound_corrections") @classmethod def validate_compound( - cls, items: Optional[list[CompoundCorrection]] - ) -> Optional[list[CompoundCorrection]]: + cls, items: list[CompoundCorrection] | None + ) -> list[CompoundCorrection] | None: if items is None: return items seen = set()