diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS new file mode 100644 index 00000000..bb29cd96 --- /dev/null +++ b/.github/CODEOWNERS @@ -0,0 +1,2 @@ +# Global ownership for everything +* @miketlk @odudex \ No newline at end of file diff --git a/.github/codeql/codeql-config.yml b/.github/codeql/codeql-config.yml new file mode 100644 index 00000000..cc07175e --- /dev/null +++ b/.github/codeql/codeql-config.yml @@ -0,0 +1,4 @@ +name: CodeQL config + +paths-ignore: + - tests/** diff --git a/.github/dependency-review-reviewed-purls.txt b/.github/dependency-review-reviewed-purls.txt new file mode 100644 index 00000000..9524dfdb --- /dev/null +++ b/.github/dependency-review-reviewed-purls.txt @@ -0,0 +1,68 @@ +# Reviewed dependency baseline for .github/workflows/dependency-review.yml. +# Entries are package URL bases without versions. Additions to dependency +# manifests must be reviewed here before the workflow gate will pass. +pkg:githubactions/actions/attest-build-provenance +pkg:githubactions/actions/checkout +pkg:githubactions/actions/dependency-review-action +pkg:githubactions/actions/download-artifact +pkg:githubactions/actions/setup-python +pkg:githubactions/actions/upload-artifact +pkg:githubactions/github/codeql-action/analyze +pkg:githubactions/github/codeql-action/init +pkg:githubactions/pypa/gh-action-pypi-publish +pkg:githubactions/rhysd/actionlint +pkg:pypi/babel +pkg:pypi/backrefs +pkg:pypi/black +pkg:pypi/certifi +pkg:pypi/cfgv +pkg:pypi/charset-normalizer +pkg:pypi/click +pkg:pypi/colorama +pkg:pypi/coverage +pkg:pypi/distlib +pkg:pypi/exceptiongroup +pkg:pypi/filelock +pkg:pypi/ghp-import +pkg:pypi/griffelib +pkg:pypi/identify +pkg:pypi/idna +pkg:pypi/iniconfig +pkg:pypi/jinja2 +pkg:pypi/librt +pkg:pypi/markdown +pkg:pypi/markupsafe +pkg:pypi/mergedeep +pkg:pypi/mkdocs +pkg:pypi/mkdocs-autorefs +pkg:pypi/mkdocs-get-deps +pkg:pypi/mkdocs-material +pkg:pypi/mkdocs-material-extensions +pkg:pypi/mkdocstrings +pkg:pypi/mkdocstrings-python +pkg:pypi/mypy +pkg:pypi/mypy-extensions +pkg:pypi/nodeenv +pkg:pypi/packaging +pkg:pypi/paginate +pkg:pypi/pathspec +pkg:pypi/platformdirs +pkg:pypi/pluggy +pkg:pypi/pre-commit +pkg:pypi/pygments +pkg:pypi/pymdown-extensions +pkg:pypi/pytest +pkg:pypi/pytest-cov +pkg:pypi/python-dateutil +pkg:pypi/python-discovery +pkg:pypi/pytokens +pkg:pypi/pyyaml +pkg:pypi/pyyaml-env-tag +pkg:pypi/requests +pkg:pypi/ruff +pkg:pypi/six +pkg:pypi/tomli +pkg:pypi/typing-extensions +pkg:pypi/urllib3 +pkg:pypi/virtualenv +pkg:pypi/watchdog diff --git a/.github/scripts/require-reviewed-dependencies.sh b/.github/scripts/require-reviewed-dependencies.sh new file mode 100755 index 00000000..4ddd3d57 --- /dev/null +++ b/.github/scripts/require-reviewed-dependencies.sh @@ -0,0 +1,35 @@ +#!/usr/bin/env bash +set -euo pipefail + +changes=${DEPENDENCY_CHANGES:-[]} +reviewed=${REVIEWED_DEPENDENCY_PURLS:-} +reviewed_file="" + +if [ -n "${REVIEWED_DEPENDENCY_PURLS_FILE:-}" ]; then + reviewed_file=$(cat "$REVIEWED_DEPENDENCY_PURLS_FILE") +fi + +pending=$( + jq -r --arg reviewed "$reviewed" --arg reviewed_file "$reviewed_file" ' + (($reviewed + "\n" + $reviewed_file) + | split("\n") + | [.[] | split(",")[] | split("#")[0] | gsub("^\\s+|\\s+$"; "") | select(length > 0)] + ) as $allowlist + | [ + .[] + | select(.change_type == "added") + | select((.package_url | split("@")[0]) as $purl | ($allowlist | index($purl) | not)) + | "\(.manifest): \(.package_url)" + ] + | .[] + ' <<< "$changes" +) + +if [ -n "$pending" ]; then + { + echo "New dependencies require maintainer review." + echo "Add reviewed package URLs without versions to REVIEWED_DEPENDENCY_PURLS or REVIEWED_DEPENDENCY_PURLS_FILE." + echo "$pending" + } >&2 + exit 1 +fi diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 00000000..fcc2c0f2 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,102 @@ +name: CI + +on: + push: + branches: + - master + pull_request: + +permissions: + contents: read + +jobs: + actionlint: + name: Workflow lint + runs-on: ubuntu-latest + permissions: + contents: read + steps: + - uses: actions/checkout@v6 + - uses: rhysd/actionlint@914e7df21a07ef503a81201c76d2b11c789d3fca + + action-pin-policy: + name: GitHub Actions pin policy + runs-on: ubuntu-latest + permissions: + contents: read + steps: + - uses: actions/checkout@v6 + + - name: Verify third-party Action pins + run: python tools/verify_github_actions_pins.py --self-test + + tests: + name: Tests (Python ${{ matrix.python-version }}) + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + python-version: ["3.10", "3.11", "3.12", "3.13"] + permissions: + contents: read + steps: + - uses: actions/checkout@v6 + + - uses: actions/setup-python@v6 + with: + python-version: ${{ matrix.python-version }} + cache: pip + cache-dependency-path: constraints-dev.txt + + - name: Install pinned CI dependencies + run: python -m pip install --require-hashes -r constraints-dev.txt + + - name: Install package + run: python -m pip install --no-deps -e . + + # TODO: Re-enable Ruff once the repository has a committed lint baseline or + # focused cleanup for existing violations. Ruff remains configured and + # installed so local cleanup can continue without blocking CI. + + - name: Tests + run: pytest + + package: + name: Build and verify artifacts + runs-on: ubuntu-latest + needs: + - actionlint + - action-pin-policy + - tests + permissions: + contents: read + steps: + - uses: actions/checkout@v6 + + - uses: actions/setup-python@v6 + with: + python-version: "3.10" + cache: pip + cache-dependency-path: constraints-dev.txt + + - name: Install pinned CI dependencies + run: python -m pip install --require-hashes -r constraints-dev.txt + + - name: Build artifacts + run: python -m build --sdist --wheel --no-isolation + + - name: Verify built artifacts + run: python tools/verify_package_artifacts.py --inspection-log "$RUNNER_TEMP/ci-package-inspection.md" + + - name: Smoke install from local artifacts + run: | + python -m venv "$RUNNER_TEMP/venv-smoke" + . "$RUNNER_TEMP/venv-smoke/bin/activate" + python -m pip install --no-index --find-links dist embit + python -c "import embit; print(embit.__file__)" + + - name: Upload inspection log + uses: actions/upload-artifact@v7 + with: + name: ci-package-inspection + path: ${{ runner.temp }}/ci-package-inspection.md diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml new file mode 100644 index 00000000..4c057387 --- /dev/null +++ b/.github/workflows/codeql.yml @@ -0,0 +1,49 @@ +name: CodeQL + +on: + push: + branches: + - master + pull_request: + schedule: + - cron: "23 4 * * 1" + +permissions: + contents: read + security-events: write + +jobs: + analyze: + name: Analyze (${{ matrix.language }}) + runs-on: ubuntu-latest + permissions: + contents: read + security-events: write + strategy: + fail-fast: false + matrix: + language: + - python + - actions + steps: + - uses: actions/checkout@v6 + + - uses: github/codeql-action/init@v4 + with: + languages: ${{ matrix.language }} + queries: security-and-quality + config-file: ./.github/codeql/codeql-config.yml + + - uses: github/codeql-action/analyze@v4 + with: + category: /language:${{ matrix.language }} + upload: always + post-processed-sarif-path: ${{ runner.temp }}/sarif-results + + - name: Upload SARIF artifact + if: always() + uses: actions/upload-artifact@v7 + with: + name: codeql-sarif-${{ matrix.language }} + path: ${{ runner.temp }}/sarif-results + if-no-files-found: warn diff --git a/.github/workflows/dependency-review.yml b/.github/workflows/dependency-review.yml new file mode 100644 index 00000000..d462eab0 --- /dev/null +++ b/.github/workflows/dependency-review.yml @@ -0,0 +1,67 @@ +name: Dependency Review + +on: + pull_request: + workflow_dispatch: + +permissions: + contents: read + pull-requests: read + +env: + FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: "true" + +jobs: + dependency-review: + if: github.event_name == 'pull_request' + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v6 + + - id: review + uses: actions/dependency-review-action@v4 + with: + fail-on-severity: low + fail-on-scopes: runtime, development, unknown + license-check: true + vulnerability-check: true + + - name: Require review for new dependencies + env: + DEPENDENCY_CHANGES: ${{ steps.review.outputs.dependency-changes }} + REVIEWED_DEPENDENCY_PURLS_FILE: .github/dependency-review-reviewed-purls.txt + run: .github/scripts/require-reviewed-dependencies.sh + + review-gate-fixtures: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v6 + + - name: Passes reviewed added dependency + env: + DEPENDENCY_CHANGES: >- + [{"change_type":"added","manifest":"poetry.lock","package_url":"pkg:pypi/example-package@1.2.3"}] + REVIEWED_DEPENDENCY_PURLS: " pkg:pypi/example-package " + run: .github/scripts/require-reviewed-dependencies.sh + + - name: Ignores non-added dependency changes + env: + DEPENDENCY_CHANGES: >- + [{"change_type":"modified","manifest":"poetry.lock","package_url":"pkg:pypi/unreviewed-package@1.2.3"},{"change_type":"removed","manifest":"poetry.lock","package_url":"pkg:pypi/removed-package@1.2.3"}] + REVIEWED_DEPENDENCY_PURLS: "" + run: .github/scripts/require-reviewed-dependencies.sh + + - name: Fails unreviewed added dependency + env: + DEPENDENCY_CHANGES: >- + [{"change_type":"added","manifest":"poetry.lock","package_url":"pkg:pypi/example-package@1.2.3"}] + REVIEWED_DEPENDENCY_PURLS: "" + run: | + set +e + output=$(.github/scripts/require-reviewed-dependencies.sh 2>&1) + exit_code=$? + set -e + echo "$output" + test "$exit_code" -ne 0 + grep -q "New dependencies require maintainer review" <<< "$output" + grep -q "poetry.lock: pkg:pypi/example-package@1.2.3" <<< "$output" diff --git a/.github/workflows/package-content-verification.yml b/.github/workflows/package-content-verification.yml new file mode 100644 index 00000000..7b6c769c --- /dev/null +++ b/.github/workflows/package-content-verification.yml @@ -0,0 +1,64 @@ +name: Package Content Verification + +on: + push: + branches: + - master + paths: + - ".github/workflows/**" + - ".gitmodules" + - "pyproject.toml" + - "setup.py" + - "MANIFEST.in" + - "constraints-dev.txt" + - "src/embit/util/ctypes_secp256k1.py" + - "docs/package-content-policy.md" + - "tools/verify_package_artifacts.py" + - "secp256k1/**" + pull_request: + paths: + - ".github/workflows/**" + - ".gitmodules" + - "pyproject.toml" + - "setup.py" + - "MANIFEST.in" + - "constraints-dev.txt" + - "src/embit/util/ctypes_secp256k1.py" + - "docs/package-content-policy.md" + - "tools/verify_package_artifacts.py" + - "secp256k1/**" + +permissions: + contents: read + +jobs: + verify: + runs-on: ubuntu-latest + permissions: + contents: read + steps: + - uses: actions/checkout@v6 + + - uses: actions/setup-python@v6 + with: + python-version: "3.10" + cache: pip + cache-dependency-path: constraints-dev.txt + + - name: Verify vendored submodule URL + run: grep -nE '^[[:space:]]*url = https://github.com/ElementsProject/secp256k1-zkp\.git$' .gitmodules + + - name: Install pinned CI dependencies + run: python -m pip install --require-hashes -r constraints-dev.txt + + - name: Build artifacts + run: python -m build --sdist --wheel --no-isolation + + - name: Verify artifacts and metadata + run: python tools/verify_package_artifacts.py --inspection-log "$RUNNER_TEMP/package-content-verification.md" + + - name: Upload inspection log + uses: actions/upload-artifact@v7 + with: + name: package-content-verification + path: ${{ runner.temp }}/package-content-verification.md diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 00000000..f4a63f2c --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,136 @@ +name: Release + +on: + push: + tags: + - "v*" + workflow_dispatch: + inputs: + tag: + description: Existing release tag to publish + required: true + type: string + +permissions: + contents: read + +env: + FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: "true" + +jobs: + build: + name: Build and verify release artifacts + runs-on: ubuntu-latest + permissions: + contents: read + outputs: + release_tag: ${{ steps.resolve-tag.outputs.release_tag }} + steps: + - name: Resolve release tag + id: resolve-tag + run: | + if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then + echo "release_tag=${{ inputs.tag }}" >> "$GITHUB_OUTPUT" + else + echo "release_tag=${GITHUB_REF_NAME}" >> "$GITHUB_OUTPUT" + fi + + - uses: actions/checkout@v6 + with: + ref: ${{ steps.resolve-tag.outputs.release_tag }} + fetch-depth: 0 + persist-credentials: false + + - name: Verify tag exists locally + run: git rev-parse --verify "refs/tags/${{ steps.resolve-tag.outputs.release_tag }}" + + - name: Verify tag commit is merged into default branch + env: + DEFAULT_BRANCH: ${{ github.event.repository.default_branch }} + run: | + git fetch --no-tags origin "${DEFAULT_BRANCH}" + TAG_COMMIT="$(git rev-parse HEAD)" + git merge-base --is-ancestor "$TAG_COMMIT" "origin/${DEFAULT_BRANCH}" + + - uses: actions/setup-python@v6 + with: + python-version: "3.10" + + - name: Install pinned CI dependencies + run: python -m pip install --require-hashes -r constraints-dev.txt + + - name: Install package + run: python -m pip install --no-deps -e . + + - name: Run tests + run: pytest + + - name: Build artifacts + run: python -m build --sdist --wheel --no-isolation + + - name: Verify artifacts + run: python tools/verify_package_artifacts.py --inspection-log release-metadata/release-package-inspection.md + + - name: Smoke install from local artifacts + run: | + python -m venv "$RUNNER_TEMP/venv-smoke" + . "$RUNNER_TEMP/venv-smoke/bin/activate" + python -m pip install --no-index --find-links dist embit + python -c "import embit; print(embit.__file__)" + + - name: Generate release SBOM + run: python tools/generate_release_sbom.py --output release-metadata/embit-sbom.cdx.json + + - name: Generate release checksums + working-directory: dist + run: | + mkdir -p ../release-metadata + sha256sum -- *.tar.gz *.whl > ../release-metadata/SHA256SUMS + + - name: Upload release bundle + uses: actions/upload-artifact@v7 + with: + name: release-bundle-${{ steps.resolve-tag.outputs.release_tag }} + path: | + dist/*.tar.gz + dist/*.whl + release-metadata/SHA256SUMS + release-metadata/embit-sbom.cdx.json + release-metadata/release-package-inspection.md + + publish: + name: Publish to PyPI + runs-on: ubuntu-latest + needs: + - build + environment: + name: pypi + permissions: + contents: read + attestations: write + id-token: write + steps: + - name: Download validated release bundle + uses: actions/download-artifact@v8 + with: + name: release-bundle-${{ needs.build.outputs.release_tag }} + path: release-bundle + + - name: Verify artifact hashes + working-directory: release-bundle/dist + run: sha256sum --check ../release-metadata/SHA256SUMS + + - name: Generate sdist build attestation + uses: actions/attest-build-provenance@v4 + with: + subject-path: release-bundle/dist/*.tar.gz + + - name: Generate wheel build attestation + uses: actions/attest-build-provenance@v4 + with: + subject-path: release-bundle/dist/*.whl + + - name: Publish release artifacts + uses: pypa/gh-action-pypi-publish@cef221092ed1bacb1cc03d23a2d87d1d172e277b + with: + packages-dir: release-bundle/dist diff --git a/.gitignore b/.gitignore index fbe491f3..a2e7c4ef 100644 --- a/.gitignore +++ b/.gitignore @@ -130,3 +130,8 @@ dmypy.json #VSCode settings.json + +## Hardening: never commit local native secp256k1 artifacts +src/embit/util/prebuilt/*.so +src/embit/util/prebuilt/*.dylib +src/embit/util/prebuilt/*.dll diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 65644462..83fa5116 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,6 +1,6 @@ repos: - repo: https://github.com/psf/black - rev: 22.3.0 + rev: 26.3.1 hooks: - id: black language_version: python3 diff --git a/MANIFEST.in b/MANIFEST.in new file mode 100644 index 00000000..559403ae --- /dev/null +++ b/MANIFEST.in @@ -0,0 +1,31 @@ +include LICENSE +include README.md +include pyproject.toml + +recursive-include src/embit *.py +recursive-include src/embit/wordlists * + +prune src/embit/util/prebuilt +prune src/embit.egg-info +prune docs +prune tests +prune .ai +prune .tmp + +exclude setup.py +exclude src/embit.egg-info/SOURCES.txt + +global-exclude *.so +global-exclude *.dylib +global-exclude *.dll +global-exclude *.egg-info +global-exclude *.dist-info +global-exclude *.pth +global-exclude sitecustomize.py +global-exclude usercustomize.py +global-exclude *.whl +global-exclude *.egg +global-exclude __pycache__ +global-exclude __pycache__/* +global-exclude *.pyc +global-exclude *.pyo diff --git a/README.md b/README.md index 5237a854..e9811121 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # embit -A minimal bitcoin library for MicroPython and Python3 with a focus on embedded systems. +A minimal bitcoin library for MicroPython and Python 3.10+ with a focus on embedded systems. Should remain minimal to fit in a microcontroller. Also easy to audit. @@ -16,7 +16,7 @@ Support the project: `bc1qd4flfrxjctls9ya244u39hd67pcprhvka723gv` Requires a custom MicroPython build with extended [`hashlib`](https://github.com/diybitcoinhardware/f469-disco/tree/master/usermods/uhashlib) module and [`secp256k1`](https://github.com/diybitcoinhardware/secp256k1-embedded) bindings. -To install copy the content of `embit` folder to the board. To save some space you can remove files `embit/util/ctypes_secp256k1.py` and `embit/util/pyhashlib.py` - they are used only in Python3. +To install copy the content of `embit` folder to the board. To save some space you can remove files `embit/util/ctypes_secp256k1.py` and `embit/util/pyhashlib.py` - they are used only in CPython. ## Python 3 @@ -24,9 +24,17 @@ To install run `pip3 install embit`. To install in development mode (editable) clone and run `pip3 install -e .` from the root folder. -PyPi installation includes prebuilt libraries for common platforms (win, macos, linux, raspi) - see [`src/embit/util/prebuilt/`](./src/embit/util/prebuilt/) folder. Library is built from [libsecp-zkp](https://github.com/ElementsProject/secp256k1-zkp) fork for Liquid support, but will work with pure [libsecp256k1](https://github.com/bitcoin-core/secp256k1) as well - just Liquid functionality doesn't work. If it fails to use the prebuilt or system library it will fallback to pure python implementation. +PyPI artifacts are pure Python and do not bundle prebuilt `libsecp256k1` binaries. +If a compatible system `libsecp256k1` is installed, `embit` can use the ctypes backend. +If no compatible system library is available, `embit` automatically falls back to the pure Python implementation. +ctypes library discovery order is: -If you want to build the lib yourself, see: [Building secp256k1 for `embit`](/secp256k1/README.md). +1. `secp256k1/secp256k1-zkp/.libs` (repo-local build outputs) +2. system loader (`libsecp256k1`) +3. system loader (`secp256k1`) +4. `src/embit/util/prebuilt/*` (compatibility-only path; no binaries are shipped in package artifacts) + +To build and install `libsecp256k1` locally, see: [Building secp256k1 for `embit`](/secp256k1/README.md). ## Using non-English BIP39 wordlists @@ -93,3 +101,12 @@ Run tests with micropython: cd tests micropython ./run_tests.py ``` + +Inspect built package contents (latest wheel and sdist from `dist/`): + +```sh +python tools/package_inspect.py +``` + +This helper prints selected package metadata (`Name`, `Version`, `Requires-Python`, +dependencies/extras, project URLs) and full file lists for both artifacts. diff --git a/RELEASING.md b/RELEASING.md new file mode 100644 index 00000000..a9c838b9 --- /dev/null +++ b/RELEASING.md @@ -0,0 +1,53 @@ +# Releasing `embit` + +This project publishes from GitHub Actions only. Do not run `twine upload` from local machines. + +## Release Invariants + +- Release tags must reference commits already merged into the protected default branch. +- Artifacts must be built once in the unprivileged build job, then reused in publish. +- Publishing must use PyPI Trusted Publisher through the protected `pypi` environment. +- Release artifacts must remain pure Python (no bundled native libraries). + +## Pre-Publish Checklist (Before Approving `pypi`) + +- Confirm the tag resolves to the intended commit already merged on the protected default branch. +- Confirm CI passed tests, package-content checks, and artifact inspection for that exact commit. +- Compare artifact filenames and SHA256 values against CI-generated checksums. +- Confirm the build job generated `release-metadata/embit-sbom.cdx.json`. +- Extract the release bundle into a clean temporary directory and confirm it contains only the expected release files: + `dist/*.tar.gz`, `dist/*.whl`, `release-metadata/SHA256SUMS`, + `release-metadata/embit-sbom.cdx.json`, and + `release-metadata/release-package-inspection.md`. +- Run installed-artifact smoke imports from outside the repository checkout so repo-local paths cannot affect backend selection. +- Confirm the publish-only job downloads previously built artifacts and cannot rebuild. +- Confirm release notes match the tagged changes and version. + +## Post-Publish Checklist + +- Compare PyPI artifact SHA256 values against CI-generated checksums. +- Compare GitHub Release artifact SHA256 values against CI-generated checksums. +- Confirm PyPI and GitHub Release artifacts correspond to the intended tag and commit. +- Confirm published artifact set matches workflow output exactly with no unexpected files. +- Confirm artifact attestations were generated and attached for the release artifacts. +- Confirm the SBOM in the release bundle names the released version and built artifacts. +- Verify PyPI metadata (`Name`, `Version`, `Requires-Python`, dependencies/extras, project URLs) matches repository metadata. +- Confirm the release workflow did not restore reusable caches from other workflows. +- When practical, perform approval and verification from a fresh browser session or separate device from day-to-day development. + +## Rollback Path (If Any Verification Fails) + +1. Yank the affected release on PyPI immediately. +2. Pause new releases and disable release workflow dispatch until investigation starts. +3. Revoke or rotate any possibly exposed credentials and review GitHub/PyPI security events. +4. If compromise is plausible, revoke PyPI Trusted Publisher until workflow integrity is re-established. +5. Publish an incident note identifying affected versions and required user action. + +## Prohibited Release Paths + +- No `twine upload` from laptops. +- No local build-push-distribute flow for releases. +- No emergency direct uploads. +- No release from a dirty local checkout. +- No publishing from forks. +- No approval of `pypi` before build-and-verify outputs are reviewed. diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 00000000..7ed46ef6 --- /dev/null +++ b/SECURITY.md @@ -0,0 +1,42 @@ +# Security Policy + +## Supported Versions + +Security fixes are provided for the latest PyPI release line. + +| Version | Supported | +| --- | --- | +| 0.8.x | yes | +| < 0.8.0 | no | + +## Reporting a Vulnerability + +Please report vulnerabilities using GitHub private vulnerability reporting: + +- + +Do not open public issues for security-sensitive reports. + +Include: + +- affected version(s) +- impact and attack prerequisites +- clear reproduction steps or proof of concept +- any proposed mitigation + +## Response Expectations + +- Initial acknowledgement target: within 3 business days +- Triage target (severity and scope): within 7 business days +- Ongoing updates: at least weekly until resolution + +## Disclosure Expectations + +- Keep details private until maintainers publish a fix or mitigation. +- Coordinate public disclosure timing with maintainers. +- For supply-chain incidents, assume CI and maintainer credentials may be impacted until proven otherwise. + +## Release Integrity Notes + +- Releases are expected to be built in GitHub Actions and published via PyPI Trusted Publisher. +- Maintainers verify artifact hashes, attestations, and PyPI metadata after publish (see `RELEASING.md`). diff --git a/constraints-dev.txt b/constraints-dev.txt new file mode 100644 index 00000000..e77ddece --- /dev/null +++ b/constraints-dev.txt @@ -0,0 +1,910 @@ +# Pinned + hashed dev toolchain constraints (Python >= 3.10). +# Generated from poetry.lock plus reviewed CI build frontend pins. +# Includes transitive dependencies for the selected dev toolchain. +# Install with: pip install --require-hashes -r constraints-dev.txt + +babel==2.18.0 ; python_version >= "3.10" \ + --hash=sha256:e2b422b277c2b9a9630c1d7903c2a00d0830c409c59ac8cae9081c92f1aeba35 \ + --hash=sha256:b80b99a14bd085fcacfa15c9165f651fbb3406e66cc603abf11c5750937c992d + +backrefs==6.2 ; python_version >= "3.10" \ + --hash=sha256:0fdc7b012420b6b144410342caeb8adc54c6866cf12064abc9bb211302e496f8 \ + --hash=sha256:08aa7fae530c6b2361d7bdcbda1a7c454e330cc9dbcd03f5c23205e430e5c3be \ + --hash=sha256:c3f4b9cb2af8cda0d87ab4f57800b57b95428488477be164dd2b47be54db0c90 \ + --hash=sha256:12df81596ab511f783b7d87c043ce26bc5b0288cf3bb03610fe76b8189282b2b \ + --hash=sha256:e5f805ae09819caa1aa0623b4a83790e7028604aa2b8c73ba602c4454e665de7 \ + --hash=sha256:664e33cd88c6840b7625b826ecf2555f32d491800900f5a541f772c485f7cda7 \ + --hash=sha256:f44ff4d48808b243b6c0cdc6231e22195c32f77046018141556c66f8bab72a49 + +black==26.3.1 ; python_version >= "3.10" \ + --hash=sha256:86a8b5035fce64f5dcd1b794cf8ec4d31fe458cf6ce3986a30deb434df82a1d2 \ + --hash=sha256:5602bdb96d52d2d0672f24f6ffe5218795736dd34807fd0fd55ccd6bf206168b \ + --hash=sha256:6c54a4a82e291a1fee5137371ab488866b7c86a3305af4026bdd4dc78642e1ac \ + --hash=sha256:6e131579c243c98f35bce64a7e08e87fb2d610544754675d4a0e73a070a5aa3a \ + --hash=sha256:5ed0ca58586c8d9a487352a96b15272b7fa55d139fc8496b519e78023a8dab0a \ + --hash=sha256:28ef38aee69e4b12fda8dba75e21f9b4f979b490c8ac0baa7cb505369ac9e1ff \ + --hash=sha256:bf9bf162ed91a26f1adba8efda0b573bc6924ec1408a52cc6f82cb73ec2b142c \ + --hash=sha256:474c27574d6d7037c1bc875a81d9be0a9a4f9ee95e62800dab3cfaadbf75acd5 \ + --hash=sha256:5e9d0d86df21f2e1677cc4bd090cd0e446278bcbbe49bf3659c308c3e402843e \ + --hash=sha256:9a5e9f45e5d5e1c5b5c29b3bd4265dcc90e8b92cf4534520896ed77f791f4da5 \ + --hash=sha256:b5e6f89631eb88a7302d416594a32faeee9fb8fb848290da9d0a5f2903519fc1 \ + --hash=sha256:41cd2012d35b47d589cb8a16faf8a32ef7a336f56356babd9fcf70939ad1897f \ + --hash=sha256:0f76ff19ec5297dd8e66eb64deda23631e642c9393ab592826fd4bdc97a4bce7 \ + --hash=sha256:ddb113db38838eb9f043623ba274cfaf7d51d5b0c22ecb30afe58b1bb8322983 \ + --hash=sha256:dfdd51fc3e64ea4f35873d1b3fb25326773d55d2329ff8449139ebaad7357efb \ + --hash=sha256:855822d90f884905362f602880ed8b5df1b7e3ee7d0db2502d4388a954cc8c54 \ + --hash=sha256:8a33d657f3276328ce00e4d37fe70361e1ec7614da5d7b6e78de5426cb56332f \ + --hash=sha256:f1cd08e99d2f9317292a311dfe578fd2a24b15dbce97792f9c4d752275c1fa56 \ + --hash=sha256:c7e72339f841b5a237ff14f7d3880ddd0fc7f98a1199e8c4327f9a4f478c1839 \ + --hash=sha256:afc622538b430aa4c8c853f7f63bc582b3b8030fd8c80b70fb5fa5b834e575c2 \ + --hash=sha256:2d6bfaf7fd0993b420bed691f20f9492d53ce9a2bcccea4b797d34e947318a78 \ + --hash=sha256:f89f2ab047c76a9c03f78d0d66ca519e389519902fa27e7a91117ef7611c0568 \ + --hash=sha256:b07fc0dab849d24a80a29cfab8d8a19187d1c4685d8a5e6385a5ce323c1f015f \ + --hash=sha256:0126ae5b7c09957da2bdbd91a9ba1207453feada9e9fe51992848658c6c8e01c \ + --hash=sha256:92c0ec1f2cc149551a2b7b47efc32c866406b6891b0ee4625e95967c8f4acfb1 \ + --hash=sha256:2bd5aa94fc267d38bb21a70d7410a89f1a1d318841855f698746f8e7f51acd1b \ + --hash=sha256:2c50f5063a9641c7eed7795014ba37b0f5fa227f3d408b968936e24bc0566b07 + +build==1.2.2.post1 ; python_version >= "3.10" \ + --hash=sha256:1d61c0887fa860c01971625baae8bdd338e517b836a2f70dd1f7aa3a6b2fc5b5 \ + --hash=sha256:b36993e92ca9375a219c99e606a122ff365a760a2d4bba0caa09bd5278b608b7 + +certifi==2026.2.25 ; python_version >= "3.10" \ + --hash=sha256:027692e4402ad994f1c42e52a4997a9763c646b73e4096e4d5d6db8af1d6f0fa \ + --hash=sha256:e887ab5cee78ea814d3472169153c2d12cd43b14bd03329a39a9c6e2e80bfba7 + +cfgv==3.5.0 ; python_version >= "3.10" \ + --hash=sha256:a8dc6b26ad22ff227d2634a65cb388215ce6cc96bbcc5cfde7641ae87e8dacc0 \ + --hash=sha256:d5b1034354820651caa73ede66a6294d6e95c1b00acc5e9b098e917404669132 + +charset-normalizer==3.4.6 ; python_version >= "3.10" \ + --hash=sha256:2e1d8ca8611099001949d1cdfaefc510cf0f212484fe7c565f735b68c78c3c95 \ + --hash=sha256:e25369dc110d58ddf29b949377a93e0716d72a24f62bad72b2b39f155949c1fd \ + --hash=sha256:259695e2ccc253feb2a016303543d691825e920917e31f894ca1a687982b1de4 \ + --hash=sha256:dda86aba335c902b6149a02a55b38e96287157e609200811837678214ba2b1db \ + --hash=sha256:51fb3c322c81d20567019778cb5a4a6f2dc1c200b886bc0d636238e364848c89 \ + --hash=sha256:4482481cb0572180b6fd976a4d5c72a30263e98564da68b86ec91f0fe35e8565 \ + --hash=sha256:39f5068d35621da2881271e5c3205125cc456f54e9030d3f723288c873a71bf9 \ + --hash=sha256:8bea55c4eef25b0b19a0337dc4e3f9a15b00d569c77211fa8cde38684f234fb7 \ + --hash=sha256:f0cdaecd4c953bfae0b6bb64910aaaca5a424ad9c72d85cb88417bb9814f7550 \ + --hash=sha256:150b8ce8e830eb7ccb029ec9ca36022f756986aaaa7956aad6d9ec90089338c0 \ + --hash=sha256:e68c14b04827dd76dcbd1aeea9e604e3e4b78322d8faf2f8132c7138efa340a8 \ + --hash=sha256:3778fd7d7cd04ae8f54651f4a7a0bd6e39a0cf20f801720a4c21d80e9b7ad6b0 \ + --hash=sha256:dad6e0f2e481fffdcf776d10ebee25e0ef89f16d691f1e5dee4b586375fdc64b \ + --hash=sha256:74a2e659c7ecbc73562e2a15e05039f1e22c75b7c7618b4b574a3ea9118d1557 \ + --hash=sha256:aa9cccf4a44b9b62d8ba8b4dd06c649ba683e4bf04eea606d2e94cfc2d6ff4d6 \ + --hash=sha256:e985a16ff513596f217cee86c21371b8cd011c0f6f056d0920aa2d926c544058 \ + --hash=sha256:82060f995ab5003a2d6e0f4ad29065b7672b6593c8c63559beefe5b443242c3e \ + --hash=sha256:60c74963d8350241a79cb8feea80e54d518f72c26db618862a8f53e5023deaf9 \ + --hash=sha256:f6e4333fb15c83f7d1482a76d45a0818897b3d33f00efd215528ff7c51b8e35d \ + --hash=sha256:bc72863f4d9aba2e8fd9085e63548a324ba706d2ea2c83b260da08a59b9482de \ + --hash=sha256:9cc4fc6c196d6a8b76629a70ddfcd4635a6898756e2d9cac5565cf0654605d73 \ + --hash=sha256:0c173ce3a681f309f31b87125fecec7a5d1347261ea11ebbb856fa6006b23c8c \ + --hash=sha256:c907cdc8109f6c619e6254212e794d6548373cc40e1ec75e6e3823d9135d29cc \ + --hash=sha256:404a1e552cf5b675a87f0651f8b79f5f1e6fd100ee88dc612f89aa16abd4486f \ + --hash=sha256:e3c701e954abf6fc03a49f7c579cc80c2c6cc52525340ca3186c41d3f33482ef \ + --hash=sha256:7a6967aaf043bceabab5412ed6bd6bd26603dae84d5cb75bf8d9a74a4959d398 \ + --hash=sha256:5feb91325bbceade6afab43eb3b508c63ee53579fe896c77137ded51c6b6958e \ + --hash=sha256:f820f24b09e3e779fe84c3c456cb4108a7aa639b0d1f02c28046e11bfcd088ed \ + --hash=sha256:b35b200d6a71b9839a46b9b7fff66b6638bb52fc9658aa58796b0326595d3021 \ + --hash=sha256:9ca4c0b502ab399ef89248a2c84c54954f77a070f28e546a85e91da627d1301e \ + --hash=sha256:a9e68c9d88823b274cf1e72f28cb5dc89c990edf430b0bfd3e2fb0785bfeabf4 \ + --hash=sha256:97d0235baafca5f2b09cf332cc275f021e694e8362c6bb9c96fc9a0eb74fc316 \ + --hash=sha256:2ef7fedc7a6ecbe99969cd09632516738a97eeb8bd7258bf8a0f23114c057dab \ + --hash=sha256:a4ea868bc28109052790eb2b52a9ab33f3aa7adc02f96673526ff47419490e21 \ + --hash=sha256:836ab36280f21fc1a03c99cd05c6b7af70d2697e374c7af0b61ed271401a72a2 \ + --hash=sha256:f1ce721c8a7dfec21fcbdfe04e8f68174183cf4e8188e0645e92aa23985c57ff \ + --hash=sha256:0e28d62a8fc7a1fa411c43bd65e346f3bce9716dc51b897fbe930c5987b402d5 \ + --hash=sha256:530d548084c4a9f7a16ed4a294d459b4f229db50df689bfe92027452452943a0 \ + --hash=sha256:30f445ae60aad5e1f8bdbb3108e39f6fbc09f4ea16c815c66578878325f8f15a \ + --hash=sha256:ac2393c73378fea4e52aa56285a3d64be50f1a12395afef9cce47772f60334c2 \ + --hash=sha256:90ca27cd8da8118b18a52d5f547859cc1f8354a00cd1e8e5120df3e30d6279e5 \ + --hash=sha256:8e5a94886bedca0f9b78fecd6afb6629142fd2605aa70a125d49f4edc6037ee6 \ + --hash=sha256:695f5c2823691a25f17bc5d5ffe79fa90972cc34b002ac6c843bb8a1720e950d \ + --hash=sha256:231d4da14bcd9301310faf492051bee27df11f2bc7549bc0bb41fef11b82daa2 \ + --hash=sha256:a056d1ad2633548ca18ffa2f85c202cfb48b68615129143915b8dc72a806a923 \ + --hash=sha256:c2274ca724536f173122f36c98ce188fd24ce3dad886ec2b7af859518ce008a4 \ + --hash=sha256:c8ae56368f8cc97c7e40a7ee18e1cedaf8e780cd8bc5ed5ac8b81f238614facb \ + --hash=sha256:899d28f422116b08be5118ef350c292b36fc15ec2daeb9ea987c89281c7bb5c4 \ + --hash=sha256:11afb56037cbc4b1555a34dd69151e8e069bee82e613a73bef6e714ce733585f \ + --hash=sha256:423fb7e748a08f854a08a222b983f4df1912b1daedce51a72bd24fe8f26a1843 \ + --hash=sha256:d73beaac5e90173ac3deb9928a74763a6d230f494e4bfb422c217a0ad8e629bf \ + --hash=sha256:d60377dce4511655582e300dc1e5a5f24ba0cb229005a1d5c8d0cb72bb758ab8 \ + --hash=sha256:530e8cebeea0d76bdcf93357aa5e41336f48c3dc709ac52da2bb167c5b8271d9 \ + --hash=sha256:a26611d9987b230566f24a0a125f17fe0de6a6aff9f25c9f564aaa2721a5fb88 \ + --hash=sha256:34315ff4fc374b285ad7f4a0bf7dcbfe769e1b104230d40f49f700d4ab6bbd84 \ + --hash=sha256:5f8ddd609f9e1af8c7bd6e2aca279c931aefecd148a14402d4e368f3171769fd \ + --hash=sha256:80d0a5615143c0b3225e5e3ef22c8d5d51f3f72ce0ea6fb84c943546c7b25b6c \ + --hash=sha256:92734d4d8d187a354a556626c221cd1a892a4e0802ccb2af432a1d85ec012194 \ + --hash=sha256:613f19aa6e082cf96e17e3ffd89383343d0d589abda756b7764cf78361fd41dc \ + --hash=sha256:2b1a63e8224e401cafe7739f77efd3f9e7f5f2026bda4aead8e59afab537784f \ + --hash=sha256:6cceb5473417d28edd20c6c984ab6fee6c6267d38d906823ebfe20b03d607dc2 \ + --hash=sha256:d7de2637729c67d67cf87614b566626057e95c303bc0a55ffe391f5205e7003d \ + --hash=sha256:572d7c822caf521f0525ba1bce1a622a0b85cf47ffbdae6c9c19e3b5ac3c4389 \ + --hash=sha256:a4474d924a47185a06411e0064b803c68be044be2d60e50e8bddcc2649957c1f \ + --hash=sha256:9cc6e6d9e571d2f863fa77700701dae73ed5f78881efc8b3f9a4398772ff53e8 \ + --hash=sha256:ef5960d965e67165d75b7c7ffc60a83ec5abfc5c11b764ec13ea54fbef8b4421 \ + --hash=sha256:b3694e3f87f8ac7ce279d4355645b3c878d24d1424581b46282f24b92f5a4ae2 \ + --hash=sha256:5d11595abf8dd942a77883a39d81433739b287b6aa71620f15164f8096221b30 \ + --hash=sha256:7bda6eebafd42133efdca535b04ccb338ab29467b3f7bf79569883676fc628db \ + --hash=sha256:bbc8c8650c6e51041ad1be191742b8b421d05bbd3410f43fa2a00c8db87678e8 \ + --hash=sha256:22c6f0c2fbc31e76c3b8a86fba1a56eda6166e238c29cdd3d14befdb4a4e4815 \ + --hash=sha256:7edbed096e4a4798710ed6bc75dcaa2a21b68b6c356553ac4823c3658d53743a \ + --hash=sha256:7f9019c9cb613f084481bd6a100b12e1547cf2efe362d873c2e31e4035a6fa43 \ + --hash=sha256:58c948d0d086229efc484fe2f30c2d382c86720f55cd9bc33591774348ad44e0 \ + --hash=sha256:419a9d91bd238052642a51938af8ac05da5b3343becde08d5cdeab9046df9ee1 \ + --hash=sha256:5273b9f0b5835ff0350c0828faea623c68bfa65b792720c453e22b25cc72930f \ + --hash=sha256:0e901eb1049fdb80f5bd11ed5ea1e498ec423102f7a9b9e4645d5b8204ff2815 \ + --hash=sha256:b4ff1d35e8c5bd078be89349b6f3a845128e685e751b6ea1169cf2160b344c4d \ + --hash=sha256:74119174722c4349af9708993118581686f343adc1c8c9c007d59be90d077f3f \ + --hash=sha256:e5bcc1a1ae744e0bb59641171ae53743760130600da8db48cbb6e4918e186e4e \ + --hash=sha256:ad8faf8df23f0378c6d527d8b0b15ea4a2e23c89376877c598c4870d1b2c7866 \ + --hash=sha256:f5ea69428fa1b49573eef0cc44a1d43bebd45ad0c611eb7d7eac760c7ae771bc \ + --hash=sha256:06a7e86163334edfc5d20fe104db92fcd666e5a5df0977cb5680a506fe26cc8e \ + --hash=sha256:e1f6e2f00a6b8edb562826e4632e26d063ac10307e80f7461f7de3ad8ef3f077 \ + --hash=sha256:95b52c68d64c1878818687a473a10547b3292e82b6f6fe483808fb1468e2f52f \ + --hash=sha256:7504e9b7dc05f99a9bbb4525c67a2c155073b44d720470a148b34166a69c054e \ + --hash=sha256:172985e4ff804a7ad08eebec0a1640ece87ba5041d565fff23c8f99c1f389484 \ + --hash=sha256:4be9f4830ba8741527693848403e2c457c16e499100963ec711b1c6f2049b7c7 \ + --hash=sha256:79090741d842f564b1b2827c0b82d846405b744d31e84f18d7a7b41c20e473ff \ + --hash=sha256:87725cfb1a4f1f8c2fc9890ae2f42094120f4b44db9360be5d99a4c6b0e03a9e \ + --hash=sha256:fcce033e4021347d80ed9c66dcf1e7b1546319834b74445f561d2e2221de5659 \ + --hash=sha256:ca0276464d148c72defa8bb4390cce01b4a0e425f3b50d1435aa6d7a18107602 \ + --hash=sha256:197c1a244a274bb016dd8b79204850144ef77fe81c5b797dc389327adb552407 \ + --hash=sha256:2a24157fa36980478dd1770b585c0f30d19e18f4fb0c47c13aa568f871718579 \ + --hash=sha256:cd5e2801c89992ed8c0a3f0293ae83c159a60d9a5d685005383ef4caca77f2c4 \ + --hash=sha256:47955475ac79cc504ef2704b192364e51d0d473ad452caedd0002605f780101c \ + --hash=sha256:659a1e1b500fac8f2779dd9e1570464e012f43e580371470b45277a27baa7532 \ + --hash=sha256:f61aa92e4aad0be58eb6eb4e0c21acf32cf8065f4b2cae5665da756c4ceef982 \ + --hash=sha256:f50498891691e0864dc3da965f340fada0771f6142a378083dc4608f4ea513e2 \ + --hash=sha256:bf625105bb9eef28a56a943fec8c8a98aeb80e7d7db99bd3c388137e6eb2d237 \ + --hash=sha256:2bd9d128ef93637a5d7a6af25363cf5dec3fa21cf80e68055aad627f280e8afa \ + --hash=sha256:d08ec48f0a1c48d75d0356cea971921848fb620fdeba805b28f937e90691209f \ + --hash=sha256:1ed80ff870ca6de33f4d953fda4d55654b9a2b340ff39ab32fa3adbcd718f264 \ + --hash=sha256:f98059e4fcd3e3e4e2d632b7cf81c2faae96c43c60b569e9c621468082f1d104 \ + --hash=sha256:ab30e5e3e706e3063bc6de96b118688cb10396b70bb9864a430f67df98c61ecc \ + --hash=sha256:d5f5d1e9def3405f60e3ca8232d56f35c98fb7bf581efcc60051ebf53cb8b611 \ + --hash=sha256:461598cd852bfa5a61b09cae2b1c02e2efcd166ee5516e243d540ac24bfa68a7 \ + --hash=sha256:71be7e0e01753a89cf024abf7ecb6bca2c81738ead80d43004d9b5e3f1244e64 \ + --hash=sha256:df01808ee470038c3f8dc4f48620df7225c49c2d6639e38f96e6d6ac6e6f7b0e \ + --hash=sha256:69dd852c2f0ad631b8b60cfbe25a28c0058a894de5abb566619c205ce0550eae \ + --hash=sha256:517ad0e93394ac532745129ceabdf2696b609ec9f87863d337140317ebce1c14 \ + --hash=sha256:31215157227939b4fb3d740cd23fe27be0439afef67b785a1eb78a3ae69cba9e \ + --hash=sha256:ecbbd45615a6885fe3240eb9db73b9e62518b611850fdf8ab08bd56de7ad2b17 \ + --hash=sha256:c45a03a4c69820a399f1dda9e1d8fbf3562eda46e7720458180302021b08f778 \ + --hash=sha256:e8aeb10fcbe92767f0fa69ad5a72deca50d0dca07fbde97848997d778a50c9fe \ + --hash=sha256:54fae94be3d75f3e573c9a1b5402dc593de19377013c9a0e4285e3d402dd3a2a \ + --hash=sha256:2f7fdd9b6e6c529d6a2501a2d36b240109e78a8ceaef5687cfcfa2bbe671d297 \ + --hash=sha256:4d1d02209e06550bdaef34af58e041ad71b88e624f5d825519da3a3308e22687 \ + --hash=sha256:8bc5f0687d796c05b1e28ab0d38a50e6309906ee09375dd3aff6a9c09dd6e8f4 \ + --hash=sha256:ee4ec14bc1680d6b0afab9aea2ef27e26d2024f18b24a2d7155a52b60da7e833 \ + --hash=sha256:d1a2ee9c1499fc8f86f4521f27a973c914b211ffa87322f4ee33bb35392da2c5 \ + --hash=sha256:48696db7f18afb80a068821504296eb0787d9ce239b91ca15059d1d3eaacf13b \ + --hash=sha256:4f41da960b196ea355357285ad1316a00099f22d0929fe168343b99b254729c9 \ + --hash=sha256:802168e03fba8bbc5ce0d866d589e4b1ca751d06edee69f7f3a19c5a9fe6b597 \ + --hash=sha256:8761ac29b6c81574724322a554605608a9960769ea83d2c73e396f3df896ad54 \ + --hash=sha256:1cf0a70018692f85172348fe06d3a4b63f94ecb055e13a00c644d368eb82e5b8 \ + --hash=sha256:3516bbb8d42169de9e61b8520cbeeeb716f12f4ecfe3fd30a9919aa16c806ca8 \ + --hash=sha256:947cf925bc916d90adba35a64c82aace04fa39b46b52d4630ece166655905a69 \ + --hash=sha256:1ae6b62897110aa7c79ea2f5dd38d1abca6db663687c0b1ad9aed6f6bae3d9d6 + +click==8.3.1 ; python_version >= "3.10" \ + --hash=sha256:981153a64e25f12d547d3426c367a4857371575ee7ad18df2a6183ab0545b2a6 \ + --hash=sha256:12ff4785d337a1bb490bb7e9c2b1ee5da3112e94a8622f26a6c77f5d2fc6842a + +colorama==0.4.6 ; python_version >= "3.10" \ + --hash=sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6 \ + --hash=sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44 + +coverage==7.13.5 ; python_version >= "3.10" \ + --hash=sha256:e0723d2c96324561b9aa76fb982406e11d93cdb388a7a7da2b16e04719cf7ca5 \ + --hash=sha256:52f444e86475992506b32d4e5ca55c24fc88d73bcbda0e9745095b28ef4dc0cf \ + --hash=sha256:704de6328e3d612a8f6c07000a878ff38181ec3263d5a11da1db294fa6a9bdf8 \ + --hash=sha256:a1a6d79a14e1ec1832cabc833898636ad5f3754a678ef8bb4908515208bf84f4 \ + --hash=sha256:79060214983769c7ba3f0cee10b54c97609dca4d478fa1aa32b914480fd5738d \ + --hash=sha256:356e76b46783a98c2a2fe81ec79df4883a1e62895ea952968fb253c114e7f930 \ + --hash=sha256:0cef0cdec915d11254a7f549c1170afecce708d30610c6abdded1f74e581666d \ + --hash=sha256:dc022073d063b25a402454e5712ef9e007113e3a676b96c5f29b2bda29352f40 \ + --hash=sha256:9b74db26dfea4f4e50d48a4602207cd1e78be33182bc9cbf22da94f332f99878 \ + --hash=sha256:ad146744ca4fd09b50c482650e3c1b1f4dfa1d4792e0a04a369c7f23336f0400 \ + --hash=sha256:c555b48be1853fe3997c11c4bd521cdd9a9612352de01fa4508f16ec341e6fe0 \ + --hash=sha256:7034b5c56a58ae5e85f23949d52c14aca2cfc6848a31764995b7de88f13a1ea0 \ + --hash=sha256:eb7fdf1ef130660e7415e0253a01a7d5a88c9c4d158bcf75cbbd922fd65a5b58 \ + --hash=sha256:3e1bb5f6c78feeb1be3475789b14a0f0a5b47d505bfc7267126ccbd50289999e \ + --hash=sha256:66a80c616f80181f4d643b0f9e709d97bcea413ecd9631e1dedc7401c8e6695d \ + --hash=sha256:145ede53ccbafb297c1c9287f788d1bc3efd6c900da23bf6931b09eafc931587 \ + --hash=sha256:0672854dc733c342fa3e957e0605256d2bf5934feeac328da9e0b5449634a642 \ + --hash=sha256:ec10e2a42b41c923c2209b846126c6582db5e43a33157e9870ba9fb70dc7854b \ + --hash=sha256:be3d4bbad9d4b037791794ddeedd7d64a56f5933a2c1373e18e9e568b9141686 \ + --hash=sha256:4d2afbc5cc54d286bfb54541aa50b64cdb07a718227168c87b9e2fb8f25e1743 \ + --hash=sha256:3ad050321264c49c2fa67bb599100456fc51d004b82534f379d16445da40fb75 \ + --hash=sha256:7300c8a6d13335b29bb76d7651c66af6bd8658517c43499f110ddc6717bfc209 \ + --hash=sha256:eb07647a5738b89baab047f14edd18ded523de60f3b30e75c2acc826f79c839a \ + --hash=sha256:9adb6688e3b53adffefd4a52d72cbd8b02602bfb8f74dcd862337182fd4d1a4e \ + --hash=sha256:7c8d4bc913dd70b93488d6c496c77f3aff5ea99a07e36a18f865bca55adef8bd \ + --hash=sha256:0e3c426ffc4cd952f54ee9ffbdd10345709ecc78a3ecfd796a57236bfad0b9b8 \ + --hash=sha256:259b69bb83ad9894c4b25be2528139eecba9a82646ebdda2d9db1ba28424a6bf \ + --hash=sha256:258354455f4e86e3e9d0d17571d522e13b4e1e19bf0f8596bcf9476d61e7d8a9 \ + --hash=sha256:bff95879c33ec8da99fc9b6fe345ddb5be6414b41d6d1ad1c8f188d26f36e028 \ + --hash=sha256:460cf0114c5016fa841214ff5564aa4864f11948da9440bc97e21ad1f4ba1e01 \ + --hash=sha256:0e223ce4b4ed47f065bfb123687686512e37629be25cc63728557ae7db261422 \ + --hash=sha256:6e3370441f4513c6252bf042b9c36d22491142385049243253c7e48398a15a9f \ + --hash=sha256:03ccc709a17a1de074fb1d11f217342fb0d2b1582ed544f554fc9fc3f07e95f5 \ + --hash=sha256:3f4818d065964db3c1c66dc0fbdac5ac692ecbc875555e13374fdbe7eedb4376 \ + --hash=sha256:012d5319e66e9d5a218834642d6c35d265515a62f01157a45bcc036ecf947256 \ + --hash=sha256:8dd02af98971bdb956363e4827d34425cb3df19ee550ef92855b0acb9c7ce51c \ + --hash=sha256:f08fd75c50a760c7eb068ae823777268daaf16a80b918fa58eea888f8e3919f5 \ + --hash=sha256:843ea8643cf967d1ac7e8ecd4bb00c99135adf4816c0c0593fdcc47b597fcf09 \ + --hash=sha256:9d44d7aa963820b1b971dbecd90bfe5fe8f81cff79787eb6cca15750bd2f79b9 \ + --hash=sha256:7132bed4bd7b836200c591410ae7d97bf7ae8be6fc87d160b2bd881df929e7bf \ + --hash=sha256:a698e363641b98843c517817db75373c83254781426e94ada3197cabbc2c919c \ + --hash=sha256:bdba0a6b8812e8c7df002d908a9a2ea3c36e92611b5708633c50869e6d922fdf \ + --hash=sha256:d2c87e0c473a10bffe991502eac389220533024c8082ec1ce849f4218dded810 \ + --hash=sha256:bf69236a9a81bdca3bff53796237aab096cdbf8d78a66ad61e992d9dac7eb2de \ + --hash=sha256:5ec4af212df513e399cf11610cc27063f1586419e814755ab362e50a85ea69c1 \ + --hash=sha256:941617e518602e2d64942c88ec8499f7fbd49d3f6c4327d3a71d43a1973032f3 \ + --hash=sha256:da305e9937617ee95c2e39d8ff9f040e0487cbf1ac174f777ed5eddd7a7c1f26 \ + --hash=sha256:78e696e1cc714e57e8b25760b33a8b1026b7048d270140d25dafe1b0a1ee05a3 \ + --hash=sha256:02ca0eed225b2ff301c474aeeeae27d26e2537942aa0f87491d3e147e784a82b \ + --hash=sha256:04690832cbea4e4663d9149e05dba142546ca05cb1848816760e7f58285c970a \ + --hash=sha256:0590e44dd2745c696a778f7bab6aa95256de2cbc8b8cff4f7db8ff09813d6969 \ + --hash=sha256:d7cfad2d6d81dd298ab6b89fe72c3b7b05ec7544bdda3b707ddaecff8d25c161 \ + --hash=sha256:e092b9499de38ae0fbfbc603a74660eb6ff3e869e507b50d85a13b6db9863e15 \ + --hash=sha256:48c39bc4a04d983a54a705a6389512883d4a3b9862991b3617d547940e9f52b1 \ + --hash=sha256:2d3807015f138ffea1ed9afeeb8624fd781703f2858b62a8dd8da5a0994c57b6 \ + --hash=sha256:ee2aa19e03161671ec964004fb74b2257805d9710bf14a5c704558b9d8dbaf17 \ + --hash=sha256:ce1998c0483007608c8382f4ff50164bfc5bd07a2246dd272aa4043b75e61e85 \ + --hash=sha256:631efb83f01569670a5e866ceb80fe483e7c159fac6f167e6571522636104a0b \ + --hash=sha256:f4cd16206ad171cbc2470dbea9103cf9a7607d5fe8c242fdf1edf36174020664 \ + --hash=sha256:0428cbef5783ad91fe240f673cc1f76b25e74bbfe1a13115e4aa30d3f538162d \ + --hash=sha256:e0b216a19534b2427cc201a26c25da4a48633f29a487c61258643e89d28200c0 \ + --hash=sha256:972a9cd27894afe4bc2b1480107054e062df08e671df7c2f18c205e805ccd806 \ + --hash=sha256:4b59148601efcd2bac8c4dbf1f0ad6391693ccf7a74b8205781751637076aee3 \ + --hash=sha256:505d7083c8b0c87a8fa8c07370c285847c1f77739b22e299ad75a6af6c32c5c9 \ + --hash=sha256:60365289c3741e4db327e7baff2a4aaacf22f788e80fa4683393891b70a89fbd \ + --hash=sha256:1b88c69c8ef5d4b6fe7dea66d6636056a0f6a7527c440e890cf9259011f5e606 \ + --hash=sha256:5b13955d31d1633cf9376908089b7cebe7d15ddad7aeaabcbe969a595a97e95e \ + --hash=sha256:f70c9ab2595c56f81a89620e22899eea8b212a4041bd728ac6f4a28bf5d3ddd0 \ + --hash=sha256:084b84a8c63e8d6fc7e3931b316a9bcafca1458d753c539db82d31ed20091a87 \ + --hash=sha256:ad14385487393e386e2ea988b09d62dd42c397662ac2dabc3832d71253eee479 \ + --hash=sha256:7f2c47b36fe7709a6e83bfadf4eefb90bd25fbe4014d715224c4316f808e59a2 \ + --hash=sha256:67e9bc5449801fad0e5dff329499fb090ba4c5800b86805c80617b4e29809b2a \ + --hash=sha256:da86cdcf10d2519e10cabb8ac2de03da1bcb6e4853790b7fbd48523332e3a819 \ + --hash=sha256:0ecf12ecb326fe2c339d93fc131816f3a7367d223db37817208905c89bded911 \ + --hash=sha256:fbabfaceaeb587e16f7008f7795cd80d20ec548dc7f94fbb0d4ec2e038ce563f \ + --hash=sha256:9bb2a28101a443669a423b665939381084412b81c3f8c0fcfbac57f4e30b5b8e \ + --hash=sha256:bd3a2fbc1c6cccb3c5106140d87cc6a8715110373ef42b63cf5aea29df8c217a \ + --hash=sha256:6c36ddb64ed9d7e496028d1d00dfec3e428e0aabf4006583bb1839958d280510 \ + --hash=sha256:380e8e9084d8eb38db3a9176a1a4f3c0082c3806fa0dc882d1d87abc3c789247 \ + --hash=sha256:e808af52a0513762df4d945ea164a24b37f2f518cbe97e03deaa0ee66139b4d6 \ + --hash=sha256:e301d30dd7e95ae068671d746ba8c34e945a82682e62918e41b2679acd2051a0 \ + --hash=sha256:800bc829053c80d240a687ceeb927a94fd108bbdc68dfbe505d0d75ab578a882 \ + --hash=sha256:0b67af5492adb31940ee418a5a655c28e48165da5afab8c7fa6fd72a142f8740 \ + --hash=sha256:c9136ff29c3a91e25b1d1552b5308e53a1e0653a23e53b6366d7c2dcbbaf8a16 \ + --hash=sha256:cff784eef7f0b8f6cb28804fbddcfa99f89efe4cc35fb5627e3ac58f91ed3ac0 \ + --hash=sha256:68a4953be99b17ac3c23b6efbc8a38330d99680c9458927491d18700ef23ded0 \ + --hash=sha256:35a31f2b1578185fbe6aa2e74cea1b1d0bbf4c552774247d9160d29b80ed56cc \ + --hash=sha256:2aa055ae1857258f9e0045be26a6d62bdb47a72448b62d7b55f4820f361a2633 \ + --hash=sha256:1b11eef33edeae9d142f9b4358edb76273b3bfd30bc3df9a4f95d0e49caf94e8 \ + --hash=sha256:10a0c37f0b646eaff7cce1874c31d1f1ccb297688d4c747291f4f4c70741cc8b \ + --hash=sha256:b5db73ba3c41c7008037fa731ad5459fc3944cb7452fc0aa9f822ad3533c583c \ + --hash=sha256:750db93a81e3e5a9831b534be7b1229df848b2e125a604fe6651e48aa070e5f9 \ + --hash=sha256:9ddb4f4a5479f2539644be484da179b653273bca1a323947d48ab107b3ed1f29 \ + --hash=sha256:d8a7a2049c14f413163e2bdabd37e41179b1d1ccb10ffc6ccc4b7a718429c607 \ + --hash=sha256:e1c85e0b6c05c592ea6d8768a66a254bfb3874b53774b12d4c89c481eb78cb90 \ + --hash=sha256:777c4d1eff1b67876139d24288aaf1817f6c03d6bae9c5cc8d27b83bcfe38fe3 \ + --hash=sha256:6697e29b93707167687543480a40f0db8f356e86d9f67ddf2e37e2dfd91a9dab \ + --hash=sha256:8fdf453a942c3e4d99bd80088141c4c6960bb232c409d9c3558e2dbaa3998562 \ + --hash=sha256:32ca0c0114c9834a43f045a87dcebd69d108d8ffb666957ea65aa132f50332e2 \ + --hash=sha256:8769751c10f339021e2638cd354e13adeac54004d1941119b2c96fe5276d45ea \ + --hash=sha256:cec2d83125531bd153175354055cdb7a09987af08a9430bd173c937c6d0fba2a \ + --hash=sha256:0cd9ed7a8b181775459296e402ca4fb27db1279740a24e93b3b41942ebe4b215 \ + --hash=sha256:301e3b7dfefecaca37c9f1aa6f0049b7d4ab8dd933742b607765d757aca77d43 \ + --hash=sha256:9dacc2ad679b292709e0f5fc1ac74a6d4d5562e424058962c7bb0c658ad25e45 \ + --hash=sha256:34b02417cf070e173989b3db962f7ed56d2f644307b2cf9d5a0f258e13084a61 \ + --hash=sha256:c81f6515c4c40141f83f502b07bbfa5c240ba25bbe73da7b33f1e5b6120ff179 + +distlib==0.4.0 ; python_version >= "3.10" \ + --hash=sha256:9659f7d87e46584a30b5780e43ac7a2143098441670ff0a49d5f9034c54a6c16 \ + --hash=sha256:feec40075be03a04501a973d81f633735b4b69f98b05450592310c0f401a4e0d + +exceptiongroup==1.3.1 ; python_version == "3.10" \ + --hash=sha256:a7a39a3bd276781e98394987d3a5701d0c4edffb633bb7a5144577f82c773598 \ + --hash=sha256:8b412432c6055b0b7d14c310000ae93352ed6754f70fa8f7c34141f91c4e3219 + +filelock==3.25.2 ; python_version >= "3.10" \ + --hash=sha256:ca8afb0da15f229774c9ad1b455ed96e85a81373065fb10446672f64444ddf70 \ + --hash=sha256:b64ece2b38f4ca29dd3e810287aa8c48182bbecd1ae6e9ae126c9b35f1382694 + +ghp-import==2.1.0 ; python_version >= "3.10" \ + --hash=sha256:9c535c4c61193c2df8871222567d7fd7e5014d835f97dc7b7439069e2413d343 \ + --hash=sha256:8337dd7b50877f163d4c0289bc1f1c7f127550241988d568c1db512c4324a619 + +griffelib==2.0.2 ; python_version >= "3.10" \ + --hash=sha256:925c857658fb1ba40c0772c37acbc2ab650bd794d9c1b9726922e36ea4117ea1 \ + --hash=sha256:3cf20b3bc470e83763ffbf236e0076b1211bac1bc67de13daf494640f2de707e + +identify==2.6.18 ; python_version >= "3.10" \ + --hash=sha256:8db9d3c8ea9079db92cafb0ebf97abdc09d52e97f4dcf773a2e694048b7cd737 \ + --hash=sha256:873ac56a5e3fd63e7438a7ecbc4d91aca692eb3fefa4534db2b7913f3fc352fd + +idna==3.11 ; python_version >= "3.10" \ + --hash=sha256:771a87f49d9defaf64091e6e6fe9c18d4833f140bd19464795bc32d966ca37ea \ + --hash=sha256:795dafcc9c04ed0c1fb032c2aa73654d8e8c5023a7df64a53f39190ada629902 + +iniconfig==2.3.0 ; python_version >= "3.10" \ + --hash=sha256:f631c04d2c48c52b84d0d0549c99ff3859c98df65b3101406327ecc7d53fbf12 \ + --hash=sha256:c76315c77db068650d49c5b56314774a7804df16fee4402c1f19d6d15d8c4730 + +jinja2==3.1.6 ; python_version >= "3.10" \ + --hash=sha256:85ece4451f492d0c13c5dd7c13a64681a86afae63a5f347908daf103ce6d2f67 \ + --hash=sha256:0137fb05990d35f1275a587e9aee6d56da821fc83491a0fb838183be43f66d6d + +librt==0.8.1 ; python_version >= "3.10" and platform_python_implementation != "PyPy" \ + --hash=sha256:81fd938344fecb9373ba1b155968c8a329491d2ce38e7ddb76f30ffb938f12dc \ + --hash=sha256:5db05697c82b3a2ec53f6e72b2ed373132b0c2e05135f0696784e97d7f5d48e7 \ + --hash=sha256:d56bc4011975f7460bea7b33e1ff425d2f1adf419935ff6707273c77f8a4ada6 \ + --hash=sha256:5cdc0f588ff4b663ea96c26d2a230c525c6fc62b28314edaaaca8ed5af931ad0 \ + --hash=sha256:97c2b54ff6717a7a563b72627990bec60d8029df17df423f0ed37d56a17a176b \ + --hash=sha256:8f1125e6bbf2f1657d9a2f3ccc4a2c9b0c8b176965bb565dd4d86be67eddb4b6 \ + --hash=sha256:8f4bb453f408137d7581be309b2fbc6868a80e7ef60c88e689078ee3a296ae71 \ + --hash=sha256:c336d61d2fe74a3195edc1646d53ff1cddd3a9600b09fa6ab75e5514ba4862a7 \ + --hash=sha256:eb5656019db7c4deacf0c1a55a898c5bb8f989be904597fcb5232a2f4828fa05 \ + --hash=sha256:c25d9e338d5bed46c1632f851babf3d13c78f49a225462017cf5e11e845c5891 \ + --hash=sha256:aaab0e307e344cb28d800957ef3ec16605146ef0e59e059a60a176d19543d1b7 \ + --hash=sha256:56e04c14b696300d47b3bc5f1d10a00e86ae978886d0cee14e5714fafb5df5d2 \ + --hash=sha256:681dc2451d6d846794a828c16c22dc452d924e9f700a485b7ecb887a30aad1fd \ + --hash=sha256:a3b4350b13cc0e6f5bec8fa7caf29a8fb8cdc051a3bae45cfbfd7ce64f009965 \ + --hash=sha256:ac1e7817fd0ed3d14fd7c5df91daed84c48e4c2a11ee99c0547f9f62fdae13da \ + --hash=sha256:747328be0c5b7075cde86a0e09d7a9196029800ba75a1689332348e998fb85c0 \ + --hash=sha256:f0af2bd2bc204fa27f3d6711d0f360e6b8c684a035206257a81673ab924aa11e \ + --hash=sha256:d480de377f5b687b6b1bc0c0407426da556e2a757633cc7e4d2e1a057aa688f3 \ + --hash=sha256:d0ee06b5b5291f609ddb37b9750985b27bc567791bc87c76a569b3feed8481ac \ + --hash=sha256:9e2c6f77b9ad48ce5603b83b7da9ee3e36b3ab425353f695cba13200c5d96596 \ + --hash=sha256:439352ba9373f11cb8e1933da194dcc6206daf779ff8df0ed69c5e39113e6a99 \ + --hash=sha256:82210adabbc331dbb65d7868b105185464ef13f56f7f76688565ad79f648b0fe \ + --hash=sha256:52c224e14614b750c0a6d97368e16804a98c684657c7518752c356834fff83bb \ + --hash=sha256:c00e5c884f528c9932d278d5c9cbbea38a6b81eb62c02e06ae53751a83a4d52b \ + --hash=sha256:f7cdf7f26c2286ffb02e46d7bac56c94655540b26347673bea15fa52a6af17e9 \ + --hash=sha256:a28f2612ab566b17f3698b0da021ff9960610301607c9a5e8eaca62f5e1c350a \ + --hash=sha256:60a78b694c9aee2a0f1aaeaa7d101cf713e92e8423a941d2897f4fa37908dab9 \ + --hash=sha256:758509ea3f1eba2a57558e7e98f4659d0ea7670bff49673b0dde18a3c7e6c0eb \ + --hash=sha256:039b9f2c506bd0ab0f8725aa5ba339c6f0cd19d3b514b50d134789809c24285d \ + --hash=sha256:5bb54f1205a3a6ab41a6fd71dfcdcbd278670d3a90ca502a30d9da583105b6f7 \ + --hash=sha256:05bd41cdee35b0c59c259f870f6da532a2c5ca57db95b5f23689fcb5c9e42440 \ + --hash=sha256:adfab487facf03f0d0857b8710cf82d0704a309d8ffc33b03d9302b4c64e91a9 \ + --hash=sha256:153188fe98a72f206042be10a2c6026139852805215ed9539186312d50a8e972 \ + --hash=sha256:dd3c41254ee98604b08bd5b3af5bf0a89740d4ee0711de95b65166bf44091921 \ + --hash=sha256:e0d138c7ae532908cbb342162b2611dbd4d90c941cd25ab82084aaf71d2c0bd0 \ + --hash=sha256:43353b943613c5d9c49a25aaffdba46f888ec354e71e3529a00cca3f04d66a7a \ + --hash=sha256:ff8baf1f8d3f4b6b7257fcb75a501f2a5499d0dda57645baa09d4d0d34b19444 \ + --hash=sha256:0f2ae3725904f7377e11cc37722d5d401e8b3d5851fb9273d7f4fe04f6b3d37d \ + --hash=sha256:7e6bad1cd94f6764e1e21950542f818a09316645337fd5ab9a7acc45d99a8f35 \ + --hash=sha256:cf450f498c30af55551ba4f66b9123b7185362ec8b625a773b3d39aa1a717583 \ + --hash=sha256:eca45e982fa074090057132e30585a7e8674e9e885d402eae85633e9f449ce6c \ + --hash=sha256:0c3811485fccfda840861905b8c70bba5ec094e02825598bb9d4ca3936857a04 \ + --hash=sha256:5e4af413908f77294605e28cfd98063f54b2c790561383971d2f52d113d9c363 \ + --hash=sha256:5212a5bd7fae98dae95710032902edcd2ec4dc994e883294f75c857b83f9aba0 \ + --hash=sha256:e692aa2d1d604e6ca12d35e51fdc36f4cda6345e28e36374579f7ef3611b3012 \ + --hash=sha256:4be2a5c926b9770c9e08e717f05737a269b9d0ebc5d2f0060f0fe3fe9ce47acb \ + --hash=sha256:fd1a720332ea335ceb544cf0a03f81df92abd4bb887679fd1e460976b0e6214b \ + --hash=sha256:93c2af9e01e0ef80d95ae3c720be101227edae5f2fe7e3dc63d8857fadfc5a1d \ + --hash=sha256:086a32dbb71336627e78cc1d6ee305a68d038ef7d4c39aaff41ae8c9aa46e91a \ + --hash=sha256:e11769a1dbda4da7b00a76cfffa67aa47cfa66921d2724539eee4b9ede780b79 \ + --hash=sha256:924817ab3141aca17893386ee13261f1d100d1ef410d70afe4389f2359fea4f0 \ + --hash=sha256:6cfa7fe54fd4d1f47130017351a959fe5804bda7a0bc7e07a2cdbc3fdd28d34f \ + --hash=sha256:228c2409c079f8c11fb2e5d7b277077f694cb93443eb760e00b3b83cb8b3176c \ + --hash=sha256:7aae78ab5e3206181780e56912d1b9bb9f90a7249ce12f0e8bf531d0462dd0fc \ + --hash=sha256:172d57ec04346b047ca6af181e1ea4858086c80bdf455f61994c4aa6fc3f866c \ + --hash=sha256:6b1977c4ea97ce5eb7755a78fae68d87e4102e4aaf54985e8b56806849cc06a3 \ + --hash=sha256:10c42e1f6fd06733ef65ae7bebce2872bcafd8d6e6b0a08fe0a05a23b044fb14 \ + --hash=sha256:4c8dfa264b9193c4ee19113c985c95f876fae5e51f731494fc4e0cf594990ba7 \ + --hash=sha256:01170b6729a438f0dedc4a26ed342e3dc4f02d1000b4b19f980e1877f0c297e6 \ + --hash=sha256:7b02679a0d783bdae30d443025b94465d8c3dc512f32f5b5031f93f57ac32071 \ + --hash=sha256:190b109bb69592a3401fe1ffdea41a2e73370ace2ffdc4a0e8e2b39cdea81b78 \ + --hash=sha256:e70a57ecf89a0f64c24e37f38d3fe217a58169d2fe6ed6d70554964042474023 \ + --hash=sha256:7e2f3edca35664499fbb36e4770650c4bd4a08abc1f4458eab9df4ec56389730 \ + --hash=sha256:0d2f82168e55ddefd27c01c654ce52379c0750ddc31ee86b4b266bcf4d65f2a3 \ + --hash=sha256:2c74a2da57a094bd48d03fa5d196da83d2815678385d2978657499063709abe1 \ + --hash=sha256:a355d99c4c0d8e5b770313b8b247411ed40949ca44e33e46a4789b9293a907ee \ + --hash=sha256:2eb345e8b33fb748227409c9f1233d4df354d6e54091f0e8fc53acdb2ffedeb7 \ + --hash=sha256:9be2f15e53ce4e83cc08adc29b26fb5978db62ef2a366fbdf716c8a6c8901040 \ + --hash=sha256:785ae29c1f5c6e7c2cde2c7c0e148147f4503da3abc5d44d482068da5322fd9e \ + --hash=sha256:1d3a7da44baf692f0c6aeb5b2a09c5e6fc7a703bca9ffa337ddd2e2da53f7732 \ + --hash=sha256:5fc48998000cbc39ec0d5311312dda93ecf92b39aaf184c5e817d5d440b29624 \ + --hash=sha256:e96baa6820280077a78244b2e06e416480ed859bbd8e5d641cf5742919d8beb4 \ + --hash=sha256:31362dbfe297b23590530007062c32c6f6176f6099646bb2c95ab1b00a57c382 \ + --hash=sha256:cc3656283d11540ab0ea01978378e73e10002145117055e03722417aeab30994 \ + --hash=sha256:738f08021b3142c2918c03692608baed43bc51144c29e35807682f8070ee2a3a \ + --hash=sha256:89815a22daf9c51884fb5dbe4f1ef65ee6a146e0b6a8df05f753e2e4a9359bf4 \ + --hash=sha256:bf512a71a23504ed08103a13c941f763db13fb11177beb3d9244c98c29fb4a61 \ + --hash=sha256:3dff3d3ca8db20e783b1bc7de49c0a2ab0b8387f31236d6a026597d07fcd68ac \ + --hash=sha256:08eec3a1fc435f0d09c87b6bf1ec798986a3544f446b864e4099633a56fcd9ed \ + --hash=sha256:e3f0a41487fd5fad7e760b9e8a90e251e27c2816fbc2cff36a22a0e6bcbbd9dd \ + --hash=sha256:bacdb58d9939d95cc557b4dbaa86527c9db2ac1ed76a18bc8d26f6dc8647d851 \ + --hash=sha256:b6d7ab1f01aa753188605b09a51faa44a3327400b00b8cce424c71910fc0a128 \ + --hash=sha256:4998009e7cb9e896569f4be7004f09d0ed70d386fa99d42b6d363f6d200501ac \ + --hash=sha256:2cc68eeeef5e906839c7bb0815748b5b0a974ec27125beefc0f942715785b551 \ + --hash=sha256:0bf69d79a23f4f40b8673a947a234baeeb133b5078b483b7297c5916539cf5d5 \ + --hash=sha256:22b46eabd76c1986ee7d231b0765ad387d7673bbd996aa0d0d054b38ac65d8f6 \ + --hash=sha256:237796479f4d0637d6b9cbcb926ff424a97735e68ade6facf402df4ec93375ed \ + --hash=sha256:4beb04b8c66c6ae62f8c1e0b2f097c1ebad9295c929a8d5286c05eae7c2fc7dc \ + --hash=sha256:64548cde61b692dc0dc379f4b5f59a2f582c2ebe7890d09c1ae3b9e66fa015b7 \ + --hash=sha256:be46a14693955b3bd96014ccbdb8339ee8c9346fbe11c1b78901b55125f14c73 + +markdown==3.10.2 ; python_version >= "3.10" \ + --hash=sha256:e91464b71ae3ee7afd3017d9f358ef0baf158fd9a298db92f1d4761133824c36 \ + --hash=sha256:994d51325d25ad8aa7ce4ebaec003febcce822c3f8c911e3b17c52f7f589f950 + +markupsafe==3.0.3 ; python_version >= "3.10" \ + --hash=sha256:2f981d352f04553a7171b8e44369f2af4055f888dfb147d55e42d29e29e74559 \ + --hash=sha256:e1c1493fb6e50ab01d20a22826e57520f1284df32f2d8601fdd90b6304601419 \ + --hash=sha256:1ba88449deb3de88bd40044603fafffb7bc2b055d626a330323a9ed736661695 \ + --hash=sha256:f42d0984e947b8adf7dd6dde396e720934d12c506ce84eea8476409563607591 \ + --hash=sha256:c0c0b3ade1c0b13b936d7970b1d37a57acde9199dc2aecc4c336773e1d86049c \ + --hash=sha256:0303439a41979d9e74d18ff5e2dd8c43ed6c6001fd40e5bf2e43f7bd9bbc523f \ + --hash=sha256:d2ee202e79d8ed691ceebae8e0486bd9a2cd4794cec4824e1c99b6f5009502f6 \ + --hash=sha256:177b5253b2834fe3678cb4a5f0059808258584c559193998be2601324fdeafb1 \ + --hash=sha256:2a15a08b17dd94c53a1da0438822d70ebcd13f8c3a95abe3a9ef9f11a94830aa \ + --hash=sha256:c4ffb7ebf07cfe8931028e3e4c85f0357459a3f9f9490886198848f4fa002ec8 \ + --hash=sha256:e2103a929dfa2fcaf9bb4e7c091983a49c9ac3b19c9061b6d5427dd7d14d81a1 \ + --hash=sha256:1cc7ea17a6824959616c525620e387f6dd30fec8cb44f649e31712db02123dad \ + --hash=sha256:4bd4cd07944443f5a265608cc6aab442e4f74dff8088b0dfc8238647b8f6ae9a \ + --hash=sha256:6b5420a1d9450023228968e7e6a9ce57f65d148ab56d2313fcd589eee96a7a50 \ + --hash=sha256:0bf2a864d67e76e5c9a34dc26ec616a66b9888e25e7b9460e1c76d3293bd9dbf \ + --hash=sha256:bc51efed119bc9cfdf792cdeaa4d67e8f6fcccab66ed4bfdd6bde3e59bfcbb2f \ + --hash=sha256:068f375c472b3e7acbe2d5318dea141359e6900156b5b2ba06a30b169086b91a \ + --hash=sha256:7be7b61bb172e1ed687f1754f8e7484f1c8019780f6f6b0786e76bb01c2ae115 \ + --hash=sha256:f9e130248f4462aaa8e2552d547f36ddadbeaa573879158d721bbd33dfe4743a \ + --hash=sha256:0db14f5dafddbb6d9208827849fad01f1a2609380add406671a26386cdf15a19 \ + --hash=sha256:de8a88e63464af587c950061a5e6a67d3632e36df62b986892331d4620a35c01 \ + --hash=sha256:3b562dd9e9ea93f13d53989d23a7e775fdfd1066c33494ff43f5418bc8c58a5c \ + --hash=sha256:d53197da72cc091b024dd97249dfc7794d6a56530370992a5e1a08983ad9230e \ + --hash=sha256:1872df69a4de6aead3491198eaf13810b565bdbeec3ae2dc8780f14458ec73ce \ + --hash=sha256:3a7e8ae81ae39e62a41ec302f972ba6ae23a5c5396c8e60113e9066ef893da0d \ + --hash=sha256:d6dd0be5b5b189d31db7cda48b91d7e0a9795f31430b7f271219ab30f1d3ac9d \ + --hash=sha256:94c6f0bb423f739146aec64595853541634bde58b2135f27f61c1ffd1cd4d16a \ + --hash=sha256:be8813b57049a7dc738189df53d69395eba14fb99345e0a5994914a3864c8a4b \ + --hash=sha256:83891d0e9fb81a825d9a6d61e3f07550ca70a076484292a70fde82c4b807286f \ + --hash=sha256:77f0643abe7495da77fb436f50f8dab76dbc6e5fd25d39589a0f1fe6548bfa2b \ + --hash=sha256:d88b440e37a16e651bda4c7c2b930eb586fd15ca7406cb39e211fcff3bf3017d \ + --hash=sha256:26a5784ded40c9e318cfc2bdb30fe164bdb8665ded9cd64d500a34fb42067b1c \ + --hash=sha256:35add3b638a5d900e807944a078b51922212fb3dedb01633a8defc4b01a3c85f \ + --hash=sha256:e1cf1972137e83c5d4c136c43ced9ac51d0e124706ee1c8aa8532c1287fa8795 \ + --hash=sha256:116bb52f642a37c115f517494ea5feb03889e04df47eeff5b130b1808ce7c219 \ + --hash=sha256:133a43e73a802c5562be9bbcd03d090aa5a1fe899db609c29e8c8d815c5f6de6 \ + --hash=sha256:ccfcd093f13f0f0b7fdd0f198b90053bf7b2f02a3927a30e63f3ccc9df56b676 \ + --hash=sha256:509fa21c6deb7a7a273d629cf5ec029bc209d1a51178615ddf718f5918992ab9 \ + --hash=sha256:a4afe79fb3de0b7097d81da19090f4df4f8d3a2b3adaa8764138aac2e44f3af1 \ + --hash=sha256:795e7751525cae078558e679d646ae45574b47ed6e7771863fcc079a6171a0fc \ + --hash=sha256:8485f406a96febb5140bfeca44a73e3ce5116b2501ac54fe953e488fb1d03b12 \ + --hash=sha256:bdd37121970bfd8be76c5fb069c7751683bdf373db1ed6c010162b2a130248ed \ + --hash=sha256:9a1abfdc021a164803f4d485104931fb8f8c1efd55bc6b748d2f5774e78b62c5 \ + --hash=sha256:7e68f88e5b8799aa49c85cd116c932a1ac15caaa3f5db09087854d218359e485 \ + --hash=sha256:218551f6df4868a8d527e3062d0fb968682fe92054e89978594c28e642c43a73 \ + --hash=sha256:3524b778fe5cfb3452a09d31e7b5adefeea8c5be1d43c4f810ba09f2ceb29d37 \ + --hash=sha256:4e885a3d1efa2eadc93c894a21770e4bc67899e3543680313b09f139e149ab19 \ + --hash=sha256:8709b08f4a89aa7586de0aadc8da56180242ee0ada3999749b183aa23df95025 \ + --hash=sha256:b8512a91625c9b3da6f127803b166b629725e68af71f8184ae7e7d54686a56d6 \ + --hash=sha256:9b79b7a16f7fedff2495d684f2b59b0457c3b493778c9eed31111be64d58279f \ + --hash=sha256:12c63dfb4a98206f045aa9563db46507995f7ef6d83b2f68eda65c307c6829eb \ + --hash=sha256:8f71bc33915be5186016f675cd83a1e08523649b0e33efdb898db577ef5bb009 \ + --hash=sha256:69c0b73548bc525c8cb9a251cddf1931d1db4d2258e9599c28c07ef3580ef354 \ + --hash=sha256:1b4b79e8ebf6b55351f0d91fe80f893b4743f104bff22e90697db1590e47a218 \ + --hash=sha256:ad2cf8aa28b8c020ab2fc8287b0f823d0a7d8630784c31e9ee5edea20f406287 \ + --hash=sha256:eaa9599de571d72e2daf60164784109f19978b327a3910d3e9de8c97b5b70cfe \ + --hash=sha256:c47a551199eb8eb2121d4f0f15ae0f923d31350ab9280078d1e5f12b249e0026 \ + --hash=sha256:f34c41761022dd093b4b6896d4810782ffbabe30f2d443ff5f083e0cbbb8c737 \ + --hash=sha256:457a69a9577064c05a97c41f4e65148652db078a3a509039e64d3467b9e7ef97 \ + --hash=sha256:e8afc3f2ccfa24215f8cb28dcf43f0113ac3c37c2f0f0806d8c70e4228c5cf4d \ + --hash=sha256:ec15a59cf5af7be74194f7ab02d0f59a62bdcf1a537677ce67a2537c9b87fcda \ + --hash=sha256:0eb9ff8191e8498cca014656ae6b8d61f39da5f95b488805da4bb029cccbfbaf \ + --hash=sha256:2713baf880df847f2bece4230d4d094280f4e67b1e813eec43b4c0e144a34ffe \ + --hash=sha256:729586769a26dbceff69f7a7dbbf59ab6572b99d94576a5592625d5b411576b9 \ + --hash=sha256:bdc919ead48f234740ad807933cdf545180bfbe9342c2bb451556db2ed958581 \ + --hash=sha256:5a7d5dc5140555cf21a6fefbdbf8723f06fcd2f63ef108f2854de715e4422cb4 \ + --hash=sha256:1353ef0c1b138e1907ae78e2f6c63ff67501122006b0f9abad68fda5f4ffc6ab \ + --hash=sha256:1085e7fbddd3be5f89cc898938f42c0b3c711fdcb37d75221de2666af647c175 \ + --hash=sha256:1b52b4fb9df4eb9ae465f8d0c228a00624de2334f216f178a995ccdcf82c4634 \ + --hash=sha256:fed51ac40f757d41b7c48425901843666a6677e3e8eb0abcff09e4ba6e664f50 \ + --hash=sha256:f190daf01f13c72eac4efd5c430a8de82489d9cff23c364c3ea822545032993e \ + --hash=sha256:e56b7d45a839a697b5eb268c82a71bd8c7f6c94d6fd50c3d577fa39a9f1409f5 \ + --hash=sha256:f3e98bb3798ead92273dc0e5fd0f31ade220f59a266ffd8a4f6065e0a3ce0523 \ + --hash=sha256:5678211cb9333a6468fb8d8be0305520aa073f50d17f089b5b4b477ea6e67fdc \ + --hash=sha256:915c04ba3851909ce68ccc2b8e2cd691618c4dc4c4232fb7982bca3f41fd8c3d \ + --hash=sha256:4faffd047e07c38848ce017e8725090413cd80cbc23d86e55c587bf979e579c9 \ + --hash=sha256:32001d6a8fc98c8cb5c947787c5d08b0a50663d139f1305bac5885d98d9b40fa \ + --hash=sha256:15d939a21d546304880945ca1ecb8a039db6b4dc49b2c5a400387cdae6a62e26 \ + --hash=sha256:f71a396b3bf33ecaa1626c255855702aca4d3d9fea5e051b41ac59a9c1c41edc \ + --hash=sha256:0f4b68347f8c5eab4a13419215bdfd7f8c9b19f2b25520968adfad23eb0ce60c \ + --hash=sha256:e8fc20152abba6b83724d7ff268c249fa196d8259ff481f3b1476383f8f24e42 \ + --hash=sha256:949b8d66bc381ee8b007cd945914c721d9aba8e27f71959d750a46f7c282b20b \ + --hash=sha256:3537e01efc9d4dccdf77221fb1cb3b8e1a38d5428920e0657ce299b20324d758 \ + --hash=sha256:591ae9f2a647529ca990bc681daebdd52c8791ff06c2bfa05b65163e28102ef2 \ + --hash=sha256:a320721ab5a1aba0a233739394eb907f8c8da5c98c9181d1161e77a0c8e36f2d \ + --hash=sha256:df2449253ef108a379b8b5d6b43f4b1a8e81a061d6537becd5582fba5f9196d7 \ + --hash=sha256:7c3fb7d25180895632e5d3148dbdc29ea38ccb7fd210aa27acbd1201a1902c6e \ + --hash=sha256:38664109c14ffc9e7437e86b4dceb442b0096dfe3541d7864d9cbe1da4cf36c8 \ + --hash=sha256:722695808f4b6457b320fdc131280796bdceb04ab50fe1795cd540799ebe1698 + +mergedeep==1.3.4 ; python_version >= "3.10" \ + --hash=sha256:70775750742b25c0d8f36c55aed03d24c3384d17c951b3175d898bd778ef0307 \ + --hash=sha256:0096d52e9dad9939c3d975a774666af186eda617e6ca84df4c94dec30004f2a8 + +mkdocs==1.6.1 ; python_version >= "3.10" \ + --hash=sha256:db91759624d1647f3f34aa0c3f327dd2601beae39a366d6e064c03468d35c20e \ + --hash=sha256:7b432f01d928c084353ab39c57282f29f92136665bdd6abf7c1ec8d822ef86f2 + +mkdocs-autorefs==1.4.4 ; python_version >= "3.10" \ + --hash=sha256:834ef5408d827071ad1bc69e0f39704fa34c7fc05bc8e1c72b227dfdc5c76089 \ + --hash=sha256:d54a284f27a7346b9c38f1f852177940c222da508e66edc816a0fa55fc6da197 + +mkdocs-get-deps==0.2.2 ; python_version >= "3.10" \ + --hash=sha256:e7878cbeac04860b8b5e0ca31d3abad3df9411a75a32cde82f8e44b6c16ff650 \ + --hash=sha256:8ee8d5f316cdbbb2834bc1df6e69c08fe769a83e040060de26d3c19fad3599a1 + +mkdocs-material==9.7.6 ; python_version >= "3.10" \ + --hash=sha256:71b84353921b8ea1ba84fe11c50912cc512da8fe0881038fcc9a0761c0e635ba \ + --hash=sha256:00bdde50574f776d328b1862fe65daeaf581ec309bd150f7bff345a098c64a69 + +mkdocs-material-extensions==1.3.1 ; python_version >= "3.10" \ + --hash=sha256:adff8b62700b25cb77b53358dad940f3ef973dd6db797907c49e3c2ef3ab4e31 \ + --hash=sha256:10c9511cea88f568257f960358a467d12b970e1f7b2c0e5fb2bb48cab1928443 + +mkdocstrings==1.0.3 ; python_version >= "3.10" \ + --hash=sha256:0d66d18430c2201dc7fe85134277382baaa15e6b30979f3f3bdbabd6dbdb6046 \ + --hash=sha256:ab670f55040722b49bb45865b2e93b824450fb4aef638b00d7acb493a9020434 + +mkdocstrings-python==2.0.3 ; python_version >= "3.10" \ + --hash=sha256:0b83513478bdfd803ff05aa43e9b1fca9dd22bcd9471f09ca6257f009bc5ee12 \ + --hash=sha256:c518632751cc869439b31c9d3177678ad2bfa5c21b79b863956ad68fc92c13b8 + +mypy==1.19.1 ; python_version >= "3.10" \ + --hash=sha256:5f05aa3d375b385734388e844bc01733bd33c644ab48e9684faa54e5389775ec \ + --hash=sha256:022ea7279374af1a5d78dfcab853fe6a536eebfda4b59deab53cd21f6cd9f00b \ + --hash=sha256:ee4c11e460685c3e0c64a4c5de82ae143622410950d6be863303a1c4ba0e36d6 \ + --hash=sha256:de759aafbae8763283b2ee5869c7255391fbc4de3ff171f8f030b5ec48381b74 \ + --hash=sha256:ab43590f9cd5108f41aacf9fca31841142c786827a74ab7cc8a2eacb634e09a1 \ + --hash=sha256:2899753e2f61e571b3971747e302d5f420c3fd09650e1951e99f823bc3089dac \ + --hash=sha256:d8dfc6ab58ca7dda47d9237349157500468e404b17213d44fc1cb77bce532288 \ + --hash=sha256:e3f276d8493c3c97930e354b2595a44a21348b320d859fb4a2b9f66da9ed27ab \ + --hash=sha256:2abb24cf3f17864770d18d673c85235ba52456b36a06b6afc1e07c1fdcd3d0e6 \ + --hash=sha256:a009ffa5a621762d0c926a078c2d639104becab69e79538a494bcccb62cc0331 \ + --hash=sha256:f7cee03c9a2e2ee26ec07479f38ea9c884e301d42c6d43a19d20fb014e3ba925 \ + --hash=sha256:4b84a7a18f41e167f7995200a1d07a4a6810e89d29859df936f1c3923d263042 \ + --hash=sha256:a8174a03289288c1f6c46d55cef02379b478bfbc8e358e02047487cad44c6ca1 \ + --hash=sha256:ffcebe56eb09ff0c0885e750036a095e23793ba6c2e894e7e63f6d89ad51f22e \ + --hash=sha256:b64d987153888790bcdb03a6473d321820597ab8dd9243b27a92153c4fa50fd2 \ + --hash=sha256:c35d298c2c4bba75feb2195655dfea8124d855dfd7343bf8b8c055421eaf0cf8 \ + --hash=sha256:34c81968774648ab5ac09c29a375fdede03ba253f8f8287847bd480782f73a6a \ + --hash=sha256:b10e7c2cd7870ba4ad9b2d8a6102eb5ffc1f16ca35e3de6bfa390c1113029d13 \ + --hash=sha256:e3157c7594ff2ef1634ee058aafc56a82db665c9438fd41b390f3bde1ab12250 \ + --hash=sha256:bdb12f69bcc02700c2b47e070238f42cb87f18c0bc1fc4cdb4fb2bc5fd7a3b8b \ + --hash=sha256:f859fb09d9583a985be9a493d5cfc5515b56b08f7447759a0c5deaf68d80506e \ + --hash=sha256:c9a6538e0415310aad77cb94004ca6482330fece18036b5f360b62c45814c4ef \ + --hash=sha256:da4869fc5e7f62a88f3fe0b5c919d1d9f7ea3cef92d3689de2823fd27e40aa75 \ + --hash=sha256:016f2246209095e8eda7538944daa1d60e1e8134d98983b9fc1e92c1fc0cb8dd \ + --hash=sha256:06e6170bd5836770e8104c8fdd58e5e725cfeb309f0a6c681a811f557e97eac1 \ + --hash=sha256:804bd67b8054a85447c8954215a906d6eff9cabeabe493fb6334b24f4bfff718 \ + --hash=sha256:21761006a7f497cb0d4de3d8ef4ca70532256688b0523eee02baf9eec895e27b \ + --hash=sha256:28902ee51f12e0f19e1e16fbe2f8f06b6637f482c459dd393efddd0ec7f82045 \ + --hash=sha256:481daf36a4c443332e2ae9c137dfee878fcea781a2e3f895d54bd3002a900957 \ + --hash=sha256:8bb5c6f6d043655e055be9b542aa5f3bdd30e4f3589163e85f93f3640060509f \ + --hash=sha256:7bcfc336a03a1aaa26dfce9fff3e287a3ba99872a157561cbfcebe67c13308e3 \ + --hash=sha256:b7951a701c07ea584c4fe327834b92a30825514c868b1f69c30445093fdd9d5a \ + --hash=sha256:b13cfdd6c87fc3efb69ea4ec18ef79c74c3f98b4e5498ca9b85ab3b2c2329a67 \ + --hash=sha256:4f28f99c824ecebcdaa2e55d82953e38ff60ee5ec938476796636b86afa3956e \ + --hash=sha256:c608937067d2fc5a4dd1a5ce92fd9e1398691b8c5d012d66e1ddd430e9244376 \ + --hash=sha256:409088884802d511ee52ca067707b90c883426bd95514e8cfda8281dc2effe24 \ + --hash=sha256:f1235f5ea01b7db5468d53ece6aaddf1ad0b88d9e7462b86ef96fe04995d7247 \ + --hash=sha256:19d88bb05303fe63f71dd2c6270daca27cb9401c4ca8255fe50d1d920e0eb9ba + +mypy-extensions==1.1.0 ; python_version >= "3.10" \ + --hash=sha256:1be4cccdb0f2482337c4743e60421de3a356cd97508abadd57d47403e94f5505 \ + --hash=sha256:52e68efc3284861e772bbcd66823fde5ae21fd2fdb51c62a211403730b916558 + +nodeenv==1.10.0 ; python_version >= "3.10" \ + --hash=sha256:5bb13e3eed2923615535339b3c620e76779af4cb4c6a90deccc9e36b274d3827 \ + --hash=sha256:996c191ad80897d076bdfba80a41994c2b47c68e224c542b48feba42ba00f8bb + +packaging==26.0 ; python_version >= "3.10" \ + --hash=sha256:b36f1fef9334a5588b4166f8bcd26a14e521f2b55e6b9de3aaa80d3ff7a37529 \ + --hash=sha256:00243ae351a257117b6a241061796684b084ed1c516a08c48a3f7e147a9d80b4 + +paginate==0.5.7 ; python_version >= "3.10" \ + --hash=sha256:b885e2af73abcf01d9559fd5216b57ef722f8c42affbb63942377668e35c7591 \ + --hash=sha256:22bd083ab41e1a8b4f3690544afb2c60c25e5c9a63a30fa2f483f6c60c8e5945 + +pathspec==1.0.4 ; python_version >= "3.10" \ + --hash=sha256:fb6ae2fd4e7c921a165808a552060e722767cfa526f99ca5156ed2ce45a5c723 \ + --hash=sha256:0210e2ae8a21a9137c0d470578cb0e595af87edaa6ebf12ff176f14a02e0e645 + +platformdirs==4.9.4 ; python_version >= "3.10" \ + --hash=sha256:68a9a4619a666ea6439f2ff250c12a853cd1cbd5158d258bd824a7df6be2f868 \ + --hash=sha256:1ec356301b7dc906d83f371c8f487070e99d3ccf9e501686456394622a01a934 + +pluggy==1.6.0 ; python_version >= "3.10" \ + --hash=sha256:e920276dd6813095e9377c0bc5566d94c932c33b27a3e3945d8389c374dd4746 \ + --hash=sha256:7dcc130b76258d33b90f61b658791dede3486c3e6bfb003ee5c9bfb396dd22f3 + +pre-commit==4.5.1 ; python_version >= "3.10" \ + --hash=sha256:3b3afd891e97337708c1674210f8eba659b52a38ea5f822ff142d10786221f77 \ + --hash=sha256:eb545fcff725875197837263e977ea257a402056661f09dae08e4b149b030a61 + +pygments==2.20.0 ; python_version >= "3.10" \ + --hash=sha256:81a9e26dd42fd28a23a2d169d86d7ac03b46e2f8b59ed4698fb4785f946d0176 \ + --hash=sha256:6757cd03768053ff99f3039c1a36d6c0aa0b263438fcab17520b30a303a82b5f + +pymdown-extensions==10.21 ; python_version >= "3.10" \ + --hash=sha256:91b879f9f864d49794c2d9534372b10150e6141096c3908a455e45ca72ad9d3f \ + --hash=sha256:39f4a020f40773f6b2ff31d2cd2546c2c04d0a6498c31d9c688d2be07e1767d5 + +pyproject-hooks==1.2.0 ; python_version >= "3.10" \ + --hash=sha256:9e5c6bfa8dcc30091c74b0cf803c81fdd29d94f01992a7707bc97babb1141913 \ + --hash=sha256:1e859bd5c40fae9448642dd871adf459e5e2084186e8d2c2a79a824c970da1f8 + +pytest==9.0.3 ; python_version >= "3.10" \ + --hash=sha256:2c5efc453d45394fdd706ade797c0a81091eccd1d6e4bccfcd476e2b8e0ab5d9 \ + --hash=sha256:b86ada508af81d19edeb213c681b1d48246c1a91d304c6c81a427674c17eb91c + +pytest-cov==7.1.0 ; python_version >= "3.10" \ + --hash=sha256:a0461110b7865f9a271aa1b51e516c9a95de9d696734a2f71e3e78f46e1d4678 \ + --hash=sha256:30674f2b5f6351aa09702a9c8c364f6a01c27aae0c1366ae8016160d1efc56b2 + +python-dateutil==2.9.0.post0 ; python_version >= "3.10" \ + --hash=sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3 \ + --hash=sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427 + +python-discovery==1.2.1 ; python_version >= "3.10" \ + --hash=sha256:b6a957b24c1cd79252484d3566d1b49527581d46e789aaf43181005e56201502 \ + --hash=sha256:180c4d114bff1c32462537eac5d6a332b768242b76b69c0259c7d14b1b680c9e + +pytokens==0.4.1 ; python_version >= "3.10" \ + --hash=sha256:2a44ed93ea23415c54f3face3b65ef2b844d96aeb3455b8a69b3df6beab6acc5 \ + --hash=sha256:add8bf86b71a5d9fb5b89f023a80b791e04fba57960aa790cc6125f7f1d39dfe \ + --hash=sha256:670d286910b531c7b7e3c0b453fd8156f250adb140146d234a82219459b9640c \ + --hash=sha256:4e691d7f5186bd2842c14813f79f8884bb03f5995f0575272009982c5ac6c0f7 \ + --hash=sha256:27b83ad28825978742beef057bfe406ad6ed524b2d28c252c5de7b4a6dd48fa2 \ + --hash=sha256:d70e77c55ae8380c91c0c18dea05951482e263982911fc7410b1ffd1dadd3440 \ + --hash=sha256:4a58d057208cb9075c144950d789511220b07636dd2e4708d5645d24de666bdc \ + --hash=sha256:b49750419d300e2b5a3813cf229d4e5a4c728dae470bcc89867a9ad6f25a722d \ + --hash=sha256:d9907d61f15bf7261d7e775bd5d7ee4d2930e04424bab1972591918497623a16 \ + --hash=sha256:ee44d0f85b803321710f9239f335aafe16553b39106384cef8e6de40cb4ef2f6 \ + --hash=sha256:140709331e846b728475786df8aeb27d24f48cbcf7bcd449f8de75cae7a45083 \ + --hash=sha256:6d6c4268598f762bc8e91f5dbf2ab2f61f7b95bdc07953b602db879b3c8c18e1 \ + --hash=sha256:24afde1f53d95348b5a0eb19488661147285ca4dd7ed752bbc3e1c6242a304d1 \ + --hash=sha256:5ad948d085ed6c16413eb5fec6b3e02fa00dc29a2534f088d3302c47eb59adf9 \ + --hash=sha256:3f901fe783e06e48e8cbdc82d631fca8f118333798193e026a50ce1b3757ea68 \ + --hash=sha256:8bdb9d0ce90cbf99c525e75a2fa415144fd570a1ba987380190e8b786bc6ef9b \ + --hash=sha256:5502408cab1cb18e128570f8d598981c68a50d0cbd7c61312a90507cd3a1276f \ + --hash=sha256:29d1d8fb1030af4d231789959f21821ab6325e463f0503a61d204343c9b355d1 \ + --hash=sha256:970b08dd6b86058b6dc07efe9e98414f5102974716232d10f32ff39701e841c4 \ + --hash=sha256:9bd7d7f544d362576be74f9d5901a22f317efc20046efe2034dced238cbbfe78 \ + --hash=sha256:4a14d5f5fc78ce85e426aa159489e2d5961acf0e47575e08f35584009178e321 \ + --hash=sha256:97f50fd18543be72da51dd505e2ed20d2228c74e0464e4262e4899797803d7fa \ + --hash=sha256:dc74c035f9bfca0255c1af77ddd2d6ae8419012805453e4b0e7513e17904545d \ + --hash=sha256:f66a6bbe741bd431f6d741e617e0f39ec7257ca1f89089593479347cc4d13324 \ + --hash=sha256:b35d7e5ad269804f6697727702da3c517bb8a5228afa450ab0fa787732055fc9 \ + --hash=sha256:8fcb9ba3709ff77e77f1c7022ff11d13553f3c30299a9fe246a166903e9091eb \ + --hash=sha256:79fc6b8699564e1f9b521582c35435f1bd32dd06822322ec44afdeba666d8cb3 \ + --hash=sha256:d31b97b3de0f61571a124a00ffe9a81fb9939146c122c11060725bd5aea79975 \ + --hash=sha256:967cf6e3fd4adf7de8fc73cd3043754ae79c36475c1c11d514fc72cf5490094a \ + --hash=sha256:584c80c24b078eec1e227079d56dc22ff755e0ba8654d8383b2c549107528918 \ + --hash=sha256:da5baeaf7116dced9c6bb76dc31ba04a2dc3695f3d9f74741d7910122b456edc \ + --hash=sha256:11edda0942da80ff58c4408407616a310adecae1ddd22eef8c692fe266fa5009 \ + --hash=sha256:0fc71786e629cef478cbf29d7ea1923299181d0699dbe7c3c0f4a583811d9fc1 \ + --hash=sha256:dcafc12c30dbaf1e2af0490978352e0c4041a7cde31f4f81435c2a5e8b9cabb6 \ + --hash=sha256:42f144f3aafa5d92bad964d471a581651e28b24434d184871bd02e3a0d956037 \ + --hash=sha256:34bcc734bd2f2d5fe3b34e7b3c0116bfb2397f2d9666139988e7a3eb5f7400e3 \ + --hash=sha256:941d4343bf27b605e9213b26bfa1c4bf197c9c599a9627eb7305b0defcfe40c1 \ + --hash=sha256:3ad72b851e781478366288743198101e5eb34a414f1d5627cdd585ca3b25f1db \ + --hash=sha256:682fa37ff4d8e95f7df6fe6fe6a431e8ed8e788023c6bcc0f0880a12eab80ad1 \ + --hash=sha256:30f51edd9bb7f85c748979384165601d028b84f7bd13fe14d3e065304093916a \ + --hash=sha256:26cef14744a8385f35d0e095dc8b3a7583f6c953c2e3d269c7f82484bf5ad2de \ + --hash=sha256:292052fe80923aae2260c073f822ceba21f3872ced9a68bb7953b348e561179a + +pyyaml==6.0.3 ; python_version >= "3.10" \ + --hash=sha256:c2514fceb77bc5e7a2f7adfaa1feb2fb311607c9cb518dbc378688ec73d8292f \ + --hash=sha256:9c57bb8c96f6d1808c030b1687b9b5fb476abaa47f0db9c0101f5e9f394e97f4 \ + --hash=sha256:efd7b85f94a6f21e4932043973a7ba2613b059c4a000551892ac9f1d11f5baf3 \ + --hash=sha256:22ba7cfcad58ef3ecddc7ed1db3409af68d023b7f940da23c6c2a1890976eda6 \ + --hash=sha256:6344df0d5755a2c9a276d4473ae6b90647e216ab4757f8426893b5dd2ac3f369 \ + --hash=sha256:3ff07ec89bae51176c0549bc4c63aa6202991da2d9a6129d7aef7f1407d3f295 \ + --hash=sha256:5cf4e27da7e3fbed4d6c3d8e797387aaad68102272f8f9752883bc32d61cb87b \ + --hash=sha256:214ed4befebe12df36bcc8bc2b64b396ca31be9304b8f59e25c11cf94a4c033b \ + --hash=sha256:02ea2dfa234451bbb8772601d7b8e426c2bfa197136796224e50e35a78777956 \ + --hash=sha256:b30236e45cf30d2b8e7b3e85881719e98507abed1011bf463a8fa23e9c3e98a8 \ + --hash=sha256:66291b10affd76d76f54fad28e22e51719ef9ba22b29e1d7d03d6777a9174198 \ + --hash=sha256:9c7708761fccb9397fe64bbc0395abcae8c4bf7b0eac081e12b809bf47700d0b \ + --hash=sha256:418cf3f2111bc80e0933b2cd8cd04f286338bb88bdc7bc8e6dd775ebde60b5e0 \ + --hash=sha256:5e0b74767e5f8c593e8c9b5912019159ed0533c70051e9cce3e8b6aa699fcd69 \ + --hash=sha256:28c8d926f98f432f88adc23edf2e6d4921ac26fb084b028c733d01868d19007e \ + --hash=sha256:bdb2c67c6c1390b63c6ff89f210c8fd09d9a1217a465701eac7316313c915e4c \ + --hash=sha256:44edc647873928551a01e7a563d7452ccdebee747728c1080d881d68af7b997e \ + --hash=sha256:652cb6edd41e718550aad172851962662ff2681490a8a711af6a4d288dd96824 \ + --hash=sha256:10892704fc220243f5305762e276552a0395f7beb4dbf9b14ec8fd43b57f126c \ + --hash=sha256:850774a7879607d3a6f50d36d04f00ee69e7fc816450e5f7e58d7f17f1ae5c00 \ + --hash=sha256:b8bb0864c5a28024fac8a632c443c87c5aa6f215c0b126c449ae1a150412f31d \ + --hash=sha256:1d37d57ad971609cf3c53ba6a7e365e40660e3be0e5175fa9f2365a379d6095a \ + --hash=sha256:37503bfbfc9d2c40b344d06b2199cf0e96e97957ab1c1b546fd4f87e53e5d3e4 \ + --hash=sha256:8098f252adfa6c80ab48096053f512f2321f0b998f98150cea9bd23d83e1467b \ + --hash=sha256:9f3bfb4965eb874431221a3ff3fdcddc7e74e3b07799e0e84ca4a0f867d449bf \ + --hash=sha256:7f047e29dcae44602496db43be01ad42fc6f1cc0d8cd6c83d342306c32270196 \ + --hash=sha256:fc09d0aa354569bc501d4e787133afc08552722d3ab34836a80547331bb5d4a0 \ + --hash=sha256:9149cad251584d5fb4981be1ecde53a1ca46c891a79788c0df828d2f166bda28 \ + --hash=sha256:5fdec68f91a0c6739b380c83b951e2c72ac0197ace422360e6d5a959d8d97b2c \ + --hash=sha256:ba1cc08a7ccde2d2ec775841541641e4548226580ab850948cbfda66a1befcdc \ + --hash=sha256:8dc52c23056b9ddd46818a57b78404882310fb473d63f17b07d5c40421e47f8e \ + --hash=sha256:41715c910c881bc081f1e8872880d3c650acf13dfa8214bad49ed4cede7c34ea \ + --hash=sha256:96b533f0e99f6579b3d4d4995707cf36df9100d67e0c8303a0c55b27b5f99bc5 \ + --hash=sha256:5fcd34e47f6e0b794d17de1b4ff496c00986e1c83f7ab2fb8fcfe9616ff7477b \ + --hash=sha256:64386e5e707d03a7e172c0701abfb7e10f0fb753ee1d773128192742712a98fd \ + --hash=sha256:8da9669d359f02c0b91ccc01cac4a67f16afec0dac22c2ad09f46bee0697eba8 \ + --hash=sha256:2283a07e2c21a2aa78d9c4442724ec1eb15f5e42a723b99cb3d822d48f5f7ad1 \ + --hash=sha256:ee2922902c45ae8ccada2c5b501ab86c36525b883eff4255313a253a3160861c \ + --hash=sha256:a33284e20b78bd4a18c8c2282d549d10bc8408a2a7ff57653c0cf0b9be0afce5 \ + --hash=sha256:0f29edc409a6392443abf94b9cf89ce99889a1dd5376d94316ae5145dfedd5d6 \ + --hash=sha256:f7057c9a337546edc7973c0d3ba84ddcdf0daa14533c2065749c9075001090e6 \ + --hash=sha256:eda16858a3cab07b80edaf74336ece1f986ba330fdb8ee0d6c0d68fe82bc96be \ + --hash=sha256:d0eae10f8159e8fdad514efdc92d74fd8d682c933a6dd088030f3834bc8e6b26 \ + --hash=sha256:79005a0d97d5ddabfeeea4cf676af11e647e41d81c9a7722a193022accdb6b7c \ + --hash=sha256:5498cd1645aa724a7c71c8f378eb29ebe23da2fc0d7a08071d89469bf1d2defb \ + --hash=sha256:8d1fab6bb153a416f9aeb4b8763bc0f22a5586065f86f7664fc23339fc1c1fac \ + --hash=sha256:34d5fcd24b8445fadc33f9cf348c1047101756fd760b4dacb5c3e99755703310 \ + --hash=sha256:501a031947e3a9025ed4405a168e6ef5ae3126c59f90ce0cd6f2bfc477be31b7 \ + --hash=sha256:b3bc83488de33889877a0f2543ade9f70c67d66d9ebb4ac959502e12de895788 \ + --hash=sha256:c458b6d084f9b935061bc36216e8a69a7e293a2f1e68bf956dcd9e6cbcd143f5 \ + --hash=sha256:7c6610def4f163542a622a73fb39f534f8c101d690126992300bf3207eab9764 \ + --hash=sha256:5190d403f121660ce8d1d2c1bb2ef1bd05b5f68533fc5c2ea899bd15f4399b35 \ + --hash=sha256:4a2e8cebe2ff6ab7d1050ecd59c25d4c8bd7e6f400f5f82b96557ac0abafd0ac \ + --hash=sha256:93dda82c9c22deb0a405ea4dc5f2d0cda384168e466364dec6255b293923b2f3 \ + --hash=sha256:02893d100e99e03eda1c8fd5c441d8c60103fd175728e23e431db1b589cf5ab3 \ + --hash=sha256:c1ff362665ae507275af2853520967820d9124984e0f7466736aea23d8611fba \ + --hash=sha256:6adc77889b628398debc7b65c073bcb99c4a0237b248cacaf3fe8a557563ef6c \ + --hash=sha256:a80cb027f6b349846a3bf6d73b5e95e782175e52f22108cfa17876aaeff93702 \ + --hash=sha256:00c4bdeba853cc34e7dd471f16b4114f4162dc03e6b7afcc2128711f0eca823c \ + --hash=sha256:66e1674c3ef6f541c35191caae2d429b967b99e02040f5ba928632d9a7f0f065 \ + --hash=sha256:16249ee61e95f858e83976573de0f5b2893b3677ba71c9dd36b9cf8be9ac6d65 \ + --hash=sha256:4ad1906908f2f5ae4e5a8ddfce73c320c2a1429ec52eafd27138b7f1cbe341c9 \ + --hash=sha256:ebc55a14a21cb14062aa4162f906cd962b28e2e9ea38f9b4391244cd8de4ae0b \ + --hash=sha256:b865addae83924361678b652338317d1bd7e79b1f4596f96b96c77a5a34b34da \ + --hash=sha256:c3355370a2c156cffb25e876646f149d5d68f5e0a3ce86a5084dd0b64a994917 \ + --hash=sha256:3c5677e12444c15717b902a5798264fa7909e41153cdf9ef7ad571b704a63dd9 \ + --hash=sha256:5ed875a24292240029e4483f9d4a4b8a1ae08843b9c54f43fcc11e404532a8a5 \ + --hash=sha256:0150219816b6a1fa26fb4699fb7daa9caf09eb1999f3b70fb6e786805e80375a \ + --hash=sha256:fa160448684b4e94d80416c0fa4aac48967a969efe22931448d853ada8baf926 \ + --hash=sha256:27c0abcb4a5dac13684a37f76e701e054692a9b2d3064b70f5e4eb54810553d7 \ + --hash=sha256:1ebe39cb5fc479422b83de611d14e2c0d3bb2a18bbcb01f229ab3cfbd8fee7a0 \ + --hash=sha256:2e71d11abed7344e42a8849600193d15b6def118602c4c176f748e4583246007 \ + --hash=sha256:d76623373421df22fb4cf8817020cbb7ef15c725b9d5e45f17e189bfc384190f + +pyyaml-env-tag==1.1 ; python_version >= "3.10" \ + --hash=sha256:17109e1a528561e32f026364712fee1264bc2ea6715120891174ed1b980d2e04 \ + --hash=sha256:2eb38b75a2d21ee0475d6d97ec19c63287a7e140231e4214969d0eac923cd7ff + +requests==2.33.0 ; python_version >= "3.10" \ + --hash=sha256:3324635456fa185245e24865e810cecec7b4caf933d7eb133dcde67d48cee69b \ + --hash=sha256:c7ebc5e8b0f21837386ad0e1c8fe8b829fa5f544d8df3b2253bff14ef29d7652 + +ruff==0.15.11 ; python_version >= "3.10" \ + --hash=sha256:e927cfff503135c558eb581a0c9792264aae9507904eb27809cdcff2f2c847b7 \ + --hash=sha256:7a1b5b2938d8f890b76084d4fa843604d787a912541eae85fd7e233398bbb73e \ + --hash=sha256:d4176f3d194afbdaee6e41b9ccb1a2c287dba8700047df474abfbe773825d1cb \ + --hash=sha256:3b17c886fb88203ced3afe7f14e8d5ae96e9d2f4ccc0ee66aa19f2c2675a27e4 \ + --hash=sha256:49fafa220220afe7758a487b048de4c8f9f767f37dfefad46b9dd06759d003eb \ + --hash=sha256:f2ab8427e74a00d93b8bda1307b1e60970d40f304af38bccb218e056c220120d \ + --hash=sha256:195072c0c8e1fc8f940652073df082e37a5d9cb43b4ab1e4d0566ab8977a13b7 \ + --hash=sha256:a3a0996d486af3920dec930a2e7daed4847dfc12649b537a9335585ada163e9e \ + --hash=sha256:1bef2cb556d509259f1fe440bb9cd33c756222cf0a7afe90d15edf0866702431 \ + --hash=sha256:030d921a836d7d4a12cf6e8d984a88b66094ccb0e0f17ddd55067c331191bf19 \ + --hash=sha256:0e783b599b4577788dbbb66b9addcef87e9a8832f4ce0c19e34bf55543a2f890 \ + --hash=sha256:ae90592246625ba4a34349d68ec28d4400d75182b71baa196ddb9f82db025ef5 \ + --hash=sha256:1f111d62e3c983ed20e0ca2e800f8d77433a5b1161947df99a5c2a3fb60514f0 \ + --hash=sha256:06f483d6646f59eaffba9ae30956370d3a886625f511a3108994000480621d1c \ + --hash=sha256:476a2aa56b7da0b73a3ee80b6b2f0e19cce544245479adde7baa65466664d5f3 \ + --hash=sha256:8b6756d88d7e234fb0c98c91511aae3cd519d5e3ed271cae31b20f39cb2a12a3 \ + --hash=sha256:063fed18cc1bbe0ee7393957284a6fe8b588c6a406a285af3ee3f46da2391ee4 \ + --hash=sha256:f092b21708bf0e7437ce9ada249dfe688ff9a0954fc94abab05dcea7dcd29c33 + +setuptools==80.9.0 ; python_version >= "3.10" \ + --hash=sha256:062d34222ad13e0cc312a4c02d73f059e86a4acbfbdea8f8f76b28c99f306922 + +six==1.17.0 ; python_version >= "3.10" \ + --hash=sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274 \ + --hash=sha256:ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81 + +tomli==2.4.1 ; python_full_version <= "3.11.0a6" and python_version >= "3.10" \ + --hash=sha256:f8f0fc26ec2cc2b965b7a3b87cd19c5c6b8c5e5f436b984e85f486d652285c30 \ + --hash=sha256:4ab97e64ccda8756376892c53a72bd1f964e519c77236368527f758fbc36a53a \ + --hash=sha256:96481a5786729fd470164b47cdb3e0e58062a496f455ee41b4403be77cb5a076 \ + --hash=sha256:5a881ab208c0baf688221f8cecc5401bd291d67e38a1ac884d6736cbcd8247e9 \ + --hash=sha256:47149d5bd38761ac8be13a84864bf0b7b70bc051806bc3669ab1cbc56216b23c \ + --hash=sha256:ec9bfaf3ad2df51ace80688143a6a4ebc09a248f6ff781a9945e51937008fcbc \ + --hash=sha256:ff2983983d34813c1aeb0fa89091e76c3a22889ee83ab27c5eeb45100560c049 \ + --hash=sha256:5ee18d9ebdb417e384b58fe414e8d6af9f4e7a0ae761519fb50f721de398dd4e \ + --hash=sha256:c2541745709bad0264b7d4705ad453b76ccd191e64aa6f0fc66b69a293a45ece \ + --hash=sha256:c742f741d58a28940ce01d58f0ab2ea3ced8b12402f162f4d534dfe18ba1cd6a \ + --hash=sha256:7f86fd587c4ed9dd76f318225e7d9b29cfc5a9d43de44e5754db8d1128487085 \ + --hash=sha256:ff18e6a727ee0ab0388507b89d1bc6a22b138d1e2fa56d1ad494586d61d2eae9 \ + --hash=sha256:136443dbd7e1dee43c68ac2694fde36b2849865fa258d39bf822c10e8068eac5 \ + --hash=sha256:5e262d41726bc187e69af7825504c933b6794dc3fbd5945e41a79bb14c31f585 \ + --hash=sha256:5cb41aa38891e073ee49d55fbc7839cfdb2bc0e600add13874d048c94aadddd1 \ + --hash=sha256:da25dc3563bff5965356133435b757a795a17b17d01dbc0f42fb32447ddfd917 \ + --hash=sha256:52c8ef851d9a240f11a88c003eacb03c31fc1c9c4ec64a99a0f922b93874fda9 \ + --hash=sha256:f758f1b9299d059cc3f6546ae2af89670cb1c4d48ea29c3cacc4fe7de3058257 \ + --hash=sha256:36d2bd2ad5fb9eaddba5226aa02c8ec3fa4f192631e347b3ed28186d43be6b54 \ + --hash=sha256:eb0dc4e38e6a1fd579e5d50369aa2e10acfc9cace504579b2faabb478e76941a \ + --hash=sha256:c7f2c7f2b9ca6bdeef8f0fa897f8e05085923eb091721675170254cbc5b02897 \ + --hash=sha256:f3c6818a1a86dd6dca7ddcaaf76947d5ba31aecc28cb1b67009a5877c9a64f3f \ + --hash=sha256:d312ef37c91508b0ab2cee7da26ec0b3ed2f03ce12bd87a588d771ae15dcf82d \ + --hash=sha256:51529d40e3ca50046d7606fa99ce3956a617f9b36380da3b7f0dd3dd28e68cb5 \ + --hash=sha256:2190f2e9dd7508d2a90ded5ed369255980a1bcdd58e52f7fe24b8162bf9fedbd \ + --hash=sha256:8d65a2fbf9d2f8352685bc1364177ee3923d6baf5e7f43ea4959d7d8bc326a36 \ + --hash=sha256:4b605484e43cdc43f0954ddae319fb75f04cc10dd80d830540060ee7cd0243cd \ + --hash=sha256:fd0409a3653af6c147209d267a0e4243f0ae46b011aa978b1080359fddc9b6cf \ + --hash=sha256:a120733b01c45e9a0c34aeef92bf0cf1d56cfe81ed9d47d562f9ed591a9828ac \ + --hash=sha256:559db847dc486944896521f68d8190be1c9e719fced785720d2216fe7022b662 \ + --hash=sha256:01f520d4f53ef97964a240a035ec2a869fe1a37dde002b57ebc4417a27ccd853 \ + --hash=sha256:7f94b27a62cfad8496c8d2513e1a222dd446f095fca8987fceef261225538a15 \ + --hash=sha256:ede3e6487c5ef5d28634ba3f31f989030ad6af71edfb0055cbbd14189ff240ba \ + --hash=sha256:3d48a93ee1c9b79c04bb38772ee1b64dcf18ff43085896ea460ca8dec96f35f6 \ + --hash=sha256:88dceee75c2c63af144e456745e10101eb67361050196b0b6af5d717254dddf7 \ + --hash=sha256:b8c198f8c1805dc42708689ed6864951fd2494f924149d3e4bce7710f8eb5232 \ + --hash=sha256:d4d8fe59808a54658fcc0160ecfb1b30f9089906c50b23bcb4c69eddc19ec2b4 \ + --hash=sha256:7008df2e7655c495dd12d2a4ad038ff878d4ca4b81fccaf82b714e07eae4402c \ + --hash=sha256:1d8591993e228b0c930c4bb0db464bdad97b3289fb981255d6c9a41aedc84b2d \ + --hash=sha256:734e20b57ba95624ecf1841e72b53f6e186355e216e5412de414e3c51e5e3c41 \ + --hash=sha256:8a650c2dbafa08d42e51ba0b62740dae4ecb9338eefa093aa5c78ceb546fcd5c \ + --hash=sha256:504aa796fe0569bb43171066009ead363de03675276d2d121ac1a4572397870f \ + --hash=sha256:b1d22e6e9387bf4739fbe23bfa80e93f6b0373a7f1b96c6227c32bef95a4d7a8 \ + --hash=sha256:2c1c351919aca02858f740c6d33adea0c5deea37f9ecca1cc1ef9e884a619d26 \ + --hash=sha256:eab21f45c7f66c13f2a9e0e1535309cee140182a9cdae1e041d02e47291e8396 \ + --hash=sha256:0d85819802132122da43cb86656f8d1f8c6587d54ae7dcaf30e90533028b49fe \ + --hash=sha256:7c7e1a961a0b2f2472c1ac5b69affa0ae1132c39adcb67aba98568702b9cc23f + +typing-extensions==4.15.0 ; python_version >= "3.10" \ + --hash=sha256:f0fa19c6845758ab08074a0cfa8b7aecb71c999ca73d62883bc25cc018c4e548 \ + --hash=sha256:0cea48d173cc12fa28ecabc3b837ea3cf6f38c6d1136f85cbaaf598984861466 + +urllib3==2.6.3 ; python_version >= "3.10" \ + --hash=sha256:bf272323e553dfb2e87d9bfd225ca7b0f467b919d7bbd355436d3fd37cb0acd4 \ + --hash=sha256:1b62b6884944a57dbe321509ab94fd4d3b307075e0c2eae991ac71ee15ad38ed + +virtualenv==21.2.0 ; python_version >= "3.10" \ + --hash=sha256:1bd755b504931164a5a496d217c014d098426cddc79363ad66ac78125f9d908f \ + --hash=sha256:1720dc3a62ef5b443092e3f499228599045d7fea4c79199770499df8becf9098 + +watchdog==6.0.0 ; python_version >= "3.10" \ + --hash=sha256:d1cdb490583ebd691c012b3d6dae011000fe42edb7a82ece80965b42abd61f26 \ + --hash=sha256:bc64ab3bdb6a04d69d4023b29422170b74681784ffb9463ed4870cf2f3e66112 \ + --hash=sha256:c897ac1b55c5a1461e16dae288d22bb2e412ba9807df8397a635d88f671d36c3 \ + --hash=sha256:6eb11feb5a0d452ee41f824e271ca311a09e250441c262ca2fd7ebcf2461a06c \ + --hash=sha256:ef810fbf7b781a5a593894e4f439773830bdecb885e6880d957d5b9382a960d2 \ + --hash=sha256:afd0fe1b2270917c5e23c2a65ce50c2a4abb63daafb0d419fde368e272a76b7c \ + --hash=sha256:bdd4e6f14b8b18c334febb9c4425a878a2ac20efd1e0b231978e7b150f92a948 \ + --hash=sha256:c7c15dda13c4eb00d6fb6fc508b3c0ed88b9d5d374056b239c4ad1611125c860 \ + --hash=sha256:6f10cb2d5902447c7d0da897e2c6768bca89174d0c6e1e30abec5421af97a5b0 \ + --hash=sha256:490ab2ef84f11129844c23fb14ecf30ef3d8a6abafd3754a6f75ca1e6654136c \ + --hash=sha256:76aae96b00ae814b181bb25b1b98076d5fc84e8a53cd8885a318b42b6d3a5134 \ + --hash=sha256:a175f755fc2279e0b7312c0035d52e27211a5bc39719dd529625b1930917345b \ + --hash=sha256:e6f0e77c9417e7cd62af82529b10563db3423625c5fce018430b249bf977f9e8 \ + --hash=sha256:90c8e78f3b94014f7aaae121e6b909674df5b46ec24d6bebc45c44c56729af2a \ + --hash=sha256:e7631a77ffb1f7d2eefa4445ebbee491c720a5661ddf6df3498ebecae5ed375c \ + --hash=sha256:c7ac31a19f4545dd92fc25d200694098f42c9a8e391bc00bdd362c5736dbf881 \ + --hash=sha256:9513f27a1a582d9808cf21a07dae516f0fab1cf2d7683a742c498b93eedabb11 \ + --hash=sha256:7a0e56874cfbc4b9b05c60c8a1926fedf56324bb08cfbc188969777940aef3aa \ + --hash=sha256:e6439e374fc012255b4ec786ae3c4bc838cd7309a540e5fe0952d03687d8804e \ + --hash=sha256:7607498efa04a3542ae3e05e64da8202e58159aa1fa4acddf7678d34a35d4f13 \ + --hash=sha256:9041567ee8953024c83343288ccc458fd0a2d811d6a0fd68c4c22609e3490379 \ + --hash=sha256:82dc3e3143c7e38ec49d61af98d6558288c415eac98486a5c581726e0737c00e \ + --hash=sha256:212ac9b8bf1161dc91bd09c048048a95ca3a4c4f5e5d4a7d1b1a7d5752a7f96f \ + --hash=sha256:e3df4cbb9a450c6d49318f6d14f4bbc80d763fa587ba46ec86f99f9e6876bb26 \ + --hash=sha256:2cce7cfc2008eb51feb6aab51251fd79b85d9894e98ba847408f662b3395ca3c \ + --hash=sha256:20ffe5b202af80ab4266dcd3e91aae72bf2da48c0d33bdb15c66658e685e94e2 \ + --hash=sha256:07df1fdd701c5d4c8e55ef6cf55b8f0120fe1aef7ef39a1c6fc6bc2e606d517a \ + --hash=sha256:cbafb470cf848d93b5d013e2ecb245d4aa1c8fd0504e863ccefa32445359d680 \ + --hash=sha256:a1914259fa9e1454315171103c6a30961236f508b9b623eae470268bbcc6a22f \ + --hash=sha256:9ddf7c82fda3ae8e24decda1338ede66e1c99883db93711d8fb941eaa2d8c282 + +wheel==0.45.1 ; python_version >= "3.10" \ + --hash=sha256:708e7481cc80179af0e556bbf0cc00b8444c7321e2700b8d8580231d13017248 diff --git a/docs/README.md b/docs/README.md index 4663db02..2d6fc8c3 100644 --- a/docs/README.md +++ b/docs/README.md @@ -1,8 +1,9 @@ # Overview -`embit` is a bitcoin library designed to run either with Python 3 on a PC or with [MicroPython](https://micropython.org/) on embedded devices. +`embit` is a bitcoin library designed to run either with CPython 3.10+ on a PC or with [MicroPython](https://micropython.org/) on embedded devices. -For cryptography it uses [libsecp256k1](https://github.com/bitcoin-core/secp256k1) library maintained by [Bitcoin Core](https://bitcoincore.org/) team for elliptic, and everything else is implemented in python. +For cryptography, `embit` uses [libsecp256k1](https://github.com/bitcoin-core/secp256k1) through optional ctypes bindings when available, otherwise it uses the pure-Python fallback implementation. +On CPython, ctypes lookup order is local `secp256k1/secp256k1-zkp/.libs`, then system `libsecp256k1`, then system `secp256k1`, then `src/embit/util/prebuilt` (compatibility-only; no shipped binaries). ## Supported features: @@ -16,7 +17,7 @@ For cryptography it uses [libsecp256k1](https://github.com/bitcoin-core/secp256k ## Installation -**Python 3:** +**CPython 3.10+:** ```sh pip3 install embit ``` @@ -29,7 +30,7 @@ pip3 install embit This script generates bip39 recovery phrase, converts it to the root key, derives native segwit xpub, prints first 5 receiving addresses, parses PSBT transaction and signs it. -We use `hexlify` and `%s` formatting to keep it compatible with MicroPython, if you use Python3 you can use `.hex()` and f-strings. +We use `hexlify` and `%s` formatting to keep it compatible with MicroPython; on CPython you can use `.hex()` and f-strings. For more details check out the [API docs](./api/README.md) diff --git a/docs/package-content-policy.md b/docs/package-content-policy.md new file mode 100644 index 00000000..b3d70e2c --- /dev/null +++ b/docs/package-content-policy.md @@ -0,0 +1,33 @@ +# Package Content Policy + +This project publishes source-only Python artifacts. + +## Allowed in published artifacts + +- Python source files required at runtime (`src/embit/**/*.py`) +- Build metadata and license files + +## Forbidden in published artifacts + +- `.pth` files +- `sitecustomize.py` and `usercustomize.py` +- Bundled native binaries (`*.so`, `*.dylib`, `*.dll`) in any package path +- Bundled executable binaries beyond the reviewed native library suffixes above +- Nested distributions (`*.whl`, `*.tar.gz`, `*.egg`) embedded inside sdist/wheel payloads +- Nested package metadata payloads (`*.egg-info`, `*.dist-info`) from other distributions +- Prebuilt native artifacts under `src/embit/util/prebuilt/` +- Top-level executable scripts unless intentionally declared in packaging metadata and reviewed +- Install-time network access +- Install-time execution hooks, including custom setup/install commands or hidden startup injection paths +- Release-time code generation from the network + +## Packaging controls + +- `MANIFEST.in` is the source of truth for sdist include/prune behavior. +- Setuptools package data must not include native artifacts. +- `setup.py` must remain declarative. It must not define custom command classes, + install hooks, or other build/install execution paths. +- Artifact verification must inspect built sdists and wheels, not only the + source tree, and confirm mechanically checkable forbidden payloads are absent + before release. +- Non-mechanical policy items must be covered by release-sensitive file review. diff --git a/poetry.lock b/poetry.lock new file mode 100644 index 00000000..ce78e778 --- /dev/null +++ b/poetry.lock @@ -0,0 +1,1634 @@ +# This file is automatically @generated by Poetry 2.3.2 and should not be changed by hand. + +[[package]] +name = "babel" +version = "2.18.0" +description = "Internationalization utilities" +optional = true +python-versions = ">=3.8" +groups = ["main"] +markers = "extra == \"dev\"" +files = [ + {file = "babel-2.18.0-py3-none-any.whl", hash = "sha256:e2b422b277c2b9a9630c1d7903c2a00d0830c409c59ac8cae9081c92f1aeba35"}, + {file = "babel-2.18.0.tar.gz", hash = "sha256:b80b99a14bd085fcacfa15c9165f651fbb3406e66cc603abf11c5750937c992d"}, +] + +[package.extras] +dev = ["backports.zoneinfo ; python_version < \"3.9\"", "freezegun (>=1.0,<2.0)", "jinja2 (>=3.0)", "pytest (>=6.0)", "pytest-cov", "pytz", "setuptools", "tzdata ; sys_platform == \"win32\""] + +[[package]] +name = "backrefs" +version = "6.2" +description = "A wrapper around re and regex that adds additional back references." +optional = true +python-versions = ">=3.9" +groups = ["main"] +markers = "extra == \"dev\"" +files = [ + {file = "backrefs-6.2-py310-none-any.whl", hash = "sha256:0fdc7b012420b6b144410342caeb8adc54c6866cf12064abc9bb211302e496f8"}, + {file = "backrefs-6.2-py311-none-any.whl", hash = "sha256:08aa7fae530c6b2361d7bdcbda1a7c454e330cc9dbcd03f5c23205e430e5c3be"}, + {file = "backrefs-6.2-py312-none-any.whl", hash = "sha256:c3f4b9cb2af8cda0d87ab4f57800b57b95428488477be164dd2b47be54db0c90"}, + {file = "backrefs-6.2-py313-none-any.whl", hash = "sha256:12df81596ab511f783b7d87c043ce26bc5b0288cf3bb03610fe76b8189282b2b"}, + {file = "backrefs-6.2-py314-none-any.whl", hash = "sha256:e5f805ae09819caa1aa0623b4a83790e7028604aa2b8c73ba602c4454e665de7"}, + {file = "backrefs-6.2-py39-none-any.whl", hash = "sha256:664e33cd88c6840b7625b826ecf2555f32d491800900f5a541f772c485f7cda7"}, + {file = "backrefs-6.2.tar.gz", hash = "sha256:f44ff4d48808b243b6c0cdc6231e22195c32f77046018141556c66f8bab72a49"}, +] + +[package.extras] +extras = ["regex"] + +[[package]] +name = "black" +version = "26.3.1" +description = "The uncompromising code formatter." +optional = true +python-versions = ">=3.10" +groups = ["main"] +markers = "extra == \"dev\"" +files = [ + {file = "black-26.3.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:86a8b5035fce64f5dcd1b794cf8ec4d31fe458cf6ce3986a30deb434df82a1d2"}, + {file = "black-26.3.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:5602bdb96d52d2d0672f24f6ffe5218795736dd34807fd0fd55ccd6bf206168b"}, + {file = "black-26.3.1-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6c54a4a82e291a1fee5137371ab488866b7c86a3305af4026bdd4dc78642e1ac"}, + {file = "black-26.3.1-cp310-cp310-win_amd64.whl", hash = "sha256:6e131579c243c98f35bce64a7e08e87fb2d610544754675d4a0e73a070a5aa3a"}, + {file = "black-26.3.1-cp310-cp310-win_arm64.whl", hash = "sha256:5ed0ca58586c8d9a487352a96b15272b7fa55d139fc8496b519e78023a8dab0a"}, + {file = "black-26.3.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:28ef38aee69e4b12fda8dba75e21f9b4f979b490c8ac0baa7cb505369ac9e1ff"}, + {file = "black-26.3.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:bf9bf162ed91a26f1adba8efda0b573bc6924ec1408a52cc6f82cb73ec2b142c"}, + {file = "black-26.3.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:474c27574d6d7037c1bc875a81d9be0a9a4f9ee95e62800dab3cfaadbf75acd5"}, + {file = "black-26.3.1-cp311-cp311-win_amd64.whl", hash = "sha256:5e9d0d86df21f2e1677cc4bd090cd0e446278bcbbe49bf3659c308c3e402843e"}, + {file = "black-26.3.1-cp311-cp311-win_arm64.whl", hash = "sha256:9a5e9f45e5d5e1c5b5c29b3bd4265dcc90e8b92cf4534520896ed77f791f4da5"}, + {file = "black-26.3.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:b5e6f89631eb88a7302d416594a32faeee9fb8fb848290da9d0a5f2903519fc1"}, + {file = "black-26.3.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:41cd2012d35b47d589cb8a16faf8a32ef7a336f56356babd9fcf70939ad1897f"}, + {file = "black-26.3.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0f76ff19ec5297dd8e66eb64deda23631e642c9393ab592826fd4bdc97a4bce7"}, + {file = "black-26.3.1-cp312-cp312-win_amd64.whl", hash = "sha256:ddb113db38838eb9f043623ba274cfaf7d51d5b0c22ecb30afe58b1bb8322983"}, + {file = "black-26.3.1-cp312-cp312-win_arm64.whl", hash = "sha256:dfdd51fc3e64ea4f35873d1b3fb25326773d55d2329ff8449139ebaad7357efb"}, + {file = "black-26.3.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:855822d90f884905362f602880ed8b5df1b7e3ee7d0db2502d4388a954cc8c54"}, + {file = "black-26.3.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:8a33d657f3276328ce00e4d37fe70361e1ec7614da5d7b6e78de5426cb56332f"}, + {file = "black-26.3.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f1cd08e99d2f9317292a311dfe578fd2a24b15dbce97792f9c4d752275c1fa56"}, + {file = "black-26.3.1-cp313-cp313-win_amd64.whl", hash = "sha256:c7e72339f841b5a237ff14f7d3880ddd0fc7f98a1199e8c4327f9a4f478c1839"}, + {file = "black-26.3.1-cp313-cp313-win_arm64.whl", hash = "sha256:afc622538b430aa4c8c853f7f63bc582b3b8030fd8c80b70fb5fa5b834e575c2"}, + {file = "black-26.3.1-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:2d6bfaf7fd0993b420bed691f20f9492d53ce9a2bcccea4b797d34e947318a78"}, + {file = "black-26.3.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:f89f2ab047c76a9c03f78d0d66ca519e389519902fa27e7a91117ef7611c0568"}, + {file = "black-26.3.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b07fc0dab849d24a80a29cfab8d8a19187d1c4685d8a5e6385a5ce323c1f015f"}, + {file = "black-26.3.1-cp314-cp314-win_amd64.whl", hash = "sha256:0126ae5b7c09957da2bdbd91a9ba1207453feada9e9fe51992848658c6c8e01c"}, + {file = "black-26.3.1-cp314-cp314-win_arm64.whl", hash = "sha256:92c0ec1f2cc149551a2b7b47efc32c866406b6891b0ee4625e95967c8f4acfb1"}, + {file = "black-26.3.1-py3-none-any.whl", hash = "sha256:2bd5aa94fc267d38bb21a70d7410a89f1a1d318841855f698746f8e7f51acd1b"}, + {file = "black-26.3.1.tar.gz", hash = "sha256:2c50f5063a9641c7eed7795014ba37b0f5fa227f3d408b968936e24bc0566b07"}, +] + +[package.dependencies] +click = ">=8.0.0" +mypy-extensions = ">=0.4.3" +packaging = ">=22.0" +pathspec = ">=1.0.0" +platformdirs = ">=2" +pytokens = ">=0.4.0,<0.5.0" +tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""} +typing-extensions = {version = ">=4.0.1", markers = "python_version < \"3.11\""} + +[package.extras] +colorama = ["colorama (>=0.4.3)"] +d = ["aiohttp (>=3.10)"] +jupyter = ["ipython (>=7.8.0)", "tokenize-rt (>=3.2.0)"] +uvloop = ["uvloop (>=0.15.2) ; sys_platform != \"win32\"", "winloop (>=0.5.0) ; sys_platform == \"win32\""] + +[[package]] +name = "certifi" +version = "2026.2.25" +description = "Python package for providing Mozilla's CA Bundle." +optional = true +python-versions = ">=3.7" +groups = ["main"] +markers = "extra == \"dev\"" +files = [ + {file = "certifi-2026.2.25-py3-none-any.whl", hash = "sha256:027692e4402ad994f1c42e52a4997a9763c646b73e4096e4d5d6db8af1d6f0fa"}, + {file = "certifi-2026.2.25.tar.gz", hash = "sha256:e887ab5cee78ea814d3472169153c2d12cd43b14bd03329a39a9c6e2e80bfba7"}, +] + +[[package]] +name = "cfgv" +version = "3.5.0" +description = "Validate configuration and produce human readable error messages." +optional = true +python-versions = ">=3.10" +groups = ["main"] +markers = "extra == \"dev\"" +files = [ + {file = "cfgv-3.5.0-py2.py3-none-any.whl", hash = "sha256:a8dc6b26ad22ff227d2634a65cb388215ce6cc96bbcc5cfde7641ae87e8dacc0"}, + {file = "cfgv-3.5.0.tar.gz", hash = "sha256:d5b1034354820651caa73ede66a6294d6e95c1b00acc5e9b098e917404669132"}, +] + +[[package]] +name = "charset-normalizer" +version = "3.4.6" +description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet." +optional = true +python-versions = ">=3.7" +groups = ["main"] +markers = "extra == \"dev\"" +files = [ + {file = "charset_normalizer-3.4.6-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:2e1d8ca8611099001949d1cdfaefc510cf0f212484fe7c565f735b68c78c3c95"}, + {file = "charset_normalizer-3.4.6-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e25369dc110d58ddf29b949377a93e0716d72a24f62bad72b2b39f155949c1fd"}, + {file = "charset_normalizer-3.4.6-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:259695e2ccc253feb2a016303543d691825e920917e31f894ca1a687982b1de4"}, + {file = "charset_normalizer-3.4.6-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:dda86aba335c902b6149a02a55b38e96287157e609200811837678214ba2b1db"}, + {file = "charset_normalizer-3.4.6-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:51fb3c322c81d20567019778cb5a4a6f2dc1c200b886bc0d636238e364848c89"}, + {file = "charset_normalizer-3.4.6-cp310-cp310-manylinux_2_31_armv7l.whl", hash = "sha256:4482481cb0572180b6fd976a4d5c72a30263e98564da68b86ec91f0fe35e8565"}, + {file = "charset_normalizer-3.4.6-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:39f5068d35621da2881271e5c3205125cc456f54e9030d3f723288c873a71bf9"}, + {file = "charset_normalizer-3.4.6-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:8bea55c4eef25b0b19a0337dc4e3f9a15b00d569c77211fa8cde38684f234fb7"}, + {file = "charset_normalizer-3.4.6-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:f0cdaecd4c953bfae0b6bb64910aaaca5a424ad9c72d85cb88417bb9814f7550"}, + {file = "charset_normalizer-3.4.6-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:150b8ce8e830eb7ccb029ec9ca36022f756986aaaa7956aad6d9ec90089338c0"}, + {file = "charset_normalizer-3.4.6-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:e68c14b04827dd76dcbd1aeea9e604e3e4b78322d8faf2f8132c7138efa340a8"}, + {file = "charset_normalizer-3.4.6-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:3778fd7d7cd04ae8f54651f4a7a0bd6e39a0cf20f801720a4c21d80e9b7ad6b0"}, + {file = "charset_normalizer-3.4.6-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:dad6e0f2e481fffdcf776d10ebee25e0ef89f16d691f1e5dee4b586375fdc64b"}, + {file = "charset_normalizer-3.4.6-cp310-cp310-win32.whl", hash = "sha256:74a2e659c7ecbc73562e2a15e05039f1e22c75b7c7618b4b574a3ea9118d1557"}, + {file = "charset_normalizer-3.4.6-cp310-cp310-win_amd64.whl", hash = "sha256:aa9cccf4a44b9b62d8ba8b4dd06c649ba683e4bf04eea606d2e94cfc2d6ff4d6"}, + {file = "charset_normalizer-3.4.6-cp310-cp310-win_arm64.whl", hash = "sha256:e985a16ff513596f217cee86c21371b8cd011c0f6f056d0920aa2d926c544058"}, + {file = "charset_normalizer-3.4.6-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:82060f995ab5003a2d6e0f4ad29065b7672b6593c8c63559beefe5b443242c3e"}, + {file = "charset_normalizer-3.4.6-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:60c74963d8350241a79cb8feea80e54d518f72c26db618862a8f53e5023deaf9"}, + {file = "charset_normalizer-3.4.6-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:f6e4333fb15c83f7d1482a76d45a0818897b3d33f00efd215528ff7c51b8e35d"}, + {file = "charset_normalizer-3.4.6-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:bc72863f4d9aba2e8fd9085e63548a324ba706d2ea2c83b260da08a59b9482de"}, + {file = "charset_normalizer-3.4.6-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9cc4fc6c196d6a8b76629a70ddfcd4635a6898756e2d9cac5565cf0654605d73"}, + {file = "charset_normalizer-3.4.6-cp311-cp311-manylinux_2_31_armv7l.whl", hash = "sha256:0c173ce3a681f309f31b87125fecec7a5d1347261ea11ebbb856fa6006b23c8c"}, + {file = "charset_normalizer-3.4.6-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:c907cdc8109f6c619e6254212e794d6548373cc40e1ec75e6e3823d9135d29cc"}, + {file = "charset_normalizer-3.4.6-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:404a1e552cf5b675a87f0651f8b79f5f1e6fd100ee88dc612f89aa16abd4486f"}, + {file = "charset_normalizer-3.4.6-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:e3c701e954abf6fc03a49f7c579cc80c2c6cc52525340ca3186c41d3f33482ef"}, + {file = "charset_normalizer-3.4.6-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:7a6967aaf043bceabab5412ed6bd6bd26603dae84d5cb75bf8d9a74a4959d398"}, + {file = "charset_normalizer-3.4.6-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:5feb91325bbceade6afab43eb3b508c63ee53579fe896c77137ded51c6b6958e"}, + {file = "charset_normalizer-3.4.6-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:f820f24b09e3e779fe84c3c456cb4108a7aa639b0d1f02c28046e11bfcd088ed"}, + {file = "charset_normalizer-3.4.6-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:b35b200d6a71b9839a46b9b7fff66b6638bb52fc9658aa58796b0326595d3021"}, + {file = "charset_normalizer-3.4.6-cp311-cp311-win32.whl", hash = "sha256:9ca4c0b502ab399ef89248a2c84c54954f77a070f28e546a85e91da627d1301e"}, + {file = "charset_normalizer-3.4.6-cp311-cp311-win_amd64.whl", hash = "sha256:a9e68c9d88823b274cf1e72f28cb5dc89c990edf430b0bfd3e2fb0785bfeabf4"}, + {file = "charset_normalizer-3.4.6-cp311-cp311-win_arm64.whl", hash = "sha256:97d0235baafca5f2b09cf332cc275f021e694e8362c6bb9c96fc9a0eb74fc316"}, + {file = "charset_normalizer-3.4.6-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:2ef7fedc7a6ecbe99969cd09632516738a97eeb8bd7258bf8a0f23114c057dab"}, + {file = "charset_normalizer-3.4.6-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a4ea868bc28109052790eb2b52a9ab33f3aa7adc02f96673526ff47419490e21"}, + {file = "charset_normalizer-3.4.6-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:836ab36280f21fc1a03c99cd05c6b7af70d2697e374c7af0b61ed271401a72a2"}, + {file = "charset_normalizer-3.4.6-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:f1ce721c8a7dfec21fcbdfe04e8f68174183cf4e8188e0645e92aa23985c57ff"}, + {file = "charset_normalizer-3.4.6-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0e28d62a8fc7a1fa411c43bd65e346f3bce9716dc51b897fbe930c5987b402d5"}, + {file = "charset_normalizer-3.4.6-cp312-cp312-manylinux_2_31_armv7l.whl", hash = "sha256:530d548084c4a9f7a16ed4a294d459b4f229db50df689bfe92027452452943a0"}, + {file = "charset_normalizer-3.4.6-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:30f445ae60aad5e1f8bdbb3108e39f6fbc09f4ea16c815c66578878325f8f15a"}, + {file = "charset_normalizer-3.4.6-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:ac2393c73378fea4e52aa56285a3d64be50f1a12395afef9cce47772f60334c2"}, + {file = "charset_normalizer-3.4.6-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:90ca27cd8da8118b18a52d5f547859cc1f8354a00cd1e8e5120df3e30d6279e5"}, + {file = "charset_normalizer-3.4.6-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:8e5a94886bedca0f9b78fecd6afb6629142fd2605aa70a125d49f4edc6037ee6"}, + {file = "charset_normalizer-3.4.6-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:695f5c2823691a25f17bc5d5ffe79fa90972cc34b002ac6c843bb8a1720e950d"}, + {file = "charset_normalizer-3.4.6-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:231d4da14bcd9301310faf492051bee27df11f2bc7549bc0bb41fef11b82daa2"}, + {file = "charset_normalizer-3.4.6-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:a056d1ad2633548ca18ffa2f85c202cfb48b68615129143915b8dc72a806a923"}, + {file = "charset_normalizer-3.4.6-cp312-cp312-win32.whl", hash = "sha256:c2274ca724536f173122f36c98ce188fd24ce3dad886ec2b7af859518ce008a4"}, + {file = "charset_normalizer-3.4.6-cp312-cp312-win_amd64.whl", hash = "sha256:c8ae56368f8cc97c7e40a7ee18e1cedaf8e780cd8bc5ed5ac8b81f238614facb"}, + {file = "charset_normalizer-3.4.6-cp312-cp312-win_arm64.whl", hash = "sha256:899d28f422116b08be5118ef350c292b36fc15ec2daeb9ea987c89281c7bb5c4"}, + {file = "charset_normalizer-3.4.6-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:11afb56037cbc4b1555a34dd69151e8e069bee82e613a73bef6e714ce733585f"}, + {file = "charset_normalizer-3.4.6-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:423fb7e748a08f854a08a222b983f4df1912b1daedce51a72bd24fe8f26a1843"}, + {file = "charset_normalizer-3.4.6-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:d73beaac5e90173ac3deb9928a74763a6d230f494e4bfb422c217a0ad8e629bf"}, + {file = "charset_normalizer-3.4.6-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:d60377dce4511655582e300dc1e5a5f24ba0cb229005a1d5c8d0cb72bb758ab8"}, + {file = "charset_normalizer-3.4.6-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:530e8cebeea0d76bdcf93357aa5e41336f48c3dc709ac52da2bb167c5b8271d9"}, + {file = "charset_normalizer-3.4.6-cp313-cp313-manylinux_2_31_armv7l.whl", hash = "sha256:a26611d9987b230566f24a0a125f17fe0de6a6aff9f25c9f564aaa2721a5fb88"}, + {file = "charset_normalizer-3.4.6-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:34315ff4fc374b285ad7f4a0bf7dcbfe769e1b104230d40f49f700d4ab6bbd84"}, + {file = "charset_normalizer-3.4.6-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:5f8ddd609f9e1af8c7bd6e2aca279c931aefecd148a14402d4e368f3171769fd"}, + {file = "charset_normalizer-3.4.6-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:80d0a5615143c0b3225e5e3ef22c8d5d51f3f72ce0ea6fb84c943546c7b25b6c"}, + {file = "charset_normalizer-3.4.6-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:92734d4d8d187a354a556626c221cd1a892a4e0802ccb2af432a1d85ec012194"}, + {file = "charset_normalizer-3.4.6-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:613f19aa6e082cf96e17e3ffd89383343d0d589abda756b7764cf78361fd41dc"}, + {file = "charset_normalizer-3.4.6-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:2b1a63e8224e401cafe7739f77efd3f9e7f5f2026bda4aead8e59afab537784f"}, + {file = "charset_normalizer-3.4.6-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:6cceb5473417d28edd20c6c984ab6fee6c6267d38d906823ebfe20b03d607dc2"}, + {file = "charset_normalizer-3.4.6-cp313-cp313-win32.whl", hash = "sha256:d7de2637729c67d67cf87614b566626057e95c303bc0a55ffe391f5205e7003d"}, + {file = "charset_normalizer-3.4.6-cp313-cp313-win_amd64.whl", hash = "sha256:572d7c822caf521f0525ba1bce1a622a0b85cf47ffbdae6c9c19e3b5ac3c4389"}, + {file = "charset_normalizer-3.4.6-cp313-cp313-win_arm64.whl", hash = "sha256:a4474d924a47185a06411e0064b803c68be044be2d60e50e8bddcc2649957c1f"}, + {file = "charset_normalizer-3.4.6-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:9cc6e6d9e571d2f863fa77700701dae73ed5f78881efc8b3f9a4398772ff53e8"}, + {file = "charset_normalizer-3.4.6-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ef5960d965e67165d75b7c7ffc60a83ec5abfc5c11b764ec13ea54fbef8b4421"}, + {file = "charset_normalizer-3.4.6-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:b3694e3f87f8ac7ce279d4355645b3c878d24d1424581b46282f24b92f5a4ae2"}, + {file = "charset_normalizer-3.4.6-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:5d11595abf8dd942a77883a39d81433739b287b6aa71620f15164f8096221b30"}, + {file = "charset_normalizer-3.4.6-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7bda6eebafd42133efdca535b04ccb338ab29467b3f7bf79569883676fc628db"}, + {file = "charset_normalizer-3.4.6-cp314-cp314-manylinux_2_31_armv7l.whl", hash = "sha256:bbc8c8650c6e51041ad1be191742b8b421d05bbd3410f43fa2a00c8db87678e8"}, + {file = "charset_normalizer-3.4.6-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:22c6f0c2fbc31e76c3b8a86fba1a56eda6166e238c29cdd3d14befdb4a4e4815"}, + {file = "charset_normalizer-3.4.6-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:7edbed096e4a4798710ed6bc75dcaa2a21b68b6c356553ac4823c3658d53743a"}, + {file = "charset_normalizer-3.4.6-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:7f9019c9cb613f084481bd6a100b12e1547cf2efe362d873c2e31e4035a6fa43"}, + {file = "charset_normalizer-3.4.6-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:58c948d0d086229efc484fe2f30c2d382c86720f55cd9bc33591774348ad44e0"}, + {file = "charset_normalizer-3.4.6-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:419a9d91bd238052642a51938af8ac05da5b3343becde08d5cdeab9046df9ee1"}, + {file = "charset_normalizer-3.4.6-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:5273b9f0b5835ff0350c0828faea623c68bfa65b792720c453e22b25cc72930f"}, + {file = "charset_normalizer-3.4.6-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:0e901eb1049fdb80f5bd11ed5ea1e498ec423102f7a9b9e4645d5b8204ff2815"}, + {file = "charset_normalizer-3.4.6-cp314-cp314-win32.whl", hash = "sha256:b4ff1d35e8c5bd078be89349b6f3a845128e685e751b6ea1169cf2160b344c4d"}, + {file = "charset_normalizer-3.4.6-cp314-cp314-win_amd64.whl", hash = "sha256:74119174722c4349af9708993118581686f343adc1c8c9c007d59be90d077f3f"}, + {file = "charset_normalizer-3.4.6-cp314-cp314-win_arm64.whl", hash = "sha256:e5bcc1a1ae744e0bb59641171ae53743760130600da8db48cbb6e4918e186e4e"}, + {file = "charset_normalizer-3.4.6-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:ad8faf8df23f0378c6d527d8b0b15ea4a2e23c89376877c598c4870d1b2c7866"}, + {file = "charset_normalizer-3.4.6-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f5ea69428fa1b49573eef0cc44a1d43bebd45ad0c611eb7d7eac760c7ae771bc"}, + {file = "charset_normalizer-3.4.6-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:06a7e86163334edfc5d20fe104db92fcd666e5a5df0977cb5680a506fe26cc8e"}, + {file = "charset_normalizer-3.4.6-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:e1f6e2f00a6b8edb562826e4632e26d063ac10307e80f7461f7de3ad8ef3f077"}, + {file = "charset_normalizer-3.4.6-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:95b52c68d64c1878818687a473a10547b3292e82b6f6fe483808fb1468e2f52f"}, + {file = "charset_normalizer-3.4.6-cp314-cp314t-manylinux_2_31_armv7l.whl", hash = "sha256:7504e9b7dc05f99a9bbb4525c67a2c155073b44d720470a148b34166a69c054e"}, + {file = "charset_normalizer-3.4.6-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:172985e4ff804a7ad08eebec0a1640ece87ba5041d565fff23c8f99c1f389484"}, + {file = "charset_normalizer-3.4.6-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:4be9f4830ba8741527693848403e2c457c16e499100963ec711b1c6f2049b7c7"}, + {file = "charset_normalizer-3.4.6-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:79090741d842f564b1b2827c0b82d846405b744d31e84f18d7a7b41c20e473ff"}, + {file = "charset_normalizer-3.4.6-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:87725cfb1a4f1f8c2fc9890ae2f42094120f4b44db9360be5d99a4c6b0e03a9e"}, + {file = "charset_normalizer-3.4.6-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:fcce033e4021347d80ed9c66dcf1e7b1546319834b74445f561d2e2221de5659"}, + {file = "charset_normalizer-3.4.6-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:ca0276464d148c72defa8bb4390cce01b4a0e425f3b50d1435aa6d7a18107602"}, + {file = "charset_normalizer-3.4.6-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:197c1a244a274bb016dd8b79204850144ef77fe81c5b797dc389327adb552407"}, + {file = "charset_normalizer-3.4.6-cp314-cp314t-win32.whl", hash = "sha256:2a24157fa36980478dd1770b585c0f30d19e18f4fb0c47c13aa568f871718579"}, + {file = "charset_normalizer-3.4.6-cp314-cp314t-win_amd64.whl", hash = "sha256:cd5e2801c89992ed8c0a3f0293ae83c159a60d9a5d685005383ef4caca77f2c4"}, + {file = "charset_normalizer-3.4.6-cp314-cp314t-win_arm64.whl", hash = "sha256:47955475ac79cc504ef2704b192364e51d0d473ad452caedd0002605f780101c"}, + {file = "charset_normalizer-3.4.6-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:659a1e1b500fac8f2779dd9e1570464e012f43e580371470b45277a27baa7532"}, + {file = "charset_normalizer-3.4.6-cp38-cp38-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f61aa92e4aad0be58eb6eb4e0c21acf32cf8065f4b2cae5665da756c4ceef982"}, + {file = "charset_normalizer-3.4.6-cp38-cp38-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:f50498891691e0864dc3da965f340fada0771f6142a378083dc4608f4ea513e2"}, + {file = "charset_normalizer-3.4.6-cp38-cp38-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:bf625105bb9eef28a56a943fec8c8a98aeb80e7d7db99bd3c388137e6eb2d237"}, + {file = "charset_normalizer-3.4.6-cp38-cp38-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2bd9d128ef93637a5d7a6af25363cf5dec3fa21cf80e68055aad627f280e8afa"}, + {file = "charset_normalizer-3.4.6-cp38-cp38-manylinux_2_31_armv7l.whl", hash = "sha256:d08ec48f0a1c48d75d0356cea971921848fb620fdeba805b28f937e90691209f"}, + {file = "charset_normalizer-3.4.6-cp38-cp38-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:1ed80ff870ca6de33f4d953fda4d55654b9a2b340ff39ab32fa3adbcd718f264"}, + {file = "charset_normalizer-3.4.6-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:f98059e4fcd3e3e4e2d632b7cf81c2faae96c43c60b569e9c621468082f1d104"}, + {file = "charset_normalizer-3.4.6-cp38-cp38-musllinux_1_2_armv7l.whl", hash = "sha256:ab30e5e3e706e3063bc6de96b118688cb10396b70bb9864a430f67df98c61ecc"}, + {file = "charset_normalizer-3.4.6-cp38-cp38-musllinux_1_2_ppc64le.whl", hash = "sha256:d5f5d1e9def3405f60e3ca8232d56f35c98fb7bf581efcc60051ebf53cb8b611"}, + {file = "charset_normalizer-3.4.6-cp38-cp38-musllinux_1_2_riscv64.whl", hash = "sha256:461598cd852bfa5a61b09cae2b1c02e2efcd166ee5516e243d540ac24bfa68a7"}, + {file = "charset_normalizer-3.4.6-cp38-cp38-musllinux_1_2_s390x.whl", hash = "sha256:71be7e0e01753a89cf024abf7ecb6bca2c81738ead80d43004d9b5e3f1244e64"}, + {file = "charset_normalizer-3.4.6-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:df01808ee470038c3f8dc4f48620df7225c49c2d6639e38f96e6d6ac6e6f7b0e"}, + {file = "charset_normalizer-3.4.6-cp38-cp38-win32.whl", hash = "sha256:69dd852c2f0ad631b8b60cfbe25a28c0058a894de5abb566619c205ce0550eae"}, + {file = "charset_normalizer-3.4.6-cp38-cp38-win_amd64.whl", hash = "sha256:517ad0e93394ac532745129ceabdf2696b609ec9f87863d337140317ebce1c14"}, + {file = "charset_normalizer-3.4.6-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:31215157227939b4fb3d740cd23fe27be0439afef67b785a1eb78a3ae69cba9e"}, + {file = "charset_normalizer-3.4.6-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ecbbd45615a6885fe3240eb9db73b9e62518b611850fdf8ab08bd56de7ad2b17"}, + {file = "charset_normalizer-3.4.6-cp39-cp39-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:c45a03a4c69820a399f1dda9e1d8fbf3562eda46e7720458180302021b08f778"}, + {file = "charset_normalizer-3.4.6-cp39-cp39-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:e8aeb10fcbe92767f0fa69ad5a72deca50d0dca07fbde97848997d778a50c9fe"}, + {file = "charset_normalizer-3.4.6-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:54fae94be3d75f3e573c9a1b5402dc593de19377013c9a0e4285e3d402dd3a2a"}, + {file = "charset_normalizer-3.4.6-cp39-cp39-manylinux_2_31_armv7l.whl", hash = "sha256:2f7fdd9b6e6c529d6a2501a2d36b240109e78a8ceaef5687cfcfa2bbe671d297"}, + {file = "charset_normalizer-3.4.6-cp39-cp39-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:4d1d02209e06550bdaef34af58e041ad71b88e624f5d825519da3a3308e22687"}, + {file = "charset_normalizer-3.4.6-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:8bc5f0687d796c05b1e28ab0d38a50e6309906ee09375dd3aff6a9c09dd6e8f4"}, + {file = "charset_normalizer-3.4.6-cp39-cp39-musllinux_1_2_armv7l.whl", hash = "sha256:ee4ec14bc1680d6b0afab9aea2ef27e26d2024f18b24a2d7155a52b60da7e833"}, + {file = "charset_normalizer-3.4.6-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:d1a2ee9c1499fc8f86f4521f27a973c914b211ffa87322f4ee33bb35392da2c5"}, + {file = "charset_normalizer-3.4.6-cp39-cp39-musllinux_1_2_riscv64.whl", hash = "sha256:48696db7f18afb80a068821504296eb0787d9ce239b91ca15059d1d3eaacf13b"}, + {file = "charset_normalizer-3.4.6-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:4f41da960b196ea355357285ad1316a00099f22d0929fe168343b99b254729c9"}, + {file = "charset_normalizer-3.4.6-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:802168e03fba8bbc5ce0d866d589e4b1ca751d06edee69f7f3a19c5a9fe6b597"}, + {file = "charset_normalizer-3.4.6-cp39-cp39-win32.whl", hash = "sha256:8761ac29b6c81574724322a554605608a9960769ea83d2c73e396f3df896ad54"}, + {file = "charset_normalizer-3.4.6-cp39-cp39-win_amd64.whl", hash = "sha256:1cf0a70018692f85172348fe06d3a4b63f94ecb055e13a00c644d368eb82e5b8"}, + {file = "charset_normalizer-3.4.6-cp39-cp39-win_arm64.whl", hash = "sha256:3516bbb8d42169de9e61b8520cbeeeb716f12f4ecfe3fd30a9919aa16c806ca8"}, + {file = "charset_normalizer-3.4.6-py3-none-any.whl", hash = "sha256:947cf925bc916d90adba35a64c82aace04fa39b46b52d4630ece166655905a69"}, + {file = "charset_normalizer-3.4.6.tar.gz", hash = "sha256:1ae6b62897110aa7c79ea2f5dd38d1abca6db663687c0b1ad9aed6f6bae3d9d6"}, +] + +[[package]] +name = "click" +version = "8.3.1" +description = "Composable command line interface toolkit" +optional = true +python-versions = ">=3.10" +groups = ["main"] +markers = "extra == \"dev\"" +files = [ + {file = "click-8.3.1-py3-none-any.whl", hash = "sha256:981153a64e25f12d547d3426c367a4857371575ee7ad18df2a6183ab0545b2a6"}, + {file = "click-8.3.1.tar.gz", hash = "sha256:12ff4785d337a1bb490bb7e9c2b1ee5da3112e94a8622f26a6c77f5d2fc6842a"}, +] + +[package.dependencies] +colorama = {version = "*", markers = "platform_system == \"Windows\""} + +[[package]] +name = "colorama" +version = "0.4.6" +description = "Cross-platform colored terminal text." +optional = true +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" +groups = ["main"] +markers = "extra == \"dev\"" +files = [ + {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"}, + {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, +] + +[[package]] +name = "coverage" +version = "7.13.5" +description = "Code coverage measurement for Python" +optional = true +python-versions = ">=3.10" +groups = ["main"] +markers = "extra == \"dev\"" +files = [ + {file = "coverage-7.13.5-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e0723d2c96324561b9aa76fb982406e11d93cdb388a7a7da2b16e04719cf7ca5"}, + {file = "coverage-7.13.5-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:52f444e86475992506b32d4e5ca55c24fc88d73bcbda0e9745095b28ef4dc0cf"}, + {file = "coverage-7.13.5-cp310-cp310-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:704de6328e3d612a8f6c07000a878ff38181ec3263d5a11da1db294fa6a9bdf8"}, + {file = "coverage-7.13.5-cp310-cp310-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:a1a6d79a14e1ec1832cabc833898636ad5f3754a678ef8bb4908515208bf84f4"}, + {file = "coverage-7.13.5-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:79060214983769c7ba3f0cee10b54c97609dca4d478fa1aa32b914480fd5738d"}, + {file = "coverage-7.13.5-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:356e76b46783a98c2a2fe81ec79df4883a1e62895ea952968fb253c114e7f930"}, + {file = "coverage-7.13.5-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:0cef0cdec915d11254a7f549c1170afecce708d30610c6abdded1f74e581666d"}, + {file = "coverage-7.13.5-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:dc022073d063b25a402454e5712ef9e007113e3a676b96c5f29b2bda29352f40"}, + {file = "coverage-7.13.5-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:9b74db26dfea4f4e50d48a4602207cd1e78be33182bc9cbf22da94f332f99878"}, + {file = "coverage-7.13.5-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:ad146744ca4fd09b50c482650e3c1b1f4dfa1d4792e0a04a369c7f23336f0400"}, + {file = "coverage-7.13.5-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:c555b48be1853fe3997c11c4bd521cdd9a9612352de01fa4508f16ec341e6fe0"}, + {file = "coverage-7.13.5-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:7034b5c56a58ae5e85f23949d52c14aca2cfc6848a31764995b7de88f13a1ea0"}, + {file = "coverage-7.13.5-cp310-cp310-win32.whl", hash = "sha256:eb7fdf1ef130660e7415e0253a01a7d5a88c9c4d158bcf75cbbd922fd65a5b58"}, + {file = "coverage-7.13.5-cp310-cp310-win_amd64.whl", hash = "sha256:3e1bb5f6c78feeb1be3475789b14a0f0a5b47d505bfc7267126ccbd50289999e"}, + {file = "coverage-7.13.5-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:66a80c616f80181f4d643b0f9e709d97bcea413ecd9631e1dedc7401c8e6695d"}, + {file = "coverage-7.13.5-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:145ede53ccbafb297c1c9287f788d1bc3efd6c900da23bf6931b09eafc931587"}, + {file = "coverage-7.13.5-cp311-cp311-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:0672854dc733c342fa3e957e0605256d2bf5934feeac328da9e0b5449634a642"}, + {file = "coverage-7.13.5-cp311-cp311-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:ec10e2a42b41c923c2209b846126c6582db5e43a33157e9870ba9fb70dc7854b"}, + {file = "coverage-7.13.5-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:be3d4bbad9d4b037791794ddeedd7d64a56f5933a2c1373e18e9e568b9141686"}, + {file = "coverage-7.13.5-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:4d2afbc5cc54d286bfb54541aa50b64cdb07a718227168c87b9e2fb8f25e1743"}, + {file = "coverage-7.13.5-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:3ad050321264c49c2fa67bb599100456fc51d004b82534f379d16445da40fb75"}, + {file = "coverage-7.13.5-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:7300c8a6d13335b29bb76d7651c66af6bd8658517c43499f110ddc6717bfc209"}, + {file = "coverage-7.13.5-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:eb07647a5738b89baab047f14edd18ded523de60f3b30e75c2acc826f79c839a"}, + {file = "coverage-7.13.5-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:9adb6688e3b53adffefd4a52d72cbd8b02602bfb8f74dcd862337182fd4d1a4e"}, + {file = "coverage-7.13.5-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:7c8d4bc913dd70b93488d6c496c77f3aff5ea99a07e36a18f865bca55adef8bd"}, + {file = "coverage-7.13.5-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:0e3c426ffc4cd952f54ee9ffbdd10345709ecc78a3ecfd796a57236bfad0b9b8"}, + {file = "coverage-7.13.5-cp311-cp311-win32.whl", hash = "sha256:259b69bb83ad9894c4b25be2528139eecba9a82646ebdda2d9db1ba28424a6bf"}, + {file = "coverage-7.13.5-cp311-cp311-win_amd64.whl", hash = "sha256:258354455f4e86e3e9d0d17571d522e13b4e1e19bf0f8596bcf9476d61e7d8a9"}, + {file = "coverage-7.13.5-cp311-cp311-win_arm64.whl", hash = "sha256:bff95879c33ec8da99fc9b6fe345ddb5be6414b41d6d1ad1c8f188d26f36e028"}, + {file = "coverage-7.13.5-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:460cf0114c5016fa841214ff5564aa4864f11948da9440bc97e21ad1f4ba1e01"}, + {file = "coverage-7.13.5-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:0e223ce4b4ed47f065bfb123687686512e37629be25cc63728557ae7db261422"}, + {file = "coverage-7.13.5-cp312-cp312-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:6e3370441f4513c6252bf042b9c36d22491142385049243253c7e48398a15a9f"}, + {file = "coverage-7.13.5-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:03ccc709a17a1de074fb1d11f217342fb0d2b1582ed544f554fc9fc3f07e95f5"}, + {file = "coverage-7.13.5-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3f4818d065964db3c1c66dc0fbdac5ac692ecbc875555e13374fdbe7eedb4376"}, + {file = "coverage-7.13.5-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:012d5319e66e9d5a218834642d6c35d265515a62f01157a45bcc036ecf947256"}, + {file = "coverage-7.13.5-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:8dd02af98971bdb956363e4827d34425cb3df19ee550ef92855b0acb9c7ce51c"}, + {file = "coverage-7.13.5-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:f08fd75c50a760c7eb068ae823777268daaf16a80b918fa58eea888f8e3919f5"}, + {file = "coverage-7.13.5-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:843ea8643cf967d1ac7e8ecd4bb00c99135adf4816c0c0593fdcc47b597fcf09"}, + {file = "coverage-7.13.5-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:9d44d7aa963820b1b971dbecd90bfe5fe8f81cff79787eb6cca15750bd2f79b9"}, + {file = "coverage-7.13.5-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:7132bed4bd7b836200c591410ae7d97bf7ae8be6fc87d160b2bd881df929e7bf"}, + {file = "coverage-7.13.5-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:a698e363641b98843c517817db75373c83254781426e94ada3197cabbc2c919c"}, + {file = "coverage-7.13.5-cp312-cp312-win32.whl", hash = "sha256:bdba0a6b8812e8c7df002d908a9a2ea3c36e92611b5708633c50869e6d922fdf"}, + {file = "coverage-7.13.5-cp312-cp312-win_amd64.whl", hash = "sha256:d2c87e0c473a10bffe991502eac389220533024c8082ec1ce849f4218dded810"}, + {file = "coverage-7.13.5-cp312-cp312-win_arm64.whl", hash = "sha256:bf69236a9a81bdca3bff53796237aab096cdbf8d78a66ad61e992d9dac7eb2de"}, + {file = "coverage-7.13.5-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:5ec4af212df513e399cf11610cc27063f1586419e814755ab362e50a85ea69c1"}, + {file = "coverage-7.13.5-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:941617e518602e2d64942c88ec8499f7fbd49d3f6c4327d3a71d43a1973032f3"}, + {file = "coverage-7.13.5-cp313-cp313-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:da305e9937617ee95c2e39d8ff9f040e0487cbf1ac174f777ed5eddd7a7c1f26"}, + {file = "coverage-7.13.5-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:78e696e1cc714e57e8b25760b33a8b1026b7048d270140d25dafe1b0a1ee05a3"}, + {file = "coverage-7.13.5-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:02ca0eed225b2ff301c474aeeeae27d26e2537942aa0f87491d3e147e784a82b"}, + {file = "coverage-7.13.5-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:04690832cbea4e4663d9149e05dba142546ca05cb1848816760e7f58285c970a"}, + {file = "coverage-7.13.5-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:0590e44dd2745c696a778f7bab6aa95256de2cbc8b8cff4f7db8ff09813d6969"}, + {file = "coverage-7.13.5-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:d7cfad2d6d81dd298ab6b89fe72c3b7b05ec7544bdda3b707ddaecff8d25c161"}, + {file = "coverage-7.13.5-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:e092b9499de38ae0fbfbc603a74660eb6ff3e869e507b50d85a13b6db9863e15"}, + {file = "coverage-7.13.5-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:48c39bc4a04d983a54a705a6389512883d4a3b9862991b3617d547940e9f52b1"}, + {file = "coverage-7.13.5-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:2d3807015f138ffea1ed9afeeb8624fd781703f2858b62a8dd8da5a0994c57b6"}, + {file = "coverage-7.13.5-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:ee2aa19e03161671ec964004fb74b2257805d9710bf14a5c704558b9d8dbaf17"}, + {file = "coverage-7.13.5-cp313-cp313-win32.whl", hash = "sha256:ce1998c0483007608c8382f4ff50164bfc5bd07a2246dd272aa4043b75e61e85"}, + {file = "coverage-7.13.5-cp313-cp313-win_amd64.whl", hash = "sha256:631efb83f01569670a5e866ceb80fe483e7c159fac6f167e6571522636104a0b"}, + {file = "coverage-7.13.5-cp313-cp313-win_arm64.whl", hash = "sha256:f4cd16206ad171cbc2470dbea9103cf9a7607d5fe8c242fdf1edf36174020664"}, + {file = "coverage-7.13.5-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:0428cbef5783ad91fe240f673cc1f76b25e74bbfe1a13115e4aa30d3f538162d"}, + {file = "coverage-7.13.5-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:e0b216a19534b2427cc201a26c25da4a48633f29a487c61258643e89d28200c0"}, + {file = "coverage-7.13.5-cp313-cp313t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:972a9cd27894afe4bc2b1480107054e062df08e671df7c2f18c205e805ccd806"}, + {file = "coverage-7.13.5-cp313-cp313t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:4b59148601efcd2bac8c4dbf1f0ad6391693ccf7a74b8205781751637076aee3"}, + {file = "coverage-7.13.5-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:505d7083c8b0c87a8fa8c07370c285847c1f77739b22e299ad75a6af6c32c5c9"}, + {file = "coverage-7.13.5-cp313-cp313t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:60365289c3741e4db327e7baff2a4aaacf22f788e80fa4683393891b70a89fbd"}, + {file = "coverage-7.13.5-cp313-cp313t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:1b88c69c8ef5d4b6fe7dea66d6636056a0f6a7527c440e890cf9259011f5e606"}, + {file = "coverage-7.13.5-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:5b13955d31d1633cf9376908089b7cebe7d15ddad7aeaabcbe969a595a97e95e"}, + {file = "coverage-7.13.5-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:f70c9ab2595c56f81a89620e22899eea8b212a4041bd728ac6f4a28bf5d3ddd0"}, + {file = "coverage-7.13.5-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:084b84a8c63e8d6fc7e3931b316a9bcafca1458d753c539db82d31ed20091a87"}, + {file = "coverage-7.13.5-cp313-cp313t-musllinux_1_2_riscv64.whl", hash = "sha256:ad14385487393e386e2ea988b09d62dd42c397662ac2dabc3832d71253eee479"}, + {file = "coverage-7.13.5-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:7f2c47b36fe7709a6e83bfadf4eefb90bd25fbe4014d715224c4316f808e59a2"}, + {file = "coverage-7.13.5-cp313-cp313t-win32.whl", hash = "sha256:67e9bc5449801fad0e5dff329499fb090ba4c5800b86805c80617b4e29809b2a"}, + {file = "coverage-7.13.5-cp313-cp313t-win_amd64.whl", hash = "sha256:da86cdcf10d2519e10cabb8ac2de03da1bcb6e4853790b7fbd48523332e3a819"}, + {file = "coverage-7.13.5-cp313-cp313t-win_arm64.whl", hash = "sha256:0ecf12ecb326fe2c339d93fc131816f3a7367d223db37817208905c89bded911"}, + {file = "coverage-7.13.5-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:fbabfaceaeb587e16f7008f7795cd80d20ec548dc7f94fbb0d4ec2e038ce563f"}, + {file = "coverage-7.13.5-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:9bb2a28101a443669a423b665939381084412b81c3f8c0fcfbac57f4e30b5b8e"}, + {file = "coverage-7.13.5-cp314-cp314-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:bd3a2fbc1c6cccb3c5106140d87cc6a8715110373ef42b63cf5aea29df8c217a"}, + {file = "coverage-7.13.5-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:6c36ddb64ed9d7e496028d1d00dfec3e428e0aabf4006583bb1839958d280510"}, + {file = "coverage-7.13.5-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:380e8e9084d8eb38db3a9176a1a4f3c0082c3806fa0dc882d1d87abc3c789247"}, + {file = "coverage-7.13.5-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:e808af52a0513762df4d945ea164a24b37f2f518cbe97e03deaa0ee66139b4d6"}, + {file = "coverage-7.13.5-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:e301d30dd7e95ae068671d746ba8c34e945a82682e62918e41b2679acd2051a0"}, + {file = "coverage-7.13.5-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:800bc829053c80d240a687ceeb927a94fd108bbdc68dfbe505d0d75ab578a882"}, + {file = "coverage-7.13.5-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:0b67af5492adb31940ee418a5a655c28e48165da5afab8c7fa6fd72a142f8740"}, + {file = "coverage-7.13.5-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:c9136ff29c3a91e25b1d1552b5308e53a1e0653a23e53b6366d7c2dcbbaf8a16"}, + {file = "coverage-7.13.5-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:cff784eef7f0b8f6cb28804fbddcfa99f89efe4cc35fb5627e3ac58f91ed3ac0"}, + {file = "coverage-7.13.5-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:68a4953be99b17ac3c23b6efbc8a38330d99680c9458927491d18700ef23ded0"}, + {file = "coverage-7.13.5-cp314-cp314-win32.whl", hash = "sha256:35a31f2b1578185fbe6aa2e74cea1b1d0bbf4c552774247d9160d29b80ed56cc"}, + {file = "coverage-7.13.5-cp314-cp314-win_amd64.whl", hash = "sha256:2aa055ae1857258f9e0045be26a6d62bdb47a72448b62d7b55f4820f361a2633"}, + {file = "coverage-7.13.5-cp314-cp314-win_arm64.whl", hash = "sha256:1b11eef33edeae9d142f9b4358edb76273b3bfd30bc3df9a4f95d0e49caf94e8"}, + {file = "coverage-7.13.5-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:10a0c37f0b646eaff7cce1874c31d1f1ccb297688d4c747291f4f4c70741cc8b"}, + {file = "coverage-7.13.5-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:b5db73ba3c41c7008037fa731ad5459fc3944cb7452fc0aa9f822ad3533c583c"}, + {file = "coverage-7.13.5-cp314-cp314t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:750db93a81e3e5a9831b534be7b1229df848b2e125a604fe6651e48aa070e5f9"}, + {file = "coverage-7.13.5-cp314-cp314t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:9ddb4f4a5479f2539644be484da179b653273bca1a323947d48ab107b3ed1f29"}, + {file = "coverage-7.13.5-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d8a7a2049c14f413163e2bdabd37e41179b1d1ccb10ffc6ccc4b7a718429c607"}, + {file = "coverage-7.13.5-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:e1c85e0b6c05c592ea6d8768a66a254bfb3874b53774b12d4c89c481eb78cb90"}, + {file = "coverage-7.13.5-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:777c4d1eff1b67876139d24288aaf1817f6c03d6bae9c5cc8d27b83bcfe38fe3"}, + {file = "coverage-7.13.5-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:6697e29b93707167687543480a40f0db8f356e86d9f67ddf2e37e2dfd91a9dab"}, + {file = "coverage-7.13.5-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:8fdf453a942c3e4d99bd80088141c4c6960bb232c409d9c3558e2dbaa3998562"}, + {file = "coverage-7.13.5-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:32ca0c0114c9834a43f045a87dcebd69d108d8ffb666957ea65aa132f50332e2"}, + {file = "coverage-7.13.5-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:8769751c10f339021e2638cd354e13adeac54004d1941119b2c96fe5276d45ea"}, + {file = "coverage-7.13.5-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:cec2d83125531bd153175354055cdb7a09987af08a9430bd173c937c6d0fba2a"}, + {file = "coverage-7.13.5-cp314-cp314t-win32.whl", hash = "sha256:0cd9ed7a8b181775459296e402ca4fb27db1279740a24e93b3b41942ebe4b215"}, + {file = "coverage-7.13.5-cp314-cp314t-win_amd64.whl", hash = "sha256:301e3b7dfefecaca37c9f1aa6f0049b7d4ab8dd933742b607765d757aca77d43"}, + {file = "coverage-7.13.5-cp314-cp314t-win_arm64.whl", hash = "sha256:9dacc2ad679b292709e0f5fc1ac74a6d4d5562e424058962c7bb0c658ad25e45"}, + {file = "coverage-7.13.5-py3-none-any.whl", hash = "sha256:34b02417cf070e173989b3db962f7ed56d2f644307b2cf9d5a0f258e13084a61"}, + {file = "coverage-7.13.5.tar.gz", hash = "sha256:c81f6515c4c40141f83f502b07bbfa5c240ba25bbe73da7b33f1e5b6120ff179"}, +] + +[package.dependencies] +tomli = {version = "*", optional = true, markers = "python_full_version <= \"3.11.0a6\" and extra == \"toml\""} + +[package.extras] +toml = ["tomli ; python_full_version <= \"3.11.0a6\""] + +[[package]] +name = "distlib" +version = "0.4.0" +description = "Distribution utilities" +optional = true +python-versions = "*" +groups = ["main"] +markers = "extra == \"dev\"" +files = [ + {file = "distlib-0.4.0-py2.py3-none-any.whl", hash = "sha256:9659f7d87e46584a30b5780e43ac7a2143098441670ff0a49d5f9034c54a6c16"}, + {file = "distlib-0.4.0.tar.gz", hash = "sha256:feec40075be03a04501a973d81f633735b4b69f98b05450592310c0f401a4e0d"}, +] + +[[package]] +name = "exceptiongroup" +version = "1.3.1" +description = "Backport of PEP 654 (exception groups)" +optional = true +python-versions = ">=3.7" +groups = ["main"] +markers = "extra == \"dev\" and python_version == \"3.10\"" +files = [ + {file = "exceptiongroup-1.3.1-py3-none-any.whl", hash = "sha256:a7a39a3bd276781e98394987d3a5701d0c4edffb633bb7a5144577f82c773598"}, + {file = "exceptiongroup-1.3.1.tar.gz", hash = "sha256:8b412432c6055b0b7d14c310000ae93352ed6754f70fa8f7c34141f91c4e3219"}, +] + +[package.dependencies] +typing-extensions = {version = ">=4.6.0", markers = "python_version < \"3.13\""} + +[package.extras] +test = ["pytest (>=6)"] + +[[package]] +name = "filelock" +version = "3.25.2" +description = "A platform independent file lock." +optional = true +python-versions = ">=3.10" +groups = ["main"] +markers = "extra == \"dev\"" +files = [ + {file = "filelock-3.25.2-py3-none-any.whl", hash = "sha256:ca8afb0da15f229774c9ad1b455ed96e85a81373065fb10446672f64444ddf70"}, + {file = "filelock-3.25.2.tar.gz", hash = "sha256:b64ece2b38f4ca29dd3e810287aa8c48182bbecd1ae6e9ae126c9b35f1382694"}, +] + +[[package]] +name = "ghp-import" +version = "2.1.0" +description = "Copy your docs directly to the gh-pages branch." +optional = true +python-versions = "*" +groups = ["main"] +markers = "extra == \"dev\"" +files = [ + {file = "ghp-import-2.1.0.tar.gz", hash = "sha256:9c535c4c61193c2df8871222567d7fd7e5014d835f97dc7b7439069e2413d343"}, + {file = "ghp_import-2.1.0-py3-none-any.whl", hash = "sha256:8337dd7b50877f163d4c0289bc1f1c7f127550241988d568c1db512c4324a619"}, +] + +[package.dependencies] +python-dateutil = ">=2.8.1" + +[package.extras] +dev = ["flake8", "markdown", "twine", "wheel"] + +[[package]] +name = "griffelib" +version = "2.0.2" +description = "Signatures for entire Python programs. Extract the structure, the frame, the skeleton of your project, to generate API documentation or find breaking changes in your API." +optional = true +python-versions = ">=3.10" +groups = ["main"] +markers = "extra == \"dev\"" +files = [ + {file = "griffelib-2.0.2-py3-none-any.whl", hash = "sha256:925c857658fb1ba40c0772c37acbc2ab650bd794d9c1b9726922e36ea4117ea1"}, + {file = "griffelib-2.0.2.tar.gz", hash = "sha256:3cf20b3bc470e83763ffbf236e0076b1211bac1bc67de13daf494640f2de707e"}, +] + +[package.extras] +pypi = ["pip (>=24.0)", "platformdirs (>=4.2)", "wheel (>=0.42)"] + +[[package]] +name = "identify" +version = "2.6.18" +description = "File identification library for Python" +optional = true +python-versions = ">=3.10" +groups = ["main"] +markers = "extra == \"dev\"" +files = [ + {file = "identify-2.6.18-py2.py3-none-any.whl", hash = "sha256:8db9d3c8ea9079db92cafb0ebf97abdc09d52e97f4dcf773a2e694048b7cd737"}, + {file = "identify-2.6.18.tar.gz", hash = "sha256:873ac56a5e3fd63e7438a7ecbc4d91aca692eb3fefa4534db2b7913f3fc352fd"}, +] + +[package.extras] +license = ["ukkonen"] + +[[package]] +name = "idna" +version = "3.11" +description = "Internationalized Domain Names in Applications (IDNA)" +optional = true +python-versions = ">=3.8" +groups = ["main"] +markers = "extra == \"dev\"" +files = [ + {file = "idna-3.11-py3-none-any.whl", hash = "sha256:771a87f49d9defaf64091e6e6fe9c18d4833f140bd19464795bc32d966ca37ea"}, + {file = "idna-3.11.tar.gz", hash = "sha256:795dafcc9c04ed0c1fb032c2aa73654d8e8c5023a7df64a53f39190ada629902"}, +] + +[package.extras] +all = ["flake8 (>=7.1.1)", "mypy (>=1.11.2)", "pytest (>=8.3.2)", "ruff (>=0.6.2)"] + +[[package]] +name = "iniconfig" +version = "2.3.0" +description = "brain-dead simple config-ini parsing" +optional = true +python-versions = ">=3.10" +groups = ["main"] +markers = "extra == \"dev\"" +files = [ + {file = "iniconfig-2.3.0-py3-none-any.whl", hash = "sha256:f631c04d2c48c52b84d0d0549c99ff3859c98df65b3101406327ecc7d53fbf12"}, + {file = "iniconfig-2.3.0.tar.gz", hash = "sha256:c76315c77db068650d49c5b56314774a7804df16fee4402c1f19d6d15d8c4730"}, +] + +[[package]] +name = "jinja2" +version = "3.1.6" +description = "A very fast and expressive template engine." +optional = true +python-versions = ">=3.7" +groups = ["main"] +markers = "extra == \"dev\"" +files = [ + {file = "jinja2-3.1.6-py3-none-any.whl", hash = "sha256:85ece4451f492d0c13c5dd7c13a64681a86afae63a5f347908daf103ce6d2f67"}, + {file = "jinja2-3.1.6.tar.gz", hash = "sha256:0137fb05990d35f1275a587e9aee6d56da821fc83491a0fb838183be43f66d6d"}, +] + +[package.dependencies] +MarkupSafe = ">=2.0" + +[package.extras] +i18n = ["Babel (>=2.7)"] + +[[package]] +name = "librt" +version = "0.8.1" +description = "Mypyc runtime library" +optional = true +python-versions = ">=3.9" +groups = ["main"] +markers = "extra == \"dev\" and platform_python_implementation != \"PyPy\"" +files = [ + {file = "librt-0.8.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:81fd938344fecb9373ba1b155968c8a329491d2ce38e7ddb76f30ffb938f12dc"}, + {file = "librt-0.8.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:5db05697c82b3a2ec53f6e72b2ed373132b0c2e05135f0696784e97d7f5d48e7"}, + {file = "librt-0.8.1-cp310-cp310-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:d56bc4011975f7460bea7b33e1ff425d2f1adf419935ff6707273c77f8a4ada6"}, + {file = "librt-0.8.1-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5cdc0f588ff4b663ea96c26d2a230c525c6fc62b28314edaaaca8ed5af931ad0"}, + {file = "librt-0.8.1-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:97c2b54ff6717a7a563b72627990bec60d8029df17df423f0ed37d56a17a176b"}, + {file = "librt-0.8.1-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:8f1125e6bbf2f1657d9a2f3ccc4a2c9b0c8b176965bb565dd4d86be67eddb4b6"}, + {file = "librt-0.8.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:8f4bb453f408137d7581be309b2fbc6868a80e7ef60c88e689078ee3a296ae71"}, + {file = "librt-0.8.1-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:c336d61d2fe74a3195edc1646d53ff1cddd3a9600b09fa6ab75e5514ba4862a7"}, + {file = "librt-0.8.1-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:eb5656019db7c4deacf0c1a55a898c5bb8f989be904597fcb5232a2f4828fa05"}, + {file = "librt-0.8.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:c25d9e338d5bed46c1632f851babf3d13c78f49a225462017cf5e11e845c5891"}, + {file = "librt-0.8.1-cp310-cp310-win32.whl", hash = "sha256:aaab0e307e344cb28d800957ef3ec16605146ef0e59e059a60a176d19543d1b7"}, + {file = "librt-0.8.1-cp310-cp310-win_amd64.whl", hash = "sha256:56e04c14b696300d47b3bc5f1d10a00e86ae978886d0cee14e5714fafb5df5d2"}, + {file = "librt-0.8.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:681dc2451d6d846794a828c16c22dc452d924e9f700a485b7ecb887a30aad1fd"}, + {file = "librt-0.8.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:a3b4350b13cc0e6f5bec8fa7caf29a8fb8cdc051a3bae45cfbfd7ce64f009965"}, + {file = "librt-0.8.1-cp311-cp311-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:ac1e7817fd0ed3d14fd7c5df91daed84c48e4c2a11ee99c0547f9f62fdae13da"}, + {file = "librt-0.8.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:747328be0c5b7075cde86a0e09d7a9196029800ba75a1689332348e998fb85c0"}, + {file = "librt-0.8.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f0af2bd2bc204fa27f3d6711d0f360e6b8c684a035206257a81673ab924aa11e"}, + {file = "librt-0.8.1-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:d480de377f5b687b6b1bc0c0407426da556e2a757633cc7e4d2e1a057aa688f3"}, + {file = "librt-0.8.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:d0ee06b5b5291f609ddb37b9750985b27bc567791bc87c76a569b3feed8481ac"}, + {file = "librt-0.8.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:9e2c6f77b9ad48ce5603b83b7da9ee3e36b3ab425353f695cba13200c5d96596"}, + {file = "librt-0.8.1-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:439352ba9373f11cb8e1933da194dcc6206daf779ff8df0ed69c5e39113e6a99"}, + {file = "librt-0.8.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:82210adabbc331dbb65d7868b105185464ef13f56f7f76688565ad79f648b0fe"}, + {file = "librt-0.8.1-cp311-cp311-win32.whl", hash = "sha256:52c224e14614b750c0a6d97368e16804a98c684657c7518752c356834fff83bb"}, + {file = "librt-0.8.1-cp311-cp311-win_amd64.whl", hash = "sha256:c00e5c884f528c9932d278d5c9cbbea38a6b81eb62c02e06ae53751a83a4d52b"}, + {file = "librt-0.8.1-cp311-cp311-win_arm64.whl", hash = "sha256:f7cdf7f26c2286ffb02e46d7bac56c94655540b26347673bea15fa52a6af17e9"}, + {file = "librt-0.8.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:a28f2612ab566b17f3698b0da021ff9960610301607c9a5e8eaca62f5e1c350a"}, + {file = "librt-0.8.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:60a78b694c9aee2a0f1aaeaa7d101cf713e92e8423a941d2897f4fa37908dab9"}, + {file = "librt-0.8.1-cp312-cp312-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:758509ea3f1eba2a57558e7e98f4659d0ea7670bff49673b0dde18a3c7e6c0eb"}, + {file = "librt-0.8.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:039b9f2c506bd0ab0f8725aa5ba339c6f0cd19d3b514b50d134789809c24285d"}, + {file = "librt-0.8.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5bb54f1205a3a6ab41a6fd71dfcdcbd278670d3a90ca502a30d9da583105b6f7"}, + {file = "librt-0.8.1-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:05bd41cdee35b0c59c259f870f6da532a2c5ca57db95b5f23689fcb5c9e42440"}, + {file = "librt-0.8.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:adfab487facf03f0d0857b8710cf82d0704a309d8ffc33b03d9302b4c64e91a9"}, + {file = "librt-0.8.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:153188fe98a72f206042be10a2c6026139852805215ed9539186312d50a8e972"}, + {file = "librt-0.8.1-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:dd3c41254ee98604b08bd5b3af5bf0a89740d4ee0711de95b65166bf44091921"}, + {file = "librt-0.8.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:e0d138c7ae532908cbb342162b2611dbd4d90c941cd25ab82084aaf71d2c0bd0"}, + {file = "librt-0.8.1-cp312-cp312-win32.whl", hash = "sha256:43353b943613c5d9c49a25aaffdba46f888ec354e71e3529a00cca3f04d66a7a"}, + {file = "librt-0.8.1-cp312-cp312-win_amd64.whl", hash = "sha256:ff8baf1f8d3f4b6b7257fcb75a501f2a5499d0dda57645baa09d4d0d34b19444"}, + {file = "librt-0.8.1-cp312-cp312-win_arm64.whl", hash = "sha256:0f2ae3725904f7377e11cc37722d5d401e8b3d5851fb9273d7f4fe04f6b3d37d"}, + {file = "librt-0.8.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:7e6bad1cd94f6764e1e21950542f818a09316645337fd5ab9a7acc45d99a8f35"}, + {file = "librt-0.8.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:cf450f498c30af55551ba4f66b9123b7185362ec8b625a773b3d39aa1a717583"}, + {file = "librt-0.8.1-cp313-cp313-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:eca45e982fa074090057132e30585a7e8674e9e885d402eae85633e9f449ce6c"}, + {file = "librt-0.8.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0c3811485fccfda840861905b8c70bba5ec094e02825598bb9d4ca3936857a04"}, + {file = "librt-0.8.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5e4af413908f77294605e28cfd98063f54b2c790561383971d2f52d113d9c363"}, + {file = "librt-0.8.1-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:5212a5bd7fae98dae95710032902edcd2ec4dc994e883294f75c857b83f9aba0"}, + {file = "librt-0.8.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:e692aa2d1d604e6ca12d35e51fdc36f4cda6345e28e36374579f7ef3611b3012"}, + {file = "librt-0.8.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:4be2a5c926b9770c9e08e717f05737a269b9d0ebc5d2f0060f0fe3fe9ce47acb"}, + {file = "librt-0.8.1-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:fd1a720332ea335ceb544cf0a03f81df92abd4bb887679fd1e460976b0e6214b"}, + {file = "librt-0.8.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:93c2af9e01e0ef80d95ae3c720be101227edae5f2fe7e3dc63d8857fadfc5a1d"}, + {file = "librt-0.8.1-cp313-cp313-win32.whl", hash = "sha256:086a32dbb71336627e78cc1d6ee305a68d038ef7d4c39aaff41ae8c9aa46e91a"}, + {file = "librt-0.8.1-cp313-cp313-win_amd64.whl", hash = "sha256:e11769a1dbda4da7b00a76cfffa67aa47cfa66921d2724539eee4b9ede780b79"}, + {file = "librt-0.8.1-cp313-cp313-win_arm64.whl", hash = "sha256:924817ab3141aca17893386ee13261f1d100d1ef410d70afe4389f2359fea4f0"}, + {file = "librt-0.8.1-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:6cfa7fe54fd4d1f47130017351a959fe5804bda7a0bc7e07a2cdbc3fdd28d34f"}, + {file = "librt-0.8.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:228c2409c079f8c11fb2e5d7b277077f694cb93443eb760e00b3b83cb8b3176c"}, + {file = "librt-0.8.1-cp314-cp314-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:7aae78ab5e3206181780e56912d1b9bb9f90a7249ce12f0e8bf531d0462dd0fc"}, + {file = "librt-0.8.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:172d57ec04346b047ca6af181e1ea4858086c80bdf455f61994c4aa6fc3f866c"}, + {file = "librt-0.8.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6b1977c4ea97ce5eb7755a78fae68d87e4102e4aaf54985e8b56806849cc06a3"}, + {file = "librt-0.8.1-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:10c42e1f6fd06733ef65ae7bebce2872bcafd8d6e6b0a08fe0a05a23b044fb14"}, + {file = "librt-0.8.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:4c8dfa264b9193c4ee19113c985c95f876fae5e51f731494fc4e0cf594990ba7"}, + {file = "librt-0.8.1-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:01170b6729a438f0dedc4a26ed342e3dc4f02d1000b4b19f980e1877f0c297e6"}, + {file = "librt-0.8.1-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:7b02679a0d783bdae30d443025b94465d8c3dc512f32f5b5031f93f57ac32071"}, + {file = "librt-0.8.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:190b109bb69592a3401fe1ffdea41a2e73370ace2ffdc4a0e8e2b39cdea81b78"}, + {file = "librt-0.8.1-cp314-cp314-win32.whl", hash = "sha256:e70a57ecf89a0f64c24e37f38d3fe217a58169d2fe6ed6d70554964042474023"}, + {file = "librt-0.8.1-cp314-cp314-win_amd64.whl", hash = "sha256:7e2f3edca35664499fbb36e4770650c4bd4a08abc1f4458eab9df4ec56389730"}, + {file = "librt-0.8.1-cp314-cp314-win_arm64.whl", hash = "sha256:0d2f82168e55ddefd27c01c654ce52379c0750ddc31ee86b4b266bcf4d65f2a3"}, + {file = "librt-0.8.1-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:2c74a2da57a094bd48d03fa5d196da83d2815678385d2978657499063709abe1"}, + {file = "librt-0.8.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:a355d99c4c0d8e5b770313b8b247411ed40949ca44e33e46a4789b9293a907ee"}, + {file = "librt-0.8.1-cp314-cp314t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:2eb345e8b33fb748227409c9f1233d4df354d6e54091f0e8fc53acdb2ffedeb7"}, + {file = "librt-0.8.1-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9be2f15e53ce4e83cc08adc29b26fb5978db62ef2a366fbdf716c8a6c8901040"}, + {file = "librt-0.8.1-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:785ae29c1f5c6e7c2cde2c7c0e148147f4503da3abc5d44d482068da5322fd9e"}, + {file = "librt-0.8.1-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:1d3a7da44baf692f0c6aeb5b2a09c5e6fc7a703bca9ffa337ddd2e2da53f7732"}, + {file = "librt-0.8.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:5fc48998000cbc39ec0d5311312dda93ecf92b39aaf184c5e817d5d440b29624"}, + {file = "librt-0.8.1-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:e96baa6820280077a78244b2e06e416480ed859bbd8e5d641cf5742919d8beb4"}, + {file = "librt-0.8.1-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:31362dbfe297b23590530007062c32c6f6176f6099646bb2c95ab1b00a57c382"}, + {file = "librt-0.8.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:cc3656283d11540ab0ea01978378e73e10002145117055e03722417aeab30994"}, + {file = "librt-0.8.1-cp314-cp314t-win32.whl", hash = "sha256:738f08021b3142c2918c03692608baed43bc51144c29e35807682f8070ee2a3a"}, + {file = "librt-0.8.1-cp314-cp314t-win_amd64.whl", hash = "sha256:89815a22daf9c51884fb5dbe4f1ef65ee6a146e0b6a8df05f753e2e4a9359bf4"}, + {file = "librt-0.8.1-cp314-cp314t-win_arm64.whl", hash = "sha256:bf512a71a23504ed08103a13c941f763db13fb11177beb3d9244c98c29fb4a61"}, + {file = "librt-0.8.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:3dff3d3ca8db20e783b1bc7de49c0a2ab0b8387f31236d6a026597d07fcd68ac"}, + {file = "librt-0.8.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:08eec3a1fc435f0d09c87b6bf1ec798986a3544f446b864e4099633a56fcd9ed"}, + {file = "librt-0.8.1-cp39-cp39-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:e3f0a41487fd5fad7e760b9e8a90e251e27c2816fbc2cff36a22a0e6bcbbd9dd"}, + {file = "librt-0.8.1-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:bacdb58d9939d95cc557b4dbaa86527c9db2ac1ed76a18bc8d26f6dc8647d851"}, + {file = "librt-0.8.1-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b6d7ab1f01aa753188605b09a51faa44a3327400b00b8cce424c71910fc0a128"}, + {file = "librt-0.8.1-cp39-cp39-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:4998009e7cb9e896569f4be7004f09d0ed70d386fa99d42b6d363f6d200501ac"}, + {file = "librt-0.8.1-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:2cc68eeeef5e906839c7bb0815748b5b0a974ec27125beefc0f942715785b551"}, + {file = "librt-0.8.1-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:0bf69d79a23f4f40b8673a947a234baeeb133b5078b483b7297c5916539cf5d5"}, + {file = "librt-0.8.1-cp39-cp39-musllinux_1_2_riscv64.whl", hash = "sha256:22b46eabd76c1986ee7d231b0765ad387d7673bbd996aa0d0d054b38ac65d8f6"}, + {file = "librt-0.8.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:237796479f4d0637d6b9cbcb926ff424a97735e68ade6facf402df4ec93375ed"}, + {file = "librt-0.8.1-cp39-cp39-win32.whl", hash = "sha256:4beb04b8c66c6ae62f8c1e0b2f097c1ebad9295c929a8d5286c05eae7c2fc7dc"}, + {file = "librt-0.8.1-cp39-cp39-win_amd64.whl", hash = "sha256:64548cde61b692dc0dc379f4b5f59a2f582c2ebe7890d09c1ae3b9e66fa015b7"}, + {file = "librt-0.8.1.tar.gz", hash = "sha256:be46a14693955b3bd96014ccbdb8339ee8c9346fbe11c1b78901b55125f14c73"}, +] + +[[package]] +name = "markdown" +version = "3.10.2" +description = "Python implementation of John Gruber's Markdown." +optional = true +python-versions = ">=3.10" +groups = ["main"] +markers = "extra == \"dev\"" +files = [ + {file = "markdown-3.10.2-py3-none-any.whl", hash = "sha256:e91464b71ae3ee7afd3017d9f358ef0baf158fd9a298db92f1d4761133824c36"}, + {file = "markdown-3.10.2.tar.gz", hash = "sha256:994d51325d25ad8aa7ce4ebaec003febcce822c3f8c911e3b17c52f7f589f950"}, +] + +[package.extras] +docs = ["mdx_gh_links (>=0.2)", "mkdocs (>=1.6)", "mkdocs-gen-files", "mkdocs-literate-nav", "mkdocs-nature (>=0.6)", "mkdocs-section-index", "mkdocstrings[python] (>=0.28.3)"] +testing = ["coverage", "pyyaml"] + +[[package]] +name = "markupsafe" +version = "3.0.3" +description = "Safely add untrusted strings to HTML/XML markup." +optional = true +python-versions = ">=3.9" +groups = ["main"] +markers = "extra == \"dev\"" +files = [ + {file = "markupsafe-3.0.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:2f981d352f04553a7171b8e44369f2af4055f888dfb147d55e42d29e29e74559"}, + {file = "markupsafe-3.0.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:e1c1493fb6e50ab01d20a22826e57520f1284df32f2d8601fdd90b6304601419"}, + {file = "markupsafe-3.0.3-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1ba88449deb3de88bd40044603fafffb7bc2b055d626a330323a9ed736661695"}, + {file = "markupsafe-3.0.3-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f42d0984e947b8adf7dd6dde396e720934d12c506ce84eea8476409563607591"}, + {file = "markupsafe-3.0.3-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:c0c0b3ade1c0b13b936d7970b1d37a57acde9199dc2aecc4c336773e1d86049c"}, + {file = "markupsafe-3.0.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:0303439a41979d9e74d18ff5e2dd8c43ed6c6001fd40e5bf2e43f7bd9bbc523f"}, + {file = "markupsafe-3.0.3-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:d2ee202e79d8ed691ceebae8e0486bd9a2cd4794cec4824e1c99b6f5009502f6"}, + {file = "markupsafe-3.0.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:177b5253b2834fe3678cb4a5f0059808258584c559193998be2601324fdeafb1"}, + {file = "markupsafe-3.0.3-cp310-cp310-win32.whl", hash = "sha256:2a15a08b17dd94c53a1da0438822d70ebcd13f8c3a95abe3a9ef9f11a94830aa"}, + {file = "markupsafe-3.0.3-cp310-cp310-win_amd64.whl", hash = "sha256:c4ffb7ebf07cfe8931028e3e4c85f0357459a3f9f9490886198848f4fa002ec8"}, + {file = "markupsafe-3.0.3-cp310-cp310-win_arm64.whl", hash = "sha256:e2103a929dfa2fcaf9bb4e7c091983a49c9ac3b19c9061b6d5427dd7d14d81a1"}, + {file = "markupsafe-3.0.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:1cc7ea17a6824959616c525620e387f6dd30fec8cb44f649e31712db02123dad"}, + {file = "markupsafe-3.0.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:4bd4cd07944443f5a265608cc6aab442e4f74dff8088b0dfc8238647b8f6ae9a"}, + {file = "markupsafe-3.0.3-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6b5420a1d9450023228968e7e6a9ce57f65d148ab56d2313fcd589eee96a7a50"}, + {file = "markupsafe-3.0.3-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0bf2a864d67e76e5c9a34dc26ec616a66b9888e25e7b9460e1c76d3293bd9dbf"}, + {file = "markupsafe-3.0.3-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:bc51efed119bc9cfdf792cdeaa4d67e8f6fcccab66ed4bfdd6bde3e59bfcbb2f"}, + {file = "markupsafe-3.0.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:068f375c472b3e7acbe2d5318dea141359e6900156b5b2ba06a30b169086b91a"}, + {file = "markupsafe-3.0.3-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:7be7b61bb172e1ed687f1754f8e7484f1c8019780f6f6b0786e76bb01c2ae115"}, + {file = "markupsafe-3.0.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:f9e130248f4462aaa8e2552d547f36ddadbeaa573879158d721bbd33dfe4743a"}, + {file = "markupsafe-3.0.3-cp311-cp311-win32.whl", hash = "sha256:0db14f5dafddbb6d9208827849fad01f1a2609380add406671a26386cdf15a19"}, + {file = "markupsafe-3.0.3-cp311-cp311-win_amd64.whl", hash = "sha256:de8a88e63464af587c950061a5e6a67d3632e36df62b986892331d4620a35c01"}, + {file = "markupsafe-3.0.3-cp311-cp311-win_arm64.whl", hash = "sha256:3b562dd9e9ea93f13d53989d23a7e775fdfd1066c33494ff43f5418bc8c58a5c"}, + {file = "markupsafe-3.0.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:d53197da72cc091b024dd97249dfc7794d6a56530370992a5e1a08983ad9230e"}, + {file = "markupsafe-3.0.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:1872df69a4de6aead3491198eaf13810b565bdbeec3ae2dc8780f14458ec73ce"}, + {file = "markupsafe-3.0.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3a7e8ae81ae39e62a41ec302f972ba6ae23a5c5396c8e60113e9066ef893da0d"}, + {file = "markupsafe-3.0.3-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d6dd0be5b5b189d31db7cda48b91d7e0a9795f31430b7f271219ab30f1d3ac9d"}, + {file = "markupsafe-3.0.3-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:94c6f0bb423f739146aec64595853541634bde58b2135f27f61c1ffd1cd4d16a"}, + {file = "markupsafe-3.0.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:be8813b57049a7dc738189df53d69395eba14fb99345e0a5994914a3864c8a4b"}, + {file = "markupsafe-3.0.3-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:83891d0e9fb81a825d9a6d61e3f07550ca70a076484292a70fde82c4b807286f"}, + {file = "markupsafe-3.0.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:77f0643abe7495da77fb436f50f8dab76dbc6e5fd25d39589a0f1fe6548bfa2b"}, + {file = "markupsafe-3.0.3-cp312-cp312-win32.whl", hash = "sha256:d88b440e37a16e651bda4c7c2b930eb586fd15ca7406cb39e211fcff3bf3017d"}, + {file = "markupsafe-3.0.3-cp312-cp312-win_amd64.whl", hash = "sha256:26a5784ded40c9e318cfc2bdb30fe164bdb8665ded9cd64d500a34fb42067b1c"}, + {file = "markupsafe-3.0.3-cp312-cp312-win_arm64.whl", hash = "sha256:35add3b638a5d900e807944a078b51922212fb3dedb01633a8defc4b01a3c85f"}, + {file = "markupsafe-3.0.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:e1cf1972137e83c5d4c136c43ced9ac51d0e124706ee1c8aa8532c1287fa8795"}, + {file = "markupsafe-3.0.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:116bb52f642a37c115f517494ea5feb03889e04df47eeff5b130b1808ce7c219"}, + {file = "markupsafe-3.0.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:133a43e73a802c5562be9bbcd03d090aa5a1fe899db609c29e8c8d815c5f6de6"}, + {file = "markupsafe-3.0.3-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ccfcd093f13f0f0b7fdd0f198b90053bf7b2f02a3927a30e63f3ccc9df56b676"}, + {file = "markupsafe-3.0.3-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:509fa21c6deb7a7a273d629cf5ec029bc209d1a51178615ddf718f5918992ab9"}, + {file = "markupsafe-3.0.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:a4afe79fb3de0b7097d81da19090f4df4f8d3a2b3adaa8764138aac2e44f3af1"}, + {file = "markupsafe-3.0.3-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:795e7751525cae078558e679d646ae45574b47ed6e7771863fcc079a6171a0fc"}, + {file = "markupsafe-3.0.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:8485f406a96febb5140bfeca44a73e3ce5116b2501ac54fe953e488fb1d03b12"}, + {file = "markupsafe-3.0.3-cp313-cp313-win32.whl", hash = "sha256:bdd37121970bfd8be76c5fb069c7751683bdf373db1ed6c010162b2a130248ed"}, + {file = "markupsafe-3.0.3-cp313-cp313-win_amd64.whl", hash = "sha256:9a1abfdc021a164803f4d485104931fb8f8c1efd55bc6b748d2f5774e78b62c5"}, + {file = "markupsafe-3.0.3-cp313-cp313-win_arm64.whl", hash = "sha256:7e68f88e5b8799aa49c85cd116c932a1ac15caaa3f5db09087854d218359e485"}, + {file = "markupsafe-3.0.3-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:218551f6df4868a8d527e3062d0fb968682fe92054e89978594c28e642c43a73"}, + {file = "markupsafe-3.0.3-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:3524b778fe5cfb3452a09d31e7b5adefeea8c5be1d43c4f810ba09f2ceb29d37"}, + {file = "markupsafe-3.0.3-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4e885a3d1efa2eadc93c894a21770e4bc67899e3543680313b09f139e149ab19"}, + {file = "markupsafe-3.0.3-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8709b08f4a89aa7586de0aadc8da56180242ee0ada3999749b183aa23df95025"}, + {file = "markupsafe-3.0.3-cp313-cp313t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:b8512a91625c9b3da6f127803b166b629725e68af71f8184ae7e7d54686a56d6"}, + {file = "markupsafe-3.0.3-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:9b79b7a16f7fedff2495d684f2b59b0457c3b493778c9eed31111be64d58279f"}, + {file = "markupsafe-3.0.3-cp313-cp313t-musllinux_1_2_riscv64.whl", hash = "sha256:12c63dfb4a98206f045aa9563db46507995f7ef6d83b2f68eda65c307c6829eb"}, + {file = "markupsafe-3.0.3-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:8f71bc33915be5186016f675cd83a1e08523649b0e33efdb898db577ef5bb009"}, + {file = "markupsafe-3.0.3-cp313-cp313t-win32.whl", hash = "sha256:69c0b73548bc525c8cb9a251cddf1931d1db4d2258e9599c28c07ef3580ef354"}, + {file = "markupsafe-3.0.3-cp313-cp313t-win_amd64.whl", hash = "sha256:1b4b79e8ebf6b55351f0d91fe80f893b4743f104bff22e90697db1590e47a218"}, + {file = "markupsafe-3.0.3-cp313-cp313t-win_arm64.whl", hash = "sha256:ad2cf8aa28b8c020ab2fc8287b0f823d0a7d8630784c31e9ee5edea20f406287"}, + {file = "markupsafe-3.0.3-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:eaa9599de571d72e2daf60164784109f19978b327a3910d3e9de8c97b5b70cfe"}, + {file = "markupsafe-3.0.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:c47a551199eb8eb2121d4f0f15ae0f923d31350ab9280078d1e5f12b249e0026"}, + {file = "markupsafe-3.0.3-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f34c41761022dd093b4b6896d4810782ffbabe30f2d443ff5f083e0cbbb8c737"}, + {file = "markupsafe-3.0.3-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:457a69a9577064c05a97c41f4e65148652db078a3a509039e64d3467b9e7ef97"}, + {file = "markupsafe-3.0.3-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:e8afc3f2ccfa24215f8cb28dcf43f0113ac3c37c2f0f0806d8c70e4228c5cf4d"}, + {file = "markupsafe-3.0.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:ec15a59cf5af7be74194f7ab02d0f59a62bdcf1a537677ce67a2537c9b87fcda"}, + {file = "markupsafe-3.0.3-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:0eb9ff8191e8498cca014656ae6b8d61f39da5f95b488805da4bb029cccbfbaf"}, + {file = "markupsafe-3.0.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:2713baf880df847f2bece4230d4d094280f4e67b1e813eec43b4c0e144a34ffe"}, + {file = "markupsafe-3.0.3-cp314-cp314-win32.whl", hash = "sha256:729586769a26dbceff69f7a7dbbf59ab6572b99d94576a5592625d5b411576b9"}, + {file = "markupsafe-3.0.3-cp314-cp314-win_amd64.whl", hash = "sha256:bdc919ead48f234740ad807933cdf545180bfbe9342c2bb451556db2ed958581"}, + {file = "markupsafe-3.0.3-cp314-cp314-win_arm64.whl", hash = "sha256:5a7d5dc5140555cf21a6fefbdbf8723f06fcd2f63ef108f2854de715e4422cb4"}, + {file = "markupsafe-3.0.3-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:1353ef0c1b138e1907ae78e2f6c63ff67501122006b0f9abad68fda5f4ffc6ab"}, + {file = "markupsafe-3.0.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:1085e7fbddd3be5f89cc898938f42c0b3c711fdcb37d75221de2666af647c175"}, + {file = "markupsafe-3.0.3-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1b52b4fb9df4eb9ae465f8d0c228a00624de2334f216f178a995ccdcf82c4634"}, + {file = "markupsafe-3.0.3-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:fed51ac40f757d41b7c48425901843666a6677e3e8eb0abcff09e4ba6e664f50"}, + {file = "markupsafe-3.0.3-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:f190daf01f13c72eac4efd5c430a8de82489d9cff23c364c3ea822545032993e"}, + {file = "markupsafe-3.0.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:e56b7d45a839a697b5eb268c82a71bd8c7f6c94d6fd50c3d577fa39a9f1409f5"}, + {file = "markupsafe-3.0.3-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:f3e98bb3798ead92273dc0e5fd0f31ade220f59a266ffd8a4f6065e0a3ce0523"}, + {file = "markupsafe-3.0.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:5678211cb9333a6468fb8d8be0305520aa073f50d17f089b5b4b477ea6e67fdc"}, + {file = "markupsafe-3.0.3-cp314-cp314t-win32.whl", hash = "sha256:915c04ba3851909ce68ccc2b8e2cd691618c4dc4c4232fb7982bca3f41fd8c3d"}, + {file = "markupsafe-3.0.3-cp314-cp314t-win_amd64.whl", hash = "sha256:4faffd047e07c38848ce017e8725090413cd80cbc23d86e55c587bf979e579c9"}, + {file = "markupsafe-3.0.3-cp314-cp314t-win_arm64.whl", hash = "sha256:32001d6a8fc98c8cb5c947787c5d08b0a50663d139f1305bac5885d98d9b40fa"}, + {file = "markupsafe-3.0.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:15d939a21d546304880945ca1ecb8a039db6b4dc49b2c5a400387cdae6a62e26"}, + {file = "markupsafe-3.0.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:f71a396b3bf33ecaa1626c255855702aca4d3d9fea5e051b41ac59a9c1c41edc"}, + {file = "markupsafe-3.0.3-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0f4b68347f8c5eab4a13419215bdfd7f8c9b19f2b25520968adfad23eb0ce60c"}, + {file = "markupsafe-3.0.3-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e8fc20152abba6b83724d7ff268c249fa196d8259ff481f3b1476383f8f24e42"}, + {file = "markupsafe-3.0.3-cp39-cp39-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:949b8d66bc381ee8b007cd945914c721d9aba8e27f71959d750a46f7c282b20b"}, + {file = "markupsafe-3.0.3-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:3537e01efc9d4dccdf77221fb1cb3b8e1a38d5428920e0657ce299b20324d758"}, + {file = "markupsafe-3.0.3-cp39-cp39-musllinux_1_2_riscv64.whl", hash = "sha256:591ae9f2a647529ca990bc681daebdd52c8791ff06c2bfa05b65163e28102ef2"}, + {file = "markupsafe-3.0.3-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:a320721ab5a1aba0a233739394eb907f8c8da5c98c9181d1161e77a0c8e36f2d"}, + {file = "markupsafe-3.0.3-cp39-cp39-win32.whl", hash = "sha256:df2449253ef108a379b8b5d6b43f4b1a8e81a061d6537becd5582fba5f9196d7"}, + {file = "markupsafe-3.0.3-cp39-cp39-win_amd64.whl", hash = "sha256:7c3fb7d25180895632e5d3148dbdc29ea38ccb7fd210aa27acbd1201a1902c6e"}, + {file = "markupsafe-3.0.3-cp39-cp39-win_arm64.whl", hash = "sha256:38664109c14ffc9e7437e86b4dceb442b0096dfe3541d7864d9cbe1da4cf36c8"}, + {file = "markupsafe-3.0.3.tar.gz", hash = "sha256:722695808f4b6457b320fdc131280796bdceb04ab50fe1795cd540799ebe1698"}, +] + +[[package]] +name = "mergedeep" +version = "1.3.4" +description = "A deep merge function for 🐍." +optional = true +python-versions = ">=3.6" +groups = ["main"] +markers = "extra == \"dev\"" +files = [ + {file = "mergedeep-1.3.4-py3-none-any.whl", hash = "sha256:70775750742b25c0d8f36c55aed03d24c3384d17c951b3175d898bd778ef0307"}, + {file = "mergedeep-1.3.4.tar.gz", hash = "sha256:0096d52e9dad9939c3d975a774666af186eda617e6ca84df4c94dec30004f2a8"}, +] + +[[package]] +name = "mkdocs" +version = "1.6.1" +description = "Project documentation with Markdown." +optional = true +python-versions = ">=3.8" +groups = ["main"] +markers = "extra == \"dev\"" +files = [ + {file = "mkdocs-1.6.1-py3-none-any.whl", hash = "sha256:db91759624d1647f3f34aa0c3f327dd2601beae39a366d6e064c03468d35c20e"}, + {file = "mkdocs-1.6.1.tar.gz", hash = "sha256:7b432f01d928c084353ab39c57282f29f92136665bdd6abf7c1ec8d822ef86f2"}, +] + +[package.dependencies] +click = ">=7.0" +colorama = {version = ">=0.4", markers = "platform_system == \"Windows\""} +ghp-import = ">=1.0" +jinja2 = ">=2.11.1" +markdown = ">=3.3.6" +markupsafe = ">=2.0.1" +mergedeep = ">=1.3.4" +mkdocs-get-deps = ">=0.2.0" +packaging = ">=20.5" +pathspec = ">=0.11.1" +pyyaml = ">=5.1" +pyyaml-env-tag = ">=0.1" +watchdog = ">=2.0" + +[package.extras] +i18n = ["babel (>=2.9.0)"] +min-versions = ["babel (==2.9.0)", "click (==7.0)", "colorama (==0.4) ; platform_system == \"Windows\"", "ghp-import (==1.0)", "importlib-metadata (==4.4) ; python_version < \"3.10\"", "jinja2 (==2.11.1)", "markdown (==3.3.6)", "markupsafe (==2.0.1)", "mergedeep (==1.3.4)", "mkdocs-get-deps (==0.2.0)", "packaging (==20.5)", "pathspec (==0.11.1)", "pyyaml (==5.1)", "pyyaml-env-tag (==0.1)", "watchdog (==2.0)"] + +[[package]] +name = "mkdocs-autorefs" +version = "1.4.4" +description = "Automatically link across pages in MkDocs." +optional = true +python-versions = ">=3.9" +groups = ["main"] +markers = "extra == \"dev\"" +files = [ + {file = "mkdocs_autorefs-1.4.4-py3-none-any.whl", hash = "sha256:834ef5408d827071ad1bc69e0f39704fa34c7fc05bc8e1c72b227dfdc5c76089"}, + {file = "mkdocs_autorefs-1.4.4.tar.gz", hash = "sha256:d54a284f27a7346b9c38f1f852177940c222da508e66edc816a0fa55fc6da197"}, +] + +[package.dependencies] +Markdown = ">=3.3" +markupsafe = ">=2.0.1" +mkdocs = ">=1.1" + +[[package]] +name = "mkdocs-get-deps" +version = "0.2.2" +description = "An extra command for MkDocs that infers required PyPI packages from `plugins` in mkdocs.yml" +optional = true +python-versions = ">=3.9" +groups = ["main"] +markers = "extra == \"dev\"" +files = [ + {file = "mkdocs_get_deps-0.2.2-py3-none-any.whl", hash = "sha256:e7878cbeac04860b8b5e0ca31d3abad3df9411a75a32cde82f8e44b6c16ff650"}, + {file = "mkdocs_get_deps-0.2.2.tar.gz", hash = "sha256:8ee8d5f316cdbbb2834bc1df6e69c08fe769a83e040060de26d3c19fad3599a1"}, +] + +[package.dependencies] +mergedeep = ">=1.3.4" +platformdirs = ">=2.2.0" +pyyaml = ">=5.1" + +[[package]] +name = "mkdocs-material" +version = "9.7.6" +description = "Documentation that simply works" +optional = true +python-versions = ">=3.8" +groups = ["main"] +markers = "extra == \"dev\"" +files = [ + {file = "mkdocs_material-9.7.6-py3-none-any.whl", hash = "sha256:71b84353921b8ea1ba84fe11c50912cc512da8fe0881038fcc9a0761c0e635ba"}, + {file = "mkdocs_material-9.7.6.tar.gz", hash = "sha256:00bdde50574f776d328b1862fe65daeaf581ec309bd150f7bff345a098c64a69"}, +] + +[package.dependencies] +babel = ">=2.10" +backrefs = ">=5.7.post1" +colorama = ">=0.4" +jinja2 = ">=3.1" +markdown = ">=3.2" +mkdocs = ">=1.6,<2" +mkdocs-material-extensions = ">=1.3" +paginate = ">=0.5" +pygments = ">=2.16" +pymdown-extensions = ">=10.2" +requests = ">=2.30" + +[package.extras] +git = ["mkdocs-git-committers-plugin-2 (>=1.1)", "mkdocs-git-revision-date-localized-plugin (>=1.2.4)"] +imaging = ["cairosvg (>=2.6)", "pillow (>=10.2)"] +recommended = ["mkdocs-minify-plugin (>=0.7)", "mkdocs-redirects (>=1.2)", "mkdocs-rss-plugin (>=1.6)"] + +[[package]] +name = "mkdocs-material-extensions" +version = "1.3.1" +description = "Extension pack for Python Markdown and MkDocs Material." +optional = true +python-versions = ">=3.8" +groups = ["main"] +markers = "extra == \"dev\"" +files = [ + {file = "mkdocs_material_extensions-1.3.1-py3-none-any.whl", hash = "sha256:adff8b62700b25cb77b53358dad940f3ef973dd6db797907c49e3c2ef3ab4e31"}, + {file = "mkdocs_material_extensions-1.3.1.tar.gz", hash = "sha256:10c9511cea88f568257f960358a467d12b970e1f7b2c0e5fb2bb48cab1928443"}, +] + +[[package]] +name = "mkdocstrings" +version = "1.0.3" +description = "Automatic documentation from sources, for MkDocs." +optional = true +python-versions = ">=3.10" +groups = ["main"] +markers = "extra == \"dev\"" +files = [ + {file = "mkdocstrings-1.0.3-py3-none-any.whl", hash = "sha256:0d66d18430c2201dc7fe85134277382baaa15e6b30979f3f3bdbabd6dbdb6046"}, + {file = "mkdocstrings-1.0.3.tar.gz", hash = "sha256:ab670f55040722b49bb45865b2e93b824450fb4aef638b00d7acb493a9020434"}, +] + +[package.dependencies] +Jinja2 = ">=3.1" +Markdown = ">=3.6" +MarkupSafe = ">=1.1" +mkdocs = ">=1.6" +mkdocs-autorefs = ">=1.4" +mkdocstrings-python = {version = ">=1.16.2", optional = true, markers = "extra == \"python\""} +pymdown-extensions = ">=6.3" + +[package.extras] +crystal = ["mkdocstrings-crystal (>=0.3.4)"] +python = ["mkdocstrings-python (>=1.16.2)"] +python-legacy = ["mkdocstrings-python-legacy (>=0.2.1)"] + +[[package]] +name = "mkdocstrings-python" +version = "2.0.3" +description = "A Python handler for mkdocstrings." +optional = true +python-versions = ">=3.10" +groups = ["main"] +markers = "extra == \"dev\"" +files = [ + {file = "mkdocstrings_python-2.0.3-py3-none-any.whl", hash = "sha256:0b83513478bdfd803ff05aa43e9b1fca9dd22bcd9471f09ca6257f009bc5ee12"}, + {file = "mkdocstrings_python-2.0.3.tar.gz", hash = "sha256:c518632751cc869439b31c9d3177678ad2bfa5c21b79b863956ad68fc92c13b8"}, +] + +[package.dependencies] +griffelib = ">=2.0" +mkdocs-autorefs = ">=1.4" +mkdocstrings = ">=0.30" +typing-extensions = {version = ">=4.0", markers = "python_version < \"3.11\""} + +[[package]] +name = "mypy" +version = "1.19.1" +description = "Optional static typing for Python" +optional = true +python-versions = ">=3.9" +groups = ["main"] +markers = "extra == \"dev\"" +files = [ + {file = "mypy-1.19.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:5f05aa3d375b385734388e844bc01733bd33c644ab48e9684faa54e5389775ec"}, + {file = "mypy-1.19.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:022ea7279374af1a5d78dfcab853fe6a536eebfda4b59deab53cd21f6cd9f00b"}, + {file = "mypy-1.19.1-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ee4c11e460685c3e0c64a4c5de82ae143622410950d6be863303a1c4ba0e36d6"}, + {file = "mypy-1.19.1-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:de759aafbae8763283b2ee5869c7255391fbc4de3ff171f8f030b5ec48381b74"}, + {file = "mypy-1.19.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:ab43590f9cd5108f41aacf9fca31841142c786827a74ab7cc8a2eacb634e09a1"}, + {file = "mypy-1.19.1-cp310-cp310-win_amd64.whl", hash = "sha256:2899753e2f61e571b3971747e302d5f420c3fd09650e1951e99f823bc3089dac"}, + {file = "mypy-1.19.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:d8dfc6ab58ca7dda47d9237349157500468e404b17213d44fc1cb77bce532288"}, + {file = "mypy-1.19.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:e3f276d8493c3c97930e354b2595a44a21348b320d859fb4a2b9f66da9ed27ab"}, + {file = "mypy-1.19.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:2abb24cf3f17864770d18d673c85235ba52456b36a06b6afc1e07c1fdcd3d0e6"}, + {file = "mypy-1.19.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a009ffa5a621762d0c926a078c2d639104becab69e79538a494bcccb62cc0331"}, + {file = "mypy-1.19.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:f7cee03c9a2e2ee26ec07479f38ea9c884e301d42c6d43a19d20fb014e3ba925"}, + {file = "mypy-1.19.1-cp311-cp311-win_amd64.whl", hash = "sha256:4b84a7a18f41e167f7995200a1d07a4a6810e89d29859df936f1c3923d263042"}, + {file = "mypy-1.19.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:a8174a03289288c1f6c46d55cef02379b478bfbc8e358e02047487cad44c6ca1"}, + {file = "mypy-1.19.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ffcebe56eb09ff0c0885e750036a095e23793ba6c2e894e7e63f6d89ad51f22e"}, + {file = "mypy-1.19.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b64d987153888790bcdb03a6473d321820597ab8dd9243b27a92153c4fa50fd2"}, + {file = "mypy-1.19.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c35d298c2c4bba75feb2195655dfea8124d855dfd7343bf8b8c055421eaf0cf8"}, + {file = "mypy-1.19.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:34c81968774648ab5ac09c29a375fdede03ba253f8f8287847bd480782f73a6a"}, + {file = "mypy-1.19.1-cp312-cp312-win_amd64.whl", hash = "sha256:b10e7c2cd7870ba4ad9b2d8a6102eb5ffc1f16ca35e3de6bfa390c1113029d13"}, + {file = "mypy-1.19.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:e3157c7594ff2ef1634ee058aafc56a82db665c9438fd41b390f3bde1ab12250"}, + {file = "mypy-1.19.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:bdb12f69bcc02700c2b47e070238f42cb87f18c0bc1fc4cdb4fb2bc5fd7a3b8b"}, + {file = "mypy-1.19.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f859fb09d9583a985be9a493d5cfc5515b56b08f7447759a0c5deaf68d80506e"}, + {file = "mypy-1.19.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c9a6538e0415310aad77cb94004ca6482330fece18036b5f360b62c45814c4ef"}, + {file = "mypy-1.19.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:da4869fc5e7f62a88f3fe0b5c919d1d9f7ea3cef92d3689de2823fd27e40aa75"}, + {file = "mypy-1.19.1-cp313-cp313-win_amd64.whl", hash = "sha256:016f2246209095e8eda7538944daa1d60e1e8134d98983b9fc1e92c1fc0cb8dd"}, + {file = "mypy-1.19.1-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:06e6170bd5836770e8104c8fdd58e5e725cfeb309f0a6c681a811f557e97eac1"}, + {file = "mypy-1.19.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:804bd67b8054a85447c8954215a906d6eff9cabeabe493fb6334b24f4bfff718"}, + {file = "mypy-1.19.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:21761006a7f497cb0d4de3d8ef4ca70532256688b0523eee02baf9eec895e27b"}, + {file = "mypy-1.19.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:28902ee51f12e0f19e1e16fbe2f8f06b6637f482c459dd393efddd0ec7f82045"}, + {file = "mypy-1.19.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:481daf36a4c443332e2ae9c137dfee878fcea781a2e3f895d54bd3002a900957"}, + {file = "mypy-1.19.1-cp314-cp314-win_amd64.whl", hash = "sha256:8bb5c6f6d043655e055be9b542aa5f3bdd30e4f3589163e85f93f3640060509f"}, + {file = "mypy-1.19.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:7bcfc336a03a1aaa26dfce9fff3e287a3ba99872a157561cbfcebe67c13308e3"}, + {file = "mypy-1.19.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:b7951a701c07ea584c4fe327834b92a30825514c868b1f69c30445093fdd9d5a"}, + {file = "mypy-1.19.1-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b13cfdd6c87fc3efb69ea4ec18ef79c74c3f98b4e5498ca9b85ab3b2c2329a67"}, + {file = "mypy-1.19.1-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4f28f99c824ecebcdaa2e55d82953e38ff60ee5ec938476796636b86afa3956e"}, + {file = "mypy-1.19.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:c608937067d2fc5a4dd1a5ce92fd9e1398691b8c5d012d66e1ddd430e9244376"}, + {file = "mypy-1.19.1-cp39-cp39-win_amd64.whl", hash = "sha256:409088884802d511ee52ca067707b90c883426bd95514e8cfda8281dc2effe24"}, + {file = "mypy-1.19.1-py3-none-any.whl", hash = "sha256:f1235f5ea01b7db5468d53ece6aaddf1ad0b88d9e7462b86ef96fe04995d7247"}, + {file = "mypy-1.19.1.tar.gz", hash = "sha256:19d88bb05303fe63f71dd2c6270daca27cb9401c4ca8255fe50d1d920e0eb9ba"}, +] + +[package.dependencies] +librt = {version = ">=0.6.2", markers = "platform_python_implementation != \"PyPy\""} +mypy_extensions = ">=1.0.0" +pathspec = ">=0.9.0" +tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""} +typing_extensions = ">=4.6.0" + +[package.extras] +dmypy = ["psutil (>=4.0)"] +faster-cache = ["orjson"] +install-types = ["pip"] +mypyc = ["setuptools (>=50)"] +reports = ["lxml"] + +[[package]] +name = "mypy-extensions" +version = "1.1.0" +description = "Type system extensions for programs checked with the mypy type checker." +optional = true +python-versions = ">=3.8" +groups = ["main"] +markers = "extra == \"dev\"" +files = [ + {file = "mypy_extensions-1.1.0-py3-none-any.whl", hash = "sha256:1be4cccdb0f2482337c4743e60421de3a356cd97508abadd57d47403e94f5505"}, + {file = "mypy_extensions-1.1.0.tar.gz", hash = "sha256:52e68efc3284861e772bbcd66823fde5ae21fd2fdb51c62a211403730b916558"}, +] + +[[package]] +name = "nodeenv" +version = "1.10.0" +description = "Node.js virtual environment builder" +optional = true +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" +groups = ["main"] +markers = "extra == \"dev\"" +files = [ + {file = "nodeenv-1.10.0-py2.py3-none-any.whl", hash = "sha256:5bb13e3eed2923615535339b3c620e76779af4cb4c6a90deccc9e36b274d3827"}, + {file = "nodeenv-1.10.0.tar.gz", hash = "sha256:996c191ad80897d076bdfba80a41994c2b47c68e224c542b48feba42ba00f8bb"}, +] + +[[package]] +name = "packaging" +version = "26.0" +description = "Core utilities for Python packages" +optional = true +python-versions = ">=3.8" +groups = ["main"] +markers = "extra == \"dev\"" +files = [ + {file = "packaging-26.0-py3-none-any.whl", hash = "sha256:b36f1fef9334a5588b4166f8bcd26a14e521f2b55e6b9de3aaa80d3ff7a37529"}, + {file = "packaging-26.0.tar.gz", hash = "sha256:00243ae351a257117b6a241061796684b084ed1c516a08c48a3f7e147a9d80b4"}, +] + +[[package]] +name = "paginate" +version = "0.5.7" +description = "Divides large result sets into pages for easier browsing" +optional = true +python-versions = "*" +groups = ["main"] +markers = "extra == \"dev\"" +files = [ + {file = "paginate-0.5.7-py2.py3-none-any.whl", hash = "sha256:b885e2af73abcf01d9559fd5216b57ef722f8c42affbb63942377668e35c7591"}, + {file = "paginate-0.5.7.tar.gz", hash = "sha256:22bd083ab41e1a8b4f3690544afb2c60c25e5c9a63a30fa2f483f6c60c8e5945"}, +] + +[package.extras] +dev = ["pytest", "tox"] +lint = ["black"] + +[[package]] +name = "pathspec" +version = "1.0.4" +description = "Utility library for gitignore style pattern matching of file paths." +optional = true +python-versions = ">=3.9" +groups = ["main"] +markers = "extra == \"dev\"" +files = [ + {file = "pathspec-1.0.4-py3-none-any.whl", hash = "sha256:fb6ae2fd4e7c921a165808a552060e722767cfa526f99ca5156ed2ce45a5c723"}, + {file = "pathspec-1.0.4.tar.gz", hash = "sha256:0210e2ae8a21a9137c0d470578cb0e595af87edaa6ebf12ff176f14a02e0e645"}, +] + +[package.extras] +hyperscan = ["hyperscan (>=0.7)"] +optional = ["typing-extensions (>=4)"] +re2 = ["google-re2 (>=1.1)"] +tests = ["pytest (>=9)", "typing-extensions (>=4.15)"] + +[[package]] +name = "platformdirs" +version = "4.9.4" +description = "A small Python package for determining appropriate platform-specific dirs, e.g. a `user data dir`." +optional = true +python-versions = ">=3.10" +groups = ["main"] +markers = "extra == \"dev\"" +files = [ + {file = "platformdirs-4.9.4-py3-none-any.whl", hash = "sha256:68a9a4619a666ea6439f2ff250c12a853cd1cbd5158d258bd824a7df6be2f868"}, + {file = "platformdirs-4.9.4.tar.gz", hash = "sha256:1ec356301b7dc906d83f371c8f487070e99d3ccf9e501686456394622a01a934"}, +] + +[[package]] +name = "pluggy" +version = "1.6.0" +description = "plugin and hook calling mechanisms for python" +optional = true +python-versions = ">=3.9" +groups = ["main"] +markers = "extra == \"dev\"" +files = [ + {file = "pluggy-1.6.0-py3-none-any.whl", hash = "sha256:e920276dd6813095e9377c0bc5566d94c932c33b27a3e3945d8389c374dd4746"}, + {file = "pluggy-1.6.0.tar.gz", hash = "sha256:7dcc130b76258d33b90f61b658791dede3486c3e6bfb003ee5c9bfb396dd22f3"}, +] + +[package.extras] +dev = ["pre-commit", "tox"] +testing = ["coverage", "pytest", "pytest-benchmark"] + +[[package]] +name = "pre-commit" +version = "4.5.1" +description = "A framework for managing and maintaining multi-language pre-commit hooks." +optional = true +python-versions = ">=3.10" +groups = ["main"] +markers = "extra == \"dev\"" +files = [ + {file = "pre_commit-4.5.1-py2.py3-none-any.whl", hash = "sha256:3b3afd891e97337708c1674210f8eba659b52a38ea5f822ff142d10786221f77"}, + {file = "pre_commit-4.5.1.tar.gz", hash = "sha256:eb545fcff725875197837263e977ea257a402056661f09dae08e4b149b030a61"}, +] + +[package.dependencies] +cfgv = ">=2.0.0" +identify = ">=1.0.0" +nodeenv = ">=0.11.1" +pyyaml = ">=5.1" +virtualenv = ">=20.10.0" + +[[package]] +name = "pygments" +version = "2.20.0" +description = "Pygments is a syntax highlighting package written in Python." +optional = true +python-versions = ">=3.9" +groups = ["main"] +markers = "extra == \"dev\"" +files = [ + {file = "pygments-2.20.0-py3-none-any.whl", hash = "sha256:81a9e26dd42fd28a23a2d169d86d7ac03b46e2f8b59ed4698fb4785f946d0176"}, + {file = "pygments-2.20.0.tar.gz", hash = "sha256:6757cd03768053ff99f3039c1a36d6c0aa0b263438fcab17520b30a303a82b5f"}, +] + +[package.extras] +windows-terminal = ["colorama (>=0.4.6)"] + +[[package]] +name = "pymdown-extensions" +version = "10.21" +description = "Extension pack for Python Markdown." +optional = true +python-versions = ">=3.9" +groups = ["main"] +markers = "extra == \"dev\"" +files = [ + {file = "pymdown_extensions-10.21-py3-none-any.whl", hash = "sha256:91b879f9f864d49794c2d9534372b10150e6141096c3908a455e45ca72ad9d3f"}, + {file = "pymdown_extensions-10.21.tar.gz", hash = "sha256:39f4a020f40773f6b2ff31d2cd2546c2c04d0a6498c31d9c688d2be07e1767d5"}, +] + +[package.dependencies] +markdown = ">=3.6" +pyyaml = "*" + +[package.extras] +extra = ["pygments (>=2.19.1)"] + +[[package]] +name = "pytest" +version = "9.0.3" +description = "pytest: simple powerful testing with Python" +optional = true +python-versions = ">=3.10" +groups = ["main"] +markers = "extra == \"dev\"" +files = [ + {file = "pytest-9.0.3-py3-none-any.whl", hash = "sha256:2c5efc453d45394fdd706ade797c0a81091eccd1d6e4bccfcd476e2b8e0ab5d9"}, + {file = "pytest-9.0.3.tar.gz", hash = "sha256:b86ada508af81d19edeb213c681b1d48246c1a91d304c6c81a427674c17eb91c"}, +] + +[package.dependencies] +colorama = {version = ">=0.4", markers = "sys_platform == \"win32\""} +exceptiongroup = {version = ">=1", markers = "python_version < \"3.11\""} +iniconfig = ">=1.0.1" +packaging = ">=22" +pluggy = ">=1.5,<2" +pygments = ">=2.7.2" +tomli = {version = ">=1", markers = "python_version < \"3.11\""} + +[package.extras] +dev = ["argcomplete", "attrs (>=19.2)", "hypothesis (>=3.56)", "mock", "requests", "setuptools", "xmlschema"] + +[[package]] +name = "pytest-cov" +version = "7.1.0" +description = "Pytest plugin for measuring coverage." +optional = true +python-versions = ">=3.9" +groups = ["main"] +markers = "extra == \"dev\"" +files = [ + {file = "pytest_cov-7.1.0-py3-none-any.whl", hash = "sha256:a0461110b7865f9a271aa1b51e516c9a95de9d696734a2f71e3e78f46e1d4678"}, + {file = "pytest_cov-7.1.0.tar.gz", hash = "sha256:30674f2b5f6351aa09702a9c8c364f6a01c27aae0c1366ae8016160d1efc56b2"}, +] + +[package.dependencies] +coverage = {version = ">=7.10.6", extras = ["toml"]} +pluggy = ">=1.2" +pytest = ">=7" + +[package.extras] +testing = ["process-tests", "pytest-xdist", "virtualenv"] + +[[package]] +name = "python-dateutil" +version = "2.9.0.post0" +description = "Extensions to the standard Python datetime module" +optional = true +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7" +groups = ["main"] +markers = "extra == \"dev\"" +files = [ + {file = "python-dateutil-2.9.0.post0.tar.gz", hash = "sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3"}, + {file = "python_dateutil-2.9.0.post0-py2.py3-none-any.whl", hash = "sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427"}, +] + +[package.dependencies] +six = ">=1.5" + +[[package]] +name = "python-discovery" +version = "1.2.1" +description = "Python interpreter discovery" +optional = true +python-versions = ">=3.8" +groups = ["main"] +markers = "extra == \"dev\"" +files = [ + {file = "python_discovery-1.2.1-py3-none-any.whl", hash = "sha256:b6a957b24c1cd79252484d3566d1b49527581d46e789aaf43181005e56201502"}, + {file = "python_discovery-1.2.1.tar.gz", hash = "sha256:180c4d114bff1c32462537eac5d6a332b768242b76b69c0259c7d14b1b680c9e"}, +] + +[package.dependencies] +filelock = ">=3.15.4" +platformdirs = ">=4.3.6,<5" + +[package.extras] +docs = ["furo (>=2025.12.19)", "sphinx (>=9.1)", "sphinx-autodoc-typehints (>=3.6.3)", "sphinxcontrib-mermaid (>=2)"] +testing = ["covdefaults (>=2.3)", "coverage (>=7.5.4)", "pytest (>=8.3.5)", "pytest-mock (>=3.14)", "setuptools (>=75.1)"] + +[[package]] +name = "pytokens" +version = "0.4.1" +description = "A Fast, spec compliant Python 3.14+ tokenizer that runs on older Pythons." +optional = true +python-versions = ">=3.8" +groups = ["main"] +markers = "extra == \"dev\"" +files = [ + {file = "pytokens-0.4.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:2a44ed93ea23415c54f3face3b65ef2b844d96aeb3455b8a69b3df6beab6acc5"}, + {file = "pytokens-0.4.1-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:add8bf86b71a5d9fb5b89f023a80b791e04fba57960aa790cc6125f7f1d39dfe"}, + {file = "pytokens-0.4.1-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:670d286910b531c7b7e3c0b453fd8156f250adb140146d234a82219459b9640c"}, + {file = "pytokens-0.4.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:4e691d7f5186bd2842c14813f79f8884bb03f5995f0575272009982c5ac6c0f7"}, + {file = "pytokens-0.4.1-cp310-cp310-win_amd64.whl", hash = "sha256:27b83ad28825978742beef057bfe406ad6ed524b2d28c252c5de7b4a6dd48fa2"}, + {file = "pytokens-0.4.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:d70e77c55ae8380c91c0c18dea05951482e263982911fc7410b1ffd1dadd3440"}, + {file = "pytokens-0.4.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4a58d057208cb9075c144950d789511220b07636dd2e4708d5645d24de666bdc"}, + {file = "pytokens-0.4.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b49750419d300e2b5a3813cf229d4e5a4c728dae470bcc89867a9ad6f25a722d"}, + {file = "pytokens-0.4.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:d9907d61f15bf7261d7e775bd5d7ee4d2930e04424bab1972591918497623a16"}, + {file = "pytokens-0.4.1-cp311-cp311-win_amd64.whl", hash = "sha256:ee44d0f85b803321710f9239f335aafe16553b39106384cef8e6de40cb4ef2f6"}, + {file = "pytokens-0.4.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:140709331e846b728475786df8aeb27d24f48cbcf7bcd449f8de75cae7a45083"}, + {file = "pytokens-0.4.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6d6c4268598f762bc8e91f5dbf2ab2f61f7b95bdc07953b602db879b3c8c18e1"}, + {file = "pytokens-0.4.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:24afde1f53d95348b5a0eb19488661147285ca4dd7ed752bbc3e1c6242a304d1"}, + {file = "pytokens-0.4.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:5ad948d085ed6c16413eb5fec6b3e02fa00dc29a2534f088d3302c47eb59adf9"}, + {file = "pytokens-0.4.1-cp312-cp312-win_amd64.whl", hash = "sha256:3f901fe783e06e48e8cbdc82d631fca8f118333798193e026a50ce1b3757ea68"}, + {file = "pytokens-0.4.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:8bdb9d0ce90cbf99c525e75a2fa415144fd570a1ba987380190e8b786bc6ef9b"}, + {file = "pytokens-0.4.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5502408cab1cb18e128570f8d598981c68a50d0cbd7c61312a90507cd3a1276f"}, + {file = "pytokens-0.4.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:29d1d8fb1030af4d231789959f21821ab6325e463f0503a61d204343c9b355d1"}, + {file = "pytokens-0.4.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:970b08dd6b86058b6dc07efe9e98414f5102974716232d10f32ff39701e841c4"}, + {file = "pytokens-0.4.1-cp313-cp313-win_amd64.whl", hash = "sha256:9bd7d7f544d362576be74f9d5901a22f317efc20046efe2034dced238cbbfe78"}, + {file = "pytokens-0.4.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:4a14d5f5fc78ce85e426aa159489e2d5961acf0e47575e08f35584009178e321"}, + {file = "pytokens-0.4.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:97f50fd18543be72da51dd505e2ed20d2228c74e0464e4262e4899797803d7fa"}, + {file = "pytokens-0.4.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:dc74c035f9bfca0255c1af77ddd2d6ae8419012805453e4b0e7513e17904545d"}, + {file = "pytokens-0.4.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:f66a6bbe741bd431f6d741e617e0f39ec7257ca1f89089593479347cc4d13324"}, + {file = "pytokens-0.4.1-cp314-cp314-win_amd64.whl", hash = "sha256:b35d7e5ad269804f6697727702da3c517bb8a5228afa450ab0fa787732055fc9"}, + {file = "pytokens-0.4.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:8fcb9ba3709ff77e77f1c7022ff11d13553f3c30299a9fe246a166903e9091eb"}, + {file = "pytokens-0.4.1-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:79fc6b8699564e1f9b521582c35435f1bd32dd06822322ec44afdeba666d8cb3"}, + {file = "pytokens-0.4.1-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d31b97b3de0f61571a124a00ffe9a81fb9939146c122c11060725bd5aea79975"}, + {file = "pytokens-0.4.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:967cf6e3fd4adf7de8fc73cd3043754ae79c36475c1c11d514fc72cf5490094a"}, + {file = "pytokens-0.4.1-cp314-cp314t-win_amd64.whl", hash = "sha256:584c80c24b078eec1e227079d56dc22ff755e0ba8654d8383b2c549107528918"}, + {file = "pytokens-0.4.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:da5baeaf7116dced9c6bb76dc31ba04a2dc3695f3d9f74741d7910122b456edc"}, + {file = "pytokens-0.4.1-cp38-cp38-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:11edda0942da80ff58c4408407616a310adecae1ddd22eef8c692fe266fa5009"}, + {file = "pytokens-0.4.1-cp38-cp38-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0fc71786e629cef478cbf29d7ea1923299181d0699dbe7c3c0f4a583811d9fc1"}, + {file = "pytokens-0.4.1-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:dcafc12c30dbaf1e2af0490978352e0c4041a7cde31f4f81435c2a5e8b9cabb6"}, + {file = "pytokens-0.4.1-cp38-cp38-win_amd64.whl", hash = "sha256:42f144f3aafa5d92bad964d471a581651e28b24434d184871bd02e3a0d956037"}, + {file = "pytokens-0.4.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:34bcc734bd2f2d5fe3b34e7b3c0116bfb2397f2d9666139988e7a3eb5f7400e3"}, + {file = "pytokens-0.4.1-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:941d4343bf27b605e9213b26bfa1c4bf197c9c599a9627eb7305b0defcfe40c1"}, + {file = "pytokens-0.4.1-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:3ad72b851e781478366288743198101e5eb34a414f1d5627cdd585ca3b25f1db"}, + {file = "pytokens-0.4.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:682fa37ff4d8e95f7df6fe6fe6a431e8ed8e788023c6bcc0f0880a12eab80ad1"}, + {file = "pytokens-0.4.1-cp39-cp39-win_amd64.whl", hash = "sha256:30f51edd9bb7f85c748979384165601d028b84f7bd13fe14d3e065304093916a"}, + {file = "pytokens-0.4.1-py3-none-any.whl", hash = "sha256:26cef14744a8385f35d0e095dc8b3a7583f6c953c2e3d269c7f82484bf5ad2de"}, + {file = "pytokens-0.4.1.tar.gz", hash = "sha256:292052fe80923aae2260c073f822ceba21f3872ced9a68bb7953b348e561179a"}, +] + +[package.extras] +dev = ["black", "build", "mypy", "pytest", "pytest-cov", "setuptools", "tox", "twine", "wheel"] + +[[package]] +name = "pyyaml" +version = "6.0.3" +description = "YAML parser and emitter for Python" +optional = true +python-versions = ">=3.8" +groups = ["main"] +markers = "extra == \"dev\"" +files = [ + {file = "PyYAML-6.0.3-cp38-cp38-macosx_10_13_x86_64.whl", hash = "sha256:c2514fceb77bc5e7a2f7adfaa1feb2fb311607c9cb518dbc378688ec73d8292f"}, + {file = "PyYAML-6.0.3-cp38-cp38-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9c57bb8c96f6d1808c030b1687b9b5fb476abaa47f0db9c0101f5e9f394e97f4"}, + {file = "PyYAML-6.0.3-cp38-cp38-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:efd7b85f94a6f21e4932043973a7ba2613b059c4a000551892ac9f1d11f5baf3"}, + {file = "PyYAML-6.0.3-cp38-cp38-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:22ba7cfcad58ef3ecddc7ed1db3409af68d023b7f940da23c6c2a1890976eda6"}, + {file = "PyYAML-6.0.3-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:6344df0d5755a2c9a276d4473ae6b90647e216ab4757f8426893b5dd2ac3f369"}, + {file = "PyYAML-6.0.3-cp38-cp38-win32.whl", hash = "sha256:3ff07ec89bae51176c0549bc4c63aa6202991da2d9a6129d7aef7f1407d3f295"}, + {file = "PyYAML-6.0.3-cp38-cp38-win_amd64.whl", hash = "sha256:5cf4e27da7e3fbed4d6c3d8e797387aaad68102272f8f9752883bc32d61cb87b"}, + {file = "pyyaml-6.0.3-cp310-cp310-macosx_10_13_x86_64.whl", hash = "sha256:214ed4befebe12df36bcc8bc2b64b396ca31be9304b8f59e25c11cf94a4c033b"}, + {file = "pyyaml-6.0.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:02ea2dfa234451bbb8772601d7b8e426c2bfa197136796224e50e35a78777956"}, + {file = "pyyaml-6.0.3-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b30236e45cf30d2b8e7b3e85881719e98507abed1011bf463a8fa23e9c3e98a8"}, + {file = "pyyaml-6.0.3-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:66291b10affd76d76f54fad28e22e51719ef9ba22b29e1d7d03d6777a9174198"}, + {file = "pyyaml-6.0.3-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9c7708761fccb9397fe64bbc0395abcae8c4bf7b0eac081e12b809bf47700d0b"}, + {file = "pyyaml-6.0.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:418cf3f2111bc80e0933b2cd8cd04f286338bb88bdc7bc8e6dd775ebde60b5e0"}, + {file = "pyyaml-6.0.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:5e0b74767e5f8c593e8c9b5912019159ed0533c70051e9cce3e8b6aa699fcd69"}, + {file = "pyyaml-6.0.3-cp310-cp310-win32.whl", hash = "sha256:28c8d926f98f432f88adc23edf2e6d4921ac26fb084b028c733d01868d19007e"}, + {file = "pyyaml-6.0.3-cp310-cp310-win_amd64.whl", hash = "sha256:bdb2c67c6c1390b63c6ff89f210c8fd09d9a1217a465701eac7316313c915e4c"}, + {file = "pyyaml-6.0.3-cp311-cp311-macosx_10_13_x86_64.whl", hash = "sha256:44edc647873928551a01e7a563d7452ccdebee747728c1080d881d68af7b997e"}, + {file = "pyyaml-6.0.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:652cb6edd41e718550aad172851962662ff2681490a8a711af6a4d288dd96824"}, + {file = "pyyaml-6.0.3-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:10892704fc220243f5305762e276552a0395f7beb4dbf9b14ec8fd43b57f126c"}, + {file = "pyyaml-6.0.3-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:850774a7879607d3a6f50d36d04f00ee69e7fc816450e5f7e58d7f17f1ae5c00"}, + {file = "pyyaml-6.0.3-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b8bb0864c5a28024fac8a632c443c87c5aa6f215c0b126c449ae1a150412f31d"}, + {file = "pyyaml-6.0.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:1d37d57ad971609cf3c53ba6a7e365e40660e3be0e5175fa9f2365a379d6095a"}, + {file = "pyyaml-6.0.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:37503bfbfc9d2c40b344d06b2199cf0e96e97957ab1c1b546fd4f87e53e5d3e4"}, + {file = "pyyaml-6.0.3-cp311-cp311-win32.whl", hash = "sha256:8098f252adfa6c80ab48096053f512f2321f0b998f98150cea9bd23d83e1467b"}, + {file = "pyyaml-6.0.3-cp311-cp311-win_amd64.whl", hash = "sha256:9f3bfb4965eb874431221a3ff3fdcddc7e74e3b07799e0e84ca4a0f867d449bf"}, + {file = "pyyaml-6.0.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:7f047e29dcae44602496db43be01ad42fc6f1cc0d8cd6c83d342306c32270196"}, + {file = "pyyaml-6.0.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:fc09d0aa354569bc501d4e787133afc08552722d3ab34836a80547331bb5d4a0"}, + {file = "pyyaml-6.0.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9149cad251584d5fb4981be1ecde53a1ca46c891a79788c0df828d2f166bda28"}, + {file = "pyyaml-6.0.3-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:5fdec68f91a0c6739b380c83b951e2c72ac0197ace422360e6d5a959d8d97b2c"}, + {file = "pyyaml-6.0.3-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ba1cc08a7ccde2d2ec775841541641e4548226580ab850948cbfda66a1befcdc"}, + {file = "pyyaml-6.0.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:8dc52c23056b9ddd46818a57b78404882310fb473d63f17b07d5c40421e47f8e"}, + {file = "pyyaml-6.0.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:41715c910c881bc081f1e8872880d3c650acf13dfa8214bad49ed4cede7c34ea"}, + {file = "pyyaml-6.0.3-cp312-cp312-win32.whl", hash = "sha256:96b533f0e99f6579b3d4d4995707cf36df9100d67e0c8303a0c55b27b5f99bc5"}, + {file = "pyyaml-6.0.3-cp312-cp312-win_amd64.whl", hash = "sha256:5fcd34e47f6e0b794d17de1b4ff496c00986e1c83f7ab2fb8fcfe9616ff7477b"}, + {file = "pyyaml-6.0.3-cp312-cp312-win_arm64.whl", hash = "sha256:64386e5e707d03a7e172c0701abfb7e10f0fb753ee1d773128192742712a98fd"}, + {file = "pyyaml-6.0.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:8da9669d359f02c0b91ccc01cac4a67f16afec0dac22c2ad09f46bee0697eba8"}, + {file = "pyyaml-6.0.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:2283a07e2c21a2aa78d9c4442724ec1eb15f5e42a723b99cb3d822d48f5f7ad1"}, + {file = "pyyaml-6.0.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ee2922902c45ae8ccada2c5b501ab86c36525b883eff4255313a253a3160861c"}, + {file = "pyyaml-6.0.3-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a33284e20b78bd4a18c8c2282d549d10bc8408a2a7ff57653c0cf0b9be0afce5"}, + {file = "pyyaml-6.0.3-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0f29edc409a6392443abf94b9cf89ce99889a1dd5376d94316ae5145dfedd5d6"}, + {file = "pyyaml-6.0.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:f7057c9a337546edc7973c0d3ba84ddcdf0daa14533c2065749c9075001090e6"}, + {file = "pyyaml-6.0.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:eda16858a3cab07b80edaf74336ece1f986ba330fdb8ee0d6c0d68fe82bc96be"}, + {file = "pyyaml-6.0.3-cp313-cp313-win32.whl", hash = "sha256:d0eae10f8159e8fdad514efdc92d74fd8d682c933a6dd088030f3834bc8e6b26"}, + {file = "pyyaml-6.0.3-cp313-cp313-win_amd64.whl", hash = "sha256:79005a0d97d5ddabfeeea4cf676af11e647e41d81c9a7722a193022accdb6b7c"}, + {file = "pyyaml-6.0.3-cp313-cp313-win_arm64.whl", hash = "sha256:5498cd1645aa724a7c71c8f378eb29ebe23da2fc0d7a08071d89469bf1d2defb"}, + {file = "pyyaml-6.0.3-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:8d1fab6bb153a416f9aeb4b8763bc0f22a5586065f86f7664fc23339fc1c1fac"}, + {file = "pyyaml-6.0.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:34d5fcd24b8445fadc33f9cf348c1047101756fd760b4dacb5c3e99755703310"}, + {file = "pyyaml-6.0.3-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:501a031947e3a9025ed4405a168e6ef5ae3126c59f90ce0cd6f2bfc477be31b7"}, + {file = "pyyaml-6.0.3-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:b3bc83488de33889877a0f2543ade9f70c67d66d9ebb4ac959502e12de895788"}, + {file = "pyyaml-6.0.3-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c458b6d084f9b935061bc36216e8a69a7e293a2f1e68bf956dcd9e6cbcd143f5"}, + {file = "pyyaml-6.0.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:7c6610def4f163542a622a73fb39f534f8c101d690126992300bf3207eab9764"}, + {file = "pyyaml-6.0.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:5190d403f121660ce8d1d2c1bb2ef1bd05b5f68533fc5c2ea899bd15f4399b35"}, + {file = "pyyaml-6.0.3-cp314-cp314-win_amd64.whl", hash = "sha256:4a2e8cebe2ff6ab7d1050ecd59c25d4c8bd7e6f400f5f82b96557ac0abafd0ac"}, + {file = "pyyaml-6.0.3-cp314-cp314-win_arm64.whl", hash = "sha256:93dda82c9c22deb0a405ea4dc5f2d0cda384168e466364dec6255b293923b2f3"}, + {file = "pyyaml-6.0.3-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:02893d100e99e03eda1c8fd5c441d8c60103fd175728e23e431db1b589cf5ab3"}, + {file = "pyyaml-6.0.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:c1ff362665ae507275af2853520967820d9124984e0f7466736aea23d8611fba"}, + {file = "pyyaml-6.0.3-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6adc77889b628398debc7b65c073bcb99c4a0237b248cacaf3fe8a557563ef6c"}, + {file = "pyyaml-6.0.3-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a80cb027f6b349846a3bf6d73b5e95e782175e52f22108cfa17876aaeff93702"}, + {file = "pyyaml-6.0.3-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:00c4bdeba853cc34e7dd471f16b4114f4162dc03e6b7afcc2128711f0eca823c"}, + {file = "pyyaml-6.0.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:66e1674c3ef6f541c35191caae2d429b967b99e02040f5ba928632d9a7f0f065"}, + {file = "pyyaml-6.0.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:16249ee61e95f858e83976573de0f5b2893b3677ba71c9dd36b9cf8be9ac6d65"}, + {file = "pyyaml-6.0.3-cp314-cp314t-win_amd64.whl", hash = "sha256:4ad1906908f2f5ae4e5a8ddfce73c320c2a1429ec52eafd27138b7f1cbe341c9"}, + {file = "pyyaml-6.0.3-cp314-cp314t-win_arm64.whl", hash = "sha256:ebc55a14a21cb14062aa4162f906cd962b28e2e9ea38f9b4391244cd8de4ae0b"}, + {file = "pyyaml-6.0.3-cp39-cp39-macosx_10_13_x86_64.whl", hash = "sha256:b865addae83924361678b652338317d1bd7e79b1f4596f96b96c77a5a34b34da"}, + {file = "pyyaml-6.0.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:c3355370a2c156cffb25e876646f149d5d68f5e0a3ce86a5084dd0b64a994917"}, + {file = "pyyaml-6.0.3-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3c5677e12444c15717b902a5798264fa7909e41153cdf9ef7ad571b704a63dd9"}, + {file = "pyyaml-6.0.3-cp39-cp39-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:5ed875a24292240029e4483f9d4a4b8a1ae08843b9c54f43fcc11e404532a8a5"}, + {file = "pyyaml-6.0.3-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0150219816b6a1fa26fb4699fb7daa9caf09eb1999f3b70fb6e786805e80375a"}, + {file = "pyyaml-6.0.3-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:fa160448684b4e94d80416c0fa4aac48967a969efe22931448d853ada8baf926"}, + {file = "pyyaml-6.0.3-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:27c0abcb4a5dac13684a37f76e701e054692a9b2d3064b70f5e4eb54810553d7"}, + {file = "pyyaml-6.0.3-cp39-cp39-win32.whl", hash = "sha256:1ebe39cb5fc479422b83de611d14e2c0d3bb2a18bbcb01f229ab3cfbd8fee7a0"}, + {file = "pyyaml-6.0.3-cp39-cp39-win_amd64.whl", hash = "sha256:2e71d11abed7344e42a8849600193d15b6def118602c4c176f748e4583246007"}, + {file = "pyyaml-6.0.3.tar.gz", hash = "sha256:d76623373421df22fb4cf8817020cbb7ef15c725b9d5e45f17e189bfc384190f"}, +] + +[[package]] +name = "pyyaml-env-tag" +version = "1.1" +description = "A custom YAML tag for referencing environment variables in YAML files." +optional = true +python-versions = ">=3.9" +groups = ["main"] +markers = "extra == \"dev\"" +files = [ + {file = "pyyaml_env_tag-1.1-py3-none-any.whl", hash = "sha256:17109e1a528561e32f026364712fee1264bc2ea6715120891174ed1b980d2e04"}, + {file = "pyyaml_env_tag-1.1.tar.gz", hash = "sha256:2eb38b75a2d21ee0475d6d97ec19c63287a7e140231e4214969d0eac923cd7ff"}, +] + +[package.dependencies] +pyyaml = "*" + +[[package]] +name = "requests" +version = "2.33.0" +description = "Python HTTP for Humans." +optional = true +python-versions = ">=3.10" +groups = ["main"] +markers = "extra == \"dev\"" +files = [ + {file = "requests-2.33.0-py3-none-any.whl", hash = "sha256:3324635456fa185245e24865e810cecec7b4caf933d7eb133dcde67d48cee69b"}, + {file = "requests-2.33.0.tar.gz", hash = "sha256:c7ebc5e8b0f21837386ad0e1c8fe8b829fa5f544d8df3b2253bff14ef29d7652"}, +] + +[package.dependencies] +certifi = ">=2023.5.7" +charset_normalizer = ">=2,<4" +idna = ">=2.5,<4" +urllib3 = ">=1.26,<3" + +[package.extras] +socks = ["PySocks (>=1.5.6,!=1.5.7)"] +test = ["PySocks (>=1.5.6,!=1.5.7)", "pytest (>=3)", "pytest-cov", "pytest-httpbin (==2.1.0)", "pytest-mock", "pytest-xdist"] +use-chardet-on-py3 = ["chardet (>=3.0.2,<8)"] + +[[package]] +name = "ruff" +version = "0.15.11" +description = "An extremely fast Python linter and code formatter, written in Rust." +optional = true +python-versions = ">=3.7" +groups = ["main"] +markers = "extra == \"dev\"" +files = [ + {file = "ruff-0.15.11-py3-none-linux_armv6l.whl", hash = "sha256:e927cfff503135c558eb581a0c9792264aae9507904eb27809cdcff2f2c847b7"}, + {file = "ruff-0.15.11-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:7a1b5b2938d8f890b76084d4fa843604d787a912541eae85fd7e233398bbb73e"}, + {file = "ruff-0.15.11-py3-none-macosx_11_0_arm64.whl", hash = "sha256:d4176f3d194afbdaee6e41b9ccb1a2c287dba8700047df474abfbe773825d1cb"}, + {file = "ruff-0.15.11-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3b17c886fb88203ced3afe7f14e8d5ae96e9d2f4ccc0ee66aa19f2c2675a27e4"}, + {file = "ruff-0.15.11-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:49fafa220220afe7758a487b048de4c8f9f767f37dfefad46b9dd06759d003eb"}, + {file = "ruff-0.15.11-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f2ab8427e74a00d93b8bda1307b1e60970d40f304af38bccb218e056c220120d"}, + {file = "ruff-0.15.11-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:195072c0c8e1fc8f940652073df082e37a5d9cb43b4ab1e4d0566ab8977a13b7"}, + {file = "ruff-0.15.11-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a3a0996d486af3920dec930a2e7daed4847dfc12649b537a9335585ada163e9e"}, + {file = "ruff-0.15.11-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1bef2cb556d509259f1fe440bb9cd33c756222cf0a7afe90d15edf0866702431"}, + {file = "ruff-0.15.11-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:030d921a836d7d4a12cf6e8d984a88b66094ccb0e0f17ddd55067c331191bf19"}, + {file = "ruff-0.15.11-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:0e783b599b4577788dbbb66b9addcef87e9a8832f4ce0c19e34bf55543a2f890"}, + {file = "ruff-0.15.11-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:ae90592246625ba4a34349d68ec28d4400d75182b71baa196ddb9f82db025ef5"}, + {file = "ruff-0.15.11-py3-none-musllinux_1_2_i686.whl", hash = "sha256:1f111d62e3c983ed20e0ca2e800f8d77433a5b1161947df99a5c2a3fb60514f0"}, + {file = "ruff-0.15.11-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:06f483d6646f59eaffba9ae30956370d3a886625f511a3108994000480621d1c"}, + {file = "ruff-0.15.11-py3-none-win32.whl", hash = "sha256:476a2aa56b7da0b73a3ee80b6b2f0e19cce544245479adde7baa65466664d5f3"}, + {file = "ruff-0.15.11-py3-none-win_amd64.whl", hash = "sha256:8b6756d88d7e234fb0c98c91511aae3cd519d5e3ed271cae31b20f39cb2a12a3"}, + {file = "ruff-0.15.11-py3-none-win_arm64.whl", hash = "sha256:063fed18cc1bbe0ee7393957284a6fe8b588c6a406a285af3ee3f46da2391ee4"}, + {file = "ruff-0.15.11.tar.gz", hash = "sha256:f092b21708bf0e7437ce9ada249dfe688ff9a0954fc94abab05dcea7dcd29c33"}, +] + +[[package]] +name = "six" +version = "1.17.0" +description = "Python 2 and 3 compatibility utilities" +optional = true +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7" +groups = ["main"] +markers = "extra == \"dev\"" +files = [ + {file = "six-1.17.0-py2.py3-none-any.whl", hash = "sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274"}, + {file = "six-1.17.0.tar.gz", hash = "sha256:ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81"}, +] + +[[package]] +name = "tomli" +version = "2.4.1" +description = "A lil' TOML parser" +optional = true +python-versions = ">=3.8" +groups = ["main"] +markers = "extra == \"dev\" and python_full_version <= \"3.11.0a6\"" +files = [ + {file = "tomli-2.4.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:f8f0fc26ec2cc2b965b7a3b87cd19c5c6b8c5e5f436b984e85f486d652285c30"}, + {file = "tomli-2.4.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:4ab97e64ccda8756376892c53a72bd1f964e519c77236368527f758fbc36a53a"}, + {file = "tomli-2.4.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:96481a5786729fd470164b47cdb3e0e58062a496f455ee41b4403be77cb5a076"}, + {file = "tomli-2.4.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5a881ab208c0baf688221f8cecc5401bd291d67e38a1ac884d6736cbcd8247e9"}, + {file = "tomli-2.4.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:47149d5bd38761ac8be13a84864bf0b7b70bc051806bc3669ab1cbc56216b23c"}, + {file = "tomli-2.4.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:ec9bfaf3ad2df51ace80688143a6a4ebc09a248f6ff781a9945e51937008fcbc"}, + {file = "tomli-2.4.1-cp311-cp311-win32.whl", hash = "sha256:ff2983983d34813c1aeb0fa89091e76c3a22889ee83ab27c5eeb45100560c049"}, + {file = "tomli-2.4.1-cp311-cp311-win_amd64.whl", hash = "sha256:5ee18d9ebdb417e384b58fe414e8d6af9f4e7a0ae761519fb50f721de398dd4e"}, + {file = "tomli-2.4.1-cp311-cp311-win_arm64.whl", hash = "sha256:c2541745709bad0264b7d4705ad453b76ccd191e64aa6f0fc66b69a293a45ece"}, + {file = "tomli-2.4.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:c742f741d58a28940ce01d58f0ab2ea3ced8b12402f162f4d534dfe18ba1cd6a"}, + {file = "tomli-2.4.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:7f86fd587c4ed9dd76f318225e7d9b29cfc5a9d43de44e5754db8d1128487085"}, + {file = "tomli-2.4.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ff18e6a727ee0ab0388507b89d1bc6a22b138d1e2fa56d1ad494586d61d2eae9"}, + {file = "tomli-2.4.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:136443dbd7e1dee43c68ac2694fde36b2849865fa258d39bf822c10e8068eac5"}, + {file = "tomli-2.4.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:5e262d41726bc187e69af7825504c933b6794dc3fbd5945e41a79bb14c31f585"}, + {file = "tomli-2.4.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:5cb41aa38891e073ee49d55fbc7839cfdb2bc0e600add13874d048c94aadddd1"}, + {file = "tomli-2.4.1-cp312-cp312-win32.whl", hash = "sha256:da25dc3563bff5965356133435b757a795a17b17d01dbc0f42fb32447ddfd917"}, + {file = "tomli-2.4.1-cp312-cp312-win_amd64.whl", hash = "sha256:52c8ef851d9a240f11a88c003eacb03c31fc1c9c4ec64a99a0f922b93874fda9"}, + {file = "tomli-2.4.1-cp312-cp312-win_arm64.whl", hash = "sha256:f758f1b9299d059cc3f6546ae2af89670cb1c4d48ea29c3cacc4fe7de3058257"}, + {file = "tomli-2.4.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:36d2bd2ad5fb9eaddba5226aa02c8ec3fa4f192631e347b3ed28186d43be6b54"}, + {file = "tomli-2.4.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:eb0dc4e38e6a1fd579e5d50369aa2e10acfc9cace504579b2faabb478e76941a"}, + {file = "tomli-2.4.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c7f2c7f2b9ca6bdeef8f0fa897f8e05085923eb091721675170254cbc5b02897"}, + {file = "tomli-2.4.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f3c6818a1a86dd6dca7ddcaaf76947d5ba31aecc28cb1b67009a5877c9a64f3f"}, + {file = "tomli-2.4.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:d312ef37c91508b0ab2cee7da26ec0b3ed2f03ce12bd87a588d771ae15dcf82d"}, + {file = "tomli-2.4.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:51529d40e3ca50046d7606fa99ce3956a617f9b36380da3b7f0dd3dd28e68cb5"}, + {file = "tomli-2.4.1-cp313-cp313-win32.whl", hash = "sha256:2190f2e9dd7508d2a90ded5ed369255980a1bcdd58e52f7fe24b8162bf9fedbd"}, + {file = "tomli-2.4.1-cp313-cp313-win_amd64.whl", hash = "sha256:8d65a2fbf9d2f8352685bc1364177ee3923d6baf5e7f43ea4959d7d8bc326a36"}, + {file = "tomli-2.4.1-cp313-cp313-win_arm64.whl", hash = "sha256:4b605484e43cdc43f0954ddae319fb75f04cc10dd80d830540060ee7cd0243cd"}, + {file = "tomli-2.4.1-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:fd0409a3653af6c147209d267a0e4243f0ae46b011aa978b1080359fddc9b6cf"}, + {file = "tomli-2.4.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:a120733b01c45e9a0c34aeef92bf0cf1d56cfe81ed9d47d562f9ed591a9828ac"}, + {file = "tomli-2.4.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:559db847dc486944896521f68d8190be1c9e719fced785720d2216fe7022b662"}, + {file = "tomli-2.4.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:01f520d4f53ef97964a240a035ec2a869fe1a37dde002b57ebc4417a27ccd853"}, + {file = "tomli-2.4.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:7f94b27a62cfad8496c8d2513e1a222dd446f095fca8987fceef261225538a15"}, + {file = "tomli-2.4.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:ede3e6487c5ef5d28634ba3f31f989030ad6af71edfb0055cbbd14189ff240ba"}, + {file = "tomli-2.4.1-cp314-cp314-win32.whl", hash = "sha256:3d48a93ee1c9b79c04bb38772ee1b64dcf18ff43085896ea460ca8dec96f35f6"}, + {file = "tomli-2.4.1-cp314-cp314-win_amd64.whl", hash = "sha256:88dceee75c2c63af144e456745e10101eb67361050196b0b6af5d717254dddf7"}, + {file = "tomli-2.4.1-cp314-cp314-win_arm64.whl", hash = "sha256:b8c198f8c1805dc42708689ed6864951fd2494f924149d3e4bce7710f8eb5232"}, + {file = "tomli-2.4.1-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:d4d8fe59808a54658fcc0160ecfb1b30f9089906c50b23bcb4c69eddc19ec2b4"}, + {file = "tomli-2.4.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:7008df2e7655c495dd12d2a4ad038ff878d4ca4b81fccaf82b714e07eae4402c"}, + {file = "tomli-2.4.1-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1d8591993e228b0c930c4bb0db464bdad97b3289fb981255d6c9a41aedc84b2d"}, + {file = "tomli-2.4.1-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:734e20b57ba95624ecf1841e72b53f6e186355e216e5412de414e3c51e5e3c41"}, + {file = "tomli-2.4.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:8a650c2dbafa08d42e51ba0b62740dae4ecb9338eefa093aa5c78ceb546fcd5c"}, + {file = "tomli-2.4.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:504aa796fe0569bb43171066009ead363de03675276d2d121ac1a4572397870f"}, + {file = "tomli-2.4.1-cp314-cp314t-win32.whl", hash = "sha256:b1d22e6e9387bf4739fbe23bfa80e93f6b0373a7f1b96c6227c32bef95a4d7a8"}, + {file = "tomli-2.4.1-cp314-cp314t-win_amd64.whl", hash = "sha256:2c1c351919aca02858f740c6d33adea0c5deea37f9ecca1cc1ef9e884a619d26"}, + {file = "tomli-2.4.1-cp314-cp314t-win_arm64.whl", hash = "sha256:eab21f45c7f66c13f2a9e0e1535309cee140182a9cdae1e041d02e47291e8396"}, + {file = "tomli-2.4.1-py3-none-any.whl", hash = "sha256:0d85819802132122da43cb86656f8d1f8c6587d54ae7dcaf30e90533028b49fe"}, + {file = "tomli-2.4.1.tar.gz", hash = "sha256:7c7e1a961a0b2f2472c1ac5b69affa0ae1132c39adcb67aba98568702b9cc23f"}, +] + +[[package]] +name = "typing-extensions" +version = "4.15.0" +description = "Backported and Experimental Type Hints for Python 3.9+" +optional = true +python-versions = ">=3.9" +groups = ["main"] +markers = "extra == \"dev\"" +files = [ + {file = "typing_extensions-4.15.0-py3-none-any.whl", hash = "sha256:f0fa19c6845758ab08074a0cfa8b7aecb71c999ca73d62883bc25cc018c4e548"}, + {file = "typing_extensions-4.15.0.tar.gz", hash = "sha256:0cea48d173cc12fa28ecabc3b837ea3cf6f38c6d1136f85cbaaf598984861466"}, +] + +[[package]] +name = "urllib3" +version = "2.6.3" +description = "HTTP library with thread-safe connection pooling, file post, and more." +optional = true +python-versions = ">=3.9" +groups = ["main"] +markers = "extra == \"dev\"" +files = [ + {file = "urllib3-2.6.3-py3-none-any.whl", hash = "sha256:bf272323e553dfb2e87d9bfd225ca7b0f467b919d7bbd355436d3fd37cb0acd4"}, + {file = "urllib3-2.6.3.tar.gz", hash = "sha256:1b62b6884944a57dbe321509ab94fd4d3b307075e0c2eae991ac71ee15ad38ed"}, +] + +[package.extras] +brotli = ["brotli (>=1.2.0) ; platform_python_implementation == \"CPython\"", "brotlicffi (>=1.2.0.0) ; platform_python_implementation != \"CPython\""] +h2 = ["h2 (>=4,<5)"] +socks = ["pysocks (>=1.5.6,!=1.5.7,<2.0)"] +zstd = ["backports-zstd (>=1.0.0) ; python_version < \"3.14\""] + +[[package]] +name = "virtualenv" +version = "21.2.0" +description = "Virtual Python Environment builder" +optional = true +python-versions = ">=3.8" +groups = ["main"] +markers = "extra == \"dev\"" +files = [ + {file = "virtualenv-21.2.0-py3-none-any.whl", hash = "sha256:1bd755b504931164a5a496d217c014d098426cddc79363ad66ac78125f9d908f"}, + {file = "virtualenv-21.2.0.tar.gz", hash = "sha256:1720dc3a62ef5b443092e3f499228599045d7fea4c79199770499df8becf9098"}, +] + +[package.dependencies] +distlib = ">=0.3.7,<1" +filelock = {version = ">=3.24.2,<4", markers = "python_version >= \"3.10\""} +platformdirs = ">=3.9.1,<5" +python-discovery = ">=1" +typing-extensions = {version = ">=4.13.2", markers = "python_version < \"3.11\""} + +[[package]] +name = "watchdog" +version = "6.0.0" +description = "Filesystem events monitoring" +optional = true +python-versions = ">=3.9" +groups = ["main"] +markers = "extra == \"dev\"" +files = [ + {file = "watchdog-6.0.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:d1cdb490583ebd691c012b3d6dae011000fe42edb7a82ece80965b42abd61f26"}, + {file = "watchdog-6.0.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:bc64ab3bdb6a04d69d4023b29422170b74681784ffb9463ed4870cf2f3e66112"}, + {file = "watchdog-6.0.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c897ac1b55c5a1461e16dae288d22bb2e412ba9807df8397a635d88f671d36c3"}, + {file = "watchdog-6.0.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:6eb11feb5a0d452ee41f824e271ca311a09e250441c262ca2fd7ebcf2461a06c"}, + {file = "watchdog-6.0.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:ef810fbf7b781a5a593894e4f439773830bdecb885e6880d957d5b9382a960d2"}, + {file = "watchdog-6.0.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:afd0fe1b2270917c5e23c2a65ce50c2a4abb63daafb0d419fde368e272a76b7c"}, + {file = "watchdog-6.0.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:bdd4e6f14b8b18c334febb9c4425a878a2ac20efd1e0b231978e7b150f92a948"}, + {file = "watchdog-6.0.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:c7c15dda13c4eb00d6fb6fc508b3c0ed88b9d5d374056b239c4ad1611125c860"}, + {file = "watchdog-6.0.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:6f10cb2d5902447c7d0da897e2c6768bca89174d0c6e1e30abec5421af97a5b0"}, + {file = "watchdog-6.0.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:490ab2ef84f11129844c23fb14ecf30ef3d8a6abafd3754a6f75ca1e6654136c"}, + {file = "watchdog-6.0.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:76aae96b00ae814b181bb25b1b98076d5fc84e8a53cd8885a318b42b6d3a5134"}, + {file = "watchdog-6.0.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:a175f755fc2279e0b7312c0035d52e27211a5bc39719dd529625b1930917345b"}, + {file = "watchdog-6.0.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:e6f0e77c9417e7cd62af82529b10563db3423625c5fce018430b249bf977f9e8"}, + {file = "watchdog-6.0.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:90c8e78f3b94014f7aaae121e6b909674df5b46ec24d6bebc45c44c56729af2a"}, + {file = "watchdog-6.0.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:e7631a77ffb1f7d2eefa4445ebbee491c720a5661ddf6df3498ebecae5ed375c"}, + {file = "watchdog-6.0.0-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:c7ac31a19f4545dd92fc25d200694098f42c9a8e391bc00bdd362c5736dbf881"}, + {file = "watchdog-6.0.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:9513f27a1a582d9808cf21a07dae516f0fab1cf2d7683a742c498b93eedabb11"}, + {file = "watchdog-6.0.0-pp39-pypy39_pp73-macosx_10_15_x86_64.whl", hash = "sha256:7a0e56874cfbc4b9b05c60c8a1926fedf56324bb08cfbc188969777940aef3aa"}, + {file = "watchdog-6.0.0-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:e6439e374fc012255b4ec786ae3c4bc838cd7309a540e5fe0952d03687d8804e"}, + {file = "watchdog-6.0.0-py3-none-manylinux2014_aarch64.whl", hash = "sha256:7607498efa04a3542ae3e05e64da8202e58159aa1fa4acddf7678d34a35d4f13"}, + {file = "watchdog-6.0.0-py3-none-manylinux2014_armv7l.whl", hash = "sha256:9041567ee8953024c83343288ccc458fd0a2d811d6a0fd68c4c22609e3490379"}, + {file = "watchdog-6.0.0-py3-none-manylinux2014_i686.whl", hash = "sha256:82dc3e3143c7e38ec49d61af98d6558288c415eac98486a5c581726e0737c00e"}, + {file = "watchdog-6.0.0-py3-none-manylinux2014_ppc64.whl", hash = "sha256:212ac9b8bf1161dc91bd09c048048a95ca3a4c4f5e5d4a7d1b1a7d5752a7f96f"}, + {file = "watchdog-6.0.0-py3-none-manylinux2014_ppc64le.whl", hash = "sha256:e3df4cbb9a450c6d49318f6d14f4bbc80d763fa587ba46ec86f99f9e6876bb26"}, + {file = "watchdog-6.0.0-py3-none-manylinux2014_s390x.whl", hash = "sha256:2cce7cfc2008eb51feb6aab51251fd79b85d9894e98ba847408f662b3395ca3c"}, + {file = "watchdog-6.0.0-py3-none-manylinux2014_x86_64.whl", hash = "sha256:20ffe5b202af80ab4266dcd3e91aae72bf2da48c0d33bdb15c66658e685e94e2"}, + {file = "watchdog-6.0.0-py3-none-win32.whl", hash = "sha256:07df1fdd701c5d4c8e55ef6cf55b8f0120fe1aef7ef39a1c6fc6bc2e606d517a"}, + {file = "watchdog-6.0.0-py3-none-win_amd64.whl", hash = "sha256:cbafb470cf848d93b5d013e2ecb245d4aa1c8fd0504e863ccefa32445359d680"}, + {file = "watchdog-6.0.0-py3-none-win_ia64.whl", hash = "sha256:a1914259fa9e1454315171103c6a30961236f508b9b623eae470268bbcc6a22f"}, + {file = "watchdog-6.0.0.tar.gz", hash = "sha256:9ddf7c82fda3ae8e24decda1338ede66e1c99883db93711d8fb941eaa2d8c282"}, +] + +[package.extras] +watchmedo = ["PyYAML (>=3.10)"] + +[extras] +dev = ["black", "mkdocs", "mkdocs-material", "mkdocstrings", "mypy", "pre-commit", "pytest", "pytest-cov", "requests", "ruff"] + +[metadata] +lock-version = "2.1" +python-versions = ">=3.10" +content-hash = "ff21e1f94e8f320b2bd160a02755b14419dfc60f9fb136f3adaed91432831fde" diff --git a/pyproject.toml b/pyproject.toml index b37b810d..67325e3b 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,34 +1,36 @@ -[tool.poetry] -name = "embit" -version = "0.8.0" -description="A minimal bitcoin library for MicroPython and Python3 with a focus on embedded systems." -license="MIT" -authors= ["Stepan Snigirev "] - -[tool.poetry.urls] -repository = "https://github.com/diybitcoinhardware/embit" - -[tool.poetry.dependencies] -python = "^3.0" - [build-system] -requires = ["setuptools>=42.0", "wheel"] +requires = [ + "setuptools==80.9.0", + "wheel==0.45.1", +] build-backend = "setuptools.build_meta" [project] name = "embit" version = "0.8.0" -description="A minimal bitcoin library for MicroPython and Python3 with a focus on embedded systems." +description = "A minimal bitcoin library for MicroPython and CPython 3.10+ with a focus on embedded systems." readme = "README.md" +requires-python = ">=3.10" authors = [ {name = "Stepan Snigirev", email = "snigirev.stepan@gmail.com"}, ] classifiers = [ "Programming Language :: Python :: 3", + "Programming Language :: Python :: 3.10", + "Programming Language :: Python :: 3.11", + "Programming Language :: Python :: 3.12", + "Programming Language :: Python :: 3.13", "License :: OSI Approved :: MIT License", "Operating System :: OS Independent", ] +[project.urls] +Source = "https://github.com/diybitcoinhardware/embit" +Issues = "https://github.com/diybitcoinhardware/embit/issues" +Changelog = "https://github.com/diybitcoinhardware/embit/releases" +Security = "https://github.com/diybitcoinhardware/embit/security" +Documentation = "https://github.com/diybitcoinhardware/embit/tree/master/docs" + [project.optional-dependencies] dev = [ "pytest", @@ -36,17 +38,42 @@ dev = [ "requests", # for integration tests "mypy", "black", + "ruff", "pre-commit", "mkdocs", "mkdocs-material", "mkdocstrings[python]", ] +[tool.ruff] +line-length = 88 + +[tool.ruff.lint] +select = ["E", "F", "B", "S"] + [tool.setuptools.packages.find] where = ["src"] -[tool.setuptools.package-data] -embit = ["util/prebuilt/*"] +[tool.setuptools] +include-package-data = false + +[tool.setuptools.exclude-package-data] +"*" = [ + "*.so", + "*.dll", + "*.dylib", + "*.pth", + "sitecustomize.py", + "usercustomize.py", + "*.egg", + "*.whl", + "*.tar.gz", + "*.egg-info/*", + "*.dist-info/*", +] +embit = [ + "util/prebuilt/*", +] [tool.pytest.ini_options] @@ -74,4 +101,3 @@ warn_return_any = true warn_unreachable = true warn_unused_configs = true no_implicit_reexport = false - diff --git a/secp256k1/README.md b/secp256k1/README.md index 64f86016..502daa47 100644 --- a/secp256k1/README.md +++ b/secp256k1/README.md @@ -1,5 +1,6 @@ # Building secp256k1 for `embit` -If you don't want to use a prebuilt binary packaged with `embit`, you can build it yourself. +`embit` PyPI artifacts do not include prebuilt `libsecp256k1` binaries. +If you want the optional ctypes backend, build and install `libsecp256k1` locally. ## Clone `embit` recursively @@ -21,7 +22,10 @@ On your target platform run: make ``` -Copy the resulting binary from the `build/` subdir to `embit`'s `src/embit/util/prebuilt` directory. +Install the resulting library into a standard system library location so the dynamic loader can find it (for example `/usr/local/lib` on Unix-like systems), then refresh your linker environment as needed for your platform. + +Note: runtime lookup prefers local `secp256k1/secp256k1-zkp/.libs` and system library paths. +`src/embit/util/prebuilt` is still checked last for compatibility with older local setups, but binaries are not shipped by this project. To clean build directory use: ```shell diff --git a/src/embit/util/ctypes_secp256k1.py b/src/embit/util/ctypes_secp256k1.py index 8068355e..5ce2b6c3 100644 --- a/src/embit/util/ctypes_secp256k1.py +++ b/src/embit/util/ctypes_secp256k1.py @@ -1,23 +1,20 @@ -import ctypes, os +import ctypes +import os import ctypes.util import platform import threading -from ctypes import ( - cast, - byref, - c_char, - c_byte, - c_int, - c_uint, - c_char_p, - c_size_t, - c_void_p, - c_uint64, - create_string_buffer, - CFUNCTYPE, - POINTER, -) +cast = ctypes.cast +byref = ctypes.byref +c_char = ctypes.c_char +c_int = ctypes.c_int +c_uint = ctypes.c_uint +c_char_p = ctypes.c_char_p +c_size_t = ctypes.c_size_t +c_void_p = ctypes.c_void_p +c_uint64 = ctypes.c_uint64 +CFUNCTYPE = ctypes.CFUNCTYPE +POINTER = ctypes.POINTER _lock = threading.Lock() @@ -48,48 +45,128 @@ def _copy(a: bytes) -> bytes: return a[:1] + a[1:] -def _find_library(): - library_path = None - extension = "" - if platform.system() == "Darwin": - extension = ".dylib" - elif platform.system() == "Linux": - extension = ".so" - elif platform.system() == "Windows": - extension = ".dll" - - path = os.path.join( - os.path.dirname(__file__), - "prebuilt/libsecp256k1_%s_%s%s" - % (platform.system().lower(), platform.machine().lower(), extension), +def _platform_binary_ext(): + system = platform.system().lower() + if system.startswith("msys") or system.startswith("mingw") or system == "windows": + return ".dll" + if system == "darwin": + return ".dylib" + return ".so" + + +def _iter_local_library_paths(): + local_lib_dir = os.path.abspath( + os.path.join( + os.path.dirname(__file__), + "..", + "..", + "..", + "secp256k1", + "secp256k1-zkp", + ".libs", + ) ) - if os.path.isfile(path): - return path - # try searching - if not library_path: - library_path = ctypes.util.find_library("libsecp256k1") - if not library_path: - library_path = ctypes.util.find_library("secp256k1") - # library search failed - if not library_path: - if platform.system() == "Linux" and os.path.isfile( - "/usr/local/lib/libsecp256k1.so.0" - ): - library_path = "/usr/local/lib/libsecp256k1.so.0" - return library_path + for libname in ( + "libsecp256k1.dylib", + "libsecp256k1.0.dylib", + "libsecp256k1.so", + "libsecp256k1.so.0", + "libsecp256k1.dll", + ): + yield os.path.join(local_lib_dir, libname) + + +def _iter_prebuilt_library_paths(): + prebuilt_dir = os.path.join(os.path.dirname(__file__), "prebuilt") + system = platform.system().lower() + machine = platform.machine().lower() + if system.startswith("msys") or system.startswith("mingw"): + system = "windows" + ext = _platform_binary_ext() + for libname in ( + f"libsecp256k1_{system}_{machine}{ext}", + f"libsecp256k1{ext}", + "libsecp256k1.0.dylib", + "libsecp256k1.so.0", + ): + yield os.path.join(prebuilt_dir, libname) + + +def _library_search_order(): + """ + Ordered candidates for auditability: + 1) repo-local secp256k1-zkp/.libs artifacts + 2) system loader query: libsecp256k1 + 3) system loader query: secp256k1 + 4) in-tree prebuilt artifacts + """ + for path in _iter_local_library_paths(): + yield ("path", path) + yield ("system", "libsecp256k1") + yield ("system", "secp256k1") + for path in _iter_prebuilt_library_paths(): + yield ("path", path) + + +def _iter_resolved_library_candidates(): + seen = set() + for candidate_type, candidate in _library_search_order(): + if candidate_type == "path": + if not os.path.isfile(candidate): + continue + resolved = candidate + else: + resolved = ctypes.util.find_library(candidate) + if not resolved: + continue + if resolved in seen: + continue + seen.add(resolved) + yield resolved @locked def _init(flags=(CONTEXT_SIGN | CONTEXT_VERIFY)): - library_path = _find_library() - # meh, can't find library - if not library_path: + preferred_symbols = ( + "secp256k1_ecdh", + "secp256k1_xonly_pubkey_from_pubkey", + "secp256k1_keypair_create", + "secp256k1_ecdsa_sign_recoverable", + "secp256k1_generator_generate_blinded", + "secp256k1_pedersen_commitment_parse", + "secp256k1_rangeproof_rewind", + "secp256k1_surjectionproof_generate", + ) + + fallback = None + secp256k1 = None + + for candidate in _iter_resolved_library_candidates(): + try: + loaded = ctypes.cdll.LoadLibrary(candidate) + except OSError: + continue + has_preferred = True + for symbol in preferred_symbols: + try: + getattr(loaded, symbol) + except AttributeError: + has_preferred = False + break + if has_preferred: + secp256k1 = loaded + break + if fallback is None: + fallback = loaded + + if secp256k1 is None and fallback is not None: + secp256k1 = fallback + + if secp256k1 is None: raise RuntimeError( "Can't find libsecp256k1 library. Make sure to compile and install it." ) - secp256k1 = ctypes.cdll.LoadLibrary(library_path) - secp256k1.secp256k1_context_create.argtypes = [c_uint] secp256k1.secp256k1_context_create.restype = c_void_p @@ -194,7 +271,7 @@ def _init(flags=(CONTEXT_SIGN | CONTEXT_VERIFY)): c_void_p, # data ] secp256k1.secp256k1_ecdh.restype = c_int - except: + except Exception: # noqa: S110 pass # schnorr sig @@ -231,7 +308,7 @@ def _init(flags=(CONTEXT_SIGN | CONTEXT_VERIFY)): c_char_p, # secret ] secp256k1.secp256k1_keypair_create.restype = c_int - except: + except Exception: # noqa: S110 pass # recoverable module @@ -278,7 +355,7 @@ def _init(flags=(CONTEXT_SIGN | CONTEXT_VERIFY)): c_char_p, ] secp256k1.secp256k1_ecdsa_recover.restype = c_int - except: + except Exception: # noqa: S110 pass # zkp modules @@ -467,12 +544,12 @@ def _init(flags=(CONTEXT_SIGN | CONTEXT_VERIFY)): ] secp256k1.secp256k1_surjectionproof_parse.restype = c_int - except: + except Exception: # noqa: S110 pass secp256k1.ctx = secp256k1.secp256k1_context_create(flags) - r = secp256k1.secp256k1_context_randomize(secp256k1.ctx, os.urandom(32)) + secp256k1.secp256k1_context_randomize(secp256k1.ctx, os.urandom(32)) return secp256k1 @@ -579,7 +656,7 @@ def ecdsa_signature_normalize(sig, context=_secp.ctx): if len(sig) != 64: raise ValueError("Signature should be 64 bytes long") sig2 = bytes(64) - r = _secp.secp256k1_ecdsa_signature_normalize(context, sig2, sig) + _secp.secp256k1_ecdsa_signature_normalize(context, sig2, sig) return sig2 @@ -643,7 +720,6 @@ def ec_pubkey_negate(pubkey, context=_secp.ctx): def ec_privkey_tweak_add(secret, tweak, context=_secp.ctx): if len(secret) != 32 or len(tweak) != 32: raise ValueError("Secret and tweak should both be 32 bytes long") - t = _copy(tweak) if _secp.secp256k1_ec_privkey_tweak_add(context, secret, tweak) == 0: raise ValueError("Failed to tweak the secret") return None @@ -655,7 +731,6 @@ def ec_pubkey_tweak_add(pub, tweak, context=_secp.ctx): raise ValueError("Public key should be 64 bytes long") if len(tweak) != 32: raise ValueError("Tweak should be 32 bytes long") - t = _copy(tweak) if _secp.secp256k1_ec_pubkey_tweak_add(context, pub, tweak) == 0: raise ValueError("Failed to tweak the public key") return None @@ -730,7 +805,7 @@ def _hashfn(out, x, y): y = ctypes.string_at(y, 32) try: res = hashfn(x, y, data) - except Exception as e: + except Exception: return 0 out = cast(out, POINTER(c_char * 32)) out.contents.value = res diff --git a/src/embit/util/prebuilt/libsecp256k1_darwin_arm64.dylib b/src/embit/util/prebuilt/libsecp256k1_darwin_arm64.dylib deleted file mode 100755 index 5c5726c3..00000000 Binary files a/src/embit/util/prebuilt/libsecp256k1_darwin_arm64.dylib and /dev/null differ diff --git a/src/embit/util/prebuilt/libsecp256k1_darwin_x86_64.dylib b/src/embit/util/prebuilt/libsecp256k1_darwin_x86_64.dylib deleted file mode 100644 index c1796b4f..00000000 Binary files a/src/embit/util/prebuilt/libsecp256k1_darwin_x86_64.dylib and /dev/null differ diff --git a/src/embit/util/prebuilt/libsecp256k1_linux_aarch64.so b/src/embit/util/prebuilt/libsecp256k1_linux_aarch64.so deleted file mode 100644 index bcd0b94e..00000000 Binary files a/src/embit/util/prebuilt/libsecp256k1_linux_aarch64.so and /dev/null differ diff --git a/src/embit/util/prebuilt/libsecp256k1_linux_armv6l.so b/src/embit/util/prebuilt/libsecp256k1_linux_armv6l.so deleted file mode 100644 index 7b29ec5c..00000000 Binary files a/src/embit/util/prebuilt/libsecp256k1_linux_armv6l.so and /dev/null differ diff --git a/src/embit/util/prebuilt/libsecp256k1_linux_armv7l.so b/src/embit/util/prebuilt/libsecp256k1_linux_armv7l.so deleted file mode 100644 index 7b29ec5c..00000000 Binary files a/src/embit/util/prebuilt/libsecp256k1_linux_armv7l.so and /dev/null differ diff --git a/src/embit/util/prebuilt/libsecp256k1_linux_x86_64.so b/src/embit/util/prebuilt/libsecp256k1_linux_x86_64.so deleted file mode 100644 index f5aa398c..00000000 Binary files a/src/embit/util/prebuilt/libsecp256k1_linux_x86_64.so and /dev/null differ diff --git a/src/embit/util/prebuilt/libsecp256k1_windows_amd64.dll b/src/embit/util/prebuilt/libsecp256k1_windows_amd64.dll deleted file mode 100644 index 4afeae25..00000000 Binary files a/src/embit/util/prebuilt/libsecp256k1_windows_amd64.dll and /dev/null differ diff --git a/src/embit/util/secp256k1.py b/src/embit/util/secp256k1.py index a3ed8d9a..a616bc64 100644 --- a/src/embit/util/secp256k1.py +++ b/src/embit/util/secp256k1.py @@ -1,12 +1,76 @@ +# ruff: noqa: F403, E722 try: # if it's micropython - from micropython import const + # `from micropython import const` is the discriminator: it raises ImportError on + # CPython, forcing the fallback branch. Without it, `from secp256k1 import *` + # can resolve to the repo-local `secp256k1/` libsecp256k1 build directory as an + # implicit namespace package and silently succeed with an empty namespace. + from micropython import const # noqa: F401 from secp256k1 import * except: # we are in python try: # try ctypes bindings + from . import ctypes_secp256k1 as _ctypes_secp256k1 from .ctypes_secp256k1 import * + _secp = _ctypes_secp256k1._secp + + # For optional modules (ECDH/Schnorr/recovery/ZKP), keep ctypes when symbols + # are exported by the loaded library, otherwise use pure-Python fallbacks when + # available or hide the API so tests can skip capability-dependent paths. + from . import py_secp256k1 as _py_secp256k1 + + def _has_ctypes_symbol(symbol_name): + try: + getattr(_secp, symbol_name) + return True + except AttributeError: + return False + + _OPTIONAL_SYMBOLS = { + "ecdh": "secp256k1_ecdh", + "xonly_pubkey_from_pubkey": "secp256k1_xonly_pubkey_from_pubkey", + "schnorrsig_verify": "secp256k1_schnorrsig_verify", + "schnorrsig_sign": "secp256k1_schnorrsig_sign", + "keypair_create": "secp256k1_keypair_create", + "ecdsa_sign_recoverable": "secp256k1_ecdsa_sign_recoverable", + "ecdsa_recoverable_signature_parse_compact": ( + "secp256k1_ecdsa_recoverable_signature_parse_compact" + ), + "ecdsa_recoverable_signature_serialize_compact": ( + "secp256k1_ecdsa_recoverable_signature_serialize_compact" + ), + "ecdsa_recoverable_signature_convert": ( + "secp256k1_ecdsa_recoverable_signature_convert" + ), + "ecdsa_recover": "secp256k1_ecdsa_recover", + "generator_parse": "secp256k1_generator_parse", + "generator_generate": "secp256k1_generator_generate", + "generator_generate_blinded": "secp256k1_generator_generate_blinded", + "generator_serialize": "secp256k1_generator_serialize", + "pedersen_commitment_parse": "secp256k1_pedersen_commitment_parse", + "pedersen_commitment_serialize": "secp256k1_pedersen_commitment_serialize", + "pedersen_commit": "secp256k1_pedersen_commit", + "pedersen_blind_generator_blind_sum": ( + "secp256k1_pedersen_blind_generator_blind_sum" + ), + "pedersen_verify_tally": "secp256k1_pedersen_verify_tally", + "rangeproof_rewind": "secp256k1_rangeproof_rewind", + "rangeproof_verify": "secp256k1_rangeproof_verify", + "rangeproof_sign": "secp256k1_rangeproof_sign", + "surjectionproof_initialize": "secp256k1_surjectionproof_initialize", + "surjectionproof_generate": "secp256k1_surjectionproof_generate", + "surjectionproof_verify": "secp256k1_surjectionproof_verify", + "surjectionproof_serialize": "secp256k1_surjectionproof_serialize", + "surjectionproof_parse": "secp256k1_surjectionproof_parse", + } + for _name, _symbol in _OPTIONAL_SYMBOLS.items(): + if _has_ctypes_symbol(_symbol): + continue + if hasattr(_py_secp256k1, _name): + globals()[_name] = getattr(_py_secp256k1, _name) + elif _name in globals(): + del globals()[_name] except: # fallback to python version from .py_secp256k1 import * diff --git a/tests/tests/test_bindings.py b/tests/tests/test_bindings.py index 4dae0e43..d0cbc519 100644 --- a/tests/tests/test_bindings.py +++ b/tests/tests/test_bindings.py @@ -1,14 +1,64 @@ -from binascii import unhexlify, hexlify -from unittest import TestCase +from binascii import hexlify +from unittest import TestCase, skipUnless from embit.util import py_secp256k1 -from embit.util import ctypes_secp256k1 - +try: + from embit.util import ctypes_secp256k1 +except Exception: # pragma: no cover - environment dependent + ctypes_secp256k1 = None + print( + "[tests] libsecp256k1 ctypes backend unavailable; " + "skipping ctypes parity tests and using pure-Python fallback." + ) + _CTYPES_AVAILABLE = False +else: + _CTYPES_AVAILABLE = True + _CTYPES_SECP = getattr(ctypes_secp256k1, "_secp", None) + + +def _ctypes_has_symbol(name): + if not _CTYPES_AVAILABLE or _CTYPES_SECP is None: + return False + try: + getattr(_CTYPES_SECP, name) + return True + except AttributeError: + return False + + +_RECOVERY_AVAILABLE = all( + _ctypes_has_symbol(symbol) + for symbol in ( + "secp256k1_ecdsa_sign_recoverable", + "secp256k1_ecdsa_recoverable_signature_parse_compact", + "secp256k1_ecdsa_recoverable_signature_serialize_compact", + "secp256k1_ecdsa_recoverable_signature_convert", + "secp256k1_ecdsa_recover", + ) +) +_SCHNORR_AVAILABLE = all( + _ctypes_has_symbol(symbol) + for symbol in ( + "secp256k1_xonly_pubkey_from_pubkey", + "secp256k1_schnorrsig_verify", + "secp256k1_schnorrsig_sign", + "secp256k1_keypair_create", + ) +) + + +@skipUnless( + _CTYPES_AVAILABLE, + "libsecp256k1 ctypes backend unavailable; pure-Python fallback in use", +) class BindingsTest(TestCase): def test_identity(self): """1 * G""" for secp256k1 in [py_secp256k1, ctypes_secp256k1]: - answer = b"0479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8" + answer = ( + b"0479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f8179" + b"8483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8" + ) one = 1 bone = one.to_bytes(32, "big") g = secp256k1.ec_pubkey_create(bone) @@ -63,20 +113,10 @@ def test_cross(self): ctypes_secp256k1.ec_pubkey_add(pub1, b"9" * 32), ) - def test_recovery(self): - secret = b"1" * 32 - msg = b"2" * 32 - sig = ctypes_secp256k1.ecdsa_sign_recoverable(msg, secret) - sig2 = py_secp256k1.ecdsa_sign_recoverable(msg, secret) - self.assertEqual(sig, sig2) - - # signature (r,s) = (4,4), which can be recovered with all 4 recids. - sig = (b"\x04" + b"\x00" * 31) * 2 - for i in range(4): - pub = ctypes_secp256k1.ecdsa_recover(sig + bytes([i]), msg) - pub2 = py_secp256k1.ecdsa_recover(sig + bytes([i]), msg) - self.assertEqual(pub, pub2) - + @skipUnless( + _SCHNORR_AVAILABLE, + "ctypes backend missing schnorr symbols; skipping parity checks", + ) def test_schnorr(self): for i in range(1, 10): secret = bytes([i] * 32) @@ -112,3 +152,21 @@ def test_schnorr(self): ctypes_secp256k1.keypair_create(secret), py_secp256k1.keypair_create(secret), ) + + @skipUnless( + _RECOVERY_AVAILABLE, + "ctypes backend missing recovery symbols; skipping parity checks", + ) + def test_recovery(self): + secret = b"1" * 32 + msg = b"2" * 32 + sig = ctypes_secp256k1.ecdsa_sign_recoverable(msg, secret) + sig2 = py_secp256k1.ecdsa_sign_recoverable(msg, secret) + self.assertEqual(sig, sig2) + + # signature (r,s) = (4,4), which can be recovered with all 4 recids. + sig = (b"\x04" + b"\x00" * 31) * 2 + for i in range(4): + pub = ctypes_secp256k1.ecdsa_recover(sig + bytes([i]), msg) + pub2 = py_secp256k1.ecdsa_recover(sig + bytes([i]), msg) + self.assertEqual(pub, pub2) diff --git a/tests/tests/test_ecc.py b/tests/tests/test_ecc.py index bb842184..bcae9bc4 100644 --- a/tests/tests/test_ecc.py +++ b/tests/tests/test_ecc.py @@ -1,13 +1,27 @@ -from binascii import unhexlify, hexlify +from binascii import hexlify from unittest import TestCase +import sys from embit.ec import PublicKey, PrivateKey, Signature, secp256k1 from io import BytesIO +if sys.implementation.name == "micropython": + print("[tests] ECC backend: micropython integrated secp256k1") +elif hasattr(secp256k1, "_secp"): + library_path = getattr(secp256k1._secp, "_name", "") + print( + "[tests] ECC backend: system libsecp256k1 via ctypes (%s)" % library_path + ) +else: + print("[tests] ECC backend: pure-Python fallback (no system libsecp256k1)") + class ECCTest(TestCase): def test_identity(self): """1 * G""" - answer = b"0479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8" + answer = ( + b"0479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f8179" + b"8483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8" + ) one = 1 bone = one.to_bytes(32, "big") g = secp256k1.ec_pubkey_create(bone) @@ -34,7 +48,10 @@ def test_pubkeys(self): ( b"\x00" * 31 + b"\x01", False, - "0479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8", + ( + "0479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f8179" + "8483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8" + ), ), ] pub2 = PublicKey.from_string( @@ -68,8 +85,8 @@ def test_pubkeys(self): self.assertEqual(priv > pub, priv.sec() > pub.sec()) self.assertEqual(pub2 < pub, pub2 < priv) self.assertEqual(pub2 > pub, pub2 > priv) - priv == priv - pub == pub + self.assertEqual(priv, priv) + self.assertEqual(pub, pub) self.assertEqual(str(priv), priv.wif()) self.assertEqual(str(priv), priv.to_base58()) hash(priv) diff --git a/tests/tests/test_ecdh.py b/tests/tests/test_ecdh.py index 20396115..03ca6d84 100644 --- a/tests/tests/test_ecdh.py +++ b/tests/tests/test_ecdh.py @@ -1,8 +1,18 @@ -from unittest import TestCase -from embit.ec import PublicKey, PrivateKey +from unittest import TestCase, skipUnless +from embit.ec import PrivateKey, secp256k1 import hashlib +_ECDH_AVAILABLE = hasattr(secp256k1, "ecdh") +if not _ECDH_AVAILABLE: + print( + "[tests] ECDH backend unavailable; " + "skipping ECDH tests and using pure-Python fallback." + ) + +@skipUnless( + _ECDH_AVAILABLE, "ECDH backend unavailable; pure-Python fallback in use" +) class ECDHTest(TestCase): def test_one(self): """ECDH of privkey=1 and any pubkey should give sha256(pubkey)""" diff --git a/tests/tests/test_liquid.py b/tests/tests/test_liquid.py index 76f9f8b1..cc8a290b 100644 --- a/tests/tests/test_liquid.py +++ b/tests/tests/test_liquid.py @@ -1,16 +1,37 @@ -from unittest import TestCase +# ruff: noqa: E501 +from unittest import TestCase, skipUnless from embit.util import secp256k1 -from binascii import hexlify, unhexlify +from binascii import unhexlify from embit.liquid.pset import PSET -from embit.liquid.transaction import LTransaction, LTransactionInput, LTransactionOutput +from embit.liquid.transaction import LTransaction from embit.liquid import slip77 from embit.script import Script from embit.liquid.descriptor import LDescriptor +from embit.descriptor.errors import DescriptorError from embit.bip32 import HDKey from embit.ec import PrivateKey from embit.hashes import tagged_hash - +_LIQUID_REQUIRED_FUNCS = [ + "ec_pubkey_tweak_mul", + "ec_privkey_tweak_mul", + "generator_generate_blinded", + "pedersen_commit", + "rangeproof_rewind", + "surjectionproof_generate", +] +_LIQUID_AVAILABLE = all(hasattr(secp256k1, func) for func in _LIQUID_REQUIRED_FUNCS) +if not _LIQUID_AVAILABLE: + print( + "[tests] Liquid/ZKP backend unavailable; " + "skipping liquid tests and using pure-Python fallback." + ) + + +@skipUnless( + _LIQUID_AVAILABLE, + "Liquid/ZKP backend unavailable; pure-Python fallback in use", +) class LiquidTest(TestCase): def test_value_commitment(self): # scalars in little endian @@ -141,7 +162,7 @@ def test_descriptors(self): desc = LDescriptor.from_string(d) self.assertEqual(d, str(desc)) # test we can derive addresses - addr = desc.derive(0).address() + self.assertIsNotNone(desc.derive(0).address()) invalid_descs = [ # slip77 must be WIF key "blinded(slip77(xprvA18YC5Aog5LxHgMrSv5t9QaHyfh5DU8Pr8zFTP5QhJSTjdg3mSpEyxLZfNQaEc8sALUtsHeDJYsp8YnobhjJT9D7JADoEV4wXiMuNMYDLZ2),%s)" @@ -150,7 +171,7 @@ def test_descriptors(self): "blinded(L2t59TFgKmc83tPJD1rTy2KxJt44CMMQYsECXdz75xSqVv1X9Tvr,%s)" % multi, ] for d in invalid_descs: - with self.assertRaises(Exception): + with self.assertRaises((ValueError, RuntimeError, DescriptorError)): LDescriptor.from_string(d) def test_blind_unblind(self): diff --git a/tests/tests/test_threading.py b/tests/tests/test_threading.py index 34d6c44e..decf6c69 100644 --- a/tests/tests/test_threading.py +++ b/tests/tests/test_threading.py @@ -1,8 +1,10 @@ -from unittest import TestCase +# ruff: noqa: E501 +from unittest import TestCase, skipUnless from embit.ec import PrivateKey from embit.liquid.pset import PSET from embit.hashes import tagged_hash from embit.liquid import slip77 +from embit.util import secp256k1 import threading mbkey = PrivateKey.from_string("L2U2zGBgimb2vNee3bTw2y936PDJZXq3p7nMXEWuPP5MmpE1nCfv") @@ -12,6 +14,27 @@ REBLINDED = "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" + +_THREADING_REQUIRED_FUNCS = [ + "generator_generate_blinded", + "pedersen_commit", + "rangeproof_rewind", + "surjectionproof_generate", +] +_THREADING_AVAILABLE = all( + hasattr(secp256k1, func) for func in _THREADING_REQUIRED_FUNCS +) +if not _THREADING_AVAILABLE: + print( + "[tests] ZKP/threading backend unavailable; " + "skipping threading tests and using pure-Python fallback." + ) + + +@skipUnless( + _THREADING_AVAILABLE, + "ZKP/threading backend unavailable; pure-Python fallback in use", +) class ThreadingTest(TestCase): # run blind-unblind in a thread or multiple threads def unblind_thread(self, pset, i, unblinded=None, blinded=None, reblinded=None): diff --git a/tools/generate_release_sbom.py b/tools/generate_release_sbom.py new file mode 100644 index 00000000..92ad016d --- /dev/null +++ b/tools/generate_release_sbom.py @@ -0,0 +1,170 @@ +from __future__ import annotations + +import argparse +import hashlib +import json +import zipfile +from datetime import datetime, timezone +from pathlib import Path +from uuid import NAMESPACE_URL, uuid5 + +try: + import tomllib +except ModuleNotFoundError: + import tomli as tomllib + + +def parse_args() -> argparse.Namespace: + parser = argparse.ArgumentParser() + parser.add_argument("--dist-dir", default="dist") + parser.add_argument("--output", default="release-metadata/embit-sbom.cdx.json") + return parser.parse_args() + + +def latest_file(dist_dir: Path, pattern: str) -> Path: + matches = sorted(dist_dir.glob(pattern)) + if not matches: + raise SystemExit(f"no artifact found for pattern {pattern!r} in {dist_dir}") + return matches[-1] + + +def file_hash(path: Path) -> str: + digest = hashlib.sha256() + with path.open("rb") as f: + for chunk in iter(lambda: f.read(1024 * 1024), b""): + digest.update(chunk) + return digest.hexdigest() + + +def wheel_metadata(wheel: Path) -> dict[str, list[str] | str]: + with zipfile.ZipFile(wheel) as zf: + metadata_name = next( + name for name in zf.namelist() if name.endswith(".dist-info/METADATA") + ) + metadata = zf.read(metadata_name).decode("utf-8") + + fields: dict[str, list[str] | str] = {"requires_dist": []} + for line in metadata.splitlines(): + if line.startswith("Summary: "): + fields["summary"] = line.removeprefix("Summary: ") + elif line.startswith("License: "): + fields["license"] = line.removeprefix("License: ") + elif line.startswith("Requires-Dist: "): + if "extra ==" not in line: + raise SystemExit( + f"wheel contains unexpected runtime dependency metadata: {line}" + ) + casted = fields["requires_dist"] + if not isinstance(casted, list): + raise SystemExit("internal error: requires_dist must be a list") + casted.append(line.removeprefix("Requires-Dist: ")) + return fields + + +def external_reference(path: Path) -> dict: + digest = file_hash(path) + return { + "type": "distribution", + "url": f"file:dist/{path.name}", + "comment": f"SHA256: {digest}", + "hashes": [{"alg": "SHA-256", "content": digest}], + } + + +def read_pyproject() -> dict: + with Path("pyproject.toml").open("rb") as f: + return tomllib.load(f) + + +def build_sbom(*, wheel: Path, sdist: Path) -> dict: + pyproject = read_pyproject() + project = pyproject["project"] + metadata = wheel_metadata(wheel) + name = project["name"] + version = project["version"] + package_ref = f"pkg:pypi/{name}@{version}" + serial_source = f"{package_ref}:{file_hash(wheel)}:{file_hash(sdist)}" + serial_number = f"urn:uuid:{uuid5(NAMESPACE_URL, serial_source)}" + + component = { + "type": "library", + "bom-ref": package_ref, + "name": name, + "version": version, + "description": metadata.get("summary", project.get("description", "")), + "purl": package_ref, + "hashes": [ + { + "alg": "SHA-256", + "content": hashlib.sha256( + f"{file_hash(wheel)}\n{file_hash(sdist)}".encode("utf-8") + ).hexdigest(), + } + ], + "externalReferences": [ + external_reference(wheel), + external_reference(sdist), + *[ + {"type": "website", "url": url, "comment": label} + for label, url in sorted(project.get("urls", {}).items()) + ], + ], + "properties": [ + { + "name": "embit:requires-python", + "value": project["requires-python"], + }, + { + "name": "embit:runtime-dependencies", + "value": "none", + }, + ], + } + + license_text = metadata.get("license") + if isinstance(license_text, str) and license_text: + component["licenses"] = [{"license": {"name": license_text}}] + + return { + "$schema": "http://cyclonedx.org/schema/bom-1.5.schema.json", + "bomFormat": "CycloneDX", + "specVersion": "1.5", + "serialNumber": serial_number, + "version": 1, + "metadata": { + "timestamp": datetime.now(timezone.utc).replace(microsecond=0).isoformat(), + "tools": { + "components": [ + { + "type": "application", + "name": "tools/generate_release_sbom.py", + } + ] + }, + "component": component, + }, + "components": [], + "dependencies": [{"ref": package_ref, "dependsOn": []}], + } + + +def write_sbom(path: Path, sbom: dict) -> None: + path.parent.mkdir(parents=True, exist_ok=True) + with path.open("w", encoding="utf-8") as f: + json.dump(sbom, f, indent=2, sort_keys=True) + f.write("\n") + + +def main() -> None: + args = parse_args() + dist_dir = Path(args.dist_dir) + wheel = latest_file(dist_dir, "embit-*.whl") + sdist = latest_file(dist_dir, "embit-*.tar.gz") + output = Path(args.output) + + write_sbom(output, build_sbom(wheel=wheel, sdist=sdist)) + print(f"wrote {output}") + + +if __name__ == "__main__": + main() diff --git a/tools/package_inspect.py b/tools/package_inspect.py new file mode 100644 index 00000000..2b884301 --- /dev/null +++ b/tools/package_inspect.py @@ -0,0 +1,28 @@ +from pathlib import Path +import tarfile +import zipfile + +PACKAGE = "embit" + +wheel = sorted(Path("dist").glob(PACKAGE + "-*.whl"))[-1] +sdist = sorted(Path("dist").glob(PACKAGE + "-*.tar.gz"))[-1] + +with zipfile.ZipFile(wheel) as zf: + meta = [n for n in zf.namelist() if n.endswith(".dist-info/METADATA")][0] + data = zf.read(meta).decode() + +print(f"# PACKAGE CONTENT OF \"{PACKAGE}\"") +print("\n## METADATA\n") +for line in data.splitlines(): + if line.startswith(("Name:", "Version:", "Requires-Python:", "Project-URL:", "Requires-Dist:", "Provides-Extra:")): + print(line, " ") + +print("\n## SDIST FILE LIST\n") +with tarfile.open(sdist, "r:gz") as tf: + for member in tf.getmembers(): + print(member.name, " ") + +print("\n## WHEEL FILE LIST\n") +with zipfile.ZipFile(wheel) as zf: + for name in zf.namelist(): + print(name, " ") diff --git a/tools/verify_github_actions_pins.py b/tools/verify_github_actions_pins.py new file mode 100644 index 00000000..ff864451 --- /dev/null +++ b/tools/verify_github_actions_pins.py @@ -0,0 +1,224 @@ +#!/usr/bin/env python3 +"""Verify third-party GitHub Actions are pinned to full commit SHAs.""" + +from __future__ import annotations + +import argparse +import re +import sys +import tempfile +from dataclasses import dataclass +from pathlib import Path + + +FULL_SHA_RE = re.compile(r"^[0-9a-fA-F]{40}$") +USES_RE = re.compile(r"^\s*(?:-\s*)?uses:\s*(?P.+?)\s*$") +FIRST_PARTY_OWNERS = {"actions", "github"} + + +@dataclass(frozen=True) +class Finding: + path: Path + line: int + value: str + reason: str + + +def strip_inline_comment(value: str) -> str: + quote: str | None = None + escaped = False + for index, char in enumerate(value): + if escaped: + escaped = False + continue + if char == "\\": + escaped = True + continue + if quote: + if char == quote: + quote = None + continue + if char in {"'", '"'}: + quote = char + continue + if char == "#": + return value[:index].rstrip() + return value.strip() + + +def normalize_scalar(value: str) -> str: + value = strip_inline_comment(value) + if len(value) >= 2 and value[0] == value[-1] and value[0] in {"'", '"'}: + return value[1:-1] + return value + + +def is_out_of_scope(value: str) -> bool: + if value.startswith("./") or value.startswith("../") or value.startswith("/"): + return True + if value.startswith("docker://"): + return True + owner = value.split("/", 1)[0].lower() + return owner in FIRST_PARTY_OWNERS + + +def check_uses_value(value: str) -> str | None: + if is_out_of_scope(value): + return None + if "@" not in value: + return "missing ref" + _action, ref = value.rsplit("@", 1) + if not ref: + return "missing ref" + if not FULL_SHA_RE.fullmatch(ref): + return "ref is not a full 40-character commit SHA" + return None + + +def scan_file(path: Path) -> list[Finding]: + findings: list[Finding] = [] + with path.open("r", encoding="utf-8") as workflow: + for line_number, line in enumerate(workflow, start=1): + match = USES_RE.match(line) + if not match: + continue + value = normalize_scalar(match.group("value")) + if not value or value in {"|", ">"}: + findings.append( + Finding( + path, + line_number, + value or "", + "missing action reference", + ) + ) + continue + reason = check_uses_value(value) + if reason is not None: + findings.append(Finding(path, line_number, value, reason)) + return findings + + +def workflow_files(root: Path) -> list[Path]: + workflows_dir = root / ".github" / "workflows" + if not workflows_dir.exists(): + return [] + return sorted( + path + for pattern in ("*.yml", "*.yaml") + for path in workflows_dir.glob(pattern) + if path.is_file() + ) + + +def scan_repository(root: Path) -> list[Finding]: + findings: list[Finding] = [] + for path in workflow_files(root): + findings.extend(scan_file(path)) + return findings + + +def write_workflow(root: Path, name: str, uses_values: list[str]) -> None: + workflow_dir = root / ".github" / "workflows" + workflow_dir.mkdir(parents=True, exist_ok=True) + steps = "\n".join(f" - uses: {value}" for value in uses_values) + (workflow_dir / name).write_text( + "\n".join( + [ + "name: fixture", + "on: pull_request", + "jobs:", + " check:", + " runs-on: ubuntu-latest", + " steps:", + steps, + "", + ] + ), + encoding="utf-8", + ) + + +def run_self_test() -> int: + failing_values = [ + "thirdparty/action@v1", + "thirdparty/action@main", + "thirdparty/action@release/v1", + "thirdparty/action@1234567", + "thirdparty/action", + "thirdparty/action@${{ inputs.ref }}", + "thirdparty/action/path@v1", + ] + passing_values = [ + "thirdparty/action@0123456789abcdef0123456789abcdef01234567", + "thirdparty/action/path@0123456789abcdef0123456789abcdef01234567", + "actions/checkout@v4", + "github/codeql-action/init@v3", + "./.github/actions/local-action", + "docker://python:3.13-slim", + ] + with tempfile.TemporaryDirectory() as tmp: + root = Path(tmp) + write_workflow(root, "failing.yml", failing_values) + write_workflow(root, "passing.yaml", passing_values) + findings = scan_repository(root) + + found_values = {finding.value for finding in findings} + missing_failures = sorted(set(failing_values) - found_values) + unexpected_failures = sorted(found_values - set(failing_values)) + if missing_failures or unexpected_failures: + if missing_failures: + print("Self-test missed expected failures:", file=sys.stderr) + for value in missing_failures: + print(f" {value}", file=sys.stderr) + if unexpected_failures: + print("Self-test produced unexpected failures:", file=sys.stderr) + for value in unexpected_failures: + print(f" {value}", file=sys.stderr) + return 1 + print("GitHub Actions pin verifier self-test passed") + return 0 + + +def print_findings(findings: list[Finding]) -> None: + print( + "Third-party GitHub Actions must be pinned to full commit SHAs.", + file=sys.stderr, + ) + for finding in findings: + print( + f"{finding.path}:{finding.line}: {finding.value} ({finding.reason})", + file=sys.stderr, + ) + + +def main(argv: list[str] | None = None) -> int: + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument( + "--self-test", + action="store_true", + help="run fixture-based verifier tests before scanning the repository", + ) + parser.add_argument( + "--root", + type=Path, + default=Path.cwd(), + help="repository root to scan", + ) + args = parser.parse_args(argv) + + if args.self_test: + result = run_self_test() + if result != 0: + return result + + findings = scan_repository(args.root) + if findings: + print_findings(findings) + return 1 + print("GitHub Actions third-party pin policy passed") + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/tools/verify_package_artifacts.py b/tools/verify_package_artifacts.py new file mode 100644 index 00000000..f05579b1 --- /dev/null +++ b/tools/verify_package_artifacts.py @@ -0,0 +1,382 @@ +from __future__ import annotations + +import argparse +import email +import tarfile +import zipfile +from dataclasses import dataclass +from pathlib import Path +from typing import Iterable + +try: + import tomllib +except ModuleNotFoundError: + import tomli as tomllib + +from packaging.markers import Marker +from packaging.requirements import InvalidRequirement, Requirement +from packaging.utils import canonicalize_name + + +FORBIDDEN_SUFFIXES = ( + ".pth", + "sitecustomize.py", + "usercustomize.py", + ".so", + ".dylib", + ".dll", + ".tar.gz", + ".whl", + ".egg", +) + +FORBIDDEN_SUBSTRINGS = ( + "util/prebuilt/", +) + + +@dataclass(frozen=True) +class ArtifactMetadata: + headers: email.message.Message + entry_points: set[str] + label: str + + +def parse_args() -> argparse.Namespace: + parser = argparse.ArgumentParser() + parser.add_argument("--dist-dir", default="dist") + parser.add_argument( + "--inspection-log", + default=None, + help="Optional path to store a package inspect style report.", + ) + return parser.parse_args() + + +def latest_file(dist_dir: Path, pattern: str) -> Path: + matches = sorted(dist_dir.glob(pattern)) + if not matches: + raise SystemExit(f"no artifact found for pattern {pattern!r} in {dist_dir}") + return matches[-1] + + +def read_pyproject() -> dict: + with Path("pyproject.toml").open("rb") as f: + return tomllib.load(f) + + +def parse_requirement(requirement: str) -> Requirement: + try: + return Requirement(requirement) + except InvalidRequirement as exc: + raise SystemExit(f"could not parse requirement {requirement!r}: {exc}") from exc + + +def normalized_requirement(requirement: str | Requirement) -> str: + parsed = ( + parse_requirement(requirement) + if isinstance(requirement, str) + else requirement + ) + normalized = canonicalize_name(parsed.name) + if parsed.extras: + extras = ",".join(sorted(canonicalize_name(extra) for extra in parsed.extras)) + normalized += f"[{extras}]" + if parsed.url: + normalized += f" @ {parsed.url}" + normalized += str(parsed.specifier) + if parsed.marker is not None: + normalized += f"; {parsed.marker}" + return normalized + + +def requirement_with_extra(requirement: str, extra: str) -> Requirement: + parsed = parse_requirement(requirement) + extra_marker = f'extra == "{extra}"' + if parsed.marker is None: + parsed.marker = Marker(extra_marker) + else: + parsed.marker = Marker(f"({parsed.marker}) and {extra_marker}") + return parsed + + +def requirement_has_expected_extra_marker(requirement: str, extras: set[str]) -> bool: + marker = parse_requirement(requirement).marker + if marker is None: + return False + marker_text = str(marker) + return any(f'extra == "{extra}"' in marker_text for extra in extras) + + +def expected_metadata_from_pyproject(pyproject: dict) -> dict: + project = pyproject["project"] + optional_dependencies = { + canonicalize_name(extra): requirements + for extra, requirements in project.get("optional-dependencies", {}).items() + } + return { + "name": project["name"], + "version": project["version"], + "requires_python": project["requires-python"], + "classifiers": set(project.get("classifiers", [])), + "project_urls": { + f"{key}, {value}" for key, value in project.get("urls", {}).items() + }, + "extras": set(optional_dependencies), + "optional_requirements": { + normalized_requirement(requirement_with_extra(requirement, extra)) + for extra, requirements in optional_dependencies.items() + for requirement in requirements + }, + } + + +def parse_entry_points(contents: str) -> set[str]: + entries: set[str] = set() + section: str | None = None + for raw_line in contents.splitlines(): + line = raw_line.strip() + if not line or line.startswith("#"): + continue + if line.startswith("[") and line.endswith("]"): + section = line[1:-1].strip() + continue + if section is not None: + entries.add(f"{section}:{line}") + return entries + + +def inspect_sdist(sdist: Path, expected: dict) -> ArtifactMetadata: + with tarfile.open(sdist, "r:gz") as tf: + members = [member.name for member in tf.getmembers() if member.isfile()] + root_prefix = Path(members[0]).parts[0] + pkg_info_member = tf.extractfile(f"{root_prefix}/PKG-INFO") + if pkg_info_member is None: + raise SystemExit("sdist PKG-INFO missing") + metadata = email.message_from_bytes(pkg_info_member.read()) + try: + entry_points_member = tf.extractfile( + f"{root_prefix}/src/{expected['name']}.egg-info/entry_points.txt" + ) + except KeyError: + entry_points_member = None + entry_points = ( + parse_entry_points(entry_points_member.read().decode()) + if entry_points_member is not None + else set() + ) + verify_artifact_members( + label="sdist", + members=members, + allowed_dist_info_prefix=None, + allowed_egg_info_prefix=f"{root_prefix}/src/{expected['name']}.egg-info/", + ) + verify_metadata( + metadata=metadata, + entry_points=entry_points, + expected=expected, + label="sdist", + require_dependency_metadata=False, + ) + return ArtifactMetadata(headers=metadata, entry_points=entry_points, label="sdist") + + +def inspect_wheel(wheel: Path, expected: dict) -> ArtifactMetadata: + with zipfile.ZipFile(wheel) as zf: + members = zf.namelist() + metadata_name = next( + name for name in members if name.endswith(".dist-info/METADATA") + ) + metadata = email.message_from_bytes(zf.read(metadata_name)) + entry_points_name = next( + (name for name in members if name.endswith(".dist-info/entry_points.txt")), + None, + ) + entry_points = ( + parse_entry_points(zf.read(entry_points_name).decode()) + if entry_points_name is not None + else set() + ) + verify_artifact_members( + label="wheel", + members=members, + allowed_dist_info_prefix=( + f"{expected['name'].replace('-', '_')}-{expected['version']}.dist-info/" + ), + allowed_egg_info_prefix=None, + ) + verify_metadata( + metadata=metadata, + entry_points=entry_points, + expected=expected, + label="wheel", + require_dependency_metadata=True, + ) + return ArtifactMetadata(headers=metadata, entry_points=entry_points, label="wheel") + + +def verify_artifact_members( + *, + label: str, + members: Iterable[str], + allowed_dist_info_prefix: str | None, + allowed_egg_info_prefix: str | None, +) -> None: + for member in members: + normalized = member.lstrip("./") + if any(normalized.endswith(suffix) for suffix in FORBIDDEN_SUFFIXES): + raise SystemExit(f"{label} contains forbidden file: {member}") + for substring in FORBIDDEN_SUBSTRINGS: + if substring in normalized: + raise SystemExit(f"{label} contains forbidden path: {member}") + if ".dist-info/" in normalized and ( + allowed_dist_info_prefix is None + or not normalized.startswith(allowed_dist_info_prefix) + ): + raise SystemExit(f"{label} contains unexpected dist-info payload: {member}") + if ".egg-info/" in normalized and ( + allowed_egg_info_prefix is None + or not normalized.startswith(allowed_egg_info_prefix) + ): + raise SystemExit(f"{label} contains unexpected egg-info payload: {member}") + if normalized.endswith("/"): + continue + if normalized.startswith("embit-") and "/scripts/" in normalized: + raise SystemExit(f"{label} contains unexpected script payload: {member}") + + +def verify_metadata( + *, + metadata: email.message.Message, + entry_points: set[str], + expected: dict, + label: str, + require_dependency_metadata: bool, +) -> None: + if metadata.get("Name") != expected["name"]: + raise SystemExit(f"{label} name mismatch: {metadata.get('Name')!r}") + if metadata.get("Version") != expected["version"]: + raise SystemExit(f"{label} version mismatch: {metadata.get('Version')!r}") + if metadata.get("Requires-Python") != expected["requires_python"]: + raise SystemExit( + f"{label} requires-python mismatch: {metadata.get('Requires-Python')!r}" + ) + classifiers = set(metadata.get_all("Classifier", [])) + if classifiers != expected["classifiers"]: + raise SystemExit( + f"{label} classifiers mismatch: " + f"{sorted(classifiers)!r} != {sorted(expected['classifiers'])!r}" + ) + + project_urls = set(metadata.get_all("Project-URL", [])) + if project_urls != expected["project_urls"]: + raise SystemExit( + f"{label} project URLs mismatch: {sorted(project_urls)!r} != " + f"{sorted(expected['project_urls'])!r}" + ) + + extras = set(metadata.get_all("Provides-Extra", [])) + if require_dependency_metadata and extras != expected["extras"]: + raise SystemExit( + f"{label} extras mismatch: " + f"{sorted(extras)!r} != {sorted(expected['extras'])!r}" + ) + if not require_dependency_metadata and extras and extras != expected["extras"]: + raise SystemExit( + f"{label} extras mismatch: " + f"{sorted(extras)!r} != {sorted(expected['extras'])!r}" + ) + + requirement_headers = metadata.get_all("Requires-Dist", []) + requirements = {normalized_requirement(req) for req in requirement_headers} + if ( + require_dependency_metadata + and requirements != expected["optional_requirements"] + ): + raise SystemExit( + f"{label} dependency metadata mismatch: {sorted(requirements)!r} != " + f"{sorted(expected['optional_requirements'])!r}" + ) + if ( + not require_dependency_metadata + and requirements + and requirements != expected["optional_requirements"] + ): + raise SystemExit( + f"{label} dependency metadata mismatch: {sorted(requirements)!r} != " + f"{sorted(expected['optional_requirements'])!r}" + ) + for requirement in requirement_headers: + if not requirement_has_expected_extra_marker(requirement, expected["extras"]): + raise SystemExit( + f"{label} has unexpected non-extra dependency: {requirement}" + ) + + if entry_points: + raise SystemExit( + f"{label} contains unexpected entry points: {sorted(entry_points)!r}" + ) + + +def write_inspection_log(log_path: Path, *, wheel: Path, sdist: Path) -> None: + log_path.parent.mkdir(parents=True, exist_ok=True) + with log_path.open("w", encoding="utf-8") as f: + f.write(render_inspection_report(wheel=wheel, sdist=sdist)) + + +def render_inspection_report(*, wheel: Path, sdist: Path) -> str: + sections: list[str] = ['# PACKAGE CONTENT OF "embit"\n'] + + with zipfile.ZipFile(wheel) as zf: + metadata_name = next( + name for name in zf.namelist() if name.endswith(".dist-info/METADATA") + ) + data = zf.read(metadata_name).decode() + sections.append("\n## METADATA\n") + for line in data.splitlines(): + if line.startswith( + ( + "Name:", + "Version:", + "Requires-Python:", + "Classifier:", + "Project-URL:", + "Requires-Dist:", + "Provides-Extra:", + ) + ): + sections.append(f"{line} \n") + + sections.append("\n## SDIST FILE LIST\n") + with tarfile.open(sdist, "r:gz") as tf: + for member in tf.getmembers(): + sections.append(f"{member.name} \n") + + sections.append("\n## WHEEL FILE LIST\n") + with zipfile.ZipFile(wheel) as zf: + for name in zf.namelist(): + sections.append(f"{name} \n") + + return "".join(sections) + + +def main() -> None: + args = parse_args() + dist_dir = Path(args.dist_dir) + pyproject = read_pyproject() + expected = expected_metadata_from_pyproject(pyproject) + + wheel = latest_file(dist_dir, "embit-*.whl") + sdist = latest_file(dist_dir, "embit-*.tar.gz") + + inspect_wheel(wheel, expected) + inspect_sdist(sdist, expected) + + if args.inspection_log is not None: + write_inspection_log(Path(args.inspection_log), wheel=wheel, sdist=sdist) + + print(f"verified {wheel.name} and {sdist.name}") + + +if __name__ == "__main__": + main()