Skip to main content

pytest-poolwatch

See whether your pytest concurrency pool is actually full.

PyPI Python pytest CI License: MIT Status: Alpha

Concurrency profiling and scheduler-underfill diagnostics for pytest.

Documentation · Examples · JSON schema · Issues

pytest-poolwatch reconstructs when tests actually overlapped and shows whether configured execution slots stayed busy while runnable tests were still queued. It observes public pytest reports; it does not replace the scheduler, runner, or fixture lifecycle.

Why PoolWatch?

Ordinary duration reports tell you which tests were slow. PoolWatch answers a different question: did the scheduler keep the available concurrency busy?

  • Measure peak and time-weighted average concurrency.
  • Separate scheduler underfill from normal end-of-suite drain.
  • Quantify utilization and idle slot-seconds.
  • Inspect active and queued test timelines.
  • Export versioned JSON and a self-contained HTML report.
  • Work without a server, database, agent, or private pytest API.

Installation

python -m pip install pytest-poolwatch

With uv:

uv add --dev pytest-poolwatch

Installing the package is enough for pytest to discover its pytest11 plugin entry point.

Quick start

pytest --poolwatch --poolwatch-target=40

PoolWatch adds a summary after the normal pytest result:

============================= PoolWatch summary =============================
Configured concurrency:                 40
Target source:                command_line
Peak active tests:                      40
Average active tests:                 32.60
Concurrency utilization:             81.5%
Scheduler underfill:                17m 23s
Peak queued tests:                     126
Idle slot-seconds:                 4,912.00

Generate machine-readable and visual reports at the same time:

pytest --poolwatch \
  --poolwatch-target=40 \
  --poolwatch-json=.poolwatch/run.json \
  --poolwatch-html=.poolwatch/run.html

The HTML report is a portable, read-only notebook containing concurrency and queue charts, underfill windows, pytest phase totals, and the slowest attempts.

The diagnostic that matters

PoolWatch does not label every idle slot as a scheduling problem:

State Queue Active tests Diagnosis
Work is waiting and capacity is unused > 0 < target Scheduler underfill
No work remains to start 0 < target Normal suite drain
Capacity is unknown any observed peak Baseline only; pass an explicit target for a definitive diagnosis

This distinction is what exposed the refill bug fixed by pytest-asyncio-cooperative PR #86: the controlled pre-fix workload measured 34.2% utilization and 0.885s of underfill, while the fixed scheduler measured 73.1% utilization with no material underfill. Exact timings depend on the machine; the behavioral assertions do not.

Configuration

Command-line values take precedence over pytest configuration:

[tool.pytest.ini_options]
poolwatch = true
poolwatch_target = 40
poolwatch_json = ".poolwatch/run.json"
poolwatch_html = ".poolwatch/run.html"
poolwatch_underfill_threshold = 0.1
CLI option Purpose
--poolwatch Enable profiling and the terminal summary.
--poolwatch-target=N Set the expected active-test capacity.
--poolwatch-json=PATH Write schema-versioned JSON and enable PoolWatch.
--poolwatch-html=PATH Write self-contained HTML and enable PoolWatch.
--poolwatch-underfill-threshold=SECONDS Ignore shorter underfill windows.

Exclude a deliberately unrepresentative test:

import pytest


@pytest.mark.poolwatch_ignore
def test_one_off_migration():
    ...

See the configuration guide and metrics reference for the full behavior.

Compatibility

Runner or plugin Status Capacity source
pytest Supported Serial capacity of 1
pytest-asyncio Supported Serial test protocols
pytest-xdist Supported Numeric workers, or observed workers for auto
pytest-asyncio-cooperative Supported on its own max_asyncio_tasks
pytest-asyncio-concurrent Conservative support Explicit target or observed peak
Custom scheduler Supported with configuration --poolwatch-target=N
pytest-xdist + pytest-asyncio-cooperative Unsupported upstream combination Both plugins replace overlapping runtest-loop behavior

When both schedulers are detected in an active run, PoolWatch prefers the xdist worker count rather than inventing a combined capacity. Merely installing xdist does not suppress cooperative-only capacity detection. Do not enable the two schedulers together; their execution hooks conflict independently of PoolWatch.

PoolWatch relies on public pytest hooks and report timestamps. Compatibility details and known limitations are documented in the compatibility guide.

Documentation

Development

git clone https://github.com/Butterski/pytest-poolwatch.git
cd pytest-poolwatch
uv sync --locked --dev
uv run ruff check .
uv run ruff format --check .
uv run ty check
uv run coverage run -m pytest
uv run coverage combine
uv run coverage report
uv build

The test matrix covers Python 3.11–3.14. Releases are built from GitHub Releases and published through PyPI Trusted Publishing.

Scope

Version 0.1 measures test overlap, configured capacity, queued work, utilization, and scheduler underfill. It does not currently measure event-loop lag or attribute blocking calls. poolwatch_blocking is a documentation marker reserved for that future work.

PoolWatch is distributed under the MIT License. Contributions and reproducible scheduler workloads are welcome.

Download files

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

Source Distribution

pytest_poolwatch-0.1.1.tar.gz (17.5 kB view details)

Uploaded Source

Built Distribution

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

pytest_poolwatch-0.1.1-py3-none-any.whl (20.8 kB view details)

Uploaded Python 3

File details

Details for the file pytest_poolwatch-0.1.1.tar.gz.

File metadata

  • Download URL: pytest_poolwatch-0.1.1.tar.gz
  • Upload date:
  • Size: 17.5 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for pytest_poolwatch-0.1.1.tar.gz
Algorithm Hash digest
SHA256 d589ef8801df3909359e915c8ee14e4136aa5881d61170955dfb6af1361bcd64
MD5 61dcfa97a2ea083cc0b2454b9a9af259
BLAKE2b-256 14a3fd435408dc3f0e9734a38f469810e4174b41c7cb4271f6d0eec3227732b4

See more details on using hashes here.

Provenance

The following attestation bundles were made for pytest_poolwatch-0.1.1.tar.gz:

Publisher: publish.yml on Butterski/pytest-poolwatch

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file pytest_poolwatch-0.1.1-py3-none-any.whl.

File metadata

File hashes

Hashes for pytest_poolwatch-0.1.1-py3-none-any.whl
Algorithm Hash digest
SHA256 e3a27ccfe178f63e7a9266f9e0736e8735fa37c3ab6626d30b12aaba40dcc0ff
MD5 d09762554c286e219e0ce28cb4cb6f6c
BLAKE2b-256 206bfa49930c5beb27e22c6d76248f87cc53b4e810fb9bc29910fe93bd025df0

See more details on using hashes here.

Provenance

The following attestation bundles were made for pytest_poolwatch-0.1.1-py3-none-any.whl:

Publisher: publish.yml on Butterski/pytest-poolwatch

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

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