Automatic retry for failed Behave scenarios — tag overrides, exception filtering, flakiness stats
Project description
behave-retry
Automatic retry for failed Behave scenarios — real re-execution, tag overrides, exception filtering, and flakiness stats.
Why?
Behave has no built-in retry. When a scenario fails due to flakiness (timing, network, race conditions), there's no way to re-run it automatically. Cucumber has --retry natively. Behave doesn't.
behave-retry fills that gap by patching Behave's Scenario.run to re-execute failed scenarios automatically — with tag overrides, exception filtering, and flakiness stats.
Comparison
| Feature | behave-retry | Cucumber --retry |
pytest-rerunfailures |
|---|---|---|---|
| Per-scenario retry override | @retry:N tag |
@retry N tag |
@pytest.mark.flaky(reruns=N) |
| Exception filtering | retry_on=[...] |
No | reruns_exceptions |
| Tag filtering | retry_tags=["@flaky"] |
No | No |
| Global retry budget | max_total_retries |
No | No |
| Exponential backoff | retry_delay + backoff_factor |
No | reruns_delay (fixed) |
| On-retry callback | on_retry |
No | No |
| Retry stats | Human + JSON | No | No |
| Scenario Outline support | Per-example keys | N/A | N/A |
| Runtime dependencies | Zero | — | pytest plugin |
Install
pip install behave-retry
Quick start
# environment.py
from behave_retry import setup_retry, after_scenario_hook, retry_report
def before_all(context):
setup_retry(context, max_retries=3)
def after_scenario(context, scenario):
after_scenario_hook(context, scenario)
def after_all(context):
print(retry_report(context))
That's it. Failed scenarios will now be re-executed up to 3 times automatically.
Features
- Global retry — retry all failed scenarios up to N times
- Tag-filtered retry — only retry scenarios with specific tags (
@flaky) - Exception-filtered retry — only retry on specific exception types or string names
- Per-scenario override —
@retry:Ntag overrides global config - Feature-level tags —
@retry:Non Feature inherits to scenarios - Global retry budget — limit total retries across all scenarios
- Retry delay and backoff — configurable delay with exponential backoff
- On-retry callback — custom logic before each retry (cleanup, screenshots, etc.)
- Flakiness stats — human-readable summary and machine-readable JSON export
- Scenario Outline support — unique keys per example, independent retry counts
- Logging — via standard
loggingmodule underbehave_retrylogger - Type-safe —
py.typedmarker included, full type hints, mypy clean
Configuration
setup_retry(
context,
max_retries=3, # max retries per scenario
retry_tags=["@flaky"], # only retry tagged scenarios
retry_on=[AssertionError, TimeoutError], # only retry these exceptions
retry_delay=2.0, # 2s delay before first retry
backoff_factor=2.0, # double delay each retry (2s, 4s, 8s)
on_retry=lambda ctx, sc, att, exc: print(f"Retry {sc.name} #{att}: {exc}"),
max_total_retries=20, # stop after 20 total retries across all scenarios
)
See the configuration guide for full details.
How it works
setup_retrypatchesbehave.model.Scenario.runwith a retry-aware wrapper.- When a scenario fails, the wrapper checks:
- Does the scenario have retries remaining? (global
max_retriesor@retry:Noverride) - Is the scenario tagged for retry? (if
retry_tagsis set) - Is the exception type eligible? (if
retry_onis set) - Is the global retry budget exhausted? (if
max_total_retriesis set)
- Does the scenario have retries remaining? (global
- If all checks pass, it resets the scenario state and re-runs it.
- Stats are tracked and available via
retry_report()orstats.to_dict().
Documentation
| Section | Description |
|---|---|
| Installation | Install from PyPI or source |
| Quick start | Three-step setup guide |
| Features | Complete feature walkthrough with examples |
| Configuration | All parameters, validation, and precedence rules |
| Examples | Real-world recipes for common use cases |
| API reference | Full autodoc API |
| Changelog | Version history |
Contributing
Contributions are welcome! See CONTRIBUTING.md for guidelines.
License
MIT — Copyright (c) 2026 Mathias Paulenko
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file behave_retry-1.8.2.tar.gz.
File metadata
- Download URL: behave_retry-1.8.2.tar.gz
- Upload date:
- Size: 44.9 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
8b86e95e6947ac761801c6b4e6bcbc57d66ca8da935fec788628a9ba5702fefd
|
|
| MD5 |
5fdd4fe1dd9dc8dd9062fc4223196591
|
|
| BLAKE2b-256 |
27390d944e80aacf817de3af58a1fbeb87b92cd58953d45aac20363c53b943a0
|
Provenance
The following attestation bundles were made for behave_retry-1.8.2.tar.gz:
Publisher:
release.yml on MathiasPaulenko/behave-retry
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
behave_retry-1.8.2.tar.gz -
Subject digest:
8b86e95e6947ac761801c6b4e6bcbc57d66ca8da935fec788628a9ba5702fefd - Sigstore transparency entry: 2164081201
- Sigstore integration time:
-
Permalink:
MathiasPaulenko/behave-retry@4a692726e5cf1e55506c101db5ab34ad73b14aee -
Branch / Tag:
refs/tags/v1.8.2 - Owner: https://github.com/MathiasPaulenko
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@4a692726e5cf1e55506c101db5ab34ad73b14aee -
Trigger Event:
push
-
Statement type:
File details
Details for the file behave_retry-1.8.2-py3-none-any.whl.
File metadata
- Download URL: behave_retry-1.8.2-py3-none-any.whl
- Upload date:
- Size: 13.3 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
a2c0ea85e89b16882752e03194c8ea2b345cada0f45135aa8b75ac60a0655008
|
|
| MD5 |
58998dbfb21316baebbea7c35529efa0
|
|
| BLAKE2b-256 |
beee3491bae7bd33ebc6dde1e3bac6ca3620aa4e79cf03a0306742c1bf518b27
|
Provenance
The following attestation bundles were made for behave_retry-1.8.2-py3-none-any.whl:
Publisher:
release.yml on MathiasPaulenko/behave-retry
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
behave_retry-1.8.2-py3-none-any.whl -
Subject digest:
a2c0ea85e89b16882752e03194c8ea2b345cada0f45135aa8b75ac60a0655008 - Sigstore transparency entry: 2164081398
- Sigstore integration time:
-
Permalink:
MathiasPaulenko/behave-retry@4a692726e5cf1e55506c101db5ab34ad73b14aee -
Branch / Tag:
refs/tags/v1.8.2 - Owner: https://github.com/MathiasPaulenko
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@4a692726e5cf1e55506c101db5ab34ad73b14aee -
Trigger Event:
push
-
Statement type: