diff --git a/docs/source/command_line.rst b/docs/source/command_line.rst index a9b1dda6832f..cd304f3a67da 100644 --- a/docs/source/command_line.rst +++ b/docs/source/command_line.rst @@ -429,8 +429,8 @@ 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 @@ -438,6 +438,9 @@ of the above sections. 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`` diff --git a/docs/source/config_file.rst b/docs/source/config_file.rst index d01fa4893644..94df11682a84 100644 --- a/docs/source/config_file.rst +++ b/docs/source/config_file.rst @@ -310,8 +310,8 @@ 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 @@ -319,6 +319,9 @@ Miscellaneous strictness flags 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 diff --git a/mypy/semanal.py b/mypy/semanal.py index 2664d40ddb2f..ea22b4243ca2 100644 --- a/mypy/semanal.py +++ b/mypy/semanal.py @@ -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 @@ -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 diff --git a/test-data/unit/check-flags.test b/test-data/unit/check-flags.test index 7e8cf53c8c89..f67ffa48372c 100644 --- a/test-data/unit/check-flags.test +++ b/test-data/unit/check-flags.test @@ -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