Welcome to the tket development guide
May 22, 2026 · View on GitHub
This guide is intended to help you get started with developing tket.
If you find any errors or omissions in this document, please open an issue!
#️⃣ Setting up the development environment
You can setup the development environment in two ways:
The Nix way
The easiest way to setup the development environment is to use the provided
devenv.nix file. This will setup a development shell with all the
required dependencies.
To use this, you will need to install devenv. Once you have it running, open a shell with:
devenv shell
All the required dependencies should be available. You can automate loading the shell by setting up direnv.
Manual setup
To setup the environment manually you will need:
- Just: https://just.systems/
- Rust
>=1.91: https://www.rust-lang.org/tools/install - cargo-nextest: https://nexte.st/docs/installation/pre-built-binaries/
- uv
>=0.11: docs.astral.sh/uv/getting-started/installation - conan
>=2.0.0,<3: This gets installed byjust setup/uv tool install conan - Optional: llvm
== 21.1. The "llvm" feature (backed by the sub-cratehugr-llvm) requires LLVM 21.1 installed. We use the rust bindings from llvm-sys.
Once you have these installed, install the required python dependencies and setup pre-commit hooks with:
just setup
Note on LLVM
You will need llvm 21.1 installed in order for just check to run all its
checks successfully. On Debian-based systems you can install it as the
llvm-21 package; you may also need to install libpolly-21-dev. You should
set the environment variable LLVM_SYS_211_PREFIX to point to its location
(e.g. /usr/lib/llvm-21) when running just check.
Using hugrenv without Nix
If you are not using Nix, you can fetch the same prebuilt llvm + tket
artifacts used by CI using
just fetch-hugrenv
with an optional installation path parameter.
This command:
- detects your OS/architecture;
- downloads the matching
hugrverse-envrelease tarballs; - uses the version and supported target matrix pinned in
hugrenv.lock; - extracts them into the path you provide, or
./target/hugrenv/by default; - prints the environment variables to export for your shell.
Using the default installation path is convenient for single-repo local work, but if you want to manage multiple hugrenv versions (or share one install across multiple repositories), prefer an explicit central location and/or versioned install paths.
For bash/zsh this includes:
TKET_C_API_PATH=/path/to/hugrenvLLVM_SYS_211_PREFIX=/path/to/hugrenvLIBCLANG_PATH=/path/to/hugrenv/libPATH=/path/to/hugrenv/bin:$PATHLD_LIBRARY_PATH(Linux) orDYLD_LIBRARY_PATH(macOS) including/path/to/hugrenv/liband/path/to/hugrenv/lib64.
🚀 Local development using the tket python library
If you want to use the tket python library directly from the repository, you can build it with:
just build
This will build the python wheels and make them available in the target/wheels folder.
Alternatively, you can build the package directly into a virtual environment as an editable package.
That way, you can make changes to the python code and see the changes reflected in your environment.
For this you must have maturin installed (you can install it with pip install maturin) and run:
maturin develop
🏃 Running the tests
To compile and test the code, run:
just test
# or, to test only the rust code or the python code
just test-rust
just test-python
Run just to see all available commands.
💅 Coding Style
We use rustfmt and ruff to enforce a consistent coding style. The CI will fail if the code is not formatted correctly.
To format your code, run:
just format
We also use various linters to catch common mistakes and enforce best practices. To run these, use:
just check
To quickly fix common issues, run:
just fix
# or, to fix only the rust code or the python code
just fix-rust
just fix-python
📈 Code Coverage
We run coverage checks on the CI. Once you submit a PR, you can review the line-by-line coverage report on codecov.
To run the coverage checks locally, first install cargo-llvm-cov.
cargo install cargo-llvm-cov
Then run the tests:
just coverage
This will generate a coverage file that can be opened with your favourite coverage viewer. In VSCode, you can use
coverage-gutters.
🌐 Contributing to tket
We welcome contributions to tket! Please open an issue or pull request if you have any questions or suggestions.
PRs should be made against the main branch, and should pass all CI checks before being merged. This includes using the conventional commits format for the PR title.
Some tests may be skipped based on the changes made. To run all the tests in your PR mark it with a 'X-run-thorough-ci-tests' label and push new commits to it.
The general format of a contribution title should be:
<type>(<scope>)!: <description>
Where the scope is optional, and the ! is only included if this is a semver breaking change that requires a major version bump.
We accept the following contribution types:
- feat: New features.
- fix: Bug fixes.
- docs: Improvements to the documentation.
- style: Formatting, missing semi colons, etc; no code change.
- refactor: Refactoring code without changing behaviour.
- perf: Code refactoring focused on improving performance.
- test: Adding missing tests, refactoring tests; no production code change.
- ci: CI related changes. These changes are not published in the changelog.
- chore: Updating build tasks, package manager configs, etc. These changes are not published in the changelog.
- revert: Reverting previous commits.
🛠️ Common problems and solutions
Here is a list of common problems found during development, and how to solve them.
-
ImportError: cannot import name 'metadata' from 'tket._tket' (.../tket/_tket.abi3.so)when running python tests.The Rust bindings need to be recompiled. Run
uv run maturin developto recompile them manually or usejust test-pythoninstead ofpytestto do it automatically. -
ValueError: Missing 'project' metadata table in configurationwhen runningmaturin develop.This is caused by an outdated version of
uv. Runuv self updateto update to the latest version.
:shipit: Releasing new versions
We use automation to bump the version number and generate changelog entries
based on the conventional commits labels. Release PRs are created automatically
for each package when new changes are merged into the main branch. Once the PR is
approved by someone in the release team and is merged, the new package
is published on PyPI or crates.io as appropriate.
The changelog can be manually edited before merging the release PR. Note however that modifying the diff before other changes are merged will cause the automation to close the release PR and create a new one to avoid conflicts.
Rust crate release
Rust releases are managed by release-plz. This tool will automatically detect
breaking changes even when they are not marked as such in the commit message,
and bump the version accordingly.
To modify the version being released, update the Cargo.toml,
CHANGELOG.md, PR name, and PR description in the release PR with the desired version. You may also have to update the dates.
Rust pre-release versions should be formatted as 0.1.0-alpha.1 (or -beta, or -rc).
Python package release
Python releases are managed by release-please. This tool always bumps the
minor version (or the pre-release version if the previous version was a
pre-release).
To override the version getting released, you must merge a PR to main containing
Release-As: 0.1.0 in the description.
Python pre-release versions should be formatted as 0.1.0a1 (or b1, rc1).
Patch releases
Sometimes we need to release a patch version to fix a critical bug, but we don't want to include all the changes that have been merged into the main branch. In this case, you can create a new branch from the latest release tag and cherry-pick the commits you want to include in the patch release.
Rust patch releases
You can use release-plz to automatically generate the changelogs and bump the package versions.
# If you have cargo-semver-checks installed,
# release-plz will ensure your changes don't break the semver rules.
cargo install cargo-semver-checks --locked
# Analyze the new comments to generate the changelogs / bump the versions
release-plz update
Once the branch is ready, create a draft PR so that the release team can review it.
Now someone from the release team can run release-plz on the unmerged
branch to create the github releases and publish to crates.io.
# Make sure you are logged in to `crates.io`
cargo login <your_crates_io_token>
# Get a github token with permissions to create releases
GITHUB_TOKEN=<your_github_token>
# Run release-plz
release-plz release --git-token $GITHUB_TOKEN
Python patch releases
You will need to modify the version and changelog manually in this case. Check the existing release PRs for examples on how to do this. Once the branch is ready, create a draft PR so that the release team can review it.
The wheel building process and publication to PyPI is handled by the CI.
Just create a github release from the unmerged branch.
The release tag should follow the format used in the previous releases, e.g. tket-py-v0.1.1.
🎫 TKET, pytket, and TKET2
For historical reasons, the repository hosting this project is called tket2.
pytket / tket (a.k.a. tket1) was the first version of the tket library, written in C++ and Python.
TKET2 was the grounds-up rewrite of the compiler, based on the HUGR IR and aimed at
optimising quantum programs with complex classical control flow, type generics, etc.
For simplicity, the tket2 libraries were renamed to tket. Throughout the
project we refer to the new codebase as tket, while the old codebase is
referred to as pytket, legacy tket, or tket1 depending on the context.
Some instances of the tket2 have been left in the codebase to avoid breaking
the API, but these may be removed in the future.