diff --git a/mypy/newsemanal/semanal.py b/mypy/newsemanal/semanal.py index 4ef0fc44f8ddf..a8986c5444c8c 100644 --- a/mypy/newsemanal/semanal.py +++ b/mypy/newsemanal/semanal.py @@ -1628,10 +1628,20 @@ def visit_import_from(self, imp: ImportFrom) -> None: import_id = self.correct_relative_import(imp) module = self.modules.get(import_id) for id, as_id in imp.names: - node = module.names.get(id) if module else None + possible_module_id = import_id + '.' + id + if module is None: + node = None + elif import_id == self.cur_mod_id and possible_module_id in self.modules: + # Submodule takes precedence over definition in surround package, for + # compatibility with runtime semantics in typical use cases. This + # could more precisely model runtime semantics by taking into account + # the line number beyond which the local definition should take + # precedence, but doesn't seem to be important in most use cases. + node = SymbolTableNode(GDEF, self.modules[possible_module_id]) + else: + node = module.names.get(id) missing = False - possible_module_id = import_id + '.' + id imported_id = as_id or id # If the module does not contain a symbol with the name 'id', diff --git a/test-data/unit/check-newsemanal.test b/test-data/unit/check-newsemanal.test index 528304cb3cb51..370b93d641c0a 100644 --- a/test-data/unit/check-newsemanal.test +++ b/test-data/unit/check-newsemanal.test @@ -2516,3 +2516,72 @@ def get() -> int: ... import typing t = typing.typevar('t') # E: Module has no attribute "typevar" [builtins fixtures/module.pyi] + +[case testNewAnalyzerImportFromTopLevelFunction] +import a.b # This works at runtime +reveal_type(a.b) # N +[file a/__init__.py] +from .b import B +from . import b as c +def b() -> None: pass +reveal_type(b) # N +reveal_type(c.B()) # N +x: Forward +class Forward: + ... + +[file a/b.py] +class B: ... +[builtins fixtures/module.pyi] + +[out] +tmp/a/__init__.py:4: note: Revealed type is 'def ()' +tmp/a/__init__.py:5: note: Revealed type is 'a.b.B' +main:2: note: Revealed type is 'def ()' + +[case testNewAnalyzerImportFromTopLevelAlias] +import a.b # This works at runtime +reveal_type(a.b) # N +[file a/__init__.py] +from .b import B +from . import b as c +b = int +y: b +reveal_type(y) # N +reveal_type(c.B) # N +x: Forward +class Forward: + ... + +[file a/b.py] +class B: ... +[builtins fixtures/module.pyi] + +[out] +tmp/a/__init__.py:5: note: Revealed type is 'builtins.int' +tmp/a/__init__.py:6: note: Revealed type is 'def () -> a.b.B' +main:2: note: Revealed type is 'def () -> builtins.int' + +[case testNewAnalyzerImportAmbiguousWithTopLevelFunction] +import a.b # This works at runtime +x: a.b.B # E +reveal_type(a.b) # N +[file a/__init__.py] +import a.b +import a.b as c +def b() -> None: pass +reveal_type(b) # N +reveal_type(c.B()) # N +x: Forward +class Forward: + ... + +[file a/b.py] +class B: ... +[builtins fixtures/module.pyi] + +[out] +tmp/a/__init__.py:4: note: Revealed type is 'def ()' +tmp/a/__init__.py:5: note: Revealed type is 'a.b.B' +main:2: error: Name 'a.b.B' is not defined +main:3: note: Revealed type is 'def ()'