A pytest plugin to run marked tests in a subprocess
Project description
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
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file pytest_subproc-0.1.1.tar.gz.
File metadata
- Download URL: pytest_subproc-0.1.1.tar.gz
- Upload date:
- Size: 18.1 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
02957475763e068c608efcb9a43f17a43e3da0bdce886e9158cc060394e68b20
|
|
| MD5 |
b9fb3a0d8b810699f05b89c0619c0b4c
|
|
| BLAKE2b-256 |
187a21dff1c7ed56d333e0ea18115c8fab9118b245c3a0ac9eb819182999e609
|
File details
Details for the file pytest_subproc-0.1.1-py3-none-any.whl.
File metadata
- Download URL: pytest_subproc-0.1.1-py3-none-any.whl
- Upload date:
- Size: 10.1 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
3cabeeef71ac0a53aa877855f69b3274583b3b5c40e6581af282d879efbf1d0a
|
|
| MD5 |
452cdd8c73228eabe425af718ebd4393
|
|
| BLAKE2b-256 |
5aa022c7ff57430443dd773fbc0d76875f726b7d4aa08527d78259b517003314
|