pytest-subproc
Run marked pytest test functions in an isolated subprocess to protect the main process from crashes caused by C++ panics, segfaults, or hangs.
Installation
pip install pytest-subproc
Usage
Mark any test with @pytest.mark.subproc to run it in a subprocess:
import pytest
@pytest.mark.subproc
def test_isolated():
assert True
Parameters
@pytest.mark.subproc(timeout=None, condition=None)
| Parameter | Type | Default | Description |
|---|---|---|---|
timeout |
float |
None |
Timeout in seconds. Kills the subprocess if the test exceeds this limit. Falls back to subproc_default_timeout ini option. |
condition |
bool or () -> bool |
True |
When falsy, the test runs in the main process normally. Useful for conditional isolation (e.g., only in CI). |
@pytest.mark.subproc(timeout=30)
def test_with_timeout():
...
@pytest.mark.subproc(condition=lambda: os.environ.get("CI") == "true")
def test_ci_only():
...
@pytest.mark.subproc(timeout=10, condition=False)
def test_never_subprocess():
...
Configuration via pyproject.toml / pytest.ini
[tool.pytest.ini_options]
subproc_default_timeout = 30
Or on the command line:
pytest --subprocess-timeout=30
Module-level configuration
Set defaults for all @pytest.mark.subproc tests in a directory tree by calling pytest_subproc methods in a conftest.py:
import pytest_subproc
# Default timeout (lowest priority: marker > CLI > ini > this)
pytest_subproc.config_default_timeout(30)
# Default condition for spawning (lowest priority: marker > this)
pytest_subproc.config_global_enabled(True)
pytest_subproc.config_global_enabled(lambda: os.environ.get("CI") == "true") # callable
@pytest.mark.timeout interaction
When a test has both @pytest.mark.subproc(timeout=5) and @pytest.mark.timeout(3),
the shorter value (3s) is used as the subprocess timeout. This prevents
pytest-timeout from killing the main process while the subprocess is still
running — our plugin cancels pytest-timeout's timer and enforces the
effective timeout on the subprocess itself.
@pytest.mark.subproc(timeout=5)
@pytest.mark.timeout(3) # ← effective timeout (shorter wins)
def test_obey_the_shorter():
...
How It Works
-
Interception —
pytest_runtest_protocol(tryfirst) takes over the protocol forsubproc-marked tests. -
Isolated run — The main process spawns a child that runs
pytest.main([nodeid, --rootdir, ...])for that single test. The full lifecycle (conftest loading, fixture resolution, setup, call, teardown) happens inside the subprocess. -
Timeout & cleanup — The child is created with
start_new_sessionso the entire process group (including test‑spawned children) is killed when the timeout fires. -
Result — A plugin inside the subprocess captures the outcome and exception; these are pickled to a temp file. The parent re‑raises the exception so
xfail,skip, etc. work as expected. -
Config parity —
asyncio_mode,xfail_strictand other ini settings are forwarded to the subprocess via--override-ini.
Comparison with pytest-forked
pytest-subproc |
pytest-forked |
|
|---|---|---|
| Mechanism | Spawns a new Python process (subprocess) |
Forks the existing process (os.fork) |
| Fixtures | Re‑evaluated in the child (full conftest + fixture resolution) | Inherited from parent via copy‑on‑write |
| Windows | ✅ Supported | ❌ Not available |
| Crash isolation | Full — the child has its own PID and memory space; a segfault cannot reach the parent | Partial — forked process shares file descriptors and some kernel state with the parent |
| Timeout | Built‑in timeout parameter on the marker, with process‑tree termination |
Only via external pytest-timeout plugin |
| Process‑tree cleanup | Kills the entire process group on timeout (os.killpg / taskkill /T) |
No automatic child‑process cleanup |
| Startup cost | Moderate — a new Python interpreter starts and runs pytest.main for one test |
Low — fork is (mostly) copy‑on‑write |
pytest-xdist |
✅ Compatible | ✅ Compatible |
Feature Compatibility
| Feature | Status |
|---|---|
| Python >= 3.7 | ✅ |
| Function, session, and module-level fixtures | ✅ |
| stdout/stderr captured and shown on failure | ✅ |
pytest.mark.asyncio + asyncio_mode = "auto" |
✅ |
@pytest.mark.parametrize |
✅ |
@pytest.mark.xfail(raises=...) |
✅ |
| Custom exception pickling / re-raising | ✅ |
flaky retries |
✅ |
pytest-timeout signal handling |
✅ |
pytest-cov coverage passthrough |
✅ |
Requirements
- Python >= 3.7
- pytest >= 7.0
License
MIT
Release files for pytest-subproc 0.1.2
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| pytest_subproc-0.1.2.tar.gz | 17.8 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| pytest_subproc-0.1.2-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 27.3 kB
Release files / pytest_subproc-0.1.2.tar.gz
| Download URL | pytest_subproc-0.1.2.tar.gz |
|---|---|
| Size | 17.8 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
60f266edbd380b1e7090ce8790dcc3c983b2d56e9cffbc00dbcc2e076a3fac0f
|
|
BLAKE2b-256 checksum How to use checksums |
50f61249e990751a2f4b36faf5923aa2dbfa669ae5d96b113fb276152a77fee3
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.13.14
|
Release files / pytest_subproc-0.1.2-py3-none-any.whl
| Download URL | pytest_subproc-0.1.2-py3-none-any.whl |
|---|---|
| Size | 9.5 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
c0bc7212cebac5325f3a70276177a7a1f4a2fa5d23ff8c5bdb452e5ab4b60403
|
|
BLAKE2b-256 checksum How to use checksums |
baa264319d4441f21bde7c5299dba36a596fb7212f7d64f6ce6f8180e96ad5c7
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.13.14
|