diff --git a/.github/actions/python-install-uv/action.yml b/.github/actions/python-install-uv/action.yml new file mode 100644 index 00000000..523ec3bf --- /dev/null +++ b/.github/actions/python-install-uv/action.yml @@ -0,0 +1,75 @@ +name: Setup Python Venv (uv) + +# Composite action for Python projects that installs with uv + +inputs: + python-version: + description: A Python version to use + required: true + type: string + uv-version: + description: The uv version to use + required: false + type: string + default: "latest" + working-directory: + description: The working directory + required: false + type: string + default: . + delete-uv-lock: + description: Delete uv.lock file in order to test against newer versions + required: false + type: boolean + default: false + optional-dependency-groups: + description: Which optional group or groups to install. Use comma delimiter for multiple groups e.g `dev,doc`. + required: false + type: string + default: "" + +runs: + using: "composite" + steps: + + #---------------------------------------------- + # delete uv.lock file in order to run tests against newer versions + #---------------------------------------------- + - name: Delete uv.lock file + if: ${{ inputs.delete-uv-lock == 'true' }} + shell: bash + working-directory: ${{ inputs.working-directory }} + run: | + echo "::Warning title=uv.lock::uv.lock is not deleted in this workflow run even though this was requested. See https://github.com/GNS-Science/nshm-github-actions/issues/26." + + #---------------------------------------------- + # install uv (and Python via uv) + # caching is handled automatically by setup-uv + #---------------------------------------------- + - name: Install uv and Python + uses: astral-sh/setup-uv@v6 + with: + version: ${{ inputs.uv-version }} + python-version: ${{ inputs.python-version }} + enable-cache: true + cache-dependency-glob: ${{ inputs.working-directory }}/uv.lock + + #---------------------------------------------- + # install dependencies via uv sync + #---------------------------------------------- + - name: Install dependencies + shell: bash + working-directory: ${{ inputs.working-directory }} + env: + GROUPS: ${{ inputs.optional-dependency-groups }} + run: | + if [ -z "$GROUPS" ]; then + uv sync --no-install-project --all-extras + else + GROUP_FLAGS="" + IFS=',' read -ra GROUPS_ARR <<< "$GROUPS" + for g in "${GROUPS_ARR[@]}"; do + GROUP_FLAGS="$GROUP_FLAGS --group ${g// /}" + done + uv sync --no-install-project --all-extras $GROUP_FLAGS + fi diff --git a/.github/workflows/deploy-to-aws-test.yml b/.github/workflows/deploy-to-aws-test.yml index 243e067d..6fce065d 100644 --- a/.github/workflows/deploy-to-aws-test.yml +++ b/.github/workflows/deploy-to-aws-test.yml @@ -20,6 +20,7 @@ jobs: uses: ./.github/workflows/deploy-to-aws.yml with: python-version: 3.11 + node-version: 22.22.1 working-directory: samplePythonProject aws-login: false smoketest-query: 'query Query {country(code: "NZ") {capital}}' diff --git a/.github/workflows/deploy-to-aws.yml b/.github/workflows/deploy-to-aws.yml index ea593cf0..fdfcf7ea 100644 --- a/.github/workflows/deploy-to-aws.yml +++ b/.github/workflows/deploy-to-aws.yml @@ -99,7 +99,7 @@ jobs: working-directory: ${{ inputs.working-directory }} environment: ${{ (inputs.environment && ((github.ref == 'refs/heads/main') && 'AWS_PROD' || 'AWS_TEST')) || '' }} steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 - uses: actions/setup-python@v5 if: ${{ inputs.python-version != 'None' }} diff --git a/.github/workflows/python-deploy-docs-uv.yml b/.github/workflows/python-deploy-docs-uv.yml new file mode 100644 index 00000000..71fccf04 --- /dev/null +++ b/.github/workflows/python-deploy-docs-uv.yml @@ -0,0 +1,95 @@ +name: Python Deploy Docs (uv) + +# Publishes documentation for a python package to GitHub pages using uv. Uses `mkdocs` for building documentation. +# Would commonly be one of two jobs along with Python Release Workflow in a repo workflow triggered by e.g. tagging a version. + +on: + workflow_call: + inputs: + python-version: + description: The python version to use + required: true + type: string + default: "3.10" + uv-version: + description: The uv version to use + required: false + type: string + default: "latest" + working-directory: + description: The working directory + required: false + type: string + default: . + timeout-minutes: + description: Maximum runtime in minutes + required: false + type: number + default: 10 + optional-dependency-groups: + description: Which optional group or groups to install. Use comma delimiter for multiple groups e.g `dev,doc`. + required: false + type: string + default: dev + + +# Sets permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages +permissions: + contents: write + pages: write + id-token: write + +# Allow only one concurrent deployment, skipping runs queued between the run in-progress and latest queued. +# However, do NOT cancel in-progress runs as we want to allow these production deployments to complete. +concurrency: + group: "pages" + cancel-in-progress: false + +jobs: + deploy: + environment: + name: github-pages + url: ${{ steps.deployment.outputs.page_url }} + runs-on: ubuntu-latest + timeout-minutes: ${{ inputs.timeout-minutes }} + defaults: + run: + working-directory: ${{ inputs.working-directory }} + + steps: + + - uses: actions/checkout@v5 + + - name: install_package + uses: GNS-Science/nshm-github-actions/.github/actions/python-install-uv@main + with: + python-version: ${{ inputs.python-version }} + uv-version: ${{ inputs.uv-version }} + working-directory: ${{ inputs.working-directory }} + optional-dependency-groups: ${{ inputs.optional-dependency-groups }} + + #---------------------------------------------- + # build documentation + # - uv sync installs the project root by default; the composite action + # uses --no-install-project, so sync here to bring it in before building + #---------------------------------------------- + - name: build documentation + run: | + uv sync + uv run mkdocs build + + #---------------------------------------------- + # package and upload artifact for deployment to GitHub Pages + #---------------------------------------------- + - name: Upload artifact + uses: actions/upload-pages-artifact@v4 + with: + path: ${{ inputs.working-directory }}/site + + + #---------------------------------------------- + # deploy page + #---------------------------------------------- + - name: Deploy Docs to GitHub Pages + id: deployment + uses: actions/deploy-pages@v4 diff --git a/.github/workflows/python-deploy-docs.yml b/.github/workflows/python-deploy-docs.yml index 287227f6..5ef55727 100644 --- a/.github/workflows/python-deploy-docs.yml +++ b/.github/workflows/python-deploy-docs.yml @@ -58,7 +58,7 @@ jobs: steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 - name: install_package uses: GNS-Science/nshm-github-actions/.github/actions/python-install@main diff --git a/.github/workflows/python-release-uv.yml b/.github/workflows/python-release-uv.yml new file mode 100644 index 00000000..ed4197ad --- /dev/null +++ b/.github/workflows/python-release-uv.yml @@ -0,0 +1,117 @@ +name: Python Release (uv) + +# Creates a GitHub release and publishes the package to PyPI using uv. Would commonly be one of two jobs along +# with Python Deploy Docs in a repo workflow triggered by e.g. tagging a version. +# +# Requires the secret `PYPI_API_TOKEN`. + +on: + workflow_call: + inputs: + python-version: + description: The python version to use + required: true + type: string + default: "3.10" + uv-version: + description: The uv version to use + required: false + type: string + default: "latest" + working-directory: + description: The working directory + required: false + type: string + default: . + timeout-minutes: + description: Maximum runtime in minutes + required: false + type: number + default: 10 + pypi-publish: + description: Publish to PyPI or not + required: false + type: boolean + default: true + + +# Allow only one concurrent deployment, skipping runs queued between the run in-progress and latest queued. +# However, do NOT cancel in-progress runs as we want to allow these production deployments to complete. +concurrency: + group: "pages" + cancel-in-progress: false + +jobs: + release: + runs-on: ubuntu-latest + timeout-minutes: ${{ inputs.timeout-minutes }} + defaults: + run: + shell: bash + working-directory: ${{ inputs.working-directory }} + steps: + + - uses: actions/checkout@v5 + + - name: install_package + uses: GNS-Science/nshm-github-actions/.github/actions/python-install-uv@main + with: + python-version: ${{ inputs.python-version }} + uv-version: ${{ inputs.uv-version }} + working-directory: ${{ inputs.working-directory }} + + #---------------------------------------------- + # get version + #---------------------------------------------- + - name: Get version from tag + id: tag_name + run: | + echo name=current_version::${GITHUB_REF#refs/tags/v} >> "$GITHUB_OUTPUT" + shell: bash + + #---------------------------------------------- + # get changelog entry + #---------------------------------------------- + - name: Get Changelog Entry + id: changelog_reader + uses: GNS-Science/changelog-reader-action@master + with: + validation_depth: 10 + version: ${{ steps.tag_name.outputs.current_version }} + path: ${{ inputs.working-directory }}/CHANGELOG.md + + #---------------------------------------------- + # build wheels and tarball + #---------------------------------------------- + - name: Build wheels and source tarball + run: >- + uv build + + - name: show temporary files + run: >- + ls -l + + #---------------------------------------------- + # create GitHub release + #---------------------------------------------- + - name: create github release + id: create_release + uses: GNS-Science/action-gh-release@master + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + body: ${{ steps.changelog_reader.outputs.changes }} + files: ${{ inputs.working-directory }}/dist/*.whl + draft: false + prerelease: false + + #---------------------------------------------- + # publish to PyPI + #---------------------------------------------- + - name: publish to PyPI + if: ${{ inputs.pypi-publish }} + uses: pypa/gh-action-pypi-publish@release/v1 + with: + user: __token__ + password: ${{ secrets.PYPI_API_TOKEN }} + skip-existing: true diff --git a/.github/workflows/python-release.yml b/.github/workflows/python-release.yml index fbc811f9..fd19b4b1 100644 --- a/.github/workflows/python-release.yml +++ b/.github/workflows/python-release.yml @@ -51,7 +51,7 @@ jobs: working-directory: ${{ inputs.working-directory }} steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 - name: install_package uses: GNS-Science/nshm-github-actions/.github/actions/python-install@main diff --git a/.github/workflows/python-run-tests-test-uv.yml b/.github/workflows/python-run-tests-test-uv.yml new file mode 100644 index 00000000..c8fd3936 --- /dev/null +++ b/.github/workflows/python-run-tests-test-uv.yml @@ -0,0 +1,20 @@ +# A workflow that runs on push/PR to check that python-run-tests-uv.yml works + +name: python-run-tests-uv test workflow + +on: + workflow_dispatch: + push: + branches: [main, pre-release] + pull_request: + branches: [main, pre-release] + +jobs: + call-test-workflow: + uses: ./.github/workflows/python-run-tests-uv.yml + with: + operating-systems: "['ubuntu-latest', 'macos-latest', 'windows-latest']" + python-versions: "['3.10', '3.11', '3.12', '3.13']" + working-directory: samplePythonProjectUv + fail-on-codecov-error: false + secrets: inherit diff --git a/.github/workflows/python-run-tests-test.yml b/.github/workflows/python-run-tests-test.yml index 00d8c1c7..e8591c8f 100644 --- a/.github/workflows/python-run-tests-test.yml +++ b/.github/workflows/python-run-tests-test.yml @@ -18,7 +18,7 @@ jobs: uses: ./.github/workflows/python-run-tests.yml with: operating-systems: "['ubuntu-latest', 'macos-latest', 'windows-latest']" - python-versions: "['3.9', '3.10', '3.11']" + python-versions: "['3.10', '3.11', '3.12', '3.13']" working-directory: samplePythonProject fail-on-codecov-error: false secrets: inherit diff --git a/.github/workflows/python-run-tests-uv.yml b/.github/workflows/python-run-tests-uv.yml new file mode 100644 index 00000000..d66600ae --- /dev/null +++ b/.github/workflows/python-run-tests-uv.yml @@ -0,0 +1,145 @@ +name: Run Python Tests (uv) + +# Reusable workflow for Python projects that run tests with uv +# +# For each combination of `python-versions` and `operating-systems`: +# - runs `tox` via uv for the current Python installation +# - uploads coverage data to Codecov - only for the first Python version and a Linux OS to avoid double-ups +# +# Matrix arrays `operating-systems` and `python-versions` are required and need to be provided as stringified JSON. +# See default values for examples. This is because reusable workflows cannot take arrays as arguments. +# +# Set up the SCHEDULED_GITHUB_SLACK_WEBHOOK secret to receive Slack notification of build failures of scheduled runs. +# Messages will be posted to the `cwg-scheduled-builds` channel: https://nshmrevisionproject.slack.com/archives/C08QYS2ER9T + +on: + workflow_call: + inputs: + operating-systems: + description: A stringified JSON array of all operating systems to use + required: true + type: string + default: "['ubuntu-latest', 'windows-latest', 'macos-latest']" + python-versions: + description: A stringified JSON array of all Python versions to use + required: true + type: string + default: "['3.10', '3.11', '3.12']" + fail-fast: + description: Set to false if you want all jobs finish running even if one fails. See https://docs.github.com/en/actions/writing-workflows/workflow-syntax-for-github-actions#jobsjob_idstrategyfail-fast + required: false + type: boolean + default: true + timeout-minutes: + description: Maximum runtime in minutes + required: false + type: number + default: 10 + uv-version: + description: The uv version to use + required: false + type: string + default: "latest" + fail-on-codecov-error: + description: Whether to fail if Codecov upload encounters an error + required: false + type: boolean + default: true + working-directory: + description: The working directory + required: false + type: string + default: . + delete-uv-lock: + description: Delete uv.lock file in order to test against newer versions + required: false + type: boolean + default: false + optional-dependency-groups: + description: Which optional group or groups to install. Use comma delimiter for multiple groups e.g `dev,doc`. + required: false + type: string + default: dev + +jobs: + run_unit_tests: + timeout-minutes: ${{ inputs.timeout-minutes }} + + strategy: + fail-fast: ${{ inputs.fail-fast}} + matrix: + python-version: ${{ fromJson(inputs.python-versions) }} + os: ${{ fromJson(inputs.operating-systems) }} + + runs-on: ${{ matrix.os }} + defaults: + run: + shell: bash + working-directory: ${{ inputs.working-directory }} + env: + OS: ${{ matrix.os }} + PYTHON: ${{ matrix.python-version }} + steps: + + - uses: actions/checkout@v5 + + - name: install_package + uses: GNS-Science/nshm-github-actions/.github/actions/python-install-uv@main + with: + python-version: ${{ matrix.python-version }} + uv-version: ${{ inputs.uv-version }} + working-directory: ${{ inputs.working-directory }} + delete-uv-lock: ${{ inputs.delete-uv-lock }} + optional-dependency-groups: ${{ inputs.optional-dependency-groups }} + + #---------------------------------------------- + # Install tox-gh-actions if not already present + #---------------------------------------------- + - name: Install tox-gh-actions + working-directory: ${{ inputs.working-directory }} + run: | + if uv pip list | grep -q tox-gh-actions; then + echo "tox-gh-actions is already installed" + else + uv add --group dev tox-gh-actions + fi + + #---------------------------------------------- + # Test with tox + #---------------------------------------------- + - name: test with tox (uses tox-gh-actions to select correct environment) + run: | + uv run tox + + #---------------------------------------------- + # list files + #---------------------------------------------- + - name: list files + run: ls -l . + + #---------------------------------------------- + # upload coverage report to Codecov + #---------------------------------------------- + - name: Submit coverage report + if: fromJson(inputs.python-versions)[0] == matrix.python-version && runner.os == 'Linux' + uses: codecov/codecov-action@v5 + with: + files: ./coverage.xml + flags: unittests + name: codecov-umbrella + token: ${{ secrets.CODECOV_TOKEN }} + verbose: true + env_vars: OS,PYTHON + fail_ci_if_error: ${{ inputs.fail-on-codecov-error }} + + #---------------------------------------------- + # Post error msg to Slack for failed scheduled builds + #---------------------------------------------- + - name: Post a Slack message if build failed + uses: slackapi/slack-github-action@v2.0.0 + if: failure() && github.event_name == 'schedule' + with: + webhook: ${{ secrets.SCHEDULED_GITHUB_SLACK_WEBHOOK }} + webhook-type: incoming-webhook + payload: | + text: "⚠️*${{ job.status }}*\n<${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}|${{ github.event.repository.name }}, ${{ matrix.os }}, Python ${{ matrix.python-version }}>" diff --git a/.github/workflows/python-run-tests.yml b/.github/workflows/python-run-tests.yml index ae8ab4e4..1e3336c2 100644 --- a/.github/workflows/python-run-tests.yml +++ b/.github/workflows/python-run-tests.yml @@ -25,7 +25,7 @@ on: description: A stringified JSON array of all Python versions to use required: true type: string - default: "['3.10', '3.11']" + default: "['3.10', '3.11', '3.12']" fail-fast: description: Set to false if you want all jobs finish running even if one fails. See https://docs.github.com/en/actions/writing-workflows/workflow-syntax-for-github-actions#jobsjob_idstrategyfail-fast required: false @@ -82,7 +82,7 @@ jobs: PYTHON: ${{ matrix.python-version }} steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 - name: install_package uses: GNS-Science/nshm-github-actions/.github/actions/python-install@main diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 00000000..6a7be20a --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,30 @@ +# CLAUDE.md + +This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. + +## Repository purpose + +Central home for GNS-Science NSHM **reusable GitHub Actions workflows** and composite actions, consumed by downstream NSHM repos via `uses: GNS-Science/nshm-github-actions/.github/workflows/.yml@`. This repo is not an application — changes here ship by being referenced from other repos' workflows. + +## Layout + +- `.github/workflows/` — reusable workflows (callable via `workflow_call`): + - `python-run-tests.yml` — runs `poetry run tox`, uploads to Codecov (needs `CODECOV_TOKEN`). + - `python-release.yml` — GitHub release + PyPI publish (needs `PYPI_API_TOKEN`). + - `python-deploy-docs.yml` — mkdocs build + deploy to GitHub Pages (`github-pages` env must permit the ref). + - `deploy-to-aws.yml` — deploys Python/JS APIs to AWS; see file header for full input/secret matrix. + - `*-test.yml` — self-test workflows that invoke the reusable workflows against `samplePythonProject/` on push/PR to verify changes before consumers pick them up. +- `.github/actions/` — composite actions reused by the workflows: + - `python-install/` — standardized Python + Poetry setup. + - `apiSmokeTest/` — post-deploy smoke test for deployed APIs. +- `samplePythonProject/` — minimal Poetry + tox project used exclusively as a fixture for the `*-test.yml` workflows. Not a published package. + +## Working on workflows + +- When editing a reusable workflow, also check/update its `*-test.yml` counterpart and run it (push to a branch) to validate end-to-end before merging — downstream repos pin to refs here, so breakage is remote. +- Downstream examples in `README.MD` pin to `@main` or feature branches; breaking input/secret changes need a coordinated bump. +- Secrets/inputs for `deploy-to-aws.yml` are documented inline in the workflow file — treat it as the source of truth over README. + +## samplePythonProject + +Uses Poetry + tox. To reproduce the test-workflow locally: `cd samplePythonProject && poetry install && poetry run tox`. Only touch it when the fixture needs to exercise a new workflow feature. diff --git a/README.MD b/README.MD index 4114d382..6ea816ab 100644 --- a/README.MD +++ b/README.MD @@ -20,7 +20,7 @@ jobs: uses: GNS-Science/nshm-github-actions/.github/workflows/python-run-tests.yml@feature/create-test-workflow with: operating-systems: "['ubuntu-latest', 'macos-latest', 'windows.latest']" - python-versions: "['3.9', '3.10', '3.11']" + python-versions: "['3.10', '3.11', '3.12']" secrets: inherit ``` @@ -59,6 +59,52 @@ jobs: secrets: inherit ``` +## uv Workflows + +The workflows below are uv-based equivalents of the Poetry workflows above. Use them for projects that have migrated from Poetry to [uv](https://docs.astral.sh/uv/). + +### [Python Tests Workflow (uv)](./.github/workflows/python-run-tests-uv.yml) + +Equivalent of the Poetry tests workflow. Runs `uv run tox` and uploads coverage data to Codecov. + +Minimal setup: + +```yml +jobs: + call-test-workflow: + uses: GNS-Science/nshm-github-actions/.github/workflows/python-run-tests-uv.yml@main + with: + operating-systems: "['ubuntu-latest', 'macos-latest', 'windows-latest']" + python-versions: "['3.10', '3.11', '3.12']" + secrets: inherit +``` + +### [Python Release Workflow (uv)](./.github/workflows/python-release-uv.yml) + +Creates a GitHub release and publishes the package to PyPI using `uv build`. Requires the secret `PYPI_API_TOKEN`. + +```yml +jobs: + release-and-distribute: + uses: GNS-Science/nshm-github-actions/.github/workflows/python-release-uv.yml@main + with: + python-version: '3.12' + secrets: inherit +``` + +### [Python Deploy Docs Workflow (uv)](./.github/workflows/python-deploy-docs-uv.yml) + +Publishes mkdocs documentation to GitHub Pages using uv. + +```yml +jobs: + deploy-docs: + uses: GNS-Science/nshm-github-actions/.github/workflows/python-deploy-docs-uv.yml@main + with: + python-version: '3.12' + secrets: inherit +``` + ## API Deployment [deploy-to-aws.yml](./.github/workflows/deploy-to-aws.yml) deploys Python and JS APIs to AWS. diff --git a/samplePythonProject/poetry.lock b/samplePythonProject/poetry.lock index c1191a3c..9d7342b6 100644 --- a/samplePythonProject/poetry.lock +++ b/samplePythonProject/poetry.lock @@ -1,4 +1,4 @@ -# This file is automatically @generated by Poetry 2.1.3 and should not be changed by hand. +# This file is automatically @generated by Poetry 2.3.1 and should not be changed by hand. [[package]] name = "cachetools" @@ -56,7 +56,7 @@ description = "Backport of PEP 654 (exception groups)" optional = false python-versions = ">=3.7" groups = ["main"] -markers = "python_version < \"3.11\"" +markers = "python_version == \"3.10\"" files = [ {file = "exceptiongroup-1.2.2-py3-none-any.whl", hash = "sha256:3111b9d131c238bec2f8f516e123e14ba243563fb135d3fe885990585aa7795b"}, {file = "exceptiongroup-1.2.2.tar.gz", hash = "sha256:47c2edf7c6738fafb49fd34290706d1a1a2f4d1c6df275526b62cbb4aa5393cc"}, @@ -189,7 +189,7 @@ description = "A lil' TOML parser" optional = false python-versions = ">=3.8" groups = ["main", "dev"] -markers = "python_version < \"3.11\"" +markers = "python_version == \"3.10\"" files = [ {file = "tomli-2.2.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:678e4fa69e4575eb77d103de3df8a895e1591b48e740211bd1067378c69e8249"}, {file = "tomli-2.2.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:023aa114dd824ade0100497eb2318602af309e5a55595f76b626d6d9f3b7b0a6"}, @@ -260,7 +260,7 @@ description = "Backported and Experimental Type Hints for Python 3.8+" optional = false python-versions = ">=3.8" groups = ["dev"] -markers = "python_version < \"3.11\"" +markers = "python_version == \"3.10\"" files = [ {file = "typing_extensions-4.13.2-py3-none-any.whl", hash = "sha256:a439e7c04b49fec3e5d3e2beaa21755cadbbdc391694e28ccdd36ca4a1408f8c"}, {file = "typing_extensions-4.13.2.tar.gz", hash = "sha256:e6c81219bd689f51865d9e372991c540bda33a0379d5573cddb9a3a23f7caaef"}, @@ -289,5 +289,5 @@ test = ["covdefaults (>=2.3)", "coverage (>=7.2.7)", "coverage-enable-subprocess [metadata] lock-version = "2.1" -python-versions = ">=3.9,<4.0" -content-hash = "807c203c4df942d596acda53d8ba762afab00b92e00b68632083b0efaf030c80" +python-versions = ">=3.10,<4.0" +content-hash = "a1c5d522fe29b831e055d3d1ed9090a7d4cab4c8030cefb2df855d55a4d64004" diff --git a/samplePythonProject/pyproject.toml b/samplePythonProject/pyproject.toml index 7a7f137d..a9ea098b 100644 --- a/samplePythonProject/pyproject.toml +++ b/samplePythonProject/pyproject.toml @@ -15,7 +15,7 @@ dependencies = [ ] [tool.poetry.dependencies] -python = ">=3.9,<4.0" +python = ">=3.10,<4.0" [build-system] requires = ["poetry-core>=2.0.0,<3.0.0"] diff --git a/samplePythonProject/tox.ini b/samplePythonProject/tox.ini index 922d609a..0c592ad8 100644 --- a/samplePythonProject/tox.ini +++ b/samplePythonProject/tox.ini @@ -1,5 +1,5 @@ [tox] -envlist = py39, py310, py311, py312 +envlist = py310, py311, py312, py313 [testenv] deps = pytest diff --git a/samplePythonProjectUv/README.md b/samplePythonProjectUv/README.md new file mode 100644 index 00000000..fb98c1f7 --- /dev/null +++ b/samplePythonProjectUv/README.md @@ -0,0 +1,3 @@ +# samplePythonProjectUv + +A minimal uv-based fixture used by the `python-*-uv` self-test workflows. diff --git a/samplePythonProjectUv/pyproject.toml b/samplePythonProjectUv/pyproject.toml new file mode 100644 index 00000000..4dd5f972 --- /dev/null +++ b/samplePythonProjectUv/pyproject.toml @@ -0,0 +1,22 @@ +[project] +name = "theproject" +version = "0.1.0" +description = "" +readme = "README.md" +requires-python = ">=3.10,<4.0" +dependencies = [] + +[build-system] +requires = ["hatchling"] +build-backend = "hatchling.build" + +[tool.hatch.build.targets.wheel] +packages = ["src/theproject"] + +[dependency-groups] +dev = [ + "pytest>=8.3.5,<9.0.0", + "tox>=4.25.0", + "tox-uv>=1.0.0", + "tox-gh-actions>=3.0.0", +] diff --git a/samplePythonProjectUv/src/theproject/__init__.py b/samplePythonProjectUv/src/theproject/__init__.py new file mode 100644 index 00000000..b289a51f --- /dev/null +++ b/samplePythonProjectUv/src/theproject/__init__.py @@ -0,0 +1,2 @@ +def echo_stuff(msg: str): + print(msg) \ No newline at end of file diff --git a/samplePythonProjectUv/src/theproject/dummyFile.py b/samplePythonProjectUv/src/theproject/dummyFile.py new file mode 100644 index 00000000..e69de29b diff --git a/samplePythonProjectUv/tests/test_basic.py b/samplePythonProjectUv/tests/test_basic.py new file mode 100644 index 00000000..95c5ab74 --- /dev/null +++ b/samplePythonProjectUv/tests/test_basic.py @@ -0,0 +1,2 @@ +def test_example(): + assert 1 + 1 == 2 diff --git a/samplePythonProjectUv/tox.ini b/samplePythonProjectUv/tox.ini new file mode 100644 index 00000000..0c592ad8 --- /dev/null +++ b/samplePythonProjectUv/tox.ini @@ -0,0 +1,6 @@ +[tox] +envlist = py310, py311, py312, py313 + +[testenv] +deps = pytest +commands = pytest diff --git a/samplePythonProjectUv/uv.lock b/samplePythonProjectUv/uv.lock new file mode 100644 index 00000000..08a2df19 --- /dev/null +++ b/samplePythonProjectUv/uv.lock @@ -0,0 +1,338 @@ +version = 1 +revision = 3 +requires-python = ">=3.10, <4.0" + +[[package]] +name = "cachetools" +version = "7.0.5" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/af/dd/57fe3fdb6e65b25a5987fd2cdc7e22db0aef508b91634d2e57d22928d41b/cachetools-7.0.5.tar.gz", hash = "sha256:0cd042c24377200c1dcd225f8b7b12b0ca53cc2c961b43757e774ebe190fd990", size = 37367, upload-time = "2026-03-09T20:51:29.451Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/06/f3/39cf3367b8107baa44f861dc802cbf16263c945b62d8265d36034fc07bea/cachetools-7.0.5-py3-none-any.whl", hash = "sha256:46bc8ebefbe485407621d0a4264b23c080cedd913921bad7ac3ed2f26c183114", size = 13918, upload-time = "2026-03-09T20:51:27.33Z" }, +] + +[[package]] +name = "colorama" +version = "0.4.6" +source = { registry = "https://pypi.org/simple" } +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/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.4.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/96/8e/709914eb2b5749865801041647dc7f4e6d00b549cfe88b65ca192995f07c/distlib-0.4.0.tar.gz", hash = "sha256:feec40075be03a04501a973d81f633735b4b69f98b05450592310c0f401a4e0d", size = 614605, upload-time = "2025-07-17T16:52:00.465Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/33/6b/e0547afaf41bf2c42e52430072fa5658766e3d65bd4b03a563d1b6336f57/distlib-0.4.0-py2.py3-none-any.whl", hash = "sha256:9659f7d87e46584a30b5780e43ac7a2143098441670ff0a49d5f9034c54a6c16", size = 469047, upload-time = "2025-07-17T16:51:58.613Z" }, +] + +[[package]] +name = "exceptiongroup" +version = "1.3.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "typing-extensions", marker = "python_full_version < '3.13'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/50/79/66800aadf48771f6b62f7eb014e352e5d06856655206165d775e675a02c9/exceptiongroup-1.3.1.tar.gz", hash = "sha256:8b412432c6055b0b7d14c310000ae93352ed6754f70fa8f7c34141f91c4e3219", size = 30371, upload-time = "2025-11-21T23:01:54.787Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/8a/0e/97c33bf5009bdbac74fd2beace167cab3f978feb69cc36f1ef79360d6c4e/exceptiongroup-1.3.1-py3-none-any.whl", hash = "sha256:a7a39a3bd276781e98394987d3a5701d0c4edffb633bb7a5144577f82c773598", size = 16740, upload-time = "2025-11-21T23:01:53.443Z" }, +] + +[[package]] +name = "filelock" +version = "3.25.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/94/b8/00651a0f559862f3bb7d6f7477b192afe3f583cc5e26403b44e59a55ab34/filelock-3.25.2.tar.gz", hash = "sha256:b64ece2b38f4ca29dd3e810287aa8c48182bbecd1ae6e9ae126c9b35f1382694", size = 40480, upload-time = "2026-03-11T20:45:38.487Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a4/a5/842ae8f0c08b61d6484b52f99a03510a3a72d23141942d216ebe81fefbce/filelock-3.25.2-py3-none-any.whl", hash = "sha256:ca8afb0da15f229774c9ad1b455ed96e85a81373065fb10446672f64444ddf70", size = 26759, upload-time = "2026-03-11T20:45:37.437Z" }, +] + +[[package]] +name = "iniconfig" +version = "2.3.0" +source = { registry = "https://pypi.org/simple" } +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/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 = "packaging" +version = "26.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/65/ee/299d360cdc32edc7d2cf530f3accf79c4fca01e96ffc950d8a52213bd8e4/packaging-26.0.tar.gz", hash = "sha256:00243ae351a257117b6a241061796684b084ed1c516a08c48a3f7e147a9d80b4", size = 143416, upload-time = "2026-01-21T20:50:39.064Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b7/b9/c538f279a4e237a006a2c98387d081e9eb060d203d8ed34467cc0f0b9b53/packaging-26.0-py3-none-any.whl", hash = "sha256:b36f1fef9334a5588b4166f8bcd26a14e521f2b55e6b9de3aaa80d3ff7a37529", size = 74366, upload-time = "2026-01-21T20:50:37.788Z" }, +] + +[[package]] +name = "platformdirs" +version = "4.9.6" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/9f/4a/0883b8e3802965322523f0b200ecf33d31f10991d0401162f4b23c698b42/platformdirs-4.9.6.tar.gz", hash = "sha256:3bfa75b0ad0db84096ae777218481852c0ebc6c727b3168c1b9e0118e458cf0a", size = 29400, upload-time = "2026-04-09T00:04:10.812Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/75/a6/a0a304dc33b49145b21f4808d763822111e67d1c3a32b524a1baf947b6e1/platformdirs-4.9.6-py3-none-any.whl", hash = "sha256:e61adb1d5e5cb3441b4b7710bea7e4c12250ca49439228cc1021c00dcfac0917", size = 21348, upload-time = "2026-04-09T00:04:09.463Z" }, +] + +[[package]] +name = "pluggy" +version = "1.6.0" +source = { registry = "https://pypi.org/simple" } +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/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 = "pygments" +version = "2.20.0" +source = { registry = "https://pypi.org/simple" } +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/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 = "pyproject-api" +version = "1.10.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "packaging" }, + { name = "tomli", marker = "python_full_version < '3.11'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/45/7b/c0e1333b61d41c69e59e5366e727b18c4992688caf0de1be10b3e5265f6b/pyproject_api-1.10.0.tar.gz", hash = "sha256:40c6f2d82eebdc4afee61c773ed208c04c19db4c4a60d97f8d7be3ebc0bbb330", size = 22785, upload-time = "2025-10-09T19:12:27.21Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/54/cc/cecf97be298bee2b2a37dd360618c819a2a7fd95251d8e480c1f0eb88f3b/pyproject_api-1.10.0-py3-none-any.whl", hash = "sha256:8757c41a79c0f4ab71b99abed52b97ecf66bd20b04fa59da43b5840bac105a09", size = 13218, upload-time = "2025-10-09T19:12:24.428Z" }, +] + +[[package]] +name = "pytest" +version = "8.4.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "colorama", marker = "sys_platform == 'win32'" }, + { name = "exceptiongroup", marker = "python_full_version < '3.11'" }, + { name = "iniconfig" }, + { name = "packaging" }, + { name = "pluggy" }, + { name = "pygments" }, + { name = "tomli", marker = "python_full_version < '3.11'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/a3/5c/00a0e072241553e1a7496d638deababa67c5058571567b92a7eaa258397c/pytest-8.4.2.tar.gz", hash = "sha256:86c0d0b93306b961d58d62a4db4879f27fe25513d4b969df351abdddb3c30e01", size = 1519618, upload-time = "2025-09-04T14:34:22.711Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a8/a4/20da314d277121d6534b3a980b29035dcd51e6744bd79075a6ce8fa4eb8d/pytest-8.4.2-py3-none-any.whl", hash = "sha256:872f880de3fc3a5bdc88a11b39c9710c3497a547cfa9320bc3c5e62fbf272e79", size = 365750, upload-time = "2025-09-04T14:34:20.226Z" }, +] + +[[package]] +name = "python-discovery" +version = "1.2.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "filelock" }, + { name = "platformdirs" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/de/ef/3bae0e537cfe91e8431efcba4434463d2c5a65f5a89edd47c6cf2f03c55f/python_discovery-1.2.2.tar.gz", hash = "sha256:876e9c57139eb757cb5878cbdd9ae5379e5d96266c99ef731119e04fffe533bb", size = 58872, upload-time = "2026-04-07T17:28:49.249Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d8/db/795879cc3ddfe338599bddea6388cc5100b088db0a4caf6e6c1af1c27e04/python_discovery-1.2.2-py3-none-any.whl", hash = "sha256:e1ae95d9af875e78f15e19aed0c6137ab1bb49c200f21f5061786490c9585c7a", size = 31894, upload-time = "2026-04-07T17:28:48.09Z" }, +] + +[[package]] +name = "theproject" +version = "0.1.0" +source = { editable = "." } + +[package.dev-dependencies] +dev = [ + { name = "pytest" }, + { name = "tox" }, + { name = "tox-gh-actions" }, + { name = "tox-uv" }, +] + +[package.metadata] + +[package.metadata.requires-dev] +dev = [ + { name = "pytest", specifier = ">=8.3.5,<9.0.0" }, + { name = "tox", specifier = ">=4.25.0" }, + { name = "tox-gh-actions", specifier = ">=3.0.0" }, + { name = "tox-uv", specifier = ">=1.0.0" }, +] + +[[package]] +name = "tomli" +version = "2.4.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/22/de/48c59722572767841493b26183a0d1cc411d54fd759c5607c4590b6563a6/tomli-2.4.1.tar.gz", hash = "sha256:7c7e1a961a0b2f2472c1ac5b69affa0ae1132c39adcb67aba98568702b9cc23f", size = 17543, upload-time = "2026-03-25T20:22:03.828Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f4/11/db3d5885d8528263d8adc260bb2d28ebf1270b96e98f0e0268d32b8d9900/tomli-2.4.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:f8f0fc26ec2cc2b965b7a3b87cd19c5c6b8c5e5f436b984e85f486d652285c30", size = 154704, upload-time = "2026-03-25T20:21:10.473Z" }, + { url = "https://files.pythonhosted.org/packages/6d/f7/675db52c7e46064a9aa928885a9b20f4124ecb9bc2e1ce74c9106648d202/tomli-2.4.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:4ab97e64ccda8756376892c53a72bd1f964e519c77236368527f758fbc36a53a", size = 149454, upload-time = "2026-03-25T20:21:12.036Z" }, + { url = "https://files.pythonhosted.org/packages/61/71/81c50943cf953efa35bce7646caab3cf457a7d8c030b27cfb40d7235f9ee/tomli-2.4.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:96481a5786729fd470164b47cdb3e0e58062a496f455ee41b4403be77cb5a076", size = 237561, upload-time = "2026-03-25T20:21:13.098Z" }, + { url = "https://files.pythonhosted.org/packages/48/c1/f41d9cb618acccca7df82aaf682f9b49013c9397212cb9f53219e3abac37/tomli-2.4.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5a881ab208c0baf688221f8cecc5401bd291d67e38a1ac884d6736cbcd8247e9", size = 243824, upload-time = "2026-03-25T20:21:14.569Z" }, + { url = "https://files.pythonhosted.org/packages/22/e4/5a816ecdd1f8ca51fb756ef684b90f2780afc52fc67f987e3c61d800a46d/tomli-2.4.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:47149d5bd38761ac8be13a84864bf0b7b70bc051806bc3669ab1cbc56216b23c", size = 242227, upload-time = "2026-03-25T20:21:15.712Z" }, + { url = "https://files.pythonhosted.org/packages/6b/49/2b2a0ef529aa6eec245d25f0c703e020a73955ad7edf73e7f54ddc608aa5/tomli-2.4.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:ec9bfaf3ad2df51ace80688143a6a4ebc09a248f6ff781a9945e51937008fcbc", size = 247859, upload-time = "2026-03-25T20:21:17.001Z" }, + { url = "https://files.pythonhosted.org/packages/83/bd/6c1a630eaca337e1e78c5903104f831bda934c426f9231429396ce3c3467/tomli-2.4.1-cp311-cp311-win32.whl", hash = "sha256:ff2983983d34813c1aeb0fa89091e76c3a22889ee83ab27c5eeb45100560c049", size = 97204, upload-time = "2026-03-25T20:21:18.079Z" }, + { url = "https://files.pythonhosted.org/packages/42/59/71461df1a885647e10b6bb7802d0b8e66480c61f3f43079e0dcd315b3954/tomli-2.4.1-cp311-cp311-win_amd64.whl", hash = "sha256:5ee18d9ebdb417e384b58fe414e8d6af9f4e7a0ae761519fb50f721de398dd4e", size = 108084, upload-time = "2026-03-25T20:21:18.978Z" }, + { url = "https://files.pythonhosted.org/packages/b8/83/dceca96142499c069475b790e7913b1044c1a4337e700751f48ed723f883/tomli-2.4.1-cp311-cp311-win_arm64.whl", hash = "sha256:c2541745709bad0264b7d4705ad453b76ccd191e64aa6f0fc66b69a293a45ece", size = 95285, upload-time = "2026-03-25T20:21:20.309Z" }, + { url = "https://files.pythonhosted.org/packages/c1/ba/42f134a3fe2b370f555f44b1d72feebb94debcab01676bf918d0cb70e9aa/tomli-2.4.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:c742f741d58a28940ce01d58f0ab2ea3ced8b12402f162f4d534dfe18ba1cd6a", size = 155924, upload-time = "2026-03-25T20:21:21.626Z" }, + { url = "https://files.pythonhosted.org/packages/dc/c7/62d7a17c26487ade21c5422b646110f2162f1fcc95980ef7f63e73c68f14/tomli-2.4.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:7f86fd587c4ed9dd76f318225e7d9b29cfc5a9d43de44e5754db8d1128487085", size = 150018, upload-time = "2026-03-25T20:21:23.002Z" }, + { url = "https://files.pythonhosted.org/packages/5c/05/79d13d7c15f13bdef410bdd49a6485b1c37d28968314eabee452c22a7fda/tomli-2.4.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ff18e6a727ee0ab0388507b89d1bc6a22b138d1e2fa56d1ad494586d61d2eae9", size = 244948, upload-time = "2026-03-25T20:21:24.04Z" }, + { url = "https://files.pythonhosted.org/packages/10/90/d62ce007a1c80d0b2c93e02cab211224756240884751b94ca72df8a875ca/tomli-2.4.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:136443dbd7e1dee43c68ac2694fde36b2849865fa258d39bf822c10e8068eac5", size = 253341, upload-time = "2026-03-25T20:21:25.177Z" }, + { url = "https://files.pythonhosted.org/packages/1a/7e/caf6496d60152ad4ed09282c1885cca4eea150bfd007da84aea07bcc0a3e/tomli-2.4.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:5e262d41726bc187e69af7825504c933b6794dc3fbd5945e41a79bb14c31f585", size = 248159, upload-time = "2026-03-25T20:21:26.364Z" }, + { url = "https://files.pythonhosted.org/packages/99/e7/c6f69c3120de34bbd882c6fba7975f3d7a746e9218e56ab46a1bc4b42552/tomli-2.4.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:5cb41aa38891e073ee49d55fbc7839cfdb2bc0e600add13874d048c94aadddd1", size = 253290, upload-time = "2026-03-25T20:21:27.46Z" }, + { url = "https://files.pythonhosted.org/packages/d6/2f/4a3c322f22c5c66c4b836ec58211641a4067364f5dcdd7b974b4c5da300c/tomli-2.4.1-cp312-cp312-win32.whl", hash = "sha256:da25dc3563bff5965356133435b757a795a17b17d01dbc0f42fb32447ddfd917", size = 98141, upload-time = "2026-03-25T20:21:28.492Z" }, + { url = "https://files.pythonhosted.org/packages/24/22/4daacd05391b92c55759d55eaee21e1dfaea86ce5c571f10083360adf534/tomli-2.4.1-cp312-cp312-win_amd64.whl", hash = "sha256:52c8ef851d9a240f11a88c003eacb03c31fc1c9c4ec64a99a0f922b93874fda9", size = 108847, upload-time = "2026-03-25T20:21:29.386Z" }, + { url = "https://files.pythonhosted.org/packages/68/fd/70e768887666ddd9e9f5d85129e84910f2db2796f9096aa02b721a53098d/tomli-2.4.1-cp312-cp312-win_arm64.whl", hash = "sha256:f758f1b9299d059cc3f6546ae2af89670cb1c4d48ea29c3cacc4fe7de3058257", size = 95088, upload-time = "2026-03-25T20:21:30.677Z" }, + { url = "https://files.pythonhosted.org/packages/07/06/b823a7e818c756d9a7123ba2cda7d07bc2dd32835648d1a7b7b7a05d848d/tomli-2.4.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:36d2bd2ad5fb9eaddba5226aa02c8ec3fa4f192631e347b3ed28186d43be6b54", size = 155866, upload-time = "2026-03-25T20:21:31.65Z" }, + { url = "https://files.pythonhosted.org/packages/14/6f/12645cf7f08e1a20c7eb8c297c6f11d31c1b50f316a7e7e1e1de6e2e7b7e/tomli-2.4.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:eb0dc4e38e6a1fd579e5d50369aa2e10acfc9cace504579b2faabb478e76941a", size = 149887, upload-time = "2026-03-25T20:21:33.028Z" }, + { url = "https://files.pythonhosted.org/packages/5c/e0/90637574e5e7212c09099c67ad349b04ec4d6020324539297b634a0192b0/tomli-2.4.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c7f2c7f2b9ca6bdeef8f0fa897f8e05085923eb091721675170254cbc5b02897", size = 243704, upload-time = "2026-03-25T20:21:34.51Z" }, + { url = "https://files.pythonhosted.org/packages/10/8f/d3ddb16c5a4befdf31a23307f72828686ab2096f068eaf56631e136c1fdd/tomli-2.4.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f3c6818a1a86dd6dca7ddcaaf76947d5ba31aecc28cb1b67009a5877c9a64f3f", size = 251628, upload-time = "2026-03-25T20:21:36.012Z" }, + { url = "https://files.pythonhosted.org/packages/e3/f1/dbeeb9116715abee2485bf0a12d07a8f31af94d71608c171c45f64c0469d/tomli-2.4.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:d312ef37c91508b0ab2cee7da26ec0b3ed2f03ce12bd87a588d771ae15dcf82d", size = 247180, upload-time = "2026-03-25T20:21:37.136Z" }, + { url = "https://files.pythonhosted.org/packages/d3/74/16336ffd19ed4da28a70959f92f506233bd7cfc2332b20bdb01591e8b1d1/tomli-2.4.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:51529d40e3ca50046d7606fa99ce3956a617f9b36380da3b7f0dd3dd28e68cb5", size = 251674, upload-time = "2026-03-25T20:21:38.298Z" }, + { url = "https://files.pythonhosted.org/packages/16/f9/229fa3434c590ddf6c0aa9af64d3af4b752540686cace29e6281e3458469/tomli-2.4.1-cp313-cp313-win32.whl", hash = "sha256:2190f2e9dd7508d2a90ded5ed369255980a1bcdd58e52f7fe24b8162bf9fedbd", size = 97976, upload-time = "2026-03-25T20:21:39.316Z" }, + { url = "https://files.pythonhosted.org/packages/6a/1e/71dfd96bcc1c775420cb8befe7a9d35f2e5b1309798f009dca17b7708c1e/tomli-2.4.1-cp313-cp313-win_amd64.whl", hash = "sha256:8d65a2fbf9d2f8352685bc1364177ee3923d6baf5e7f43ea4959d7d8bc326a36", size = 108755, upload-time = "2026-03-25T20:21:40.248Z" }, + { url = "https://files.pythonhosted.org/packages/83/7a/d34f422a021d62420b78f5c538e5b102f62bea616d1d75a13f0a88acb04a/tomli-2.4.1-cp313-cp313-win_arm64.whl", hash = "sha256:4b605484e43cdc43f0954ddae319fb75f04cc10dd80d830540060ee7cd0243cd", size = 95265, upload-time = "2026-03-25T20:21:41.219Z" }, + { url = "https://files.pythonhosted.org/packages/3c/fb/9a5c8d27dbab540869f7c1f8eb0abb3244189ce780ba9cd73f3770662072/tomli-2.4.1-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:fd0409a3653af6c147209d267a0e4243f0ae46b011aa978b1080359fddc9b6cf", size = 155726, upload-time = "2026-03-25T20:21:42.23Z" }, + { url = "https://files.pythonhosted.org/packages/62/05/d2f816630cc771ad836af54f5001f47a6f611d2d39535364f148b6a92d6b/tomli-2.4.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:a120733b01c45e9a0c34aeef92bf0cf1d56cfe81ed9d47d562f9ed591a9828ac", size = 149859, upload-time = "2026-03-25T20:21:43.386Z" }, + { url = "https://files.pythonhosted.org/packages/ce/48/66341bdb858ad9bd0ceab5a86f90eddab127cf8b046418009f2125630ecb/tomli-2.4.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:559db847dc486944896521f68d8190be1c9e719fced785720d2216fe7022b662", size = 244713, upload-time = "2026-03-25T20:21:44.474Z" }, + { url = "https://files.pythonhosted.org/packages/df/6d/c5fad00d82b3c7a3ab6189bd4b10e60466f22cfe8a08a9394185c8a8111c/tomli-2.4.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:01f520d4f53ef97964a240a035ec2a869fe1a37dde002b57ebc4417a27ccd853", size = 252084, upload-time = "2026-03-25T20:21:45.62Z" }, + { url = "https://files.pythonhosted.org/packages/00/71/3a69e86f3eafe8c7a59d008d245888051005bd657760e96d5fbfb0b740c2/tomli-2.4.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:7f94b27a62cfad8496c8d2513e1a222dd446f095fca8987fceef261225538a15", size = 247973, upload-time = "2026-03-25T20:21:46.937Z" }, + { url = "https://files.pythonhosted.org/packages/67/50/361e986652847fec4bd5e4a0208752fbe64689c603c7ae5ea7cb16b1c0ca/tomli-2.4.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:ede3e6487c5ef5d28634ba3f31f989030ad6af71edfb0055cbbd14189ff240ba", size = 256223, upload-time = "2026-03-25T20:21:48.467Z" }, + { url = "https://files.pythonhosted.org/packages/8c/9a/b4173689a9203472e5467217e0154b00e260621caa227b6fa01feab16998/tomli-2.4.1-cp314-cp314-win32.whl", hash = "sha256:3d48a93ee1c9b79c04bb38772ee1b64dcf18ff43085896ea460ca8dec96f35f6", size = 98973, upload-time = "2026-03-25T20:21:49.526Z" }, + { url = "https://files.pythonhosted.org/packages/14/58/640ac93bf230cd27d002462c9af0d837779f8773bc03dee06b5835208214/tomli-2.4.1-cp314-cp314-win_amd64.whl", hash = "sha256:88dceee75c2c63af144e456745e10101eb67361050196b0b6af5d717254dddf7", size = 109082, upload-time = "2026-03-25T20:21:50.506Z" }, + { url = "https://files.pythonhosted.org/packages/d5/2f/702d5e05b227401c1068f0d386d79a589bb12bf64c3d2c72ce0631e3bc49/tomli-2.4.1-cp314-cp314-win_arm64.whl", hash = "sha256:b8c198f8c1805dc42708689ed6864951fd2494f924149d3e4bce7710f8eb5232", size = 96490, upload-time = "2026-03-25T20:21:51.474Z" }, + { url = "https://files.pythonhosted.org/packages/45/4b/b877b05c8ba62927d9865dd980e34a755de541eb65fffba52b4cc495d4d2/tomli-2.4.1-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:d4d8fe59808a54658fcc0160ecfb1b30f9089906c50b23bcb4c69eddc19ec2b4", size = 164263, upload-time = "2026-03-25T20:21:52.543Z" }, + { url = "https://files.pythonhosted.org/packages/24/79/6ab420d37a270b89f7195dec5448f79400d9e9c1826df982f3f8e97b24fd/tomli-2.4.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:7008df2e7655c495dd12d2a4ad038ff878d4ca4b81fccaf82b714e07eae4402c", size = 160736, upload-time = "2026-03-25T20:21:53.674Z" }, + { url = "https://files.pythonhosted.org/packages/02/e0/3630057d8eb170310785723ed5adcdfb7d50cb7e6455f85ba8a3deed642b/tomli-2.4.1-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1d8591993e228b0c930c4bb0db464bdad97b3289fb981255d6c9a41aedc84b2d", size = 270717, upload-time = "2026-03-25T20:21:55.129Z" }, + { url = "https://files.pythonhosted.org/packages/7a/b4/1613716072e544d1a7891f548d8f9ec6ce2faf42ca65acae01d76ea06bb0/tomli-2.4.1-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:734e20b57ba95624ecf1841e72b53f6e186355e216e5412de414e3c51e5e3c41", size = 278461, upload-time = "2026-03-25T20:21:56.228Z" }, + { url = "https://files.pythonhosted.org/packages/05/38/30f541baf6a3f6df77b3df16b01ba319221389e2da59427e221ef417ac0c/tomli-2.4.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:8a650c2dbafa08d42e51ba0b62740dae4ecb9338eefa093aa5c78ceb546fcd5c", size = 274855, upload-time = "2026-03-25T20:21:57.653Z" }, + { url = "https://files.pythonhosted.org/packages/77/a3/ec9dd4fd2c38e98de34223b995a3b34813e6bdadf86c75314c928350ed14/tomli-2.4.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:504aa796fe0569bb43171066009ead363de03675276d2d121ac1a4572397870f", size = 283144, upload-time = "2026-03-25T20:21:59.089Z" }, + { url = "https://files.pythonhosted.org/packages/ef/be/605a6261cac79fba2ec0c9827e986e00323a1945700969b8ee0b30d85453/tomli-2.4.1-cp314-cp314t-win32.whl", hash = "sha256:b1d22e6e9387bf4739fbe23bfa80e93f6b0373a7f1b96c6227c32bef95a4d7a8", size = 108683, upload-time = "2026-03-25T20:22:00.214Z" }, + { url = "https://files.pythonhosted.org/packages/12/64/da524626d3b9cc40c168a13da8335fe1c51be12c0a63685cc6db7308daae/tomli-2.4.1-cp314-cp314t-win_amd64.whl", hash = "sha256:2c1c351919aca02858f740c6d33adea0c5deea37f9ecca1cc1ef9e884a619d26", size = 121196, upload-time = "2026-03-25T20:22:01.169Z" }, + { url = "https://files.pythonhosted.org/packages/5a/cd/e80b62269fc78fc36c9af5a6b89c835baa8af28ff5ad28c7028d60860320/tomli-2.4.1-cp314-cp314t-win_arm64.whl", hash = "sha256:eab21f45c7f66c13f2a9e0e1535309cee140182a9cdae1e041d02e47291e8396", size = 100393, upload-time = "2026-03-25T20:22:02.137Z" }, + { url = "https://files.pythonhosted.org/packages/7b/61/cceae43728b7de99d9b847560c262873a1f6c98202171fd5ed62640b494b/tomli-2.4.1-py3-none-any.whl", hash = "sha256:0d85819802132122da43cb86656f8d1f8c6587d54ae7dcaf30e90533028b49fe", size = 14583, upload-time = "2026-03-25T20:22:03.012Z" }, +] + +[[package]] +name = "tomli-w" +version = "1.2.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/19/75/241269d1da26b624c0d5e110e8149093c759b7a286138f4efd61a60e75fe/tomli_w-1.2.0.tar.gz", hash = "sha256:2dd14fac5a47c27be9cd4c976af5a12d87fb1f0b4512f81d69cce3b35ae25021", size = 7184, upload-time = "2025-01-15T12:07:24.262Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c7/18/c86eb8e0202e32dd3df50d43d7ff9854f8e0603945ff398974c1d91ac1ef/tomli_w-1.2.0-py3-none-any.whl", hash = "sha256:188306098d013b691fcadc011abd66727d3c414c571bb01b1a174ba8c983cf90", size = 6675, upload-time = "2025-01-15T12:07:22.074Z" }, +] + +[[package]] +name = "tox" +version = "4.52.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "cachetools" }, + { name = "colorama" }, + { name = "filelock" }, + { name = "packaging" }, + { name = "platformdirs" }, + { name = "pluggy" }, + { name = "pyproject-api" }, + { name = "python-discovery" }, + { name = "tomli", marker = "python_full_version < '3.11'" }, + { name = "tomli-w" }, + { name = "typing-extensions", marker = "python_full_version < '3.11'" }, + { name = "virtualenv" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/bc/fb/d7d634eb513f741ffd40f4c262b7feea19d5c616882eb554045c620670a6/tox-4.52.1.tar.gz", hash = "sha256:297e71ea0ae4ef3acc45cb5fdf080b74537e6ecb5eea7d4646fa7322ca10473e", size = 273730, upload-time = "2026-04-09T16:46:45.838Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/3a/70/0d4fb1eefa05a24ca2f58272b4c4718090dd5ed7e38b54b9a7e757bfafc8/tox-4.52.1-py3-none-any.whl", hash = "sha256:3c4eef0a64f319df0b67dacdb7edcfeda87c8cc722581af5d98dd54f3ffdd8ef", size = 212179, upload-time = "2026-04-09T16:46:44.5Z" }, +] + +[[package]] +name = "tox-gh-actions" +version = "3.5.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "tox" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/9c/11/6c3f818887c37a144a4b48bfc440150f006bdac7a68d92de1046680f578f/tox_gh_actions-3.5.0.tar.gz", hash = "sha256:cc8e148c4513042e5019973e5672594c3df241b035e7fb550f6f778588110051", size = 18815, upload-time = "2025-10-22T14:12:24.846Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/fd/9e/8d50f3b3fc4af8c73154f64d4a2293bfa2d517a19000e70ef2d614254084/tox_gh_actions-3.5.0-py3-none-any.whl", hash = "sha256:070790114c92f4c94337047515ca5e077ceb269a5cb9366e0a0b3440a024eca1", size = 9910, upload-time = "2025-10-22T14:12:23.405Z" }, +] + +[[package]] +name = "tox-uv" +version = "1.35.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "tox-uv-bare" }, + { name = "uv" }, +] +wheels = [ + { url = "https://files.pythonhosted.org/packages/06/f1/c1ccb36e2d37a81bdd08a57a798cbc93153644207f1d97803bb37a4396c0/tox_uv-1.35.0-py3-none-any.whl", hash = "sha256:633a56ef0c9ccd6c83a491faf5591cb0eba07de0876f6d504aa35b645736d232", size = 6375, upload-time = "2026-04-09T17:59:38.96Z" }, +] + +[[package]] +name = "tox-uv-bare" +version = "1.35.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "packaging" }, + { name = "tomli", marker = "python_full_version < '3.11'" }, + { name = "tox" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/b1/4b/2c929a667d33201bb3852c190ec02634ff8a82ad1ab2c69eeda33cf09cc6/tox_uv_bare-1.35.0.tar.gz", hash = "sha256:1bd935ac7be9b7b60f84c15e138a7f5b2bd26f6cc4c9e0bfa77dd50c7a8ab746", size = 31389, upload-time = "2026-04-09T17:59:41.471Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/13/e8/fe21bd86ce17871ea840f1069b9f9cc0cf39cda0eaab455ae03f4f94e06b/tox_uv_bare-1.35.0-py3-none-any.whl", hash = "sha256:280302d08ef4f26fc9e9a8d07a94a3e64642fd5a91d70cf50628620f7c305868", size = 21879, upload-time = "2026-04-09T17:59:40.342Z" }, +] + +[[package]] +name = "typing-extensions" +version = "4.15.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/72/94/1a15dd82efb362ac84269196e94cf00f187f7ed21c242792a923cdb1c61f/typing_extensions-4.15.0.tar.gz", hash = "sha256:0cea48d173cc12fa28ecabc3b837ea3cf6f38c6d1136f85cbaaf598984861466", size = 109391, upload-time = "2025-08-25T13:49:26.313Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/18/67/36e9267722cc04a6b9f15c7f3441c2363321a3ea07da7ae0c0707beb2a9c/typing_extensions-4.15.0-py3-none-any.whl", hash = "sha256:f0fa19c6845758ab08074a0cfa8b7aecb71c999ca73d62883bc25cc018c4e548", size = 44614, upload-time = "2025-08-25T13:49:24.86Z" }, +] + +[[package]] +name = "uv" +version = "0.11.6" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/dd/f3/8aceeab67ea69805293ab290e7ca8cc1b61a064d28b8a35c76d8eba063dd/uv-0.11.6.tar.gz", hash = "sha256:e3b21b7e80024c95ff339fcd147ac6fc3dd98d3613c9d45d3a1f4fd1057f127b", size = 4073298, upload-time = "2026-04-09T12:09:01.738Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/1f/fe/4b61a3d5ad9d02e8a4405026ccd43593d7044598e0fa47d892d4dafe44c9/uv-0.11.6-py3-none-linux_armv6l.whl", hash = "sha256:ada04dcf89ddea5b69d27ac9cdc5ef575a82f90a209a1392e930de504b2321d6", size = 23780079, upload-time = "2026-04-09T12:08:56.609Z" }, + { url = "https://files.pythonhosted.org/packages/52/db/d27519a9e1a5ffee9d71af1a811ad0e19ce7ab9ae815453bef39dd479389/uv-0.11.6-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:5be013888420f96879c6e0d3081e7bcf51b539b034a01777041934457dfbedf3", size = 23214721, upload-time = "2026-04-09T12:09:32.228Z" }, + { url = "https://files.pythonhosted.org/packages/a6/8f/4399fa8b882bd7e0efffc829f73ab24d117d490a93e6bc7104a50282b854/uv-0.11.6-py3-none-macosx_11_0_arm64.whl", hash = "sha256:ffa5dc1cbb52bdce3b8447e83d1601a57ad4da6b523d77d4b47366db8b1ceb18", size = 21750109, upload-time = "2026-04-09T12:09:24.357Z" }, + { url = "https://files.pythonhosted.org/packages/32/07/5a12944c31c3dda253632da7a363edddb869ed47839d4d92a2dc5f546c93/uv-0.11.6-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.musllinux_1_1_aarch64.whl", hash = "sha256:bfb107b4dade1d2c9e572992b06992d51dd5f2136eb8ceee9e62dd124289e825", size = 23551146, upload-time = "2026-04-09T12:09:10.439Z" }, + { url = "https://files.pythonhosted.org/packages/79/5b/2ec8b0af80acd1016ed596baf205ddc77b19ece288473b01926c4a9cf6db/uv-0.11.6-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.musllinux_1_1_armv7l.whl", hash = "sha256:9e2fe7ce12161d8016b7deb1eaad7905a76ff7afec13383333ca75e0c4b5425d", size = 23331192, upload-time = "2026-04-09T12:09:34.792Z" }, + { url = "https://files.pythonhosted.org/packages/62/7d/eea35935f2112b21c296a3e42645f3e4b1aa8bcd34dcf13345fbd55134b7/uv-0.11.6-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:7ed9c6f70c25e8dfeedddf4eddaf14d353f5e6b0eb43da9a14d3a1033d51d915", size = 23337686, upload-time = "2026-04-09T12:09:18.522Z" }, + { url = "https://files.pythonhosted.org/packages/21/47/2584f5ab618f6ebe9bdefb2f765f2ca8540e9d739667606a916b35449eec/uv-0.11.6-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d68a013e609cebf82077cbeeb0809ed5e205257814273bfd31e02fc0353bbfc2", size = 25008139, upload-time = "2026-04-09T12:09:03.983Z" }, + { url = "https://files.pythonhosted.org/packages/95/81/497ae5c1d36355b56b97dc59f550c7e89d0291c163a3f203c6f341dff195/uv-0.11.6-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:93f736dddca03dae732c6fdea177328d3bc4bf137c75248f3d433c57416a4311", size = 25712458, upload-time = "2026-04-09T12:09:07.598Z" }, + { url = "https://files.pythonhosted.org/packages/3c/1c/74083238e4fab2672b63575b9008f1ea418b02a714bcfcf017f4f6a309b6/uv-0.11.6-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e96a66abe53fced0e3389008b8d2eff8278cfa8bb545d75631ae8ceb9c929aba", size = 24915507, upload-time = "2026-04-09T12:08:50.892Z" }, + { url = "https://files.pythonhosted.org/packages/5a/ee/e14fe10ba455a823ed18233f12de6699a601890905420b5c504abf115116/uv-0.11.6-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0b096311b2743b228df911a19532b3f18fa420bf9530547aecd6a8e04bbfaccd", size = 24971011, upload-time = "2026-04-09T12:08:54.016Z" }, + { url = "https://files.pythonhosted.org/packages/3c/a1/7b9c83eaadf98e343317ff6384a7227a4855afd02cdaf9696bcc71ee6155/uv-0.11.6-py3-none-manylinux_2_28_aarch64.whl", hash = "sha256:904d537b4a6e798015b4a64ff5622023bd4601b43b6cd1e5f423d63471f5e948", size = 23640234, upload-time = "2026-04-09T12:09:15.735Z" }, + { url = "https://files.pythonhosted.org/packages/d6/51/75ccdd23e76ff1703b70eb82881cd5b4d2a954c9679f8ef7e0136ef2cfab/uv-0.11.6-py3-none-manylinux_2_31_riscv64.musllinux_1_1_riscv64.whl", hash = "sha256:4ed8150c26b5e319381d75ae2ce6aba1e9c65888f4850f4e3b3fa839953c90a5", size = 24452664, upload-time = "2026-04-09T12:09:26.875Z" }, + { url = "https://files.pythonhosted.org/packages/4d/86/ace80fe47d8d48b5e3b5aee0b6eb1a49deaacc2313782870250b3faa36f5/uv-0.11.6-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:1c9218c8d4ac35ca6e617fb0951cc0ab2d907c91a6aea2617de0a5494cf162c0", size = 24494599, upload-time = "2026-04-09T12:09:37.368Z" }, + { url = "https://files.pythonhosted.org/packages/05/2d/4b642669b56648194f026de79bc992cbfc3ac2318b0a8d435f3c284934e8/uv-0.11.6-py3-none-musllinux_1_1_i686.whl", hash = "sha256:9e211c83cc890c569b86a4183fcf5f8b6f0c7adc33a839b699a98d30f1310d3a", size = 24159150, upload-time = "2026-04-09T12:09:13.17Z" }, + { url = "https://files.pythonhosted.org/packages/ae/24/7eecd76fe983a74fed1fc700a14882e70c4e857f1d562a9f2303d4286c12/uv-0.11.6-py3-none-musllinux_1_1_x86_64.whl", hash = "sha256:d2a1d2089afdf117ad19a4c1dd36b8189c00ae1ad4135d3bfbfced82342595cf", size = 25164324, upload-time = "2026-04-09T12:08:59.56Z" }, + { url = "https://files.pythonhosted.org/packages/27/e0/bbd4ba7c2e5067bbba617d87d306ec146889edaeeaa2081d3e122178ca08/uv-0.11.6-py3-none-win32.whl", hash = "sha256:6e8344f38fa29f85dcfd3e62dc35a700d2448f8e90381077ef393438dcd5012e", size = 22865693, upload-time = "2026-04-09T12:09:21.415Z" }, + { url = "https://files.pythonhosted.org/packages/a5/33/1983ce113c538a856f2d620d16e39691962ecceef091a84086c5785e32e5/uv-0.11.6-py3-none-win_amd64.whl", hash = "sha256:a28bea69c1186303d1200f155c7a28c449f8a4431e458fcf89360cc7ef546e40", size = 25371258, upload-time = "2026-04-09T12:09:40.52Z" }, + { url = "https://files.pythonhosted.org/packages/35/01/be0873f44b9c9bc250fcbf263367fcfc1f59feab996355bcb6b52fff080d/uv-0.11.6-py3-none-win_arm64.whl", hash = "sha256:a78f6d64b9950e24061bc7ec7f15ff8089ad7f5a976e7b65fcadce58fe02f613", size = 23869585, upload-time = "2026-04-09T12:09:29.425Z" }, +] + +[[package]] +name = "virtualenv" +version = "21.2.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "distlib" }, + { name = "filelock" }, + { name = "platformdirs" }, + { name = "python-discovery" }, + { name = "typing-extensions", marker = "python_full_version < '3.11'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/97/c5/aff062c66b42e2183201a7ace10c6b2e959a9a16525c8e8ca8e59410d27a/virtualenv-21.2.1.tar.gz", hash = "sha256:b66ffe81301766c0d5e2208fc3576652c59d44e7b731fc5f5ed701c9b537fa78", size = 5844770, upload-time = "2026-04-09T18:47:11.482Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/20/0e/f083a76cb590e60dff3868779558eefefb8dfb7c9ed020babc7aa014ccbf/virtualenv-21.2.1-py3-none-any.whl", hash = "sha256:bd16b49c53562b28cf1a3ad2f36edb805ad71301dee70ddc449e5c88a9f919a2", size = 5828326, upload-time = "2026-04-09T18:47:09.331Z" }, +]