11import signal
2+ import sys
23import textwrap
34
45import mypy .version
56from packaging .version import Version
67import pexpect
78import pytest
89
10+ import pytest_mypy
11+
912
1013MYPY_VERSION = Version (mypy .version .__version__ )
1114PYTEST_VERSION = Version (pytest .__version__ )
15+ PYTHON_VERSION = Version (
16+ "." .join (
17+ str (token )
18+ for token in [
19+ sys .version_info .major ,
20+ sys .version_info .minor ,
21+ sys .version_info .micro ,
22+ ]
23+ )
24+ )
1225
1326
1427@pytest .fixture (
@@ -100,7 +113,7 @@ def pyfunc(x: int) -> str:
100113 assert "_mypy_results_path" not in result .stderr .str ()
101114
102115
103- def test_mypy_annotation_unchecked (testdir , xdist_args ):
116+ def test_mypy_annotation_unchecked (testdir , xdist_args , tmp_path , monkeypatch ):
104117 """Verify that annotation-unchecked warnings do not manifest as an error."""
105118 testdir .makepyfile (
106119 """
@@ -109,6 +122,29 @@ def pyfunc(x):
109122 return x * y
110123 """ ,
111124 )
125+ min_mypy_version = Version ("0.990" )
126+ if MYPY_VERSION < min_mypy_version :
127+ # mypy doesn't emit annotation-unchecked warnings until 0.990:
128+ fake_mypy_path = tmp_path / "mypy"
129+ fake_mypy_path .mkdir ()
130+ (fake_mypy_path / "__init__.py" ).touch ()
131+ (fake_mypy_path / "api.py" ).write_text (
132+ textwrap .dedent (
133+ """
134+ def run(*args, **kwargs):
135+ return (
136+ "test_mypy_annotation_unchecked.py:2:"
137+ " note: By default the bodies of untyped functions"
138+ " are not checked, consider using --check-untyped-defs"
139+ " [annotation-unchecked]\\ nSuccess: no issues found in"
140+ " 1 source file\\ n",
141+ "",
142+ 0,
143+ )
144+ """
145+ )
146+ )
147+ monkeypatch .setenv ("PYTHONPATH" , str (tmp_path ))
112148 result = testdir .runpytest_subprocess (* xdist_args )
113149 result .assert_outcomes ()
114150 result = testdir .runpytest_subprocess ("--mypy" , * xdist_args )
@@ -552,3 +588,68 @@ def test_mypy_item_collect(request):
552588 mypy_status_check = 1
553589 result .assert_outcomes (passed = test_count + mypy_file_checks + mypy_status_check )
554590 assert result .ret == 0
591+
592+
593+ @pytest .mark .xfail (
594+ MYPY_VERSION < Version ("0.750" ),
595+ raises = AssertionError ,
596+ reason = "https://github.com/python/mypy/issues/7800" ,
597+ )
598+ def test_mypy_results_from_mypy_with_opts ():
599+ """MypyResults.from_mypy respects passed options."""
600+ mypy_results = pytest_mypy .MypyResults .from_mypy ([], opts = ["--version" ])
601+ assert mypy_results .status == 0
602+ assert mypy_results .abspath_errors == {}
603+ assert str (MYPY_VERSION ) in mypy_results .stdout
604+
605+
606+ @pytest .mark .xfail (
607+ Version ("3.7" ) < PYTHON_VERSION < Version ("3.9" )
608+ and Version ("0.710" ) <= MYPY_VERSION < Version ("0.720" ),
609+ raises = AssertionError ,
610+ reason = "Mypy crashes for some reason." ,
611+ )
612+ def test_mypy_no_output (testdir , xdist_args ):
613+ """No terminal summary is shown if there is no output from mypy."""
614+ type_ignore = (
615+ "# type: ignore"
616+ if (
617+ PYTEST_VERSION
618+ < Version ("6.0" ) # Pytest didn't add type annotations until 6.0.
619+ or MYPY_VERSION < Version ("0.710" )
620+ )
621+ else ""
622+ )
623+ testdir .makepyfile (
624+ # Mypy prints a success message to stderr by default:
625+ # "Success: no issues found in 1 source file"
626+ # Clear stderr and unmatched_stdout to simulate mypy having no output:
627+ conftest = f"""
628+ import pytest { type_ignore }
629+
630+ @pytest.hookimpl(hookwrapper=True)
631+ def pytest_terminal_summary(config):
632+ mypy_results_path = getattr(config, "_mypy_results_path", None)
633+ if not mypy_results_path:
634+ # xdist worker
635+ return
636+ pytest_mypy = config.pluginmanager.getplugin("mypy")
637+ with open(mypy_results_path, mode="w") as results_f:
638+ pytest_mypy.MypyResults(
639+ opts=[],
640+ stdout="",
641+ stderr="",
642+ status=0,
643+ abspath_errors={{}},
644+ unmatched_stdout="",
645+ ).dump(results_f)
646+ yield
647+ """ ,
648+ )
649+ result = testdir .runpytest_subprocess ("--mypy" , * xdist_args )
650+ mypy_file_checks = 1
651+ mypy_status_check = 1
652+ mypy_checks = mypy_file_checks + mypy_status_check
653+ result .assert_outcomes (passed = mypy_checks )
654+ assert result .ret == 0
655+ assert f"= { pytest_mypy .terminal_summary_title } =" not in str (result .stdout )
0 commit comments