mypy icon indicating copy to clipboard operation
mypy copied to clipboard

Cannot silence "unused 'type: ignore'" in version specific code

Open serhiy-storchaka opened this issue 5 years ago • 20 comments

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.

serhiy-storchaka avatar May 14 '20 13:05 serhiy-storchaka

As a workaround for this issue, you can use cast

hauntsaninja avatar May 14 '20 19:05 hauntsaninja

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.

serhiy-storchaka avatar May 18 '20 07:05 serhiy-storchaka

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.

hauntsaninja avatar May 18 '20 19:05 hauntsaninja

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.

serhiy-storchaka avatar May 19 '20 15:05 serhiy-storchaka

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.

serhiy-storchaka avatar May 19 '20 15:05 serhiy-storchaka

I think we need a way to silence unused type ignore messages for individual type ignores short of disabling the warning.

msullivan avatar May 20 '20 01:05 msullivan

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…?

wsanchez avatar Oct 01 '20 22:10 wsanchez

Note the select issue I mention above appears to be new between Mypy 0.770 and 0.782.

wsanchez avatar Oct 01 '20 22:10 wsanchez

@wsanchez That is different. Are you using the right syntax for the platform check? Please ask on Gitter.

gvanrossum avatar Oct 01 '20 22:10 gvanrossum

@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.

wsanchez avatar Oct 01 '20 22:10 wsanchez

# type: ignore # noqa and # noqa don't work either? Used --no-warn-unused-ignores

PatMyron avatar Dec 15 '20 22:12 PatMyron

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)

hauntsaninja avatar Dec 15 '20 22:12 hauntsaninja

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+.

bryanforbes avatar Mar 24 '21 15:03 bryanforbes

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).

stinovlas avatar Aug 03 '21 07:08 stinovlas

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]

pawamoy avatar Nov 21 '21 19:11 pawamoy

Or maybe

from ast import Index

def _get_index_value(node: Index):
    return node.value  # type: ignore[attr-defined,unused-ignore]

pawamoy avatar Nov 21 '21 22:11 pawamoy

Dup of #5940? (Or maybe that should be treated as the dup?)

posita avatar Dec 03 '21 13:12 posita

#12286 would be a good solution to this.

KotlinIsland avatar Mar 10 '22 02:03 KotlinIsland

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')

uri-granta avatar Jun 16 '22 10:06 uri-granta

I think if you are using basedmypy you can write

print("based") # type: ignore[operator, unused-ignore]

KotlinIsland avatar Jun 16 '22 18:06 KotlinIsland

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!

senarvi avatar Mar 17 '23 18:03 senarvi

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)

matteosantama avatar Mar 26 '23 23:03 matteosantama

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")

hauntsaninja avatar Mar 27 '23 02:03 hauntsaninja

Thanks to ilevkivskyi, in the next release of mypy # type: ignore[unused-ignore] should work.

hauntsaninja avatar May 21 '23 20:05 hauntsaninja