Skip to content

Commit

Permalink
wip
Browse files Browse the repository at this point in the history
  • Loading branch information
pawamoy committed Apr 9, 2024
1 parent 70c58fc commit 5118a46
Show file tree
Hide file tree
Showing 10 changed files with 278 additions and 88 deletions.
2 changes: 1 addition & 1 deletion src/griffe/agents/nodes/_runtime.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ def __init__(self, obj: Any, name: str, parent: ObjectNode | None = None) -> Non
# which triggers the __getattr__ method of the object, which in
# turn can raise various exceptions. Probably not just __getattr__.
# See https://github.com/pawamoy/pytkdocs/issues/45
logger.debug(f"Could not unwrap {name}: {error!r}")
logger.could_not_unwrap(name=name, error=error)

self.obj: Any = obj
"""The actual Python object."""
Expand Down
44 changes: 30 additions & 14 deletions src/griffe/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,10 @@
import sys
from datetime import datetime, timezone
from pathlib import Path
from typing import IO, TYPE_CHECKING, Any, Callable, Sequence
from typing import IO, TYPE_CHECKING, Any, Callable, ClassVar, Sequence

import colorama
from colorama import Fore, Style

from griffe import debug
from griffe.diff import find_breaking_changes
Expand All @@ -32,7 +33,7 @@
from griffe.extensions.base import load_extensions
from griffe.git import get_latest_tag, get_repo_root
from griffe.loader import GriffeLoader, load, load_git
from griffe.logger import get_logger
from griffe.logger import LogLevel, get_logger
from griffe.stats import _format_stats

