diff --git a/.github/workflows/lint-and-test.yaml b/.github/workflows/lint-and-test.yaml index 6f53143..af24027 100644 --- a/.github/workflows/lint-and-test.yaml +++ b/.github/workflows/lint-and-test.yaml @@ -54,7 +54,7 @@ jobs: - name: Set up oldest supported Python on ${{ matrix.os }} uses: actions/setup-python@v5 with: - python-version: 3.9 + python-version: "3.10" - name: Run tests on oldest supported Python run: | python -m pip install ".[dev]" diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 5e8d9bb..d4c9591 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,6 +1,6 @@ repos: - repo: https://github.com/pre-commit/pre-commit-hooks - rev: v4.5.0 + rev: v4.6.0 hooks: - id: check-added-large-files - id: check-json @@ -10,24 +10,37 @@ repos: - id: end-of-file-fixer - id: mixed-line-ending - repo: https://github.com/pre-commit/mirrors-mypy - rev: v1.9.0 + rev: v1.11.2 hooks: - id: mypy types_or: [ python, pyi ] args: [--ignore-missing-imports, --explicit-package-bases] - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.3.4 + rev: v0.6.8 hooks: - id: ruff args: [--fix, --exit-non-zero-on-fix] - id: ruff-format - - repo: https://github.com/PyCQA/bandit - rev: 1.7.8 + - repo: https://github.com/astral-sh/uv-pre-commit + rev: 0.4.17 hooks: - - id: bandit - args: [-c, pyproject.toml] - additional_dependencies: ["bandit[toml]"] - - repo: https://github.com/jsh9/pydoclint - rev: 0.4.1 - hooks: - - id: pydoclint + - id: pip-compile + name: pip-compile requirements-dev.txt + args: + - --no-annotate + - --no-strip-extras + - --python-version=3.10 + - --extra=dev + - --output-file=requirements-dev.txt + - pyproject.toml + files: ^(pyproject\.toml|requirements-dev\.txt)$ + - id: pip-compile + name: pip-compile requirements-docs.txt + args: + - --no-annotate + - --no-strip-extras + - --python-version=3.10 + - --extra=docs + - --output-file=requirements-docs.txt + - pyproject.toml + files: ^(pyproject\.toml|requirements-docs\.txt)$ diff --git a/examples/huggingface/benchmark.py b/examples/huggingface/benchmark.py index 050396b..5765124 100644 --- a/examples/huggingface/benchmark.py +++ b/examples/huggingface/benchmark.py @@ -1,8 +1,8 @@ import os import tempfile import time +from collections.abc import Sequence from functools import cache, lru_cache, partial -from typing import Sequence import torch from datasets import Dataset, load_dataset diff --git a/examples/mnist/mnist.py b/examples/mnist/mnist.py index 0c77789..1643e1e 100644 --- a/examples/mnist/mnist.py +++ b/examples/mnist/mnist.py @@ -7,9 +7,9 @@ """ import random +from collections.abc import Mapping from dataclasses import dataclass from pathlib import Path -from typing import Mapping import flax.linen as nn import fsspec diff --git a/examples/prefect/src/runner.py b/examples/prefect/src/runner.py index 8e11294..909d3cb 100644 --- a/examples/prefect/src/runner.py +++ b/examples/prefect/src/runner.py @@ -1,5 +1,3 @@ -from __future__ import annotations - import asyncio import os diff --git a/examples/prefect/src/training.py b/examples/prefect/src/training.py index beb92d0..7a34f83 100644 --- a/examples/prefect/src/training.py +++ b/examples/prefect/src/training.py @@ -1,5 +1,3 @@ -from __future__ import annotations - import numpy as np from prefect import flow, task from sklearn import base diff --git a/pyproject.toml b/pyproject.toml index 78f0810..317af6f 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -6,7 +6,7 @@ build-backend = "setuptools.build_meta" name = "nnbench" description = "A small framework for benchmarking machine learning models." keywords = ["Benchmarking", "Machine Learning"] -requires-python = ">=3.9" +requires-python = ">=3.10" readme = "README.md" license = { text = "Apache-2.0" } authors = [{ name = "appliedAI Initiative", email = "info+oss@appliedai.de" }] @@ -31,7 +31,7 @@ classifiers = [ "Typing :: Typed", ] -dependencies = ["tabulate"] +dependencies = ["tabulate", "typing-extensions; python_version< '3.11'"] dynamic = ["version"] @@ -82,7 +82,7 @@ allow_redefinition = true check_untyped_defs = true disallow_incomplete_defs = true pretty = true -python_version = "3.11" +python_version = "3.10" strict_optional = false warn_unreachable = true @@ -94,10 +94,12 @@ ignore_missing_imports = true # explicitly set src folder for isort to understand first-party imports correctly. src = ["src"] line-length = 100 +target-version = "py310" [tool.ruff.lint] -# Enable pycodestyle errors & warnings (`E`, `W`), Pyflakes (`F`), and isort (`I`) by default. -select = ["E", "F", "I", "W"] +# Enable pycodestyle errors & warnings (`E`, `W`), Pyflakes (`F`), isort (`I`), +# and pyupgrade (`UP`) by default. +select = ["E", "F", "I", "W", "UP"] ignore = [ # Line too long "E501", diff --git a/requirements-dev.txt b/requirements-dev.txt index a0ecbd8..73f6088 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -1,29 +1,27 @@ -# -# This file is autogenerated by pip-compile with Python 3.11 -# by the following command: -# -# pip-compile --extra=dev --no-annotate --output-file=requirements-dev.txt pyproject.toml -# -build==1.1.1 +# This file was autogenerated by uv via the following command: +# uv pip compile --no-annotate --no-strip-extras --python-version=3.10 --extra=dev --output-file=requirements-dev.txt pyproject.toml +build==1.2.2 cfgv==3.4.0 distlib==0.3.8 -filelock==3.13.3 -fsspec==2024.3.1 -identify==2.5.35 +exceptiongroup==1.2.2 +filelock==3.16.1 +fsspec==2024.9.0 +identify==2.6.1 +importlib-metadata==8.5.0 iniconfig==2.0.0 -nodeenv==1.8.0 -numpy==1.26.4 -packaging==24.0 -platformdirs==4.2.0 -pluggy==1.4.0 -pre-commit==3.7.0 -psutil==5.9.8 -pyarrow==15.0.2 -pyproject-hooks==1.0.0 -pytest==8.1.1 -pyyaml==6.0.1 +nodeenv==1.9.1 +numpy==2.1.1 +packaging==24.1 +platformdirs==4.3.6 +pluggy==1.5.0 +pre-commit==3.8.0 +psutil==6.0.0 +pyarrow==17.0.0 +pyproject-hooks==1.2.0 +pytest==8.3.3 +pyyaml==6.0.2 tabulate==0.9.0 -virtualenv==20.25.1 - -# The following packages are considered to be unsafe in a requirements file: -# setuptools +tomli==2.0.1 +typing-extensions==4.12.2 +virtualenv==20.26.6 +zipp==3.20.2 diff --git a/requirements-docs.txt b/requirements-docs.txt index d4945c4..3095bfd 100644 --- a/requirements-docs.txt +++ b/requirements-docs.txt @@ -1,64 +1,64 @@ -# -# This file is autogenerated by pip-compile with Python 3.11 -# by the following command: -# -# pip-compile --extra=docs --no-annotate --output-file=requirements-docs.txt pyproject.toml -# -anyio==4.3.0 -babel==2.14.0 -black==24.3.0 -certifi==2024.2.2 +# This file was autogenerated by uv via the following command: +# uv pip compile --no-annotate --no-strip-extras --python-version=3.10 --extra=docs --output-file=requirements-docs.txt pyproject.toml +anyio==4.6.0 +babel==2.16.0 +black==24.8.0 +certifi==2024.8.30 charset-normalizer==3.3.2 click==8.1.7 colorama==0.4.6 docstring-parser==0.16 essentials==1.1.5 essentials-openapi==1.0.9 +exceptiongroup==1.2.2 ghp-import==2.1.0 -griffe==0.42.1 +griffe==1.3.2 h11==0.14.0 -httpcore==1.0.4 -httpx==0.27.0 -idna==3.6 -importlib-metadata==7.1.0 -importlib-resources==6.4.0 -jinja2==3.1.3 -markdown==3.5.2 +httpcore==1.0.5 +httpx==0.27.2 +idna==3.10 +importlib-metadata==8.5.0 +importlib-resources==6.4.5 +jinja2==3.1.4 +markdown==3.7 markdown-it-py==3.0.0 markupsafe==2.1.5 mdurl==0.1.2 mergedeep==1.3.4 -mike==2.0.0 -mkdocs==1.5.3 -mkdocs-autorefs==1.0.1 -mkdocs-callouts==1.13.2 +mike==2.1.3 +mkdocs==1.6.1 +mkdocs-autorefs==1.2.0 +mkdocs-callouts==1.14.0 mkdocs-gen-files==0.5.0 +mkdocs-get-deps==0.2.0 mkdocs-include-dir-to-nav==1.2.0 mkdocs-literate-nav==0.6.1 -mkdocs-material==9.5.15 +mkdocs-material==9.5.39 mkdocs-material-extensions==1.3.1 -mkdocs-section-index==0.3.8 -mkdocstrings[python]==0.24.1 -mkdocstrings-python==1.9.0 +mkdocs-section-index==0.3.9 +mkdocstrings[python]==0.26.1 +mkdocstrings-python==1.11.1 mypy-extensions==1.0.0 -neoteroi-mkdocs==1.0.5 -packaging==24.0 -paginate==0.5.6 +neoteroi-mkdocs==1.1.0 +packaging==24.1 +paginate==0.5.7 pathspec==0.12.1 -platformdirs==4.2.0 -pygments==2.17.2 -pymdown-extensions==10.7.1 -pyparsing==3.1.2 +platformdirs==4.3.6 +pygments==2.18.0 +pymdown-extensions==10.11.1 +pyparsing==3.1.4 python-dateutil==2.9.0.post0 -pyyaml==6.0.1 +pyyaml==6.0.2 pyyaml-env-tag==0.1 -regex==2023.12.25 -requests==2.31.0 -rich==13.7.1 +regex==2024.9.11 +requests==2.32.3 +rich==13.9.1 six==1.16.0 sniffio==1.3.1 tabulate==0.9.0 -urllib3==2.2.1 +tomli==2.0.1 +typing-extensions==4.12.2 +urllib3==2.2.3 verspec==0.1.0 -watchdog==4.0.0 -zipp==3.18.1 +watchdog==5.0.3 +zipp==3.20.2 diff --git a/src/nnbench/context.py b/src/nnbench/context.py index 8a563b8..c02fa91 100644 --- a/src/nnbench/context.py +++ b/src/nnbench/context.py @@ -1,11 +1,10 @@ """Utilities for collecting context key-value pairs as metadata in benchmark runs.""" -from __future__ import annotations - import itertools import platform import sys -from typing import Any, Callable, Iterator, Literal +from collections.abc import Callable, Iterator +from typing import Any, Literal ContextProvider = Callable[[], dict[str, Any]] """A function providing a dictionary of context values.""" @@ -89,8 +88,7 @@ def git_subprocess(args: list[str]) -> subprocess.CompletedProcess: return subprocess.run( # nosec: B603 [git, *args], - stdout=subprocess.PIPE, - stderr=subprocess.PIPE, + capture_output=True, encoding="utf-8", ) diff --git a/src/nnbench/core.py b/src/nnbench/core.py index 56b15da..ce09cc9 100644 --- a/src/nnbench/core.py +++ b/src/nnbench/core.py @@ -1,13 +1,12 @@ """Data model, registration, and parametrization facilities for defining benchmarks.""" -from __future__ import annotations - import inspect import itertools import sys import types import warnings -from typing import Any, Callable, Iterable, Union, get_args, get_origin, overload +from collections.abc import Callable, Iterable +from typing import Any, Union, get_args, get_origin, overload from nnbench.types import Benchmark from nnbench.types.benchmark import NoOp diff --git a/src/nnbench/reporter/__init__.py b/src/nnbench/reporter/__init__.py index e22f9f7..20eac29 100644 --- a/src/nnbench/reporter/__init__.py +++ b/src/nnbench/reporter/__init__.py @@ -2,8 +2,6 @@ A lightweight interface for refining, displaying, and streaming benchmark results to various sinks. """ -from __future__ import annotations - from .base import BenchmarkReporter from .duckdb_sql import DuckDBReporter from .file import FileReporter diff --git a/src/nnbench/reporter/base.py b/src/nnbench/reporter/base.py index d2204e7..fb68d92 100644 --- a/src/nnbench/reporter/base.py +++ b/src/nnbench/reporter/base.py @@ -1,7 +1,6 @@ -from __future__ import annotations - import re -from typing import Any, Callable +from collections.abc import Callable +from typing import Any from tabulate import tabulate diff --git a/src/nnbench/reporter/duckdb_sql.py b/src/nnbench/reporter/duckdb_sql.py index 70180f0..12a169a 100644 --- a/src/nnbench/reporter/duckdb_sql.py +++ b/src/nnbench/reporter/duckdb_sql.py @@ -1,5 +1,3 @@ -from __future__ import annotations - import os import shutil import sys @@ -130,6 +128,6 @@ def read_sql( return BenchmarkRecord(context=context, benchmarks=benchmarks) - def raw_sql(self, query: str) -> duckdb.DuckDBPyRelation: + def raw_sql(self, query: str) -> "duckdb.DuckDBPyRelation": rel = self.conn.sql(query=query) return rel diff --git a/src/nnbench/reporter/file.py b/src/nnbench/reporter/file.py index 55b5078..835289c 100644 --- a/src/nnbench/reporter/file.py +++ b/src/nnbench/reporter/file.py @@ -1,10 +1,9 @@ -from __future__ import annotations - import os import threading +from collections.abc import Callable, Sequence from dataclasses import dataclass, field from pathlib import Path -from typing import IO, Any, Callable, Literal, Sequence, cast +from typing import IO, Any, Literal, cast from nnbench.reporter.base import BenchmarkReporter from nnbench.types import BenchmarkRecord diff --git a/src/nnbench/runner.py b/src/nnbench/runner.py index 517e8cc..f23993e 100644 --- a/src/nnbench/runner.py +++ b/src/nnbench/runner.py @@ -1,7 +1,5 @@ """The abstract benchmark runner interface, which can be overridden for custom benchmark workloads.""" -from __future__ import annotations - import collections import contextlib import inspect @@ -10,10 +8,11 @@ import sys import time import warnings +from collections.abc import Callable, Generator, Sequence from dataclasses import asdict from datetime import datetime from pathlib import Path -from typing import Any, Callable, Generator, Sequence, get_origin +from typing import Any, get_origin from nnbench.context import Context, ContextProvider from nnbench.types import Benchmark, BenchmarkRecord, Parameters, State @@ -24,7 +23,7 @@ def iscontainer(s: Any) -> bool: - return isinstance(s, (tuple, list)) + return isinstance(s, tuple | list) def isdunder(s: str) -> bool: diff --git a/src/nnbench/transforms/base.py b/src/nnbench/transforms/base.py index a4a3cd8..b21e8e6 100644 --- a/src/nnbench/transforms/base.py +++ b/src/nnbench/transforms/base.py @@ -1,7 +1,7 @@ """Metaclasses for defining transforms acting on benchmark records.""" from abc import ABC, abstractmethod -from typing import Sequence +from collections.abc import Sequence from nnbench.types import BenchmarkRecord diff --git a/src/nnbench/transforms/params.py b/src/nnbench/transforms/params.py index 6c8c70c..82d7b10 100644 --- a/src/nnbench/transforms/params.py +++ b/src/nnbench/transforms/params.py @@ -1,4 +1,5 @@ -from typing import Any, Sequence +from collections.abc import Sequence +from typing import Any from nnbench.transforms import ManyToManyTransform, OneToOneTransform from nnbench.types import BenchmarkRecord diff --git a/src/nnbench/types/benchmark.py b/src/nnbench/types/benchmark.py index a9bf97c..a18dc63 100644 --- a/src/nnbench/types/benchmark.py +++ b/src/nnbench/types/benchmark.py @@ -1,20 +1,17 @@ """Type interfaces for benchmarks and benchmark collections.""" -from __future__ import annotations - import copy +from collections.abc import Callable, Mapping from dataclasses import dataclass, field from types import MappingProxyType -from typing import Any, Callable, Literal, Mapping +from typing import Any, Literal + +from typing_extensions import Self from nnbench.context import Context from nnbench.types.interface import Interface -def NoOp(state: State, params: Mapping[str, Any] = MappingProxyType({})) -> None: - pass - - @dataclass(frozen=True) class State: name: str @@ -23,6 +20,10 @@ class State: family_index: int +def NoOp(state: State, params: Mapping[str, Any] = MappingProxyType({})) -> None: + pass + + @dataclass(frozen=True) class BenchmarkRecord: context: Context @@ -68,7 +69,7 @@ def compact( return result @classmethod - def expand(cls, bms: list[dict[str, Any]]) -> BenchmarkRecord: + def expand(cls, bms: list[dict[str, Any]]) -> Self: """ Expand a list of deserialized JSON-like objects into a benchmark record. This is equivalent to extracting the context given by the method it was diff --git a/src/nnbench/types/interface.py b/src/nnbench/types/interface.py index 8624546..da0a381 100644 --- a/src/nnbench/types/interface.py +++ b/src/nnbench/types/interface.py @@ -1,10 +1,11 @@ """Type interface for the function interface""" -from __future__ import annotations - import inspect +from collections.abc import Callable from dataclasses import dataclass -from typing import Any, Callable, TypeVar +from typing import Any, TypeVar + +from typing_extensions import Self T = TypeVar("T") Variable = tuple[str, type, Any] @@ -37,7 +38,7 @@ class Interface: returntype: type @classmethod - def from_callable(cls, fn: Callable, defaults: dict[str, Any]) -> Interface: + def from_callable(cls, fn: Callable, defaults: dict[str, Any]) -> Self: """ Creates an interface instance from the given callable. """ diff --git a/src/nnbench/types/memo.py b/src/nnbench/types/memo.py index fcfad49..716abda 100644 --- a/src/nnbench/types/memo.py +++ b/src/nnbench/types/memo.py @@ -1,11 +1,10 @@ -from __future__ import annotations - import collections import functools import inspect import logging import threading -from typing import Any, Callable, Generic, TypeVar, get_args, get_origin +from collections.abc import Callable +from typing import Any, Generic, TypeVar, get_args, get_origin T = TypeVar("T") Variable = tuple[str, type, Any] diff --git a/src/nnbench/util.py b/src/nnbench/util.py index 2a4d88c..d4a2a8d 100644 --- a/src/nnbench/util.py +++ b/src/nnbench/util.py @@ -1,7 +1,5 @@ """Various utilities related to benchmark collection, filtering, and more.""" -from __future__ import annotations - import importlib import importlib.util import os diff --git a/tests/test_memos.py b/tests/test_memos.py index 01bf398..2f73a44 100644 --- a/tests/test_memos.py +++ b/tests/test_memos.py @@ -1,4 +1,4 @@ -from typing import Generator +from collections.abc import Generator import pytest