Skip to content

Commit

Permalink
refactor: Recurse immediately into non-discoverable submodules (no pa…
Browse files Browse the repository at this point in the history
…th on disk) during dynamic analysis
  • Loading branch information
pawamoy committed May 12, 2024
1 parent fc794c2 commit d0b7a1d
Show file tree
Hide file tree
Showing 2 changed files with 67 additions and 3 deletions.
33 changes: 30 additions & 3 deletions src/griffe/agents/inspector.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
from griffe.expressions import safe_get_annotation
from griffe.extensions.base import Extensions, load_extensions
from griffe.importer import dynamic_import
from griffe.logger import get_logger

if TYPE_CHECKING:
from pathlib import Path
Expand All @@ -43,6 +44,7 @@
from griffe.expressions import Expr


logger = get_logger(__name__)
empty = Signature.empty


Expand Down Expand Up @@ -218,9 +220,34 @@ def generic_inspect(self, node: ObjectNode) -> None:
before_inspector.inspect(node)

for child in node.children:
target_path = child.alias_target_path
if target_path:
self.current.set_member(child.name, Alias(child.name, target_path))
if target_path := child.alias_target_path:
# If the child is an actual submodule of the current module,
# and has no `__file__` set, we won't find it on the disk so we must inspect it now.
# For that we instantiate a new inspector and use it to inspect the submodule,
# then assign the submodule as member of the current module.
# If the submodule has a `__file__` set, the loader should find it on the disk,
# so we skip it here (no member, no alias, just skip it).
if child.is_module and target_path == f"{self.current.path}.{child.name}":
if not hasattr(child.obj, "__file__"):
logger.debug(f"Module {target_path} is not discoverable on disk, inspecting right now")
inspector = Inspector(
child.name,
filepath=None,
parent=self.current.module,
extensions=self.extensions,
docstring_parser=self.docstring_parser,
docstring_options=self.docstring_options,
lines_collection=self.lines_collection,
modules_collection=self.modules_collection,
)
try:
inspector.inspect_module(child)
finally:
self.extensions.attach_inspector(self)
self.current.set_member(child.name, inspector.current.module)
# Otherwise, alias the object.
else:
self.current.set_member(child.name, Alias(child.name, target_path))
else:
self.inspect(child)

Expand Down
37 changes: 37 additions & 0 deletions tests/test_inspector.py
Original file line number Diff line number Diff line change
Expand Up @@ -110,3 +110,40 @@ def test_inspecting_package_and_module_with_same_names() -> None:
with temporary_pypackage("package", {"package.py": "a = 0"}) as tmp_package:
inspect("package.package", filepath=tmp_package.path / "package.py", import_paths=[tmp_package.tmpdir])
_clear_sys_modules("package")


def test_inspecting_module_with_submodules() -> None:
"""Inspecting a module shouldn't register any of its submodules if they're not imported."""
with temporary_pypackage("pkg", ["mod.py"]) as tmp_package:
pkg = inspect("pkg", filepath=tmp_package.path / "__init__.py")
assert "mod" not in pkg.members
_clear_sys_modules("pkg")


def test_inspecting_module_with_imported_submodules() -> None:
"""When inspecting a package on the disk, direct submodules should be skipped entirely."""
with temporary_pypackage(
"pkg",
{
"__init__.py": "from pkg import subpkg\nfrom pkg.subpkg import mod",
"subpkg/__init__.py": "a = 0",
"subpkg/mod.py": "b = 0",
},
) as tmp_package:
pkg = inspect("pkg", filepath=tmp_package.path / "__init__.py")
assert "subpkg" not in pkg.members
assert "mod" in pkg.members
assert pkg["mod"].is_alias
assert pkg["mod"].target_path == "pkg.subpkg.mod"
_clear_sys_modules("pkg")


def test_inspecting_objects_from_private_builtin_stdlib_moduless() -> None:
"""Inspect objects from private built-in modules in the standard library."""
ast = inspect("ast")
assert "Assign" in ast.members
assert not ast["Assign"].is_alias

ast = inspect("_ast")
assert "Assign" in ast.members
assert not ast["Assign"].is_alias

0 comments on commit d0b7a1d

Please # to comment.