Skip to main content

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

  1. Interceptionpytest_runtest_protocol (tryfirst) takes over the protocol for subproc-marked tests.

  2. 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.

  3. Timeout & cleanup — The child is created with start_new_session so the entire process group (including test‑spawned children) is killed when the timeout fires.

  4. 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.

  5. Config parityasyncio_mode, xfail_strict and 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


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

pytest_subproc-0.1.2.tar.gz (17.8 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

pytest_subproc-0.1.2-py3-none-any.whl (9.5 kB view details)

Uploaded Python 3

File details

Details for the file pytest_subproc-0.1.2.tar.gz.

File metadata

  • Download URL: pytest_subproc-0.1.2.tar.gz
  • Upload date:
  • Size: 17.8 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.13.14

File hashes

Hashes for pytest_subproc-0.1.2.tar.gz
Algorithm Hash digest
SHA256 60f266edbd380b1e7090ce8790dcc3c983b2d56e9cffbc00dbcc2e076a3fac0f
MD5 6c2b362a3d0d0e724779e286a5b8b62f
BLAKE2b-256 50f61249e990751a2f4b36faf5923aa2dbfa669ae5d96b113fb276152a77fee3

See more details on using hashes here.

File details

Details for the file pytest_subproc-0.1.2-py3-none-any.whl.

File metadata

  • Download URL: pytest_subproc-0.1.2-py3-none-any.whl
  • Upload date:
  • Size: 9.5 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.13.14

File hashes

Hashes for pytest_subproc-0.1.2-py3-none-any.whl
Algorithm Hash digest
SHA256 c0bc7212cebac5325f3a70276177a7a1f4a2fa5d23ff8c5bdb452e5ab4b60403
MD5 2f3a9b6ac31625ec10b4198a22dbe826
BLAKE2b-256 baa264319d4441f21bde7c5299dba36a596fb7212f7d64f6ce6f8180e96ad5c7

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page