Skip to content

Commit

Permalink
Merge pull request #180 from dmtucker/issue177
Browse files Browse the repository at this point in the history
Add --mypy-xfail
  • Loading branch information
dmtucker authored Sep 17, 2024
2 parents 23131ba + c7225b5 commit 34a215c
Show file tree
Hide file tree
Showing 2 changed files with 82 additions and 2 deletions.
28 changes: 26 additions & 2 deletions src/pytest_mypy/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,11 @@ def pytest_addoption(parser: pytest.Parser) -> None:
action="store_true",
help="ignore mypy's exit status",
)
group.addoption(
"--mypy-xfail",
action="store_true",
help="xfail mypy errors",
)


def _xdist_worker(config: pytest.Config) -> Dict[str, Any]:
Expand Down Expand Up @@ -170,6 +175,7 @@ def pytest_collect_file(
parent.config.option.mypy_config_file,
parent.config.option.mypy_ignore_missing_imports,
parent.config.option.mypy_no_status_check,
parent.config.option.mypy_xfail,
],
):
# Do not create MypyFile instance for a .py file if a
Expand Down Expand Up @@ -234,6 +240,13 @@ def runtest(self) -> None:
error.partition(":")[2].partition(":")[0].strip() == "note"
for error in errors
):
if self.session.config.option.mypy_xfail:
self.add_marker(
pytest.mark.xfail(
raises=MypyError,
reason="mypy errors are expected by --mypy-xfail.",
)
)
raise MypyError(file_error_formatter(self, results, errors))
warnings.warn("\n" + "\n".join(errors), MypyWarning)

Expand All @@ -253,6 +266,15 @@ def runtest(self) -> None:
"""Raise a MypyError if mypy exited with a non-zero status."""
results = MypyResults.from_session(self.session)
if results.status:
if self.session.config.option.mypy_xfail:
self.add_marker(
pytest.mark.xfail(
raises=MypyError,
reason=(
"A non-zero mypy exit status is expected by --mypy-xfail."
),
)
)
raise MypyError(f"mypy exited with status {results.status}.")


Expand Down Expand Up @@ -366,9 +388,11 @@ def pytest_terminal_summary(
except FileNotFoundError:
# No MypyItems executed.
return
if results.unmatched_stdout or results.stderr:
if config.option.mypy_xfail or results.unmatched_stdout or results.stderr:
terminalreporter.section(terminal_summary_title)
if results.unmatched_stdout:
if config.option.mypy_xfail:
terminalreporter.write(results.stdout)
elif results.unmatched_stdout:
color = {"red": True} if results.status else {"green": True}
terminalreporter.write_line(results.unmatched_stdout, **color)
if results.stderr:
Expand Down
56 changes: 56 additions & 0 deletions tests/test_pytest_mypy.py
Original file line number Diff line number Diff line change
Expand Up @@ -561,3 +561,59 @@ def test_mypy_no_status_check(testdir, xdist_args):
result = testdir.runpytest_subprocess("--mypy-no-status-check", *xdist_args)
result.assert_outcomes(passed=mypy_file_checks)
assert result.ret == pytest.ExitCode.OK


def test_mypy_xfail_passes(testdir, xdist_args):
"""Verify that --mypy-xfail passes passes."""
testdir.makepyfile(thon="one: int = 1")
result = testdir.runpytest_subprocess("--mypy", *xdist_args)
mypy_file_checks = 1
mypy_status_check = 1
result.assert_outcomes(passed=mypy_file_checks + mypy_status_check)
assert result.ret == pytest.ExitCode.OK
result = testdir.runpytest_subprocess("--mypy-xfail", *xdist_args)
result.assert_outcomes(passed=mypy_file_checks + mypy_status_check)
assert result.ret == pytest.ExitCode.OK


def test_mypy_xfail_xfails(testdir, xdist_args):
"""Verify that --mypy-xfail xfails failures."""
testdir.makepyfile(thon="one: str = 1")
result = testdir.runpytest_subprocess("--mypy", *xdist_args)
mypy_file_checks = 1
mypy_status_check = 1
result.assert_outcomes(failed=mypy_file_checks + mypy_status_check)
assert result.ret == pytest.ExitCode.TESTS_FAILED
result = testdir.runpytest_subprocess("--mypy-xfail", *xdist_args)
result.assert_outcomes(xfailed=mypy_file_checks + mypy_status_check)
assert result.ret == pytest.ExitCode.OK


def test_mypy_xfail_reports_stdout(testdir, xdist_args):
"""Verify that --mypy-xfail reports stdout from mypy."""
stdout = "a distinct string on stdout"
testdir.makepyfile(
conftest=f"""
import pytest
@pytest.hookimpl(trylast=True)
def pytest_configure(config):
pytest_mypy = config.pluginmanager.getplugin("mypy")
mypy_config_stash = config.stash[pytest_mypy.stash_key["config"]]
with open(mypy_config_stash.mypy_results_path, mode="w") as results_f:
pytest_mypy.MypyResults(
opts=[],
stdout="{stdout}",
stderr="",
status=0,
abspath_errors={{}},
unmatched_stdout="",
).dump(results_f)
""",
)
result = testdir.runpytest_subprocess("--mypy", *xdist_args)
assert result.ret == pytest.ExitCode.OK
assert stdout not in result.stdout.str()
result = testdir.runpytest_subprocess("--mypy-xfail", *xdist_args)
assert result.ret == pytest.ExitCode.OK
assert stdout in result.stdout.str()

0 comments on commit 34a215c

Please sign in to comment.