diff --git a/.github/actions/check-changes/action.yml b/.github/actions/check-changes/action.yml new file mode 100644 index 0000000..79c0cb7 --- /dev/null +++ b/.github/actions/check-changes/action.yml @@ -0,0 +1,52 @@ +name: Detect changed files. +description: Checks if any relevant files have changed in the current pull request or commit. Always returns true if running on the default branch, to ensure all changes are thoroughly checked. + +# The file filters are defined in `.github/change-filters.yml`. +# +# Requires +# ``` +# permissions: +# pull-requests: read +# ``` + +# Set job outputs to values from filter step +# These outputs are always true when running after a merge to main, or if the PR has a `run-ci-checks` label. +outputs: + extensions: + description: "The extension definitions have changed" + value: ${{ steps.override.outputs.out == 'true' || steps.change-filters.outputs.std-extensions == 'true' }} + rust: + description: "Rust files have changed" + value: ${{ steps.override.outputs.out == 'true' || steps.change-filters.outputs.rust == 'true' }} + rust-core: + description: "The main tket rust library has changed" + value: ${{ steps.override.outputs.out == 'true' || steps.change-filters.outputs.rust == 'true' }} + python: + description: "Python files have changed" + value: ${{ steps.override.outputs.out == 'true' || steps.change-filters.outputs.python == 'true' }} + +runs: + using: composite + # Check if changes were made to the relevant files. + # Always returns true if running on the default branch, to ensure all changes are thoroughly checked. + steps: + - name: Override label + shell: bash + id: override + run: | + echo "Label contains run-ci-checks: $OVERRIDE_LABEL" + if [ "$OVERRIDE_LABEL" == "true" ]; then + echo "Overriding due to label 'run-ci-checks'" + echo "out=true" >> $GITHUB_OUTPUT + elif [ "$DEFAULT_BRANCH" == "true" ]; then + echo "Overriding due to running on the default branch" + echo "out=true" >> $GITHUB_OUTPUT + fi + env: + OVERRIDE_LABEL: ${{ github.event_name == 'pull_request' && contains( github.event.pull_request.labels.*.name, 'S-run-thorough-ci-tests') }} + DEFAULT_BRANCH: ${{ github.ref_name == github.event.repository.default_branch }} + - if: steps.override.outputs.out != 'true' + uses: dorny/paths-filter@v3 + id: change-filters + with: + filters: .github/change-filters.yml diff --git a/.github/change-filters.yml b/.github/change-filters.yml index 771324e..5b2edad 100644 --- a/.github/change-filters.yml +++ b/.github/change-filters.yml @@ -10,12 +10,12 @@ rust: &rust - *schema - ".github/workflows/ci.yml" - "impl/rs/**" - - "impl/Cargo.toml" - - "impl/Cargo.lock" + - "Cargo.toml" + - "Cargo.lock" python: &python - *schema - ".github/workflows/ci.yml" - "impl/py/**" - - "impl/pyproject.toml" - - "impl/uv.lock" + - "pyproject.toml" + - "uv.lock" diff --git a/.github/workflows/ci-py.yml b/.github/workflows/ci-py.yml new file mode 100644 index 0000000..314a486 --- /dev/null +++ b/.github/workflows/ci-py.yml @@ -0,0 +1,131 @@ +name: Continuous integration 🐍 + +on: + push: + branches: + - main + pull_request: + branches: + - '**' + merge_group: + types: [checks_requested] + workflow_dispatch: {} + +env: + SCCACHE_GHA_ENABLED: "true" + # Pinned version for the uv package manager + UV_VERSION: "0.8.12" + UV_FROZEN: 1 + +jobs: + # Check if changes were made to the relevant files. + # Always returns true if running on the default branch, to ensure all changes are thoroughly checked. + changes: + name: Check for changes + runs-on: ubuntu-latest + permissions: + pull-requests: read + outputs: + python: ${{ steps.filter.outputs.python }} + steps: + - uses: actions/checkout@v5 + - uses: ./.github/actions/check-changes + id: filter + + check: + needs: changes + if: ${{ needs.changes.outputs.python == 'true' }} + + name: check python ${{ matrix.python-version }} + runs-on: ubuntu-latest + + strategy: + matrix: + python-version: ['3.10', '3.13'] + + steps: + - uses: actions/checkout@v5 + - name: Run sccache-cache + uses: mozilla-actions/sccache-action@v0.0.9 + + - name: Set up uv + uses: astral-sh/setup-uv@v6 + with: + version: ${{ env.UV_VERSION }} + enable-cache: true + - name: Setup dependencies. + run: uv sync --python ${{ matrix.python-version }} + + # TODO: Fix lints and re-enable + #- name: Type check with mypy + # run: uv run mypy . + + - name: Check formatting with ruff + run: uv run ruff format --check + + - name: Lint with ruff + run: uv run ruff check + + test: + needs: [changes] + if: ${{ needs.changes.outputs.python == 'true' }} + name: test python ${{ matrix.python-version.py }} + runs-on: ubuntu-latest + + strategy: + matrix: + python-version: + - { py: '3.10', coverage: false } + - { py: '3.13', coverage: true } + steps: + - uses: actions/checkout@v5 + + - name: Set up uv + uses: astral-sh/setup-uv@v6 + with: + version: ${{ env.UV_VERSION }} + enable-cache: true + + - name: Setup dependencies + run: uv sync --python ${{ matrix.python-version.py }} + + - name: Run tests + if: github.event_name == 'merge_group' || !matrix.python-version.coverage + run: | + uv run pytest + + - name: Run python tests with coverage instrumentation + if: github.event_name != 'merge_group' && matrix.python-version.coverage + run: | + uv run pytest --cov=./ --cov-report=xml + + - name: Upload python coverage to codecov.io + if: github.event_name != 'merge_group' && matrix.python-version.coverage + uses: codecov/codecov-action@v5 + with: + files: coverage.xml + name: python + flags: python + token: ${{ secrets.CODECOV_TOKEN }} + + # This is a meta job to mark successful completion of the required checks, + # even if they are skipped due to no changes in the relevant files. + required-checks: + name: Required checks 🐍 + needs: [ + changes, + check, + test, + ] + if: ${{ !cancelled() }} + runs-on: ubuntu-latest + steps: + - name: Fail if required checks failed + if: ${{ contains(needs.*.result, 'failure') || contains(needs.*.result, 'cancelled') }} + run: | + echo "Required checks failed" + echo "Please check the logs for more information" + exit 1 + - name: Pass if all required checks passed + run: | + echo "All required checks passed" diff --git a/.github/workflows/ci.yml b/.github/workflows/ci-rs.yml similarity index 58% rename from .github/workflows/ci.yml rename to .github/workflows/ci-rs.yml index bb344ce..a648c88 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci-rs.yml @@ -1,4 +1,4 @@ -name: Continuous integration +name: Continuous integration 🦀 on: push: @@ -25,39 +25,17 @@ jobs: changes: name: Check for changes runs-on: ubuntu-latest - # Required permissions permissions: - contents: read pull-requests: read - # Set job outputs to values from filter step - # These outputs are always true when running after a merge to main, or if the PR has a `run-ci-checks` label. outputs: - schema: ${{ steps.filter.outputs.schema == 'true' || steps.override.outputs.out == 'true' }} - rust: ${{ steps.filter.outputs.rust == 'true' || steps.override.outputs.out == 'true' }} - python: ${{ steps.filter.outputs.python == 'true' || steps.override.outputs.out == 'true' }} + rust: ${{ steps.filter.outputs.rust }} steps: - - uses: actions/checkout@v5 - - name: Override label - id: override - run: | - echo "Label contains run-ci-checks: $OVERRIDE_LABEL" - if [ "$OVERRIDE_LABEL" == "true" ]; then - echo "Overriding due to label 'run-ci-checks'" - echo "out=true" >> $GITHUB_OUTPUT - elif [ "$DEFAULT_BRANCH" == "true" ]; then - echo "Overriding due to running on the default branch" - echo "out=true" >> $GITHUB_OUTPUT - fi - env: - OVERRIDE_LABEL: ${{ github.event_name == 'pull_request' && contains( github.event.pull_request.labels.*.name, 'run-ci-checks') }} - DEFAULT_BRANCH: ${{ github.ref_name == github.event.repository.default_branch }} - - uses: dorny/paths-filter@v3 - id: filter - with: - filters: .github/change-filters.yml + - uses: actions/checkout@v5 + - uses: ./.github/actions/check-changes + id: filter rs-check: - name: 🦀 Check lints + name: Check lints needs: changes if: ${{ needs.changes.outputs.rust == 'true' }} runs-on: ubuntu-latest @@ -78,7 +56,7 @@ jobs: RUSTDOCFLAGS: "-Dwarnings" rs-benches: - name: 🦀 Build Rust benchmarks 🏋️ + name: Build Rust benchmarks 🏋️ needs: changes if: ${{ needs.changes.outputs.rust == 'true' && github.event_name != 'merge_group' }} runs-on: ubuntu-latest @@ -95,7 +73,7 @@ jobs: needs: changes if: ${{ needs.changes.outputs.rust == 'true' }} runs-on: ubuntu-latest - name: 🦀 tests (Rust stable, all features) + name: tests (Rust stable, all features) steps: - uses: actions/checkout@v5 - uses: mozilla-actions/sccache-action@v0.0.9 @@ -121,7 +99,7 @@ jobs: # Stable is covered by `tests-stable-all-features` # Nightly is covered by `tests-nightly-coverage` rust: ["1.85", beta] - name: 🦀 tests (Rust ${{ matrix.rust }}) + name: tests (Rust ${{ matrix.rust }}) steps: - uses: actions/checkout@v5 - uses: mozilla-actions/sccache-action@v0.0.9 @@ -142,7 +120,7 @@ jobs: # Run only if there are changes in the relevant files if: ${{ needs.changes.outputs.rust == 'true' && github.event_name != 'merge_group' }} runs-on: ubuntu-latest - name: 🦀 tests (Rust nightly, coverage) + name: tests (Rust nightly, coverage) steps: - uses: actions/checkout@v5 - uses: mozilla-actions/sccache-action@v0.0.9 @@ -169,67 +147,15 @@ jobs: flags: rust #token: ${{ secrets.CODECOV_TOKEN }} - # Ensure that the generated capnp implementations are up to date - # - # Run `just update-capnp` to update the generated code. - capnp-schema: - needs: [changes] - if: ${{ needs.changes.outputs.schema == 'true' && github.event_name != 'merge_group' }} - name: Keep the capnp generated code up-to-date with the schema - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v5 - - uses: mozilla-actions/sccache-action@v0.0.9 - - name: Install stable toolchain - uses: dtolnay/rust-toolchain@stable - - name: Install CapnProto - run: | - curl -O https://capnproto.org/capnproto-c++-${CAPNPROTO_VERSION}.tar.gz - tar zxf capnproto-c++-${CAPNPROTO_VERSION}.tar.gz - cd capnproto-c++-${CAPNPROTO_VERSION} - ./configure - make -j6 check - sudo make install - - name: Get cargo binstall - uses: cargo-bins/cargo-binstall@main - - name: Install capnproto-rust plugin - run: cargo binstall capnpc - - name: Regenerate the Rust capnp code - run: | - capnp compile \ - -orust:impl/rs/src \ - --src-prefix=impl \ - impl/capnp/jeff.capnp - - name: Regenerate the C++ capnp code - run: | - patch -p0 < impl/capnp/cpp_namespace.patch - capnp compile \ - -oc++:impl/cpp/src \ - --src-prefix=impl \ - impl/capnp/jeff.capnp - patch -p0 -R < impl/capnp/cpp_namespace.patch - - name: Re-encode the test .jeff files - run: ./examples/encode_examples.sh - - name: Check if the generated capnproto code is up to date - run: | - git diff --exit-code impl/rs/src/capnp/ impl/cpp/src/capnp/ examples/ - if [ $? -ne 0 ]; then - echo "The capnp generated code is not up to date" - echo "Please run 'just update-capnp' and commit the changes" - exit 1 - fi - # This is a meta job to mark successful completion of the required checks, # even if they are skipped due to no changes in the relevant files. required-checks: name: Required checks - needs: - [ - changes, - rs-check, - rs-tests-stable-all-features, - # We don't include `capnp-schema` as a required check, since it takes a while to install the latest capnp system lib - ] + needs: [ + changes, + rs-check, + rs-tests-stable-all-features, + ] if: ${{ !cancelled() }} runs-on: ubuntu-latest steps: diff --git a/.github/workflows/ci-schema.yml b/.github/workflows/ci-schema.yml new file mode 100644 index 0000000..c749bc3 --- /dev/null +++ b/.github/workflows/ci-schema.yml @@ -0,0 +1,108 @@ +name: Continuous integration 📜 + +on: + push: + branches: + - main + pull_request: + branches: + - '**' + merge_group: + types: [checks_requested] + workflow_dispatch: {} + +env: + SCCACHE_GHA_ENABLED: "true" + CAPNPROTO_VERSION: "1.1.0" + +jobs: + # Check if changes were made to the relevant files. + # Always returns true if running on the default branch, to ensure all changes are thoroughly checked. + changes: + name: Check for changes + runs-on: ubuntu-latest + permissions: + pull-requests: read + outputs: + schema: ${{ steps.filter.outputs.schema }} + steps: + - uses: actions/checkout@v5 + - uses: ./.github/actions/check-changes + id: filter + + # Ensure that the generated capnp implementations are up to date + # + # Run `just update-capnp` to update the generated code. + capnp-schema: + needs: [changes] + if: ${{ needs.changes.outputs.schema == 'true' && github.event_name != 'merge_group' }} + name: Keep the capnp generated code up-to-date with the schema + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v5 + - uses: mozilla-actions/sccache-action@v0.0.9 + - name: Install stable toolchain + uses: dtolnay/rust-toolchain@stable + - name: Install CapnProto + run: | + curl -O https://capnproto.org/capnproto-c++-${CAPNPROTO_VERSION}.tar.gz + tar zxf capnproto-c++-${CAPNPROTO_VERSION}.tar.gz + cd capnproto-c++-${CAPNPROTO_VERSION} + ./configure + make -j6 check + sudo make install + - name: Get cargo binstall + uses: cargo-bins/cargo-binstall@main + - name: Install capnproto-rust plugin + run: cargo binstall capnpc + - name: Regenerate the Rust capnp code + run: | + capnp compile \ + -orust:impl/rs/src \ + --src-prefix=impl \ + impl/capnp/jeff.capnp + - name: Regenerate the C++ capnp code + run: | + patch -p0 < impl/capnp/cpp_namespace.patch + capnp compile \ + -oc++:impl/cpp/src \ + --src-prefix=impl \ + impl/capnp/jeff.capnp + patch -p0 -R < impl/capnp/cpp_namespace.patch + - name: Re-encode the test .jeff files + run: ./examples/encode_examples.sh + - name: Copy the latest capnp schema to the python package + run: cp impl/capnp/jeff.capnp impl/py/src/jeff-format/data/jeff.capnp + - name: Check if the generated capnproto code is up to date + run: | + git diff --exit-code \ + impl/rs/src/capnp/ \ + impl/cpp/src/capnp/ \ + examples/ \ + impl/py/src/jeff-format/data/jeff.capnp + if [ $? -ne 0 ]; then + echo "The capnp generated code is not up to date" + echo "Please run 'just update-capnp' and commit the changes" + exit 1 + fi + + # This is a meta job to mark successful completion of the required checks, + # even if they are skipped due to no changes in the relevant files. + required-checks: + name: Required checks + needs: [ + changes, + capnp-schema, + ] + if: ${{ !cancelled() }} + runs-on: ubuntu-latest + steps: + - name: Fail if required checks failed + if: ${{ contains(needs.*.result, 'failure') || contains(needs.*.result, 'cancelled') }} + run: | + echo "Required checks failed" + echo "Please check the logs for more information" + exit 1 + - name: Pass if all required checks passed + run: | + echo "All required checks passed" diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 9d3aa98..62463ca 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -40,63 +40,64 @@ repos: - id: ruff-format name: ruff format description: Format python code with `ruff`. - entry: sh -c 'cd impl && uv run ruff format' + entry: uv run ruff format language: system files: \.py$ pass_filenames: false - id: ruff-check name: ruff description: Check python code with `ruff`. - entry: sh -c 'cd impl && uv run ruff check --fix --exit-non-zero-on-fix' - language: system - files: \.py$ - pass_filenames: false - - id: mypy-check - name: mypy - description: Check python code with `mypy`. - entry: sh -c 'cd impl && uv run mypy .' + entry: uv run ruff check --fix --exit-non-zero-on-fix language: system files: \.py$ pass_filenames: false + # TODO: Fix lints and re-enable + #- id: mypy-check + # name: mypy + # description: Check python code with `mypy`. + # entry: uv run mypy . + # language: system + # files: \.py$ + # pass_filenames: false - id: cargo-fmt name: cargo format description: Format rust code with `cargo fmt`. - entry: sh -c 'cd impl && cargo fmt --all' + entry: cargo fmt --all language: system files: \.rs$ pass_filenames: false - id: cargo-check name: cargo check description: Check rust code with `cargo check`. - entry: sh -c 'cd impl && cargo check --all --all-features --workspace' + entry: cargo check --all --all-features --workspace language: system files: \.rs$ pass_filenames: false - id: cargo-test name: cargo test description: Run tests with `cargo test`. - entry: sh -c 'cd impl && cargo test --all-features --workspace' + entry: cargo test --all-features --workspace language: system files: \.rs$ pass_filenames: false - id: cargo-clippy name: cargo clippy description: Run clippy lints with `cargo clippy`. - entry: sh -c 'cd impl && cargo clippy --all-targets --all-features --workspace -- -D warnings' + entry: cargo clippy --all-targets --all-features --workspace -- -D warnings language: system files: \.rs$ pass_filenames: false - id: cargo-doc name: cargo doc description: Generate documentation with `cargo doc`. - entry: sh -c "cd impl && RUSTDOCFLAGS=-Dwarnings cargo doc --no-deps --all-features --workspace" + entry: sh -c "RUSTDOCFLAGS=-Dwarnings cargo doc --no-deps --all-features --workspace" language: system files: \.rs$ pass_filenames: false - id: py-test name: pytest description: Run python tests - entry: sh -c 'cd impl && uv run pytest' + entry: uv run pytest language: system files: \.py$ pass_filenames: false diff --git a/impl/py/CHANGELOG.md b/impl/py/CHANGELOG.md new file mode 100644 index 0000000..39034f0 --- /dev/null +++ b/impl/py/CHANGELOG.md @@ -0,0 +1,3 @@ +# Changelog + +The jeff python wrapper is distributed via the [jeff-format](https://pypi.org/project/jeff-format/) pypi package. diff --git a/impl/py/LICENCE b/impl/py/LICENCE new file mode 100644 index 0000000..261eeb9 --- /dev/null +++ b/impl/py/LICENCE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/impl/py/README.md b/impl/py/README.md new file mode 100644 index 0000000..a0bff1a --- /dev/null +++ b/impl/py/README.md @@ -0,0 +1,27 @@ +# jeff-format + +[![pypi][]](https://pypi.org/project/jeff-format/) +[![py-version][]](https://pypi.org/project/jeff-format/) + + [py-version]: https://img.shields.io/pypi/pyversions/jeff-format + [pypi]: https://img.shields.io/pypi/v/jeff-format + +Python bindings for the *jeff* exchange format. +See the [jeff project](https://github.com/unitaryfoundation/jeff) for more information. + +## Recent Changes + +See [CHANGELOG][] for a list of changes. The minimum supported rust +version will only change on major releases. + +## Development + +See [DEVELOPMENT][] for instructions on setting up the development environment. + +## License + +This project is licensed under Apache License, Version 2.0 ([LICENSE][] or http://www.apache.org/licenses/LICENSE-2.0). + + [LICENSE]: https://github.com/unitaryfoundation/jeff/blob/main/LICENSE + [CHANGELOG]: https://github.com/unitaryfoundation/jeff/blob/main/impl/py/CHANGELOG.md + [DEVELOPMENT]: https://github.com/unitaryfoundation/jeff/blob/main/DEVELOPMENT.md diff --git a/impl/py/pyproject.toml b/impl/py/pyproject.toml new file mode 100644 index 0000000..a051394 --- /dev/null +++ b/impl/py/pyproject.toml @@ -0,0 +1,39 @@ +[project] +name = "jeff-format" +version = "0.1.0" +requires-python = ">=3.10" +description = "Python bindings for the `jeff` exchange format." +license = { file = "LICENCE" } +readme = "README.md" +authors = [] +maintainers = [] + +classifiers = [ + "Development Status :: 3 - Alpha", + + "Intended Audience :: Developers", + "Intended Audience :: Science/Research", + "Topic :: Software Development :: Compilers", + + "License :: OSI Approved :: Apache Software License", + + "Programming Language :: Python :: 3", + "Programming Language :: Python :: 3.10", + "Programming Language :: Python :: 3.11", + "Programming Language :: Python :: 3.12", + "Programming Language :: Python :: 3.13", + "Programming Language :: Python :: 3.14", + "Programming Language :: Python :: 3 :: Only", +] + +dependencies = [ + # TODO: Temporarily disabled + # "pycapnp ~= 2.0.0" +] + +[tool.hatch.build.targets.wheel] +packages = ["src/jeff"] + +[build-system] +requires = ["hatchling"] +build-backend = "hatchling.build" diff --git a/impl/py/jeff.py b/impl/py/src/jeff/__init__.py similarity index 94% rename from impl/py/jeff.py rename to impl/py/src/jeff/__init__.py index 2e67aae..8d30186 100644 --- a/impl/py/jeff.py +++ b/impl/py/src/jeff/__init__.py @@ -17,19 +17,15 @@ from __future__ import annotations -import os import textwrap from abc import ABC, abstractmethod from typing import Any, Iterable -import capnp +# from .capnp import load_schema -# capnp warns about this environment variable being set -if "PWD" in os.environ: - del os.environ["PWD"] - -capnp.remove_import_hook() -schema = capnp.load("impl/capnp/jeff.capnp") +# TODO: Temporarily disabled +# schema = load_schema() +schema = None # TODO: add remaining op instructions # TODO: add methods to convert read-only data to cached (builder) instances, remove '_update_cache' @@ -45,7 +41,22 @@ Paulis = ("i", "x", "y", "z") -KnownGates = ("gphase", "i", "x", "y", "z", "s", "t", "r1", "rx", "ry", "rz", "h", "u", "swap") +KnownGates = ( + "gphase", + "i", + "x", + "y", + "z", + "s", + "t", + "r1", + "rx", + "ry", + "rz", + "h", + "u", + "swap", +) ################ # Core classes # @@ -397,7 +408,9 @@ def inputs(self) -> list[JeffValue]: if self._inputs is not _Empty: return self._inputs - return [JeffValue.from_encoding(inp, self._func) for inp in self._raw_data.inputs] + return [ + JeffValue.from_encoding(inp, self._func) for inp in self._raw_data.inputs + ] @inputs.setter def inputs(self, inputs: list[JeffValue]): @@ -409,7 +422,9 @@ def outputs(self) -> list[JeffValue]: if self._outputs is not _Empty: return self._outputs - return [JeffValue.from_encoding(out, self._func) for out in self._raw_data.outputs] + return [ + JeffValue.from_encoding(out, self._func) for out in self._raw_data.outputs + ] @outputs.setter def outputs(self, outputs: list[JeffValue]): @@ -468,7 +483,7 @@ def __str__(self): if outputs := self.outputs: string += ", ".join(str(out) for out in outputs) - string += f" = " + string += " = " string += f"{self.instruction_name} " @@ -494,7 +509,10 @@ class JeffRegion: _operations: list[JeffOp] = _Empty def __init__( - self, sources: list[JeffValue], targets: list[JeffValue], operations: list[JeffOp] + self, + sources: list[JeffValue], + targets: list[JeffValue], + operations: list[JeffOp], ): self._sources = sources self._targets = targets @@ -569,7 +587,8 @@ def sources(self) -> list[JeffValue]: return self._sources return [ - JeffValue.from_encoding(source, self.parent_func) for source in self._raw_data.sources + JeffValue.from_encoding(source, self.parent_func) + for source in self._raw_data.sources ] @sources.setter @@ -583,7 +602,8 @@ def targets(self) -> list[JeffValue]: return self._targets return [ - JeffValue.from_encoding(target, self.parent_func) for target in self._raw_data.targets + JeffValue.from_encoding(target, self.parent_func) + for target in self._raw_data.targets ] @targets.setter @@ -597,7 +617,10 @@ def operations(self) -> list[JeffOp]: if self._operations is not _Empty: return self._operations - return [JeffOp.from_encoding(op, self.parent_func) for op in self._raw_data.operations] + return [ + JeffOp.from_encoding(op, self.parent_func) + for op in self._raw_data.operations + ] @operations.setter def operations(self, operations: list[JeffOp]): @@ -632,12 +655,12 @@ def __str__(self): string += " in :" if sources := self.sources: string += f" {', '.join(str(src) for src in sources)}" - string += f"\n" + string += "\n" for op in self: string += f"{textwrap.indent(str(op), ' ')}\n" - string += f" out:" + string += " out:" if targets := self.targets: string += f" {', '.join(str(tgt) for tgt in targets)}" string += "" @@ -716,7 +739,7 @@ def __str__(self): string = f"func @{self.name}" string += f"({', '.join(str(ty) for ty in input_types)})" - string += f" -> " + string += " -> " string += f"({', '.join(str(ty) for ty in output_types)})" if isinstance(self, FunctionDef): @@ -850,7 +873,9 @@ def __getitem__(self, idx): if self._body is not _Empty: return self._body[idx] - return JeffOp.from_encoding(self._raw_data.definition.body.operations[idx], self) + return JeffOp.from_encoding( + self._raw_data.definition.body.operations[idx], self + ) class FunctionDecl(JeffFunc): @@ -903,7 +928,10 @@ def inputs(self) -> list[JeffType]: if self._inputs is not _Empty: return self._inputs - return [JeffType.from_encoding(inp.type) for inp in self._raw_data.declaration.inputs] + return [ + JeffType.from_encoding(inp.type) + for inp in self._raw_data.declaration.inputs + ] @inputs.setter def inputs(self, inputs: list[JeffType]): @@ -915,7 +943,10 @@ def outputs(self) -> list[JeffType]: if self._outputs is not _Empty: return self._outputs - return [JeffType.from_encoding(out.type) for out in self._raw_data.declaration.outputs] + return [ + JeffType.from_encoding(out.type) + for out in self._raw_data.declaration.outputs + ] @outputs.setter def outputs(self, outputs: list[JeffType]): @@ -1128,7 +1159,7 @@ def __str__(self): string += "\n\n" for i, func in enumerate(self): - string += f"{'[entry] 'if i == self.entrypoint else ''}{func}\n" + string += f"{'[entry] ' if i == self.entrypoint else ''}{func}\n" return string @@ -1151,7 +1182,9 @@ class JeffGate(ABC): _power: int = _Empty def from_encoding(gate: schema.QubitGate, op: JeffOp): - cls = {"custom": CustomGate, "wellKnown": WellKnowGate, "ppr": PPRGate}[str(gate.which)] + cls = {"custom": CustomGate, "wellKnown": WellKnowGate, "ppr": PPRGate}[ + str(gate.which) + ] obj = cls.__new__(cls) obj._raw_data = gate obj._op = op @@ -1225,7 +1258,7 @@ def __str__(self): if num_controls := self.num_controls: string += f"numControls={num_controls}, " if self.adjoint: - string += f"adjoint, " + string += "adjoint, " if (power := self.power) != 1: string += f"power={power}, " return string @@ -1279,7 +1312,20 @@ def num_qubits(self): match self.kind: case "gphase": return 0 - case "i" | "x" | "y" | "z" | "s" | "t" | "r1" | "rx" | "ry" | "rz" | "h" | "u": + case ( + "i" + | "x" + | "y" + | "z" + | "s" + | "t" + | "r1" + | "rx" + | "ry" + | "rz" + | "h" + | "u" + ): return 1 case "swap": return 2 @@ -1359,7 +1405,9 @@ def name(self) -> str: if self._name is not _Empty: return self._name - assert (func := self._op._func) and (mod := func._module) and not mod.is_dirty, ( + assert ( + (func := self._op._func) and (mod := func._module) and not mod.is_dirty + ), ( "The parent module is not present or dirty, and no name has been cached. " "Please call `refresh` on the module to access this attribute." ) @@ -1414,7 +1462,9 @@ class PPRGate(JeffGate): _pauli_string: list[str] = _Empty - def __init__(self, pauli_string: list[str], num_controls: int, adjoint: bool, power: int): + def __init__( + self, pauli_string: list[str], num_controls: int, adjoint: bool, power: int + ): self._pauli_string = pauli_string self._num_controls = num_controls self._adjoint = adjoint @@ -1455,7 +1505,7 @@ def pauli_string(self, pauli_string: list[str]): # Python integration def __str__(self): - string = f"(PPR, " + string = "(PPR, " string += f"pauliString={self.pauli_string}, " string += super().__str__() string = string[:-2] + ")" @@ -1548,7 +1598,10 @@ def branches(self) -> list[JeffRegion]: if self._branches is not _Empty: return self._branches - return [JeffRegion.from_encoding(branch, self) for branch in self._raw_data.switch.branches] + return [ + JeffRegion.from_encoding(branch, self) + for branch in self._raw_data.switch.branches + ] @branches.setter def branches(self, branches: list[JeffRegion]): @@ -1583,8 +1636,8 @@ def __str__(self): string += f"{textwrap.indent(str(branch), ' ')}" if branch := self.default: - string += f"\n" - string += f" default:\n" + string += "\n" + string += " default:\n" string += f"{textwrap.indent(str(branch), ' ')}" return string @@ -1643,7 +1696,7 @@ def body(self, body: JeffRegion): def __str__(self): string = "\n" - string += f" body:\n" + string += " body:\n" string += f"{textwrap.indent(str(self.body), ' ')}" return string @@ -1696,7 +1749,9 @@ def condition(self) -> JeffRegion: if self._condition is not _Empty: return self._condition - return JeffRegion.from_encoding(getattr(self._raw_data, "while").condition, self) + return JeffRegion.from_encoding( + getattr(self._raw_data, "while").condition, self + ) @condition.setter def condition(self, condition: JeffRegion): @@ -1723,9 +1778,9 @@ def body(self, body: JeffRegion): def __str__(self): string = "\n" - string += f" while:\n" + string += " while:\n" string += f"{textwrap.indent(str(self.condition), ' ')}" - string += f" do:\n" + string += " do:\n" string += f"{textwrap.indent(str(self.body), ' ')}" return string @@ -1802,9 +1857,9 @@ def condition(self, condition: JeffRegion): def __str__(self): string = "\n" - string += f" do:\n" + string += " do:\n" string += f"{textwrap.indent(str(self.body), ' ')}" - string += f" while:\n" + string += " while:\n" string += f"{textwrap.indent(str(self.condition), ' ')}" return string @@ -1856,7 +1911,9 @@ def quantum_gate( if name in KnownGates: gate = WellKnowGate(name, len(control_qubits), adjoint, power) else: - gate = CustomGate(name, len(qubits), len(params), len(control_qubits), adjoint, power) + gate = CustomGate( + name, len(qubits), len(params), len(control_qubits), adjoint, power + ) qubit_inputs = qubits + control_qubits inputs = qubit_inputs + params outputs = [JeffValue(QubitType()) for _ in qubit_inputs] @@ -1898,21 +1955,21 @@ def switch_case( ): """Instantiate a switch-case operation. Cases run from 0 to len(branches)-1.""" for branch in branches + [default] if default else branches: - assert len(branch.sources) == len( - branches[0].sources - ), "all branches require the same number of sources" + assert len(branch.sources) == len(branches[0].sources), ( + "all branches require the same number of sources" + ) assert all( map(lambda x, y: x.type == y.type, branch.sources, branches[0].sources) ), "all branches require the same source type signature" - assert len(branch.targets) == len( - branches[0].targets - ), "all branches require the same number of targets" + assert len(branch.targets) == len(branches[0].targets), ( + "all branches require the same number of targets" + ) assert all( map(lambda x, y: x.type == y.type, branch.targets, branches[0].targets) ), "all branches require the same target type signature" - assert all( - map(lambda x, y: x.type == y.type, region_args, branches[0].sources) - ), "the initial region_args must match the source type signature of the branches" + assert all(map(lambda x, y: x.type == y.type, region_args, branches[0].sources)), ( + "the initial region_args must match the source type signature of the branches" + ) scf = SwitchSCF(branches, default) inputs = [index] + region_args diff --git a/impl/py/src/jeff/capnp.py b/impl/py/src/jeff/capnp.py new file mode 100644 index 0000000..c19ba47 --- /dev/null +++ b/impl/py/src/jeff/capnp.py @@ -0,0 +1,21 @@ +"""Utility functions for tket extensions.""" + +import os +from pathlib import Path + +from typing import Any + + +def load_schema() -> Any: + import capnp + + capnp.remove_import_hook() + + import jeff + + # capnp warns about this environment variable being set + if "PWD" in os.environ: + del os.environ["PWD"] + + capnp_file = Path(jeff.__file__).joinpath("data", "jeff.capnp") + return capnp.load(capnp_file) diff --git a/impl/py/src/jeff/data/jeff.capnp b/impl/py/src/jeff/data/jeff.capnp new file mode 100644 index 0000000..3536cb6 --- /dev/null +++ b/impl/py/src/jeff/data/jeff.capnp @@ -0,0 +1,1455 @@ + +@0xcc0f7aa735ae288c; + +using ValueIndex = UInt32; +using Length = UInt32; +using Bitwidth = UInt8; +using FuncIndex = UInt16; +using StringIndex = UInt16; + +enum FloatPrecision { + float32 @0; + float64 @1; +} + +enum Pauli { + i @0; + x @1; + y @2; + z @3; +} + +enum WellKnownGate { + # For each well-known gate, the inputs and outputs provided are when + # uncontrolled. Controlled gates will follow the same convention as custom + # gates: + # + # Inputs: + # - ...inputs: The `qubit` inputs of the well-known gate. + # - `qubit` x controlQubits: The control qubits for the operation. + # - ...inputs: The `float` inputs of the well-known gate. + # + # Outputs: + # - ...outputs: The outputs of the well-known gate. + # - `qubit` x controlQubits: The control qubit outputs for the operation. + + gphase @13; + # Global phase operation on the "vacuum" state (no qubits). + # G = | exp(iθ) | + # + # Inputs: + # * Rotation in radians (`float`) + # + # Outputs: + # + + i @12; + # Identity (no-op) gate. Pi radians X rotation. + # I = | 1 0 | + # | 0 1 | + # + # Inputs: + # * Qubit to act on (`qubit`) + # + # Outputs: + # * Qubit acted on (`qubit`) + + x @0; + # Pauli-X (NOT) gate. Pi radians X rotation. + # X = | 0 1 | + # | 1 0 | + # + # Inputs: + # * Qubit to act on (`qubit`) + # + # Outputs: + # * Qubit acted on (`qubit`) + + y @1; + # Pauli-Y gate. Pi radians Y rotation. + # Y = | 0 -i | + # | i 0 | + # + # Inputs: + # * Qubit to act on (`qubit`) + # + # Outputs: + # * Qubit acted on (`qubit`) + + z @2; + # Pauli-Z gate. Pi radians Z rotation. + # Z = | 1 0 | + # | 0 -1 | + # + # Inputs: + # * Qubit to act on (`qubit`) + # + # Outputs: + # * Qubit acted on (`qubit`) + + s @3; + # Root-Z gate. Pi/2 radians Z rotation. + # S = | 1 0 | + # | 0 i | + # + # Inputs: + # * Qubit to act on (`qubit`) + # + # Outputs: + # * Qubit acted on (`qubit`) + + t @4; + # T gate. Pi/4 radians Z rotation. + # T = | 1 0 | + # | 0 exp(iπ/4)| + # + # Inputs: + # * Qubit to act on (`qubit`) + # + # Outputs: + # * Qubit acted on (`qubit`) + + r1 @5; + # Rotation about the |1> state. + # R1(θ) = | 1 0 | + # | 0 exp(iθ) | + # + # Inputs: + # * Qubit to act on (`qubit`) + # * Rotation in radians (`float`) + # + # Outputs: + # * Qubit acted on (`qubit`) + + rx @6; + # Rotation about the X axis. + # Rx(θ) = | cos(θ/2) -isin(θ/2) | + # | -isin(θ/2) cos(θ/2) | + # + # Inputs: + # * Qubit to act on (`qubit`) + # * Rotation in radians (`float`) + # + # Outputs: + # * Qubit acted on (`qubit`) + + ry @7; + # Rotation about the Y axis. + # Ry(θ) = | cos(θ/2) -sin(θ/2) | + # | sin(θ/2) cos(θ/2) | + # + # Inputs: + # * Qubit to act on (`qubit`) + # * Rotation in radians (`float`) + # + # Outputs: + # * Qubit acted on (`qubit`) + + rz @8; + # Rotation about the Z axis. + # Rz(θ) = | exp(-iθ/2) 0 | + # | 0 exp(iθ/2) | + # + # Inputs: + # * Qubit to act on (`qubit`) + # * Rotation in radians (`float`) + # + # Outputs: + # * Qubit acted on (`qubit`) + + h @9; + # Hadamard gate. + # H = 1/√2 | 1 1 | + # | 1 -1 | + # + # Inputs: + # * Qubit to act on (`qubit`) + # + # Outputs: + # * Qubit acted on (`qubit`) + + u @10; + # Euler gate. + # U(θ,φ,λ) = | cos(θ/2) -exp(iλ)sin(θ/2) | + # | exp(iφ)sin(θ/2) exp(i(λ+φ))cos(θ/2) | + # + # Inputs: + # * Qubit to act on (`qubit`) + # * Theta in radians (`float`) + # * Phi in radians (`float`) + # * Lambda in radians (`float`) + # + # Outputs: + # * Qubit acted on (`qubit`) + + swap @11; + # Swap gate. Swaps the state of two qubits. + # + # Inputs: + # * First qubit (`qubit`) + # * Second qubit (`qubit`) + # + # Outputs: + # * First qubit after swap (`qubit`) + # * Second qubit after swap (`qubit`) +} + +struct Module { + version @0 :UInt32; + # The version of the format. + + tool @5 :Text; + toolVersion @6 :Text; + # Generating tool name and version. + + functions @1 :List(Function); + # The functions in the module. + + strings @2 :List(Text); + # String table. + # + # The strings in the table are referenced by `StringIndex`s in the module. + + metadata @3 :List(Meta); + # Metadata attached to the module. + + entrypoint @4 :FuncIndex; + # Index into the `functions` list of the entrypoint function. +} + +struct Function { + # A function declaration or definition. + + name @0 :StringIndex; + # The name of the function. + # + # Function names must be unique within a module. + + metadata @3 :List(Meta); + # Metadata attached to the function. + + union { + definition :group { + # A function definition, containing a dataflow region. + + body @1 :Region; + # The body of a function definition. + + values @2 :List(Value); + # The hyperedge values defined within the function. + # + # `ValueIndex`s in the function's body refer to the values in this list. + } + + declaration :group { + # A function declaration, containing only a signature. + + inputs @4 :List(Value); + # The function input types. + + outputs @5 :List(Value); + # The function output types. + } + } +} + +struct Region { + sources @0 :List(ValueIndex); + # The source ports with the values that are to be supplied as arguments to the region. + + targets @1 :List(ValueIndex); + # The target ports with the values that are returned from the region. + + operations @2 :List(Op); + # The operations in the region. + # + # The order of operations in this list is not significant. + + metadata @3 :List(Meta); + # Metadata attached to the region. +} + +struct Op { + # Operations. + + inputs @0 :List(ValueIndex); + # The input ports with the values that are consumed by the operation. + + outputs @1 :List(ValueIndex); + # The output ports with the values that are produced by the operation. + + metadata @2 :List(Meta); + # Metadata attached to the operation. + + instruction :union { + qubit @3 :QubitOp; + qureg @4 :QuregOp; + int @5 :IntOp; + intArray @6 :IntArrayOp; + float @7 :FloatOp; + floatArray @8 :FloatArrayOp; + scf @9 :ScfOp; + func @10 :FuncOp; + } +} + +struct Value { + type @0 :Type; + # The type of the value. + + metadata @1 :List(Meta); + # Metadata attached to the value. +} + +struct Type { + union { + qubit @0 :Void; + # Quantum bits. + # + # Qubits are linear types. + + qureg @1 :Void; + # Quantum registers. + # + # A quantum register is an array of slots that can hold qubits. + # Slots of a quantum register can either be empty or filled with a qubit. + # Quantum registers are linear types. + # The length of the register is not known at compile time. + + int @2 :Bitwidth; + # Integers. + # + # The type does not distinguish between signed and unsigned integers. + # Instead it is up to the operation to interpret the integer as signed or unsigned. + # Signed integers are represented using two's complement. + # + # Integers of bitwidth 1 can be used as classical bits or boolean values. + + intArray @3 :Bitwidth; + # Integer arrays. + # + # The length of the array is not known at compile time. + # + # Arrays of integers of bitwidth 1 can be used as classical bit arrays. + + float @4 :FloatPrecision; + # Floating point numbers. + # + # The length of the array is not known at compile time. + + floatArray @5 :FloatPrecision; + # Floating point number arrays. + # + # The length of the array is not known at compile time. + } +} + +struct Meta { + # Metadata. + + name @0 :StringIndex; + # The name of the metadata. + + value @1 :AnyPointer; + # The value of the metadata. + # + # The format of this field is determined by the metadata name. + # For example, it may be a string, binary data, list or a capnp struct. +} + +struct QubitOp { + # Operations for qubits. + + union { + alloc @0 :Void; + # Allocates a new qubit in the |0> state. + # + # Outputs: + # - `qubit`: The newly allocated qubit. + + free @1 :Void; + # Frees a qubit. + # + # This operation makes no assumptions about the state of the qubit. + # + # Inputs: + # - `qubit`: The qubit to free. + + freeZero @2 :Void; + # Frees a qubit in the |0> state. + # + # This operation can be used to avoid performing resets when it is known + # that the qubit has already been reset. It is undefined behavior to free + # a qubit that is not in the |0> state. + # + # Inputs: + # - `qubit`: The qubit to free. + + measure @3 :Void; + # Perform a destructive measurement of a qubit in the computational basis. + # + # Inputs: + # - `qubit`: The qubit to measure. + # + # Outputs: + # - `int(1)`: The measurement result. + + measureNd @4 :Void; + # Perform a non-destructive measurement of a qubit in the computational basis. + # + # Inputs: + # - `qubit`: The qubit to measure. + # + # Outputs: + # - `qubit`: The measured qubit. + # - `int(1)`: The measurement result. + + reset @5 :Void; + # Resets a qubit to the |0> state. + # + # Inputs: + # - `qubit`: The qubit to reset. + # + # Outputs: + # - `qubit`: The reset qubit. + + gate @6 :QubitGate; + # Apply a qubit gate. + # + # Inputs & Outputs vary - check `QubitGate` + } +} + +struct QubitGate { + # Unitary operations on qubits. + + union { + wellKnown @0 :WellKnownGate; + # Apply a well-known quantum gate. + # + # The signature of the gate is determined by the enum value. Refer to the documentation for each well-known value. + # + # Inputs: + # - ...inputs: The input qubits to the operation. + # - `qubit` x controlQubits: The control qubits for the operation. + # - ...inputs: Additional floating point arguments. + # + # Outputs: + # - ...outputs: The output qubits. + # - `qubit` x controlQubits: The control qubit outputs for the operation. + + ppr :group { + # Apply an arbitrary Pauli-product rotation gate. + # + # The operation is characterized by a rotation angle θ and a Pauli tensor product P: + # PPR(θ) = exp(iθP), P = P_1 ⊗ P_2 ⊗ ... ⊗ P_n + # + # Inputs: + # - `qubit` x len(pauliString): The input qubits to the operation. + # - `qubit` x controlQubits: The control qubits for the operation. + # - `float(N)`: Rotation angle in radians. + # + # Outputs: + # - `qubit` x len(pauliString): The output qubits. + # - `qubit` x controlQubits: The control qubit outputs for the operation. + pauliString @7 : List(Pauli); + } + + custom :group { + # Apply an arbitrary quantum gate. + # + # The signature of the gate is determined by its parameters. + # + # Inputs: + # - `qubit` x numQubits: The input qubits to the operation. + # - `qubit` x controlQubits: The control qubits for the operation. + # - `float(N)` x numParams: Additional floating point arguments. + # + # Outputs: + # - `qubit` x numQubits: The output qubits. + # - `qubit` x controlQubits: The control qubit outputs for the operation. + + name @1 :StringIndex; + # The name of the gate. + + numQubits @2 :UInt8; + # The number of qubits that the gate acts on. + + numParams @3 :UInt8; + # The number of floating point parameters that the gate takes as + # inputs, after the qubit values. + } + } + + controlQubits @4 :UInt8; + # The number of control qubits to the operation. + + adjoint @5 :Bool; + # Whether to apply the adjoint of the named gate. + + power @6 :UInt8; + # A number of times to apply this gate in sequence. +} + +struct QuregOp { + # Operations for quantum registers. + + union { + alloc @0 :Void; + # Allocates a new qubit register given a number of qubits in the |0> state. + # + # Inputs: + # - `int(32)`: The number of qubits to allocate. + # + # Outputs: + # - `qureg`: The newly allocated qubit register. + + free @10 :Void; + # Frees a qubit register. + # + # This operation makes no assumptions about the state of the qubits. + # + # Inputs: + # - `qureg`: The qubit register to free. + + freeZero @1 :Void; + # Frees a qubit register, assuming that all qubits are in the |0> state. + # + # It is undefined behavior to free a qubit register containing qubits that are not in the |0> state. + # + # Inputs: + # - `qureg`: The qubit register to free. + + extractIndex @2 :Void; + # Extracts a single qubit from a qubit register. + # + # The slot must have been filled before and is marked as empty after the extraction. + # + # Inputs: + # - `qureg`: The qubit register to extract from. + # - `int(32)`: The index of the qubit to extract. + # + # Outputs: + # - `qureg`: The modified qubit register. + # - `qubit`: The extracted qubit. + + insertIndex @3 :Void; + # Insert a single qubit from a qubit register. + # + # The slot must have been empty before and is marked as filled after the insertion. + # + # Inputs: + # - `qureg`: The qubit register to insert into. + # - `qubit`: The qubit to insert. + # - `int(32)`: The index of the slot to insert into. + # + # Outputs: + # - `qureg`: The modified qubit register. + + extractSlice @4 :Void; + # Extract a slice of qubits from a qubit register given a range of indices. + # + # All slots in the range are marked as empty in the original register. + # + # Inputs: + # - `qureg`: The qubit register to extract from. + # - `int(32)`: The start index of the slice to extract. + # - `int(32)`: The length of the slice to extract. + # + # Outputs: + # - `qureg`: The modified qubit register. + # - `qureg`: The extracted qubit register. + + insertSlice @5 :Void; + # Insert a slice of qubits into a qubit register. + # + # All slots in the inserted range in the original register must have been empty. + # + # Inputs: + # - `qureg`: The qubit register to insert into. + # - `qureg`: The qubit register to insert. + # - `int(32)`: The start index of the slice to insert into. + # + # Outputs: + # - `qureg`: The modified qubit register. + + length @6 :Void; + # Returns the length of the qubit register. + # + # Inputs: + # - `qureg`: The qubit register. + # + # Outputs: + # - `qureg`: The qubit register. + # - `int(32)`: The length of the qubit register. + + split @7 :Void; + # Splits a qubit register into two qubit registers at a given index. + # + # Inputs: + # - `qureg`: The qubit register to split. + # - `int(32)`: The index to split at. + # + # Outputs: + # - `qureg`: The qubit register before the split. + # - `qureg`: The qubit register after the split. + + join @8 :Void; + # Joins together two qubit registers into a single qubit register. + # + # Inputs: + # - `qureg`: The first qubit register. + # - `qureg`: The second qubit register. + # + # Outputs: + # - `qureg`: The joined qubit register. + + create @9 :Void; + # Creates a qubit register from a variable number of input qubits. + # + # Inputs: + # - `... qubit`: The qubits that the register should contain. + # + # Outputs: + # - `qureg`: The qubit register containing the input qubits. + } +} + +struct IntOp { + # Operations for integers. + + union { + const1 @0 :Bool; + # Create a constant 1 bit integer. + # + # Outputs: + # - `int(1)`: The constant 1 bit integer. + + const8 @1 :UInt8; + # Create a constant 8 bit integer. + # + # Outputs: + # - `int(8)`: The constant 8 bit integer. + + const16 @2 :UInt16; + # Create a constant 16 bit integer. + # + # Outputs: + # - `int(16)`: The constant 16 bit integer. + + const32 @3 :UInt32; + # Create a constant 32 bit integer. + # + # Outputs: + # - `int(32)`: The constant 32 bit integer. + + const64 @4 :UInt64; + # Create a constant 64 bit integer. + # + # Outputs: + # - `int(64)`: The constant 64 bit integer. + + add @5 :Void; + # Add two integers. + # + # Inputs: + # - `int(N)`: First integer operand. + # - `int(N)`: Second integer operand. + # + # Outputs: + # - `int(N)`: Sum of the inputs. + + sub @6 :Void; + # Subtract two integers. + # + # Inputs: + # - `int(N)`: First integer operand. + # - `int(N)`: Second integer operand. + # + # Outputs: + # - `int(N)`: Difference of the inputs. + mul @7 :Void; + # Multiply two integers. + # + # Inputs: + # - `int(N)`: First integer operand. + # - `int(N)`: Second integer operand. + # + # Outputs: + # - `int(N)`: Product of the inputs. + + divS @8 :Void; + # Divide two signed integers. + # + # Inputs: + # - `int(N)`: First integer operand. + # - `int(N)`: Second integer operand. + # + # Outputs: + # - `int(N)`: Quotient of the inputs. + + divU @9 :Void; + # Divide two unsigned integers. + # + # Inputs: + # - `int(N)`: First integer operand. + # - `int(N)`: Second integer operand. + # + # Outputs: + # - `int(N)`: Quotient of the inputs. + + pow @10 :Void; + # Take the power of an integer + # + # Inputs: + # - `int(N)`: Base integer. + # - `int(N)`: Exponent integer. + # + # Outputs: + # - `int(N)`: Base raised to exponent power. + + and @11 :Void; + # Logical bitwise AND. + # + # Inputs: + # - `int(N)`: First integer operand. + # - `int(N)`: Second integer operand. + # + # Outputs: + # - `int(N)`: Bitwise AND of inputs. + + or @12 :Void; + # Logical bitwise OR. + # + # Inputs: + # - `int(N)`: First integer operand. + # - `int(N)`: Second integer operand. + # + # Outputs: + # - `int(N)`: Bitwise OR of inputs. + + xor @13 :Void; + # Logical bitwise XOR. + # + # Inputs: + # - `int(N)`: First integer operand. + # - `int(N)`: Second integer operand. + # + # Outputs: + # - `int(N)`: Bitwise XOR of inputs. + + not @14 :Void; + # Logical bitwise NOT. + # + # Inputs: + # - `int(N)`: Integer operand. + # + # Outputs: + # - `int(N)`: Bitwise NOT of input. + + minS @15 :Void; + # Minimum of two signed integers. + # + # Inputs: + # - `int(N)`: First integer operand. + # - `int(N)`: Second integer operand. + # + # Outputs: + # - `int(N)`: Minimum of inputs. + + minU @16 :Void; + # Minimum of two unsigned integers. + # + # Inputs: + # - `int(N)`: First integer operand. + # - `int(N)`: Second integer operand. + # + # Outputs: + # - `int(N)`: Minimum of inputs. + + maxS @17 :Void; + # Maximum of two signed integers. + # + # Inputs: + # - `int(N)`: First integer operand. + # - `int(N)`: Second integer operand. + # + # Outputs: + # - `int(N)`: Maximum of inputs. + + maxU @18 :Void; + # Maximum of two unsigned integers. + # + # Inputs: + # - `int(N)`: First integer operand. + # - `int(N)`: Second integer operand. + # + # Outputs: + # - `int(N)`: Maximum of inputs. + + eq @19 :Void; + # Test two integers for equality. + # + # Inputs: + # - `int(N)`: First integer operand. + # - `int(N)`: Second integer operand. + # + # Outputs: + # - `int(1)`: True if equal, false otherwise. + + ltS @20 :Void; + # Check if one signed integer is strictly less than another. + # + # Inputs: + # - `int(N)`: First integer operand. + # - `int(N)`: Second integer operand. + # + # Outputs: + # - `int(1)`: True if first less than second, false otherwise. + + lteS @21 :Void; + # Check if one signed integer is less than or equal to another. + # + # Inputs: + # - `int(N)`: First integer operand. + # - `int(N)`: Second integer operand. + # + # Outputs: + # - `int(1)`: True if first less than or equal to second, false otherwise. + + ltU @22 :Void; + # Check if one signed integer is strictly less than another. + # + # Inputs: + # - `int(N)`: First integer operand. + # - `int(N)`: Second integer operand. + # + # Outputs: + # - `int(1)`: True if first less than second, false otherwise. + + lteU @23 :Void; + # Check if one unsigned integer is less than or equal to another. + # + # Inputs: + # - `int(N)`: First integer operand. + # - `int(N)`: Second integer operand. + # + # Outputs: + # - `int(1)`: True if first less than or equal to second, false otherwise. + + abs @24 :Void; + # Take the absolute value of a signed integer. + # + # Inputs: + # - `int(N)`: Integer operand. + # + # Outputs: + # - `int(N)`: Absolute value of input. + + remS @25 :Void; + # Remainder of a division of two signed integers. + # + # Inputs: + # - `int(N)`: First integer operand. + # - `int(N)`: Second integer operand. + # + # Outputs: + # - `int(N)`: Remainder of division. + + remU @26 :Void; + # Remainder of a division of two unsigned integers. + # + # Inputs: + # - `int(N)`: First integer operand. + # - `int(N)`: Second integer operand. + # + # Outputs: + # - `int(N)`: Remainder of division. + + shl @27 :Void; + # Logical shift left. + # + # Inputs: + # - `int(N)`: Value to shift. + # - `int(N)`: Shift amount. + # + # Outputs: + # - `int(N)`: Shifted value. + + shr @28 :Void; + # Logical shift right. + # + # Inputs: + # - `int(N)`: Value to shift. + # - `int(N)`: Shift amount. + # + # Outputs: + # - `int(N)`: Shifted value. + } +} + +struct IntArrayOp { + # Operations for integer arrays. + + union { + const1 @0 :List(Bool); + # Create a constant 1 bit integer array. + # + # Outputs: + # - `int_array(1)`: The constant 1 bit integer array. + + const8 @1 :List(UInt8); + # Create a constant 8 bit integer array. + # + # Outputs: + # - `int_array(8)`: The constant 8 bit integer array. + + const16 @2 :List(UInt16); + # Create a constant 16 bit integer array. + # + # Outputs: + # - `int_array(16)`: The constant 16 bit integer array. + + const32 @3 :List(UInt32); + # Create a constant 32 bit integer array. + # + # Outputs: + # - `int_array(32)`: The constant 32 bit integer array. + + const64 @4 :List(UInt64); + # Create a constant 64 bit integer array. + # + # Outputs: + # - `int_array(64)`: The constant 64 bit integer array. + + zero @5 :Bitwidth; + # Create a zeroed integer array of a given bitwidth with dynamic length. + # + # Inputs: + # - `int(32)`: The length of the integer array. + # + # Outputs: + # - `int_array(N)`: The zeroed integer array. + + getIndex @6 :Void; + # Get the value of an integer array at a given index. + # + # Inputs: + # - `int_array(N)`: The integer array. + # - `int(32)`: The index of the value to get. + # + # Outputs: + # - `int(N)`: The value at the given index. + + setIndex @7 :Void; + # Set the value of an integer array at a given index. + # + # Inputs: + # - `int_array(N)`: The integer array. + # - `int(32)`: The index of the value to set. + # - `int(N)`: The new value to set. + # + # Outputs: + # - `int_array(N)`: The modified integer array. + + length @8 :Void; + # Get the length of an integer array. + # + # Inputs: + # - `int_array(N)`: The integer array. + # + # Outputs: + # - `int(32)`: The length of the integer array. + + create @9 :Void; + # Creates an integer array from a variable number of input values. + # + # Inputs: + # - `... int(N)`: The integer values that the array should contain. + # + # Outputs: + # - `int_array(N)`: The integer array containing the input values. + } +} + +struct FloatOp { + # Operations for floats. + + union { + const32 @0 :Float32; + # Create a constant 32 bit float. + # + # Outputs: + # - `float(32)`: The constant 32 bit float. + + const64 @1 :Float64; + # Create a constant 64 bit float. + # + # Outputs: + # - `float(64)`: The constant 64 bit float. + + add @2 :Void; + # Add two floats. + # + # Inputs: + # - `float(N)`: First float operand. + # - `float(N)`: Second float operand. + # + # Outputs: + # - `float(N)`: Sum of the inputs. + + sub @3 :Void; + # Subtract two floats. + # + # Inputs: + # - `float(N)`: First float operand. + # - `float(N)`: Second float operand. + # + # Outputs: + # - `float(N)`: Difference of the inputs. + + mul @4 :Void; + # Multiply two floats. + # + # Inputs: + # - `float(N)`: First float operand. + # - `float(N)`: Second float operand. + # + # Outputs: + # - `float(N)`: Product of the inputs. + + pow @5 :Void; + # Calculate one float raised to the power of another. + # + # Inputs: + # - `float(N)`: The base. + # - `float(N)`: The exponent. + # + # Outputs: + # - `float(N)`: The base raised to the power of the exponent. + + eq @6 :Void; + # Test two floats for equality. + # + # Inputs: + # - `float(N)`: First float operand. + # - `float(N)`: Second float operand. + # + # Outputs: + # - `int(1)`: True if equal, false otherwise. + + lt @7 :Void; + # Check if one float is strictly less than another. + # + # Inputs: + # - `float(N)`: First float operand. + # - `float(N)`: Second float operand. + # + # Outputs: + # - `int(1)`: True if first is less than second, false otherwise. + + lte @8 :Void; + # Check if one float is less than or equal to another. + # + # Inputs: + # - `float(N)`: First float operand. + # - `float(N)`: Second float operand. + # + # Outputs: + # - `int(1)`: True if first is less than or equal to second, false otherwise. + + sqrt @9 :Void; + # Calculate the square root of a float. + # + # Inputs: + # - `float(N)`: The float operand. + # + # Outputs: + # - `float(N)`: The square root of the input. + + abs @10 :Void; + # Calculate the absolute value of a float. + # + # Inputs: + # - `float(N)`: The float operand. + # + # Outputs: + # - `float(N)`: The absolute value of the input. + + ceil @11 :Void; + # Round a float up to the nearest integer. + # + # Inputs: + # - `float(N)`: The float operand. + # + # Outputs: + # - `float(N)`: The ceiling of the input. + + floor @12 :Void; + # Round a float down to the nearest integer. + # + # Inputs: + # - `float(N)`: The float operand. + # + # Outputs: + # - `float(N)`: The floor of the input. + + isNan @13 :Void; + # Check if a float is NaN. + # + # Inputs: + # - `float(N)`: The float operand. + # + # Outputs: + # - `int(1)`: True if NaN, false otherwise. + + isInf @14 :Void; + # Check if a float is infinite. + # + # Inputs: + # - `float(N)`: The float operand. + # + # Outputs: + # - `int(1)`: True if infinite, false otherwise. + + exp @15 :Void; + # Calculate e raised to the power of a float. + # + # Inputs: + # - `float(N)`: The float operand. + # + # Outputs: + # - `float(N)`: e raised to the input power. + + log @16 :Void; + # Calculate the natural logarithm of a float. + # + # Inputs: + # - `float(N)`: The float operand. + # + # Outputs: + # - `float(N)`: The natural logarithm of the input. + + sin @17 :Void; + # Calculate the sine of a float. + # + # Inputs: + # - `float(N)`: The float operand in radians. + # + # Outputs: + # - `float(N)`: The sine of the input. + + cos @18 :Void; + # Calculate the cosine of a float. + # + # Inputs: + # - `float(N)`: The float operand in radians. + # + # Outputs: + # - `float(N)`: The cosine of the input. + + tan @19 :Void; + # Calculate the tangent of a float. + # + # Inputs: + # - `float(N)`: The float operand in radians. + # + # Outputs: + # - `float(N)`: The tangent of the input. + + asin @20 :Void; + # Calculate the arcsine of a float. + # + # Inputs: + # - `float(N)`: The float operand. + # + # Outputs: + # - `float(N)`: The arcsine of the input in radians. + + acos @21 :Void; + # Calculate the arccosine of a float. + # + # Inputs: + # - `float(N)`: The float operand. + # + # Outputs: + # - `float(N)`: The arccosine of the input in radians. + + atan @22 :Void; + # Calculate the arctangent of a float. + # + # Inputs: + # - `float(N)`: The float operand. + # + # Outputs: + # - `float(N)`: The arctangent of the input in radians. + + atan2 @23 :Void; + # Calculate the 2-argument arctangent. + # + # Inputs: + # - `float(N)`: y coordinate + # - `float(N)`: x coordinate + # + # Outputs: + # - `float(N)`: The arctangent of y/x in radians. + + sinh @24 :Void; + # Calculate the hyperbolic sine of a float. + # + # Inputs: + # - `float(N)`: The float operand. + # + # Outputs: + # - `float(N)`: The hyperbolic sine of the input. + + cosh @25 :Void; + # Calculate the hyperbolic cosine of a float. + # + # Inputs: + # - `float(N)`: The float operand. + # + # Outputs: + # - `float(N)`: The hyperbolic cosine of the input. + + tanh @26 :Void; + # Calculate the hyperbolic tangent of a float. + # + # Inputs: + # - `float(N)`: The float operand. + # + # Outputs: + # - `float(N)`: The hyperbolic tangent of the input. + + asinh @27 :Void; + # Calculate the inverse hyperbolic sine of a float. + # + # Inputs: + # - `float(N)`: The float operand. + # + # Outputs: + # - `float(N)`: The inverse hyperbolic sine of the input. + + acosh @28 :Void; + # Calculate the inverse hyperbolic cosine of a float. + # + # Inputs: + # - `float(N)`: The float operand. + # + # Outputs: + # - `float(N)`: The inverse hyperbolic cosine of the input. + + atanh @29 :Void; + # Calculate the inverse hyperbolic tangent of a float. + # + # Inputs: + # - `float(N)`: The float operand. + # + # Outputs: + # - `float(N)`: The inverse hyperbolic tangent of the input. + + max @30 :Void; + # Maximum of two floats. + # + # Inputs: + # - `float(N)`: First float operand. + # - `float(N)`: Second float operand. + # + # Outputs: + # - `float(N)`: Maximum of the inputs. + + min @31 :Void; + # Minimum of two floats. + # + # Inputs: + # - `float(N)`: First float operand. + # - `float(N)`: Second float operand. + # + # Outputs: + # - `float(N)`: Minimum of the inputs. + } +} + +struct FloatArrayOp { + # Operations for float arrays. + + union { + const32 @0 :List(Float32); + # Create a constant 32 bit float array. + # + # Outputs: + # - `float_array(32)`: The constant 32 bit float array. + + const64 @1 :List(Float64); + # Create a constant 64 bit float array. + # + # Outputs: + # - `float_array(64)`: The constant 64 bit float array. + + zero @2 :FloatPrecision; + # Create a zeroed float array of a given precision with dynamic length. + # + # Inputs: + # - `int(32)`: The length of the float array. + # + # Outputs: + # - `float_array(N)`: The zeroed float array. + + getIndex @3 :Void; + # Get the value of a float array at a given index. + # + # Inputs: + # - `float_array(N)`: The float array. + # - `int(32)`: The index of the value to get. + # + # Outputs: + # - `float(N)`: The value at the given index. + + setIndex @4 :Void; + # Set the value of a float array at a given index. + # + # Inputs: + # - `float_array(N)`: The float array. + # - `int(32)`: The index to set. + # - `float(N)`: The value to set. + # + # Outputs: + # - `float_array(N)`: The modified float array. + + length @5 :Void; + # Get the length of a float array. + # + # Inputs: + # - `float_array(N)`: The float array. + # + # Outputs: + # - `int(32)`: The length of the float array. + + create @6 :Void; + # Creates a float array from a variable number of input values. + # + # Inputs: + # - `... float(N)`: The float values that the array should contain. + # + # Outputs: + # - `float_array(N)`: The float array containing the input values. + } +} + +struct ScfOp { + # Operations for structured control flow. + + union { + switch :group { + # Switch statement. + # + # The first input to the switch is an integer that selects the branch by index. + # The operation has a region for every branch, together with an optional default region. + # If there is no default region, it is an error if the index does not match any branch. + # + # Inputs: + # - `int(N)`: The value to switch on. + # - `... inputs`: Any number of input values that are passed to the chosen branch. + # + # Outputs: + # - `... outputs`: Any number of output values that are returned from the chosen branch. + # + # Each region must have the signature `(...inputs) -> (... outputs)`. + + branches @0 :List(Region); + # The branches of the switch. + + default @1 :Region; + # The optional default branch of the switch. + } + + for @2 :Region; + # For loop. + # + # The loop iterates from start to stop (exclusive) by step. + # The region is the loop body that is executed once for each iteration. + # The loop maintains a state consisting of any number of values. + # Each iteration receives the state from the previous iteration, + # or the initial state for the first iteration. + # When the loop finishes, the final state is returned. + # Iterations also have access to the current iteration value. + # + # Inputs: + # - `int(N)`: The (signed) start value. + # - `int(N)`: The (signed) stop value (exclusive). + # - `int(N)`: The (signed) step value. + # - `... state`: Any number of values that are passed to the loop body. + # + # Outputs: + # - `... state`: Any number of values that are returned from the loop body. + # + # The region must have the signature `(int(N), ... state) -> (... state)`. + # The first parameter is the current iteration value. + # It is undefined behavior if step is zero. + + while :group { + # While loop. + # + # The condition is checked before each iteration. + # If the condition is true, the loop body is executed. + # The loop maintains a state consisting of any number of values. + # Each iteration receives the state from the previous iteration, + # or the initial state for the first iteration. + # When the loop finishes, the final state is returned. + # + # Inputs: + # - `... state`: Any number of values that are passed to the condition and body. + # + # Outputs: + # - `... state`: Any number of values that are returned from the body. + + condition @3 :Region; + # The condition region that determines whether to continue looping. + # + # The region must have the signature `(... state) -> (int(1))`. + # The output is the condition result - true to continue, false to stop. + # The condition can only evaluate the state, not modify it. + + body @4 :Region; + # The body region that is executed on each iteration. + # + # The region must have the signature `(... state) -> (... state)`. + # The outputs are passed as inputs to the condition region for the next iteration. + } + + doWhile :group { + # Do-while loop. + # + # The loop body is executed once, then the condition is checked. + # If the condition is true, the loop body is executed again. + # The loop maintains a state consisting of any number of values. + # Each iteration receives the state from the previous iteration, + # or the initial state for the first iteration. + # When the loop finishes, the final state is returned. + # + # Inputs: + # - `... state`: Any number of values that are passed to the condition and body. + # + # Outputs: + # - `... state`: Any number of values that are returned from the body. + + body @5 :Region; + # The body region that is executed on each iteration. + # + # The region must have the signature `(... state) -> (... state)`. + # The outputs are passed as inputs to the condition region for the current iteration. + + condition @6 :Region; + # The condition region that determines whether to continue looping. + # + # The region must have the signature `(... state) -> (int(1))`. + # The output is the condition result - true to continue, false to stop. + # The condition can only evaluate the state, not modify it. + } + } +} + +struct FuncOp { + funcCall @0 :FuncIndex; + # Call a function. +} diff --git a/impl/py/src/jeff/py.typed b/impl/py/src/jeff/py.typed new file mode 100644 index 0000000..e69de29 diff --git a/impl/py/tests/__init__.py b/impl/py/tests/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/impl/py/tests/test_reader.py b/impl/py/tests/test_reader.py new file mode 100644 index 0000000..e06e537 --- /dev/null +++ b/impl/py/tests/test_reader.py @@ -0,0 +1,2 @@ +def test_hello_world() -> None: + assert 2 + 2 != "🐟" diff --git a/justfile b/justfile index 018ab1e..db8699d 100644 --- a/justfile +++ b/justfile @@ -11,6 +11,15 @@ setup: check: uv run pre-commit run --all-files +# Run the tests for all the bindings. +test: test-rs test-py +# Run the tests for the rust code. +test-rs *TEST_ARGS: + cargo test {{TEST_ARGS}} +# Run the tests for the python code. +test-py *TEST_ARGS: + uv run pytest {{TEST_ARGS}} + # Auto-fix all lints. fix: fix-rs fix-py # Auto-fix all the lints in the rust code. @@ -42,6 +51,8 @@ coverage-py: update-capnp: # Always use the latest version of capnproto-rust cargo install capnpc + # Copy the definition to the python package + cp impl/capnp/jeff.capnp impl/py/src/jeff-format/data/jeff.capnp # Re-generate rust capnp files capnp compile -orust:impl/rs/src --src-prefix=impl impl/capnp/jeff.capnp # Re-generate c++ capnp files diff --git a/pyproject.toml b/pyproject.toml index b35fd43..8376797 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,16 +1,14 @@ [tool.uv.workspace] -members = [] - -[tool.uv] - -dev-dependencies = [ - "pre-commit >=4.1,<5", - "pytest >=8.3.4,<9", - "pytest-cov >=6.0.0,<7", - "mypy >=1.15.0,<2", - "ruff >=0.9.6,<0.10", -] +members = ["impl/py"] +[dependency-groups] +dev = [{ include-group = "lint" }, { include-group = "test" }] +lint = ["pre-commit ~=4.3", "ruff ~=0.12.10", "mypy ~=1.17"] +test = ["pytest ~=8.4", "pytest-cov ~=6.2", "mypy ~=1.17"] [tool.mypy] strict = true + +[build-system] +requires = ["hatchling"] +build-backend = "hatchling.build" diff --git a/uv.lock b/uv.lock index eaaf76c..bb9a3d7 100644 --- a/uv.lock +++ b/uv.lock @@ -1,16 +1,29 @@ version = 1 revision = 3 -requires-python = ">=3.13" +requires-python = ">=3.10" [manifest] +members = [ + "jeff-format", +] [manifest.dependency-groups] dev = [ - { name = "mypy", specifier = ">=1.15.0,<2" }, - { name = "pre-commit", specifier = ">=4.1,<5" }, - { name = "pytest", specifier = ">=8.3.4,<9" }, - { name = "pytest-cov", specifier = ">=6.0.0,<7" }, - { name = "ruff", specifier = ">=0.9.6,<0.10" }, + { name = "mypy", specifier = "~=1.17" }, + { name = "pre-commit", specifier = "~=4.3" }, + { name = "pytest", specifier = "~=8.4" }, + { name = "pytest-cov", specifier = "~=6.2" }, + { name = "ruff", specifier = "~=0.12.10" }, +] +lint = [ + { name = "mypy", specifier = "~=1.17" }, + { name = "pre-commit", specifier = "~=4.3" }, + { name = "ruff", specifier = "~=0.12.10" }, +] +test = [ + { name = "mypy", specifier = "~=1.17" }, + { name = "pytest", specifier = "~=8.4" }, + { name = "pytest-cov", specifier = "~=6.2" }, ] [[package]] @@ -37,6 +50,38 @@ version = "7.10.4" source = { registry = "https://pypi.org/simple" } sdist = { url = "https://files.pythonhosted.org/packages/d6/4e/08b493f1f1d8a5182df0044acc970799b58a8d289608e0d891a03e9d269a/coverage-7.10.4.tar.gz", hash = "sha256:25f5130af6c8e7297fd14634955ba9e1697f47143f289e2a23284177c0061d27", size = 823798, upload-time = "2025-08-17T00:26:43.314Z" } wheels = [ + { url = "https://files.pythonhosted.org/packages/9c/f4/350759710db50362685f922259c140592dba15eb4e2325656a98413864d9/coverage-7.10.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d92d6edb0ccafd20c6fbf9891ca720b39c2a6a4b4a6f9cf323ca2c986f33e475", size = 216403, upload-time = "2025-08-17T00:24:19.083Z" }, + { url = "https://files.pythonhosted.org/packages/29/7e/e467c2bb4d5ecfd166bfd22c405cce4c50de2763ba1d78e2729c59539a42/coverage-7.10.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:7202da14dc0236884fcc45665ffb2d79d4991a53fbdf152ab22f69f70923cc22", size = 216802, upload-time = "2025-08-17T00:24:21.824Z" }, + { url = "https://files.pythonhosted.org/packages/62/ab/2accdd1ccfe63b890e5eb39118f63c155202df287798364868a2884a50af/coverage-7.10.4-cp310-cp310-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:ada418633ae24ec8d0fcad5efe6fc7aa3c62497c6ed86589e57844ad04365674", size = 243558, upload-time = "2025-08-17T00:24:23.569Z" }, + { url = "https://files.pythonhosted.org/packages/43/04/c14c33d0cfc0f4db6b3504d01a47f4c798563d932a836fd5f2dbc0521d3d/coverage-7.10.4-cp310-cp310-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:b828e33eca6c3322adda3b5884456f98c435182a44917ded05005adfa1415500", size = 245370, upload-time = "2025-08-17T00:24:24.858Z" }, + { url = "https://files.pythonhosted.org/packages/99/71/147053061f1f51c1d3b3d040c3cb26876964a3a0dca0765d2441411ca568/coverage-7.10.4-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:802793ba397afcfdbe9f91f89d65ae88b958d95edc8caf948e1f47d8b6b2b606", size = 247228, upload-time = "2025-08-17T00:24:26.167Z" }, + { url = "https://files.pythonhosted.org/packages/cc/92/7ef882205d4d4eb502e6154ee7122c1a1b1ce3f29d0166921e0fb550a5d3/coverage-7.10.4-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:d0b23512338c54101d3bf7a1ab107d9d75abda1d5f69bc0887fd079253e4c27e", size = 245270, upload-time = "2025-08-17T00:24:27.424Z" }, + { url = "https://files.pythonhosted.org/packages/ab/3d/297a20603abcc6c7d89d801286eb477b0b861f3c5a4222730f1c9837be3e/coverage-7.10.4-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:f36b7dcf72d06a8c5e2dd3aca02be2b1b5db5f86404627dff834396efce958f2", size = 243287, upload-time = "2025-08-17T00:24:28.697Z" }, + { url = "https://files.pythonhosted.org/packages/65/f9/b04111438f41f1ddd5dc88706d5f8064ae5bb962203c49fe417fa23a362d/coverage-7.10.4-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:fce316c367a1dc2c411821365592eeb335ff1781956d87a0410eae248188ba51", size = 244164, upload-time = "2025-08-17T00:24:30.393Z" }, + { url = "https://files.pythonhosted.org/packages/1e/e5/c7d9eb7a9ea66cf92d069077719fb2b07782dcd7050b01a9b88766b52154/coverage-7.10.4-cp310-cp310-win32.whl", hash = "sha256:8c5dab29fc8070b3766b5fc85f8d89b19634584429a2da6d42da5edfadaf32ae", size = 218917, upload-time = "2025-08-17T00:24:31.67Z" }, + { url = "https://files.pythonhosted.org/packages/66/30/4d9d3b81f5a836b31a7428b8a25e6d490d4dca5ff2952492af130153c35c/coverage-7.10.4-cp310-cp310-win_amd64.whl", hash = "sha256:4b0d114616f0fccb529a1817457d5fb52a10e106f86c5fb3b0bd0d45d0d69b93", size = 219822, upload-time = "2025-08-17T00:24:32.89Z" }, + { url = "https://files.pythonhosted.org/packages/ec/ba/2c9817e62018e7d480d14f684c160b3038df9ff69c5af7d80e97d143e4d1/coverage-7.10.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:05d5f98ec893d4a2abc8bc5f046f2f4367404e7e5d5d18b83de8fde1093ebc4f", size = 216514, upload-time = "2025-08-17T00:24:34.188Z" }, + { url = "https://files.pythonhosted.org/packages/e3/5a/093412a959a6b6261446221ba9fb23bb63f661a5de70b5d130763c87f916/coverage-7.10.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:9267efd28f8994b750d171e58e481e3bbd69e44baed540e4c789f8e368b24b88", size = 216914, upload-time = "2025-08-17T00:24:35.881Z" }, + { url = "https://files.pythonhosted.org/packages/2c/1f/2fdf4a71cfe93b07eae845ebf763267539a7d8b7e16b062f959d56d7e433/coverage-7.10.4-cp311-cp311-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:4456a039fdc1a89ea60823d0330f1ac6f97b0dbe9e2b6fb4873e889584b085fb", size = 247308, upload-time = "2025-08-17T00:24:37.61Z" }, + { url = "https://files.pythonhosted.org/packages/ba/16/33f6cded458e84f008b9f6bc379609a6a1eda7bffe349153b9960803fc11/coverage-7.10.4-cp311-cp311-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:c2bfbd2a9f7e68a21c5bd191be94bfdb2691ac40d325bac9ef3ae45ff5c753d9", size = 249241, upload-time = "2025-08-17T00:24:38.919Z" }, + { url = "https://files.pythonhosted.org/packages/84/98/9c18e47c889be58339ff2157c63b91a219272503ee32b49d926eea2337f2/coverage-7.10.4-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0ab7765f10ae1df7e7fe37de9e64b5a269b812ee22e2da3f84f97b1c7732a0d8", size = 251346, upload-time = "2025-08-17T00:24:40.507Z" }, + { url = "https://files.pythonhosted.org/packages/6d/07/00a6c0d53e9a22d36d8e95ddd049b860eef8f4b9fd299f7ce34d8e323356/coverage-7.10.4-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:0a09b13695166236e171ec1627ff8434b9a9bae47528d0ba9d944c912d33b3d2", size = 249037, upload-time = "2025-08-17T00:24:41.904Z" }, + { url = "https://files.pythonhosted.org/packages/3e/0e/1e1b944d6a6483d07bab5ef6ce063fcf3d0cc555a16a8c05ebaab11f5607/coverage-7.10.4-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:5c9e75dfdc0167d5675e9804f04a56b2cf47fb83a524654297000b578b8adcb7", size = 247090, upload-time = "2025-08-17T00:24:43.193Z" }, + { url = "https://files.pythonhosted.org/packages/62/43/2ce5ab8a728b8e25ced077111581290ffaef9efaf860a28e25435ab925cf/coverage-7.10.4-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:c751261bfe6481caba15ec005a194cb60aad06f29235a74c24f18546d8377df0", size = 247732, upload-time = "2025-08-17T00:24:44.906Z" }, + { url = "https://files.pythonhosted.org/packages/a4/f3/706c4a24f42c1c5f3a2ca56637ab1270f84d9e75355160dc34d5e39bb5b7/coverage-7.10.4-cp311-cp311-win32.whl", hash = "sha256:051c7c9e765f003c2ff6e8c81ccea28a70fb5b0142671e4e3ede7cebd45c80af", size = 218961, upload-time = "2025-08-17T00:24:46.241Z" }, + { url = "https://files.pythonhosted.org/packages/e8/aa/6b9ea06e0290bf1cf2a2765bba89d561c5c563b4e9db8298bf83699c8b67/coverage-7.10.4-cp311-cp311-win_amd64.whl", hash = "sha256:1a647b152f10be08fb771ae4a1421dbff66141e3d8ab27d543b5eb9ea5af8e52", size = 219851, upload-time = "2025-08-17T00:24:48.795Z" }, + { url = "https://files.pythonhosted.org/packages/8b/be/f0dc9ad50ee183369e643cd7ed8f2ef5c491bc20b4c3387cbed97dd6e0d1/coverage-7.10.4-cp311-cp311-win_arm64.whl", hash = "sha256:b09b9e4e1de0d406ca9f19a371c2beefe3193b542f64a6dd40cfcf435b7d6aa0", size = 218530, upload-time = "2025-08-17T00:24:50.164Z" }, + { url = "https://files.pythonhosted.org/packages/9e/4a/781c9e4dd57cabda2a28e2ce5b00b6be416015265851060945a5ed4bd85e/coverage-7.10.4-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:a1f0264abcabd4853d4cb9b3d164adbf1565da7dab1da1669e93f3ea60162d79", size = 216706, upload-time = "2025-08-17T00:24:51.528Z" }, + { url = "https://files.pythonhosted.org/packages/6a/8c/51255202ca03d2e7b664770289f80db6f47b05138e06cce112b3957d5dfd/coverage-7.10.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:536cbe6b118a4df231b11af3e0f974a72a095182ff8ec5f4868c931e8043ef3e", size = 216939, upload-time = "2025-08-17T00:24:53.171Z" }, + { url = "https://files.pythonhosted.org/packages/06/7f/df11131483698660f94d3c847dc76461369782d7a7644fcd72ac90da8fd0/coverage-7.10.4-cp312-cp312-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:9a4c0d84134797b7bf3f080599d0cd501471f6c98b715405166860d79cfaa97e", size = 248429, upload-time = "2025-08-17T00:24:54.934Z" }, + { url = "https://files.pythonhosted.org/packages/eb/fa/13ac5eda7300e160bf98f082e75f5c5b4189bf3a883dd1ee42dbedfdc617/coverage-7.10.4-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:7c155fc0f9cee8c9803ea0ad153ab6a3b956baa5d4cd993405dc0b45b2a0b9e0", size = 251178, upload-time = "2025-08-17T00:24:56.353Z" }, + { url = "https://files.pythonhosted.org/packages/9a/bc/f63b56a58ad0bec68a840e7be6b7ed9d6f6288d790760647bb88f5fea41e/coverage-7.10.4-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0a5f2ab6e451d4b07855d8bcf063adf11e199bff421a4ba57f5bb95b7444ca62", size = 252313, upload-time = "2025-08-17T00:24:57.692Z" }, + { url = "https://files.pythonhosted.org/packages/2b/b6/79338f1ea27b01266f845afb4485976211264ab92407d1c307babe3592a7/coverage-7.10.4-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:685b67d99b945b0c221be0780c336b303a7753b3e0ec0d618c795aada25d5e7a", size = 250230, upload-time = "2025-08-17T00:24:59.293Z" }, + { url = "https://files.pythonhosted.org/packages/bc/93/3b24f1da3e0286a4dc5832427e1d448d5296f8287464b1ff4a222abeeeb5/coverage-7.10.4-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:0c079027e50c2ae44da51c2e294596cbc9dbb58f7ca45b30651c7e411060fc23", size = 248351, upload-time = "2025-08-17T00:25:00.676Z" }, + { url = "https://files.pythonhosted.org/packages/de/5f/d59412f869e49dcc5b89398ef3146c8bfaec870b179cc344d27932e0554b/coverage-7.10.4-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:3749aa72b93ce516f77cf5034d8e3c0dfd45c6e8a163a602ede2dc5f9a0bb927", size = 249788, upload-time = "2025-08-17T00:25:02.354Z" }, + { url = "https://files.pythonhosted.org/packages/cc/52/04a3b733f40a0cc7c4a5b9b010844111dbf906df3e868b13e1ce7b39ac31/coverage-7.10.4-cp312-cp312-win32.whl", hash = "sha256:fecb97b3a52fa9bcd5a7375e72fae209088faf671d39fae67261f37772d5559a", size = 219131, upload-time = "2025-08-17T00:25:03.79Z" }, + { url = "https://files.pythonhosted.org/packages/83/dd/12909fc0b83888197b3ec43a4ac7753589591c08d00d9deda4158df2734e/coverage-7.10.4-cp312-cp312-win_amd64.whl", hash = "sha256:26de58f355626628a21fe6a70e1e1fad95702dafebfb0685280962ae1449f17b", size = 219939, upload-time = "2025-08-17T00:25:05.494Z" }, + { url = "https://files.pythonhosted.org/packages/83/c7/058bb3220fdd6821bada9685eadac2940429ab3c97025ce53549ff423cc1/coverage-7.10.4-cp312-cp312-win_arm64.whl", hash = "sha256:67e8885408f8325198862bc487038a4980c9277d753cb8812510927f2176437a", size = 218572, upload-time = "2025-08-17T00:25:06.897Z" }, { url = "https://files.pythonhosted.org/packages/46/b0/4a3662de81f2ed792a4e425d59c4ae50d8dd1d844de252838c200beed65a/coverage-7.10.4-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:2b8e1d2015d5dfdbf964ecef12944c0c8c55b885bb5c0467ae8ef55e0e151233", size = 216735, upload-time = "2025-08-17T00:25:08.617Z" }, { url = "https://files.pythonhosted.org/packages/c5/e8/e2dcffea01921bfffc6170fb4406cffb763a3b43a047bbd7923566708193/coverage-7.10.4-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:25735c299439018d66eb2dccf54f625aceb78645687a05f9f848f6e6c751e169", size = 216982, upload-time = "2025-08-17T00:25:10.384Z" }, { url = "https://files.pythonhosted.org/packages/9d/59/cc89bb6ac869704d2781c2f5f7957d07097c77da0e8fdd4fd50dbf2ac9c0/coverage-7.10.4-cp313-cp313-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:715c06cb5eceac4d9b7cdf783ce04aa495f6aff657543fea75c30215b28ddb74", size = 247981, upload-time = "2025-08-17T00:25:11.854Z" }, @@ -84,6 +129,11 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/bb/78/983efd23200921d9edb6bd40512e1aa04af553d7d5a171e50f9b2b45d109/coverage-7.10.4-py3-none-any.whl", hash = "sha256:065d75447228d05121e5c938ca8f0e91eed60a1eb2d1258d42d5084fecfc3302", size = 208365, upload-time = "2025-08-17T00:26:41.479Z" }, ] +[package.optional-dependencies] +toml = [ + { name = "tomli", marker = "python_full_version <= '3.11'" }, +] + [[package]] name = "distlib" version = "0.4.0" @@ -93,6 +143,18 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/33/6b/e0547afaf41bf2c42e52430072fa5658766e3d65bd4b03a563d1b6336f57/distlib-0.4.0-py2.py3-none-any.whl", hash = "sha256:9659f7d87e46584a30b5780e43ac7a2143098441670ff0a49d5f9034c54a6c16", size = 469047, upload-time = "2025-07-17T16:51:58.613Z" }, ] +[[package]] +name = "exceptiongroup" +version = "1.3.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "typing-extensions", marker = "python_full_version < '3.13'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/0b/9f/a65090624ecf468cdca03533906e7c69ed7588582240cfe7cc9e770b50eb/exceptiongroup-1.3.0.tar.gz", hash = "sha256:b241f5885f560bc56a59ee63ca4c6a8bfa46ae4ad651af316d4e81817bb9fd88", size = 29749, upload-time = "2025-05-10T17:42:51.123Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/36/f4/c6e662dade71f56cd2f3735141b265c3c79293c109549c1e6933b0651ffc/exceptiongroup-1.3.0-py3-none-any.whl", hash = "sha256:4d111e6e0c13d0644cad6ddaa7ed0261a0b36971f6d23e7ec9b4b9097da78a10", size = 16674, upload-time = "2025-05-10T17:42:49.33Z" }, +] + [[package]] name = "filelock" version = "3.19.1" @@ -120,6 +182,11 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/2c/e1/e6716421ea10d38022b952c159d5161ca1193197fb744506875fbb87ea7b/iniconfig-2.1.0-py3-none-any.whl", hash = "sha256:9deba5723312380e77435581c6bf4935c94cbfab9b1ed33ef8d238ea168eb760", size = 6050, upload-time = "2025-03-19T20:10:01.071Z" }, ] +[[package]] +name = "jeff-format" +version = "0.1.0" +source = { editable = "impl/py" } + [[package]] name = "mypy" version = "1.17.1" @@ -127,10 +194,29 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "mypy-extensions" }, { name = "pathspec" }, + { name = "tomli", marker = "python_full_version < '3.11'" }, { name = "typing-extensions" }, ] sdist = { url = "https://files.pythonhosted.org/packages/8e/22/ea637422dedf0bf36f3ef238eab4e455e2a0dcc3082b5cc067615347ab8e/mypy-1.17.1.tar.gz", hash = "sha256:25e01ec741ab5bb3eec8ba9cdb0f769230368a22c959c4937360efb89b7e9f01", size = 3352570, upload-time = "2025-07-31T07:54:19.204Z" } wheels = [ + { url = "https://files.pythonhosted.org/packages/77/a9/3d7aa83955617cdf02f94e50aab5c830d205cfa4320cf124ff64acce3a8e/mypy-1.17.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:3fbe6d5555bf608c47203baa3e72dbc6ec9965b3d7c318aa9a4ca76f465bd972", size = 11003299, upload-time = "2025-07-31T07:54:06.425Z" }, + { url = "https://files.pythonhosted.org/packages/83/e8/72e62ff837dd5caaac2b4a5c07ce769c8e808a00a65e5d8f94ea9c6f20ab/mypy-1.17.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:80ef5c058b7bce08c83cac668158cb7edea692e458d21098c7d3bce35a5d43e7", size = 10125451, upload-time = "2025-07-31T07:53:52.974Z" }, + { url = "https://files.pythonhosted.org/packages/7d/10/f3f3543f6448db11881776f26a0ed079865926b0c841818ee22de2c6bbab/mypy-1.17.1-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c4a580f8a70c69e4a75587bd925d298434057fe2a428faaf927ffe6e4b9a98df", size = 11916211, upload-time = "2025-07-31T07:53:18.879Z" }, + { url = "https://files.pythonhosted.org/packages/06/bf/63e83ed551282d67bb3f7fea2cd5561b08d2bb6eb287c096539feb5ddbc5/mypy-1.17.1-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:dd86bb649299f09d987a2eebb4d52d10603224500792e1bee18303bbcc1ce390", size = 12652687, upload-time = "2025-07-31T07:53:30.544Z" }, + { url = "https://files.pythonhosted.org/packages/69/66/68f2eeef11facf597143e85b694a161868b3b006a5fbad50e09ea117ef24/mypy-1.17.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:a76906f26bd8d51ea9504966a9c25419f2e668f012e0bdf3da4ea1526c534d94", size = 12896322, upload-time = "2025-07-31T07:53:50.74Z" }, + { url = "https://files.pythonhosted.org/packages/a3/87/8e3e9c2c8bd0d7e071a89c71be28ad088aaecbadf0454f46a540bda7bca6/mypy-1.17.1-cp310-cp310-win_amd64.whl", hash = "sha256:e79311f2d904ccb59787477b7bd5d26f3347789c06fcd7656fa500875290264b", size = 9507962, upload-time = "2025-07-31T07:53:08.431Z" }, + { url = "https://files.pythonhosted.org/packages/46/cf/eadc80c4e0a70db1c08921dcc220357ba8ab2faecb4392e3cebeb10edbfa/mypy-1.17.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:ad37544be07c5d7fba814eb370e006df58fed8ad1ef33ed1649cb1889ba6ff58", size = 10921009, upload-time = "2025-07-31T07:53:23.037Z" }, + { url = "https://files.pythonhosted.org/packages/5d/c1/c869d8c067829ad30d9bdae051046561552516cfb3a14f7f0347b7d973ee/mypy-1.17.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:064e2ff508e5464b4bd807a7c1625bc5047c5022b85c70f030680e18f37273a5", size = 10047482, upload-time = "2025-07-31T07:53:26.151Z" }, + { url = "https://files.pythonhosted.org/packages/98/b9/803672bab3fe03cee2e14786ca056efda4bb511ea02dadcedde6176d06d0/mypy-1.17.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:70401bbabd2fa1aa7c43bb358f54037baf0586f41e83b0ae67dd0534fc64edfd", size = 11832883, upload-time = "2025-07-31T07:53:47.948Z" }, + { url = "https://files.pythonhosted.org/packages/88/fb/fcdac695beca66800918c18697b48833a9a6701de288452b6715a98cfee1/mypy-1.17.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e92bdc656b7757c438660f775f872a669b8ff374edc4d18277d86b63edba6b8b", size = 12566215, upload-time = "2025-07-31T07:54:04.031Z" }, + { url = "https://files.pythonhosted.org/packages/7f/37/a932da3d3dace99ee8eb2043b6ab03b6768c36eb29a02f98f46c18c0da0e/mypy-1.17.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:c1fdf4abb29ed1cb091cf432979e162c208a5ac676ce35010373ff29247bcad5", size = 12751956, upload-time = "2025-07-31T07:53:36.263Z" }, + { url = "https://files.pythonhosted.org/packages/8c/cf/6438a429e0f2f5cab8bc83e53dbebfa666476f40ee322e13cac5e64b79e7/mypy-1.17.1-cp311-cp311-win_amd64.whl", hash = "sha256:ff2933428516ab63f961644bc49bc4cbe42bbffb2cd3b71cc7277c07d16b1a8b", size = 9507307, upload-time = "2025-07-31T07:53:59.734Z" }, + { url = "https://files.pythonhosted.org/packages/17/a2/7034d0d61af8098ec47902108553122baa0f438df8a713be860f7407c9e6/mypy-1.17.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:69e83ea6553a3ba79c08c6e15dbd9bfa912ec1e493bf75489ef93beb65209aeb", size = 11086295, upload-time = "2025-07-31T07:53:28.124Z" }, + { url = "https://files.pythonhosted.org/packages/14/1f/19e7e44b594d4b12f6ba8064dbe136505cec813549ca3e5191e40b1d3cc2/mypy-1.17.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:1b16708a66d38abb1e6b5702f5c2c87e133289da36f6a1d15f6a5221085c6403", size = 10112355, upload-time = "2025-07-31T07:53:21.121Z" }, + { url = "https://files.pythonhosted.org/packages/5b/69/baa33927e29e6b4c55d798a9d44db5d394072eef2bdc18c3e2048c9ed1e9/mypy-1.17.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:89e972c0035e9e05823907ad5398c5a73b9f47a002b22359b177d40bdaee7056", size = 11875285, upload-time = "2025-07-31T07:53:55.293Z" }, + { url = "https://files.pythonhosted.org/packages/90/13/f3a89c76b0a41e19490b01e7069713a30949d9a6c147289ee1521bcea245/mypy-1.17.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:03b6d0ed2b188e35ee6d5c36b5580cffd6da23319991c49ab5556c023ccf1341", size = 12737895, upload-time = "2025-07-31T07:53:43.623Z" }, + { url = "https://files.pythonhosted.org/packages/23/a1/c4ee79ac484241301564072e6476c5a5be2590bc2e7bfd28220033d2ef8f/mypy-1.17.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:c837b896b37cd103570d776bda106eabb8737aa6dd4f248451aecf53030cdbeb", size = 12931025, upload-time = "2025-07-31T07:54:17.125Z" }, + { url = "https://files.pythonhosted.org/packages/89/b8/7409477be7919a0608900e6320b155c72caab4fef46427c5cc75f85edadd/mypy-1.17.1-cp312-cp312-win_amd64.whl", hash = "sha256:665afab0963a4b39dff7c1fa563cc8b11ecff7910206db4b2e64dd1ba25aed19", size = 9584664, upload-time = "2025-07-31T07:54:12.842Z" }, { url = "https://files.pythonhosted.org/packages/5b/82/aec2fc9b9b149f372850291827537a508d6c4d3664b1750a324b91f71355/mypy-1.17.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:93378d3203a5c0800c6b6d850ad2f19f7a3cdf1a3701d3416dbf128805c6a6a7", size = 11075338, upload-time = "2025-07-31T07:53:38.873Z" }, { url = "https://files.pythonhosted.org/packages/07/ac/ee93fbde9d2242657128af8c86f5d917cd2887584cf948a8e3663d0cd737/mypy-1.17.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:15d54056f7fe7a826d897789f53dd6377ec2ea8ba6f776dc83c2902b899fee81", size = 10113066, upload-time = "2025-07-31T07:54:14.707Z" }, { url = "https://files.pythonhosted.org/packages/5a/68/946a1e0be93f17f7caa56c45844ec691ca153ee8b62f21eddda336a2d203/mypy-1.17.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:209a58fed9987eccc20f2ca94afe7257a8f46eb5df1fb69958650973230f91e6", size = 11875473, upload-time = "2025-07-31T07:53:14.504Z" }, @@ -231,10 +317,12 @@ version = "8.4.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "colorama", marker = "sys_platform == 'win32'" }, + { name = "exceptiongroup", marker = "python_full_version < '3.11'" }, { name = "iniconfig" }, { name = "packaging" }, { name = "pluggy" }, { name = "pygments" }, + { name = "tomli", marker = "python_full_version < '3.11'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/08/ba/45911d754e8eba3d5a841a5ce61a65a685ff1798421ac054f85aa8747dfb/pytest-8.4.1.tar.gz", hash = "sha256:7c67fd69174877359ed9371ec3af8a3d2b04741818c51e5e99cc1742251fa93c", size = 1517714, upload-time = "2025-06-18T05:48:06.109Z" } wheels = [ @@ -246,7 +334,7 @@ name = "pytest-cov" version = "6.2.1" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "coverage" }, + { name = "coverage", extra = ["toml"] }, { name = "pluggy" }, { name = "pytest" }, ] @@ -261,6 +349,33 @@ version = "6.0.2" source = { registry = "https://pypi.org/simple" } sdist = { url = "https://files.pythonhosted.org/packages/54/ed/79a089b6be93607fa5cdaedf301d7dfb23af5f25c398d5ead2525b063e17/pyyaml-6.0.2.tar.gz", hash = "sha256:d584d9ec91ad65861cc08d42e834324ef890a082e591037abe114850ff7bbc3e", size = 130631, upload-time = "2024-08-06T20:33:50.674Z" } wheels = [ + { url = "https://files.pythonhosted.org/packages/9b/95/a3fac87cb7158e231b5a6012e438c647e1a87f09f8e0d123acec8ab8bf71/PyYAML-6.0.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0a9a2848a5b7feac301353437eb7d5957887edbf81d56e903999a75a3d743086", size = 184199, upload-time = "2024-08-06T20:31:40.178Z" }, + { url = "https://files.pythonhosted.org/packages/c7/7a/68bd47624dab8fd4afbfd3c48e3b79efe09098ae941de5b58abcbadff5cb/PyYAML-6.0.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:29717114e51c84ddfba879543fb232a6ed60086602313ca38cce623c1d62cfbf", size = 171758, upload-time = "2024-08-06T20:31:42.173Z" }, + { url = "https://files.pythonhosted.org/packages/49/ee/14c54df452143b9ee9f0f29074d7ca5516a36edb0b4cc40c3f280131656f/PyYAML-6.0.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8824b5a04a04a047e72eea5cec3bc266db09e35de6bdfe34c9436ac5ee27d237", size = 718463, upload-time = "2024-08-06T20:31:44.263Z" }, + { url = "https://files.pythonhosted.org/packages/4d/61/de363a97476e766574650d742205be468921a7b532aa2499fcd886b62530/PyYAML-6.0.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7c36280e6fb8385e520936c3cb3b8042851904eba0e58d277dca80a5cfed590b", size = 719280, upload-time = "2024-08-06T20:31:50.199Z" }, + { url = "https://files.pythonhosted.org/packages/6b/4e/1523cb902fd98355e2e9ea5e5eb237cbc5f3ad5f3075fa65087aa0ecb669/PyYAML-6.0.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ec031d5d2feb36d1d1a24380e4db6d43695f3748343d99434e6f5f9156aaa2ed", size = 751239, upload-time = "2024-08-06T20:31:52.292Z" }, + { url = "https://files.pythonhosted.org/packages/b7/33/5504b3a9a4464893c32f118a9cc045190a91637b119a9c881da1cf6b7a72/PyYAML-6.0.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:936d68689298c36b53b29f23c6dbb74de12b4ac12ca6cfe0e047bedceea56180", size = 695802, upload-time = "2024-08-06T20:31:53.836Z" }, + { url = "https://files.pythonhosted.org/packages/5c/20/8347dcabd41ef3a3cdc4f7b7a2aff3d06598c8779faa189cdbf878b626a4/PyYAML-6.0.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:23502f431948090f597378482b4812b0caae32c22213aecf3b55325e049a6c68", size = 720527, upload-time = "2024-08-06T20:31:55.565Z" }, + { url = "https://files.pythonhosted.org/packages/be/aa/5afe99233fb360d0ff37377145a949ae258aaab831bde4792b32650a4378/PyYAML-6.0.2-cp310-cp310-win32.whl", hash = "sha256:2e99c6826ffa974fe6e27cdb5ed0021786b03fc98e5ee3c5bfe1fd5015f42b99", size = 144052, upload-time = "2024-08-06T20:31:56.914Z" }, + { url = "https://files.pythonhosted.org/packages/b5/84/0fa4b06f6d6c958d207620fc60005e241ecedceee58931bb20138e1e5776/PyYAML-6.0.2-cp310-cp310-win_amd64.whl", hash = "sha256:a4d3091415f010369ae4ed1fc6b79def9416358877534caf6a0fdd2146c87a3e", size = 161774, upload-time = "2024-08-06T20:31:58.304Z" }, + { url = "https://files.pythonhosted.org/packages/f8/aa/7af4e81f7acba21a4c6be026da38fd2b872ca46226673c89a758ebdc4fd2/PyYAML-6.0.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:cc1c1159b3d456576af7a3e4d1ba7e6924cb39de8f67111c735f6fc832082774", size = 184612, upload-time = "2024-08-06T20:32:03.408Z" }, + { url = "https://files.pythonhosted.org/packages/8b/62/b9faa998fd185f65c1371643678e4d58254add437edb764a08c5a98fb986/PyYAML-6.0.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:1e2120ef853f59c7419231f3bf4e7021f1b936f6ebd222406c3b60212205d2ee", size = 172040, upload-time = "2024-08-06T20:32:04.926Z" }, + { url = "https://files.pythonhosted.org/packages/ad/0c/c804f5f922a9a6563bab712d8dcc70251e8af811fce4524d57c2c0fd49a4/PyYAML-6.0.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5d225db5a45f21e78dd9358e58a98702a0302f2659a3c6cd320564b75b86f47c", size = 736829, upload-time = "2024-08-06T20:32:06.459Z" }, + { url = "https://files.pythonhosted.org/packages/51/16/6af8d6a6b210c8e54f1406a6b9481febf9c64a3109c541567e35a49aa2e7/PyYAML-6.0.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5ac9328ec4831237bec75defaf839f7d4564be1e6b25ac710bd1a96321cc8317", size = 764167, upload-time = "2024-08-06T20:32:08.338Z" }, + { url = "https://files.pythonhosted.org/packages/75/e4/2c27590dfc9992f73aabbeb9241ae20220bd9452df27483b6e56d3975cc5/PyYAML-6.0.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3ad2a3decf9aaba3d29c8f537ac4b243e36bef957511b4766cb0057d32b0be85", size = 762952, upload-time = "2024-08-06T20:32:14.124Z" }, + { url = "https://files.pythonhosted.org/packages/9b/97/ecc1abf4a823f5ac61941a9c00fe501b02ac3ab0e373c3857f7d4b83e2b6/PyYAML-6.0.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:ff3824dc5261f50c9b0dfb3be22b4567a6f938ccce4587b38952d85fd9e9afe4", size = 735301, upload-time = "2024-08-06T20:32:16.17Z" }, + { url = "https://files.pythonhosted.org/packages/45/73/0f49dacd6e82c9430e46f4a027baa4ca205e8b0a9dce1397f44edc23559d/PyYAML-6.0.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:797b4f722ffa07cc8d62053e4cff1486fa6dc094105d13fea7b1de7d8bf71c9e", size = 756638, upload-time = "2024-08-06T20:32:18.555Z" }, + { url = "https://files.pythonhosted.org/packages/22/5f/956f0f9fc65223a58fbc14459bf34b4cc48dec52e00535c79b8db361aabd/PyYAML-6.0.2-cp311-cp311-win32.whl", hash = "sha256:11d8f3dd2b9c1207dcaf2ee0bbbfd5991f571186ec9cc78427ba5bd32afae4b5", size = 143850, upload-time = "2024-08-06T20:32:19.889Z" }, + { url = "https://files.pythonhosted.org/packages/ed/23/8da0bbe2ab9dcdd11f4f4557ccaf95c10b9811b13ecced089d43ce59c3c8/PyYAML-6.0.2-cp311-cp311-win_amd64.whl", hash = "sha256:e10ce637b18caea04431ce14fabcf5c64a1c61ec9c56b071a4b7ca131ca52d44", size = 161980, upload-time = "2024-08-06T20:32:21.273Z" }, + { url = "https://files.pythonhosted.org/packages/86/0c/c581167fc46d6d6d7ddcfb8c843a4de25bdd27e4466938109ca68492292c/PyYAML-6.0.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:c70c95198c015b85feafc136515252a261a84561b7b1d51e3384e0655ddf25ab", size = 183873, upload-time = "2024-08-06T20:32:25.131Z" }, + { url = "https://files.pythonhosted.org/packages/a8/0c/38374f5bb272c051e2a69281d71cba6fdb983413e6758b84482905e29a5d/PyYAML-6.0.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ce826d6ef20b1bc864f0a68340c8b3287705cae2f8b4b1d932177dcc76721725", size = 173302, upload-time = "2024-08-06T20:32:26.511Z" }, + { url = "https://files.pythonhosted.org/packages/c3/93/9916574aa8c00aa06bbac729972eb1071d002b8e158bd0e83a3b9a20a1f7/PyYAML-6.0.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1f71ea527786de97d1a0cc0eacd1defc0985dcf6b3f17bb77dcfc8c34bec4dc5", size = 739154, upload-time = "2024-08-06T20:32:28.363Z" }, + { url = "https://files.pythonhosted.org/packages/95/0f/b8938f1cbd09739c6da569d172531567dbcc9789e0029aa070856f123984/PyYAML-6.0.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9b22676e8097e9e22e36d6b7bda33190d0d400f345f23d4065d48f4ca7ae0425", size = 766223, upload-time = "2024-08-06T20:32:30.058Z" }, + { url = "https://files.pythonhosted.org/packages/b9/2b/614b4752f2e127db5cc206abc23a8c19678e92b23c3db30fc86ab731d3bd/PyYAML-6.0.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:80bab7bfc629882493af4aa31a4cfa43a4c57c83813253626916b8c7ada83476", size = 767542, upload-time = "2024-08-06T20:32:31.881Z" }, + { url = "https://files.pythonhosted.org/packages/d4/00/dd137d5bcc7efea1836d6264f049359861cf548469d18da90cd8216cf05f/PyYAML-6.0.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:0833f8694549e586547b576dcfaba4a6b55b9e96098b36cdc7ebefe667dfed48", size = 731164, upload-time = "2024-08-06T20:32:37.083Z" }, + { url = "https://files.pythonhosted.org/packages/c9/1f/4f998c900485e5c0ef43838363ba4a9723ac0ad73a9dc42068b12aaba4e4/PyYAML-6.0.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8b9c7197f7cb2738065c481a0461e50ad02f18c78cd75775628afb4d7137fb3b", size = 756611, upload-time = "2024-08-06T20:32:38.898Z" }, + { url = "https://files.pythonhosted.org/packages/df/d1/f5a275fdb252768b7a11ec63585bc38d0e87c9e05668a139fea92b80634c/PyYAML-6.0.2-cp312-cp312-win32.whl", hash = "sha256:ef6107725bd54b262d6dedcc2af448a266975032bc85ef0172c5f059da6325b4", size = 140591, upload-time = "2024-08-06T20:32:40.241Z" }, + { url = "https://files.pythonhosted.org/packages/0c/e8/4f648c598b17c3d06e8753d7d13d57542b30d56e6c2dedf9c331ae56312e/PyYAML-6.0.2-cp312-cp312-win_amd64.whl", hash = "sha256:7e7401d0de89a9a855c839bc697c079a4af81cf878373abd7dc625847d25cbd8", size = 156338, upload-time = "2024-08-06T20:32:41.93Z" }, { url = "https://files.pythonhosted.org/packages/ef/e3/3af305b830494fa85d95f6d95ef7fa73f2ee1cc8ef5b495c7c3269fb835f/PyYAML-6.0.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:efdca5630322a10774e8e98e1af481aad470dd62c3170801852d752aa7a783ba", size = 181309, upload-time = "2024-08-06T20:32:43.4Z" }, { url = "https://files.pythonhosted.org/packages/45/9f/3b1c20a0b7a3200524eb0076cc027a970d320bd3a6592873c85c92a08731/PyYAML-6.0.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:50187695423ffe49e2deacb8cd10510bc361faac997de9efef88badc3bb9e2d1", size = 171679, upload-time = "2024-08-06T20:32:44.801Z" }, { url = "https://files.pythonhosted.org/packages/7c/9a/337322f27005c33bcb656c655fa78325b730324c78620e8328ae28b64d0c/PyYAML-6.0.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0ffe8360bab4910ef1b9e87fb812d8bc0a308b0d0eef8c8f44e0254ab3b07133", size = 733428, upload-time = "2024-08-06T20:32:46.432Z" }, @@ -274,27 +389,67 @@ wheels = [ [[package]] name = "ruff" -version = "0.9.10" +version = "0.12.10" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/3b/eb/8c073deb376e46ae767f4961390d17545e8535921d2f65101720ed8bd434/ruff-0.12.10.tar.gz", hash = "sha256:189ab65149d11ea69a2d775343adf5f49bb2426fc4780f65ee33b423ad2e47f9", size = 5310076, upload-time = "2025-08-21T18:23:22.595Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/24/e7/560d049d15585d6c201f9eeacd2fd130def3741323e5ccf123786e0e3c95/ruff-0.12.10-py3-none-linux_armv6l.whl", hash = "sha256:8b593cb0fb55cc8692dac7b06deb29afda78c721c7ccfed22db941201b7b8f7b", size = 11935161, upload-time = "2025-08-21T18:22:26.965Z" }, + { url = "https://files.pythonhosted.org/packages/d1/b0/ad2464922a1113c365d12b8f80ed70fcfb39764288ac77c995156080488d/ruff-0.12.10-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:ebb7333a45d56efc7c110a46a69a1b32365d5c5161e7244aaf3aa20ce62399c1", size = 12660884, upload-time = "2025-08-21T18:22:30.925Z" }, + { url = "https://files.pythonhosted.org/packages/d7/f1/97f509b4108d7bae16c48389f54f005b62ce86712120fd8b2d8e88a7cb49/ruff-0.12.10-py3-none-macosx_11_0_arm64.whl", hash = "sha256:d59e58586829f8e4a9920788f6efba97a13d1fa320b047814e8afede381c6839", size = 11872754, upload-time = "2025-08-21T18:22:34.035Z" }, + { url = "https://files.pythonhosted.org/packages/12/ad/44f606d243f744a75adc432275217296095101f83f966842063d78eee2d3/ruff-0.12.10-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:822d9677b560f1fdeab69b89d1f444bf5459da4aa04e06e766cf0121771ab844", size = 12092276, upload-time = "2025-08-21T18:22:36.764Z" }, + { url = "https://files.pythonhosted.org/packages/06/1f/ed6c265e199568010197909b25c896d66e4ef2c5e1c3808caf461f6f3579/ruff-0.12.10-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:37b4a64f4062a50c75019c61c7017ff598cb444984b638511f48539d3a1c98db", size = 11734700, upload-time = "2025-08-21T18:22:39.822Z" }, + { url = "https://files.pythonhosted.org/packages/63/c5/b21cde720f54a1d1db71538c0bc9b73dee4b563a7dd7d2e404914904d7f5/ruff-0.12.10-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2c6f4064c69d2542029b2a61d39920c85240c39837599d7f2e32e80d36401d6e", size = 13468783, upload-time = "2025-08-21T18:22:42.559Z" }, + { url = "https://files.pythonhosted.org/packages/02/9e/39369e6ac7f2a1848f22fb0b00b690492f20811a1ac5c1fd1d2798329263/ruff-0.12.10-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:059e863ea3a9ade41407ad71c1de2badfbe01539117f38f763ba42a1206f7559", size = 14436642, upload-time = "2025-08-21T18:22:45.612Z" }, + { url = "https://files.pythonhosted.org/packages/e3/03/5da8cad4b0d5242a936eb203b58318016db44f5c5d351b07e3f5e211bb89/ruff-0.12.10-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1bef6161e297c68908b7218fa6e0e93e99a286e5ed9653d4be71e687dff101cf", size = 13859107, upload-time = "2025-08-21T18:22:48.886Z" }, + { url = "https://files.pythonhosted.org/packages/19/19/dd7273b69bf7f93a070c9cec9494a94048325ad18fdcf50114f07e6bf417/ruff-0.12.10-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4f1345fbf8fb0531cd722285b5f15af49b2932742fc96b633e883da8d841896b", size = 12886521, upload-time = "2025-08-21T18:22:51.567Z" }, + { url = "https://files.pythonhosted.org/packages/c0/1d/b4207ec35e7babaee62c462769e77457e26eb853fbdc877af29417033333/ruff-0.12.10-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1f68433c4fbc63efbfa3ba5db31727db229fa4e61000f452c540474b03de52a9", size = 13097528, upload-time = "2025-08-21T18:22:54.609Z" }, + { url = "https://files.pythonhosted.org/packages/ff/00/58f7b873b21114456e880b75176af3490d7a2836033779ca42f50de3b47a/ruff-0.12.10-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:141ce3d88803c625257b8a6debf4a0473eb6eed9643a6189b68838b43e78165a", size = 13080443, upload-time = "2025-08-21T18:22:57.413Z" }, + { url = "https://files.pythonhosted.org/packages/12/8c/9e6660007fb10189ccb78a02b41691288038e51e4788bf49b0a60f740604/ruff-0.12.10-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:f3fc21178cd44c98142ae7590f42ddcb587b8e09a3b849cbc84edb62ee95de60", size = 11896759, upload-time = "2025-08-21T18:23:00.473Z" }, + { url = "https://files.pythonhosted.org/packages/67/4c/6d092bb99ea9ea6ebda817a0e7ad886f42a58b4501a7e27cd97371d0ba54/ruff-0.12.10-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:7d1a4e0bdfafcd2e3e235ecf50bf0176f74dd37902f241588ae1f6c827a36c56", size = 11701463, upload-time = "2025-08-21T18:23:03.211Z" }, + { url = "https://files.pythonhosted.org/packages/59/80/d982c55e91df981f3ab62559371380616c57ffd0172d96850280c2b04fa8/ruff-0.12.10-py3-none-musllinux_1_2_i686.whl", hash = "sha256:e67d96827854f50b9e3e8327b031647e7bcc090dbe7bb11101a81a3a2cbf1cc9", size = 12691603, upload-time = "2025-08-21T18:23:06.935Z" }, + { url = "https://files.pythonhosted.org/packages/ad/37/63a9c788bbe0b0850611669ec6b8589838faf2f4f959647f2d3e320383ae/ruff-0.12.10-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:ae479e1a18b439c59138f066ae79cc0f3ee250712a873d00dbafadaad9481e5b", size = 13164356, upload-time = "2025-08-21T18:23:10.225Z" }, + { url = "https://files.pythonhosted.org/packages/47/d4/1aaa7fb201a74181989970ebccd12f88c0fc074777027e2a21de5a90657e/ruff-0.12.10-py3-none-win32.whl", hash = "sha256:9de785e95dc2f09846c5e6e1d3a3d32ecd0b283a979898ad427a9be7be22b266", size = 11896089, upload-time = "2025-08-21T18:23:14.232Z" }, + { url = "https://files.pythonhosted.org/packages/ad/14/2ad38fd4037daab9e023456a4a40ed0154e9971f8d6aed41bdea390aabd9/ruff-0.12.10-py3-none-win_amd64.whl", hash = "sha256:7837eca8787f076f67aba2ca559cefd9c5cbc3a9852fd66186f4201b87c1563e", size = 13004616, upload-time = "2025-08-21T18:23:17.422Z" }, + { url = "https://files.pythonhosted.org/packages/24/3c/21cf283d67af33a8e6ed242396863af195a8a6134ec581524fd22b9811b6/ruff-0.12.10-py3-none-win_arm64.whl", hash = "sha256:cc138cc06ed9d4bfa9d667a65af7172b47840e1a98b02ce7011c391e54635ffc", size = 12074225, upload-time = "2025-08-21T18:23:20.137Z" }, +] + +[[package]] +name = "tomli" +version = "2.2.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/20/8e/fafaa6f15c332e73425d9c44ada85360501045d5ab0b81400076aff27cf6/ruff-0.9.10.tar.gz", hash = "sha256:9bacb735d7bada9cfb0f2c227d3658fc443d90a727b47f206fb33f52f3c0eac7", size = 3759776, upload-time = "2025-03-07T15:27:44.363Z" } +sdist = { url = "https://files.pythonhosted.org/packages/18/87/302344fed471e44a87289cf4967697d07e532f2421fdaf868a303cbae4ff/tomli-2.2.1.tar.gz", hash = "sha256:cd45e1dc79c835ce60f7404ec8119f2eb06d38b1deba146f07ced3bbc44505ff", size = 17175, upload-time = "2024-11-27T22:38:36.873Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/73/b2/af7c2cc9e438cbc19fafeec4f20bfcd72165460fe75b2b6e9a0958c8c62b/ruff-0.9.10-py3-none-linux_armv6l.whl", hash = "sha256:eb4d25532cfd9fe461acc83498361ec2e2252795b4f40b17e80692814329e42d", size = 10049494, upload-time = "2025-03-07T15:26:51.268Z" }, - { url = "https://files.pythonhosted.org/packages/6d/12/03f6dfa1b95ddd47e6969f0225d60d9d7437c91938a310835feb27927ca0/ruff-0.9.10-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:188a6638dab1aa9bb6228a7302387b2c9954e455fb25d6b4470cb0641d16759d", size = 10853584, upload-time = "2025-03-07T15:26:56.104Z" }, - { url = "https://files.pythonhosted.org/packages/02/49/1c79e0906b6ff551fb0894168763f705bf980864739572b2815ecd3c9df0/ruff-0.9.10-py3-none-macosx_11_0_arm64.whl", hash = "sha256:5284dcac6b9dbc2fcb71fdfc26a217b2ca4ede6ccd57476f52a587451ebe450d", size = 10155692, upload-time = "2025-03-07T15:27:01.385Z" }, - { url = "https://files.pythonhosted.org/packages/5b/01/85e8082e41585e0e1ceb11e41c054e9e36fed45f4b210991052d8a75089f/ruff-0.9.10-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:47678f39fa2a3da62724851107f438c8229a3470f533894b5568a39b40029c0c", size = 10369760, upload-time = "2025-03-07T15:27:04.023Z" }, - { url = "https://files.pythonhosted.org/packages/a1/90/0bc60bd4e5db051f12445046d0c85cc2c617095c0904f1aa81067dc64aea/ruff-0.9.10-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:99713a6e2766b7a17147b309e8c915b32b07a25c9efd12ada79f217c9c778b3e", size = 9912196, upload-time = "2025-03-07T15:27:06.93Z" }, - { url = "https://files.pythonhosted.org/packages/66/ea/0b7e8c42b1ec608033c4d5a02939c82097ddcb0b3e393e4238584b7054ab/ruff-0.9.10-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:524ee184d92f7c7304aa568e2db20f50c32d1d0caa235d8ddf10497566ea1a12", size = 11434985, upload-time = "2025-03-07T15:27:10.082Z" }, - { url = "https://files.pythonhosted.org/packages/d5/86/3171d1eff893db4f91755175a6e1163c5887be1f1e2f4f6c0c59527c2bfd/ruff-0.9.10-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:df92aeac30af821f9acf819fc01b4afc3dfb829d2782884f8739fb52a8119a16", size = 12155842, upload-time = "2025-03-07T15:27:12.727Z" }, - { url = "https://files.pythonhosted.org/packages/89/9e/700ca289f172a38eb0bca752056d0a42637fa17b81649b9331786cb791d7/ruff-0.9.10-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:de42e4edc296f520bb84954eb992a07a0ec5a02fecb834498415908469854a52", size = 11613804, upload-time = "2025-03-07T15:27:15.944Z" }, - { url = "https://files.pythonhosted.org/packages/f2/92/648020b3b5db180f41a931a68b1c8575cca3e63cec86fd26807422a0dbad/ruff-0.9.10-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d257f95b65806104b6b1ffca0ea53f4ef98454036df65b1eda3693534813ecd1", size = 13823776, upload-time = "2025-03-07T15:27:18.996Z" }, - { url = "https://files.pythonhosted.org/packages/5e/a6/cc472161cd04d30a09d5c90698696b70c169eeba2c41030344194242db45/ruff-0.9.10-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b60dec7201c0b10d6d11be00e8f2dbb6f40ef1828ee75ed739923799513db24c", size = 11302673, upload-time = "2025-03-07T15:27:21.655Z" }, - { url = "https://files.pythonhosted.org/packages/6c/db/d31c361c4025b1b9102b4d032c70a69adb9ee6fde093f6c3bf29f831c85c/ruff-0.9.10-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:d838b60007da7a39c046fcdd317293d10b845001f38bcb55ba766c3875b01e43", size = 10235358, upload-time = "2025-03-07T15:27:24.72Z" }, - { url = "https://files.pythonhosted.org/packages/d1/86/d6374e24a14d4d93ebe120f45edd82ad7dcf3ef999ffc92b197d81cdc2a5/ruff-0.9.10-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:ccaf903108b899beb8e09a63ffae5869057ab649c1e9231c05ae354ebc62066c", size = 9886177, upload-time = "2025-03-07T15:27:27.282Z" }, - { url = "https://files.pythonhosted.org/packages/00/62/a61691f6eaaac1e945a1f3f59f1eea9a218513139d5b6c2b8f88b43b5b8f/ruff-0.9.10-py3-none-musllinux_1_2_i686.whl", hash = "sha256:f9567d135265d46e59d62dc60c0bfad10e9a6822e231f5b24032dba5a55be6b5", size = 10864747, upload-time = "2025-03-07T15:27:30.637Z" }, - { url = "https://files.pythonhosted.org/packages/ee/94/2c7065e1d92a8a8a46d46d9c3cf07b0aa7e0a1e0153d74baa5e6620b4102/ruff-0.9.10-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:5f202f0d93738c28a89f8ed9eaba01b7be339e5d8d642c994347eaa81c6d75b8", size = 11360441, upload-time = "2025-03-07T15:27:33.356Z" }, - { url = "https://files.pythonhosted.org/packages/a7/8f/1f545ea6f9fcd7bf4368551fb91d2064d8f0577b3079bb3f0ae5779fb773/ruff-0.9.10-py3-none-win32.whl", hash = "sha256:bfb834e87c916521ce46b1788fbb8484966e5113c02df216680102e9eb960029", size = 10247401, upload-time = "2025-03-07T15:27:35.994Z" }, - { url = "https://files.pythonhosted.org/packages/4f/18/fb703603ab108e5c165f52f5b86ee2aa9be43bb781703ec87c66a5f5d604/ruff-0.9.10-py3-none-win_amd64.whl", hash = "sha256:f2160eeef3031bf4b17df74e307d4c5fb689a6f3a26a2de3f7ef4044e3c484f1", size = 11366360, upload-time = "2025-03-07T15:27:38.66Z" }, - { url = "https://files.pythonhosted.org/packages/35/85/338e603dc68e7d9994d5d84f24adbf69bae760ba5efd3e20f5ff2cec18da/ruff-0.9.10-py3-none-win_arm64.whl", hash = "sha256:5fd804c0327a5e5ea26615550e706942f348b197d5475ff34c19733aee4b2e69", size = 10436892, upload-time = "2025-03-07T15:27:41.687Z" }, + { url = "https://files.pythonhosted.org/packages/43/ca/75707e6efa2b37c77dadb324ae7d9571cb424e61ea73fad7c56c2d14527f/tomli-2.2.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:678e4fa69e4575eb77d103de3df8a895e1591b48e740211bd1067378c69e8249", size = 131077, upload-time = "2024-11-27T22:37:54.956Z" }, + { url = "https://files.pythonhosted.org/packages/c7/16/51ae563a8615d472fdbffc43a3f3d46588c264ac4f024f63f01283becfbb/tomli-2.2.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:023aa114dd824ade0100497eb2318602af309e5a55595f76b626d6d9f3b7b0a6", size = 123429, upload-time = "2024-11-27T22:37:56.698Z" }, + { url = "https://files.pythonhosted.org/packages/f1/dd/4f6cd1e7b160041db83c694abc78e100473c15d54620083dbd5aae7b990e/tomli-2.2.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ece47d672db52ac607a3d9599a9d48dcb2f2f735c6c2d1f34130085bb12b112a", size = 226067, upload-time = "2024-11-27T22:37:57.63Z" }, + { url = "https://files.pythonhosted.org/packages/a9/6b/c54ede5dc70d648cc6361eaf429304b02f2871a345bbdd51e993d6cdf550/tomli-2.2.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6972ca9c9cc9f0acaa56a8ca1ff51e7af152a9f87fb64623e31d5c83700080ee", size = 236030, upload-time = "2024-11-27T22:37:59.344Z" }, + { url = "https://files.pythonhosted.org/packages/1f/47/999514fa49cfaf7a92c805a86c3c43f4215621855d151b61c602abb38091/tomli-2.2.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c954d2250168d28797dd4e3ac5cf812a406cd5a92674ee4c8f123c889786aa8e", size = 240898, upload-time = "2024-11-27T22:38:00.429Z" }, + { url = "https://files.pythonhosted.org/packages/73/41/0a01279a7ae09ee1573b423318e7934674ce06eb33f50936655071d81a24/tomli-2.2.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:8dd28b3e155b80f4d54beb40a441d366adcfe740969820caf156c019fb5c7ec4", size = 229894, upload-time = "2024-11-27T22:38:02.094Z" }, + { url = "https://files.pythonhosted.org/packages/55/18/5d8bc5b0a0362311ce4d18830a5d28943667599a60d20118074ea1b01bb7/tomli-2.2.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:e59e304978767a54663af13c07b3d1af22ddee3bb2fb0618ca1593e4f593a106", size = 245319, upload-time = "2024-11-27T22:38:03.206Z" }, + { url = "https://files.pythonhosted.org/packages/92/a3/7ade0576d17f3cdf5ff44d61390d4b3febb8a9fc2b480c75c47ea048c646/tomli-2.2.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:33580bccab0338d00994d7f16f4c4ec25b776af3ffaac1ed74e0b3fc95e885a8", size = 238273, upload-time = "2024-11-27T22:38:04.217Z" }, + { url = "https://files.pythonhosted.org/packages/72/6f/fa64ef058ac1446a1e51110c375339b3ec6be245af9d14c87c4a6412dd32/tomli-2.2.1-cp311-cp311-win32.whl", hash = "sha256:465af0e0875402f1d226519c9904f37254b3045fc5084697cefb9bdde1ff99ff", size = 98310, upload-time = "2024-11-27T22:38:05.908Z" }, + { url = "https://files.pythonhosted.org/packages/6a/1c/4a2dcde4a51b81be3530565e92eda625d94dafb46dbeb15069df4caffc34/tomli-2.2.1-cp311-cp311-win_amd64.whl", hash = "sha256:2d0f2fdd22b02c6d81637a3c95f8cd77f995846af7414c5c4b8d0545afa1bc4b", size = 108309, upload-time = "2024-11-27T22:38:06.812Z" }, + { url = "https://files.pythonhosted.org/packages/52/e1/f8af4c2fcde17500422858155aeb0d7e93477a0d59a98e56cbfe75070fd0/tomli-2.2.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:4a8f6e44de52d5e6c657c9fe83b562f5f4256d8ebbfe4ff922c495620a7f6cea", size = 132762, upload-time = "2024-11-27T22:38:07.731Z" }, + { url = "https://files.pythonhosted.org/packages/03/b8/152c68bb84fc00396b83e7bbddd5ec0bd3dd409db4195e2a9b3e398ad2e3/tomli-2.2.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8d57ca8095a641b8237d5b079147646153d22552f1c637fd3ba7f4b0b29167a8", size = 123453, upload-time = "2024-11-27T22:38:09.384Z" }, + { url = "https://files.pythonhosted.org/packages/c8/d6/fc9267af9166f79ac528ff7e8c55c8181ded34eb4b0e93daa767b8841573/tomli-2.2.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4e340144ad7ae1533cb897d406382b4b6fede8890a03738ff1683af800d54192", size = 233486, upload-time = "2024-11-27T22:38:10.329Z" }, + { url = "https://files.pythonhosted.org/packages/5c/51/51c3f2884d7bab89af25f678447ea7d297b53b5a3b5730a7cb2ef6069f07/tomli-2.2.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:db2b95f9de79181805df90bedc5a5ab4c165e6ec3fe99f970d0e302f384ad222", size = 242349, upload-time = "2024-11-27T22:38:11.443Z" }, + { url = "https://files.pythonhosted.org/packages/ab/df/bfa89627d13a5cc22402e441e8a931ef2108403db390ff3345c05253935e/tomli-2.2.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:40741994320b232529c802f8bc86da4e1aa9f413db394617b9a256ae0f9a7f77", size = 252159, upload-time = "2024-11-27T22:38:13.099Z" }, + { url = "https://files.pythonhosted.org/packages/9e/6e/fa2b916dced65763a5168c6ccb91066f7639bdc88b48adda990db10c8c0b/tomli-2.2.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:400e720fe168c0f8521520190686ef8ef033fb19fc493da09779e592861b78c6", size = 237243, upload-time = "2024-11-27T22:38:14.766Z" }, + { url = "https://files.pythonhosted.org/packages/b4/04/885d3b1f650e1153cbb93a6a9782c58a972b94ea4483ae4ac5cedd5e4a09/tomli-2.2.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:02abe224de6ae62c19f090f68da4e27b10af2b93213d36cf44e6e1c5abd19fdd", size = 259645, upload-time = "2024-11-27T22:38:15.843Z" }, + { url = "https://files.pythonhosted.org/packages/9c/de/6b432d66e986e501586da298e28ebeefd3edc2c780f3ad73d22566034239/tomli-2.2.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:b82ebccc8c8a36f2094e969560a1b836758481f3dc360ce9a3277c65f374285e", size = 244584, upload-time = "2024-11-27T22:38:17.645Z" }, + { url = "https://files.pythonhosted.org/packages/1c/9a/47c0449b98e6e7d1be6cbac02f93dd79003234ddc4aaab6ba07a9a7482e2/tomli-2.2.1-cp312-cp312-win32.whl", hash = "sha256:889f80ef92701b9dbb224e49ec87c645ce5df3fa2cc548664eb8a25e03127a98", size = 98875, upload-time = "2024-11-27T22:38:19.159Z" }, + { url = "https://files.pythonhosted.org/packages/ef/60/9b9638f081c6f1261e2688bd487625cd1e660d0a85bd469e91d8db969734/tomli-2.2.1-cp312-cp312-win_amd64.whl", hash = "sha256:7fc04e92e1d624a4a63c76474610238576942d6b8950a2d7f908a340494e67e4", size = 109418, upload-time = "2024-11-27T22:38:20.064Z" }, + { url = "https://files.pythonhosted.org/packages/04/90/2ee5f2e0362cb8a0b6499dc44f4d7d48f8fff06d28ba46e6f1eaa61a1388/tomli-2.2.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:f4039b9cbc3048b2416cc57ab3bda989a6fcf9b36cf8937f01a6e731b64f80d7", size = 132708, upload-time = "2024-11-27T22:38:21.659Z" }, + { url = "https://files.pythonhosted.org/packages/c0/ec/46b4108816de6b385141f082ba99e315501ccd0a2ea23db4a100dd3990ea/tomli-2.2.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:286f0ca2ffeeb5b9bd4fcc8d6c330534323ec51b2f52da063b11c502da16f30c", size = 123582, upload-time = "2024-11-27T22:38:22.693Z" }, + { url = "https://files.pythonhosted.org/packages/a0/bd/b470466d0137b37b68d24556c38a0cc819e8febe392d5b199dcd7f578365/tomli-2.2.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a92ef1a44547e894e2a17d24e7557a5e85a9e1d0048b0b5e7541f76c5032cb13", size = 232543, upload-time = "2024-11-27T22:38:24.367Z" }, + { url = "https://files.pythonhosted.org/packages/d9/e5/82e80ff3b751373f7cead2815bcbe2d51c895b3c990686741a8e56ec42ab/tomli-2.2.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9316dc65bed1684c9a98ee68759ceaed29d229e985297003e494aa825ebb0281", size = 241691, upload-time = "2024-11-27T22:38:26.081Z" }, + { url = "https://files.pythonhosted.org/packages/05/7e/2a110bc2713557d6a1bfb06af23dd01e7dde52b6ee7dadc589868f9abfac/tomli-2.2.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e85e99945e688e32d5a35c1ff38ed0b3f41f43fad8df0bdf79f72b2ba7bc5272", size = 251170, upload-time = "2024-11-27T22:38:27.921Z" }, + { url = "https://files.pythonhosted.org/packages/64/7b/22d713946efe00e0adbcdfd6d1aa119ae03fd0b60ebed51ebb3fa9f5a2e5/tomli-2.2.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:ac065718db92ca818f8d6141b5f66369833d4a80a9d74435a268c52bdfa73140", size = 236530, upload-time = "2024-11-27T22:38:29.591Z" }, + { url = "https://files.pythonhosted.org/packages/38/31/3a76f67da4b0cf37b742ca76beaf819dca0ebef26d78fc794a576e08accf/tomli-2.2.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:d920f33822747519673ee656a4b6ac33e382eca9d331c87770faa3eef562aeb2", size = 258666, upload-time = "2024-11-27T22:38:30.639Z" }, + { url = "https://files.pythonhosted.org/packages/07/10/5af1293da642aded87e8a988753945d0cf7e00a9452d3911dd3bb354c9e2/tomli-2.2.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:a198f10c4d1b1375d7687bc25294306e551bf1abfa4eace6650070a5c1ae2744", size = 243954, upload-time = "2024-11-27T22:38:31.702Z" }, + { url = "https://files.pythonhosted.org/packages/5b/b9/1ed31d167be802da0fc95020d04cd27b7d7065cc6fbefdd2f9186f60d7bd/tomli-2.2.1-cp313-cp313-win32.whl", hash = "sha256:d3f5614314d758649ab2ab3a62d4f2004c825922f9e370b29416484086b264ec", size = 98724, upload-time = "2024-11-27T22:38:32.837Z" }, + { url = "https://files.pythonhosted.org/packages/c7/32/b0963458706accd9afcfeb867c0f9175a741bf7b19cd424230714d722198/tomli-2.2.1-cp313-cp313-win_amd64.whl", hash = "sha256:a38aa0308e754b0e3c67e344754dff64999ff9b513e691d0e786265c93583c69", size = 109383, upload-time = "2024-11-27T22:38:34.455Z" }, + { url = "https://files.pythonhosted.org/packages/6e/c2/61d3e0f47e2b74ef40a68b9e6ad5984f6241a942f7cd3bbfbdbd03861ea9/tomli-2.2.1-py3-none-any.whl", hash = "sha256:cb55c73c5f4408779d0cf3eef9f762b9c9f147a77de7b258bef0a5628adc85cc", size = 14257, upload-time = "2024-11-27T22:38:35.385Z" }, ] [[package]] @@ -314,6 +469,7 @@ dependencies = [ { name = "distlib" }, { name = "filelock" }, { name = "platformdirs" }, + { name = "typing-extensions", marker = "python_full_version < '3.11'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/1c/14/37fcdba2808a6c615681cd216fecae00413c9dab44fb2e57805ecf3eaee3/virtualenv-20.34.0.tar.gz", hash = "sha256:44815b2c9dee7ed86e387b842a84f20b93f7f417f95886ca1996a72a4138eb1a", size = 6003808, upload-time = "2025-08-13T14:24:07.464Z" } wheels = [