Skip to content

Conversation

@jakelishman
Copy link
Member

@jakelishman jakelishman commented Oct 20, 2025

Summary

This small chain of commits follows the steps in the PyO3 migration guide to update from 0.26 to 0.27.

The main change is around the new FromPyObject signature.

The new form of the trait uses Borrowed, causing it to need another lifetime parameter, and an explicit Error type. Where appropriate / easy, I switched the error types away from the complete PyErr, even if most of our handling of those errors does eventually convert them into PyErr for now.

In places where extraction delegates to other methods, particularly where those methods are logical mirrors to FromPyObject, this commit also updates the signatures to use Borrowed instead of &Bound.

In a couple of places, the extraction being in terms of Borrowed meant that trait implementations that are only accessible on Bound (for example impl IntoIterator for &Bound<PyDict>) needed some dereferencing inserted to re-satisfy the bounds.

Details and comments

The downgrade of one particular instance of [email protected] to 0.15.5 is slightly coincidental - I had to undo some of the changes cargo applied while bumping PyO3, and apparently I was slightly overzealous. It's actually better to have slightly fewer builds of it, though, and we largely need things to be aligned since hashbrown frequently leaks out in interfaces between Qiskit, rustworkx and (conversion traits in) PyO3.

Blocked on PyO3/rust-numpy#515 for now.

The lint failure is caused by the bug fixed in PyO3/pyo3#5538, which should be backported in PyO3 0.27.1 in a day or so. If we want to merge this sooner, we can workspace-allow clippy::declare_interior_mutable_const in the root Cargo.toml.

See PyO3 0.27's migration guide for more information on some of the changes in here.

@jakelishman jakelishman added this to the 2.3.0 milestone Oct 20, 2025
@jakelishman jakelishman requested a review from a team as a code owner October 20, 2025 12:38
@jakelishman jakelishman added on hold Can not fix yet Changelog: None Do not include in changelog Rust This PR or issue is related to Rust code in the repository dependencies Pull requests that update a dependency file labels Oct 20, 2025
@qiskit-bot
Copy link
Collaborator

One or more of the following people are relevant to this code:

  • @Qiskit/terra-core

@coveralls
Copy link

coveralls commented Oct 20, 2025

Pull Request Test Coverage Report for Build 18885640393

Warning: This coverage report may be inaccurate.

This pull request's base commit is no longer the HEAD commit of its target branch. This means it includes changes from outside the original pull request, including, potentially, unrelated coverage changes.

Details

  • 264 of 289 (91.35%) changed or added relevant lines in 52 files are covered.
  • 36 unchanged lines in 5 files lost coverage.
  • Overall coverage decreased (-0.02%) to 88.168%

Changes Missing Coverage Covered Lines Changed/Added Lines %
crates/circuit/src/packed_instruction.rs 5 6 83.33%
crates/circuit/src/parameter_table.rs 1 2 50.0%
crates/qasm3/src/build.rs 0 1 0.0%
crates/qasm3/src/lib.rs 1 2 50.0%
crates/transpiler/src/passes/asap_schedule_analysis.rs 1 2 50.0%
crates/transpiler/src/equivalence.rs 3 5 60.0%
crates/circuit/src/circuit_instruction.rs 11 15 73.33%
crates/circuit/src/dag_circuit.rs 40 45 88.89%
crates/circuit/src/circuit_data.rs 25 34 73.53%
Files with Coverage Reduction New Missed Lines %
crates/circuit/src/circuit_data.rs 1 89.91%
crates/qasm2/src/expr.rs 1 93.82%
crates/qasm2/src/lex.rs 7 91.26%
crates/cext/src/transpiler/transpile_function.rs 9 59.52%
crates/qasm2/src/parse.rs 18 95.68%
Totals Coverage Status
Change from base Build 18848756770: -0.02%
Covered Lines: 93638
Relevant Lines: 106204

💛 - Coveralls

@jakelishman jakelishman force-pushed the pyo3-0.27 branch 2 times, most recently from 7b7f83d to a61c772 Compare October 28, 2025 11:42
@jakelishman jakelishman removed the on hold Can not fix yet label Oct 28, 2025
The new `cast` variants are available on both `Bound` and `Borrowed`,
and have slightly different semantics around the returned errors.
The new form of the trait uses `Borrowed`, causing it to need another
lifetime parameter, and an explicit `Error` type.  Where appropriate /
easy, I switched the error types away from the complete `PyErr`, even if
most of our handling of those errors does eventually convert them into
`PyErr` for now.

