Run marked pytest tests in grouped subprocesses (cross-platform).
Project description
pytest-isolated
A cross-platform pytest plugin that runs marked tests in isolated subprocesses with intelligent grouping.
Why pytest-isolated?
Ever had a test that passes alone but fails in the suite? Or tests that mysteriously hang on CI? pytest-isolated solves this by running tests in completely isolated subprocesses.
Common problems it solves:
- ✅ Segfaults and crashes don't kill your entire test suite
- ✅ Tests modifying global state don't affect each other
- ✅ Hanging tests timeout without blocking other tests
- ✅ C extension crashes are contained
- ✅ Resource leaks are isolated per test
Cheatsheet for pytest-forked users
This plugin is inspired by pytest-forked. See pytest-forked migration guide for a quick reference comparing features.
Quick Start
1. Install:
pip install pytest-isolated
2. Mark a flaky test:
# Example 1: Simple isolation
@pytest.mark.isolated
def test_with_clean_state():
import os
os.environ["DEBUG"] = "true"
# Other tests won't see this change
# Example 2: Crash protection
@pytest.mark.isolated
def test_that_crashes():
import ctypes
ctypes.string_at(0) # Crash is contained!
3. Run pytest normally:
pytest # Crash is isolated, suite continues
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
Basic Usage
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"
Real-World Use Cases
🌍 Isolating Global State
Prevent environment variable and configuration changes from leaking between tests:
@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 # Fresh environment
🔄 Testing Singletons
Group tests that share singleton state, or isolate them completely:
@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
Safely modify signal handlers and other process-level settings:
@pytest.mark.isolated
def test_signal_handlers():
import signal
signal.signal(signal.SIGTERM, custom_handler)
# Won't interfere with pytest or other tests
💾 Testing Database Migrations
Group expensive operations while maintaining isolation from other tests:
@pytest.mark.isolated(group="database")
class TestDatabase:
def test_migration(self):
db.migrate() # Expensive operation, runs once
def test_query(self):
# Reuses same DB from test_migration
result = db.query("SELECT 1")
assert result == [(1,)]
🔧 Testing C Extensions
Isolate tests that might crash from C code:
@pytest.mark.isolated
def test_numpy_operation():
import numpy as np
# If this segfaults, other tests still run
result = np.array([1, 2, 3])
assert len(result) == 3
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. Only mark tests that need isolation.
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
- Install pre-commit:
pip install pre-commit && pre-commit install - Run tests:
pytest tests/ -v - Open an issue before submitting PRs for new features
License
MIT License - see LICENSE file for details.
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 Distributions
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_isolated-0.4.3-py3-none-any.whl.
File metadata
- Download URL: pytest_isolated-0.4.3-py3-none-any.whl
- Upload date:
- Size: 17.1 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.11.13
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
0b2390b2bea049c88732ce286eeb66a2f1b812241a4bc20ccb45b740cc9d5fec
|
|
| MD5 |
a6ec4f6f58337bf9f74f1b8b4c54ba6c
|
|
| BLAKE2b-256 |
91fbfc8bce191bd9edb0b030a3a36e5172896a6c941dc1b60ab64e601682b082
|