Skip to content

Commit

Permalink
Make sure that 'tox -erust' fails on bad RC. (#13753)
Browse files Browse the repository at this point in the history
* Make sure that 'tox -erust' fails on bad RC.

Previously, a non-zero exit code from the subprocess would not
fail the tox run like we'd expect.

* Use script to run cargo test, simplify subprocess call.

* Use PYTHONPATH instead of PYTHONUSERBASE.

This fixes an issue where the site-packages added to sys.path
could be wrong (e.g. if using Framework-based Python on macOS).

This was happening because the venv would install qiskit using
one pathing scheme while the Python interpreter baked into the test
executable would use (potentially) another when generating the path
from PYTHONUSERBASE.

See https://docs.python.org/3/library/sysconfig.html#installation-paths
for details.

To resolve this, we just set the path to the venv's site-packages via
PYTHONPATH.

* Add CWD to search path when running run_cargo_test.py

* Update docs.
  • Loading branch information
kevinhartman authored Feb 4, 2025
1 parent 104aaca commit 8095ace
Show file tree
Hide file tree
Showing 4 changed files with 50 additions and 32 deletions.
13 changes: 3 additions & 10 deletions .azure/test-linux.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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: |
Expand Down
18 changes: 3 additions & 15 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
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")])
)

# 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
Expand Up @@ -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
Expand Down

0 comments on commit 8095ace

Please sign in to comment.