Skip to content
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
14 changes: 12 additions & 2 deletions mypy/newsemanal/semanal.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we still need the special case couple dozen lines below (can't comment there) that checks for submodules added to parent module? (The one that has # Special case: allow replacing submodules with variables.)

It seems to be it should be safe to remove it now.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll look at it later and create a separate PR if it makes sense.


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',
Expand Down
69 changes: 69 additions & 0 deletions test-data/unit/check-newsemanal.test
Original file line number Diff line number Diff line change
Expand Up @@ -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 ()'