pytest-fahhh-on-fail
A pytest plugin that plays FAHHH sound from meme whenever a test session ends in failure.
Installation
uv add --dev pytest-fahhh-on-fail
# or
pip install pytest-fahhh-on-fail
The plugin registers itself via the pytest11 entry point, so it is
auto-loaded on the next pytest run — no conftest.py changes required.
Usage
Just run pytest. If the session fails, the bundled sound.mp3 plays in the
background once the run finishes.
pytest
To try the sound immediately after cloning the repo:
uv sync
uv run pytest test_fail.py
The repo includes an intentionally-failing test_fail.py, so the run fails on
purpose and the plugin plays the bundled sound.mp3 as soon as it finishes.
Options
| Option | Description |
|---|---|
--on-fail-sound PATH |
Play a custom sound file on failure. |
--no-on-fail-sound |
Disable the failure sound. |
on_fail_sound = PATH (ini) |
Same, set in pyproject.toml or pytest.ini. |
no_on_fail_sound = true (ini) |
Disable the failure sound from config. |
pytest --on-fail-sound /path/to/beep.mp3
pytest --no-on-fail-sound
Configuration file
# pyproject.toml
[tool.pytest.ini_options]
on_fail_sound = "assets/beep.mp3"
# pytest.ini
[pytest]
on_fail_sound = assets/beep.mp3
You can also disable the sound from a config file:
[tool.pytest.ini_options]
no_on_fail_sound = true
How it works
The plugin implements pytest_sessionfinish and plays the sound only when
exitstatus == pytest.ExitCode.TESTS_FAILED (i.e. at least one test failed).
Sound playback uses playsound3 in
non-blocking mode, so it starts playing in the background right before the
session exits.
Audio problems never break the test run: if the configured file is missing or no audio backend is available, a warning is logged and pytest continues normally.
Development
uv sync
uv run pytest
The test suite uses pytest's pytester fixture to run fake sessions and verify
that the sound is triggered on failure, but not on success, missing files, or
disabling switches.
Supported Python versions
The plugin supports Python 3.10+. Run the full matrix locally with tox (powered
by tox-uv, so uv handles the virtualenvs and the lockfile):
uv run tox
CI runs the same matrix (Python 3.10–3.14) on every push to main.
Versioning
The version is dynamic: it comes from git tags, not from a version field
in pyproject.toml. Building exactly on a vX.Y.Z tag produces the clean
version X.Y.Z; building ahead of a tag produces a development version like
1.0.0.post4.dev0+ae68c71.
Releasing
Publishing is tag-driven. The CI pipeline is a chain of separate jobs, each running only after the previous one succeeds:
- lint —
ruff check+ruff format --check - test — the full Python 3.10–3.14 matrix
- build — builds the wheel and sdist from the tag, uploads
dist/as a run artifact - publish — on a fresh runner, downloads that artifact and publishes it to PyPI
lint and test also run on every pull request and every push to main.
build and publish run only when a v* tag is pushed, and the build uses
fetch-depth: 0 so the tag is present when the version is derived. Building
and publishing are separate jobs, so the publish step never reuses the build
machine — it uploads exactly what was built.
So a release is just tagging:
git tag v1.0.1
git push origin v1.0.1
Requires a trusted publisher configured on the PyPI project
(pytest-fahhh-on-fail). If a publish run fails, fix the cause and re-push the
same tag (or a corrected one).
Release files for pytest-fahhh-on-fail 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 | |
|---|---|---|---|
| pytest_fahhh_on_fail-1.0.0.tar.gz | 42.8 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| pytest_fahhh_on_fail-1.0.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 84.8 kB
Release files / pytest_fahhh_on_fail-1.0.0.tar.gz
| Download URL | pytest_fahhh_on_fail-1.0.0.tar.gz |
|---|---|
| Size | 42.8 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
791dfdcc6fecce522147c1c75734ce65c04ddd161b3463d05cf795eac117b2e4
|
|
BLAKE2b-256 checksum How to use checksums |
ff04295e311e8ed43e275f8ad948513c42cf0957957551cc063fa206603f19ed
|
| 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 15, 2026.
Transparency logRelease files / pytest_fahhh_on_fail-1.0.0-py3-none-any.whl
| Download URL | pytest_fahhh_on_fail-1.0.0-py3-none-any.whl |
|---|---|
| Size | 42.0 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
5ceef8d68e98587b5caf9e7a11f4ac9bb2a8a907a96edeb8dd8f1e1e3b2d4f08
|
|
BLAKE2b-256 checksum How to use checksums |
314ebf4d985a873c75c80b5d2bd2aa476926c6ad64a139f6abfe4cf38339d6c0
|
| 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 15, 2026.
Transparency log