Skip to main content

pytest-resumable-stepmetrics

Structured step-level metadata, retry / attempt tracking, and resume-on-retry for pytest — plus a tiny extension system so you can attach your own domain records (and get JSON + terminal reporting for free).

pip install pytest-resumable-stepmetrics

Why

  • Track named steps inside a test with status, duration and captured logs.
  • Know which attempt a step (or record) belongs to when a test retries.
  • Resume past already-succeeded, idempotent steps on a retry — skip the expensive work instead of re-running everything from the top.
  • Attach your own structured records (a dataclass) and have them serialised to report.json and rendered as a terminal table automatically.

Quick start

def test_flow(steplog):
    with steplog("setup"):
        ...
    with steplog("do work"):
        ...

Run with a JSON report:

pytest --steplog-json

Retry & attempt tracking

Call steplog.reset_attempt() as the first statement of each attempt (e.g. inside a retry loop). run.retry_count and each step's attempt are tracked automatically:

from retry import retry  # any retry mechanism works

def test_with_retries(steplog):
    @retry(tries=3, delay=0)
    def run():
        steplog.reset_attempt()          # first line of every attempt
        with steplog("environment"):
            ...
        with steplog("flash"):
            ...                          # raise to trigger a retry
    run()

The steps table gains an Attempt column automatically when retries occur.

Resume-on-retry (opt-in, idempotent steps only)

steplog.resumable("name") records success and, on a later attempt, skips the body if it already passed. Guard the body with step.resumed:

with steplog.resumable("download artifact") as step:
    if not step.resumed:
        download()          # runs once; skipped on later attempts

⚠️ Only use resumable for pure / idempotent steps whose effects survive a retry (downloads, name resolution, hashing). Stateful steps (deploys, power cycles, connection setup) should use plain steplog(...) so they re-run.

Guard-free resume with steplog.run(...)

A with block always runs its body — so resumable needs the if not step.resumed: guard. If you'd rather skip the work automatically with no guard, pass the work as a callable to steplog.run(...); it simply isn't called when the step already passed:

def download():
    ...expensive work...

def test_flow(steplog):
    steplog.reset_attempt()
    steplog.run("download artifact", download)   # skipped entirely on retry

steplog.run returns whatever the callable returns (or None when skipped) and forwards any extra *args / **kwargs to it.

Custom records (the extension point)

Attach any dataclass with steplog.record(...). Register it with @steplog_record to name its report section and auto-stamp fields (like attempt) from the live context:

from dataclasses import dataclass
from pytest_resumable_stepmetrics import steplog_record

@steplog_record(key="deploy_actions", stamp=("attempt",))
@dataclass
class DeployAction:
    component: str
    action: str
    attempt: int = 1        # auto-filled from the current attempt

def test_deploy(steplog):
    steplog.reset_attempt()
    steplog.record(DeployAction(component="api", action="deployed"))

This produces a deploy_actions array in report.json and a terminal table — no extra wiring. A plain (unregistered) dataclass also works; it uses the snake_case class name as its key and auto-tabulates its fields.

Provide a custom renderer for full control:

@steplog_record(key="samples", render=lambda rows: my_table(rows))
@dataclass
class BenchSample:
    metric: str
    value: float

The steplog API

Call Purpose
steplog("name") Track a step (context manager).
steplog.resumable("name") Track a step that skips on retry once passed (guard with step.resumed).
steplog.run("name", func, *a, **kw) Track a callable step; skips calling func on retry (guard-free).
steplog.record(obj) Attach a custom dataclass record.
steplog.reset_attempt() Advance the attempt counter (call first each attempt).
steplog.context Mutable dict used to auto-stamp records.
steplog.collector The underlying StepLogCollector.

JSON report

--steplog-json writes one report.json per test under .steplog/ (override with --steplog-json-dir). It contains run, steps, and one array per registered record type.

License

MIT

Release files for pytest-resumable-stepmetrics 0.1.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for pytest-resumable-stepmetrics 0.1.0
File Size Uploaded
pytest_resumable_stepmetrics-0.1.0.tar.gz 14.1 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for pytest-resumable-stepmetrics 0.1.0
File Interpreter ABI Platform
pytest_resumable_stepmetrics-0.1.0-py3-none-any.whl Python 3 none any Details

Total release size: 29.7 kB

Release files / pytest_resumable_stepmetrics-0.1.0.tar.gz

Download URL pytest_resumable_stepmetrics-0.1.0.tar.gz
Size 14.1 kB
Tags Source
SHA-256 checksum
How to use checksums
2dd3706f4062bf5496245a7aa3c34e9062e316bf507787eb58f91ad362ec28e7
BLAKE2b-256 checksum
How to use checksums
952516072894d8963088adaab89ea6eeb8474b304d48a437c2f5234d0efc8555
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Aug 9, 2026.

Transparency log

Release files / pytest_resumable_stepmetrics-0.1.0-py3-none-any.whl

Download URL pytest_resumable_stepmetrics-0.1.0-py3-none-any.whl
Size 15.6 kB
Tags Python 3
SHA-256 checksum
How to use checksums
d6b7e5bc7cacdff27b2611053b5ff44368b39615037dc015f0ff4b77925da2b4
BLAKE2b-256 checksum
How to use checksums
1825c529e0204baaf9e280bfbfa02730b3084aa1603174498efdecf2d863230b
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Aug 9, 2026.

Transparency log

Release history Release notifications | RSS feed

0.1.5

2 release files

0.1.4

2 release files

0.1.3

2 release files

0.1.2

2 release files

0.1.1

2 release files

This release

0.1.0 This release

2 release 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