Source code for autodoc2.utils

"""Utility functions and types."""
from __future__ import annotations

import enum
from fnmatch import fnmatch
import os
from pathlib import Path
import typing as t

from typing_extensions import Required

PROPERTY_TYPE = t.Literal[
    "async", "staticmethod", "classmethod", "abstractmethod", "singledispatch"
]
ARGS_TYPE = t.List[
    t.Tuple[t.Optional[str], t.Optional[str], t.Optional[str], t.Optional[str]]
]


[docs]class ItemData(t.TypedDict, total=False): """A data item, for the results of the analysis.""" type: Required[str] full_name: Required[str] # this is delimited by dots doc: Required[str] range: tuple[int, int] ## module / package file_path: None | str encoding: str all: None | list[str] imports: list[tuple[str, str | None]] # path, alias # assign (data) value: None | str | t.Any # TODO make value JSON serializable annotation: None | str # function/method/overload properties: list[PROPERTY_TYPE] args: ARGS_TYPE return_annotation: None | str # class bases: list[str] # class or method doc_inherited: str # child of class inherited: str
[docs]class WarningSubtypes(enum.Enum): """The subtypes of warnings for the extension.""" CONFIG_ERROR = "config_error" """Issue with configuration validation.""" GIT_CLONE_FAILED = "git_clone" """Failed to clone a git repository.""" MISSING_MODULE = "missing_module" """If the package file/folder does not exist.""" DUPLICATE_ITEM = "dup_item" """Duplicate fully qualified name found during package analysis.""" RENDER_ERROR = "render" """Generic rendering error.""" ALL_MISSING = "all_missing" """__all__ attribute missing or empty in a module.""" ALL_RESOLUTION = "all_resolve" """Issue with resolution of an item in a module's __all__ attribute.""" NAME_NOT_FOUND = "missing"
[docs]def yield_modules( folder: str | Path, *, root_module: str | None = None, extensions: t.Sequence[str] = (".py", ".pyi"), exclude_dirs: t.Sequence[str] = ("__pycache__",), exclude_files: t.Sequence[str] = (), ) -> t.Iterable[tuple[Path, str]]: """Walk the given folder and yield all required modules. :param folder: The path to walk. :param root_module: The name of the root module, otherwise the folder name is used. :param extensions: The extensions to include. If multiple files with the same stem, only the first extension will be used. :param exclude_dirs: Directory names to exclude (matched with fnmatch). :param exclude_files: File names to exclude (matched with fnmatch). """ folder = Path(folder) root_mod = (root_module or folder.name).split(".") exc_dirs = set(exclude_dirs or []) exc_files = set(exclude_files or []) def _suffix_sort_key(s: str) -> int: return extensions.index(s) for root, dirs, filenames in os.walk(folder, topdown=True): dirs[:] = [d for d in dirs if not any(fnmatch(d, m) for m in exc_dirs)] to_yield: dict[str, list[str]] = {} for filename in filenames: if any(fnmatch(filename, m) for m in exc_files): continue name, suffix = os.path.splitext(filename) if suffix in extensions: to_yield.setdefault(name, []).append(suffix) root_path = Path(root) rel_mod = root_path.relative_to(folder).parts for name, suffixes in to_yield.items(): suffix = sorted(suffixes, key=_suffix_sort_key)[0] yield (root_path / f"{name}{suffix}", ".".join([*root_mod, *rel_mod, name]))