Skip to content

Add generated qiskit-pyo3-ffi crate#16145

Draft
jakelishman wants to merge 2 commits intoQiskit:mainfrom
jakelishman:c/rust-py
Draft

Add generated qiskit-pyo3-ffi crate#16145
jakelishman wants to merge 2 commits intoQiskit:mainfrom
jakelishman:c/rust-py

Conversation

@jakelishman
Copy link
Copy Markdown
Member

@jakelishman jakelishman commented May 5, 2026

This adds logic to our qiskit-bindgen export library to generate raw Rust-space bindings that link against the Qiskit C API using the same PyCapsule-stored vtable system that the qiskit.h header file in QISKIT_EXTENSION_MODULE mode does.

For the build process, this adds a new make command:

make qiskit-pyo3-ffi

that populates the complete crate into dist/rust. The low-level worker of the make command is the new subcommand

[cargo run -p] qiskit-bindgen-cli -- generate-pyo3 -c crates/cext -o dist/rust/qiskit-pyo3-ffi

I've done this using a partial "template" crate embedded in crates/bindgen (but not within its src tree), which is analogous to how we output the C header files; many of the infrastructure files are hand-written, and only part of the system is generated. I set up the template crate crates/bindgen/pyo3-ffi to be largely sensible with respect to using rust-analyzer-enabled IDEs or clippy commands to check the template code itself.

The template crate is not part of the workspace because it's intended for separate distribution. This commit has all the logic to produce the distribution artefact, but doesn't set up any sort of CD pipeline, because we still have some packaging decisions to make about that.

Close #15985

AI/LLM disclosure

  • I didn't use LLM tooling, or only used it privately.
  • I used the following tool to help write this PR description:
  • I used the following tool to generate or modify code:

Based on #16144.

While starting to write a Rust/PyO3 raw-FFI backend for the C API via
the Python package, I realised that a lot of the logic was about to be
duplicated from the `ctypes` output.  Had I realised this sooner, I'd
have folded this commit into ae91986[^1] to avoid an immediate refactor.

I've not attempted to de-duplicate _all_ logic that ever might occur
(for example, lifting a `Function` is going to look _mostly_ similar but
not identical), because there's enough little tweaks between the
languages to make the abstraction awkward, and we're unlikely to have
enough languages to really warrant it.  For example, the `ctypes`
binding needs to know that any custom "path" name of an `Enum` can't
actually be exported to that name, because we use Python-space true
`Enum` classes in the `ctypes` bindings for input.  This restriction
isn't present in other languages.

[^1]: ae91986: Add auto-generated `ctypes` bindings to C API (Qiskitgh-16067)
@jakelishman jakelishman added this to the 2.5.0 milestone May 5, 2026
@jakelishman jakelishman added on hold Can not fix yet Changelog: None Do not include in the GitHub Release changelog. Rust This PR or issue is related to Rust code in the repository labels May 5, 2026
@github-project-automation github-project-automation Bot moved this to Ready in Qiskit 2.5 May 5, 2026
This adds logic to our `qiskit-bindgen` export library to generate
raw Rust-space bindings that link against the Qiskit C API using the
same `PyCapsule`-stored vtable system that the `qiskit.h` header file in
`QISKIT_EXTENSION_MODULE` mode does.

For the build process, this adds a new `make` command:

```bash
make qiskit-pyo3-ffi
```

that populates the complete crate into `dist/rust`.  The low-level
worker of the `make` command is the new subcommand

```bash
[cargo run -p] qiskit-bindgen-cli -- generate-pyo3 -c crates/cext -o dist/rust/qiskit-pyo3-ffi
```

I've done this using a partial "template" crate embedded in
`crates/bindgen` (but not within its `src` tree), which is analogous to
how we output the C header files; many of the infrastructure files are
hand-written, and only part of the system is generated.  I set up the
template crate `crates/bindgen/pyo3-ffi` to be largely sensible with
respect to using `rust-analyzer`-enabled IDEs or clippy commands to
check the template code itself.

The template crate is not part of the workspace because it's intended
for separate distribution.  This commit has all the logic to produce the
distribution artefact, but doesn't set up any sort of CD pipeline,
because we still have some packaging decisions to make about that.
@coveralls
Copy link
Copy Markdown

Coverage Report for CI Build 25404074983

Coverage decreased (-0.2%) to 87.369%

Details

  • Coverage decreased (-0.2%) from the base build.
  • Patch coverage: 326 uncovered changes across 5 files (185 of 511 lines covered, 36.2%).
  • 13 coverage regressions across 3 files.

Uncovered Changes

File Changed Covered %
crates/bindgen/src/render/rust.rs 266 0 0.0%
crates/bindgen/src/lib.rs 39 0 0.0%
crates/bindgen/src/simple_ir.rs 55 41 74.55%
crates/bindgen-cli/src/main.rs 5 0 0.0%
crates/bindgen/src/render/ctypes.rs 143 141 98.6%

Coverage Regressions

13 previously-covered lines in 3 files lost coverage.

File Lines Losing Coverage Coverage
crates/qasm2/src/parse.rs 6 97.63%
crates/qasm2/src/lex.rs 5 92.03%
crates/circuit/src/parameter/symbol_expr.rs 2 74.29%

Coverage Stats

Coverage Status
Relevant Lines: 122180
Covered Lines: 106747
Line Coverage: 87.37%
Coverage Strength: 961571.42 hits per line

💛 - Coveralls

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Changelog: None Do not include in the GitHub Release changelog. on hold Can not fix yet Rust This PR or issue is related to Rust code in the repository

Projects

Status: Ready

Development

Successfully merging this pull request may close these issues.

Support writing Rust/Python extension modules with the raw C API

2 participants