if TYPE_CHECKING:
Expand Down Expand Up @@ -89,31 +90,28 @@ def _load_packages(
# Load each package.
for package in packages:
if not package:
logger.debug("Empty package name, continuing")
logger.empty_package_name()
continue
logger.info(f"Loading package {package}")
logger.loading_package(package=package)
try:
loader.load(package, try_relative_path=True, find_stubs_package=find_stubs_package)
except ModuleNotFoundError as error:
logger.error(f"Could not find package {package}: {error}") # noqa: TRY400
logger.could_not_find_package(package=package, error=error)
except ImportError as error:
logger.exception(f"Tried but could not import package {package}: {error}") # noqa: TRY401
logger.info("Finished loading packages")
logger.finished_loading()

# Resolve aliases.
if resolve_aliases:
logger.info("Starting alias resolution")
logger.starting_alias_resolution()
unresolved, iterations = loader.resolve_aliases(implicit=resolve_implicit, external=resolve_external)
if unresolved:
logger.info(f"{len(unresolved)} aliases were still unresolved after {iterations} iterations")
logger.unresolved_aliases(unresolved=len(unresolved), iterations=iterations)
else:
logger.info(f"All aliases were resolved after {iterations} iterations")
logger.all_resolved(iterations=iterations)
return loader


_level_choices = ("DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL")


def _extensions_type(value: str) -> Sequence[str | dict[str, Any]]:
try:
return json.loads(value)
Expand Down Expand Up @@ -190,7 +188,7 @@ def add_common_options(subparser: argparse.ArgumentParser) -> None:
"--log-level",
metavar="LEVEL",
default=DEFAULT_LOG_LEVEL,
choices=_level_choices,
choices=[level.value.upper() for level in LogLevel],
type=str.upper,
help="Set the log level: `DEBUG`, `INFO`, `WARNING`, `ERROR`, `CRITICAL`.",
)
Expand Down Expand Up @@ -489,6 +487,22 @@ def check(
return 0


class _ColorFormatter(logging.Formatter):
COLORS: ClassVar[dict[int, str]] = {
LogLevel.trace.number: Style.DIM,
LogLevel.debug.number: "",
LogLevel.info.number: Fore.BLUE,
LogLevel.success.number: Fore.GREEN,
LogLevel.warning.number: Fore.YELLOW,
LogLevel.error.number: Fore.RED,
LogLevel.critical.number: Style.BRIGHT + Fore.RED,
}

def format(self, record: logging.LogRecord):
formatter = logging.Formatter(f"{self.COLORS[record.levelno]}{self._fmt}{Style.RESET_ALL}")
return formatter.format(record)


def main(args: list[str] | None = None) -> int:
"""Run the main program.
Expand Down Expand Up @@ -519,7 +533,9 @@ def main(args: list[str] | None = None) -> int:
)
return 1
else:
logging.basicConfig(format="%(levelname)-10s %(message)s", level=level)
handler = logging.StreamHandler()
handler.setFormatter(_ColorFormatter())
logging.basicConfig(format="%(message)s", level=level, handlers=[handler])

# Run subcommand.
commands: dict[str, Callable[..., int]] = {"check": check, "dump": dump}
Expand Down
4 changes: 2 additions & 2 deletions src/griffe/dataclasses.py
Original file line number Diff line number Diff line change
Expand Up @@ -465,7 +465,7 @@ def inherited_members(self) -> dict[str, Alias]:
try:
mro = self.mro()
except ValueError as error:
logger.debug(error)
logger.cannot_compute_mro(error=error)
return {}
inherited_members = {}
for base in reversed(mro):
Expand Down Expand Up @@ -1588,7 +1588,7 @@ def resolved_bases(self) -> list[Object]:
if resolved_base.is_alias:
resolved_base = resolved_base.final_target
except (AliasResolutionError, CyclicAliasError, KeyError):
logger.debug(f"Base class {base_path} is not loaded, or not static, it cannot be resolved")
logger.cannot_resolve_base_class(base_path=base_path)
else:
resolved_bases.append(resolved_base)
return resolved_bases
Expand Down
6 changes: 3 additions & 3 deletions src/griffe/diff.py
Original file line number Diff line number Diff line change
Expand Up @@ -447,7 +447,7 @@ def _alias_incompatibilities(
old_member = old_obj.target if old_obj.is_alias else old_obj # type: ignore[union-attr]
new_member = new_obj.target if new_obj.is_alias else new_obj # type: ignore[union-attr]
except AliasResolutionError:
logger.debug(f"API check: {old_obj.path} | {new_obj.path}: skip alias with unknown target")
logger.skip_alias(old_obj_path=old_obj.path, new_obj_path=new_obj.path)
return

yield from _type_based_yield(old_member, new_member, ignore_private=ignore_private, seen_paths=seen_paths)
Expand All @@ -463,10 +463,10 @@ def _member_incompatibilities(
seen_paths = set() if seen_paths is None else seen_paths
for name, old_member in old_obj.all_members.items():
if ignore_private and name.startswith("_"):
logger.debug(f"API check: {old_obj.path}.{name}: skip private object")
logger.skip_private_object(obj_path=f"{old_obj.path}.{name}")
continue

logger.debug(f"API check: {old_obj.path}.{name}")
logger.checking(obj_path=f"{old_obj.path}.{name}")
try:
new_member = new_obj.all_members[name]
except KeyError:
Expand Down
6 changes: 1 addition & 5 deletions src/griffe/expressions.py
Original file line number Diff line number Diff line change
Expand Up @@ -899,11 +899,7 @@ def _build_constant(
optimize=1,
)
except SyntaxError:
logger.debug(
f"Tried and failed to parse {node.value!r} as Python code, "
"falling back to using it as a string literal "
"(postponed annotations might help: https://peps.python.org/pep-0563/)",
)
logger.failed_to_parse_node(node=node)
else:
return _build(parsed.body, parent, **kwargs) # type: ignore[attr-defined]
return {type(...): lambda _: "..."}.get(type(node.value), repr)(node.value)
Expand Down
2 changes: 1 addition & 1 deletion src/griffe/finder.py
Original file line number Diff line number Diff line change
Expand Up @@ -307,7 +307,7 @@ def iter_submodules(
for subpath in self._filter_py_modules(path):
rel_subpath = subpath.relative_to(path)
if rel_subpath.parent in skip:
logger.debug(f"Skip {subpath}, another module took precedence")
logger.skip_module(module_path=subpath)
continue
py_file = rel_subpath.suffix == ".py"
stem = rel_subpath.stem
Expand Down
49 changes: 21 additions & 28 deletions src/griffe/loader.py
Original file line number Diff line number Diff line change
Expand Up @@ -168,9 +168,9 @@ def load(
)
obj_path: str
if objspec in _builtin_modules:
logger.debug(f"{objspec} is a builtin module")
logger.builtin_module(module=objspec)
if self.allow_inspection:
logger.debug(f"Inspecting {objspec}")
logger.inspecting_module(module=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)
Expand All @@ -185,16 +185,16 @@ def load(
find_stubs_package=find_stubs_package,
)
except ModuleNotFoundError:
logger.debug(f"Could not find {objspec}")
logger.could_not_find_module(module=objspec)
if self.allow_inspection:
logger.debug(f"Trying inspection on {objspec}")
logger.trying_inspection(module=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 {objspec}: loading")
logger.found(objspec=objspec)
try:
top_module = self._load_package(package, submodules=submodules)
except LoadingError as error:
Expand Down Expand Up @@ -252,9 +252,7 @@ def resolve_aliases(
)
resolved |= next_resolved
unresolved |= next_unresolved
logger.debug(
f"Iteration {iteration} finished, {len(resolved)} aliases resolved, still {len(unresolved)} to go",
)
logger.finished_iteration(iteration=iteration, resolved=len(resolved), unresolved=len(unresolved))
return unresolved, iteration

def expand_exports(self, module: Module, seen: set | None = None) -> None:
Expand All @@ -277,14 +275,14 @@ def expand_exports(self, module: Module, seen: set | None = None) -> None:
try:
next_module = self.modules_collection.get_member(module_path)
except KeyError:
logger.debug(f"Cannot expand '{export.canonical_path}', try pre-loading corresponding package")
logger.cannot_expand(path=export.canonical_path)
continue
if next_module.path not in seen:
self.expand_exports(next_module, seen)
try:
expanded |= next_module.exports
except TypeError:
logger.warning(f"Unsupported item in {module.path}.__all__: {export} (use strings only)")
logger.unsupported_all_item(module_path=module.path, item=export)
# It's a string, simply add it to the current exports.
else:
expanded.add(export)
Expand Down Expand Up @@ -324,16 +322,15 @@ def expand_wildcards(
try:
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}")
logger.could_not_expand_wildcard_error(name=member.name, path=obj.path, error=error)
continue

# Try getting the module from which every public object is imported.
try:
target = self.modules_collection.get_member(member.target_path) # type: ignore[union-attr]
except KeyError:
logger.debug(
f"Could not expand wildcard import {member.name} in {obj.path}: "
f"{cast(Alias, member).target_path} not found in modules collection",
logger.could_not_expand_wildcard_import_error(
name=member.name, path=obj.path, target=cast(Alias, member).target_path
)
continue

Expand All @@ -342,7 +339,7 @@ def expand_wildcards(
try:
self.expand_wildcards(target, external=external, seen=seen)
except (AliasResolutionError, CyclicAliasError) as error:
logger.debug(f"Could not expand wildcard import {member.name} in {obj.path}: {error}")
logger.could_not_expand_wildcard_error(name=member.name, path=obj.path, error=error)
continue

# Collect every imported object.
Expand Down Expand Up @@ -454,16 +451,16 @@ def resolve_module_aliases(
and package not in self.modules_collection
)
if load_module:
logger.debug(f"Failed to resolve alias {member.path} -> {target}")
logger.failed_to_resolve_alias(path=member.path, target=target)
try:
self.load(package, try_relative_path=False)
except ImportError as error:
logger.debug(f"Could not follow alias {member.path}: {error}")
logger.could_not_follow_alias(path=member.path, error=error)
load_failures.add(package)
except CyclicAliasError as error:
logger.debug(str(error))
logger.cyclic_alias_error(error=error)
else:
logger.debug(f"Alias {member.path} was resolved to {member.final_target.path}") # type: ignore[union-attr]
logger.alias_resolved(path=member.path, final_path=member.final_target.path) # type: ignore[union-attr]
resolved.add(member.path)

# Recurse into unseen modules and classes.
Expand Down Expand Up @@ -531,7 +528,7 @@ def _load_module_path(
submodules: bool = True,
parent: Module | None = None,
) -> Module:
logger.debug(f"Loading path {module_path}")
logger.loading_path(filepath=module_path)
if isinstance(module_path, list):
module = self._create_module(module_name, module_path)
elif module_path.suffix in {".py", ".pyi"}:
Expand All @@ -552,7 +549,7 @@ def _load_submodules(self, module: Module) -> None:
def _load_submodule(self, module: Module, subparts: tuple[str, ...], subpath: Path) -> None:
for subpart in subparts:
if "." in subpart:
logger.debug(f"Skip {subpath}, dots in filenames are not supported")
logger.skip_module_with_dots(module=subpath)
return
try:
parent_module = self._get_or_create_parent_module(module, subparts, subpath)
Expand All @@ -575,8 +572,8 @@ def _load_submodule(self, module: Module, subparts: tuple[str, ...], subpath: Pa
# but that's because in that case Python acts like the whole tree is a regular package.
# It works when the namespace package appears in only one search path (`sys.path`),
# but will fail if it appears in multiple search paths: Python will only find the first occurrence.
# It's better to not falsely suuport this, and to warn users.
logger.debug(f"{error}. Missing __init__ module?")
# It's better to not falsely support this, and to warn users.
logger.missing_init_module(error=error)
return
submodule_name = subparts[-1]
try:
Expand All @@ -590,11 +587,7 @@ def _load_submodule(self, module: Module, subparts: tuple[str, ...], subpath: Pa
logger.debug(str(error))
else:
if submodule_name in parent_module.members:
logger.debug(
f"Submodule '{submodule.path}' is shadowing the member at the same path. "
"We recommend renaming the member or the submodule (for example prefixing it with `_`), "
"see https://mkdocstrings.github.io/griffe/best_practices/#avoid-member-submodule-name-shadowing.",
)
logger.submodule_shadowing_member(filepath=submodule.filepath, path=submodule.path)
parent_module.set_member(submodule_name, submodule)

def _create_module(self, module_name: str, module_path: Path | list[Path]) -> Module:
Expand Down
Loading

0 comments on commit 5118a46

Please sign in to comment.