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.1.tar.gz (18.1 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.1-py3-none-any.whl (10.1 kB view details)

Uploaded Python 3

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

Hashes for pytest_subproc-0.1.1.tar.gz
Algorithm Hash digest
SHA256 02957475763e068c608efcb9a43f17a43e3da0bdce886e9158cc060394e68b20
MD5 b9fb3a0d8b810699f05b89c0619c0b4c
BLAKE2b-256 187a21dff1c7ed56d333e0ea18115c8fab9118b245c3a0ac9eb819182999e609

See more details on using hashes here.

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

Hashes for pytest_subproc-0.1.1-py3-none-any.whl
Algorithm Hash digest
SHA256 3cabeeef71ac0a53aa877855f69b3274583b3b5c40e6581af282d879efbf1d0a
MD5 452cdd8c73228eabe425af718ebd4393
BLAKE2b-256 5aa022c7ff57430443dd773fbc0d76875f726b7d4aa08527d78259b517003314

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