diff --git a/docs/source/common_issues.rst b/docs/source/common_issues.rst index 956bf38a4943..0b72ee5fc7a6 100644 --- a/docs/source/common_issues.rst +++ b/docs/source/common_issues.rst @@ -36,6 +36,13 @@ error: The second line is now fine, since the ignore comment causes the name ``frobnicate`` to get an implicit ``Any`` type. +.. note:: + + The ``# type: ignore`` comment will only assign the implicit ``Any`` + type if mypy cannot find information about that particular module. So, + if we did have a stub available for ``frobnicate`` then mypy would + ignore the ``# type: ignore`` comment and typecheck the stub as usual. + Types of empty collections -------------------------- diff --git a/mypy/build.py b/mypy/build.py index 7dc88a2db939..9e7af6f80931 100644 --- a/mypy/build.py +++ b/mypy/build.py @@ -416,23 +416,17 @@ def parse_file(self, id: str, path: str, source: str) -> MypyFile: tree = parse(source, path, self.errors, options=self.options) tree._fullname = id - # We don't want to warn about 'type: ignore' comments on - # imports, but we're about to modify tree.imports, so grab - # these first. - import_lines = set(node.line for node in tree.imports) - - # Skip imports that have been ignored (so that we can ignore a C extension module without - # stub, for example), except for 'from x import *', because we wouldn't be able to - # determine which names should be defined unless we process the module. We can still - # ignore errors such as redefinitions when using the latter form. - imports = [node for node in tree.imports - if node.line not in tree.ignored_lines or isinstance(node, ImportAll)] - tree.imports = imports - if self.errors.num_messages() != num_errs: self.log("Bailing due to parse errors") self.errors.raise_error() + # We don't want to warn about unused 'type: ignore' comments on imports. + # That way, if we want to conditionally import a module that is valid + # only on one particular platform, we can silence the import so we don't + # get spurious error messages when mypy tries checking it. For example, + # we might want to conditionally import a Python 2 only module while + # checking the file as Python 3. + import_lines = set(node.line for node in tree.imports) self.errors.set_file_ignored_lines(path, tree.ignored_lines) self.errors.mark_file_ignored_lines_used(path, import_lines) return tree diff --git a/test-data/unit/check-ignore.test b/test-data/unit/check-ignore.test index 4f5e04de3ab6..98c6e05bd363 100644 --- a/test-data/unit/check-ignore.test +++ b/test-data/unit/check-ignore.test @@ -36,7 +36,23 @@ x # E: Name 'x' is not defined import m # type: ignore from m import a # type: ignore [file m.py] -+ # A parse error, but we shouldn't even parse m.py ++ +[out] +main:1: note: In module imported here: +tmp/m.py:1: error: Parse error before end of line + +[case testIgnoreAppliesOnlyToMissing] +import a # type: ignore +import b # type: ignore +reveal_type(a.foo) # E: Revealed type is 'Any' +reveal_type(b.foo) # E: Revealed type is 'builtins.int' +a.bar() +b.bar() # E: "module" has no attribute "bar" + +[file b.py] +foo = 3 + +[builtins fixtures/module_all.py] [out] [case testIgnoreImportStarFromBadModule] diff --git a/test-data/unit/check-incremental.test b/test-data/unit/check-incremental.test index 43592d6db2c7..ec1c55510c99 100644 --- a/test-data/unit/check-incremental.test +++ b/test-data/unit/check-incremental.test @@ -185,3 +185,65 @@ from parent import b [stale parent.a] [out] + +[case testIncrementalWithTypeIgnoreOnDirectImport] +import a, b + +[file a.py] +import b # type: ignore + +[file b.py] +import c + +[file c.py] + +[stale] +[out] + +[case testIncrementalWithTypeIgnoreOnImportFrom] +import a, b + +[file a.py] +from b import something # type: ignore + +[file b.py] +import c +something = 3 + +[file c.py] + +[stale] +[out] + +[case testIncrementalWithPartialTypeIgnore] +import a # type: ignore +import a.b + +[file a/__init__.py] + +[file a/b.py] + +[stale] +[out] + +[case testIncrementalAnyIsDifferentFromIgnore] +import b + +[file b.py] +from typing import Any +import a.b + +[file b.py.next] +from typing import Any + +a = 3 # type: Any +import a.b + +[file a/__init__.py] + +[file a/b.py] + +[stale b] +[out] +main:1: note: In module imported here: +tmp/b.py:4: error: Name 'a' already defined