Skip to content

Structural diff

structural_diff

Structural branch diff — symbol-level comparison between git refs.

Uses git worktrees to checkout both refs and analyze_package() on each version, then compares symbol sets by name + signature.

Example

from axm_ast.core.structural_diff import structural_diff result = structural_diff(Path("src/mylib"), "main", "feature") result["added"] [{"name": "new_func", "kind": "function", "file": "core.py", ...}]

DiffError

Bases: TypedDict

Error payload returned when validation or extraction fails.

Source code in packages/axm-ast/src/axm_ast/core/structural_diff.py
Python
class DiffError(TypedDict):
    """Error payload returned when validation or extraction fails."""

    error: str

DiffSummary

Bases: TypedDict

Aggregate counts for a structural diff.

Source code in packages/axm-ast/src/axm_ast/core/structural_diff.py
Python
class DiffSummary(TypedDict):
    """Aggregate counts for a structural diff."""

    added: int
    removed: int
    modified: int

ModifiedSymbol

Bases: TypedDict

Symbol whose signature differs between base and head.

Source code in packages/axm-ast/src/axm_ast/core/structural_diff.py
Python
class ModifiedSymbol(TypedDict):
    """Symbol whose signature differs between base and head."""

    name: str
    kind: str
    file: str
    old_signature: str | None
    new_signature: str | None

StructuralDiffResult

Bases: TypedDict

Output of :func:structural_diff.

total=False so the error variant ({"error": str}) also matches.

Source code in packages/axm-ast/src/axm_ast/core/structural_diff.py
Python
class StructuralDiffResult(TypedDict, total=False):
    """Output of :func:`structural_diff`.

    ``total=False`` so the error variant (``{"error": str}``) also matches.
    """

    added: list[SymbolEntry]
    removed: list[SymbolEntry]
    modified: list[ModifiedSymbol]
    summary: DiffSummary
    error: str

SymbolEntry

Bases: TypedDict

Single symbol record extracted from a package at a given ref.

Source code in packages/axm-ast/src/axm_ast/core/structural_diff.py
Python
class SymbolEntry(TypedDict):
    """Single symbol record extracted from a package at a given ref."""

    name: str
    kind: str
    file: str
    signature: str | None

structural_diff(pkg_path, base, head)

Compare two git refs at symbol level.

Uses git worktrees to checkout the base ref, runs analyze_package() on both versions, and diffs the symbol sets.

Parameters:

Name Type Description Default
pkg_path Path

Path to the package directory.

required
base str

Base git ref (branch, tag, or commit).

required
head str

Head git ref (branch, tag, or commit).

required

Returns:

Type Description
StructuralDiffResult

Dict with added, removed, modified, and

StructuralDiffResult

summary keys. On error, returns a dict with an

StructuralDiffResult

error key.

Example

result = structural_diff(Path("src/mylib"), "main", "feature") len(result["added"]) 3

Source code in packages/axm-ast/src/axm_ast/core/structural_diff.py
Python
def structural_diff(
    pkg_path: Path,
    base: str,
    head: str,
) -> StructuralDiffResult:
    """Compare two git refs at symbol level.

    Uses git worktrees to checkout the *base* ref, runs
    ``analyze_package()`` on both versions, and diffs the
    symbol sets.

    Args:
        pkg_path: Path to the package directory.
        base: Base git ref (branch, tag, or commit).
        head: Head git ref (branch, tag, or commit).

    Returns:
        Dict with ``added``, ``removed``, ``modified``, and
        ``summary`` keys.  On error, returns a dict with an
        ``error`` key.

    Example:
        >>> result = structural_diff(Path("src/mylib"), "main", "feature")
        >>> len(result["added"])
        3
    """
    pkg_path = pkg_path.resolve()

    validated = _validate_diff_inputs(pkg_path, base, head)
    if isinstance(validated, dict):
        return StructuralDiffResult(error=validated["error"])
    project_root, pkg_rel = validated

    head_symbols = _extract_symbols_at_ref(project_root, pkg_rel, head)
    if isinstance(head_symbols, str):
        return StructuralDiffResult(error=head_symbols)

    base_symbols = _extract_symbols_at_ref(project_root, pkg_rel, base)
    if isinstance(base_symbols, str):
        return StructuralDiffResult(error=base_symbols)

    return _compute_diff(base_symbols, head_symbols)