mypy
mypy copied to clipboard
Cannot silence "unused 'type: ignore'" in version specific code
I need to execute some code only on specific Python version (it is the use of TextIOWrapper.reconfigure() added in Python 3.7, but in following example I will use more abstract code).
import sys
if sys.version_info >= (3, 7):
x: int = 'a'
Since MyPy on Python 3.7 raises an error for it (actually because of the error in typeshed) I need to add # type: ignore:
import sys
if sys.version_info >= (3, 7):
x: int = 'a' # type: ignore
But now I will get an error unused 'type: ignore' comment when run MyPy with warn_unused_ignores = True on Python 3.6.
So I cannot use variant 1, because it is an error on 3.7, and cannot use variant 2, because it is an error on 3.6.
MyPy should not complain about unused 'type: ignore' comment in the code which it does not analyze.
MyPy version is 0.770.
As a workaround for this issue, you can use cast
It does not help in my particular issue.
The problem is that MyPy skips some parts of the code and complains about unused type: ignore in this code (which would be not ignored if this code be not skipped). It is not consistent and does not have a workaround.
Agree that there is an issue, but I think it can be worked around using cast, since that lets you remove the type ignore altogether:
~/delete λ cat test84.py
import sys
from typing import cast, Any
if sys.version_info >= (3, 7):
x: int = cast(Any, 'a')
~/delete λ mypy --python-version 3.6 --warn-unused-ignores --warn-redundant-casts test84.py
Success: no issues found in 1 source file
~/delete λ mypy --python-version 3.7 --warn-unused-ignores --warn-redundant-casts test84.py
Success: no issues found in 1 source file
Looking at the issue on typeshed, you'd need to do something like cast(Any, sys.stdout).reconfigure(errors="replace"). It's not pretty, but it should work.
x: int = 'a' was just an example. It is the simplest way that I know to trigger an error. In real code I use
sys.stdout.reconfigure(errors="replace")
and I do not know how to silence an error in this case.
Oh, and thank you for your suggestion. I tried similar workaround, but made an error in the code, so it did not work. Now I see my mistake and fixed it thank to you.
I think we need a way to silence unused type ignore messages for individual type ignores short of disabling the warning.
I'm running into a by-platform equivalent of this. If I try to import epoll from the select module on macOS, I get an [attr-defined] error, but on Linux, that's fine. Conversely, on Linux, one can't import KQ_FILTER_READ from select.
So, depending on which platform you run mypy, you get a different error, and I don't know how to suppress that class of error message.
That might be a different ticket, but it seems similar enough that maybe mentioning it in this one will do…?
Note the select issue I mention above appears to be new between Mypy 0.770 and 0.782.
@wsanchez That is different. Are you using the right syntax for the platform check? Please ask on Gitter.
@gvanrossum Oh, OK, I didn't realize that Mypy could be told to do platform-specific things. I'll read more. https://github.com/twisted/twisted/pull/1416#issuecomment-702425277 describes the issue in a bit more detail.
# type: ignore # noqa and # noqa don't work either? Used --no-warn-unused-ignores
If you read this issue, you'll notice that there's a workaround posted. I also looked at your code, and you could just fix it using https://mypy.readthedocs.io/en/stable/common_issues.html#python-version-and-system-platform-checks (so it's not really the problem discussed in this issue either)
I'm running into an issue writing stubs for sqlalchemy where it would be nice if I could silence this error (checking with strict mode on):
if sys.version_info >= (3, 7):
class OrderedDict(Dict[_KT, _VT]): ...
else:
class OrderedDict(Dict[_KT, _VT]):
def keys(self) -> List[_KT]: ... # type: ignore[override]
Without that ignore, type checking for 3.6 fails. With the ignore, type checking fails in 3.7+.
I have a similar problem. In our tests, we sometimes use try-except import blocks to differentiate between Python versions.
try:
from unittest import IsolatedAsyncioTestCase as TestCase # type: ignore
except ImportError: # pragma: no cover
from asynctest import TestCase # type: ignore
If I run this with Python 3.8, mypy complains about unused ignore. However, if I delete the ignore and run mypy under Python 3.7, it complains about not existing attribute. It seems that there is currently no way to use warn_unused_ignores with this code. That's a shame, because I have to disable it for the entire project. It would be nice to have something like # type: ignore[unused-ignore] that would ignore the unused ignore =o).
Another example:
from ast import Index
def _get_index_value(node: NodeIndex):
return node.value
No errors with Python 3.8, error: "Index" has no attribute "value" on Python 3.9+.
Obviously I could add conditions using hasattr or sys.version, but these conditions are a bit useless since Index nodes will appear only on Python 3.8 (and less?), and never on Python 3.9+ when parsing code with ast.parse (correct me if I'm wrong).
Would it make sense to be able to parametrize the ignore comment?
from ast import Index
def _get_index_value(node: Index):
return node.value # type: ignore [py>=3.9]
Or maybe
from ast import Index
def _get_index_value(node: Index):
return node.value # type: ignore[attr-defined,unused-ignore]
Dup of #5940? (Or maybe that should be treated as the dup?)
#12286 would be a good solution to this.
A possible workaround for the @stinovlas's import use case is to use getattr instead:
try:
TestCase: Any = getattr(unittest, 'IsolatedAsyncioTestCase')
except AttributeError: # pragma: no cover
TestCase = getattr(asynctest, 'TestCase')
I think if you are using basedmypy you can write
print("based") # type: ignore[operator, unused-ignore]
A possible workaround for the @stinovlas's import use case is to use getattr instead:
try: TestCase: Any = getattr(unittest, 'IsolatedAsyncioTestCase') except AttributeError: # pragma: no cover TestCase = getattr(asynctest, 'TestCase')
I've been struggling with this issue with try/except imports. I thought I had tried everything already, but this worked!
I know "me too!" comments aren't especially helpful, but here's an example where the suggested workaround (using cast) does not work.
import sys
from typing import cast, Callable
if sys.version_info >= (3, 8):
from importlib.metadata import version
else:
from importlib_metadata import version
cast(Callable[[str], str], version)
__version__ = version("my-lib")
❯ mypy --python-version 3.10
Success: no issues found in 37 source files
❯ mypy --python-version 3.7
my-lib/__init__.py:12: error: Call to untyped function "version" in typed context [no-untyped-call]
Found 1 error in 1 file (checked 37 source files)
❯ mypy --version
mypy 1.1.1 (compiled: yes)
Try version: Callable[[str], str] before your if, or to cast in the same expression that you call e.g. cast(Callable[[str], str], version)("my-lib")
Thanks to ilevkivskyi, in the next release of mypy # type: ignore[unused-ignore] should work.