Skip to content

Make mypy still check imports with # type: ignore comments if the import exists #1912

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 6 commits into from
Jul 19, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions docs/source/common_issues.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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
--------------------------

Expand Down
20 changes: 7 additions & 13 deletions mypy/build.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
18 changes: 17 additions & 1 deletion test-data/unit/check-ignore.test
Original file line number Diff line number Diff line change
Expand Up @@ -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]
Expand Down
62 changes: 62 additions & 0 deletions test-data/unit/check-incremental.test
Original file line number Diff line number Diff line change
Expand Up @@ -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