💡 pytest-fail2skip
A pytest plugin that reports failures of marked tests as skipped, so known failures don't block your CI gate.
The failure stays visible by default: conversion only happens when you opt in with --fail2skip.
📦 Installation
uv add pytest-fail2skip
🚀 Features
- ✅ Opt-in conversion — failures are converted only when
--fail2skipis passed; locally you still see the real failure - ✅ Targeted with
raises=— convert only the expected exception; a new, different failure still fails the run - ✅ Reason in the output — shown in the
-rssummary and exposed as thefail2skip_reasonuser property (JUnit XML, custom reporters) - ✅ Strict-markers safe — the marker is always registered, with or without the flag
- ✅ Standalone — changes only pytest's own report; works with any reporting plugin
🛠️ How to Use
- Mark the known-failing test:
import pytest @pytest.mark.fail2skip(reason="BUG-123: flaky upstream service", raises=ConnectionError) def test_sync_with_upstream(): ...
- Run normally to see the real result:
pytest - In CI, opt in to the conversion:
pytest --fail2skip
⚙️ Marker Reference
@pytest.mark.fail2skip(reason=None, raises=None)
| Argument | Description |
|---|---|
reason |
Shown as fail2skip: <reason>. Can also be passed positionally. Defaults to no reason given. |
raises |
An exception type or tuple of types. When set, only these failures are converted. |
Behavior when --fail2skip is set:
| Test result | Reported as |
|---|---|
Fails in the test body (and matches raises, if given) |
skipped |
Fails with an exception not in raises |
failed |
| Passes | passed |
| Errors in setup/teardown (e.g. a broken fixture) | error — not converted |
The marker can also be applied to a class, or to a whole module with pytestmark.
🤔 fail2skip vs. xfail
@pytest.mark.xfail is close, but differs in two ways that matter for a release gate:
- Default direction —
xfailhides the failure unless you pass--runxfail.fail2skipshows it unless you pass--fail2skip, so forgetting the flag fails loudly instead of silently. - Passing tests — a passing
xfailtest is reported as XPASS; a passingfail2skiptest is simply passed.
If neither matters to you, xfail(raises=..., strict=False) is the built-in alternative.
▶️ Usage Examples
Convert only expected exceptions
Pass a single exception type:
import pytest
@pytest.mark.fail2skip(reason="BUG-123: upstream service is unstable", raises=ConnectionError)
def test_sync_with_upstream():
...
Or a tuple, to convert any of several known failures:
import pytest
@pytest.mark.fail2skip(
reason="BUG-456: device sometimes drops the session",
raises=(ConnectionError, TimeoutError),
)
def test_device_handshake():
...
Subclasses match too (ConnectionResetError is converted by raises=ConnectionError).
Any other failure, such as an AssertionError from a real regression, still fails the run.
Enable in pytest.ini for CI
[pytest]
addopts =
--fail2skip
Read the reason from a custom plugin
def pytest_runtest_logreport(report):
reason = dict(report.user_properties).get("fail2skip_reason")
if report.skipped and reason:
print(f"{report.nodeid} was converted: {reason}")
🔁 Migrating from pytest-plugins
This plugin was previously the fail2skip feature of pytest-plugins. Both register the --fail2skip option,
so don't install them together until you're on a pytest-plugins release without fail2skip.
Changes when migrating:
- Converted tests are now reported as
skippedinstead ofxfailed. - The pytest-plugins report records them as
skipped(notfailed-skipped); the reason is available viauser_properties.
🤝 Contributing
- Fork the repository
- Create a feature branch:
git checkout -b feature/my-addition - Make your changes
- Commit your changes:
git commit -m 'Add my addition' - Push to the branch:
git push origin feature/my-addition - Open a Pull Request
Additions that promote clean, productive, and maintainable development are always welcome.
📄 License
MIT License — see LICENSE for details.
🙏 Thanks
Thanks for exploring this repository!
Happy coding!
Metadata
Release files for pytest-fail2skip 1.0.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | Actions |
|---|---|---|---|---|
| pytest_fail2skip-1.0.0.tar.gz | 6.0 kB | Without provenance | Details for pytest_fail2skip-1.0.0.tar.gz |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| pytest_fail2skip-1.0.0-py3-none-any.whl | Python 3 | none | any | Details for pytest_fail2skip-1.0.0-py3-none-any.whl |
Total release size: 12.4 kB
Release files / pytest_fail2skip-1.0.0.tar.gz
| Download URL | pytest_fail2skip-1.0.0.tar.gz |
|---|---|
| Size | 6.0 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
3245699940cd6db49283d5cff42146eb6d6a7359c04a477b34f9286c14fe0ee6
|
|
BLAKE2b-256 checksum How to use checksums |
d421fbac56a27d4bcdb33f45bec8afbd91e435f2d4d25593b6186874c549be2d
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.13.16
|
Release files / pytest_fail2skip-1.0.0-py3-none-any.whl
| Download URL | pytest_fail2skip-1.0.0-py3-none-any.whl |
|---|---|
| Size | 6.4 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
07998a361631c3d5f7b2b28d2c950d0ad48f2ceae0b169417815d12cb75c7434
|
|
BLAKE2b-256 checksum How to use checksums |
f3e903748a93b5d5cbbbcfc09f9feb7fbcbeb9b60f9fcd2ec62d4001eaca3729
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.13.16
|
Security for pytest-fail2skip 1.0.0
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.
No provenance found
None of the files in this release were uploaded with provenance metadata.
A note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.