Skip to content

Commit d682434

Browse files
author
hauntsaninja
committed
Fix caching of PEP 561 namespace packages with missing submodules
Fixes #12232 Another "fail every 2nd run" bug
1 parent b22c4e4 commit d682434

File tree

2 files changed

+28
-12
lines changed

2 files changed

+28
-12
lines changed

mypy/build.py

+18-12
Original file line numberDiff line numberDiff line change
@@ -730,20 +730,19 @@ def correct_rel_imp(imp: Union[ImportFrom, ImportAll]) -> str:
730730
return new_id
731731

732732
res: List[Tuple[int, str, int]] = []
733+
delayed_res: List[Tuple[int, str, int]] = []
733734
for imp in file.imports:
734735
if not imp.is_unreachable:
735736
if isinstance(imp, Import):
736737
pri = import_priority(imp, PRI_MED)
737738
ancestor_pri = import_priority(imp, PRI_LOW)
738739
for id, _ in imp.ids:
739-
# We append the target (e.g. foo.bar.baz)
740-
# before the ancestors (e.g. foo and foo.bar)
741-
# so that, if FindModuleCache finds the target
742-
# module in a package marked with py.typed
743-
# underneath a namespace package installed in
744-
# site-packages, (gasp), that cache's
745-
# knowledge of the ancestors can be primed
746-
# when it is asked to find the target.
740+
# We append the target (e.g. foo.bar.baz) before the ancestors (e.g. foo
741+
# and foo.bar) so that, if FindModuleCache finds the target module in a
742+
# package marked with py.typed underneath a namespace package installed in
743+
# site-packages, (gasp), that cache's knowledge of the ancestors
744+
# (aka FindModuleCache.ns_ancestors) can be primed when it is asked to find
745+
# the parent.
747746
res.append((pri, id, imp.line))
748747
ancestor_parts = id.split(".")[:-1]
749748
ancestors = []
@@ -752,13 +751,15 @@ def correct_rel_imp(imp: Union[ImportFrom, ImportAll]) -> str:
752751
res.append((ancestor_pri, ".".join(ancestors), imp.line))
753752
elif isinstance(imp, ImportFrom):
754753
cur_id = correct_rel_imp(imp)
754+
any_are_submodules = False
755755
all_are_submodules = True
756756
# Also add any imported names that are submodules.
757757
pri = import_priority(imp, PRI_MED)
758758
for name, __ in imp.names:
759759
sub_id = cur_id + '.' + name
760760
if self.is_module(sub_id):
761761
res.append((pri, sub_id, imp.line))
762+
any_are_submodules = True
762763
else:
763764
all_are_submodules = False
764765
# Add cur_id as a dependency, even if all of the
@@ -768,14 +769,19 @@ def correct_rel_imp(imp: Union[ImportFrom, ImportAll]) -> str:
768769
# if all of the imports are submodules, do the import at a lower
769770
# priority.
770771
pri = import_priority(imp, PRI_HIGH if not all_are_submodules else PRI_LOW)
771-
# The imported module goes in after the
772-
# submodules, for the same namespace related
773-
# reasons discussed in the Import case.
774-
res.append((pri, cur_id, imp.line))
772+
# The imported module goes in after the submodules, for the same namespace
773+
# related reasons discussed in the Import case.
774+
# There is an additional twist: if none of the submodules exist,
775+
# we delay the import in case other imports of other submodules succeed.
776+
if any_are_submodules:
777+
res.append((pri, cur_id, imp.line))
778+
else:
779+
delayed_res.append((pri, cur_id, imp.line))
775780
elif isinstance(imp, ImportAll):
776781
pri = import_priority(imp, PRI_HIGH)
777782
res.append((pri, correct_rel_imp(imp), imp.line))
778783

784+
res.extend(delayed_res)
779785
return res
780786

781787
def is_module(self, id: str) -> bool:

test-data/unit/pep561.test

+10
Original file line numberDiff line numberDiff line change
@@ -222,3 +222,13 @@ b.bf(1)
222222
[out]
223223
testNamespacePkgWStubsWithNamespacePackagesFlag.py:7: error: Argument 1 to "bf" has incompatible type "int"; expected "bool"
224224
testNamespacePkgWStubsWithNamespacePackagesFlag.py:8: error: Argument 1 to "bf" has incompatible type "int"; expected "bool"
225+
226+
227+
[case testTypedPkgNamespaceRegFromImportTwiceMissing]
228+
# pkgs: typedpkg_ns_a
229+
from typedpkg_ns import b # type: ignore
230+
from typedpkg_ns import a
231+
-- dummy should trigger a second iteration
232+
[file dummy.py.2]
233+
[out]
234+
[out2]

0 commit comments

Comments
 (0)