Skip to content

Commit f68fc74

Browse files
JukkaLGuido van Rossum
authored and
Guido van Rossum
committed
New semantic analyzer: fix ambiguity between submodule and local definition (#7017)
Now imports are biased to target the submodule of current package target, but elsewhere a local definition with the same name as a submodule takes precedence. Even though this doesn't quite reflect what happens at runtime, this seems to cover the typical use cases well, and the implementation is simple. Fixes #6828.
1 parent 8bd2fdf commit f68fc74

File tree

2 files changed

+81
-2
lines changed

2 files changed

+81
-2
lines changed

mypy/newsemanal/semanal.py

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1634,10 +1634,20 @@ def visit_import_from(self, imp: ImportFrom) -> None:
16341634
import_id = self.correct_relative_import(imp)
16351635
module = self.modules.get(import_id)
16361636
for id, as_id in imp.names:
1637-
node = module.names.get(id) if module else None
1637+
possible_module_id = import_id + '.' + id
1638+
if module is None:
1639+
node = None
1640+
elif import_id == self.cur_mod_id and possible_module_id in self.modules:
1641+
# Submodule takes precedence over definition in surround package, for
1642+
# compatibility with runtime semantics in typical use cases. This
1643+
# could more precisely model runtime semantics by taking into account
1644+
# the line number beyond which the local definition should take
1645+
# precedence, but doesn't seem to be important in most use cases.
1646+
node = SymbolTableNode(GDEF, self.modules[possible_module_id])
1647+
else:
1648+
node = module.names.get(id)
16381649

16391650
missing = False
1640-
possible_module_id = import_id + '.' + id
16411651
imported_id = as_id or id
16421652

16431653
# If the module does not contain a symbol with the name 'id',

test-data/unit/check-newsemanal.test

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2516,3 +2516,72 @@ def get() -> int: ...
25162516
import typing
25172517
t = typing.typevar('t') # E: Module has no attribute "typevar"
25182518
[builtins fixtures/module.pyi]
2519+
2520+
[case testNewAnalyzerImportFromTopLevelFunction]
2521+
import a.b # This works at runtime
2522+
reveal_type(a.b) # N
2523+
[file a/__init__.py]
2524+
from .b import B
2525+
from . import b as c
2526+
def b() -> None: pass
2527+
reveal_type(b) # N
2528+
reveal_type(c.B()) # N
2529+
x: Forward
2530+
class Forward:
2531+
...
2532+
2533+
[file a/b.py]
2534+
class B: ...
2535+
[builtins fixtures/module.pyi]
2536+
2537+
[out]
2538+
tmp/a/__init__.py:4: note: Revealed type is 'def ()'
2539+
tmp/a/__init__.py:5: note: Revealed type is 'a.b.B'
2540+
main:2: note: Revealed type is 'def ()'
2541+
2542+
[case testNewAnalyzerImportFromTopLevelAlias]
2543+
import a.b # This works at runtime
2544+
reveal_type(a.b) # N
2545+
[file a/__init__.py]
2546+
from .b import B
2547+
from . import b as c
2548+
b = int
2549+
y: b
2550+
reveal_type(y) # N
2551+
reveal_type(c.B) # N
2552+
x: Forward
2553+
class Forward:
2554+
...
2555+
2556+
[file a/b.py]
2557+
class B: ...
2558+
[builtins fixtures/module.pyi]
2559+
2560+
[out]
2561+
tmp/a/__init__.py:5: note: Revealed type is 'builtins.int'
2562+
tmp/a/__init__.py:6: note: Revealed type is 'def () -> a.b.B'
2563+
main:2: note: Revealed type is 'def () -> builtins.int'
2564+
2565+
[case testNewAnalyzerImportAmbiguousWithTopLevelFunction]
2566+
import a.b # This works at runtime
2567+
x: a.b.B # E
2568+
reveal_type(a.b) # N
2569+
[file a/__init__.py]
2570+
import a.b
2571+
import a.b as c
2572+
def b() -> None: pass
2573+
reveal_type(b) # N
2574+
reveal_type(c.B()) # N
2575+
x: Forward
2576+
class Forward:
2577+
...
2578+
2579+
[file a/b.py]
2580+
class B: ...
2581+
[builtins fixtures/module.pyi]
2582+
2583+
[out]
2584+
tmp/a/__init__.py:4: note: Revealed type is 'def ()'
2585+
tmp/a/__init__.py:5: note: Revealed type is 'a.b.B'
2586+
main:2: error: Name 'a.b.B' is not defined
2587+
main:3: note: Revealed type is 'def ()'

0 commit comments

Comments
 (0)