Hide navigation sidebar
Hide table of contents sidebar
Skip to content
Back to top
Toggle table of contents sidebar

# Source code for autodoc2.sphinx.utils

    """Handle sphinx logging."""
    from __future__ import annotations

    import typing as t

    from docutils.nodes import Element
    from docutils.statemachine import StringList
    from sphinx.application import Sphinx
    from sphinx.environment import BuildEnvironment
    from sphinx.util import logging

    from autodoc2.config import CONFIG_PREFIX, Config, ValidationError
    from autodoc2.db import Database, InMemoryDb
    from autodoc2.resolve_all import AllResolver
    from autodoc2.utils import WarningSubtypes

    if t.TYPE_CHECKING:
        from docutils.parsers.rst.states import RSTStateMachine

    LOGGER = logging.getLogger("autodoc2")

    def load_config(
        app: Sphinx,
        *,
        overrides: None | dict[str, t.Any] = None,
        location: None | Element = None,
    ) -> Config:
        """Load the configuration."""
        values: dict[str, t.Any] = {}
        overrides = overrides or {}
        config_fields = {name: field for name, _, field in Config().as_triple()}
        # check if keys in overrides are valid
        difference = set(overrides.keys()) - set(config_fields.keys())
        if difference:
            warn_sphinx(
                f"Unknown configuration keys: {', '.join(difference)}",
                WarningSubtypes.CONFIG_ERROR,
                location,
            )
        for name, field in config_fields.items():
            sphinx_name = f"{CONFIG_PREFIX}{name}"
            value = overrides.get(name, app.config[sphinx_name])
            if "sphinx_validate" in field.metadata:
                try:
                    value = field.metadata["sphinx_validate"](sphinx_name, value)
                except ValidationError as err:
                    warn_sphinx(str(err), WarningSubtypes.CONFIG_ERROR, location)
                    continue
            values[name] = value
        return Config(**values)

    def warn_sphinx(
        msg: str, subtype: WarningSubtypes, location: None | Element = None
    ) -> None:
        """Log a warning in Sphinx."""
        LOGGER.warning(
            f"{msg} [autodoc2.{subtype.value}]",
            type="autodoc2",
            subtype=subtype.value,
            location=location,
        )

    def get_database(env: BuildEnvironment) -> Database:
        """Get the database from the environment."""
        # We store it persistently in the environment so that it can be
        # accessed by directives and other extensions.
        if not hasattr(env, "autodoc2_db"):
            env.autodoc2_db = InMemoryDb()  # type: ignore
        return env.autodoc2_db  # type: ignore

    def _warn(msg: str) -> None:
        warn_sphinx(msg, WarningSubtypes.ALL_RESOLUTION)

    def get_all_analyser(env: BuildEnvironment) -> AllResolver:
        """Get the all analyser from the environment."""
        # We store it persistently in the environment so that it can be
        # accessed by directives and other extensions.
        if not hasattr(env, "autodoc2_all_analyser"):
            env.autodoc2_all_analyser = AllResolver(get_database(env), _warn)  # type: ignore
        return env.autodoc2_all_analyser  # type: ignore

    def nested_parse_generated(
        state: RSTStateMachine,
        content: list[str],
        source: str,
        line: int,
        *,
        match_titles: bool = False,
        base: Element | None = None,
    ) -> Element:
        """This function, nested parses generated content in a directive.

        All reported warnings are redirected to a specific source document and line.

        This is useful for directives that want to parse generated content.
        """
        base = Element() if base is None else base
        base.source = source
        base.line = line
        string_list = StringList(
            content,
            items=[(source, line - 1) for _ in content],
        )
        line_func = getattr(state.reporter, "get_source_and_line", None)
        state.reporter.get_source_and_line = lambda li: (
            source,
            line,
        )
        try:
            state.nested_parse(string_list, 0, base, match_titles=match_titles)
        finally:
            if line_func is not None:
                state.reporter.get_source_and_line = line_func
            else:
                del state.reporter.get_source_and_line

        return base