Skip to content

Commit 1a75f22

Browse files
committed
Enable branch code coverage
1 parent a266479 commit 1a75f22

File tree

3 files changed

+105
-21
lines changed

3 files changed

+105
-21
lines changed

src/pytest_mypy.py

Lines changed: 21 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -205,8 +205,7 @@ def runtest(self):
205205
for error in errors
206206
):
207207
raise MypyError(file_error_formatter(self, results, errors))
208-
# This line cannot be easily covered on mypy < 0.990:
209-
warnings.warn("\n" + "\n".join(errors), MypyWarning) # pragma: no cover
208+
warnings.warn("\n" + "\n".join(errors), MypyWarning)
210209

211210
def reportinfo(self):
212211
"""Produce a heading for the test report."""
@@ -258,7 +257,9 @@ def from_mypy(
258257
) -> "MypyResults":
259258
"""Generate results from mypy."""
260259

261-
if opts is None:
260+
# This is covered by test_mypy_results_from_mypy_with_opts;
261+
# however, coverage is not recognized on py38-pytest4.6:
262+
if opts is None: # pragma: no cover
262263
opts = mypy_argv[:]
263264
abspath_errors = {
264265
os.path.abspath(str(item.fspath)): [] for item in items
@@ -322,18 +323,20 @@ class MypyWarning(pytest.PytestWarning):
322323

323324
def pytest_terminal_summary(terminalreporter, config):
324325
"""Report stderr and unrecognized lines from stdout."""
325-
if _is_master(config):
326-
try:
327-
with open(config._mypy_results_path, mode="r") as results_f:
328-
results = MypyResults.load(results_f)
329-
except FileNotFoundError:
330-
# No MypyItems executed.
331-
return
332-
if results.unmatched_stdout or results.stderr:
333-
terminalreporter.section("mypy")
334-
if results.unmatched_stdout:
335-
color = {"red": True} if results.status else {"green": True}
336-
terminalreporter.write_line(results.unmatched_stdout, **color)
337-
if results.stderr:
338-
terminalreporter.write_line(results.stderr, yellow=True)
339-
os.remove(config._mypy_results_path)
326+
if not _is_master(config):
327+
# This isn't hit in pytest 5.0 for some reason.
328+
return # pragma: no cover
329+
try:
330+
with open(config._mypy_results_path, mode="r") as results_f:
331+
results = MypyResults.load(results_f)
332+
except FileNotFoundError:
333+
# No MypyItems executed.
334+
return
335+
if results.unmatched_stdout or results.stderr:
336+
terminalreporter.section("mypy")
337+
if results.unmatched_stdout:
338+
color = {"red": True} if results.status else {"green": True}
339+
terminalreporter.write_line(results.unmatched_stdout, **color)
340+
if results.stderr:
341+
terminalreporter.write_line(results.stderr, yellow=True)
342+
os.remove(config._mypy_results_path)

tests/test_pytest_mypy.py

Lines changed: 82 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@
66
import pexpect
77
import pytest
88

9+
import pytest_mypy
10+
911

1012
MYPY_VERSION = Version(mypy.version.__version__)
1113
PYTEST_VERSION = Version(pytest.__version__)
@@ -100,7 +102,7 @@ def pyfunc(x: int) -> str:
100102
assert "_mypy_results_path" not in result.stderr.str()
101103

102104

103-
def test_mypy_annotation_unchecked(testdir, xdist_args):
105+
def test_mypy_annotation_unchecked(testdir, xdist_args, tmp_path, monkeypatch):
104106
"""Verify that annotation-unchecked warnings do not manifest as an error."""
105107
testdir.makepyfile(
106108
"""
@@ -109,6 +111,29 @@ def pyfunc(x):
109111
return x * y
110112
""",
111113
)
114+
min_mypy_version = Version("0.990")
115+
if MYPY_VERSION < min_mypy_version:
116+
# mypy doesn't emit annotation-unchecked warnings until 0.990:
117+
fake_mypy_path = tmp_path / "mypy"
118+
fake_mypy_path.mkdir()
119+
(fake_mypy_path / "__init__.py").touch()
120+
(fake_mypy_path / "api.py").write_text(
121+
textwrap.dedent(
122+
"""
123+
def run(*args, **kwargs):
124+
return (
125+
"test_mypy_annotation_unchecked.py:2:"
126+
" note: By default the bodies of untyped functions"
127+
" are not checked, consider using --check-untyped-defs"
128+
" [annotation-unchecked]\\nSuccess: no issues found in"
129+
" 1 source file\\n",
130+
"",
131+
0,
132+
)
133+
"""
134+
)
135+
)
136+
monkeypatch.setenv("PYTHONPATH", str(tmp_path))
112137
result = testdir.runpytest_subprocess(*xdist_args)
113138
result.assert_outcomes()
114139
result = testdir.runpytest_subprocess("--mypy", *xdist_args)
@@ -552,3 +577,59 @@ def test_mypy_item_collect(request):
552577
mypy_status_check = 1
553578
result.assert_outcomes(passed=test_count + mypy_file_checks + mypy_status_check)
554579
assert result.ret == 0
580+
581+
582+
@pytest.mark.xfail(
583+
MYPY_VERSION < Version("0.750"),
584+
raises=AssertionError,
585+
reason="https://github.com/python/mypy/issues/7800",
586+
)
587+
def test_mypy_results_from_mypy_with_opts():
588+
"""MypyResults.from_mypy respects passed options."""
589+
mypy_results = pytest_mypy.MypyResults.from_mypy([], opts=["--version"])
590+
assert mypy_results.status == 0
591+
assert mypy_results.abspath_errors == {}
592+
assert str(MYPY_VERSION) in mypy_results.stdout
593+
594+
595+
def test_mypy_no_output(testdir, xdist_args):
596+
"""No terminal summary is shown if there is no output from mypy."""
597+
type_ignore = (
598+
"# type: ignore"
599+
if (
600+
PYTEST_VERSION
601+
< Version("6.0") # Pytest didn't add type annotations until 6.0.
602+
or MYPY_VERSION < Version("0.710")
603+
)
604+
else ""
605+
)
606+
testdir.makepyfile(
607+
conftest=f"""
608+
import tempfile
609+
610+
import pytest {type_ignore}
611+
612+
@pytest.hookimpl(hookwrapper=True)
613+
def pytest_terminal_summary(config):
614+
pytest_mypy = config.pluginmanager.getplugin("mypy")
615+
with open(config._mypy_results_path, mode="r") as results_f:
616+
results = pytest_mypy.MypyResults.load(results_f)
617+
with open(config._mypy_results_path, mode="w") as results_f:
618+
pytest_mypy.MypyResults(
619+
opts=results.opts,
620+
stdout=results.stdout,
621+
stderr="",
622+
status=results.status,
623+
abspath_errors=results.abspath_errors,
624+
unmatched_stdout="",
625+
).dump(results_f)
626+
yield
627+
""",
628+
)
629+
result = testdir.runpytest_subprocess("--mypy", *xdist_args)
630+
mypy_file_checks = 1
631+
mypy_status_check = 1
632+
mypy_checks = mypy_file_checks + mypy_status_check
633+
result.assert_outcomes(passed=mypy_checks)
634+
assert result.ret == 0
635+
assert "= mypy =" not in str(result.stdout)

tox.ini

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -44,11 +44,11 @@ deps =
4444

4545
packaging ~= 21.3
4646
pexpect ~= 4.8.0
47-
pytest-cov ~= 2.10
47+
pytest-cov ~= 4.1.0
4848
pytest-randomly ~= 3.4
4949
pytest-xdist ~= 1.34
5050

51-
commands = pytest -p no:mypy {posargs:--cov pytest_mypy --cov-fail-under 100 --cov-report term-missing -n auto}
51+
commands = pytest -p no:mypy {posargs:--cov pytest_mypy --cov-branch --cov-fail-under 100 --cov-report term-missing -n auto}
5252

5353
[pytest]
5454
testpaths = tests

0 commit comments

Comments
 (0)