Skip to main content

pytest-timing

Record when every test ran, on which pytest-xdist worker, and for how long, then render the run as a timing report in the spirit of cargo build --timings: an ASCII Gantt chart in the terminal and a self-contained HTML report with worker lanes, a concurrency graph and a sortable table.

pytest -n 4 --timing --timing-html
================================ timing report =================================
pytest-timing: 129 tests (1 error, 1 failed), 4 workers, wall 1.37s, busy 95.4%
worker |-----------|-----------|------------|-----------|-----------|--------  busy%
gw0    ░░░░░░   ▄███████████████▄███████████████████████████████████████████    93.6%
gw1    ░░░░░░░  ▄██████████████████████████████████████████████████X            94.1%
gw2    ░░░░░░░░▒▄███████████████████████████████████████████████████▄           96.2%
gw3    ░░░░░░░░░▄████████████████████████████████▄███████████XXXXX             98.2%
       0s          0.20s       0.40s        0.60s       0.80s       1.00s 1.37s
       legend: ░ boot  ▒ collect  █ tests  ▄ <50% busy  X failure

slowest 3 tests (setup ░ / call █ / teardown ▒):
gw0                                                    ████████████████████▒    0.36s
       test_big.py::test_slow[5]
gw2                                               ████████████████▒            0.30s
       test_big.py::test_slow[4]
gw1               ░░░░░░░░░░░░░▒                                               0.21s
       test_big.py::test_many[8]
HTML report written to pytest-timing.html

Works with and without -n. Without xdist the run is a single main lane.

Each lane shows worker boot, collection, tests ( marks a column that is under half busy, so idle gaps stand out) and failures. The slowest tests are drawn on the same axis with their setup / call / teardown split; note how the module-scoped fixture above lands in the setup phase of the first test on each worker.

The HTML report has the same data with a zoomable lane Gantt, a concurrency graph, filters, hover details, and a sortable table:

HTML report

Install

pip install pytest-timing            # plugin only
pip install "pytest-timing[xdist]"   # with pytest-xdist

Python 3.10+, pytest 7.3+ (the version that added wall-clock start/stop to test reports).

Usage

Option Effect
--timing Record timings and print the ASCII chart in the terminal summary.
--timing-html Write a self-contained HTML report to pytest-timing.html.
--timing-json Write the recorded run to pytest-timing.json.
--timing-trace Write a Chrome trace file for Perfetto to pytest-timing.trace.json.
--timing-html-file PATH, --timing-json-file PATH, --timing-trace-file PATH Same, to an explicit path.
--timing-top=N Rows in the slowest-tests section (default 10, 0 hides it).
--timing-min=SECONDS Hide tests shorter than this from the slowest-tests section.
--timing-ascii-style=unicode|ascii Chart glyphs.
--timing-width=N Override the terminal width for the chart.

Any output option implies --timing. The output flags are plain booleans and the -file options always take a path, so pytest --timing-json test_x.py runs exactly test_x.py.

The same settings are accepted as ini keys (timing, timing_html, timing_json, timing_trace as paths or true, plus timing_top, timing_min, timing_ascii_style) and environment variables (PYTEST_TIMING=1, PYTEST_TIMING_HTML=path, ...), so CI can enable it without touching the command line.

Every JSON run records how the session ended (finished, collect_only, interrupted, aborted, internal_error) with the reason pytest gave, and complete is derived from that.

View the trace

--timing-trace writes a Chrome Trace Event file. Open it in Perfetto UI with "Open trace file", or in chrome://tracing. Each worker is a track, every test is a bar, and the setup / call / teardown phases nest underneath it. Zoom with W/A/S/D and select a range to aggregate durations.

Re-render or merge saved runs

pytest-timing render pytest-timing.json --html report.html --ascii
pytest-timing merge shard1.json shard2.json -o all.json

merge places several runs (for example CI shards) on one shared time axis using their absolute start times.

How it works

Since pytest 7.3 every TestReport carries wall-clock start and stop timestamps. xdist serialises those to the controller unchanged and attaches the worker to the report, so the plugin only needs controller-side hooks: pytest_runtest_logreport for the setup / call / teardown phases, plus xdist's node-ready, collection-finished and node-down hooks for the worker lifecycle. Nothing runs inside the workers and nothing touches the execnet channel.

Each lane in the report shows boot (worker start-up until it is ready), collection, tests, idle gaps and the point the worker shut down. Session-scoped fixture setup is attributed to the first test's setup phase and its teardown to the last test's teardown phase, exactly as pytest reports it.

Overhead

Nothing runs inside the workers: the plugin only listens to the reports xdist already sends to the controller, and each of the three phase reports per test costs a few microseconds of bookkeeping. Rendering happens once, at the end of the session.

Measured on 5,000 trivial tests (a worst case, since the per-test cost is fixed while the tests themselves take almost nothing), best of five runs:

Configuration Wall time Overhead
single process, plugin disabled 1.29 s
single process, --timing 1.38 s +0.09 s
single process, --timing plus JSON, HTML and trace files 1.45 s +0.16 s
-n 4, plugin disabled 1.18 s
-n 4, --timing 1.25 s +0.07 s
-n 4, --timing plus all three files 1.32 s +0.14 s

That is under 20 microseconds per test for recording, plus a fixed serialisation cost per output file of roughly 10 ms per thousand tests. Output size is about 250 bytes per test for the JSON and HTML files and 600 bytes for the trace. Memory held during the run is on the same order as the JSON. The plugin registers nothing at all unless one of its options is enabled.

License

MIT

Download files

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

Source Distribution

pytest_timing-0.1.0.tar.gz (213.8 kB view details)

Uploaded Source

Built Distribution

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

pytest_timing-0.1.0-py3-none-any.whl (40.4 kB view details)

Uploaded Python 3

File details

Details for the file pytest_timing-0.1.0.tar.gz.

File metadata

  • Download URL: pytest_timing-0.1.0.tar.gz
  • Upload date:
  • Size: 213.8 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: uv/0.12.12 {"installer":{"name":"uv","version":"0.12.12","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

File hashes

Hashes for pytest_timing-0.1.0.tar.gz
Algorithm Hash digest
SHA256 ed13d5083bb0c6983aaae4595e65456956d286667e361abb7968a0e17e8c2c9c
MD5 e9f8e9edf224ea6faa0c76e5e42a5628
BLAKE2b-256 96b7840244068609632ebe6cd7bba236373a69d6b9063670ef50a1898e1a29ce

See more details on using hashes here.

File details

Details for the file pytest_timing-0.1.0-py3-none-any.whl.

File metadata

  • Download URL: pytest_timing-0.1.0-py3-none-any.whl
  • Upload date:
  • Size: 40.4 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: uv/0.12.12 {"installer":{"name":"uv","version":"0.12.12","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

File hashes

Hashes for pytest_timing-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 bf14e87f15a8b9713834b3b36a444c29f63b5f28d9ff08dd4bb2aced8e4314a9
MD5 d7f6db141ef651dad156eeab75221811
BLAKE2b-256 9362783348b6a6dd6f1eed758140f2dcb314e3382296ff17349dfb01ac87159e

See more details on using hashes here.

Release history Release notifications | RSS feed

0.2.0

2 files

This release

0.1.0 This release

2 files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page