Skip to content

Commit 6970e14

Browse files
cfulljamesMichael Howitz
andauthored
Fix handling of --rerun-except arguments (#226)
Previously, a failure had to match all --rerun-except arguments to prevent a test from rerunning. Now it will not rerun if one or more are matched. Additionally, if --only-rerun and --rerun-except are used together, a failure must match at least one --only-rerun pattern and none of the --rerun-except patterns for the test to be rerun. Previously a test would be rerun if it matched neither --only-rerun nor --rerun-except. Fixes #225 --------- Co-authored-by: Michael Howitz <mh@gocept.com>
1 parent 5f5734a commit 6970e14

File tree

3 files changed

+84
-17
lines changed

3 files changed

+84
-17
lines changed

CHANGES.rst

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,17 @@ Features
1616

1717
- Add support for pytest 7.3, 7.4.
1818

19+
Bug fixes
20+
+++++++++
21+
22+
-- Failures are now rerun only if they match at least one ``--only-rerun``
23+
pattern (if given) and none of the ``--rerun-except`` patterns. Previously,
24+
using both ``--only-rerun`` and ``--rerun-except`` together could cause
25+
failures to be rerun even if they did not match any ``--only-rerun``
26+
pattern, and when using multiple ``--rerun-except`` patterns, all failures
27+
would be rerun unless they matched every pattern.
28+
(`#225 <https://github.com/pytest-dev/pytest-rerunfailures/issues/225>`_)
29+
1930

2031
11.1.2 (2023-03-09)
2132
-------------------

pytest_rerunfailures.py

Lines changed: 33 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -315,33 +315,50 @@ def _get_rerun_filter_regex(item, regex_name):
315315
return regex
316316

317317

318+
def _matches_any_rerun_error(rerun_errors, report):
319+
for rerun_regex in rerun_errors:
320+
try:
321+
if re.search(rerun_regex, report.longrepr.reprcrash.message):
322+
return True
323+
except AttributeError:
324+
if re.search(rerun_regex, report.longreprtext):
325+
return True
326+
return False
327+
328+
329+
def _matches_any_rerun_except_error(rerun_except_errors, report):
330+
for rerun_regex in rerun_except_errors:
331+
if re.search(rerun_regex, report.longrepr.reprcrash.message):
332+
return True
333+
return False
334+
335+
318336
def _should_hard_fail_on_error(item, report):
319337
if report.outcome != "failed":
320338
return False
321339

322340
rerun_errors = _get_rerun_filter_regex(item, "only_rerun")
323341
rerun_except_errors = _get_rerun_filter_regex(item, "rerun_except")
324342

325-
if not rerun_errors and not rerun_except_errors:
326-
343+
if (not rerun_errors) and (not rerun_except_errors):
344+
# Using neither --only-rerun nor --rerun-except
327345
return False
328346

329-
if rerun_errors:
330-
for rerun_regex in rerun_errors:
331-
try:
332-
if re.search(rerun_regex, report.longrepr.reprcrash.message):
333-
return False
334-
except AttributeError:
335-
if re.search(rerun_regex, report.longreprtext):
336-
return False
337-
338-
if rerun_except_errors:
339-
for rerun_regex in rerun_except_errors:
340-
if not re.search(rerun_regex, report.longrepr.reprcrash.message):
347+
elif rerun_errors and (not rerun_except_errors):
348+
# Using --only-rerun but not --rerun-except
349+
return not _matches_any_rerun_error(rerun_errors, report)
341350

342-
return False
351+
elif (not rerun_errors) and rerun_except_errors:
352+
# Using --rerun-except but not --only-rerun
353+
return _matches_any_rerun_except_error(rerun_except_errors, report)
343354

344-
return True
355+
else:
356+
# Using both --only-rerun and --rerun-except
357+
matches_rerun_only = _matches_any_rerun_error(rerun_errors, report)
358+
matches_rerun_except = _matches_any_rerun_except_error(
359+
rerun_except_errors, report
360+
)
361+
return (not matches_rerun_only) or matches_rerun_except
345362

346363

347364
def _should_not_rerun(item, report, reruns):

test_pytest_rerunfailures.py

Lines changed: 40 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -575,6 +575,7 @@ def test_xfail():
575575
(["AssertionError: "], True),
576576
(["ERR"], False),
577577
(["AssertionError", "OSError"], True),
578+
(["ValueError", "OSError"], False),
578579
],
579580
)
580581
def test_rerun_except_flag(testdir, rerun_except_texts, should_rerun):
@@ -587,7 +588,44 @@ def test_rerun_except_flag(testdir, rerun_except_texts, should_rerun):
587588

588589
pytest_args = ["--reruns", str(num_reruns)]
589590
for rerun_except_text in rerun_except_texts:
590-
print(rerun_except_text)
591+
pytest_args.extend(["--rerun-except", rerun_except_text])
592+
result = testdir.runpytest(*pytest_args)
593+
assert_outcomes(
594+
result, passed=num_passed, failed=num_failed, rerun=num_reruns_actual
595+
)
596+
597+
598+
@pytest.mark.parametrize(
599+
"only_rerun_texts, rerun_except_texts, should_rerun",
600+
[
601+
# Matches --only-rerun, but not --rerun-except (rerun)
602+
(["ValueError"], ["Not a Match"], True),
603+
(["ValueError", "AssertionError"], ["Not a match", "OSError"], True),
604+
# Matches --only-rerun AND --rerun-except (no rerun)
605+
(["ValueError"], ["ERR"], False),
606+
(["OSError", "ValueError"], ["Not a match", "ERR"], False),
607+
# Matches --rerun-except, but not --only-rerun (no rerun)
608+
(["OSError", "AssertionError"], ["TypeError", "ValueError"], False),
609+
# Matches neither --only-rerun nor --rerun-except (no rerun)
610+
(["AssertionError"], ["OSError"], False),
611+
# --rerun-except overrides --only-rerun for same arg (no rerun)
612+
(["ValueError"], ["ValueError"], False),
613+
],
614+
)
615+
def test_rerun_except_and_only_rerun(
616+
testdir, rerun_except_texts, only_rerun_texts, should_rerun
617+
):
618+
testdir.makepyfile('def test_only_rerun_except(): raise ValueError("ERR")')
619+
620+
num_failed = 1
621+
num_passed = 0
622+
num_reruns = 1
623+
num_reruns_actual = num_reruns if should_rerun else 0
624+
625+
pytest_args = ["--reruns", str(num_reruns)]
626+
for only_rerun_text in only_rerun_texts:
627+
pytest_args.extend(["--only-rerun", only_rerun_text])
628+
for rerun_except_text in rerun_except_texts:
591629
pytest_args.extend(["--rerun-except", rerun_except_text])
592630
result = testdir.runpytest(*pytest_args)
593631
assert_outcomes(
@@ -704,6 +742,7 @@ def test_fail():
704742
("ValueError", None, True),
705743
(["ValueError"], None, True),
706744
(["OSError", "ValueError"], None, True),
745+
(["OSError", "AssertionError"], None, False),
707746
# CLI override behavior
708747
("AssertionError", "ValueError", False),
709748
("ValueError", "AssertionError", True),

0 commit comments

Comments
 (0)