diff --git a/.github/pre-commit b/.github/pre-commit index d76935698..0dc61a141 100755 --- a/.github/pre-commit +++ b/.github/pre-commit @@ -28,4 +28,30 @@ then exit 1 fi +# Run `ruff` python formatting +if ! poetry run ruff format --check +then + echo "" + echo "There are some python code style issues." + echo "Run `poetry run ruff format` first." + exit 1 +fi + +# Run `mypy` for python type checking +if ! poetry run mypy . +then + echo "" + echo "There are some python code style issues." + echo "Fix the warnings returned by `poetry run mypy .` first." + exit 1 +fi + +# Run `ruff` python linting +if ! poetry run ruff check +then + echo "" + echo "There are some python linting issues." + exit 1 +fi + exit 0 diff --git a/.github/workflows/ci-py.yml b/.github/workflows/ci-py.yml new file mode 100644 index 000000000..d83043c21 --- /dev/null +++ b/.github/workflows/ci-py.yml @@ -0,0 +1,124 @@ +name: Continuous integration 🐍 + +on: + push: + branches: + - main + pull_request: + branches: + - main + merge_group: + types: [checks_requested] + workflow_dispatch: {} + +env: + SCCACHE_GHA_ENABLED: "true" + +jobs: + # Check if changes were made to the relevant files. + # Always returns true if running on the default branch, to ensure all changes are throughly checked. + changes: + name: Check for changes in Python files + runs-on: ubuntu-latest + # Required permissions + permissions: + pull-requests: read + # Set job outputs to values from filter step + outputs: + python: ${{ github.ref_name == github.event.repository.default_branch || steps.filter.outputs.python }} + steps: + # For pull requests it's not necessary to checkout the code + - uses: dorny/paths-filter@v3 + id: filter + with: + filters: | + python: + - 'quantinuum-hugr-py/**' + - 'pyproject.toml' + + check: + needs: changes + if: ${{ needs.changes.outputs.python == 'true' }} + + name: check python + runs-on: ubuntu-latest + + strategy: + matrix: + python-version: ['3.10'] + + steps: + - uses: actions/checkout@v3 + - name: Run sccache-cache + uses: mozilla-actions/sccache-action@v0.0.3 + - name: Install poetry + run: pipx install poetry + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v3 + with: + python-version: ${{ matrix.python-version }} + cache: "poetry" + + - name: Install the project libraries + run: poetry install + + - name: Type check with mypy + run: poetry run mypy . + + - name: Check formatting with ruff + run: poetry run ruff format --check + + - name: Lint with ruff + run: poetry run ruff check + + - name: Run tests + run: poetry run pytest + + # 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: [check] + if: always() + runs-on: ubuntu-latest + steps: + - name: Fail if required checks failed + if: (failure() || cancelled()) + run: | + echo "Required checks failed" + echo "Please check the logs for more information" + exit 1 + - name: Pass if required checks passed + run: | + echo "All required checks passed" + + coverage: + needs: [changes, check] + # Run only if there are changes in the relevant files and the check job passed or was skipped + if: always() && !failure() && !cancelled() && needs.changes.outputs.python == 'true' && github.event_name != 'merge_group' + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - name: Run sccache-cache + uses: mozilla-actions/sccache-action@v0.0.3 + - name: Install poetry + run: pipx install poetry + - name: Set up Python 3.10 + uses: actions/setup-python@v3 + with: + python-version: '3.10' + cache: "poetry" + + - name: Install the project libraries + run: poetry install + + - name: Run python tests with coverage instrumentation + run: poetry run pytest --cov=./ --cov-report=xml + + - name: Upload python coverage to codecov.io + uses: codecov/codecov-action@v3 + with: + files: coverage.xml + name: python + flags: python + token: ${{ secrets.CODECOV_TOKEN }} diff --git a/.github/workflows/ci.yml b/.github/workflows/ci-rs.yml similarity index 50% rename from .github/workflows/ci.yml rename to .github/workflows/ci-rs.yml index 725dea474..4472981ba 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci-rs.yml @@ -1,4 +1,4 @@ -name: Continuous integration +name: Continuous integration 🦀 on: push: @@ -21,7 +21,31 @@ env: RUSTC_WRAPPER: "sccache" jobs: + # Check if changes were made to the relevant files. + # Always returns true if running on the default branch, to ensure all changes are throughly checked. + changes: + name: Check for changes in Rust files + runs-on: ubuntu-latest + # Required permissions + permissions: + pull-requests: read + # Set job outputs to values from filter step + outputs: + rust: ${{ github.ref_name == github.event.repository.default_branch || steps.filter.outputs.rust }} + steps: + # For pull requests it's not necessary to checkout the code + - uses: dorny/paths-filter@v3 + id: filter + with: + filters: | + rust: + - 'quantinuum-hugr/**' + - 'Cargo.toml' + - 'specification/schema/**' + check: + needs: changes + if: ${{ needs.changes.outputs.rust == 'true' }} runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 @@ -40,7 +64,8 @@ jobs: RUSTDOCFLAGS: "-Dwarnings" benches: - if: github.event_name != 'merge_group' + needs: changes + if: ${{ needs.changes.outputs.rust == 'true' }} && github.event_name != 'merge_group' runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 @@ -52,22 +77,39 @@ jobs: - name: Build benchmarks with all features run: cargo bench --verbose --no-run --workspace --all-features - tests: + # Run tests on Rust stable + tests-stable: + needs: changes + if: ${{ needs.changes.outputs.rust == 'true' }} + runs-on: ubuntu-latest + name: tests (Rust stable) + steps: + - uses: actions/checkout@v4 + - uses: mozilla-actions/sccache-action@v0.0.4 + - id: toolchain + uses: dtolnay/rust-toolchain@master + with: + toolchain: 'stable' + - name: Configure default rust toolchain + run: rustup override set ${{steps.toolchain.outputs.name}} + - name: Build with no features + run: cargo test --verbose --workspace --no-default-features --no-run + - name: Tests with no features + run: cargo test --verbose --workspace --no-default-features + - name: Build with all features + run: cargo test --verbose --workspace --all-features --no-run + - name: Tests with all features + run: cargo test --verbose --workspace --all-features + + # Run tests on other toolchains + tests-other: + needs: changes + if: ${{ needs.changes.outputs.rust == 'true' && github.event_name != 'merge_group' }} runs-on: ubuntu-latest strategy: + fail-fast: true matrix: - rust: ['1.75', stable, beta, nightly] - # workaround to ignore non-stable tests when running the merge queue checks - # see: https://github.community/t/how-to-conditionally-include-exclude-items-in-matrix-eg-based-on-branch/16853/6 - isMerge: - - ${{ github.event_name == 'merge_group' }} - exclude: - - rust: '1.75' - isMerge: true - - rust: beta - isMerge: true - - rust: nightly - isMerge: true + rust: ['1.75', beta, nightly] name: tests (Rust ${{ matrix.rust }}) steps: - uses: actions/checkout@v4 @@ -87,9 +129,28 @@ jobs: - name: Tests with all features run: cargo test --verbose --workspace --all-features + # 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: [check, tests-stable] + if: always() + runs-on: ubuntu-latest + steps: + - name: Fail if required checks failed + if: (failure() || cancelled()) + run: | + echo "Required checks failed" + echo "Please check the logs for more information" + exit 1 + - name: Pass if required checks passed + run: | + echo "All required checks passed" + coverage: - if: github.event_name != 'merge_group' - needs: [tests, check] + needs: [changes, tests-stable, tests-other, check] + # Run only if there are changes in the relevant files and the check job passed or was skipped + if: always() && !failure() && !cancelled() && needs.changes.outputs.rust == 'true' && github.event_name != 'merge_group' runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 @@ -110,6 +171,6 @@ jobs: uses: codecov/codecov-action@v4 with: files: coverage.json - name: ubuntu - env: - CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} + name: rust + flags: rust + token: ${{ secrets.CODECOV_TOKEN }} diff --git a/.gitignore b/.gitignore index 1b0b35659..836f10146 100644 --- a/.gitignore +++ b/.gitignore @@ -24,4 +24,25 @@ devenv.local.nix .pre-commit-config.yaml # Coverage report -lcov.info \ No newline at end of file +lcov.info + +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# Unit test / coverage reports +htmlcov/ +.coverage + +# Jupyter Notebook +.ipynb_checkpoints + +# Environments +.env +.venv +env/ +venv/ + +# ruff +.ruff_cache diff --git a/README.md b/README.md index 79baadaf6..749ab51db 100644 --- a/README.md +++ b/README.md @@ -48,7 +48,7 @@ See [DEVELOPMENT.md](https://github.com/CQCL/hugr/blob/main/DEVELOPMENT.md) for This project is licensed under Apache License, Version 2.0 ([LICENSE][] or http://www.apache.org/licenses/LICENSE-2.0). [API documentation here]: https://docs.rs/quantinuum-hugr/ - [build_status]: https://github.com/CQCL/hugr/workflows/Continuous%20integration/badge.svg?branch=main + [build_status]: https://github.com/CQCL/hugr/actions/workflows/ci-rs.yml/badge.svg?branch=main [msrv]: https://img.shields.io/badge/rust-1.75.0%2B-blue.svg [crates]: https://img.shields.io/crates/v/quantinuum-hugr [codecov]: https://img.shields.io/codecov/c/gh/CQCL/hugr?logo=codecov diff --git a/codecov.yml b/codecov.yml new file mode 100644 index 000000000..aec1d6163 --- /dev/null +++ b/codecov.yml @@ -0,0 +1,25 @@ +# Codecov coverage report configuration + +# Coverage report +# Do not fail if the coverage is not met +coverage: + status: + patch: + default: + informational: false + only_pulls: true + project: + default: + informational: true + +# Ignore tests and binaries +ignore: + - "quantinuum-hugr-py/tests" + +# Coverage groups config +flag_management: + default_rules: # the rules that will be followed for any flag added, generally + # Use previous coverage if one is not available for the current commit. + # + # (E.g. if the PR doesn't modify a subproject, we don't submit a coverage report for it.) + carryforward: true diff --git a/justfile b/justfile index bc395e953..941274c20 100644 --- a/justfile +++ b/justfile @@ -9,6 +9,7 @@ test: # Auto-fix all clippy warnings fix: cargo clippy --all-targets --all-features --workspace --fix --allow-staged + poetry run ruff check --fix # Run the pre-commit checks check: @@ -17,7 +18,20 @@ check: # Format the code format: cargo fmt + poetry run ruff format # Generate a test coverage report coverage: cargo llvm-cov --lcov > lcov.info + +# Load a poetry shell with the dependencies installed +pyshell: + poetry shell + +# Run the python tests +pytest: + poetry run pytest + +# Generate a python test coverage report +pycoverage: + poetry run pytest --cov=./ --cov-report=html diff --git a/poetry.lock b/poetry.lock new file mode 100644 index 000000000..6aaaf3166 --- /dev/null +++ b/poetry.lock @@ -0,0 +1,294 @@ +# This file is automatically @generated by Poetry 1.8.2 and should not be changed by hand. + +[[package]] +name = "colorama" +version = "0.4.6" +description = "Cross-platform colored terminal text." +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" +files = [ + {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"}, + {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, +] + +[[package]] +name = "coverage" +version = "7.4.4" +description = "Code coverage measurement for Python" +optional = false +python-versions = ">=3.8" +files = [ + {file = "coverage-7.4.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e0be5efd5127542ef31f165de269f77560d6cdef525fffa446de6f7e9186cfb2"}, + {file = "coverage-7.4.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:ccd341521be3d1b3daeb41960ae94a5e87abe2f46f17224ba5d6f2b8398016cf"}, + {file = "coverage-7.4.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:09fa497a8ab37784fbb20ab699c246053ac294d13fc7eb40ec007a5043ec91f8"}, + {file = "coverage-7.4.4-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b1a93009cb80730c9bca5d6d4665494b725b6e8e157c1cb7f2db5b4b122ea562"}, + {file = "coverage-7.4.4-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:690db6517f09336559dc0b5f55342df62370a48f5469fabf502db2c6d1cffcd2"}, + {file = "coverage-7.4.4-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:09c3255458533cb76ef55da8cc49ffab9e33f083739c8bd4f58e79fecfe288f7"}, + {file = "coverage-7.4.4-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:8ce1415194b4a6bd0cdcc3a1dfbf58b63f910dcb7330fe15bdff542c56949f87"}, + {file = "coverage-7.4.4-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:b91cbc4b195444e7e258ba27ac33769c41b94967919f10037e6355e998af255c"}, + {file = "coverage-7.4.4-cp310-cp310-win32.whl", hash = "sha256:598825b51b81c808cb6f078dcb972f96af96b078faa47af7dfcdf282835baa8d"}, + {file = "coverage-7.4.4-cp310-cp310-win_amd64.whl", hash = "sha256:09ef9199ed6653989ebbcaacc9b62b514bb63ea2f90256e71fea3ed74bd8ff6f"}, + {file = "coverage-7.4.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:0f9f50e7ef2a71e2fae92774c99170eb8304e3fdf9c8c3c7ae9bab3e7229c5cf"}, + {file = "coverage-7.4.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:623512f8ba53c422fcfb2ce68362c97945095b864cda94a92edbaf5994201083"}, + {file = "coverage-7.4.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0513b9508b93da4e1716744ef6ebc507aff016ba115ffe8ecff744d1322a7b63"}, + {file = "coverage-7.4.4-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:40209e141059b9370a2657c9b15607815359ab3ef9918f0196b6fccce8d3230f"}, + {file = "coverage-7.4.4-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8a2b2b78c78293782fd3767d53e6474582f62443d0504b1554370bde86cc8227"}, + {file = "coverage-7.4.4-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:73bfb9c09951125d06ee473bed216e2c3742f530fc5acc1383883125de76d9cd"}, + {file = "coverage-7.4.4-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:1f384c3cc76aeedce208643697fb3e8437604b512255de6d18dae3f27655a384"}, + {file = "coverage-7.4.4-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:54eb8d1bf7cacfbf2a3186019bcf01d11c666bd495ed18717162f7eb1e9dd00b"}, + {file = "coverage-7.4.4-cp311-cp311-win32.whl", hash = "sha256:cac99918c7bba15302a2d81f0312c08054a3359eaa1929c7e4b26ebe41e9b286"}, + {file = "coverage-7.4.4-cp311-cp311-win_amd64.whl", hash = "sha256:b14706df8b2de49869ae03a5ccbc211f4041750cd4a66f698df89d44f4bd30ec"}, + {file = "coverage-7.4.4-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:201bef2eea65e0e9c56343115ba3814e896afe6d36ffd37bab783261db430f76"}, + {file = "coverage-7.4.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:41c9c5f3de16b903b610d09650e5e27adbfa7f500302718c9ffd1c12cf9d6818"}, + {file = "coverage-7.4.4-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d898fe162d26929b5960e4e138651f7427048e72c853607f2b200909794ed978"}, + {file = "coverage-7.4.4-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3ea79bb50e805cd6ac058dfa3b5c8f6c040cb87fe83de10845857f5535d1db70"}, + {file = "coverage-7.4.4-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ce4b94265ca988c3f8e479e741693d143026632672e3ff924f25fab50518dd51"}, + {file = "coverage-7.4.4-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:00838a35b882694afda09f85e469c96367daa3f3f2b097d846a7216993d37f4c"}, + {file = "coverage-7.4.4-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:fdfafb32984684eb03c2d83e1e51f64f0906b11e64482df3c5db936ce3839d48"}, + {file = "coverage-7.4.4-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:69eb372f7e2ece89f14751fbcbe470295d73ed41ecd37ca36ed2eb47512a6ab9"}, + {file = "coverage-7.4.4-cp312-cp312-win32.whl", hash = "sha256:137eb07173141545e07403cca94ab625cc1cc6bc4c1e97b6e3846270e7e1fea0"}, + {file = "coverage-7.4.4-cp312-cp312-win_amd64.whl", hash = "sha256:d71eec7d83298f1af3326ce0ff1d0ea83c7cb98f72b577097f9083b20bdaf05e"}, + {file = "coverage-7.4.4-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:d5ae728ff3b5401cc320d792866987e7e7e880e6ebd24433b70a33b643bb0384"}, + {file = "coverage-7.4.4-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:cc4f1358cb0c78edef3ed237ef2c86056206bb8d9140e73b6b89fbcfcbdd40e1"}, + {file = "coverage-7.4.4-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8130a2aa2acb8788e0b56938786c33c7c98562697bf9f4c7d6e8e5e3a0501e4a"}, + {file = "coverage-7.4.4-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cf271892d13e43bc2b51e6908ec9a6a5094a4df1d8af0bfc360088ee6c684409"}, + {file = "coverage-7.4.4-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a4cdc86d54b5da0df6d3d3a2f0b710949286094c3a6700c21e9015932b81447e"}, + {file = "coverage-7.4.4-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:ae71e7ddb7a413dd60052e90528f2f65270aad4b509563af6d03d53e979feafd"}, + {file = "coverage-7.4.4-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:38dd60d7bf242c4ed5b38e094baf6401faa114fc09e9e6632374388a404f98e7"}, + {file = "coverage-7.4.4-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:aa5b1c1bfc28384f1f53b69a023d789f72b2e0ab1b3787aae16992a7ca21056c"}, + {file = "coverage-7.4.4-cp38-cp38-win32.whl", hash = "sha256:dfa8fe35a0bb90382837b238fff375de15f0dcdb9ae68ff85f7a63649c98527e"}, + {file = "coverage-7.4.4-cp38-cp38-win_amd64.whl", hash = "sha256:b2991665420a803495e0b90a79233c1433d6ed77ef282e8e152a324bbbc5e0c8"}, + {file = "coverage-7.4.4-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:3b799445b9f7ee8bf299cfaed6f5b226c0037b74886a4e11515e569b36fe310d"}, + {file = "coverage-7.4.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:b4d33f418f46362995f1e9d4f3a35a1b6322cb959c31d88ae56b0298e1c22357"}, + {file = "coverage-7.4.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:aadacf9a2f407a4688d700e4ebab33a7e2e408f2ca04dbf4aef17585389eff3e"}, + {file = "coverage-7.4.4-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7c95949560050d04d46b919301826525597f07b33beba6187d04fa64d47ac82e"}, + {file = "coverage-7.4.4-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ff7687ca3d7028d8a5f0ebae95a6e4827c5616b31a4ee1192bdfde697db110d4"}, + {file = "coverage-7.4.4-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:5fc1de20b2d4a061b3df27ab9b7c7111e9a710f10dc2b84d33a4ab25065994ec"}, + {file = "coverage-7.4.4-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:c74880fc64d4958159fbd537a091d2a585448a8f8508bf248d72112723974cbd"}, + {file = "coverage-7.4.4-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:742a76a12aa45b44d236815d282b03cfb1de3b4323f3e4ec933acfae08e54ade"}, + {file = "coverage-7.4.4-cp39-cp39-win32.whl", hash = "sha256:d89d7b2974cae412400e88f35d86af72208e1ede1a541954af5d944a8ba46c57"}, + {file = "coverage-7.4.4-cp39-cp39-win_amd64.whl", hash = "sha256:9ca28a302acb19b6af89e90f33ee3e1906961f94b54ea37de6737b7ca9d8827c"}, + {file = "coverage-7.4.4-pp38.pp39.pp310-none-any.whl", hash = "sha256:b2c5edc4ac10a7ef6605a966c58929ec6c1bd0917fb8c15cb3363f65aa40e677"}, + {file = "coverage-7.4.4.tar.gz", hash = "sha256:c901df83d097649e257e803be22592aedfd5182f07b3cc87d640bbb9afd50f49"}, +] + +[package.dependencies] +tomli = {version = "*", optional = true, markers = "python_full_version <= \"3.11.0a6\" and extra == \"toml\""} + +[package.extras] +toml = ["tomli"] + +[[package]] +name = "exceptiongroup" +version = "1.2.0" +description = "Backport of PEP 654 (exception groups)" +optional = false +python-versions = ">=3.7" +files = [ + {file = "exceptiongroup-1.2.0-py3-none-any.whl", hash = "sha256:4bfd3996ac73b41e9b9628b04e079f193850720ea5945fc96a08633c66912f14"}, + {file = "exceptiongroup-1.2.0.tar.gz", hash = "sha256:91f5c769735f051a4290d52edd0858999b57e5876e9f85937691bd4c9fa3ed68"}, +] + +[package.extras] +test = ["pytest (>=6)"] + +[[package]] +name = "iniconfig" +version = "2.0.0" +description = "brain-dead simple config-ini parsing" +optional = false +python-versions = ">=3.7" +files = [ + {file = "iniconfig-2.0.0-py3-none-any.whl", hash = "sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374"}, + {file = "iniconfig-2.0.0.tar.gz", hash = "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3"}, +] + +[[package]] +name = "mypy" +version = "1.9.0" +description = "Optional static typing for Python" +optional = false +python-versions = ">=3.8" +files = [ + {file = "mypy-1.9.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:f8a67616990062232ee4c3952f41c779afac41405806042a8126fe96e098419f"}, + {file = "mypy-1.9.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:d357423fa57a489e8c47b7c85dfb96698caba13d66e086b412298a1a0ea3b0ed"}, + {file = "mypy-1.9.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:49c87c15aed320de9b438ae7b00c1ac91cd393c1b854c2ce538e2a72d55df150"}, + {file = "mypy-1.9.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:48533cdd345c3c2e5ef48ba3b0d3880b257b423e7995dada04248725c6f77374"}, + {file = "mypy-1.9.0-cp310-cp310-win_amd64.whl", hash = "sha256:4d3dbd346cfec7cb98e6cbb6e0f3c23618af826316188d587d1c1bc34f0ede03"}, + {file = "mypy-1.9.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:653265f9a2784db65bfca694d1edd23093ce49740b2244cde583aeb134c008f3"}, + {file = "mypy-1.9.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:3a3c007ff3ee90f69cf0a15cbcdf0995749569b86b6d2f327af01fd1b8aee9dc"}, + {file = "mypy-1.9.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2418488264eb41f69cc64a69a745fad4a8f86649af4b1041a4c64ee61fc61129"}, + {file = "mypy-1.9.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:68edad3dc7d70f2f17ae4c6c1b9471a56138ca22722487eebacfd1eb5321d612"}, + {file = "mypy-1.9.0-cp311-cp311-win_amd64.whl", hash = "sha256:85ca5fcc24f0b4aeedc1d02f93707bccc04733f21d41c88334c5482219b1ccb3"}, + {file = "mypy-1.9.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:aceb1db093b04db5cd390821464504111b8ec3e351eb85afd1433490163d60cd"}, + {file = "mypy-1.9.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:0235391f1c6f6ce487b23b9dbd1327b4ec33bb93934aa986efe8a9563d9349e6"}, + {file = "mypy-1.9.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d4d5ddc13421ba3e2e082a6c2d74c2ddb3979c39b582dacd53dd5d9431237185"}, + {file = "mypy-1.9.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:190da1ee69b427d7efa8aa0d5e5ccd67a4fb04038c380237a0d96829cb157913"}, + {file = "mypy-1.9.0-cp312-cp312-win_amd64.whl", hash = "sha256:fe28657de3bfec596bbeef01cb219833ad9d38dd5393fc649f4b366840baefe6"}, + {file = "mypy-1.9.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:e54396d70be04b34f31d2edf3362c1edd023246c82f1730bbf8768c28db5361b"}, + {file = "mypy-1.9.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:5e6061f44f2313b94f920e91b204ec600982961e07a17e0f6cd83371cb23f5c2"}, + {file = "mypy-1.9.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:81a10926e5473c5fc3da8abb04119a1f5811a236dc3a38d92015cb1e6ba4cb9e"}, + {file = "mypy-1.9.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:b685154e22e4e9199fc95f298661deea28aaede5ae16ccc8cbb1045e716b3e04"}, + {file = "mypy-1.9.0-cp38-cp38-win_amd64.whl", hash = "sha256:5d741d3fc7c4da608764073089e5f58ef6352bedc223ff58f2f038c2c4698a89"}, + {file = "mypy-1.9.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:587ce887f75dd9700252a3abbc9c97bbe165a4a630597845c61279cf32dfbf02"}, + {file = "mypy-1.9.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:f88566144752999351725ac623471661c9d1cd8caa0134ff98cceeea181789f4"}, + {file = "mypy-1.9.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:61758fabd58ce4b0720ae1e2fea5cfd4431591d6d590b197775329264f86311d"}, + {file = "mypy-1.9.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:e49499be624dead83927e70c756970a0bc8240e9f769389cdf5714b0784ca6bf"}, + {file = "mypy-1.9.0-cp39-cp39-win_amd64.whl", hash = "sha256:571741dc4194b4f82d344b15e8837e8c5fcc462d66d076748142327626a1b6e9"}, + {file = "mypy-1.9.0-py3-none-any.whl", hash = "sha256:a260627a570559181a9ea5de61ac6297aa5af202f06fd7ab093ce74e7181e43e"}, + {file = "mypy-1.9.0.tar.gz", hash = "sha256:3cc5da0127e6a478cddd906068496a97a7618a21ce9b54bde5bf7e539c7af974"}, +] + +[package.dependencies] +mypy-extensions = ">=1.0.0" +tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""} +typing-extensions = ">=4.1.0" + +[package.extras] +dmypy = ["psutil (>=4.0)"] +install-types = ["pip"] +mypyc = ["setuptools (>=50)"] +reports = ["lxml"] + +[[package]] +name = "mypy-extensions" +version = "1.0.0" +description = "Type system extensions for programs checked with the mypy type checker." +optional = false +python-versions = ">=3.5" +files = [ + {file = "mypy_extensions-1.0.0-py3-none-any.whl", hash = "sha256:4392f6c0eb8a5668a69e23d168ffa70f0be9ccfd32b5cc2d26a34ae5b844552d"}, + {file = "mypy_extensions-1.0.0.tar.gz", hash = "sha256:75dbf8955dc00442a438fc4d0666508a9a97b6bd41aa2f0ffe9d2f2725af0782"}, +] + +[[package]] +name = "packaging" +version = "24.0" +description = "Core utilities for Python packages" +optional = false +python-versions = ">=3.7" +files = [ + {file = "packaging-24.0-py3-none-any.whl", hash = "sha256:2ddfb553fdf02fb784c234c7ba6ccc288296ceabec964ad2eae3777778130bc5"}, + {file = "packaging-24.0.tar.gz", hash = "sha256:eb82c5e3e56209074766e6885bb04b8c38a0c015d0a30036ebe7ece34c9989e9"}, +] + +[[package]] +name = "pluggy" +version = "1.4.0" +description = "plugin and hook calling mechanisms for python" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pluggy-1.4.0-py3-none-any.whl", hash = "sha256:7db9f7b503d67d1c5b95f59773ebb58a8c1c288129a88665838012cfb07b8981"}, + {file = "pluggy-1.4.0.tar.gz", hash = "sha256:8c85c2876142a764e5b7548e7d9a0e0ddb46f5185161049a79b7e974454223be"}, +] + +[package.extras] +dev = ["pre-commit", "tox"] +testing = ["pytest", "pytest-benchmark"] + +[[package]] +name = "pytest" +version = "8.1.1" +description = "pytest: simple powerful testing with Python" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pytest-8.1.1-py3-none-any.whl", hash = "sha256:2a8386cfc11fa9d2c50ee7b2a57e7d898ef90470a7a34c4b949ff59662bb78b7"}, + {file = "pytest-8.1.1.tar.gz", hash = "sha256:ac978141a75948948817d360297b7aae0fcb9d6ff6bc9ec6d514b85d5a65c044"}, +] + +[package.dependencies] +colorama = {version = "*", markers = "sys_platform == \"win32\""} +exceptiongroup = {version = ">=1.0.0rc8", markers = "python_version < \"3.11\""} +iniconfig = "*" +packaging = "*" +pluggy = ">=1.4,<2.0" +tomli = {version = ">=1", markers = "python_version < \"3.11\""} + +[package.extras] +testing = ["argcomplete", "attrs (>=19.2)", "hypothesis (>=3.56)", "mock", "pygments (>=2.7.2)", "requests", "setuptools", "xmlschema"] + +[[package]] +name = "pytest-cov" +version = "4.1.0" +description = "Pytest plugin for measuring coverage." +optional = false +python-versions = ">=3.7" +files = [ + {file = "pytest-cov-4.1.0.tar.gz", hash = "sha256:3904b13dfbfec47f003b8e77fd5b589cd11904a21ddf1ab38a64f204d6a10ef6"}, + {file = "pytest_cov-4.1.0-py3-none-any.whl", hash = "sha256:6ba70b9e97e69fcc3fb45bfeab2d0a138fb65c4d0d6a41ef33983ad114be8c3a"}, +] + +[package.dependencies] +coverage = {version = ">=5.2.1", extras = ["toml"]} +pytest = ">=4.6" + +[package.extras] +testing = ["fields", "hunter", "process-tests", "pytest-xdist", "six", "virtualenv"] + +[[package]] +name = "quantinuum-hugr" +version = "0.0.0" +description = "Quantinuum's common representation of quantum circuits and operations" +optional = false +python-versions = ">=3.10" +files = [] +develop = true + +[package.source] +type = "directory" +url = "quantinuum-hugr-py" + +[[package]] +name = "ruff" +version = "0.3.3" +description = "An extremely fast Python linter and code formatter, written in Rust." +optional = false +python-versions = ">=3.7" +files = [ + {file = "ruff-0.3.3-py3-none-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:973a0e388b7bc2e9148c7f9be8b8c6ae7471b9be37e1cc732f8f44a6f6d7720d"}, + {file = "ruff-0.3.3-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:cfa60d23269d6e2031129b053fdb4e5a7b0637fc6c9c0586737b962b2f834493"}, + {file = "ruff-0.3.3-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1eca7ff7a47043cf6ce5c7f45f603b09121a7cc047447744b029d1b719278eb5"}, + {file = "ruff-0.3.3-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:e7d3f6762217c1da954de24b4a1a70515630d29f71e268ec5000afe81377642d"}, + {file = "ruff-0.3.3-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b24c19e8598916d9c6f5a5437671f55ee93c212a2c4c569605dc3842b6820386"}, + {file = "ruff-0.3.3-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:5a6cbf216b69c7090f0fe4669501a27326c34e119068c1494f35aaf4cc683778"}, + {file = "ruff-0.3.3-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:352e95ead6964974b234e16ba8a66dad102ec7bf8ac064a23f95371d8b198aab"}, + {file = "ruff-0.3.3-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8d6ab88c81c4040a817aa432484e838aaddf8bfd7ca70e4e615482757acb64f8"}, + {file = "ruff-0.3.3-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:79bca3a03a759cc773fca69e0bdeac8abd1c13c31b798d5bb3c9da4a03144a9f"}, + {file = "ruff-0.3.3-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:2700a804d5336bcffe063fd789ca2c7b02b552d2e323a336700abb8ae9e6a3f8"}, + {file = "ruff-0.3.3-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:fd66469f1a18fdb9d32e22b79f486223052ddf057dc56dea0caaf1a47bdfaf4e"}, + {file = "ruff-0.3.3-py3-none-musllinux_1_2_i686.whl", hash = "sha256:45817af234605525cdf6317005923bf532514e1ea3d9270acf61ca2440691376"}, + {file = "ruff-0.3.3-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:0da458989ce0159555ef224d5b7c24d3d2e4bf4c300b85467b08c3261c6bc6a8"}, + {file = "ruff-0.3.3-py3-none-win32.whl", hash = "sha256:f2831ec6a580a97f1ea82ea1eda0401c3cdf512cf2045fa3c85e8ef109e87de0"}, + {file = "ruff-0.3.3-py3-none-win_amd64.whl", hash = "sha256:be90bcae57c24d9f9d023b12d627e958eb55f595428bafcb7fec0791ad25ddfc"}, + {file = "ruff-0.3.3-py3-none-win_arm64.whl", hash = "sha256:0171aab5fecdc54383993389710a3d1227f2da124d76a2784a7098e818f92d61"}, + {file = "ruff-0.3.3.tar.gz", hash = "sha256:38671be06f57a2f8aba957d9f701ea889aa5736be806f18c0cd03d6ff0cbca8d"}, +] + +[[package]] +name = "tomli" +version = "2.0.1" +description = "A lil' TOML parser" +optional = false +python-versions = ">=3.7" +files = [ + {file = "tomli-2.0.1-py3-none-any.whl", hash = "sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc"}, + {file = "tomli-2.0.1.tar.gz", hash = "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f"}, +] + +[[package]] +name = "typing-extensions" +version = "4.10.0" +description = "Backported and Experimental Type Hints for Python 3.8+" +optional = false +python-versions = ">=3.8" +files = [ + {file = "typing_extensions-4.10.0-py3-none-any.whl", hash = "sha256:69b1a937c3a517342112fb4c6df7e72fc39a38e7891a5730ed4985b5214b5475"}, + {file = "typing_extensions-4.10.0.tar.gz", hash = "sha256:b0abd7c89e8fb96f98db18d86106ff1d90ab692004eb746cf6eda2682f91b3cb"}, +] + +[metadata] +lock-version = "2.0" +python-versions = "^3.10" +content-hash = "eafdb1c10c2862ba10b0bfc815d41f1f1dd2799971c34f1a93c5a807d0319f1e" diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 000000000..ab5aef9cc --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,25 @@ +[tool.poetry] +description = "Namespace for the python packages in the HUGR repository. See the individual packages for more information." +name = "hugr-project" +version = "0.0.0" +authors = [] +readme = "README.md" +packages = [] + +package-mode = false + +[tool.poetry.group.main.dependencies] +python = "^3.10" + +[tool.poetry.group.dev.dependencies] +pytest = "^8.1.1" +pytest-cov = "^4.1.0" +mypy = "^1.9.0" +ruff = "^0.3.3" + +[tool.poetry.group.quantinuum-hugr.dependencies] +quantinuum-hugr = { path = "quantinuum-hugr-py", develop = true } + +[build-system] +requires = ["poetry-core"] +build-backend = "poetry.core.masonry.api" diff --git a/quantinuum-hugr-py/CHANGELOG.md b/quantinuum-hugr-py/CHANGELOG.md new file mode 100644 index 000000000..825c32f0d --- /dev/null +++ b/quantinuum-hugr-py/CHANGELOG.md @@ -0,0 +1 @@ +# Changelog diff --git a/quantinuum-hugr-py/README.md b/quantinuum-hugr-py/README.md new file mode 100644 index 000000000..a03779b7a --- /dev/null +++ b/quantinuum-hugr-py/README.md @@ -0,0 +1,53 @@ +quantinuum_hugr +=============== + +[![build_status][]](https://github.com/CQCL/hugr/actions) +[![codecov][]](https://codecov.io/gh/CQCL/hugr) + +The Hierarchical Unified Graph Representation (HUGR, pronounced _hugger_) is the +common representation of quantum circuits and operations in the Quantinuum +ecosystem. + +This library provides a pure-python implementation of the HUGR data model, and +a low-level API for constructing HUGR objects. + +This library is intended to be used as a dependency for other high-level tools. +See [`guppylang`][] and [`tket2`][] for examples of such tools. + +The HUGR specification is [here](https://github.com/CQCL/hugr/blob/main/specification/hugr.md). + + [`guppylang`]: https://pypi.org/project/guppylang/ + [`tket2`]: https://github.com/CQCL/tket2 + + +## Installation + +TODO + +The package name is `quantinuum_hugr`, but it hasn't been published yet. +The current experimental version can be installed from the source code: + +```bash +pip install "quantinuum_hugr@git+https://github.com/CQCL/hugr.git@main#subdirectory=quantinuum-hugr-py" +``` + +## Usage + +TODO + +## Recent Changes + +TODO + +## Development + +TODO + +## License + +This project is licensed under Apache License, Version 2.0 ([LICENSE][] or http://www.apache.org/licenses/LICENSE-2.0). + + [build_status]: https://github.com/CQCL/hugr/actions/workflows/ci-py.yml/badge.svg?branch=main + [codecov]: https://img.shields.io/codecov/c/gh/CQCL/hugr?logo=codecov + [LICENSE]: https://github.com/CQCL/hugr/blob/main/LICENCE + [CHANGELOG]: https://github.com/CQCL/hugr/blob/main/quantinuum-hugr-py/CHANGELOG.md diff --git a/quantinuum-hugr-py/pyproject.toml b/quantinuum-hugr-py/pyproject.toml new file mode 100644 index 000000000..ae1b60f8f --- /dev/null +++ b/quantinuum-hugr-py/pyproject.toml @@ -0,0 +1,19 @@ +[tool.poetry] +name = "quantinuum_hugr" +version = "0.0.0" +description = "Quantinuum's common representation for quantum programs" +classifiers = [] # TODO +keywords = [] # TODO +authors = [] # TODO +maintainers = [] # TODO +license = "Apache-2.0" +readme = "README.md" +homepage = "https://github.com/CQCL/hugr" +repository = "https://github.com/CQCL/hugr" + +[tool.poetry.dependencies] +python = ">=3.10" + +[tool.pytest.ini_options] +# Lark throws deprecation warnings for `src_parse` and `src_constants`. +filterwarnings = "ignore::DeprecationWarning:lark.*" diff --git a/quantinuum-hugr-py/src/quantinuum_hugr/__init__.py b/quantinuum-hugr-py/src/quantinuum_hugr/__init__.py new file mode 100644 index 000000000..606c3dc58 --- /dev/null +++ b/quantinuum-hugr-py/src/quantinuum_hugr/__init__.py @@ -0,0 +1,8 @@ +"""`quantinuum-hugr` is a Python package for the Quantinuum HUGR common +representation. +""" + + +def it_works() -> str: + """Return a string to confirm that the package is installed and working.""" + return "It works!" diff --git a/quantinuum-hugr-py/tests/__init__.py b/quantinuum-hugr-py/tests/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/quantinuum-hugr-py/tests/serialization/__init__.py b/quantinuum-hugr-py/tests/serialization/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/quantinuum-hugr-py/tests/serialization/test_basic.py b/quantinuum-hugr-py/tests/serialization/test_basic.py new file mode 100644 index 000000000..e29fd40c0 --- /dev/null +++ b/quantinuum-hugr-py/tests/serialization/test_basic.py @@ -0,0 +1,3 @@ +def test_it_works(): + """TODO: Replace this with a real test.""" + assert 2 + 2 != "🐟"