Skip to content

Commit

Permalink
refactor: Rename loader load_module method to load
Browse files Browse the repository at this point in the history
This method always returned the object specified by the path
given as `module` argument. It was not always a module.
So we also renamed the `module` parameter to `objspec`,
and made it positional-only, so we can have a deprecation
period for `module`, keeping it in the signature as
keyword-only. With this, both positional and keyword uses
are kept functional. The latter will emit a deprecation
warning.
  • Loading branch information
pawamoy committed Nov 10, 2023
1 parent b7048d0 commit 2bfe206
Show file tree
Hide file tree
Showing 12 changed files with 135 additions and 72 deletions.
2 changes: 1 addition & 1 deletion benchmark.sh
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ extensions = None
loader = GriffeLoader(allow_inspection=False, extensions=extensions)
for package in stdlib_packages:
try:
loader.load_module(package)
loader.load(package)
except:
pass
loader.resolve_aliases(implicit=False, external=False)
Expand Down
12 changes: 6 additions & 6 deletions docs/loading.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ For more complex needs, create and use a loader:
from griffe.loader import GriffeLoader

loader = GriffeLoader()
mkdocs = loader.load_module("mkdocs")
mkdocs = loader.load("mkdocs")
```

Similarly, the [`GriffeLoader`][griffe.loader.GriffeLoader] accepts
Expand All @@ -25,13 +25,13 @@ a number of parameters to configure how the modules are found and loaded.
If you don't want to recurse in the submodules:

```python
mkdocs = loader.load_module("mkdocs", submodules=False)
mkdocs = loader.load("mkdocs", submodules=False)
```

## Navigating into the loaded objects

Both the `load` function and the `GriffeLoader.load_module` method
return a [`Module`][griffe.dataclasses.Module] instance.
Both the `load` function and the `GriffeLoader.load` method
return an [`Object`][griffe.dataclasses.Object] instance.
There are several ways to access members of an object:

- through its `members` attribute, which is a dictionary,
Expand Down Expand Up @@ -86,8 +86,8 @@ from griffe.loader import GriffeLoader

loader = GriffeLoader()
# note that we don't load package_a
loader.load_module("package_b")
loader.load_module("package_c")
loader.load("package_b")
loader.load("package_c")
```

