Skip to content

utils

_utils

Shared utilities for audit check modules.

load_exclusions(project)

Load per-package check exclusions from pyproject.toml.

Reads the [tool.axm-init].exclude key and returns check name prefixes that should be auto-passed for this package.

Example config::

Text Only
[tool.axm-init]
exclude = ["cli", "changelog", "deps.entry_points"]

Parameters:

Name Type Description Default
project Path

Path to the project root containing pyproject.toml.

required

Returns:

Type Description
set[str]

Set of check name prefixes to exclude. Empty set if no

set[str]

exclusions are configured.

Source code in packages/axm-init/src/axm_init/checks/_utils.py
Python
def load_exclusions(project: Path) -> set[str]:
    """Load per-package check exclusions from pyproject.toml.

    Reads the ``[tool.axm-init].exclude`` key and returns check name
    prefixes that should be auto-passed for this package.

    Example config::

        [tool.axm-init]
        exclude = ["cli", "changelog", "deps.entry_points"]

    Args:
        project: Path to the project root containing ``pyproject.toml``.

    Returns:
        Set of check name prefixes to exclude.  Empty set if no
        exclusions are configured.
    """
    data = load_toml(project)
    if data is None:
        return set()

    axm_init_config = section(section(data, "tool"), "axm-init")
    if not axm_init_config:
        return set()

    raw = axm_init_config.get("exclude")
    if raw is None:
        return set()

    # Handle string → wrap in list
    if isinstance(raw, str):
        raw = [raw]

    if not isinstance(raw, list):
        logger.warning(
            "Invalid [tool.axm-init].exclude value (expected list): %r",
            raw,
        )
        return set()

    exclusions: set[str] = set()
    for item in raw:
        if isinstance(item, str) and item:
            exclusions.add(item)
        else:
            logger.warning(
                "Invalid exclusion entry in [tool.axm-init].exclude: %r",
                item,
            )

    return exclusions

load_toml(project)

Load pyproject.toml, return None if missing/corrupt.

Source code in packages/axm-init/src/axm_init/checks/_utils.py
Python
def load_toml(project: Path) -> dict[str, object] | None:
    """Load pyproject.toml, return None if missing/corrupt."""
    path = project / "pyproject.toml"
    if not path.exists():
        return None
    try:
        with path.open("rb") as f:
            return tomllib.load(f)
    except Exception:
        return None

load_toml_with_workspace_fallback(project)

Load pyproject.toml, merging workspace root tool sections for members.

When project is a UV workspace member, the workspace root's tool sections are used as a base layer. The member's own tool sections override on top (per top-level tool key).

Source code in packages/axm-init/src/axm_init/checks/_utils.py
Python
def load_toml_with_workspace_fallback(project: Path) -> dict[str, object] | None:
    """Load pyproject.toml, merging workspace root tool sections for members.

    When *project* is a UV workspace member, the workspace root's tool
    sections are used as a base layer.  The member's own tool sections
    override on top (per top-level tool key).
    """
    from axm_init.checks._workspace import find_workspace_root

    data = load_toml(project)
    if data is None:
        return None

    workspace_root = find_workspace_root(project)
    if workspace_root is None or workspace_root == project:
        return data

    root_data = load_toml(workspace_root)
    if root_data is None:
        return data

    return merge_tool_sections(root_data, data)

merge_tool_sections(base, override)

Deep-merge tool sections: override wins at leaf level.

For each tool key (ruff, mypy, coverage, …), nested dicts are merged recursively so that both root and member settings are preserved. Non-dict values use the override if present, else the base. Non-tool keys always come from override.

Source code in packages/axm-init/src/axm_init/checks/_utils.py
Python
def merge_tool_sections(
    base: dict[str, object],
    override: dict[str, object],
) -> dict[str, object]:
    """Deep-merge tool sections: *override* wins at leaf level.

    For each tool key (ruff, mypy, coverage, …), nested dicts are merged
    recursively so that both root and member settings are preserved.
    Non-dict values use the override if present, else the base.
    Non-tool keys always come from *override*.
    """
    merged: dict[str, object] = dict(override)
    base_tool = base.get("tool")
    override_tool = override.get("tool")
    if not isinstance(base_tool, dict):
        return merged
    if not isinstance(override_tool, dict):
        override_tool = {}
    merged["tool"] = _deep_merge(base_tool, override_tool)
    return merged

requires_toml(check_name, category, weight, fix)

Decorator that loads pyproject.toml and passes data to the check.

If pyproject.toml is missing or unparsable, returns a failure CheckResult immediately — eliminating the repeated null-guard preamble from every check function.

The decorated function receives (project, data) instead of just (project) — where data is the parsed TOML dict.

Parameters:

Name Type Description Default
check_name str

Check result name (e.g. "pyproject.ruff").

required
category str

Category key (e.g. "pyproject").

required
weight int

Points weight for this check.

required
fix str

Fix message for the "not found" failure.

required
Source code in packages/axm-init/src/axm_init/checks/_utils.py
Python
def requires_toml(
    check_name: str,
    category: str,
    weight: int,
    fix: str,
) -> Callable[
    [Callable[[Path, TomlTable], CheckResult]],
    Callable[[Path], CheckResult],
]:
    """Decorator that loads pyproject.toml and passes data to the check.

    If pyproject.toml is missing or unparsable, returns a failure
    ``CheckResult`` immediately — eliminating the repeated null-guard
    preamble from every check function.

    The decorated function receives ``(project, data)`` instead of just
    ``(project)`` — where ``data`` is the parsed TOML dict.

    Args:
        check_name: Check result name (e.g. ``"pyproject.ruff"``).
        category: Category key (e.g. ``"pyproject"``).
        weight: Points weight for this check.
        fix: Fix message for the "not found" failure.
    """

    def decorator(
        fn: Callable[[Path, TomlTable], CheckResult],
    ) -> Callable[[Path], CheckResult]:
        """Wrap a check function with TOML pre-loading."""

        @functools.wraps(fn)
        def wrapper(project: Path) -> CheckResult:
            """Load TOML then delegate to the wrapped check."""
            data = load_toml_with_workspace_fallback(project)
            if data is None:
                return CheckResult(
                    name=check_name,
                    category=category,
                    passed=False,
                    weight=weight,
                    message="pyproject.toml not found or unparsable",
                    details=[],
                    fix=fix,
                )
            return fn(project, data)

        return wrapper

    return decorator

section(data, key)

Return data[key] if it is a mapping, otherwise an empty mapping.

Helper for safely walking nested TOML structures without leaking object typing through .get() chains.

Source code in packages/axm-init/src/axm_init/checks/_utils.py
Python
def section(data: TomlTable, key: str) -> TomlTable:
    """Return ``data[key]`` if it is a mapping, otherwise an empty mapping.

    Helper for safely walking nested TOML structures without leaking
    ``object`` typing through ``.get()`` chains.
    """
    value = data.get(key)
    if isinstance(value, Mapping):
        return value
    return {}