diff --git a/src/pytest_mypy.py b/src/pytest_mypy.py index faa02a7..96e3406 100644 --- a/src/pytest_mypy.py +++ b/src/pytest_mypy.py @@ -208,8 +208,7 @@ def runtest(self): for error in errors ): raise MypyError(file_error_formatter(self, results, errors)) - # This line cannot be easily covered on mypy < 0.990: - warnings.warn("\n" + "\n".join(errors), MypyWarning) # pragma: no cover + warnings.warn("\n" + "\n".join(errors), MypyWarning) def reportinfo(self): """Produce a heading for the test report.""" diff --git a/tests/test_pytest_mypy.py b/tests/test_pytest_mypy.py index 924cdd2..5a9a16f 100644 --- a/tests/test_pytest_mypy.py +++ b/tests/test_pytest_mypy.py @@ -6,6 +6,8 @@ import pexpect import pytest +import pytest_mypy + MYPY_VERSION = Version(mypy.version.__version__) PYTEST_VERSION = Version(pytest.__version__) @@ -98,7 +100,7 @@ def pyfunc(x: int) -> str: assert result.ret != 0 -def test_mypy_annotation_unchecked(testdir, xdist_args): +def test_mypy_annotation_unchecked(testdir, xdist_args, tmp_path, monkeypatch): """Verify that annotation-unchecked warnings do not manifest as an error.""" testdir.makepyfile( """ @@ -107,6 +109,29 @@ def pyfunc(x): return x * y """, ) + min_mypy_version = Version("0.990") + if MYPY_VERSION < min_mypy_version: + # mypy doesn't emit annotation-unchecked warnings until 0.990: + fake_mypy_path = tmp_path / "mypy" + fake_mypy_path.mkdir() + (fake_mypy_path / "__init__.py").touch() + (fake_mypy_path / "api.py").write_text( + textwrap.dedent( + """ + def run(*args, **kwargs): + return ( + "test_mypy_annotation_unchecked.py:2:" + " note: By default the bodies of untyped functions" + " are not checked, consider using --check-untyped-defs" + " [annotation-unchecked]\\nSuccess: no issues found in" + " 1 source file\\n", + "", + 0, + ) + """ + ) + ) + monkeypatch.setenv("PYTHONPATH", str(tmp_path)) result = testdir.runpytest_subprocess(*xdist_args) result.assert_outcomes() result = testdir.runpytest_subprocess("--mypy", *xdist_args) @@ -114,9 +139,7 @@ def pyfunc(x): mypy_status_check = 1 mypy_checks = mypy_file_checks + mypy_status_check outcomes = {"passed": mypy_checks} - # mypy doesn't emit annotation-unchecked warnings until 0.990: - min_mypy_version = Version("0.990") - if MYPY_VERSION >= min_mypy_version and PYTEST_VERSION >= Version("7.0"): + if PYTEST_VERSION >= Version("7.0"): # assert_outcomes does not support `warnings` until 7.x. outcomes["warnings"] = 1 result.assert_outcomes(**outcomes) @@ -554,3 +577,51 @@ def test_mypy_item_collect(request): mypy_status_check = 1 result.assert_outcomes(passed=test_count + mypy_file_checks + mypy_status_check) assert result.ret == 0 + + +@pytest.mark.xfail( + MYPY_VERSION < Version("0.750"), + reason="https://github.com/python/mypy/issues/7800", +) +def test_mypy_results_from_mypy_with_opts(): + """MypyResults.from_mypy respects passed options.""" + mypy_results = pytest_mypy.MypyResults.from_mypy([], opts=["--version"]) + assert mypy_results.status == 0 + assert mypy_results.abspath_errors == {} + assert str(MYPY_VERSION) in mypy_results.stdout + + +def test_mypy_no_output(testdir, xdist_args): + """No terminal summary is shown if there is no output from mypy.""" + # Pytest didn't add type annotations until 6.0. + type_ignore = "# type: ignore" if PYTEST_VERSION < Version("6.0") else "" + testdir.makepyfile( + conftest=f""" + import tempfile + + import pytest {type_ignore} + + @pytest.hookimpl(hookwrapper=True) + def pytest_terminal_summary(config): + pytest_mypy = config.pluginmanager.getplugin("mypy") + with open(config._mypy_results_path, mode="r") as results_f: + results = pytest_mypy.MypyResults.load(results_f) + with open(config._mypy_results_path, mode="w") as results_f: + pytest_mypy.MypyResults( + opts=results.opts, + stdout=results.stdout, + stderr="", + status=results.status, + abspath_errors=results.abspath_errors, + unmatched_stdout="", + ).dump(results_f) + yield + """, + ) + result = testdir.runpytest_subprocess("--mypy", *xdist_args) + mypy_file_checks = 1 + mypy_status_check = 1 + mypy_checks = mypy_file_checks + mypy_status_check + result.assert_outcomes(passed=mypy_checks) + assert result.ret == 0 + assert "= mypy =" not in str(result.stdout) diff --git a/tox.ini b/tox.ini index 4817b10..52fe5ce 100644 --- a/tox.ini +++ b/tox.ini @@ -42,7 +42,7 @@ deps = pytest-randomly ~= 3.4 pytest-xdist ~= 1.34 -commands = pytest -p no:mypy {posargs:--cov pytest_mypy --cov-fail-under 100 --cov-report term-missing -n auto} +commands = pytest -p no:mypy {posargs:--cov pytest_mypy --cov-branch --cov-fail-under 100 --cov-report term-missing -n auto} [pytest] testpaths = tests