Skip to content

stubtest: find submodules missing from stubs #13030

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Jun 29, 2022
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 28 additions & 9 deletions mypy/stubtest.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
import importlib
import inspect
import os
import pkgutil
import re
import sys
import types
Expand Down Expand Up @@ -164,6 +165,17 @@ def get_description(self, concise: bool = False) -> str:
# Core logic
# ====================

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


def test_module(module_name: str) -> Iterator[Error]:
"""Tests a given module's stub against introspecting it at runtime.
Expand All @@ -175,18 +187,14 @@ def test_module(module_name: str) -> Iterator[Error]:
"""
stub = get_stub(module_name)
if stub is None:
yield Error([module_name], "failed to find stubs", MISSING, None, runtime_desc="N/A")
runtime_desc = repr(sys.modules[module_name]) if module_name in sys.modules else "N/A"
yield Error(
[module_name], "failed to find stubs", MISSING, None, runtime_desc=runtime_desc
)
return

try:
with open(os.devnull, "w") as devnull:
with warnings.catch_warnings(), redirect_stdout(devnull), redirect_stderr(devnull):
warnings.simplefilter("ignore")
runtime = importlib.import_module(module_name)
# Also run the equivalent of `from module import *`
# This could have the additional effect of loading not-yet-loaded submodules
# mentioned in __all__
__import__(module_name, fromlist=["*"])
runtime = silent_import_module(module_name)
except Exception as e:
yield Error([module_name], f"failed to import, {type(e).__name__}: {e}", stub, MISSING)
return
Expand Down Expand Up @@ -1289,7 +1297,18 @@ def build_stubs(modules: List[str], options: Options, find_submodules: bool = Fa
else:
found_sources = find_module_cache.find_modules_recursive(module)
sources.extend(found_sources)
# find submodules via mypy
all_modules.extend(s.module for s in found_sources if s.module not in all_modules)
# find submodules via pkgutil
try:
runtime = silent_import_module(module)
all_modules.extend(
m.name
for m in pkgutil.walk_packages(runtime.__path__, runtime.__name__ + ".")
if m.name not in all_modules
)
except Exception:
pass

if sources:
try:
Expand Down