From ad94eeeaee9042f5c8cba57b1e8d336fd98f565c Mon Sep 17 00:00:00 2001 From: hauntsaninja Date: Sun, 19 Feb 2023 18:27:24 -0800 Subject: [PATCH 1/3] Use error subcodes to differentiate import errors Resolves #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 sub error code mechanism is new. 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. --- mypy/build.py | 11 ++++++++++- mypy/errorcodes.py | 6 ++++++ 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/mypy/build.py b/mypy/build.py index a4817d1866c7..e444a09b785c 100644 --- a/mypy/build.py +++ b/mypy/build.py @@ -2776,7 +2776,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 diff --git a/mypy/errorcodes.py b/mypy/errorcodes.py index 3d8b1096ed4f..973871ecfb8d 100644 --- a/mypy/errorcodes.py +++ b/mypy/errorcodes.py @@ -96,6 +96,12 @@ def __str__(self) -> str: 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" From 26a8fcf8ba7765194b8e9dfabf8108fe1c6528fe Mon Sep 17 00:00:00 2001 From: hauntsaninja Date: Sun, 19 Feb 2023 19:34:33 -0800 Subject: [PATCH 2/3] fix tests --- mypy/errors.py | 8 ++++++-- test-data/unit/check-errorcodes.test | 14 +++++++------- 2 files changed, 13 insertions(+), 9 deletions(-) diff --git a/mypy/errors.py b/mypy/errors.py index 2c2c1e5ca227..862f1f52ec8a 100644 --- a/mypy/errors.py +++ b/mypy/errors.py @@ -8,7 +8,7 @@ from typing_extensions import Final, Literal, TypeAlias as _TypeAlias from mypy import errorcodes as codes -from mypy.errorcodes import IMPORT, ErrorCode +from mypy.errorcodes import IMPORT, IMPORT_UNTYPED, IMPORT_NOT_FOUND, ErrorCode from mypy.message_registry import ErrorMessage from mypy.options import Options from mypy.scope import Scope @@ -491,7 +491,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 diff --git a/test-data/unit/check-errorcodes.test b/test-data/unit/check-errorcodes.test index 8b3567ab7cf6..9fa6856eecd6 100644 --- a/test-data/unit/check-errorcodes.test +++ b/test-data/unit/check-errorcodes.test @@ -181,7 +181,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] @@ -209,7 +209,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] @@ -520,12 +520,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] From 46738b4ca0635a562e1d2c4b74a539e779fcc972 Mon Sep 17 00:00:00 2001 From: hauntsaninja Date: Sun, 19 Feb 2023 19:39:18 -0800 Subject: [PATCH 3/3] sort --- mypy/errors.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mypy/errors.py b/mypy/errors.py index 862f1f52ec8a..43eecc1542bb 100644 --- a/mypy/errors.py +++ b/mypy/errors.py @@ -8,7 +8,7 @@ from typing_extensions import Final, Literal, TypeAlias as _TypeAlias from mypy import errorcodes as codes -from mypy.errorcodes import IMPORT, IMPORT_UNTYPED, IMPORT_NOT_FOUND, 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