Skip to content

Commit e9cf4f2

Browse files
bluetechilevkivskyi
authored andcommitted
Don't reexport star-imported symbols when implicit reexports are disabled (python#7361)
Based on python#7099. Currently, in regular files, symbols imported into module A with `from B import *` are considered explicitly exported from module A as well, i.e. they are considered part of module's A public API. This behavior makes sense in stub files as a shorthand (and is specified in PEP 484), but for regular files I think it is better to be explicit: add the symbols to `__all__`. Fixes python#7042 Further discussion: python#7042 (comment)
1 parent 9bc4317 commit e9cf4f2

File tree

4 files changed

+60
-7
lines changed

4 files changed

+60
-7
lines changed

docs/source/command_line.rst

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -429,15 +429,18 @@ of the above sections.
429429
``--no-implicit-reexport``
430430
By default, imported values to a module are treated as exported and mypy allows
431431
other modules to import them. This flag changes the behavior to not re-export unless
432-
the item is imported using from-as. Note this is always treated as enabled for
433-
stub files. For example:
432+
the item is imported using from-as or is included in ``__all__``. Note this is
433+
always treated as enabled for stub files. For example:
434434

435435
.. code-block:: python
436436
437437
# This won't re-export the value
438438
from foo import bar
439439
# This will re-export it as bar and allow other modules to import it
440440
from foo import bar as bar
441+
# This will also re-export bar
442+
from foo import bar
443+
__all__ = ['bar']
441444
442445
443446
``--strict-equality``

docs/source/config_file.rst

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -310,15 +310,18 @@ Miscellaneous strictness flags
310310
``implicit_reexport`` (bool, default True)
311311
By default, imported values to a module are treated as exported and mypy allows
312312
other modules to import them. When false, mypy will not re-export unless
313-
the item is imported using from-as. Note that mypy treats stub files as if this
314-
is always disabled. For example:
313+
the item is imported using from-as or is included in ``__all__``. Note that mypy
314+
treats stub files as if this is always disabled. For example:
315315

316316
.. code-block:: python
317317
318318
# This won't re-export the value
319319
from foo import bar
320320
# This will re-export it as bar and allow other modules to import it
321321
from foo import bar as bar
322+
# This will also re-export bar
323+
from foo import bar
324+
__all__ = ['bar']
322325
323326
``strict_equality`` (bool, default False)
324327
Prohibit equality checks, identity checks, and container checks between

mypy/semanal.py

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -463,10 +463,16 @@ def add_builtin_aliases(self, tree: MypyFile) -> None:
463463
del tree.names[name]
464464

465465
def adjust_public_exports(self) -> None:
466-
"""Make variables not in __all__ not be public"""
466+
"""Adjust the module visibility of globals due to __all__."""
467467
if '__all__' in self.globals:
468468
for name, g in self.globals.items():
469-
if name not in self.all_exports:
469+
# Being included in __all__ explicitly exports and makes public.
470+
if name in self.all_exports:
471+
g.module_public = True
472+
g.module_hidden = False
473+
# But when __all__ is defined, and a symbol is not included in it,
474+
# it cannot be public.
475+
else:
470476
g.module_public = False
471477

472478
@contextmanager
@@ -1863,7 +1869,12 @@ def visit_import_all(self, i: ImportAll) -> None:
18631869
if self.process_import_over_existing_name(
18641870
name, existing_symbol, node, i):
18651871
continue
1866-
self.add_imported_symbol(name, node, i)
1872+
# In stub files, `from x import *` always reexports the symbols.
1873+
# In regular files, only if implicit reexports are enabled.
1874+
module_public = self.is_stub_file or self.options.implicit_reexport
1875+
self.add_imported_symbol(name, node, i,
1876+
module_public=module_public,
1877+
module_hidden=not module_public)
18671878
else:
18681879
# Don't add any dummy symbols for 'from x import *' if 'x' is unknown.
18691880
pass

test-data/unit/check-flags.test

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1145,6 +1145,42 @@ from other_module_1 import a
11451145
[out]
11461146
main:2: error: Module 'other_module_2' has no attribute 'a'
11471147

1148+
[case testNoImplicitReexportRespectsAll]
1149+
# flags: --no-implicit-reexport
1150+
from other_module_2 import a
1151+
from other_module_2 import b
1152+
[file other_module_1.py]
1153+
a = 5
1154+
b = 6
1155+
[file other_module_2.py]
1156+
from other_module_1 import a, b
1157+
__all__ = ('b',)
1158+
[out]
1159+
main:2: error: Module 'other_module_2' has no attribute 'a'
1160+
1161+
[case testNoImplicitReexportStarConsideredImplicit]
1162+
# flags: --no-implicit-reexport
1163+
from other_module_2 import a
1164+
[file other_module_1.py]
1165+
a = 5
1166+
[file other_module_2.py]
1167+
from other_module_1 import *
1168+
[out]
1169+
main:2: error: Module 'other_module_2' has no attribute 'a'
1170+
1171+
[case testNoImplicitReexportStarCanBeReexportedWithAll]
1172+
# flags: --no-implicit-reexport
1173+
from other_module_2 import a
1174+
from other_module_2 import b
1175+
[file other_module_1.py]
1176+
a = 5
1177+
b = 6
1178+
[file other_module_2.py]
1179+
from other_module_1 import *
1180+
__all__ = ('b',)
1181+
[out]
1182+
main:2: error: Module 'other_module_2' has no attribute 'a'
1183+
11481184
[case testNoImplicitReexportMypyIni]
11491185
# flags: --config-file tmp/mypy.ini
11501186
from other_module_2 import a

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy