Skip to content

Commit 914297e

Browse files
authored
stubtest: find submodules missing from stubs (#13030)
Co-authored-by: hauntsaninja <>
1 parent 1c03e10 commit 914297e

File tree

1 file changed

+28
-9
lines changed

1 file changed

+28
-9
lines changed

mypy/stubtest.py

Lines changed: 28 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
import importlib
1111
import inspect
1212
import os
13+
import pkgutil
1314
import re
1415
import sys
1516
import types
@@ -164,6 +165,17 @@ def get_description(self, concise: bool = False) -> str:
164165
# Core logic
165166
# ====================
166167

168+
def silent_import_module(module_name: str) -> types.ModuleType:
169+
with open(os.devnull, "w") as devnull:
170+
with warnings.catch_warnings(), redirect_stdout(devnull), redirect_stderr(devnull):
171+
warnings.simplefilter("ignore")
172+
runtime = importlib.import_module(module_name)
173+
# Also run the equivalent of `from module import *`
174+
# This could have the additional effect of loading not-yet-loaded submodules
175+
# mentioned in __all__
176+
__import__(module_name, fromlist=["*"])
177+
return runtime
178+
167179

168180
def test_module(module_name: str) -> Iterator[Error]:
169181
"""Tests a given module's stub against introspecting it at runtime.
@@ -175,18 +187,14 @@ def test_module(module_name: str) -> Iterator[Error]:
175187
"""
176188
stub = get_stub(module_name)
177189
if stub is None:
178-
yield Error([module_name], "failed to find stubs", MISSING, None, runtime_desc="N/A")
190+
runtime_desc = repr(sys.modules[module_name]) if module_name in sys.modules else "N/A"
191+
yield Error(
192+
[module_name], "failed to find stubs", MISSING, None, runtime_desc=runtime_desc
193+
)
179194
return
180195

181196
try:
182-
with open(os.devnull, "w") as devnull:
183-
with warnings.catch_warnings(), redirect_stdout(devnull), redirect_stderr(devnull):
184-
warnings.simplefilter("ignore")
185-
runtime = importlib.import_module(module_name)
186-
# Also run the equivalent of `from module import *`
187-
# This could have the additional effect of loading not-yet-loaded submodules
188-
# mentioned in __all__
189-
__import__(module_name, fromlist=["*"])
197+
runtime = silent_import_module(module_name)
190198
except Exception as e:
191199
yield Error([module_name], f"failed to import, {type(e).__name__}: {e}", stub, MISSING)
192200
return
@@ -1289,7 +1297,18 @@ def build_stubs(modules: List[str], options: Options, find_submodules: bool = Fa
12891297
else:
12901298
found_sources = find_module_cache.find_modules_recursive(module)
12911299
sources.extend(found_sources)
1300+
# find submodules via mypy
12921301
all_modules.extend(s.module for s in found_sources if s.module not in all_modules)
1302+
# find submodules via pkgutil
1303+
try:
1304+
runtime = silent_import_module(module)
1305+
all_modules.extend(
1306+
m.name
1307+
for m in pkgutil.walk_packages(runtime.__path__, runtime.__name__ + ".")
1308+
if m.name not in all_modules
1309+
)
1310+
except Exception:
1311+
pass
12931312

12941313
if sources:
12951314
try:

0 commit comments

Comments
 (0)