Skip to content

Inspect

inspect

InspectTool — inspect a single symbol by name.

InspectTool

Bases: AXMTool

Inspect a symbol across the package without knowing its file.

Registered as ast_inspect via axm.tools entry point.

Source code in packages/axm-ast/src/axm_ast/tools/inspect.py
Python
class InspectTool(AXMTool):
    """Inspect a symbol across the package without knowing its file.

    Registered as ``ast_inspect`` via axm.tools entry point.
    """

    expose_directly = True
    domain = "ast"
    tags = frozenset({"inspect", "symbol", "source"})

    agent_hint: str = (
        "Get full detail of a symbol by name,"
        " without knowing the file."
        " Returns file, start_line, end_line."
        " Use source=True to include source code."
        " Supports dotted paths like ClassName.method."
        " You can also pass a list of names via `symbols` for batch inspection."
    )

    @property
    def name(self) -> str:
        """Return tool name for registry lookup."""
        return "ast_inspect"

    @safe_execute
    def execute(
        self,
        *,
        path: str = ".",
        symbol: str | None = None,
        symbols: list[str] | None = None,
        source: bool = False,
        **kwargs: object,
    ) -> ToolResult:
        """Inspect a symbol by name.

        Args:
            path: Path to package directory.
            symbol: Symbol name to inspect (required if symbols is not provided).
                Supports dotted paths like ``ClassName.method``.
            symbols: Optional list of symbol names for batch inspection.
            source: If True, include source code in the response.

        Returns:
            ToolResult with symbol details.
        """
        if not symbol and not symbols:
            return ToolResult(
                success=False, error="symbol or symbols parameter is required"
            )

        source = bool(source)

        project_path = resolve_path(path)
        if isinstance(project_path, ToolResult):
            return project_path

        try:
            if symbols is not None:
                return self._inspect_batch(project_path, symbols, source=source)

            return self._inspect_symbol(project_path, symbol, source=source)  # type: ignore[arg-type]
        except Exception as exc:  # noqa: BLE001
            return ToolResult(success=False, error=str(exc))

    def _inspect_batch(
        self,
        project_path: Path,
        symbols: list[str],
        *,
        source: bool,
    ) -> ToolResult:
        """Inspect multiple symbols in batch."""
        if not isinstance(symbols, list):
            return ToolResult(success=False, error="symbols parameter must be a list")
        results: list[dict[str, object]] = []
        for sym in symbols:
            res = self._inspect_symbol(project_path, sym, source=source)
            if res.success and res.data and "symbol" in res.data:
                results.append(cast("dict[str, object]", res.data["symbol"]))
            else:
                results.append({"name": sym, "error": res.error})
        try:
            text = render_batch_text(results)
        except (KeyError, TypeError):
            text = None
        return ToolResult(
            success=True,
            data={"symbols": results},
            text=text,
        )

    def _inspect_symbol(
        self, project_path: Path, symbol: str, *, source: bool = False
    ) -> ToolResult:
        """Resolve a simple or dotted ``symbol`` name to inspect details.

        For a simple name, the exact-name match is preferred: ``search_symbols``
        matches on substring, so a request for ``Foo`` would otherwise also pick
        a superset like ``FooBar``. The exact match wins; when several symbols
        share the requested name across modules, a disambiguation error is
        returned; when none match exactly, resolution falls through to the
        module fallback (never a substring superset).
        """
        from axm_ast.core.analyzer import search_symbols
        from axm_ast.core.cache import get_package

        pkg = get_package(project_path)

        if "." in symbol:
            return inspect_dotted(pkg, symbol, source=source)

        # --- Simple name: function or class ---
        results = search_symbols(
            pkg,
            name=symbol,
            returns=None,
            kind=None,
            inherits=None,
        )

        # `search_symbols` matches on substring, so a request for ``Foo`` also
        # returns supersets like ``FooBar``. Prefer the exact-name match; never
        # silently pick a substring superset (mirrors ``resolve_module``).
        exact = [(mod, sym) for mod, sym in results if sym.name == symbol]

        if len(exact) > 1:
            modules = sorted(f"{mod}.{symbol}" for mod, _ in exact)
            return ToolResult(
                success=False,
                error=f"Multiple symbols match '{symbol}': {', '.join(modules)}",
            )

        if not exact:
            # --- Module fallback ---
            mod_result = inspect_module(pkg, symbol, source=source)
            if mod_result is not None:
                return mod_result
            return ToolResult(
                success=False,
                error=f"Symbol '{symbol}' not found",
            )

        _, sym = exact[0]
        file_path = find_symbol_file(pkg, sym)
        abs_path = find_symbol_abs_path(pkg, sym)
        detail = build_detail(sym, file=file_path, abs_path=abs_path, source=source)
        return ToolResult(
            success=True,
            data={"symbol": detail},
            text=render_symbol_text(detail),
        )

    # --- Backward-compatible static aliases for extracted helpers ---

    _resolve_module = staticmethod(resolve_module)
name property

Return tool name for registry lookup.

execute(*, path='.', symbol=None, symbols=None, source=False, **kwargs)

Inspect a symbol by name.

Parameters:

Name Type Description Default
path str

Path to package directory.

'.'
symbol str | None

Symbol name to inspect (required if symbols is not provided). Supports dotted paths like ClassName.method.

None
symbols list[str] | None

Optional list of symbol names for batch inspection.

None
source bool

If True, include source code in the response.

False

Returns:

Type Description
ToolResult

ToolResult with symbol details.

Source code in packages/axm-ast/src/axm_ast/tools/inspect.py
Python
@safe_execute
def execute(
    self,
    *,
    path: str = ".",
    symbol: str | None = None,
    symbols: list[str] | None = None,
    source: bool = False,
    **kwargs: object,
) -> ToolResult:
    """Inspect a symbol by name.

    Args:
        path: Path to package directory.
        symbol: Symbol name to inspect (required if symbols is not provided).
            Supports dotted paths like ``ClassName.method``.
        symbols: Optional list of symbol names for batch inspection.
        source: If True, include source code in the response.

    Returns:
        ToolResult with symbol details.
    """
    if not symbol and not symbols:
        return ToolResult(
            success=False, error="symbol or symbols parameter is required"
        )

    source = bool(source)

    project_path = resolve_path(path)
    if isinstance(project_path, ToolResult):
        return project_path

    try:
        if symbols is not None:
            return self._inspect_batch(project_path, symbols, source=source)

        return self._inspect_symbol(project_path, symbol, source=source)  # type: ignore[arg-type]
    except Exception as exc:  # noqa: BLE001
        return ToolResult(success=False, error=str(exc))