Skip to main content

Parallel test execution for free-threaded Python builds

Project description

pytest-threadpool

PyPI - Version PyPI - Python Version License GitHub CI codecov

Status: Beta · Parallel test execution for free-threaded Python builds (3.13t+).

Runs test bodies and function-scoped fixture setup concurrently in a thread pool while keeping shared fixtures (module/class/session scope) and teardown sequential.

Installation

pip install pytest-threadpool

Quick start

pytest --threadpool auto

Mark tests for parallel execution:

from pytest_threadpool import parallelizable, not_parallelizable

import pytest

@parallelizable("children")     # all nested tests run in parallel
class TestMyFeature:
    def test_a(self): ...
    def test_b(self): ...

@parallelizable("parameters")   # parametrized variants run in parallel
@pytest.mark.parametrize("x", [1, 2, 3])
def test_with_params(x): ...

@parallelizable("all")          # children + parameters combined
class TestEverything:
    @pytest.mark.parametrize("n", [1, 2])
    def test_param(self, n): ...
    def test_plain(self): ...

@not_parallelizable             # opt out of inherited parallelism
def test_must_be_sequential(): ...

Scopes

Scope Effect
children All nested tests run concurrently (children, grandchildren, etc.)
parameters Parametrized variants of the same test run concurrently
all Combines children + parameters

Fixture handling

Function-scoped fixtures are cloned per-item and set up in parallel alongside test calls. Each worker gets independent fixture instances — no shared mutable state between concurrent fixture setups.

Shared fixtures (module, class, and session scope) are resolved once sequentially before workers launch and served from cache to all items.

Scope Behavior
function Cloned per-item, set up in parallel workers
class Resolved once, cached, shared across items
module Resolved once, cached, shared across items
session Resolved once, cached, shared across items

Teardown for all scopes runs sequentially after the parallel group completes.

Marker levels

Markers can be applied at function, class, module (pytestmark), or package (__init__.py pytestmark) level. Priority (most specific wins):

not_parallelizable > own marker > class > module > package

Shared state between tests

Unlike pytest-xdist, which uses subprocesses and requires all test data to be pickleable, pytest-threadpool runs tests in threads within a single process. This means tests can share common non-pickleable, thread-safe objects — both within a parallel group and across sequential groups:

import threading
import pytest


class SharedState:
    lock = threading.Lock()          # not pickleable
    event = threading.Event()        # not pickleable
    results = {}


@pytest.mark.parallelizable("children")
class TestGroupA:
    def test_a1(self):
        with SharedState.lock:
            SharedState.results["a1"] = True

    def test_a2(self):
        with SharedState.lock:
            SharedState.results["a2"] = True


@pytest.mark.parallelizable("children")
class TestGroupB:
    def test_b1(self):
        SharedState.event.set()
        with SharedState.lock:
            SharedState.results["b1"] = True

    def test_b2(self):
        assert SharedState.event.wait(timeout=10)
        with SharedState.lock:
            SharedState.results["b2"] = True

Objects like threading.Lock, threading.Event, logging.Logger, database connections, and other non-pickleable resources can live as class attributes and be accessed freely from any test — parallel or sequential — without serialization overhead or workarounds.

Usage

# Auto-detect thread count
pytest --threadpool auto

# Fixed thread count
pytest --threadpool 8

# Normal sequential run (no flag)
pytest

Tested versions

Component Versions
Python 3.13t, 3.14t, 3.15t
pytest >=9.0.2

Known limitations

  • Private pytest API usage — The plugin relies on internal _pytest APIs (fixture finalizers, setup state, terminal writer) that have no public equivalents. These may break across pytest releases without warning.
  • No plugin compatibility guarantees — Interactions with other pytest plugins (e.g. pytest-xdist, pytest-timeout, pytest-randomly) are untested and may conflict.
  • No captured stdout in parallel — Standard output from tests running concurrently is written directly to the terminal. Pytest's built-in capture (capsys/capfd) is not supported during parallel execution.

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_threadpool-0.3.0.tar.gz (55.4 kB view details)

Uploaded Source

Built Distribution

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

pytest_threadpool-0.3.0-py3-none-any.whl (21.5 kB view details)

Uploaded Python 3

File details

Details for the file pytest_threadpool-0.3.0.tar.gz.

File metadata

  • Download URL: pytest_threadpool-0.3.0.tar.gz
  • Upload date:
  • Size: 55.4 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.10.6 {"installer":{"name":"uv","version":"0.10.6","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Fedora Linux","version":"43","id":"","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

File hashes

Hashes for pytest_threadpool-0.3.0.tar.gz
Algorithm Hash digest
SHA256 73b8f66d24b9077a8cb05c1edc11202ad231b1e4d6ff6f8c7ddd34a12c35932c
MD5 b4485755e822db744d07ca5e29193dd3
BLAKE2b-256 9760d7c5d92596c4744d18aea498057d20ee9c1ef85410d34a3475fd6fdfb6dd

See more details on using hashes here.

File details

Details for the file pytest_threadpool-0.3.0-py3-none-any.whl.

File metadata

  • Download URL: pytest_threadpool-0.3.0-py3-none-any.whl
  • Upload date:
  • Size: 21.5 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.10.6 {"installer":{"name":"uv","version":"0.10.6","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Fedora Linux","version":"43","id":"","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

File hashes

Hashes for pytest_threadpool-0.3.0-py3-none-any.whl
Algorithm Hash digest
SHA256 1135fd0cd5dcd89ea42d7f6dcd49b7512839b832dc787eff8a5f9e9981308227
MD5 0c2cdc244c59aed991e4c48d742e603a
BLAKE2b-256 c626e2525cbbdf974462ed179745b7091224c8defa0dc79fee52807a5134a845

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