Skip to content

Commit

Permalink
refactor: Split out module finder
Browse files Browse the repository at this point in the history
This commit separates the logic
of finding modules from loading them.

It also improves namespace packages
support: a single package can be
installed into multiple locations.
When passing a path to the loader,
we will still detect it's a namespace
package given the right search paths.
  • Loading branch information
pawamoy committed Jan 28, 2022
1 parent dfcf749 commit 7290642
Show file tree
Hide file tree
Showing 6 changed files with 395 additions and 254 deletions.
3 changes: 2 additions & 1 deletion src/griffe/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -113,13 +113,14 @@ def _load_packages(
):
loader = GriffeLoader(
extensions=extensions,
search_paths=search_paths,
docstring_parser=docstring_parser,
docstring_options=docstring_options,
)
for package in packages:
logger.info(f"Loading package {package}")
try:
loader.load_module(package, search_paths=search_paths)
loader.load_module(package)
except ModuleNotFoundError as error:
logger.error(f"Could not find package {package}: {error}")
except ImportError as error:
Expand Down
50 changes: 39 additions & 11 deletions src/griffe/dataclasses.py
Original file line number Diff line number Diff line change
Expand Up @@ -483,7 +483,7 @@ def package(self) -> Module:
return module

@cached_property
def filepath(self) -> Path:
def filepath(self) -> Path | list[Path]:
"""Return the file path where this object was defined.
It should never return None for non-module objects,
Expand All @@ -492,18 +492,38 @@ def filepath(self) -> Path:
If it _does_ return None, it means the tree was not built correctly.
Returns:
A file path.
A file path or a list of directories.
"""
return self.module.filepath

@cached_property
def relative_filepath(self) -> Path:
@cached_property # noqa: WPS231
def relative_filepath(self) -> Path: # noqa: WPS231
"""Return the file path where this object was defined, relative to the top module path.
Raises:
ValueError: When the relative path could not be computed.
Returns:
A file path.
"""
return self.module.filepath.relative_to(self.package.filepath.parent.parent)
package_path = self.package.filepath
if isinstance(self.module.filepath, list):
if isinstance(package_path, list):
for pkg_path in package_path:
for self_path in self.module.filepath:
with suppress(ValueError):
return self_path.relative_to(pkg_path.parent)
else:
for self_path in self.module.filepath: # noqa: WPS440
with suppress(ValueError):
return self_path.relative_to(package_path.parent.parent)
raise ValueError
if isinstance(package_path, list):
for pkg_path in package_path: # noqa: WPS440
with suppress(ValueError):
return self.module.filepath.relative_to(pkg_path.parent)
raise ValueError
return self.module.filepath.relative_to(package_path.parent.parent)

@cached_property
def path(self) -> str:
Expand Down Expand Up @@ -572,6 +592,8 @@ def lines(self) -> list[str]:
filepath = self.filepath
except BuiltinModuleError:
return []
if isinstance(filepath, list):
return []

# TODO: remove once Python 3.7 support is dropped
if self.lineno and self.endlineno is None and sys.version_info < (3, 8):
Expand Down Expand Up @@ -652,9 +674,13 @@ def as_dict(self, full: bool = False, **kwargs: Any) -> dict[str, Any]:

# TODO: remove once Python 3.7 support is dropped
@property
def _endlineno(self) -> int:
def _endlineno(self) -> int | None:
if self.kind is Kind.MODULE:
if isinstance(self.filepath, list):
return 0
return len(self.lines_collection[self.filepath])
if isinstance(self.filepath, list):
return None
tokens, tokens_by_line = self.lines_collection.tokens(self.filepath)
first_token_index = tokens_by_line[self.lineno][0]
blockfinder = inspect.BlockFinder()
Expand Down Expand Up @@ -888,7 +914,7 @@ class Module(Object):

kind = Kind.MODULE

def __init__(self, *args: Any, filepath: Path | None = None, **kwargs: Any) -> None:
def __init__(self, *args: Any, filepath: Path | list[Path] | None = None, **kwargs: Any) -> None:
"""Initialize the module.
Parameters:
Expand All @@ -897,7 +923,7 @@ def __init__(self, *args: Any, filepath: Path | None = None, **kwargs: Any) -> N
**kwargs: See [`griffe.dataclasses.Object`][].
"""
super().__init__(*args, **kwargs)
self._filepath: Path | None = filepath
self._filepath: Path | list[Path] | None = filepath

def __repr__(self) -> str:
try:
Expand All @@ -906,7 +932,7 @@ def __repr__(self) -> str:
return f"<Module({self.name!r})>"

@property
def filepath(self) -> Path:
def filepath(self) -> Path | list[Path]:
"""Get the file path of this module.
Raises:
Expand All @@ -926,6 +952,8 @@ def is_init_module(self) -> bool:
Returns:
True or False.
"""
if isinstance(self.filepath, list):
return False
try:
return self.filepath.name.split(".", 1)[0] == "__init__"
except BuiltinModuleError:
Expand Down Expand Up @@ -957,7 +985,7 @@ def is_namespace_package(self) -> bool:
True or False.
"""
try:
return self.parent is None and self.filepath.is_dir()
return self.parent is None and isinstance(self.filepath, list)
except BuiltinModuleError:
return False

Expand All @@ -971,7 +999,7 @@ def is_namespace_subpackage(self) -> bool:
try:
return (
self.parent is not None
and self.filepath.is_dir()
and isinstance(self.filepath, list)
and (
cast(Module, self.parent).is_namespace_package or cast(Module, self.parent).is_namespace_subpackage
)
Expand Down
6 changes: 5 additions & 1 deletion src/griffe/exceptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,11 @@ class NameResolutionError(GriffeError):


class UnhandledPthFileError(GriffeError):
"""Exception for unhandled .path files, when searching modules."""
"""Exception for unhandled .pth files, when searching modules."""


class UnhandledEditablesModuleError(GriffeError):
"""Exception for unhandled editables modules, when searching modules."""


class UnimportableModuleError(GriffeError):
Expand Down
Loading

0 comments on commit 7290642

Please sign in to comment.