From 8ce324be9717ac311a3b09b4db568d9592b4f216 Mon Sep 17 00:00:00 2001 From: Gregory Mermoud Date: Wed, 15 Jul 2026 12:51:19 +0000 Subject: [PATCH 1/2] Add CI, a tests stub, and a project README template This repo is the base template for the lab's Python projects, but it had no tests, no CI, and its README described the template itself rather than the project built from it. - Add .github/workflows/ci.yml, running the pre-commit hooks over all files and the test suite on every push and pull request. - Add tests/ with a stub covering the example hello function, and pytest as a dev dependency. Unlike the linters, pytest has to import the project, so it needs the environment rather than uvx. - Rename src/python-base to src/package_name. The old name contained a hyphen, so it was not a valid module name and could not be imported; the placeholder is now importable and project-agnostic. This also surfaced a ruff D104 error that had been skipped while the package name was invalid, hence the new __init__ docstring. - Turn README.md into a fill-in-the-blanks template for downstream projects, and move the template's own documentation to GUIDELINES.md. - Document how to populate tests/ and how CI works in GUIDELINES.md. Co-Authored-By: Claude Opus 4.8 --- .github/workflows/ci.yml | 48 ++++++ .gitignore | 3 - .pre-commit-config.yaml | 16 +- AGENTS.md | 47 ++++++ GUIDELINES.md | 113 +++++++++++++ README.md | 102 +++++++----- containers/apptainer.def | 58 +++++++ containers/entrypoint.sh | 14 ++ pyproject.toml | 8 +- src/package_name/__init__.py | 1 + src/{python-base => package_name}/hello.py | 0 src/python-base/__init__.py | 0 tests/test_hello.py | 8 + uv.lock | 176 ++++----------------- 14 files changed, 398 insertions(+), 196 deletions(-) create mode 100644 .github/workflows/ci.yml create mode 100644 AGENTS.md create mode 100644 GUIDELINES.md create mode 100644 containers/apptainer.def create mode 100644 containers/entrypoint.sh create mode 100644 src/package_name/__init__.py rename src/{python-base => package_name}/hello.py (100%) delete mode 100644 src/python-base/__init__.py create mode 100644 tests/test_hello.py diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..585867a --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,48 @@ +# CI template: runs the pre-commit hooks and the test suite on every push. +# +# The Python version below should match the one in containers/apptainer.def; +# change it in both places when the project moves on. + +name: CI + +on: + push: + branches: [main] + pull_request: + +# A new push to a branch cancels any run still in progress for it. +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +env: + PYTHON_VERSION: "3.12" + +jobs: + # Runs every hook in .pre-commit-config.yaml (mdformat, ruff, ty, ...) over + # the whole repo, not just the changed files. + pre-commit: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v7 + - uses: astral-sh/setup-uv@v8 + with: + python-version: ${{ env.PYTHON_VERSION }} + enable-cache: true + - uses: actions/cache@v6 + with: + path: ~/.cache/pre-commit + key: pre-commit-${{ hashFiles('.pre-commit-config.yaml') }} + - run: uvx pre-commit run --all-files --show-diff-on-failure + + pytest: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v7 + - uses: astral-sh/setup-uv@v8 + with: + python-version: ${{ env.PYTHON_VERSION }} + enable-cache: true + # --frozen fails if uv.lock is out of date with pyproject.toml. + - run: uv sync --frozen + - run: uv run pytest diff --git a/.gitignore b/.gitignore index acf0849..1fc9c00 100644 --- a/.gitignore +++ b/.gitignore @@ -4,10 +4,7 @@ __pycache__/ *$py.class # User defined folders -docs/ data/ -containers/ -models/ # Data files. *.csv diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 6fb8386..7e32fd6 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: v5.0.0 + rev: v6.0.0 hooks: - id: check-added-large-files args: ["--maxkb=4096"] @@ -9,14 +9,14 @@ repos: - id: trailing-whitespace files: \.(js|rb|md|py|sh|txt|yaml|yml|kidm)$ - - repo: https://github.com/igorshubovych/markdownlint-cli - rev: v0.30.0 + - repo: local hooks: - - id: markdownlint - files: \.md$ - language_version: "13.14.0" - args: - - --fix + - id: mdformat + name: mdformat + entry: uvx --with mdformat-gfm --with mdformat-frontmatter mdformat --wrap 100 --number + language: system + types: [markdown] + pass_filenames: true - repo: local hooks: diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000..b48d33d --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,47 @@ +# High-level project description + +> Describe your project here + +## Research workflows + +- Maintain a append-only `LOGBOOK.md` file for each iteration, with the following structure in + antichronological order: + + ``` + ## - YYYY-MM-DD + + + + + + + + + ``` + +- Maintain a `RESULTS.md` that compiles the (comparable) results obtained so far. It should be an + up-to-date version of the key results found in `LOGBOOK.md`. + +## Reporting + +Reports are written in quarto. All plots should be generated with Python cells (not copied from +elsewhere), ideally from cached results to minimize the generation time. Reporting code should be +re-usable, so favor functions instead of throwaway code. + +## Coding Style + +- Use `uv` to run scripts +- Use `uv ruff check` to lint the code and `uvx ty check` to type check it. +- Always use `uv ruff format` to format the code according to our guidelines after you are done + editing a file. +- All functions must have type hints for their arguments and return values +- All functions should have docstings (numpy style), the docstring should be short and simple, avoid + notes sections and exemples sections in docstrings unless strictly necessary. +- Code should focus on simplicity and clarity, avoid scope creep, avoid implementing non-necessary + options. +- Polars should be prefered over pandas unless really necessary (e.g., geopandas). +- Project configurations and data paths are defined in `src/package_name/data/config.yaml` and + `src/package_name/config.py`, data folder paths should not be hardcoded in scripts, rather they + should be loaded from configurations. +- Tests live in `tests/`, are run with `uv run pytest`, and follow the same style rules as the rest + of the code (type hints, short numpy-style docstrings). diff --git a/GUIDELINES.md b/GUIDELINES.md new file mode 100644 index 0000000..3346ce6 --- /dev/null +++ b/GUIDELINES.md @@ -0,0 +1,113 @@ +# Project template + +This template should be used for every Python project in the lab. It uses: + +- [`uv`](https://docs.astral.sh/uv/) for dependency management. +- [`ruff`](https://docs.astral.sh/ruff/) for code formatting. +- [`ty`](https://docs.astral.sh/ty/) for type checking. +- [`pre-commit`](https://pre-commit.com/) hooks for automated validation. +- [`pytest`](https://docs.pytest.org/) for testing. +- GitHub Actions to run the hooks and the tests on every push. + +## Dependency management + +We use [`uv`](https://docs.astral.sh/uv/) for dependency management. It is just as full-featured as +`poetry`, but _much faster_. Follow the instructions below to create a new project: + +1. Update the name of the project in `pyproject.toml`. This is the distribution name, and it usually + matches the name of the repository (hyphens are fine here). + +2. Rename the folder `src/package_name` to the name of your package, and update the import in + `tests/test_hello.py` to match. This is a Python module name, so it must use underscores rather + than hyphens (`my_project`, not `my-project`) — otherwise it cannot be imported. + +3. Run `uv sync` from the root of the repo. This will create a virtual environment and install + needed development dependencies. + +4. Add the dependencies you need (and run this same command every time you need a new package): + + ```sh + uv add polars lightgbm + ``` + +5. Take a look at the `uv`'s [Getting started guide](https://docs.astral.sh/uv/getting-started/). + +## Pre-commit hooks + +Install the pre-commit hooks: + +```sh +uvx pre-commit install +``` + +This will create a `.git/hooks/pre-commit` file that will run the pre-commit hooks every time you +commit. Upon the first commit, the hooks will be installed. + +Some hooks output error message that require a manual change (e.g., linting errors). Other hooks +perform automated fixes. Either way, you need to re-run the commit command: + +```sh +git commit -m "My message" +``` + +### Code formatting + +Among the pre-commit hooks, you will find one that runs [`ruff`](https://docs.astral.sh/ruff/) on +every Python file. It is also warmly recommended that you set up `ruff` in your IDE (e.g., Visual +Studio Code, PyCharm). + +### Typing + +We recommend the use of [type hints](https://docs.python.org/3/library/typing.html) of your code. +One of the pre-commit hooks is [`ty`](https://docs.astral.sh/ty/), which will perform type checking +when hints are available. This reduces greatly the risk of bugs and the maintainability of the code. + +## Tests + +Tests live in `tests/` and are run with [`pytest`](https://docs.pytest.org/): + +```sh +uv run pytest +``` + +Unlike the linters, `pytest` is a dev dependency in `pyproject.toml` rather than a `uvx` tool: it +has to import your package, so it needs the project environment. It is installed by `uv sync`. + +`tests/test_hello.py` is a stub covering the example `hello` function. Populate `tests/` as follows: + +1. Delete `src/package_name/hello.py` and `tests/test_hello.py` once your own code replaces the + example. + +2. Name test files `test_*.py` and test functions `test_*`, mirroring the layout of your package + (`src/package_name/foo.py` is tested by `tests/test_foo.py`). + +3. Write one test per behaviour you want to keep working, and name it after that behaviour + (`test_hello_returns_greeting`, not `test_1`). A test that would still pass if the function were + broken is not worth having. + +4. Cover the cases you are tempted to check by hand in a notebook: the empty input, the boundary, + the error path. Add a test reproducing any bug you fix, so it cannot come back. + +5. Keep tests fast and independent of the lab storage. If a test needs data, generate a small + fixture in the test itself rather than reading from a data path. + +The same style rules apply to test code as to the rest: type hints and a short numpy-style docstring +on every test function. + +## Continuous integration + +`.github/workflows/ci.yml` defines two jobs, which run on every push to `main` and on every pull +request: + +- `pre-commit` runs every hook over all files, so CI fails on anything you did not run locally. +- `pytest` runs `uv sync --frozen` and the test suite. `--frozen` fails if `uv.lock` is out of date + with `pyproject.toml`, so commit the lockfile whenever you add a dependency. + +Both jobs pin the Python version through the `PYTHON_VERSION` variable at the top of the workflow. +It should match the version in `containers/apptainer.def`; change it in both places. To reproduce a +CI failure locally: + +```sh +uvx pre-commit run --all-files +uv run pytest +``` diff --git a/README.md b/README.md index 3adac2e..d435100 100644 --- a/README.md +++ b/README.md @@ -1,59 +1,85 @@ -# Project template +# {Project name} -This template should be used for every Python project in the lab. It uses: +> One or two sentences on what this project does and why it exists. -- [`uv`](https://docs.astral.sh/uv/) for dependency management. -- [`ruff`](https://docs.astral.sh/ruff/) for code formatting. -- [`ty`](https://docs.astral.sh/ty/) for type checking. -- [`pre-commit`](https://pre-commit.com/) hooks for automated validation. +## Status -## Dependency management +{Early exploration | Active development | Stable | Archived}. {Anything a newcomer should know +before relying on this: what works, what doesn't.} -We use [`uv`](https://docs.astral.sh/uv/) for dependency management. It is just as -full-featured as `poetry`, but _much faster_. Follow the instructions below to -create a new project: +## Getting started -1. Update the name of the project in `pyproject.toml`. -2. Change the name of the folder `src/python-base` to match the project name. -3. Run `uv sync` from the root of the repo. -This will create a virtual environment and install needed development dependencies. -4. Add the dependencies you need (and run this same command every time you need - a new package): +Requires Python >=3.12 and [`uv`](https://docs.astral.sh/uv/). - ```sh - uv add polars lightgbm - ``` +```sh +uv sync # create the virtualenv and install dependencies +uvx pre-commit install # install the pre-commit hooks +``` + +Check the install works: -5. Take a look at the `uv`'s [Getting started guide](https://docs.astral.sh/uv/getting-started/). +```sh +uv run python -m {package_name}.hello +``` -## Pre-commit hooks +## Usage -Install the pre-commit hooks: +{The shortest command that produces something useful, and the output it prints. Add one example per +entry point.} ```sh -uvx pre-commit install +uv run python -m {package_name}.{entry_point} --help ``` -This will create a `.git/hooks/pre-commit` file that will run the pre-commit -hooks every time you commit. Upon the first commit, the hooks will be installed. +## Layout + +| Path | Contents | +| -------------------------- | ------------------------------------------------- | +| `src/{package_name}/` | Library code, importable as `{package_name}`. | +| `src/{package_name}/data/` | `config.yaml` — data paths and project settings. | +| `tests/` | Test suite, run with `uv run pytest`. | +| `containers/` | Apptainer definition and entrypoint. | +| {scripts, notebooks, …} | {Fill in the folders this project actually adds.} | + +Data paths live in `config.yaml` and are read through `config.py` — never hardcode them in scripts. + +## Data + +{Where the inputs come from, where they live on the lab storage, and how to get them. Say if +anything is restricted or must not be committed.} -Some hooks output error message that require a manual change (e.g., linting -errors). Other hooks perform automated fixes. Either way, you need to re-run -the commit command: +## Results + +`LOGBOOK.md` records each iteration in antichronological order (hypothesis, method, key results, +next steps). `RESULTS.md` compiles the comparable results across iterations, and is the place to +look first. + +{Link the reports here as they are produced.} + +## Containers + +Build and run the Apptainer image, binding your working directory: ```sh -git commit -m "My message" +apptainer build --build-arg PROJECT_NAME={project-name} project.sif containers/apptainer.def +apptainer run --bind /path/to/workdir:/mnt/{project-name} project.sif ``` -### Code formatting +See the header of `containers/apptainer.def` for the available build arguments. + +## Development + +`GUIDELINES.md` covers the lab-wide toolchain (`uv`, `ruff`, `ty`, `pre-commit`); `AGENTS.md` covers +the coding style and research workflow, and is what coding agents read. Both apply to this project — +document only the deviations here. + +```sh +uv run pytest # run the tests +uvx pre-commit run --all-files # run every hook, as CI does +``` -Among the pre-commit hooks, you will find one that runs -[`ruff`](https://docs.astral.sh/ruff/) on every Python file. It is also warmly -recommended that you set up `ruff` in your IDE (e.g., Visual Studio Code, PyCharm). +`.github/workflows/ci.yml` runs those same two checks on every push. -### Typing +## Contact -We recommend the use of [type hints](https://docs.python.org/3/library/typing.html) -of your code. One of the pre-commit hooks is [`ty`](https://docs.astral.sh/ty/), -which will perform type checking when hints are available. This reduces greatly the -risk of bugs and the maintainability of the code. +{Maintainer name and how to reach them.} diff --git a/containers/apptainer.def b/containers/apptainer.def new file mode 100644 index 0000000..5b890b2 --- /dev/null +++ b/containers/apptainer.def @@ -0,0 +1,58 @@ +# Minimal apptainer definition file for a Python project based on uv. +# +# Build with the defaults below, or override them: +# +# apptainer build project.sif apptainer.def +# apptainer build --build-arg PROJECT_NAME=my-project \ +# --build-arg PYTHON_VERSION=3.13 \ +# project.sif apptainer.def +# +# The project root (the parent of this folder) is copied to /opt/$PROJECT_NAME, +# and /mnt/$PROJECT_NAME is the working directory at runtime (bind your data +# there with `apptainer run --bind /path/to/workdir:/mnt/$PROJECT_NAME ...`). +# +# Running the container with no arguments runs ENTRYPOINT; define what the +# project should do there. Any command passed to `apptainer run` overrides it: +# +# apptainer run project.sif # runs ENTRYPOINT +# apptainer run project.sif python -m python_base.hello + +Bootstrap: docker +From: ghcr.io/astral-sh/uv:python{{ PYTHON_VERSION }}-bookworm-slim + +%arguments + PROJECT_NAME=python-base + PYTHON_VERSION=3.12 + MAINTAINER=unknown + VERSION=0.1 + ENTRYPOINT=containers/entrypoint.sh + +%labels + Maintainer "{{ MAINTAINER }}" + Version "{{ VERSION }}" + +%files + ../ /opt/{{ PROJECT_NAME }} + +%post + echo "**** Installing system dependencies ****" + apt-get update && apt-get install -y git curl && apt-get clean + + echo "**** Installing Python dependencies ****" + cd /opt/{{ PROJECT_NAME }} + uv venv --python {{ PYTHON_VERSION }} + uv sync + + chmod +x /opt/{{ PROJECT_NAME }}/{{ ENTRYPOINT }} + mkdir -p /mnt/{{ PROJECT_NAME }} + +%environment + export VIRTUAL_ENV="/opt/{{ PROJECT_NAME }}/.venv" + export PATH="$VIRTUAL_ENV/bin:$PATH" + +%runscript + cd /mnt/{{ PROJECT_NAME }} + if [ $# -eq 0 ]; then + exec /opt/{{ PROJECT_NAME }}/{{ ENTRYPOINT }} + fi + exec "$@" diff --git a/containers/entrypoint.sh b/containers/entrypoint.sh new file mode 100644 index 0000000..af91fc4 --- /dev/null +++ b/containers/entrypoint.sh @@ -0,0 +1,14 @@ +#!/bin/bash +# Default entrypoint for the container, run when no command is given to +# `apptainer run`. Replace the body with whatever makes sense for this project +# (a training run, a pipeline, a server, ...). +# +# The project venv is already on PATH, and the working directory is the bind +# mount /mnt/, so scripts can be called directly: +# +# python -m python_base.hello +set -euo pipefail + +echo "No entrypoint defined for this project yet." +echo "Edit containers/entrypoint.sh, or pass a command:" +echo " apptainer run project.sif python -m python_base.hello" diff --git a/pyproject.toml b/pyproject.toml index d2ebae9..ca60d16 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -25,6 +25,10 @@ extend-select = [ [tool.ruff.lint.pydocstyle] convention = "numpy" -# Set ruff as only a local deps for development. +# Dev dependencies. Linters (ruff, ty, pre-commit) are run through uvx and are +# deliberately absent here; pytest must import the project, so it lives in the env. [dependency-groups] -dev = [] +dev = ["pytest>=8"] + +[tool.pytest.ini_options] +testpaths = ["tests"] diff --git a/src/package_name/__init__.py b/src/package_name/__init__.py new file mode 100644 index 0000000..5769c84 --- /dev/null +++ b/src/package_name/__init__.py @@ -0,0 +1 @@ +"""Example package. Rename this folder to your own package name.""" diff --git a/src/python-base/hello.py b/src/package_name/hello.py similarity index 100% rename from src/python-base/hello.py rename to src/package_name/hello.py diff --git a/src/python-base/__init__.py b/src/python-base/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/tests/test_hello.py b/tests/test_hello.py new file mode 100644 index 0000000..aa3bfc0 --- /dev/null +++ b/tests/test_hello.py @@ -0,0 +1,8 @@ +"""Tests for the example module. Replace these with tests for your own code.""" + +from package_name.hello import hello + + +def test_hello_returns_greeting() -> None: + """Check that hello returns the expected greeting.""" + assert hello() == "Hello, World!" diff --git a/uv.lock b/uv.lock index 9db3e19..c5f8267 100644 --- a/uv.lock +++ b/uv.lock @@ -3,191 +3,77 @@ revision = 3 requires-python = ">=3.12" [[package]] -name = "cfgv" -version = "3.4.0" +name = "colorama" +version = "0.4.6" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/11/74/539e56497d9bd1d484fd863dd69cbbfa653cd2aa27abfe35653494d85e94/cfgv-3.4.0.tar.gz", hash = "sha256:e52591d4c5f5dead8e0f673fb16db7949d2cfb3f7da4582893288f0ded8fe560", size = 7114, upload-time = "2023-08-12T20:38:17.776Z" } +sdist = { url = "https://files.pythonhosted.org/packages/d8/53/6f443c9a4a8358a93a6792e2acffb9d9d5cb0a5cfd8802644b7b1c9a02e4/colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44", size = 27697, upload-time = "2022-10-25T02:36:22.414Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/c5/55/51844dd50c4fc7a33b653bfaba4c2456f06955289ca770a5dbd5fd267374/cfgv-3.4.0-py2.py3-none-any.whl", hash = "sha256:b7265b1f29fd3316bfcd2b330d63d024f2bfd8bcb8b0272f8e19a504856c48f9", size = 7249, upload-time = "2023-08-12T20:38:16.269Z" }, + { url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335, upload-time = "2022-10-25T02:36:20.889Z" }, ] [[package]] -name = "distlib" -version = "0.3.9" +name = "iniconfig" +version = "2.3.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/0d/dd/1bec4c5ddb504ca60fc29472f3d27e8d4da1257a854e1d96742f15c1d02d/distlib-0.3.9.tar.gz", hash = "sha256:a60f20dea646b8a33f3e7772f74dc0b2d0772d2837ee1342a00645c81edf9403", size = 613923, upload-time = "2024-10-09T18:35:47.551Z" } +sdist = { url = "https://files.pythonhosted.org/packages/72/34/14ca021ce8e5dfedc35312d08ba8bf51fdd999c576889fc2c24cb97f4f10/iniconfig-2.3.0.tar.gz", hash = "sha256:c76315c77db068650d49c5b56314774a7804df16fee4402c1f19d6d15d8c4730", size = 20503, upload-time = "2025-10-18T21:55:43.219Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/91/a1/cf2472db20f7ce4a6be1253a81cfdf85ad9c7885ffbed7047fb72c24cf87/distlib-0.3.9-py2.py3-none-any.whl", hash = "sha256:47f8c22fd27c27e25a65601af709b38e4f0a45ea4fc2e710f65755fa8caaaf87", size = 468973, upload-time = "2024-10-09T18:35:44.272Z" }, + { url = "https://files.pythonhosted.org/packages/cb/b1/3846dd7f199d53cb17f49cba7e651e9ce294d8497c8c150530ed11865bb8/iniconfig-2.3.0-py3-none-any.whl", hash = "sha256:f631c04d2c48c52b84d0d0549c99ff3859c98df65b3101406327ecc7d53fbf12", size = 7484, upload-time = "2025-10-18T21:55:41.639Z" }, ] [[package]] -name = "filelock" -version = "3.18.0" +name = "packaging" +version = "26.2" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/0a/10/c23352565a6544bdc5353e0b15fc1c563352101f30e24bf500207a54df9a/filelock-3.18.0.tar.gz", hash = "sha256:adbc88eabb99d2fec8c9c1b229b171f18afa655400173ddc653d5d01501fb9f2", size = 18075, upload-time = "2025-03-14T07:11:40.47Z" } +sdist = { url = "https://files.pythonhosted.org/packages/d7/f1/e7a6dd94a8d4a5626c03e4e99c87f241ba9e350cd9e6d75123f992427270/packaging-26.2.tar.gz", hash = "sha256:ff452ff5a3e828ce110190feff1178bb1f2ea2281fa2075aadb987c2fb221661", size = 228134, upload-time = "2026-04-24T20:15:23.917Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/4d/36/2a115987e2d8c300a974597416d9de88f2444426de9571f4b59b2cca3acc/filelock-3.18.0-py3-none-any.whl", hash = "sha256:c401f4f8377c4464e6db25fff06205fd89bdd83b65eb0488ed1b160f780e21de", size = 16215, upload-time = "2025-03-14T07:11:39.145Z" }, + { url = "https://files.pythonhosted.org/packages/df/b2/87e62e8c3e2f4b32e5fe99e0b86d576da1312593b39f47d8ceef365e95ed/packaging-26.2-py3-none-any.whl", hash = "sha256:5fc45236b9446107ff2415ce77c807cee2862cb6fac22b8a73826d0693b0980e", size = 100195, upload-time = "2026-04-24T20:15:22.081Z" }, ] [[package]] -name = "identify" -version = "2.6.10" +name = "pluggy" +version = "1.6.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/0c/83/b6ea0334e2e7327084a46aaaf71f2146fc061a192d6518c0d020120cd0aa/identify-2.6.10.tar.gz", hash = "sha256:45e92fd704f3da71cc3880036633f48b4b7265fd4de2b57627cb157216eb7eb8", size = 99201, upload-time = "2025-04-19T15:10:38.32Z" } +sdist = { url = "https://files.pythonhosted.org/packages/f9/e2/3e91f31a7d2b083fe6ef3fa267035b518369d9511ffab804f839851d2779/pluggy-1.6.0.tar.gz", hash = "sha256:7dcc130b76258d33b90f61b658791dede3486c3e6bfb003ee5c9bfb396dd22f3", size = 69412, upload-time = "2025-05-15T12:30:07.975Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/2b/d3/85feeba1d097b81a44bcffa6a0beab7b4dfffe78e82fc54978d3ac380736/identify-2.6.10-py2.py3-none-any.whl", hash = "sha256:5f34248f54136beed1a7ba6a6b5c4b6cf21ff495aac7c359e1ef831ae3b8ab25", size = 99101, upload-time = "2025-04-19T15:10:36.701Z" }, + { url = "https://files.pythonhosted.org/packages/54/20/4d324d65cc6d9205fabedc306948156824eb9f0ee1633355a8f7ec5c66bf/pluggy-1.6.0-py3-none-any.whl", hash = "sha256:e920276dd6813095e9377c0bc5566d94c932c33b27a3e3945d8389c374dd4746", size = 20538, upload-time = "2025-05-15T12:30:06.134Z" }, ] [[package]] -name = "nodeenv" -version = "1.9.1" +name = "pygments" +version = "2.20.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/43/16/fc88b08840de0e0a72a2f9d8c6bae36be573e475a6326ae854bcc549fc45/nodeenv-1.9.1.tar.gz", hash = "sha256:6ec12890a2dab7946721edbfbcd91f3319c6ccc9aec47be7c7e6b7011ee6645f", size = 47437, upload-time = "2024-06-04T18:44:11.171Z" } +sdist = { url = "https://files.pythonhosted.org/packages/c3/b2/bc9c9196916376152d655522fdcebac55e66de6603a76a02bca1b6414f6c/pygments-2.20.0.tar.gz", hash = "sha256:6757cd03768053ff99f3039c1a36d6c0aa0b263438fcab17520b30a303a82b5f", size = 4955991, upload-time = "2026-03-29T13:29:33.898Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/d2/1d/1b658dbd2b9fa9c4c9f32accbfc0205d532c8c6194dc0f2a4c0428e7128a/nodeenv-1.9.1-py2.py3-none-any.whl", hash = "sha256:ba11c9782d29c27c70ffbdda2d7415098754709be8a7056d79a737cd901155c9", size = 22314, upload-time = "2024-06-04T18:44:08.352Z" }, + { url = "https://files.pythonhosted.org/packages/f4/7e/a72dd26f3b0f4f2bf1dd8923c85f7ceb43172af56d63c7383eb62b332364/pygments-2.20.0-py3-none-any.whl", hash = "sha256:81a9e26dd42fd28a23a2d169d86d7ac03b46e2f8b59ed4698fb4785f946d0176", size = 1231151, upload-time = "2026-03-29T13:29:30.038Z" }, ] [[package]] -name = "platformdirs" -version = "4.3.8" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/fe/8b/3c73abc9c759ecd3f1f7ceff6685840859e8070c4d947c93fae71f6a0bf2/platformdirs-4.3.8.tar.gz", hash = "sha256:3d512d96e16bcb959a814c9f348431070822a6496326a4be0911c40b5a74c2bc", size = 21362, upload-time = "2025-05-07T22:47:42.121Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/fe/39/979e8e21520d4e47a0bbe349e2713c0aac6f3d853d0e5b34d76206c439aa/platformdirs-4.3.8-py3-none-any.whl", hash = "sha256:ff7059bb7eb1179e2685604f4aaf157cfd9535242bd23742eadc3c13542139b4", size = 18567, upload-time = "2025-05-07T22:47:40.376Z" }, -] - -[[package]] -name = "pre-commit" -version = "4.2.0" +name = "pytest" +version = "9.1.1" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "cfgv" }, - { name = "identify" }, - { name = "nodeenv" }, - { name = "pyyaml" }, - { name = "virtualenv" }, + { name = "colorama", marker = "sys_platform == 'win32'" }, + { name = "iniconfig" }, + { name = "packaging" }, + { name = "pluggy" }, + { name = "pygments" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/08/39/679ca9b26c7bb2999ff122d50faa301e49af82ca9c066ec061cfbc0c6784/pre_commit-4.2.0.tar.gz", hash = "sha256:601283b9757afd87d40c4c4a9b2b5de9637a8ea02eaff7adc2d0fb4e04841146", size = 193424, upload-time = "2025-03-18T21:35:20.987Z" } +sdist = { url = "https://files.pythonhosted.org/packages/e4/47/b9efed96c114afcfa3c9d3fe98a76a1d14c74a9e266d397cf6eb64be5e01/pytest-9.1.1.tar.gz", hash = "sha256:1088fbde8f2b49d95a549a195707afa7a76a3ce9bcadc26b6d71f0ffda5fe313", size = 1636369, upload-time = "2026-06-19T10:58:32.857Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/88/74/a88bf1b1efeae488a0c0b7bdf71429c313722d1fc0f377537fbe554e6180/pre_commit-4.2.0-py2.py3-none-any.whl", hash = "sha256:a009ca7205f1eb497d10b845e52c838a98b6cdd2102a6c8e4540e94ee75c58bd", size = 220707, upload-time = "2025-03-18T21:35:19.343Z" }, + { url = "https://files.pythonhosted.org/packages/24/25/1de2678b631f5a49215c6c96fff41ba892b0a34df68d6d80292b1b48aa7f/pytest-9.1.1-py3-none-any.whl", hash = "sha256:37a86b45efb9a47a61a36449063e8e18d0cab3161329fc099eb21783169c4f0c", size = 386536, upload-time = "2026-06-19T10:58:31.347Z" }, ] [[package]] name = "python-base" version = "0.1.0" source = { editable = "." } -dependencies = [ - { name = "pre-commit" }, - { name = "ruff" }, - { name = "ty" }, -] [package.dev-dependencies] dev = [ - { name = "pre-commit" }, - { name = "ruff" }, - { name = "ty" }, + { name = "pytest" }, ] [package.metadata] -requires-dist = [ - { name = "pre-commit", specifier = ">=4.2.0" }, - { name = "ruff", specifier = ">=0.11.10" }, - { name = "ty", specifier = ">=0.0.0" }, -] [package.metadata.requires-dev] -dev = [ - { name = "pre-commit", specifier = ">=4.2.0" }, - { name = "ruff", specifier = ">=0.11.10" }, - { name = "ty", specifier = ">=0.0.0" }, -] - -[[package]] -name = "pyyaml" -version = "6.0.2" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/54/ed/79a089b6be93607fa5cdaedf301d7dfb23af5f25c398d5ead2525b063e17/pyyaml-6.0.2.tar.gz", hash = "sha256:d584d9ec91ad65861cc08d42e834324ef890a082e591037abe114850ff7bbc3e", size = 130631, upload-time = "2024-08-06T20:33:50.674Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/86/0c/c581167fc46d6d6d7ddcfb8c843a4de25bdd27e4466938109ca68492292c/PyYAML-6.0.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:c70c95198c015b85feafc136515252a261a84561b7b1d51e3384e0655ddf25ab", size = 183873, upload-time = "2024-08-06T20:32:25.131Z" }, - { url = "https://files.pythonhosted.org/packages/a8/0c/38374f5bb272c051e2a69281d71cba6fdb983413e6758b84482905e29a5d/PyYAML-6.0.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ce826d6ef20b1bc864f0a68340c8b3287705cae2f8b4b1d932177dcc76721725", size = 173302, upload-time = "2024-08-06T20:32:26.511Z" }, - { url = "https://files.pythonhosted.org/packages/c3/93/9916574aa8c00aa06bbac729972eb1071d002b8e158bd0e83a3b9a20a1f7/PyYAML-6.0.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1f71ea527786de97d1a0cc0eacd1defc0985dcf6b3f17bb77dcfc8c34bec4dc5", size = 739154, upload-time = "2024-08-06T20:32:28.363Z" }, - { url = "https://files.pythonhosted.org/packages/95/0f/b8938f1cbd09739c6da569d172531567dbcc9789e0029aa070856f123984/PyYAML-6.0.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9b22676e8097e9e22e36d6b7bda33190d0d400f345f23d4065d48f4ca7ae0425", size = 766223, upload-time = "2024-08-06T20:32:30.058Z" }, - { url = "https://files.pythonhosted.org/packages/b9/2b/614b4752f2e127db5cc206abc23a8c19678e92b23c3db30fc86ab731d3bd/PyYAML-6.0.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:80bab7bfc629882493af4aa31a4cfa43a4c57c83813253626916b8c7ada83476", size = 767542, upload-time = "2024-08-06T20:32:31.881Z" }, - { url = "https://files.pythonhosted.org/packages/d4/00/dd137d5bcc7efea1836d6264f049359861cf548469d18da90cd8216cf05f/PyYAML-6.0.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:0833f8694549e586547b576dcfaba4a6b55b9e96098b36cdc7ebefe667dfed48", size = 731164, upload-time = "2024-08-06T20:32:37.083Z" }, - { url = "https://files.pythonhosted.org/packages/c9/1f/4f998c900485e5c0ef43838363ba4a9723ac0ad73a9dc42068b12aaba4e4/PyYAML-6.0.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8b9c7197f7cb2738065c481a0461e50ad02f18c78cd75775628afb4d7137fb3b", size = 756611, upload-time = "2024-08-06T20:32:38.898Z" }, - { url = "https://files.pythonhosted.org/packages/df/d1/f5a275fdb252768b7a11ec63585bc38d0e87c9e05668a139fea92b80634c/PyYAML-6.0.2-cp312-cp312-win32.whl", hash = "sha256:ef6107725bd54b262d6dedcc2af448a266975032bc85ef0172c5f059da6325b4", size = 140591, upload-time = "2024-08-06T20:32:40.241Z" }, - { url = "https://files.pythonhosted.org/packages/0c/e8/4f648c598b17c3d06e8753d7d13d57542b30d56e6c2dedf9c331ae56312e/PyYAML-6.0.2-cp312-cp312-win_amd64.whl", hash = "sha256:7e7401d0de89a9a855c839bc697c079a4af81cf878373abd7dc625847d25cbd8", size = 156338, upload-time = "2024-08-06T20:32:41.93Z" }, - { url = "https://files.pythonhosted.org/packages/ef/e3/3af305b830494fa85d95f6d95ef7fa73f2ee1cc8ef5b495c7c3269fb835f/PyYAML-6.0.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:efdca5630322a10774e8e98e1af481aad470dd62c3170801852d752aa7a783ba", size = 181309, upload-time = "2024-08-06T20:32:43.4Z" }, - { url = "https://files.pythonhosted.org/packages/45/9f/3b1c20a0b7a3200524eb0076cc027a970d320bd3a6592873c85c92a08731/PyYAML-6.0.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:50187695423ffe49e2deacb8cd10510bc361faac997de9efef88badc3bb9e2d1", size = 171679, upload-time = "2024-08-06T20:32:44.801Z" }, - { url = "https://files.pythonhosted.org/packages/7c/9a/337322f27005c33bcb656c655fa78325b730324c78620e8328ae28b64d0c/PyYAML-6.0.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0ffe8360bab4910ef1b9e87fb812d8bc0a308b0d0eef8c8f44e0254ab3b07133", size = 733428, upload-time = "2024-08-06T20:32:46.432Z" }, - { url = "https://files.pythonhosted.org/packages/a3/69/864fbe19e6c18ea3cc196cbe5d392175b4cf3d5d0ac1403ec3f2d237ebb5/PyYAML-6.0.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:17e311b6c678207928d649faa7cb0d7b4c26a0ba73d41e99c4fff6b6c3276484", size = 763361, upload-time = "2024-08-06T20:32:51.188Z" }, - { url = "https://files.pythonhosted.org/packages/04/24/b7721e4845c2f162d26f50521b825fb061bc0a5afcf9a386840f23ea19fa/PyYAML-6.0.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:70b189594dbe54f75ab3a1acec5f1e3faa7e8cf2f1e08d9b561cb41b845f69d5", size = 759523, upload-time = "2024-08-06T20:32:53.019Z" }, - { url = "https://files.pythonhosted.org/packages/2b/b2/e3234f59ba06559c6ff63c4e10baea10e5e7df868092bf9ab40e5b9c56b6/PyYAML-6.0.2-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:41e4e3953a79407c794916fa277a82531dd93aad34e29c2a514c2c0c5fe971cc", size = 726660, upload-time = "2024-08-06T20:32:54.708Z" }, - { url = "https://files.pythonhosted.org/packages/fe/0f/25911a9f080464c59fab9027482f822b86bf0608957a5fcc6eaac85aa515/PyYAML-6.0.2-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:68ccc6023a3400877818152ad9a1033e3db8625d899c72eacb5a668902e4d652", size = 751597, upload-time = "2024-08-06T20:32:56.985Z" }, - { url = "https://files.pythonhosted.org/packages/14/0d/e2c3b43bbce3cf6bd97c840b46088a3031085179e596d4929729d8d68270/PyYAML-6.0.2-cp313-cp313-win32.whl", hash = "sha256:bc2fa7c6b47d6bc618dd7fb02ef6fdedb1090ec036abab80d4681424b84c1183", size = 140527, upload-time = "2024-08-06T20:33:03.001Z" }, - { url = "https://files.pythonhosted.org/packages/fa/de/02b54f42487e3d3c6efb3f89428677074ca7bf43aae402517bc7cca949f3/PyYAML-6.0.2-cp313-cp313-win_amd64.whl", hash = "sha256:8388ee1976c416731879ac16da0aff3f63b286ffdd57cdeb95f3f2e085687563", size = 156446, upload-time = "2024-08-06T20:33:04.33Z" }, -] - -[[package]] -name = "ruff" -version = "0.11.10" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/e8/4c/4a3c5a97faaae6b428b336dcca81d03ad04779f8072c267ad2bd860126bf/ruff-0.11.10.tar.gz", hash = "sha256:d522fb204b4959909ecac47da02830daec102eeb100fb50ea9554818d47a5fa6", size = 4165632, upload-time = "2025-05-15T14:08:56.76Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/2f/9f/596c628f8824a2ce4cd12b0f0b4c0629a62dfffc5d0f742c19a1d71be108/ruff-0.11.10-py3-none-linux_armv6l.whl", hash = "sha256:859a7bfa7bc8888abbea31ef8a2b411714e6a80f0d173c2a82f9041ed6b50f58", size = 10316243, upload-time = "2025-05-15T14:08:12.884Z" }, - { url = "https://files.pythonhosted.org/packages/3c/38/c1e0b77ab58b426f8c332c1d1d3432d9fc9a9ea622806e208220cb133c9e/ruff-0.11.10-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:968220a57e09ea5e4fd48ed1c646419961a0570727c7e069842edd018ee8afed", size = 11083636, upload-time = "2025-05-15T14:08:16.551Z" }, - { url = "https://files.pythonhosted.org/packages/23/41/b75e15961d6047d7fe1b13886e56e8413be8467a4e1be0a07f3b303cd65a/ruff-0.11.10-py3-none-macosx_11_0_arm64.whl", hash = "sha256:1067245bad978e7aa7b22f67113ecc6eb241dca0d9b696144256c3a879663bca", size = 10441624, upload-time = "2025-05-15T14:08:19.032Z" }, - { url = "https://files.pythonhosted.org/packages/b6/2c/e396b6703f131406db1811ea3d746f29d91b41bbd43ad572fea30da1435d/ruff-0.11.10-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f4854fd09c7aed5b1590e996a81aeff0c9ff51378b084eb5a0b9cd9518e6cff2", size = 10624358, upload-time = "2025-05-15T14:08:21.542Z" }, - { url = "https://files.pythonhosted.org/packages/bd/8c/ee6cca8bdaf0f9a3704796022851a33cd37d1340bceaf4f6e991eb164e2e/ruff-0.11.10-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:8b4564e9f99168c0f9195a0fd5fa5928004b33b377137f978055e40008a082c5", size = 10176850, upload-time = "2025-05-15T14:08:23.682Z" }, - { url = "https://files.pythonhosted.org/packages/e9/ce/4e27e131a434321b3b7c66512c3ee7505b446eb1c8a80777c023f7e876e6/ruff-0.11.10-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5b6a9cc5b62c03cc1fea0044ed8576379dbaf751d5503d718c973d5418483641", size = 11759787, upload-time = "2025-05-15T14:08:25.733Z" }, - { url = "https://files.pythonhosted.org/packages/58/de/1e2e77fc72adc7cf5b5123fd04a59ed329651d3eab9825674a9e640b100b/ruff-0.11.10-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:607ecbb6f03e44c9e0a93aedacb17b4eb4f3563d00e8b474298a201622677947", size = 12430479, upload-time = "2025-05-15T14:08:28.013Z" }, - { url = "https://files.pythonhosted.org/packages/07/ed/af0f2340f33b70d50121628ef175523cc4c37619e98d98748c85764c8d88/ruff-0.11.10-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7b3a522fa389402cd2137df9ddefe848f727250535c70dafa840badffb56b7a4", size = 11919760, upload-time = "2025-05-15T14:08:30.956Z" }, - { url = "https://files.pythonhosted.org/packages/24/09/d7b3d3226d535cb89234390f418d10e00a157b6c4a06dfbe723e9322cb7d/ruff-0.11.10-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2f071b0deed7e9245d5820dac235cbdd4ef99d7b12ff04c330a241ad3534319f", size = 14041747, upload-time = "2025-05-15T14:08:33.297Z" }, - { url = "https://files.pythonhosted.org/packages/62/b3/a63b4e91850e3f47f78795e6630ee9266cb6963de8f0191600289c2bb8f4/ruff-0.11.10-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4a60e3a0a617eafba1f2e4186d827759d65348fa53708ca547e384db28406a0b", size = 11550657, upload-time = "2025-05-15T14:08:35.639Z" }, - { url = "https://files.pythonhosted.org/packages/46/63/a4f95c241d79402ccdbdb1d823d156c89fbb36ebfc4289dce092e6c0aa8f/ruff-0.11.10-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:da8ec977eaa4b7bf75470fb575bea2cb41a0e07c7ea9d5a0a97d13dbca697bf2", size = 10489671, upload-time = "2025-05-15T14:08:38.437Z" }, - { url = "https://files.pythonhosted.org/packages/6a/9b/c2238bfebf1e473495659c523d50b1685258b6345d5ab0b418ca3f010cd7/ruff-0.11.10-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:ddf8967e08227d1bd95cc0851ef80d2ad9c7c0c5aab1eba31db49cf0a7b99523", size = 10160135, upload-time = "2025-05-15T14:08:41.247Z" }, - { url = "https://files.pythonhosted.org/packages/ba/ef/ba7251dd15206688dbfba7d413c0312e94df3b31b08f5d695580b755a899/ruff-0.11.10-py3-none-musllinux_1_2_i686.whl", hash = "sha256:5a94acf798a82db188f6f36575d80609072b032105d114b0f98661e1679c9125", size = 11170179, upload-time = "2025-05-15T14:08:43.762Z" }, - { url = "https://files.pythonhosted.org/packages/73/9f/5c336717293203ba275dbfa2ea16e49b29a9fd9a0ea8b6febfc17e133577/ruff-0.11.10-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:3afead355f1d16d95630df28d4ba17fb2cb9c8dfac8d21ced14984121f639bad", size = 11626021, upload-time = "2025-05-15T14:08:46.451Z" }, - { url = "https://files.pythonhosted.org/packages/d9/2b/162fa86d2639076667c9aa59196c020dc6d7023ac8f342416c2f5ec4bda0/ruff-0.11.10-py3-none-win32.whl", hash = "sha256:dc061a98d32a97211af7e7f3fa1d4ca2fcf919fb96c28f39551f35fc55bdbc19", size = 10494958, upload-time = "2025-05-15T14:08:49.601Z" }, - { url = "https://files.pythonhosted.org/packages/24/f3/66643d8f32f50a4b0d09a4832b7d919145ee2b944d43e604fbd7c144d175/ruff-0.11.10-py3-none-win_amd64.whl", hash = "sha256:5cc725fbb4d25b0f185cb42df07ab6b76c4489b4bfb740a175f3a59c70e8a224", size = 11650285, upload-time = "2025-05-15T14:08:52.392Z" }, - { url = "https://files.pythonhosted.org/packages/95/3a/2e8704d19f376c799748ff9cb041225c1d59f3e7711bc5596c8cfdc24925/ruff-0.11.10-py3-none-win_arm64.whl", hash = "sha256:ef69637b35fb8b210743926778d0e45e1bffa850a7c61e428c6b971549b5f5d1", size = 10765278, upload-time = "2025-05-15T14:08:54.56Z" }, -] - -[[package]] -name = "ty" -version = "0.0.19" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/84/5e/da108b9eeb392e02ff0478a34e9651490b36af295881cb56575b83f0cc3a/ty-0.0.19.tar.gz", hash = "sha256:ee3d9ed4cb586e77f6efe3d0fe5a855673ca438a3d533a27598e1d3502a2948a", size = 5220026, upload-time = "2026-02-26T12:13:15.215Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/5a/31/fd8c6067abb275bea11523d21ecf64e1d870b1ce80cac529cf6636df1471/ty-0.0.19-py3-none-linux_armv6l.whl", hash = "sha256:29bed05d34c8a7597567b8e327c53c1aed4a07dcfbe6c81e6d60c7444936ad77", size = 10268470, upload-time = "2026-02-26T12:13:42.881Z" }, - { url = "https://files.pythonhosted.org/packages/15/de/16a11bbf7d98c75849fc41f5d008b89bb5d080a4b10dc8ea851ee2bd371b/ty-0.0.19-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:79140870c688c97ec68e723c28935ddef9d91a76d48c68e665fe7c851e628b8a", size = 10098562, upload-time = "2026-02-26T12:13:31.618Z" }, - { url = "https://files.pythonhosted.org/packages/e7/4f/086d6ff6686eadf903913c45b53ab96694b62bbfee1d8cf3e55a9b5aa4b2/ty-0.0.19-py3-none-macosx_11_0_arm64.whl", hash = "sha256:6e9c1f9cfa6a26f7881d14d75cf963af743f6c4189e6aa3e3b4056a65f22e730", size = 9604073, upload-time = "2026-02-26T12:13:24.645Z" }, - { url = "https://files.pythonhosted.org/packages/95/13/888a6b6c7ed4a880fee91bec997f775153ce86215ee4c56b868516314734/ty-0.0.19-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bbca43b050edf1db2e64ae7b79add233c2aea2855b8a876081bbd032edcd0610", size = 10106295, upload-time = "2026-02-26T12:13:40.584Z" }, - { url = "https://files.pythonhosted.org/packages/cb/e8/05a372cae8da482de73b8246fb43236bf11e24ac28c879804568108759db/ty-0.0.19-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:8acaa88ab1955ca6b15a0ccc274011c4961377fe65c3948e5d2b212f2517b87c", size = 10098234, upload-time = "2026-02-26T12:13:33.725Z" }, - { url = "https://files.pythonhosted.org/packages/c5/f1/5b0958e9e9576e7662192fe689bbb3dc88e631a4e073db3047793a547d58/ty-0.0.19-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4a901b6a6dd9d17d5b3b2e7bafc3057294e88da3f5de507347316687d7f191a1", size = 10607218, upload-time = "2026-02-26T12:13:17.576Z" }, - { url = "https://files.pythonhosted.org/packages/fb/ab/358c78b77844f58ff5aca368550ab16c719f1ab0ec892ceb1114d7500f4e/ty-0.0.19-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8deafdaaaee65fd121c66064da74a922d8501be4a2d50049c71eab521a23eff7", size = 11160593, upload-time = "2026-02-26T12:13:36.008Z" }, - { url = "https://files.pythonhosted.org/packages/95/59/827fc346d66a59fe48e9689a5ceb67dbbd5b4de2e8d4625371af39a2e8b7/ty-0.0.19-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:035e56071af280897441018f74f921b97d53aec0856f8af85f4f949df8eda07d", size = 10822392, upload-time = "2026-02-26T12:13:29.415Z" }, - { url = "https://files.pythonhosted.org/packages/81/f9/3bbfbbe35478de9bcd63848f4bc9bffda72278dd9732dbad3efc3978432e/ty-0.0.19-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:abdf5885130393ce74501dba792f48ce0a515756ec81c33a4b324bdf3509df6e", size = 10707139, upload-time = "2026-02-26T12:13:20.148Z" }, - { url = "https://files.pythonhosted.org/packages/12/9e/597023b183ec4ade83a36a0cea5c103f3bffa34f70813d46386c61447fb8/ty-0.0.19-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:877e89005c8f9d1dbff5ad14cbac9f35c528406fde38926f9b44f24830de8d6a", size = 10096933, upload-time = "2026-02-26T12:13:45.266Z" }, - { url = "https://files.pythonhosted.org/packages/1e/76/d0d2f6e674db2a17c8efa5e26682b9dfa8d34774705f35902a7b45ebd3bd/ty-0.0.19-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:39bd1da051c1e4d316efaf79dbed313255633f7c6ad6e24d29f4d9c6ffaf4de6", size = 10109547, upload-time = "2026-02-26T12:13:22.17Z" }, - { url = "https://files.pythonhosted.org/packages/a4/b0/76026c06b852a3aa4fdb5bd329fdc2175aaf3c64a3fafece9cc4df167cee/ty-0.0.19-py3-none-musllinux_1_2_i686.whl", hash = "sha256:87df8415a6c9cb27b8f1382fcdc6052e59f5b9f50f78bc14663197eb5c8d3699", size = 10289110, upload-time = "2026-02-26T12:13:38.29Z" }, - { url = "https://files.pythonhosted.org/packages/14/6c/f3b3a189816b4f079b20fe5d0d7ee38e38a472f53cc6770bb6571147e3de/ty-0.0.19-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:89b6bb23c332ed5c38dd859eb5793f887abcc936f681a40d4ea68e35eac1af33", size = 10796479, upload-time = "2026-02-26T12:13:10.992Z" }, - { url = "https://files.pythonhosted.org/packages/3d/18/caee33d1ce9dd50bd94c26cde7cda4f6971e22e474e7d72a5c86d745ad58/ty-0.0.19-py3-none-win32.whl", hash = "sha256:19b33df3aa7af7b1a9eaa4e1175c3b4dec0f5f2e140243e3492c8355c37418f3", size = 9677215, upload-time = "2026-02-26T12:13:08.519Z" }, - { url = "https://files.pythonhosted.org/packages/81/41/18fc0771d0b1da7d7cc2fc9af278d3122b754fe8b521a748734f4e16ecfd/ty-0.0.19-py3-none-win_amd64.whl", hash = "sha256:b9052c61464cdd76bc8e6796f2588c08700f25d0dcbc225bb165e390ea9d96a4", size = 10651252, upload-time = "2026-02-26T12:13:13.035Z" }, - { url = "https://files.pythonhosted.org/packages/8b/8c/26f7ce8863eb54510082747b3dfb1046ba24f16fc11de18c0e5feb36ff18/ty-0.0.19-py3-none-win_arm64.whl", hash = "sha256:9329804b66dcbae8e7af916ef4963221ed53b8ec7d09b0793591c5ae8a0f3270", size = 10093195, upload-time = "2026-02-26T12:13:26.816Z" }, -] - -[[package]] -name = "virtualenv" -version = "20.31.2" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "distlib" }, - { name = "filelock" }, - { name = "platformdirs" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/56/2c/444f465fb2c65f40c3a104fd0c495184c4f2336d65baf398e3c75d72ea94/virtualenv-20.31.2.tar.gz", hash = "sha256:e10c0a9d02835e592521be48b332b6caee6887f332c111aa79a09b9e79efc2af", size = 6076316, upload-time = "2025-05-08T17:58:23.811Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/f3/40/b1c265d4b2b62b58576588510fc4d1fe60a86319c8de99fd8e9fec617d2c/virtualenv-20.31.2-py3-none-any.whl", hash = "sha256:36efd0d9650ee985f0cad72065001e66d49a6f24eb44d98980f630686243cf11", size = 6057982, upload-time = "2025-05-08T17:58:21.15Z" }, -] +dev = [{ name = "pytest", specifier = ">=8" }] From a9cf810fa7116098dc49c1e5445ffb767d56dc88 Mon Sep 17 00:00:00 2001 From: Gregory Mermoud Date: Wed, 15 Jul 2026 12:52:19 +0000 Subject: [PATCH 2/2] Pin setup-uv to v8.3.2 astral-sh/setup-uv publishes floating major tags only up to v7, so `@v8` did not resolve and the pre-commit job failed at setup. Pin the exact release instead. actions/checkout and actions/cache do publish current major tags, so those stay as they are. Co-Authored-By: Claude Opus 4.8 --- .github/workflows/ci.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 585867a..d91b45c 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -25,7 +25,7 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v7 - - uses: astral-sh/setup-uv@v8 + - uses: astral-sh/setup-uv@v8.3.2 with: python-version: ${{ env.PYTHON_VERSION }} enable-cache: true @@ -39,7 +39,7 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v7 - - uses: astral-sh/setup-uv@v8 + - uses: astral-sh/setup-uv@v8.3.2 with: python-version: ${{ env.PYTHON_VERSION }} enable-cache: true