Skip to content

Commit

Permalink
stubtest: increase coverage (#11634)
Browse files Browse the repository at this point in the history
Co-authored-by: hauntsaninja <>
  • Loading branch information
hauntsaninja authored Dec 1, 2021
1 parent 72feb61 commit 7a2aff8
Show file tree
Hide file tree
Showing 2 changed files with 41 additions and 14 deletions.
37 changes: 24 additions & 13 deletions mypy/stubtest.py
Original file line number Diff line number Diff line change
Expand Up @@ -200,23 +200,34 @@ def verify_mypyfile(
yield Error(object_path, "is not a module", stub, runtime)
return

# Check things in the stub that are public
# Check things in the stub
to_check = set(
m
for m, o in stub.names.items()
# TODO: change `o.module_public` to `not o.module_hidden`
if o.module_public and (not m.startswith("_") or hasattr(runtime, m))
if not o.module_hidden and (not m.startswith("_") or hasattr(runtime, m))
)
runtime_public_contents = [
m
for m in dir(runtime)
if not m.startswith("_")
# Ensure that the object's module is `runtime`, since in the absence of __all__ we don't
# have a good way to detect re-exports at runtime.
and getattr(getattr(runtime, m), "__module__", None) == runtime.__name__
]
# Check all things declared in module's __all__, falling back to runtime_public_contents
to_check.update(getattr(runtime, "__all__", runtime_public_contents))

def _belongs_to_runtime(r: types.ModuleType, attr: str) -> bool:
obj = getattr(r, attr)
obj_mod = getattr(obj, "__module__", None)
if obj_mod is not None:
return obj_mod == r.__name__
return not isinstance(obj, types.ModuleType)

runtime_public_contents = (
runtime.__all__
if hasattr(runtime, "__all__")
else [
m
for m in dir(runtime)
if not m.startswith("_")
# Ensure that the object's module is `runtime`, since in the absence of __all__ we
# don't have a good way to detect re-exports at runtime.
and _belongs_to_runtime(runtime, m)
]
)
# Check all things declared in module's __all__, falling back to our best guess
to_check.update(runtime_public_contents)
to_check.difference_update({"__file__", "__doc__", "__name__", "__builtins__", "__package__"})

for entry in sorted(to_check):
Expand Down
18 changes: 17 additions & 1 deletion mypy/test/teststubtest.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,8 @@ class bool(int): ...
class str: ...
class bytes: ...
class list(Sequence[T]): ...
def property(f: T) -> T: ...
def classmethod(f: T) -> T: ...
def staticmethod(f: T) -> T: ...
Expand Down Expand Up @@ -648,7 +650,7 @@ def h(x: str): ...
runtime="",
error="h",
)
yield Case("", "__all__ = []", None) # dummy case
yield Case(stub="", runtime="__all__ = []", error=None) # dummy case
yield Case(stub="", runtime="__all__ += ['y']\ny = 5", error="y")
yield Case(stub="", runtime="__all__ += ['g']\ndef g(): pass", error="g")
# Here we should only check that runtime has B, since the stub explicitly re-exports it
Expand All @@ -660,6 +662,20 @@ def h(x: str): ...
def test_missing_no_runtime_all(self) -> Iterator[Case]:
yield Case(stub="", runtime="import sys", error=None)
yield Case(stub="", runtime="def g(): ...", error="g")
yield Case(stub="", runtime="CONSTANT = 0", error="CONSTANT")

@collect_cases
def test_non_public_1(self) -> Iterator[Case]:
yield Case(stub="__all__: list[str]", runtime="", error=None) # dummy case
yield Case(stub="_f: int", runtime="def _f(): ...", error="_f")

@collect_cases
def test_non_public_2(self) -> Iterator[Case]:
yield Case(
stub="__all__: list[str] = ['f']", runtime="__all__ = ['f']", error=None
)
yield Case(stub="f: int", runtime="def f(): ...", error="f")
yield Case(stub="g: int", runtime="def g(): ...", error="g")

@collect_cases
def test_special_dunders(self) -> Iterator[Case]:
Expand Down

0 comments on commit 7a2aff8

Please sign in to comment.