Skip to main content

Run marked pytest tests in grouped subprocesses (cross-platform).

Project description

pytest-isolated

Tests PyPI

A cross-platform pytest plugin that runs marked tests in isolated subprocesses with intelligent grouping.

Features

  • Run tests in fresh Python subprocesses to prevent state pollution
  • Group related tests to run together in the same subprocess
  • Handles crashes, timeouts, and setup/teardown failures
  • Respects pytest's standard output capture settings (-s, --capture)
  • Works with pytest reporters (JUnit XML, etc.)
  • Configurable timeouts to prevent hanging subprocesses
  • Cross-platform: Linux, macOS, Windows

Cheatsheet for pytest-forked users

This plugin is inspired by pytest-forked. See pytest-forked migration guide for a quick reference comparing features.

Installation

pip install pytest-isolated

Quick Start

Mark tests to run in isolated subprocesses:

import pytest

@pytest.mark.isolated
def test_isolated():
    # Runs in a fresh subprocess
    assert True

Tests with the same group run together in one subprocess:

# Using keyword argument
@pytest.mark.isolated(group="mygroup")
def test_one():
    shared_state.append(1)

@pytest.mark.isolated(group="mygroup")
def test_two():
    # Sees state from test_one
    assert len(shared_state) == 2

# Or using positional argument
@pytest.mark.isolated("mygroup")
def test_three():
    shared_state.append(3)

Set timeout per test group:

@pytest.mark.isolated(timeout=30)
def test_with_timeout():
    # This group gets 30 second timeout (overrides global setting)
    expensive_operation()

Note: Tests without an explicit group parameter each run in their own unique subprocess for maximum isolation.

Class and Module Markers

Apply to entire classes to share state between methods:

@pytest.mark.isolated
class TestDatabase:
    def test_setup(self):
        self.db = create_database()

    def test_query(self):
        # Shares state with test_setup
        result = self.db.query("SELECT 1")
        assert result

Apply to entire modules using pytestmark:

import pytest

pytestmark = pytest.mark.isolated

def test_one():
    # Runs in isolated subprocess
    pass

def test_two():
    # Shares subprocess with test_one
    pass

Configuration

Command Line

# Run all tests in isolation (even without @pytest.mark.isolated)
pytest --isolated

# Set isolated test timeout (seconds)
pytest --isolated-timeout=60

# Disable subprocess isolation for debugging
pytest --no-isolation

# Control output capture (standard pytest flags work with isolated tests)
pytest -s                    # Disable capture, show all output
pytest --capture=sys         # Capture at sys.stdout/stderr level

# Combine with pytest debugger
pytest --no-isolation --pdb

pytest.ini / pyproject.toml

[pytest]
isolated_timeout = 300

Or in pyproject.toml:

[tool.pytest.ini_options]
isolated_timeout = "300"

Use Cases

Testing Global State

@pytest.mark.isolated
def test_modifies_environ():
    import os
    os.environ["MY_VAR"] = "value"
    # Won't affect other tests

@pytest.mark.isolated
def test_clean_environ():
    import os
    assert "MY_VAR" not in os.environ

Testing Singletons

@pytest.mark.isolated(group="singleton_tests")
def test_singleton_init():
    from myapp import DatabaseConnection
    db = DatabaseConnection.get_instance()
    assert db is not None

@pytest.mark.isolated(group="singleton_tests")
def test_singleton_reuse():
    db = DatabaseConnection.get_instance()
    # Same instance as previous test in group

Testing Process Resources

@pytest.mark.isolated
def test_signal_handlers():
    import signal
    signal.signal(signal.SIGTERM, custom_handler)
    # Won't interfere with pytest

Output and Reporting

Failed tests automatically capture and display stdout/stderr:

@pytest.mark.isolated
def test_failing():
    print("Debug info")
    assert False

Works with standard pytest reporters:

pytest --junitxml=report.xml --durations=10

Limitations

Fixtures: Module/session fixtures run in each subprocess group. Cannot share fixture objects between parent and subprocess.

Debugging: Use --no-isolation to run all tests in the main process for easier debugging with pdb or IDE debuggers.

Performance: Subprocess creation adds ~100-500ms per group. Group related tests to minimize overhead.

Advanced

Coverage Integration

To collect coverage from isolated tests, enable subprocess tracking in pyproject.toml:

[tool.coverage.run]
parallel = true
concurrency = ["subprocess"]

See the coverage.py subprocess documentation for details.

Timeout Handling

pytest --isolated-timeout=30

Timeout errors are clearly reported with the group name and timeout duration.

Crash Detection

If a subprocess crashes, tests in that group are marked as failed with exit code information.

Subprocess Detection

import os

if os.environ.get("PYTEST_RUNNING_IN_SUBPROCESS") == "1":
    # Running in subprocess
    pass

Troubleshooting

Tests timing out: Increase timeout with --isolated-timeout=600

Missing output: Use -s or --capture=no to see output from passing tests, or -v for verbose output. pytest-isolated respects pytest's standard capture settings.

Subprocess crashes: Check for segfaults, OOM, or signal issues. Run with -v for details.

Contributing

  1. Install pre-commit: pip install pre-commit && pre-commit install
  2. Run tests: pytest tests/ -v
  3. Open an issue before submitting PRs for new features

License

MIT License - see LICENSE file for details.

Project details


Download files

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

Source Distributions

No source distribution files available for this release.See tutorial on generating distribution archives.

Built Distribution

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

pytest_isolated-0.4.1-py3-none-any.whl (15.2 kB view details)

Uploaded Python 3

File details

Details for the file pytest_isolated-0.4.1-py3-none-any.whl.

File metadata

File hashes

Hashes for pytest_isolated-0.4.1-py3-none-any.whl
Algorithm Hash digest
SHA256 39fd6eac70b7f0a42796d3c302f85397d2007de44a4f3c7c047c8c23b91e2206
MD5 f8bc1dd5f15eaefd765d874ca61e01c2
BLAKE2b-256 04a829ab80c9829d24fe01c3b89c8f3c577f39b25eda9e93fc73eefd24190c1c

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