Skip to content

Commit

Permalink
Use error subcodes to differentiate import errors (python#14740)
Browse files Browse the repository at this point in the history
Resolves python#9789

Users could use `--disable-error-code=import-untyped` to only ignore
errors about libraries not having stubs, but continue to get errors
about e.g. typos in an import name.

The error subcode mechanism is new from python#14570. Note that users will now
get a different error code depending on whether or not a package is
installed, and may not know that they can use the parent error code to
ignore the issue regardless. I think this is okay, in general type
checking results can change if you run them in two different
environments. Note also that with `--warn-unused-ignore` / `--strict`
mypy will complain about not having the most specific error code
  • Loading branch information
hauntsaninja authored Aug 10, 2023
1 parent 8c21953 commit 78339b9
Show file tree
Hide file tree
Showing 4 changed files with 29 additions and 10 deletions.
11 changes: 10 additions & 1 deletion mypy/build.py
Original file line number Diff line number Diff line change
Expand Up @@ -2780,7 +2780,16 @@ def module_not_found(
else:
daemon = manager.options.fine_grained_incremental
msg, notes = reason.error_message_templates(daemon)
errors.report(line, 0, msg.format(module=target), code=codes.IMPORT)
if reason == ModuleNotFoundReason.NOT_FOUND:
code = codes.IMPORT_NOT_FOUND
elif (
reason == ModuleNotFoundReason.FOUND_WITHOUT_TYPE_HINTS
or reason == ModuleNotFoundReason.APPROVED_STUBS_NOT_INSTALLED
):
code = codes.IMPORT_UNTYPED
else:
code = codes.IMPORT
errors.report(line, 0, msg.format(module=target), code=code)
top_level, second_level = get_top_two_prefixes(target)
if second_level in legacy_bundled_packages or second_level in non_bundled_packages:
top_level = second_level
Expand Down
6 changes: 6 additions & 0 deletions mypy/errorcodes.py
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,12 @@ def __hash__(self) -> int:
IMPORT: Final = ErrorCode(
"import", "Require that imported module can be found or has stubs", "General"
)
IMPORT_NOT_FOUND: Final = ErrorCode(
"import-not-found", "Require that imported module can be found", "General", sub_code_of=IMPORT
)
IMPORT_UNTYPED: Final = ErrorCode(
"import-untyped", "Require that imported module has stubs", "General", sub_code_of=IMPORT
)
NO_REDEF: Final = ErrorCode("no-redef", "Check that each name is defined once", "General")
FUNC_RETURNS_VALUE: Final = ErrorCode(
"func-returns-value", "Check that called function returns a value in value context", "General"
Expand Down
8 changes: 6 additions & 2 deletions mypy/errors.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
from typing_extensions import Literal, TypeAlias as _TypeAlias

from mypy import errorcodes as codes
from mypy.errorcodes import IMPORT, ErrorCode
from mypy.errorcodes import IMPORT, IMPORT_NOT_FOUND, IMPORT_UNTYPED, ErrorCode
from mypy.message_registry import ErrorMessage
from mypy.options import Options
from mypy.scope import Scope
Expand Down Expand Up @@ -510,7 +510,11 @@ def add_error_info(self, info: ErrorInfo) -> None:
if info.message in self.only_once_messages:
return
self.only_once_messages.add(info.message)
if self.seen_import_error and info.code is not IMPORT and self.has_many_errors():
if (
self.seen_import_error
and info.code not in (IMPORT, IMPORT_UNTYPED, IMPORT_NOT_FOUND)
and self.has_many_errors()
):
# Missing stubs can easily cause thousands of errors about
# Any types, especially when upgrading to mypy 0.900,
# which no longer bundles third-party library stubs. Avoid
Expand Down
14 changes: 7 additions & 7 deletions test-data/unit/check-errorcodes.test
Original file line number Diff line number Diff line change
Expand Up @@ -183,7 +183,7 @@ from defusedxml import xyz # type: ignore[import]

[case testErrorCodeBadIgnore]
import nostub # type: ignore xyz # E: Invalid "type: ignore" comment [syntax] \
# E: Cannot find implementation or library stub for module named "nostub" [import] \
# E: Cannot find implementation or library stub for module named "nostub" [import-not-found] \
# N: See https://mypy.readthedocs.io/en/stable/running_mypy.html#missing-imports
import nostub # type: ignore[ # E: Invalid "type: ignore" comment [syntax]
import nostub # type: ignore[foo # E: Invalid "type: ignore" comment [syntax]
Expand Down Expand Up @@ -211,7 +211,7 @@ def f(x, # type: int # type: ignore[
pass
[out]
main:2: error: Invalid "type: ignore" comment [syntax]
main:2: error: Cannot find implementation or library stub for module named "nostub" [import]
main:2: error: Cannot find implementation or library stub for module named "nostub" [import-not-found]
main:2: note: See https://mypy.readthedocs.io/en/stable/running_mypy.html#missing-imports
main:3: error: Invalid "type: ignore" comment [syntax]
main:4: error: Invalid "type: ignore" comment [syntax]
Expand Down Expand Up @@ -522,12 +522,12 @@ if int() is str(): # E: Non-overlapping identity check (left operand type: "int
[builtins fixtures/primitives.pyi]

[case testErrorCodeMissingModule]
from defusedxml import xyz # E: Cannot find implementation or library stub for module named "defusedxml" [import]
from nonexistent import foobar # E: Cannot find implementation or library stub for module named "nonexistent" [import]
import nonexistent2 # E: Cannot find implementation or library stub for module named "nonexistent2" [import]
from nonexistent3 import * # E: Cannot find implementation or library stub for module named "nonexistent3" [import]
from defusedxml import xyz # E: Cannot find implementation or library stub for module named "defusedxml" [import-not-found]
from nonexistent import foobar # E: Cannot find implementation or library stub for module named "nonexistent" [import-not-found]
import nonexistent2 # E: Cannot find implementation or library stub for module named "nonexistent2" [import-not-found]
from nonexistent3 import * # E: Cannot find implementation or library stub for module named "nonexistent3" [import-not-found]
from pkg import bad # E: Module "pkg" has no attribute "bad" [attr-defined]
from pkg.bad2 import bad3 # E: Cannot find implementation or library stub for module named "pkg.bad2" [import] \
from pkg.bad2 import bad3 # E: Cannot find implementation or library stub for module named "pkg.bad2" [import-not-found] \
# N: See https://mypy.readthedocs.io/en/stable/running_mypy.html#missing-imports
[file pkg/__init__.py]

Expand Down

0 comments on commit 78339b9

Please sign in to comment.