-
Notifications
You must be signed in to change notification settings - Fork 0
Project Story
pytest-poolwatch began with a scheduler graph that looked wrong.
A cooperative pytest workload had a configured concurrency limit, runnable tests still waiting, and yet active concurrency repeatedly fell far below the limit. Ordinary pytest duration reports could show slow tests, but they could not answer whether the scheduler was keeping all execution slots occupied.
The first analysis reconstructed overlapping test intervals and plotted active tests over time. That made the failure mode visible:
configured capacity: high
queued work: still present
active tests: unexpectedly low
This was not normal end-of-suite drain because the queue was not empty.
Investigation found a refill loop in pytest-asyncio-cooperative that replaced
only one task after asyncio.wait(..., FIRST_COMPLETED) returned several
completed tasks together.
The minimal fix changed one-shot replacement into refill-until-full behavior. The accompanying regression test forced several slots to become free in one batch so the bug could not hide behind event-loop timing.
That work became pytest-asyncio-cooperative PR #86.
Once the specific bug was fixed upstream, building another scheduler would have solved the wrong problem. The reusable missing tool was observability:
Given pytest reports, configured capacity, and collected work, did the active scheduler remain work-conserving?
That became pytest-poolwatch.
The initial project brief proposed a broad concurrency diagnostic tool for ordinary pytest, pytest-asyncio, cooperative async scheduling, concurrent async groups, xdist, and custom schedulers. Its central distinction was:
queued > 0 and active < target → scheduler underfill
queued = 0 and active < target → natural drain
It also imagined later event-loop watchdogs, named async phases, blocking-work markers, and safe thread offload helpers.
Version 0.1 implements the evidence that can be reconstructed reliably from public pytest reports:
- start and finish collection;
- phase-aware intervals;
- configured or detected capacity;
- active and queued timelines;
- utilization and idle slot-seconds;
- scheduler-underfill windows;
- terminal, JSON, and HTML reports;
- deterministic before/after examples.
It deliberately does not claim event-loop starvation detection. An active async test might be progressing, waiting on an API, or blocking the loop; report timestamps alone cannot distinguish those states.
The project follows the complete diagnostic cycle:
observation → measurement → analysis → root cause → upstream fix → reusable tool
PoolWatch should continue in that spirit: add a diagnosis only when a stable, public signal can support it.
“PoolWatch” describes the narrow promise: observe the execution pool. It is not a runner, scheduler, fixture manager, or cloud service.
See Examples for the reproducible regression and Roadmap for future work.
pytest-poolwatch documentation · Source · PyPI · MIT License