Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -334,6 +334,21 @@ Each retried attempt's traceback is appended to the ``rerun test summary
info`` section. The section is emitted automatically when the flag is set,
so ``-rR`` is not required.

Warn when a test is rerun
-------------------------

To make reruns more visible on CI, pass ``--rerun-warning``. Each time a
test is scheduled for rerun a ``PytestWarning`` is emitted, which tools
like `pytest-github-actions-annotate-failures
<https://pypi.org/project/pytest-github-actions-annotate-failures/>`_ can
turn into annotations:

.. code-block:: bash

$ pytest --reruns 2 --rerun-warning

Note that ``filterwarnings = error`` turns these warnings into errors.

Output
------

Expand Down
3 changes: 3 additions & 0 deletions changes/318.feature.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
Add ``--rerun-warning`` flag that emits a ``PytestWarning`` each time a test
is scheduled for rerun, to surface flaky tests in CI (e.g. via
pytest-github-actions-annotate-failures annotations).
16 changes: 16 additions & 0 deletions src/pytest_rerunfailures.py
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,15 @@ def pytest_addoption(parser):
"'rerun test summary info' section, which is emitted automatically "
"when this flag is set.",
)
group._addoption(
"--rerun-warning",
action="store_true",
dest="rerun_warning",
help="Emit a PytestWarning each time a test is scheduled for rerun. "
"Useful to surface flaky tests in CI, e.g. via annotations from "
"pytest-github-actions-annotate-failures. Note that "
"filterwarnings=error turns these warnings into errors.",
)
group.addoption(
"--max-suite-reruns",
action="store",
Expand Down Expand Up @@ -1337,6 +1346,13 @@ def pytest_runtest_protocol(item, nextitem):
continue

report.outcome = "rerun"
if item.session.config.option.rerun_warning:

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

— Comment created by Claude

Gap (low): worker-crash reruns under xdist emit no warning.

The warning is only emitted on this in-process rerun path. XDistHooks.pytest_handlecrashitem also schedules reruns (sched.mark_test_pending(crashitem) / report.outcome = "rerun") and does not check rerun_warning. So with -n and --rerun-warning, a test that crashes its worker and is rerun gets no annotation. The help text promises a warning "each time a test is scheduled for rerun". Either emit a warning on that path too, or narrow the wording.

item.warn(

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

— Comment created by Claude

Bug (high): --rerun-warning combined with filterwarnings = error crashes the whole session with INTERNALERROR.

item.warn() is called from pytest_runtest_protocol, not from inside a setup/call/teardown phase. Under -W error or filterwarnings = error, the warning is raised as an exception, and nothing catches it at this level. I reproduced it with --reruns 1 --rerun-warning -W error on a file where one test fails once: the run ends with INTERNALERROR> pytest.PytestWarning: test_x.py::test_a failed on attempt 1 and will be rerun and no tests ran. The remaining tests never run. The report is also left with outcome = "rerun" but is never logged, and any --max-suite-reruns slot has already been used up.

The help text and README say only that the warnings "turn into errors". Users will read that as a failing test, not an aborted session. Fix options: emit the warning in a way that cannot abort the protocol (for example, catch the exception and fail the test), or document the real behaviour clearly. A test covering -W error would also help.

pytest.PytestWarning(
f"{item.nodeid} failed on attempt "
f"{item.execution_count} and will be rerun"
)
)
time.sleep(delay * delay_backoff_factor ** (item.execution_count - 1))

if not parallel or works_with_current_xdist():
Expand Down
23 changes: 23 additions & 0 deletions tests/test_pytest_rerunfailures.py
Original file line number Diff line number Diff line change
Expand Up @@ -798,6 +798,29 @@ def test_skips(bad_teardown):
]


def test_rerun_warning_emitted(testdir):
testdir.makepyfile(
f"""
def test_pass():
{temporary_failure()}"""
)
result = testdir.runpytest("--reruns", "1", "--rerun-warning")
assert_outcomes(result, passed=1, rerun=1)
result.stdout.fnmatch_lines_random([
"*PytestWarning: *test_pass* failed on attempt 1 and will be rerun*"
])


def test_no_rerun_warning_by_default(testdir):
testdir.makepyfile(
f"""
def test_pass():
{temporary_failure()}"""
)
result = testdir.runpytest("--reruns", "1")
assert "will be rerun" not in result.stdout.str()


def test_rerun_show_tracebacks_for_eventual_pass(testdir):
testdir.makepyfile(
f"""
Expand Down
Loading