Closed antecrescent closed 6 months ago
@antecrescent I suggest making a *args
function here to support older versions instead.
There are projects that use matrix checks with different mypy versions. Let's change https://github.com/typeddjango/pytest-mypy-plugins/pull/138
I've tried two possible approaches but 'm not sure how to proceed and I'd be happy to hear your thoughts.
*args
approachIt does not work, because mypy does not call flush_errors
with keyword arguments.
Also the static type-checking fails because mypy complains about our flush_errors
signature not matching its own:
diff --git a/pytest_mypy_plugins/item.py b/pytest_mypy_plugins/item.py
index 2094eed..937d9f0 100644
--- a/pytest_mypy_plugins/item.py
+++ b/pytest_mypy_plugins/item.py
@@ -85,7 +85,7 @@ def run_mypy_typechecking(cmd_options: List[str], stdout: TextIO, stderr: TextIO
# discard filename parameter '_'. Mypy uses it to generate
# one junit-xml test entry per file with failures (--junit-format per_file)
# and we don't support mypy's --junit-xml option in the first place.
- def flush_errors(_: str | None, new_messages: List[str], serious: bool) -> None:
+ def flush_errors(*_: str | None, new_messages: List[str], serious: bool) -> None:
error_messages.extend(new_messages)
f = stderr if serious else stdout
try:
pytest_mypy_plugins/item.py:99: error: Argument "flush_errors" to "build" has incompatible type "Callable[[VarArg(str | None), NamedArg(list[str], 'new_messages'), NamedArg(bool, 'serious')], None]"; expected "Callable[[str | None, list[str], bool], None] | None" [arg-type]
This works fine but type-checking fails.
diff --git a/pytest_mypy_plugins/item.py b/pytest_mypy_plugins/item.py
index 2094eed..466cdac 100644
--- a/pytest_mypy_plugins/item.py
+++ b/pytest_mypy_plugins/item.py
@@ -7,6 +7,7 @@ import tempfile
from pathlib import Path
from typing import TYPE_CHECKING, Any, Dict, List, Optional, TextIO, Tuple, Union
+from packaging.version import Version
import py
import pytest
from _pytest._code import ExceptionInfo
@@ -16,6 +17,7 @@ from _pytest.config import Config
from mypy import build
from mypy.fscache import FileSystemCache
from mypy.main import process_options
+from mypy.version import __version__ as mypy_version
if TYPE_CHECKING:
from _pytest._code.code import _TracebackStyle
@@ -82,10 +84,7 @@ def run_mypy_typechecking(cmd_options: List[str], stdout: TextIO, stderr: TextIO
error_messages = []
- # discard filename parameter '_'. Mypy uses it to generate
- # one junit-xml test entry per file with failures (--junit-format per_file)
- # and we don't support mypy's --junit-xml option in the first place.
- def flush_errors(_: str | None, new_messages: List[str], serious: bool) -> None:
+ def flush_errors(new_messages: List[str], serious: bool) -> None:
error_messages.extend(new_messages)
f = stderr if serious else stdout
try:
@@ -96,7 +95,19 @@ def run_mypy_typechecking(cmd_options: List[str], stdout: TextIO, stderr: TextIO
sys.exit(ReturnCodes.FATAL_ERROR)
try:
- build.build(sources, options, flush_errors=flush_errors, fscache=fscache, stdout=stdout, stderr=stderr)
+ if Version(mypy_version) < Version("1.8.0"):
+ def short_args_wrapper(new_messages: List[str], serious: bool) -> None:
+ flush_errors(new_messages, serious)
+
+ build.build(sources, options, flush_errors=short_args_wrapper, fscache=fscache, stdout=stdout, stderr=stderr)
+ else:
+ # discard filename parameter '_'. Mypy >= 1.8.0 uses it to generate
+ # one junit-xml test entry per file with failures (--junit-format per_file)
+ # and we don't support mypy's --junit-xml option in the first place.
+ def long_args_wrapper(_: str | None, new_messages: List[str], serious: bool) -> None:
+ flush_errors(new_messages, serious)
+
+ build.build(sources, options, flush_errors=long_args_wrapper, fscache=fscache, stdout=stdout, stderr=stderr)
except SystemExit as sysexit:
return sysexit.code
The problem is that mypy type-checks both branches against its own signature, which obviously fails since only one branch has the correct signature for either
# mypy .
pytest_mypy_plugins/item.py:110: error: Argument "flush_errors" to "build" has incompatible type "Callable[[str | None, list[str], bool], None]"; expected "Callable[[list[str], bool], None] | None" [arg-type]
Found 1 error in 1 file (checked 11 source files)
With mypy 1.8.0
# mypy .
pytest_mypy_plugins/item.py:102: error: Argument "flush_errors" to "build" has incompatible type "Callable[[list[str], bool], None]"; expected "Callable[[str | None, list[str], bool], None] | None" [arg-type]
Found 1 error in 1 file (checked 11 source files)
Workaround:
Patch the second approach as follows and run mypy with --no-warn-unused-ignores
. The reason for --no-warn-unused-ignores
is the same as above. One branch passes the type-checking despite its type: ignore
comment:
diff --git a/pytest_mypy_plugins/item.py b/pytest_mypy_plugins/item.py
index 466cdac..83fffb2 100644
--- a/pytest_mypy_plugins/item.py
+++ b/pytest_mypy_plugins/item.py
@@ -99,7 +99,7 @@ def run_mypy_typechecking(cmd_options: List[str], stdout: TextIO, stderr: TextIO
def short_args_wrapper(new_messages: List[str], serious: bool) -> None:
flush_errors(new_messages, serious)
- build.build(sources, options, flush_errors=short_args_wrapper, fscache=fscache, stdout=stdout, stderr=stderr)
+ build.build(sources, options, flush_errors=short_args_wrapper, fscache=fscache, stdout=stdout, stderr=stderr) # type: ignore
else:
# discard filename parameter '_'. Mypy >= 1.8.0 uses it to generate
# one junit-xml test entry per file with failures (--junit-format per_file)
@@ -107,7 +107,7 @@ def run_mypy_typechecking(cmd_options: List[str], stdout: TextIO, stderr: TextIO
def long_args_wrapper(_: str | None, new_messages: List[str], serious: bool) -> None:
flush_errors(new_messages, serious)
- build.build(sources, options, flush_errors=long_args_wrapper, fscache=fscache, stdout=stdout, stderr=stderr)
+ build.build(sources, options, flush_errors=long_args_wrapper, fscache=fscache, stdout=stdout, stderr=stderr) # type: ignore
except SystemExit as sysexit:
return sysexit.code
*args: Any
?
I may not understand your suggestion correctly...
def flush_errors(*_: Any, new_messages: List[str], serious: bool) -> None
fails the type-checking with:
pytest_mypy_plugins/item.py:99: error: Argument "flush_errors" to "build" has incompatible type "Callable[[VarArg(Any), NamedArg(list[str], 'new_messages'), NamedArg(bool, 'serious')], None]"; expected "Callable[[str | None, list[str], bool], None] | None" [arg-type]
This also fails at runtime for the same reason listed in the first approach:
TypeError: run_mypy_typechecking.<locals>.flush_errors() missing 2 required keyword-only arguments: 'new_messages' and 'ser...
@antecrescent please, take a look at my suggestion :)
Ah yes, makes sense! I'm not very versed in Python but this seems to be a more sensible approach. I'll rebase this to get rid my erroneous commit and fix a typo in your comment.
Thank you for helping me fix this and teaching me a bit of Python :P
mypy-1.8.0 added a third parameter to the flush_error signature (https://github.com/python/mypy/commit/8c57df01386f3e29d877ca190dc4c5e5af7b92a1). 97aff1e9ae5022384542ce98b8a401c7f4c420e7 updated
flush_error
inpytest_mypy_plugins/item.py
to match it, but failed taking into account that <mypy-1.8.0 still expects the two-parameter signature to flush errors after a file is processed.As a consequence, pytest test cases fail if mypy's
process_stale_scc
function calls our three-parameterflush_error
with two arguments. I'm not familiar with the innards of mypy but this happens every time pytest runs with the--mypy-same-process
option.Reproducible setup:
Output
``` ============================= test session starts ============================== platform linux -- Python 3.12.2, pytest-8.0.2, pluggy-1.4.0 rootdir: /root/pytest-mypy-plugins configfile: pyproject.toml plugins: mypy-plugins-3.0.0 collected 60 items pytest_mypy_plugins/tests/test-extension.yml F pytest_mypy_plugins/tests/test-mypy-config.yml F pytest_mypy_plugins/tests/test-parametrized.yml FFFFFFFFFF pytest_mypy_plugins/tests/test-paths-from-env.yml F pytest_mypy_plugins/tests/test-regex_assertions.yml FFFFFFFF pytest_mypy_plugins/tests/test-simple-cases.yml FFFFFFFFF pytest_mypy_plugins/tests/test_configs/test_join_toml_configs.py ...... pytest_mypy_plugins/tests/test_explicit_configs.py ...... pytest_mypy_plugins/tests/test_input_schema.py ....... pytest_mypy_plugins/tests/test_utils.py ........... =================================== FAILURES =================================== _______________________ reveal_type_extension_is_loaded ________________________ Traceback (most recent call last): File "/tmp/venv/lib/python3.12/site-packages/_pytest/runner.py", line 342, in from_call result: Optional[TResult] = func() ^^^^^^ File "/tmp/venv/lib/python3.12/site-packages/_pytest/runner.py", line 263, inIssue
pip install mypy==1.8.0
and the same test run succeeds.