You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Add test helper for running a specific test in an isolated subprocess #2412
There are 4 places in the code base that run an individual test in an isolated subprocess (to avoid the side effects that test would cause to other tests).
We should refactor that out into a helper. Probably after #2384 which organizes the test helpers.
Here is an agent-generated rough start to such a helper:
import os
import subprocess
import sys
import pytest
# Define an environment variable key to prevent infinite subprocess loops
SUBPROCESS_ENV_KEY = "PYTEST_RUNNING_IN_SUBPROCESS"
@pytest.fixture
def run_in_subprocess(request):
"""
Reruns the current test inside an isolated python subprocess.
Skips original in-process execution if triggered.
"""
# If this token is set, we are already inside the subprocess. Proceed with test logic.
if os.environ.get(SUBPROCESS_ENV_KEY) == "1":
yield
return
# Build the exact command to re-run only this specific test node
cmd = [
sys.executable, # Current Python interpreter path
"-m", "pytest",
request.node.nodeid, # Specific ID of the active test case
"-s" # Allow output to display
]
# Inject the loop prevention variable into the environment
env = os.environ.copy()
env[SUBPROCESS_ENV_KEY] = "1"
# Spawn the subprocess and block until completion
result = subprocess.run(cmd, env=env)
# Propagate the subprocess exit status back to the main pytest suite
if result.returncode != 0:
pytest.fail(f"Test failed in subprocess with exit code {result.returncode}")
# Skip standard in-process execution since it has completed externally
pytest.skip("Test executed successfully inside subprocess.")
@mdboom I would like to take this one, if you and @Andy-Jost are happy to assign it to me. #2384 has landed, so the dependency you noted is cleared.
I have it implemented and validated locally. Two things I want to check with you before it becomes a PR, because I diverged from the sketch in the description.
There are 5 sites now, not 4.rg 'sys.executable, "-c"' gives:
The fifth is test_graph_definition_lifetime.py:790, added by #2406 the same day this issue was filed, which is presumably why the count was 4 at the time.
Why I did not use the run_in_subprocess fixture shape. The sketch re-runs the current test node with pytest <nodeid>. None of the 5 sites are shaped that way: each one runs a self-contained snippet with -c, and three of them exist specifically to isolate a process-global side effect (the CPython pending-call queue, the import-time rlcompleter patch, a deliberate glibc abort). Re-running the node would also inherit pytest's rootdir, which is the exact hazard two of these sites work around by passing a neutral cwd: python -c puts the parent's working directory at the head of sys.path, so running from cuda_core/ makes the child import the cuda/core/ source tree instead of the installed wheel.
So I extracted what the 5 sites actually share instead:
in cuda_python_test_helpers/subprocess_runner.py. cwd is required rather than defaulted, which is the part I think earns its keep: the shadowing trap is currently rediscovered per site, and two sites carry a comment explaining it. The helper also always captures text, always closes stdin (PYTHONINSPECT keeps the interpreter alive after -c), and puts both streams into the failure message.
It is a pure refactor, no behavior changes: unset_env is opt-in per call site, so the two graph tests and the bindings test keep inheriting PYTHONPATH exactly as they do today, and _run_probe keeps check=False because its callers assert on the exit code themselves.
Net is 21 added, 59 removed across the 5 sites, plus the helper and 9 CPU-only tests for it.
One open question: I put the helper's own tests at cuda_core/tests/test_subprocess_runner.py because cuda_python_test_helpers has no test directory and is not in testpaths. Happy to add one and wire it into pytest.ini instead if you would rather the helper's tests live next to the helper.
If you assign it to me I will open the PR right away.
Activity
aryanputta commentedon Aug 16, 2026
@mdboom I would like to take this one, if you and @Andy-Jost are happy to assign it to me. #2384 has landed, so the dependency you noted is cleared.
I have it implemented and validated locally. Two things I want to check with you before it becomes a PR, because I diverged from the sketch in the description.
There are 5 sites now, not 4.
rg 'sys.executable, "-c"'gives:cuda_core/tests/test_rlcompleter_patch.py:76(_run_probe)cuda_core/tests/test_rlcompleter_patch.py:158(opt-out probe)cuda_core/tests/graph/test_graph_definition_lifetime.py:790cuda_core/tests/graph/test_graph_definition_lifetime.py:819cuda_bindings/tests/test_cuda.py:1292The fifth is
test_graph_definition_lifetime.py:790, added by #2406 the same day this issue was filed, which is presumably why the count was 4 at the time.Why I did not use the
run_in_subprocessfixture shape. The sketch re-runs the current test node withpytest <nodeid>. None of the 5 sites are shaped that way: each one runs a self-contained snippet with-c, and three of them exist specifically to isolate a process-global side effect (the CPython pending-call queue, the import-time rlcompleter patch, a deliberate glibc abort). Re-running the node would also inherit pytest's rootdir, which is the exact hazard two of these sites work around by passing a neutralcwd:python -cputs the parent's working directory at the head ofsys.path, so running fromcuda_core/makes the child import thecuda/core/source tree instead of the installed wheel.So I extracted what the 5 sites actually share instead:
in
cuda_python_test_helpers/subprocess_runner.py.cwdis required rather than defaulted, which is the part I think earns its keep: the shadowing trap is currently rediscovered per site, and two sites carry a comment explaining it. The helper also always captures text, always closes stdin (PYTHONINSPECTkeeps the interpreter alive after-c), and puts both streams into the failure message.It is a pure refactor, no behavior changes:
unset_envis opt-in per call site, so the two graph tests and the bindings test keep inheritingPYTHONPATHexactly as they do today, and_run_probekeepscheck=Falsebecause its callers assert on the exit code themselves.Net is 21 added, 59 removed across the 5 sites, plus the helper and 9 CPU-only tests for it.
One open question: I put the helper's own tests at
cuda_core/tests/test_subprocess_runner.pybecausecuda_python_test_helpershas no test directory and is not intestpaths. Happy to add one and wire it intopytest.iniinstead if you would rather the helper's tests live next to the helper.If you assign it to me I will open the PR right away.