Skip to content

Commit

Permalink
feat: Add a python package (#885)
Browse files Browse the repository at this point in the history
Adds all the boilerplate for a `quantinuum-hugr` python package.

This includes conditional CI checks (dependant on the changed files),
and other metadata.

What's missing is a `release-please` configuration to generate python
changelogs. `release-plz` should already be configured to only include
changes that modify the rust project.

This is the foundation required for #486 and
CQCL/guppylang#173, and it should make adding
another package for #866 quite easy.
  • Loading branch information
aborgna-q authored Mar 19, 2024
1 parent 76d3ea3 commit c10bad0
Show file tree
Hide file tree
Showing 16 changed files with 696 additions and 22 deletions.
26 changes: 26 additions & 0 deletions .github/pre-commit
Original file line number Diff line number Diff line change
Expand Up @@ -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
124 changes: 124 additions & 0 deletions .github/workflows/ci-py.yml
Original file line number Diff line number Diff line change
@@ -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 }}
101 changes: 81 additions & 20 deletions .github/workflows/ci.yml → .github/workflows/ci-rs.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
name: Continuous integration
name: Continuous integration 🦀

on:
push:
Expand All @@ -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
Expand All @@ -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
Expand All @@ -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://gh.neting.ccmunity/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
Expand All @@ -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
Expand All @@ -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 }}
23 changes: 22 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -24,4 +24,25 @@ devenv.local.nix
.pre-commit-config.yaml

# Coverage report
lcov.info
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
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
25 changes: 25 additions & 0 deletions codecov.yml
Original file line number Diff line number Diff line change
@@ -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
14 changes: 14 additions & 0 deletions justfile
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand All @@ -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
Loading

0 comments on commit c10bad0

Please sign in to comment.