Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Make sure that 'tox -erust' fails on bad RC. #13753

Merged
merged 5 commits into from
Feb 4, 2025
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 3 additions & 10 deletions .azure/test-linux.yml
Original file line number Diff line number Diff line change
@@ -96,20 +96,13 @@ jobs:
sudo apt-get install -y graphviz
displayName: 'Install optional non-Python dependencies'

# Note that we explicitly use the virtual env with Qiskit installed to run the Rust
# tests since some of them still depend on Qiskit's Python API via PyO3.
- ${{ if eq(parameters.testRust, true) }}:
# We need to avoid linking our crates into full Python extension libraries during Rust-only
# testing because Rust/PyO3 can't handle finding a static CPython interpreter.
# Note that we use the virtual env with Qiskit installed to run the Rust
# tests since some of them still depend on Qiskit's Python API via PyO3.
- bash: |
source test-job/bin/activate
python tools/report_numpy_state.py
PYTHONUSERBASE="$VIRTUAL_ENV" cargo test --no-default-features
env:
# On Linux we link against `libpython` dynamically, but it isn't written into the rpath
# of the test executable (I'm not 100% sure why ---Jake). It's easiest just to forcibly
# include the correct place in the `dlopen` search path.
LD_LIBRARY_PATH: '$(usePython.pythonLocation)/lib:$LD_LIBRARY_PATH'
python tools/run_cargo_test.py
displayName: "Run Rust tests"

- bash: |
18 changes: 3 additions & 15 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
@@ -619,25 +619,13 @@ Then, run the following commands:

```bash
python setup.py build_rust --inplace
PYTHONUSERBASE="$VIRTUAL_ENV" cargo test --no-default-features
tools/run_cargo_test.py
```

> [!IMPORTANT]
> On Linux, you may need to first set your `LD_LIBRARY_PATH` env var to include the
> path to your Python installation's shared lib, e.g.:
> ```bash
> export LD_LIBRARY_PATH="$(python -c 'import sysconfig; print(sysconfig.get_config_var("LIBDIR"))'):$LD_LIBRARY_PATH"
> ```

The first command builds Qiskit in editable mode,
which ensures that Rust tests that interact with Qiskit's Python code actually
use the latest Python code from your working directory.

The second command actually invokes the tests via Cargo. The `PYTHONUSERBASE`
environment variable tells the embedded Python interpreter to look for packages
in your active virtual environment. The `--no-default-features`
flag is used to compile an isolated test runner without building a linked CPython
extension module (which would otherwise cause linker failures).
use the latest Python code from your working directory. The second command invokes
the tests via Cargo.

#### Calling Python from Rust tests
By default, our Cargo project configuration allows Rust tests to interact with the
43 changes: 43 additions & 0 deletions tools/run_cargo_test.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
#!/usr/bin/env python3

# This code is part of Qiskit.
#
# (C) Copyright IBM 2025.
#
# This code is licensed under the Apache License, Version 2.0. You may
# obtain a copy of this license in the LICENSE.txt file in the root directory
# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0.
#
# Any modifications or derivative works of this code must retain this
# copyright notice, and modified files need to carry a notice indicating
# that they have been altered from the originals.

"""
Utility script to invoke cargo test within the current Python environment.

Notably, this sets up the environment variables necessary for Qiskit to be
found by PyO3 / our Rust test executable.
"""

import os
import subprocess
import site
import sysconfig

# This allows the Python interpreter baked into our test executable to find the
# Qiskit installed in the active environment.
os.environ["PYTHONPATH"] = os.pathsep.join([os.getcwd()] + site.getsitepackages())

# Uncomment to debug PyO3's build / link against Python.
# os.environ["PYO3_PRINT_CONFIG"] = "1"

# On Linux, the test executable's RPATH doesn't contain libpython, so we add it
# to the dlopen search path here.
os.environ["LD_LIBRARY_PATH"] = os.pathsep.join(
filter(None, [sysconfig.get_config_var("LIBDIR"), os.getenv("LD_LIBRARY_PATH")])
)
Comment on lines +36 to +38
Copy link
Member

Choose a reason for hiding this comment

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

Is there a reason to not just do?:

Suggested change
os.environ["LD_LIBRARY_PATH"] = os.pathsep.join(
filter(None, [sysconfig.get_config_var("LIBDIR"), os.getenv("LD_LIBRARY_PATH")])
)
os.environ["LD_LIBRARY_PATH"] = os.pathsep.join([
sysconfig.get_config_var("LIBDIR"),
os.getenv("LD_LIBRARY_PATH")
])

I've always found the filter(None, ...) pattern a bit odd when you can just use a generator expression.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The reason I did that was to filter out None in the case that "LD_LIBRARY_PATH" isn't actually set to anything. I can do something different if you prefer!


# The '--no-default-features' flag is used here to disable PyO3's
# 'extension-module' when running the tests (which would otherwise cause link
# errors).
subprocess.run(["cargo", "test", "--no-default-features"], check=True)
8 changes: 1 addition & 7 deletions tox.ini
Original file line number Diff line number Diff line change
@@ -34,14 +34,8 @@ commands =
[testenv:rust]
basepython = python3
package_env = .pkg-rust
setenv =
PYTHONUSERBASE={envdir}
allowlist_externals = cargo
commands =
python -c '\
import os, subprocess, sysconfig;\
os.environ["LD_LIBRARY_PATH"] = os.pathsep.join(filter(None, [sysconfig.get_config_var("LIBDIR"), os.getenv("LD_LIBRARY_PATH")]));\
subprocess.run(["cargo", "test", "--no-default-features"])'
commands = python tools/run_cargo_test.py

# This is a special package_env used by the 'rust' env above
# to force Qiskit's Rust code to build in debug mode. We do this