From 053fd126e6af3b9f04d0ec35d3a048e21059481f Mon Sep 17 00:00:00 2001 From: Michael Lee Date: Mon, 18 Jul 2016 16:43:18 -0700 Subject: [PATCH 1/3] Add test case to repro bug --- test-data/unit/check-incremental.test | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/test-data/unit/check-incremental.test b/test-data/unit/check-incremental.test index 43592d6db2c7..51f05257c5fd 100644 --- a/test-data/unit/check-incremental.test +++ b/test-data/unit/check-incremental.test @@ -185,3 +185,18 @@ from parent import b [stale parent.a] [out] + +[case testIncrementalWithTypeIgnore] +import a, b + +[file a.py] +import b # type: ignore + +[file b.py] +import c + +[file c.py] + +[stale] +[out] + From 5a69baff975de05d809b8fe33629b4e4c46037f4 Mon Sep 17 00:00:00 2001 From: Michael Lee Date: Mon, 18 Jul 2016 22:00:36 -0700 Subject: [PATCH 2/3] Refine test case to cover more cases --- test-data/unit/check-incremental.test | 49 ++++++++++++++++++++++++++- 1 file changed, 48 insertions(+), 1 deletion(-) diff --git a/test-data/unit/check-incremental.test b/test-data/unit/check-incremental.test index 51f05257c5fd..ec1c55510c99 100644 --- a/test-data/unit/check-incremental.test +++ b/test-data/unit/check-incremental.test @@ -186,7 +186,7 @@ from parent import b [stale parent.a] [out] -[case testIncrementalWithTypeIgnore] +[case testIncrementalWithTypeIgnoreOnDirectImport] import a, b [file a.py] @@ -200,3 +200,50 @@ import c [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 From 49d1ae5a8003662d047ca22a52f2c9c93adc261c Mon Sep 17 00:00:00 2001 From: Michael Lee Date: Mon, 18 Jul 2016 22:03:21 -0700 Subject: [PATCH 3/3] Make semantic analyzer mark ignored module imports Previously, the semantic analyzer seemed to always mark import symbols as being of kind MODULE_REF during the third pass and did not preserve whether the module was previously marked with the `# type: ignore` comment or not. This caused a contradiction in some cases when mypy was run in incremental mode: suppose file a.py imported file b.py, and marked that import with the ignore annotation. Since the import is being ignored, mypy would not register that import as a dependency, but since the import (during the previous run) was marked as a MODULE_REF, mypy would attempt to load that module anyways in some cases. This commit fixes https://github.com/python/mypy/issues/1904. --- mypy/semanal.py | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/mypy/semanal.py b/mypy/semanal.py index 99cba07d0e4f..8cf828cc86a3 100644 --- a/mypy/semanal.py +++ b/mypy/semanal.py @@ -874,33 +874,42 @@ def bind_class_type_variables_in_symbol_table( return nodes def visit_import(self, i: Import) -> None: + ignored = i.line in self.cur_mod_node.ignored_lines for id, as_id in i.ids: if as_id is not None: - self.add_module_symbol(id, as_id, module_public=True, context=i) + self.add_module_symbol(id, as_id, module_public=True, + module_ignored=ignored, context=i) else: # Modules imported in a stub file without using 'as x' won't get exported when # doing 'from m import *'. module_public = not self.is_stub_file base = id.split('.')[0] self.add_module_symbol(base, base, module_public=module_public, - context=i) + module_ignored=ignored, context=i) - def add_module_symbol(self, id: str, as_id: str, module_public: bool, + def add_module_symbol(self, id: str, as_id: str, module_public: bool, module_ignored: bool, context: Context) -> None: if id in self.modules: m = self.modules[id] - self.add_symbol(as_id, SymbolTableNode(MODULE_REF, m, self.cur_mod_id, + kind = UNBOUND_IMPORTED if module_ignored else MODULE_REF + self.add_symbol(as_id, SymbolTableNode(kind, m, self.cur_mod_id, module_public=module_public), context) else: self.add_unknown_symbol(as_id, context) def visit_import_from(self, imp: ImportFrom) -> None: import_id = self.correct_relative_import(imp) + ignored = imp.line in self.cur_mod_node.ignored_lines if import_id in self.modules: module = self.modules[import_id] for id, as_id in imp.names: node = module.names.get(id) - if node and node.kind != UNBOUND_IMPORTED: + module_public = not self.is_stub_file or as_id is not None + if ignored: + symbol = SymbolTableNode(UNBOUND_IMPORTED, None, + self.cur_mod_id, module_public=module_public) + self.add_symbol(as_id or id, symbol, imp) + elif node and node.kind != UNBOUND_IMPORTED: node = self.normalize_type_alias(node, imp) if not node: return @@ -912,7 +921,6 @@ def visit_import_from(self, imp: ImportFrom) -> None: imported_id, existing_symbol, node, imp): continue # 'from m import x as x' exports x in a stub file. - module_public = not self.is_stub_file or as_id is not None symbol = SymbolTableNode(node.kind, node.node, self.cur_mod_id, node.type_override, @@ -956,7 +964,7 @@ def normalize_type_alias(self, node: SymbolTableNode, # Node refers to an aliased type such as typing.List; normalize. node = self.lookup_qualified(type_aliases[node.fullname], ctx) if node.fullname == 'typing.DefaultDict': - self.add_module_symbol('collections', '__mypy_collections__', False, ctx) + self.add_module_symbol('collections', '__mypy_collections__', False, False, ctx) node = self.lookup_qualified('__mypy_collections__.defaultdict', ctx) return node