diff --git a/src/pytest_mypy/__init__.py b/src/pytest_mypy/__init__.py index 7b35ea8..0d661d1 100644 --- a/src/pytest_mypy/__init__.py +++ b/src/pytest_mypy/__init__.py @@ -16,10 +16,10 @@ from typing import ( Any, Dict, + IO, Iterator, List, Optional, - TextIO, Tuple, Union, ) @@ -297,6 +297,7 @@ class MypyResults: """Parsed results from Mypy.""" _abspath_errors_type = typing.Dict[str, typing.List[str]] + _encoding = "utf-8" opts: List[str] stdout: str @@ -305,14 +306,14 @@ class MypyResults: abspath_errors: _abspath_errors_type unmatched_stdout: str - def dump(self, results_f: TextIO) -> None: + def dump(self, results_f: IO[bytes]) -> None: """Cache results in a format that can be parsed by load().""" - return json.dump(vars(self), results_f) + results_f.write(json.dumps(vars(self)).encode(self._encoding)) @classmethod - def load(cls, results_f: TextIO) -> MypyResults: + def load(cls, results_f: IO[bytes]) -> MypyResults: """Get results cached by dump().""" - return cls(**json.load(results_f)) + return cls(**json.loads(results_f.read().decode(cls._encoding))) @classmethod def from_mypy( @@ -360,7 +361,7 @@ def from_session(cls, session: pytest.Session) -> MypyResults: mypy_results_path = session.config.stash[stash_key["config"]].mypy_results_path with FileLock(str(mypy_results_path) + ".lock"): try: - with open(mypy_results_path, mode="r") as results_f: + with open(mypy_results_path, mode="rb") as results_f: results = cls.load(results_f) except FileNotFoundError: results = cls.from_mypy( @@ -370,7 +371,7 @@ def from_session(cls, session: pytest.Session) -> MypyResults: if isinstance(item, MypyFileItem) ], ) - with open(mypy_results_path, mode="w") as results_f: + with open(mypy_results_path, mode="wb") as results_f: results.dump(results_f) return results @@ -393,7 +394,7 @@ def pytest_terminal_summary( """Report mypy results.""" mypy_results_path = config.stash[stash_key["config"]].mypy_results_path try: - with open(mypy_results_path, mode="r") as results_f: + with open(mypy_results_path, mode="rb") as results_f: results = MypyResults.load(results_f) except FileNotFoundError: # No MypyItems executed. diff --git a/tests/test_pytest_mypy.py b/tests/test_pytest_mypy.py index ed909c7..81c538d 100644 --- a/tests/test_pytest_mypy.py +++ b/tests/test_pytest_mypy.py @@ -10,6 +10,7 @@ MYPY_VERSION = Version(mypy.version.__version__) +PYTEST_VERSION = Version(pytest.__version__) PYTHON_VERSION = Version( ".".join( str(token) @@ -60,6 +61,30 @@ def pyfunc(x: int) -> int: assert result.ret == pytest.ExitCode.OK +@pytest.mark.skipif( + PYTEST_VERSION < Version("7.4"), + reason="https://github.com/pytest-dev/pytest/pull/10935", +) +@pytest.mark.skipif( + PYTHON_VERSION < Version("3.10"), + reason="PEP 597 was added in Python 3.10.", +) +@pytest.mark.skipif( + PYTHON_VERSION >= Version("3.12") and MYPY_VERSION < Version("1.5"), + reason="https://github.com/python/mypy/pull/15558", +) +def test_mypy_encoding_warnings(testdir, monkeypatch): + """Ensure no warnings are detected by PYTHONWARNDEFAULTENCODING.""" + testdir.makepyfile("") + monkeypatch.setenv("PYTHONWARNDEFAULTENCODING", "1") + result = testdir.runpytest_subprocess("--mypy") + mypy_file_checks = 1 + mypy_status_check = 1 + mypy_checks = mypy_file_checks + mypy_status_check + expected_warnings = 2 # https://github.com/python/mypy/issues/14603 + result.assert_outcomes(passed=mypy_checks, warnings=expected_warnings) + + def test_mypy_pyi(testdir, xdist_args): """ Verify that a .py file will be skipped if @@ -524,7 +549,7 @@ def test_mypy_no_output(testdir, xdist_args): 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: + with open(mypy_config_stash.mypy_results_path, mode="wb") as results_f: pytest_mypy.MypyResults( opts=[], stdout="", @@ -602,7 +627,7 @@ def test_mypy_xfail_reports_stdout(testdir, xdist_args): 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: + with open(mypy_config_stash.mypy_results_path, mode="wb") as results_f: pytest_mypy.MypyResults( opts=[], stdout="{stdout}",