In places where extraction delegates to other methods, particularly
where those methods are logical mirrors to `FromPyObject`, this commit
also updates the signatures to use `Borrowed` instead of `&Bound`.

In a couple of places, the extraction being in terms of `Borrowed` meant
that trait implementations that are only accessible on `Bound` (for
example `impl IntoIterator for &Bound<PyDict>`) needed some
dereferencing inserted to re-satisfy the bounds.
Copy link
Member Author

@jakelishman jakelishman left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, I think this should be working now, with the release of rust-numpy 0.27 and pyo3 0.27.1. It was a bit of effort unifying the hashbrown versions between indexmap, rustworkx-core, pyo3 and qiskit, but I think I've got there now.

Copy link
Member

@mtreinish mtreinish left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Overall this LGTM. As you described in the PR summary this is mostly about adapting to the new FromPyObject trait which for the most part is pretty straightforward. I just had two small questions inline.

Err(PyTypeError::new_err(format!("invalid input: {ob}")))
Err(PyTypeError::new_err(format!(
"invalid input: {}",
ob.repr()?
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a different error message. I assume this was intentional because the Display return isn't as nice looking here?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ob changed from being Bound to Borrowed, and lost its Display impl. Using repr seemed more like what we intended than swapping to Debug, although:

  1. looking now, Bound<'_, T>::fmt used str
  2. it's maybe a PyO3 oversight that Borrowed doesn't have the same Display impl anyway.

I can swap to str if you prefer?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah I guess that probably would make more sense here. I guess I was just wondering what would be more natural here. But it's not a huge deal either way

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I changed it to ob.to_owned() in b0db82a, which will cause it to use exactly the same handling as before, since it'll then go down the Bound as Display path.

.extract(),
.extract()
// The extraction is infallible.
.map_err(|x| match x {}),
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this a normal pattern for discarding the error here? I probably have done something like:

Ok(extract().unwrap())

or something like that

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I did my way so the unwrap/expect will become a compilation error (unhandled match variants) if we change the error type and forget to update the handling. In nightly there's a Result::into_ok that would be clearer, but no date yet for a release of that.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this a normal pattern for discarding the error here?

Just to be clear, this doesn't discard the error - it completely handles all variants. It's just that the error is uninhabitable. If the error were any type (even ()) then my code would be a compile-time error.

@mtreinish mtreinish added this pull request to the merge queue Oct 28, 2025
Merged via the queue into Qiskit:main with commit 046aba2 Oct 28, 2025
23 checks passed
@github-project-automation github-project-automation bot moved this from Ready to Done in Qiskit 2.3 Oct 28, 2025
@jakelishman jakelishman deleted the pyo3-0.27 branch October 28, 2025 20:19
raynelfss pushed a commit to raynelfss/qiskit that referenced this pull request Oct 31, 2025
* Update PyO3 dependencies

* Switch uses of `Bound::downcast*` to corresponding `cast*`

The new `cast` variants are available on both `Bound` and `Borrowed`,
and have slightly different semantics around the returned errors.

* Switch to new `FromPyObject` signature

The new form of the trait uses `Borrowed`, causing it to need another
lifetime parameter, and an explicit `Error` type.  Where appropriate /
easy, I switched the error types away from the complete `PyErr`, even if
most of our handling of those errors does eventually convert them into
`PyErr` for now.

In places where extraction delegates to other methods, particularly
where those methods are logical mirrors to `FromPyObject`, this commit
also updates the signatures to use `Borrowed` instead of `&Bound`.

In a couple of places, the extraction being in terms of `Borrowed` meant
that trait implementations that are only accessible on `Bound` (for
example `impl IntoIterator for &Bound<PyDict>`) needed some
dereferencing inserted to re-satisfy the bounds.

* Switch to undeprecated `import_exception`

* Revert failure message change
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 changelog dependencies Pull requests that update a dependency file Rust This PR or issue is related to Rust code in the repository

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

4 participants