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
70 changes: 34 additions & 36 deletions mypy/semanal.py
Original file line number Diff line number Diff line change
Expand Up @@ -1708,18 +1708,19 @@ def analyze_metaclass(self, defn: ClassDef) -> None:
def visit_import(self, i: Import) -> None:
self.statement = i
for id, as_id in i.ids:
# Modules imported in a stub file without using 'import X as X' won't get exported
# When implicit re-exporting is disabled, we have the same behavior as stubs.
use_implicit_reexport = not self.is_stub_file and self.options.implicit_reexport
if as_id is not None:
self.add_module_symbol(id, as_id, module_public=True, context=i)
base_id = id
imported_id = as_id
module_public = use_implicit_reexport or id.split(".")[-1] == as_id
else:
# Modules imported in a stub file without using 'as x' won't get exported
# When implicit re-exporting is disabled, we have the same behavior as stubs.
module_public = (
not self.is_stub_file
and self.options.implicit_reexport
)
base = id.split('.')[0]
self.add_module_symbol(base, base, module_public=module_public,
context=i, module_hidden=not module_public)
base_id = id.split('.')[0]
imported_id = base_id
module_public = use_implicit_reexport
self.add_module_symbol(base_id, imported_id, context=i, module_public=module_public,
module_hidden=not module_public)

def visit_import_from(self, imp: ImportFrom) -> None:
self.statement = imp
Expand Down Expand Up @@ -1762,17 +1763,21 @@ def visit_import_from(self, imp: ImportFrom) -> None:
if gvar:
self.add_symbol(imported_id, gvar, imp)
continue

# Modules imported in a stub file without using 'from Y import X as X' will
# not get exported.
# When implicit re-exporting is disabled, we have the same behavior as stubs.
use_implicit_reexport = not self.is_stub_file and self.options.implicit_reexport
module_public = use_implicit_reexport or (as_id is not None and id == as_id)

if node and not node.module_hidden:
self.process_imported_symbol(node, module_id, id, as_id, fullname, imp)
self.process_imported_symbol(
node, module_id, id, imported_id, fullname, module_public, context=imp
)
elif module and not missing_submodule:
# Target module exists but the imported name is missing or hidden.
self.report_missing_module_attribute(module_id, id, imported_id, imp)
else:
module_public = (
not self.is_stub_file
and self.options.implicit_reexport
or as_id is not None
)
# Import of a missing (sub)module.
self.add_unknown_imported_symbol(
imported_id, imp, target_name=fullname, module_public=module_public
Expand All @@ -1782,17 +1787,10 @@ def process_imported_symbol(self,
node: SymbolTableNode,
module_id: str,
id: str,
as_id: Optional[str],
imported_id: str,
fullname: str,
module_public: bool,
context: ImportBase) -> None:
imported_id = as_id or id
# 'from m import x as x' exports x in a stub file or when implicit
# re-exports are disabled.
module_public = (
not self.is_stub_file
and self.options.implicit_reexport
or as_id is not None
)
module_hidden = not module_public and fullname not in self.modules

if isinstance(node.node, PlaceholderNode):
Expand Down Expand Up @@ -4419,12 +4417,19 @@ def add_redefinition(self,
return
i += 1

def add_local(self, node: Union[Var, FuncDef, OverloadedFuncDef], context: Context) -> None:
"""Add local variable or function."""
assert self.is_func_scope()
name = node.name
node._fullname = name
self.add_symbol(name, node, context)

def add_module_symbol(self,
id: str,
as_id: str,
module_public: bool,
context: Context,
module_hidden: bool = False) -> None:
module_public: bool,
module_hidden: bool) -> None:
"""Add symbol that is a reference to a module object."""
if id in self.modules:
node = self.modules[id]
Expand All @@ -4436,19 +4441,12 @@ def add_module_symbol(self,
as_id, context, target_name=id, module_public=module_public
)

def add_local(self, node: Union[Var, FuncDef, OverloadedFuncDef], context: Context) -> None:
"""Add local variable or function."""
assert self.is_func_scope()
name = node.name
node._fullname = name
self.add_symbol(name, node, context)

def add_imported_symbol(self,
name: str,
node: SymbolTableNode,
context: Context,
module_public: bool = True,
module_hidden: bool = False) -> None:
module_public: bool,
module_hidden: bool) -> None:
"""Add an alias to an existing symbol through import."""
assert not module_hidden or not module_public
symbol = SymbolTableNode(node.kind, node.node,
Expand Down
4 changes: 3 additions & 1 deletion mypy/test/teststubtest.py
Original file line number Diff line number Diff line change
Expand Up @@ -578,7 +578,9 @@ def h(x: str): ...
yield Case(stub="", runtime="__all__ += ['y']\ny = 5", error="y")
yield Case(stub="", runtime="__all__ += ['g']\ndef g(): pass", error="g")
# Here we should only check that runtime has B, since the stub explicitly re-exports it
yield Case(stub="from mystery import A, B as B # type: ignore", runtime="", error="B")
yield Case(
stub="from mystery import A, B as B, C as D # type: ignore", runtime="", error="B"
)

@collect_cases
def test_name_mangling(self) -> Iterator[Case]:
Expand Down
2 changes: 2 additions & 0 deletions test-data/unit/check-modules.test
Original file line number Diff line number Diff line change
Expand Up @@ -1818,6 +1818,7 @@ m = n # E: Cannot assign multiple modules to name 'm' without explicit 'types.M

[case testNoReExportFromStubs]
from stub import Iterable # E: Module 'stub' has no attribute 'Iterable'
from stub import D # E: Module 'stub' has no attribute 'D'
from stub import C

c = C()
Expand All @@ -1828,6 +1829,7 @@ reveal_type(it) # N: Revealed type is 'Any'
[file stub.pyi]
from typing import Iterable
from substub import C as C
from substub import C as D

def fun(x: Iterable[str]) -> Iterable[int]: pass

Expand Down