Skip to content

Commit 6fd2dc2

Browse files
authored
stubtest: import submodules mentioned in __all__ (#9943)
* stubtest: import submodules mentioned in __all__ This kind of helps resolves #9935 Co-authored-by: hauntsaninja <>
1 parent bc1dc95 commit 6fd2dc2

File tree

1 file changed

+13
-6
lines changed

1 file changed

+13
-6
lines changed

mypy/stubtest.py

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -161,6 +161,10 @@ def test_module(module_name: str) -> Iterator[Error]:
161161
with warnings.catch_warnings():
162162
warnings.simplefilter("ignore")
163163
runtime = importlib.import_module(module_name)
164+
# Also run the equivalent of `from module import *`
165+
# This could have the additional effect of loading not-yet-loaded submodules
166+
# mentioned in __all__
167+
__import__(module_name, fromlist=["*"])
164168
except Exception as e:
165169
yield Error([module_name], "failed to import: {}".format(e), stub, MISSING)
166170
return
@@ -200,26 +204,29 @@ def verify_mypyfile(
200204
to_check = set(
201205
m
202206
for m, o in stub.names.items()
207+
# TODO: change `o.module_public` to `not o.module_hidden`
203208
if o.module_public and (not m.startswith("_") or hasattr(runtime, m))
204209
)
205210
runtime_public_contents = [
206211
m
207212
for m in dir(runtime)
208213
if not m.startswith("_")
209-
# Ensure that the object's module is `runtime`, e.g. so that we don't pick up reexported
210-
# modules and infinitely recurse. Unfortunately, there's no way to detect an explicit
211-
# reexport missing from the stubs (that isn't specified in __all__)
214+
# Ensure that the object's module is `runtime`, since in the absence of __all__ we don't
215+
# have a good way to detect re-exports at runtime.
212216
and getattr(getattr(runtime, m), "__module__", None) == runtime.__name__
213217
]
214218
# Check all things declared in module's __all__, falling back to runtime_public_contents
215219
to_check.update(getattr(runtime, "__all__", runtime_public_contents))
216220
to_check.difference_update({"__file__", "__doc__", "__name__", "__builtins__", "__package__"})
217221

218222
for entry in sorted(to_check):
219-
stub_to_verify = stub.names[entry].node if entry in stub.names else MISSING
220-
assert stub_to_verify is not None
223+
stub_entry = stub.names[entry].node if entry in stub.names else MISSING
224+
if isinstance(stub_entry, nodes.MypyFile):
225+
# Don't recursively check exported modules, since that leads to infinite recursion
226+
continue
227+
assert stub_entry is not None
221228
yield from verify(
222-
stub_to_verify,
229+
stub_entry,
223230
getattr(runtime, entry, MISSING),
224231
object_path + [entry],
225232
)

0 commit comments

Comments
 (0)