Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 7cc438d

Browse files
jhanceilevkivskyi
authored andcommitted
Add --no-implicit-reexport flag (#6562)
Adds a --no-implicit-reexport flag, intended for giving modules the same behavior as stubs for re-exporting their imports. The goal is to make it possible to prevent bad transitive imports from causing dependency issues. And to make it easier to move things to a different module.
1 parent 186ab3f commit 7cc438d

File tree

7 files changed

+88
-6
lines changed

7 files changed

+88
-6
lines changed

docs/source/command_line.rst

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -389,6 +389,20 @@ of the above sections.
389389
# 'items' now has type List[List[str]]
390390
...
391391
392+
``--no-implicit-reexport``
393+
By default, imported values to a module are treated as exported and mypy allows
394+
other modules to import them. This flag changes the behavior to not re-export unless
395+
the item is imported using from-as. Note this is always treated as enabled for
396+
stub files. For example:
397+
398+
.. code-block:: python
399+
400+
# This won't re-export the value
401+
from foo import bar
402+
# This will re-export it as bar and allow other modules to import it
403+
from foo import bar as bar
404+
405+
392406
``--strict-equality``
393407
By default, mypy allows always-false comparisons like ``42 == 'no'``.
394408
Use this flag to prohibit such comparisons of non-overlapping types, and

docs/source/config_file.rst

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -294,6 +294,19 @@ Miscellaneous strictness flags
294294
Allows variables to be redefined with an arbitrary type, as long as the redefinition
295295
is in the same block and nesting level as the original definition.
296296

297+
``implicit-reexport`` (bool, default True)
298+
By default, imported values to a module are treated as exported and mypy allows
299+
other modules to import them. When false, mypy will not re-export unless
300+
the item is imported using from-as. Note that mypy treats stub files as if this
301+
is always disabled. For example:
302+
303+
.. code-block:: python
304+
305+
# This won't re-export the value
306+
from foo import bar
307+
# This will re-export it as bar and allow other modules to import it
308+
from foo import bar as bar
309+
297310
``strict_equality`` (bool, default False)
298311
Prohibit equality checks, identity checks, and container checks between
299312
non-overlapping types.

mypy/main.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -490,6 +490,11 @@ def add_invertible_flag(flag: str,
490490
" non-overlapping types",
491491
group=strictness_group)
492492

493+
add_invertible_flag('--no-implicit-reexport', default=True, strict_flag=True,
494+
dest='implicit_reexport',
495+
help="Treat imports as private unless aliased",
496+
group=strictness_group)
497+
493498
incremental_group = parser.add_argument_group(
494499
title='Incremental mode',
495500
description="Adjust how mypy incrementally type checks and caches modules. "

mypy/newsemanal/semanal.py

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1605,7 +1605,11 @@ def visit_import(self, i: Import) -> None:
16051605
self.add_module_symbol(id, as_id, module_public=True, context=i)
16061606
else:
16071607
# Modules imported in a stub file without using 'as x' won't get exported
1608-
module_public = not self.is_stub_file
1608+
# When implicit re-exporting is disabled, we have the same behavior as stubs.
1609+
module_public = (
1610+
not self.is_stub_file
1611+
and self.options.implicit_reexport
1612+
)
16091613
base = id.split('.')[0]
16101614
self.add_module_symbol(base, base, module_public=module_public,
16111615
context=i, module_hidden=not module_public)
@@ -1709,8 +1713,13 @@ def visit_import_from(self, imp: ImportFrom) -> None:
17091713
# Imports are special, some redefinitions are allowed, so wait until
17101714
# we know what is the new symbol node.
17111715
continue
1712-
# 'from m import x as x' exports x in a stub file.
1713-
module_public = not self.is_stub_file or as_id is not None
1716+
# 'from m import x as x' exports x in a stub file or when implicit
1717+
# re-exports are disabled.
1718+
module_public = (
1719+
not self.is_stub_file
1720+
and self.options.implicit_reexport
1721+
or as_id is not None
1722+
)
17141723
module_hidden = not module_public and possible_module_id not in self.modules
17151724
# NOTE: we take the original node even for final `Var`s. This is to support
17161725
# a common pattern when constants are re-exported (same applies to import *).

mypy/options.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ class BuildType:
4444
"local_partial_types",
4545
"mypyc",
4646
"no_implicit_optional",
47+
"implicit_reexport",
4748
"show_none_errors",
4849
"strict_optional",
4950
"strict_optional_whitelist",
@@ -151,6 +152,9 @@ def __init__(self) -> None:
151152
# Don't assume arguments with default values of None are Optional
152153
self.no_implicit_optional = False
153154

155+
# Don't re-export names unless they are imported with `from ... as ...`
156+
self.implicit_reexport = True
157+
154158
# Suppress toplevel errors caused by missing annotations
155159
self.allow_untyped_globals = False
156160

mypy/semanal.py

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1453,7 +1453,11 @@ def visit_import(self, i: Import) -> None:
14531453
self.add_module_symbol(id, as_id, module_public=True, context=i)
14541454
else:
14551455
# Modules imported in a stub file without using 'as x' won't get exported
1456-
module_public = not self.is_stub_file
1456+
# Modules with implicit reexport disabled have the same behavior as stubs.
1457+
module_public = (
1458+
not self.is_stub_file
1459+
and self.options.implicit_reexport
1460+
)
14571461
base = id.split('.')[0]
14581462
self.add_module_symbol(base, base, module_public=module_public,
14591463
context=i, module_hidden=not module_public)
@@ -1562,8 +1566,13 @@ def visit_import_from(self, imp: ImportFrom) -> None:
15621566
if self.process_import_over_existing_name(
15631567
imported_id, existing_symbol, node, imp):
15641568
continue
1565-
# 'from m import x as x' exports x in a stub file.
1566-
module_public = not self.is_stub_file or as_id is not None
1569+
# 'from m import x as x' exports x in a stub file, or when implicit reexport
1570+
# is disabled.
1571+
module_public = (
1572+
not self.is_stub_file
1573+
and self.options.implicit_reexport
1574+
or as_id is not None
1575+
)
15671576
module_hidden = not module_public and possible_module_id not in self.modules
15681577
# NOTE: we take the original node even for final `Var`s. This is to support
15691578
# a common pattern when constants are re-exported (same applies to import *).

test-data/unit/check-flags.test

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1137,3 +1137,31 @@ strict_equality = True
11371137
[[mypy-b]
11381138
strict_equality = False
11391139
[builtins fixtures/bool.pyi]
1140+
1141+
[case testNoImplicitReexport]
1142+
# flags: --no-implicit-reexport
1143+
from other_module_2 import a
1144+
[file other_module_1.py]
1145+
a = 5
1146+
[file other_module_2.py]
1147+
from other_module_1 import a
1148+
[out]
1149+
main:2: error: Module 'other_module_2' has no attribute 'a'
1150+
1151+
[case testNoImplicitReexportMypyIni]
1152+
# flags: --config-file tmp/mypy.ini
1153+
from other_module_2 import a
1154+
1155+
[file other_module_1.py]
1156+
a = 5
1157+
1158+
[file other_module_2.py]
1159+
from other_module_1 import a
1160+
1161+
[file mypy.ini]
1162+
[[mypy]
1163+
implicit_reexport = True
1164+
[[mypy-other_module_2]
1165+
implicit_reexport = False
1166+
[out]
1167+
main:2: error: Module 'other_module_2' has no attribute 'a'

0 commit comments

Comments
 (0)