If a base class cannot be resolved during computation
Expand Down
2 changes: 1 addition & 1 deletion src/griffe/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ def _load_packages(
continue
logger.info(f"Loading package {package}")
try:
loader.load_module(package)
loader.load(package)
except ModuleNotFoundError as error:
logger.error(f"Could not find package {package}: {error}") # noqa: TRY400
except ImportError as error:
Expand Down
22 changes: 16 additions & 6 deletions src/griffe/git.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@

if TYPE_CHECKING:
from griffe.collections import LinesCollection, ModulesCollection
from griffe.dataclasses import Module
from griffe.dataclasses import Object
from griffe.docstrings.parsers import Parser
from griffe.extensions import Extensions

Expand Down Expand Up @@ -114,7 +114,8 @@ def _tmp_worktree(repo: str | Path = ".", ref: str = "HEAD") -> Iterator[Path]:


def load_git(
module: str | Path,
objspec: str | Path | None = None,
/,
*,
ref: str = "HEAD",
repo: str | Path = ".",
Expand All @@ -125,8 +126,10 @@ def load_git(
docstring_options: dict[str, Any] | None = None,
lines_collection: LinesCollection | None = None,
modules_collection: ModulesCollection | None = None,
# TODO: Remove at some point.
module: str | Path | None = None,
allow_inspection: bool = True,
) -> Module:
) -> Object:
"""Load and return a module from a specific Git reference.
This function will create a temporary
Expand All @@ -136,27 +139,32 @@ def load_git(
This function requires that the `git` executable is installed.
Parameters:
module: The module path, relative to the repository root.
objspec: The Python path of an object, or file path to a module.
ref: A Git reference such as a commit, tag or branch.
repo: Path to the repository (i.e. the directory *containing* the `.git` directory)
submodules: Whether to recurse on the submodules.
This parameter only makes sense when loading a package (top-level module).
extensions: The extensions to use.
search_paths: The paths to search into (relative to the repository root).
docstring_parser: The docstring parser to use. By default, no parsing is done.
docstring_options: Additional docstring parsing options.
lines_collection: A collection of source code lines.
modules_collection: A collection of modules.
allow_inspection: Whether to allow inspecting modules when visiting them is not possible.
module: Deprecated. Use `objspec` positional-only parameter instead.
Returns:
A loaded module.
A Griffe object.
"""
with _tmp_worktree(repo, ref) as worktree:
search_paths = [worktree / path for path in search_paths or ["."]]
if isinstance(objspec, Path):
objspec = worktree / objspec
# TODO: Remove at some point.
if isinstance(module, Path):
module = worktree / module
return loader.load(
module=module,
objspec,
submodules=submodules,
try_relative_path=False,
extensions=extensions,
Expand All @@ -166,6 +174,8 @@ def load_git(
lines_collection=lines_collection,
modules_collection=modules_collection,
allow_inspection=allow_inspection,
# TODO: Remove at some point.
module=module,
)


Expand Down
109 changes: 81 additions & 28 deletions src/griffe/loader.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,14 @@
from griffe.loader import GriffeLoader
griffe = GriffeLoader()
fastapi = griffe.load_module("fastapi")
fastapi = griffe.load("fastapi")
```
"""

from __future__ import annotations

import sys
import warnings
from contextlib import suppress
from datetime import datetime, timezone
from typing import TYPE_CHECKING, Any, ClassVar, Sequence, cast
Expand Down Expand Up @@ -87,56 +88,101 @@ def __init__(
"time_spent_inspecting": 0,
}

# TODO: Remove at some point.
def load_module(
self,
module: str | Path,
*,
submodules: bool = True,
try_relative_path: bool = True,
) -> Module:
"""Load a module.
) -> Object:
"""Renamed `load`. Load an object as a Griffe object, given its dotted path.
This method was renamed [`load`][griffe.loader.GriffeLoader.load].
"""
warnings.warn("The `load_module` method was renamed `load`, and is deprecated.", DeprecationWarning, stacklevel=2)
return self.load(module, submodules=submodules, try_relative_path=try_relative_path)

def load(
self,
objspec: str | Path | None = None,
/,
*,
submodules: bool = True,
try_relative_path: bool = True,
# TODO: Remove at some point.
module: str | Path | None = None,
) -> Object:
"""Load an object as a Griffe object, given its Python or file path.
Note that this will load the whole object's package,
and return only the specified object.
The rest of the package can be accessed from the returned object
with regular methods and properties (`parent`, `members`, etc.).
Examples:
>>> loader.load("griffe.dataclasses.Module")
Class("Module")
>>> loader.load("src/griffe/dataclasses.py")
Module("dataclasses")
Parameters:
module: The module name or path.
objspec: The Python path of an object, or file path to a module.
submodules: Whether to recurse on the submodules.
This parameter only makes sense when loading a package (top-level module).
try_relative_path: Whether to try finding the module as a relative path.
module: Deprecated. Use `objspec` positional-only parameter instead.
Raises:
LoadingError: When loading a module failed for various reasons.
ModuleNotFoundError: When a module was not found and inspection is disallowed.
Returns:
A module.
A Griffe object.
"""
module_name: str
if module in _builtin_modules:
logger.debug(f"{module} is a builtin module")
# TODO: Remove at some point.
if objspec is None and module is None:
raise TypeError("load() missing 1 required positional argument: 'objspec'")
if objspec is None:
objspec = module
warnings.warn(
"Parameter 'module' was renamed 'objspec' and made positional-only.",
DeprecationWarning,
stacklevel=2,
)
obj_path: str
if objspec in _builtin_modules:
logger.debug(f"{objspec} is a builtin module")
if self.allow_inspection:
logger.debug(f"Inspecting {module}")
module_name = module # type: ignore[assignment]
top_module = self._inspect_module(module) # type: ignore[arg-type]
logger.debug(f"Inspecting {objspec}")
obj_path = objspec # type: ignore[assignment]
top_module = self._inspect_module(objspec) # type: ignore[arg-type]
self.modules_collection.set_member(top_module.path, top_module)
return self.modules_collection.get_member(module_name)
obj = self.modules_collection.get_member(obj_path)
self.extensions.call("on_package_loaded", pkg=obj)
return obj
raise LoadingError("Cannot load builtin module without inspection")
try:
module_name, package = self.finder.find_spec(module, try_relative_path=try_relative_path)
obj_path, package = self.finder.find_spec(objspec, try_relative_path=try_relative_path) # type: ignore[arg-type]
except ModuleNotFoundError:
logger.debug(f"Could not find {module}")
logger.debug(f"Could not find {objspec}")
if self.allow_inspection:
logger.debug(f"Trying inspection on {module}")
module_name = module # type: ignore[assignment]
top_module = self._inspect_module(module) # type: ignore[arg-type]
logger.debug(f"Trying inspection on {objspec}")
obj_path = objspec # type: ignore[assignment]
top_module = self._inspect_module(objspec) # type: ignore[arg-type]
self.modules_collection.set_member(top_module.path, top_module)
else:
raise
else:
logger.debug(f"Found {module}: loading")
logger.debug(f"Found {objspec}: loading")
try:
top_module = self._load_package(package, submodules=submodules)
except LoadingError as error:
logger.exception(str(error)) # noqa: TRY401
raise
return self.modules_collection.get_member(module_name)
obj = self.modules_collection.get_member(obj_path)
self.extensions.call("on_package_loaded", pkg=obj)
return obj

def resolve_aliases(
self,
Expand Down Expand Up @@ -256,7 +302,7 @@ def expand_wildcards(
if not external:
continue
try:
self.load_module(package, try_relative_path=False)
self.load(package, try_relative_path=False)
except ImportError as error:
logger.debug(f"Could not expand wildcard import {member.name} in {obj.path}: {error}")
continue
Expand Down Expand Up @@ -390,7 +436,7 @@ def resolve_module_aliases(
if load_module:
logger.debug(f"Failed to resolve alias {member.path} -> {target}")
try:
self.load_module(package, try_relative_path=False)
self.load(package, try_relative_path=False)
except ImportError as error:
logger.debug(f"Could not follow alias {member.path}: {error}")
load_failures.add(package)
Expand Down Expand Up @@ -611,7 +657,8 @@ def _expand_wildcard(self, wildcard_obj: Alias) -> list[tuple[Object | Alias, in


def load(
module: str | Path,
objspec: str | Path | None = None,
/,
*,
submodules: bool = True,
try_relative_path: bool = True,
Expand All @@ -622,7 +669,9 @@ def load(
lines_collection: LinesCollection | None = None,
modules_collection: ModulesCollection | None = None,
allow_inspection: bool = True,
) -> Module:
# TODO: Remove at some point.
module: str | Path | None = None,
) -> Object:
"""Load and return a module.
Example:
Expand All @@ -638,14 +687,15 @@ def load(
from griffe.loader import GriffeLoader
loader = GriffeLoader(...)
module = loader.load_module(...)
module = loader.load(...)
```
See the documentation for the loader: [`GriffeLoader`][griffe.loader.GriffeLoader].
Parameters:
module: The module name or path.
objspec: The Python path of an object, or file path to a module.
submodules: Whether to recurse on the submodules.
This parameter only makes sense when loading a package (top-level module).
try_relative_path: Whether to try finding the module as a relative path.
extensions: The extensions to use.
search_paths: The paths to search into.
Expand All @@ -654,9 +704,10 @@ def load(
lines_collection: A collection of source code lines.
modules_collection: A collection of modules.
allow_inspection: Whether to allow inspecting modules when visiting them is not possible.
module: Deprecated. Use `objspec` positional-only parameter instead.
Returns:
A loaded module.
A Griffe object.
"""
return GriffeLoader(
extensions=extensions,
Expand All @@ -666,10 +717,12 @@ def load(
lines_collection=lines_collection,
modules_collection=modules_collection,
allow_inspection=allow_inspection,
).load_module(
module=module,
).load(
objspec,
submodules=submodules,
try_relative_path=try_relative_path,
# TODO: Remove at some point.
module=module,
)


Expand Down
2 changes: 1 addition & 1 deletion src/griffe/tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,7 @@ def temporary_visited_package(
"""
with temporary_pypackage(package, modules, init=init) as tmp_package:
loader = GriffeLoader(search_paths=[tmp_package.tmpdir])
yield loader.load_module(tmp_package.name)
yield loader.load(tmp_package.name) # type: ignore[misc]


@contextmanager
Expand Down
6 changes: 3 additions & 3 deletions tests/test_dataclasses.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ def test_handle_aliases_chain_in_has_docstrings() -> None:
mod_b.write_text("from somelib import someobj")

loader = GriffeLoader(search_paths=[tmp_package.tmpdir])
package = loader.load_module(tmp_package.name)
package = loader.load(tmp_package.name)
assert not package.has_docstrings
loader.resolve_aliases(implicit=True)
assert not package.has_docstrings
Expand All @@ -59,15 +59,15 @@ def test_has_docstrings_does_not_trigger_alias_resolution() -> None:
mod_b.write_text("from somelib import someobj")

loader = GriffeLoader(search_paths=[tmp_package.tmpdir])
package = loader.load_module(tmp_package.name)
package = loader.load(tmp_package.name)
assert not package.has_docstrings
assert not package["mod_a.someobj"].resolved


def test_deepcopy() -> None:
"""Assert we can deep-copy object trees."""
loader = GriffeLoader()
mod = loader.load_module("griffe")
mod = loader.load("griffe")

deepcopy(mod)
deepcopy(mod.as_dict())
Expand Down
Loading

0 comments on commit 2bfe206

Please sign in to comment.