Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Don't reexport star-imported symbols when implicit reexports are disabled #7361

Merged
merged 3 commits into from Aug 19, 2019
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.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
7 changes: 5 additions & 2 deletions docs/source/command_line.rst
Expand Up @@ -429,15 +429,18 @@ of the above sections.
``--no-implicit-reexport``
By default, imported values to a module are treated as exported and mypy allows
other modules to import them. This flag changes the behavior to not re-export unless
the item is imported using from-as. Note this is always treated as enabled for
stub files. For example:
the item is imported using from-as or is included in ``__all__``. Note this is
always treated as enabled for stub files. For example:

.. code-block:: python

# This won't re-export the value
from foo import bar
# This will re-export it as bar and allow other modules to import it
from foo import bar as bar
# This will also re-export bar
from foo import bar
__all__ = ['bar']


``--strict-equality``
Expand Down
7 changes: 5 additions & 2 deletions docs/source/config_file.rst
Expand Up @@ -310,15 +310,18 @@ Miscellaneous strictness flags
``implicit_reexport`` (bool, default True)
By default, imported values to a module are treated as exported and mypy allows
other modules to import them. When false, mypy will not re-export unless
the item is imported using from-as. Note that mypy treats stub files as if this
is always disabled. For example:
the item is imported using from-as or is included in ``__all__``. Note that mypy
treats stub files as if this is always disabled. For example:

.. code-block:: python

# This won't re-export the value
from foo import bar
# This will re-export it as bar and allow other modules to import it
from foo import bar as bar
# This will also re-export bar
from foo import bar
__all__ = ['bar']

``strict_equality`` (bool, default False)
Prohibit equality checks, identity checks, and container checks between
Expand Down
17 changes: 14 additions & 3 deletions mypy/semanal.py
Expand Up @@ -463,10 +463,16 @@ def add_builtin_aliases(self, tree: MypyFile) -> None:
del tree.names[name]

def adjust_public_exports(self) -> None:
"""Make variables not in __all__ not be public"""
"""Adjust the module visibility of globals due to __all__."""
if '__all__' in self.globals:
for name, g in self.globals.items():
if name not in self.all_exports:
# Being included in __all__ explicitly exports and makes public.
if name in self.all_exports:
g.module_public = True
g.module_hidden = False
# But when __all__ is defined, and a symbol is not included in it,
# it cannot be public.
else:
g.module_public = False

@contextmanager
Expand Down Expand Up @@ -1863,7 +1869,12 @@ def visit_import_all(self, i: ImportAll) -> None:
if self.process_import_over_existing_name(
name, existing_symbol, node, i):
continue
self.add_imported_symbol(name, node, i)
# In stub files, `from x import *` always reexports the symbols.
# In regular files, only if implicit reexports are enabled.
module_public = self.is_stub_file or self.options.implicit_reexport
self.add_imported_symbol(name, node, i,
module_public=module_public,
module_hidden=not module_public)
else:
# Don't add any dummy symbols for 'from x import *' if 'x' is unknown.
pass
Expand Down
36 changes: 36 additions & 0 deletions test-data/unit/check-flags.test
Expand Up @@ -1145,6 +1145,42 @@ from other_module_1 import a
[out]
main:2: error: Module 'other_module_2' has no attribute 'a'

[case testNoImplicitReexportRespectsAll]
# flags: --no-implicit-reexport
from other_module_2 import a
from other_module_2 import b
[file other_module_1.py]
a = 5
b = 6
[file other_module_2.py]
from other_module_1 import a, b
__all__ = ('b',)
[out]
main:2: error: Module 'other_module_2' has no attribute 'a'

[case testNoImplicitReexportStarConsideredImplicit]
# flags: --no-implicit-reexport
from other_module_2 import a
[file other_module_1.py]
a = 5
[file other_module_2.py]
from other_module_1 import *
[out]
main:2: error: Module 'other_module_2' has no attribute 'a'

[case testNoImplicitReexportStarCanBeReexportedWithAll]
# flags: --no-implicit-reexport
from other_module_2 import a
from other_module_2 import b
[file other_module_1.py]
a = 5
b = 6
[file other_module_2.py]
from other_module_1 import *
__all__ = ('b',)
[out]
main:2: error: Module 'other_module_2' has no attribute 'a'

[case testNoImplicitReexportMypyIni]
# flags: --config-file tmp/mypy.ini
from other_module_2 import a
Expand Down