Skip to content

Commit e1796ad

Browse files
authored
PEP 484: only reexport for X as X forms (#9515)
See recent discussion on typing-sig leading to a PEP 484 amendment. Co-authored-by: hauntsaninja <>
1 parent b884a39 commit e1796ad

File tree

3 files changed

+39
-37
lines changed

3 files changed

+39
-37
lines changed

mypy/semanal.py

Lines changed: 34 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -1708,18 +1708,19 @@ def analyze_metaclass(self, defn: ClassDef) -> None:
17081708
def visit_import(self, i: Import) -> None:
17091709
self.statement = i
17101710
for id, as_id in i.ids:
1711+
# Modules imported in a stub file without using 'import X as X' won't get exported
1712+
# When implicit re-exporting is disabled, we have the same behavior as stubs.
1713+
use_implicit_reexport = not self.is_stub_file and self.options.implicit_reexport
17111714
if as_id is not None:
1712-
self.add_module_symbol(id, as_id, module_public=True, context=i)
1715+
base_id = id
1716+
imported_id = as_id
1717+
module_public = use_implicit_reexport or id.split(".")[-1] == as_id
17131718
else:
1714-
# Modules imported in a stub file without using 'as x' won't get exported
1715-
# When implicit re-exporting is disabled, we have the same behavior as stubs.
1716-
module_public = (
1717-
not self.is_stub_file
1718-
and self.options.implicit_reexport
1719-
)
1720-
base = id.split('.')[0]
1721-
self.add_module_symbol(base, base, module_public=module_public,
1722-
context=i, module_hidden=not module_public)
1719+
base_id = id.split('.')[0]
1720+
imported_id = base_id
1721+
module_public = use_implicit_reexport
1722+
self.add_module_symbol(base_id, imported_id, context=i, module_public=module_public,
1723+
module_hidden=not module_public)
17231724

17241725
def visit_import_from(self, imp: ImportFrom) -> None:
17251726
self.statement = imp
@@ -1762,17 +1763,21 @@ def visit_import_from(self, imp: ImportFrom) -> None:
17621763
if gvar:
17631764
self.add_symbol(imported_id, gvar, imp)
17641765
continue
1766+
1767+
# Modules imported in a stub file without using 'from Y import X as X' will
1768+
# not get exported.
1769+
# When implicit re-exporting is disabled, we have the same behavior as stubs.
1770+
use_implicit_reexport = not self.is_stub_file and self.options.implicit_reexport
1771+
module_public = use_implicit_reexport or (as_id is not None and id == as_id)
1772+
17651773
if node and not node.module_hidden:
1766-
self.process_imported_symbol(node, module_id, id, as_id, fullname, imp)
1774+
self.process_imported_symbol(
1775+
node, module_id, id, imported_id, fullname, module_public, context=imp
1776+
)
17671777
elif module and not missing_submodule:
17681778
# Target module exists but the imported name is missing or hidden.
17691779
self.report_missing_module_attribute(module_id, id, imported_id, imp)
17701780
else:
1771-
module_public = (
1772-
not self.is_stub_file
1773-
and self.options.implicit_reexport
1774-
or as_id is not None
1775-
)
17761781
# Import of a missing (sub)module.
17771782
self.add_unknown_imported_symbol(
17781783
imported_id, imp, target_name=fullname, module_public=module_public
@@ -1782,17 +1787,10 @@ def process_imported_symbol(self,
17821787
node: SymbolTableNode,
17831788
module_id: str,
17841789
id: str,
1785-
as_id: Optional[str],
1790+
imported_id: str,
17861791
fullname: str,
1792+
module_public: bool,
17871793
context: ImportBase) -> None:
1788-
imported_id = as_id or id
1789-
# 'from m import x as x' exports x in a stub file or when implicit
1790-
# re-exports are disabled.
1791-
module_public = (
1792-
not self.is_stub_file
1793-
and self.options.implicit_reexport
1794-
or as_id is not None
1795-
)
17961794
module_hidden = not module_public and fullname not in self.modules
17971795

17981796
if isinstance(node.node, PlaceholderNode):
@@ -4419,12 +4417,19 @@ def add_redefinition(self,
44194417
return
44204418
i += 1
44214419

4420+
def add_local(self, node: Union[Var, FuncDef, OverloadedFuncDef], context: Context) -> None:
4421+
"""Add local variable or function."""
4422+
assert self.is_func_scope()
4423+
name = node.name
4424+
node._fullname = name
4425+
self.add_symbol(name, node, context)
4426+
44224427
def add_module_symbol(self,
44234428
id: str,
44244429
as_id: str,
4425-
module_public: bool,
44264430
context: Context,
4427-
module_hidden: bool = False) -> None:
4431+
module_public: bool,
4432+
module_hidden: bool) -> None:
44284433
"""Add symbol that is a reference to a module object."""
44294434
if id in self.modules:
44304435
node = self.modules[id]
@@ -4436,19 +4441,12 @@ def add_module_symbol(self,
44364441
as_id, context, target_name=id, module_public=module_public
44374442
)
44384443

4439-
def add_local(self, node: Union[Var, FuncDef, OverloadedFuncDef], context: Context) -> None:
4440-
"""Add local variable or function."""
4441-
assert self.is_func_scope()
4442-
name = node.name
4443-
node._fullname = name
4444-
self.add_symbol(name, node, context)
4445-
44464444
def add_imported_symbol(self,
44474445
name: str,
44484446
node: SymbolTableNode,
44494447
context: Context,
4450-
module_public: bool = True,
4451-
module_hidden: bool = False) -> None:
4448+
module_public: bool,
4449+
module_hidden: bool) -> None:
44524450
"""Add an alias to an existing symbol through import."""
44534451
assert not module_hidden or not module_public
44544452
symbol = SymbolTableNode(node.kind, node.node,

mypy/test/teststubtest.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -578,7 +578,9 @@ def h(x: str): ...
578578
yield Case(stub="", runtime="__all__ += ['y']\ny = 5", error="y")
579579
yield Case(stub="", runtime="__all__ += ['g']\ndef g(): pass", error="g")
580580
# Here we should only check that runtime has B, since the stub explicitly re-exports it
581-
yield Case(stub="from mystery import A, B as B # type: ignore", runtime="", error="B")
581+
yield Case(
582+
stub="from mystery import A, B as B, C as D # type: ignore", runtime="", error="B"
583+
)
582584

583585
@collect_cases
584586
def test_name_mangling(self) -> Iterator[Case]:

test-data/unit/check-modules.test

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1818,6 +1818,7 @@ m = n # E: Cannot assign multiple modules to name 'm' without explicit 'types.M
18181818

18191819
[case testNoReExportFromStubs]
18201820
from stub import Iterable # E: Module 'stub' has no attribute 'Iterable'
1821+
from stub import D # E: Module 'stub' has no attribute 'D'
18211822
from stub import C
18221823

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

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

0 commit comments

Comments
 (0)