Skip to content

Index

core

Core parsing and analysis engine.

This module re-exports the main entry points: - parse_file / extract_module_info — tree-sitter parsing - analyze_package — high-level package analysis - get_package / clear_cache — cached package access

analyze_package(path)

Analyze a Python package directory.

Discovers all .py files, parses them with tree-sitter, and builds a complete PackageInfo with dependency edges.

Parameters:

Name Type Description Default
path Path

Path to the package root directory.

required

Returns:

Type Description
PackageInfo

PackageInfo with all modules and dependency edges.

Raises:

Type Description
ValueError

If path is not a directory.

Example

pkg = analyze_package(Path("src/mylib")) pkg.name 'mylib'

Source code in packages/axm-ast/src/axm_ast/core/analyzer.py
def analyze_package(path: Path) -> PackageInfo:
    """Analyze a Python package directory.

    Discovers all ``.py`` files, parses them with tree-sitter, and
    builds a complete ``PackageInfo`` with dependency edges.

    Args:
        path: Path to the package root directory.

    Returns:
        PackageInfo with all modules and dependency edges.

    Raises:
        ValueError: If path is not a directory.

    Example:
        >>> pkg = analyze_package(Path("src/mylib"))
        >>> pkg.name
        'mylib'
    """
    path = Path(path).resolve()
    if not path.is_dir():
        msg = f"{path} is not a directory"
        raise ValueError(msg)

    t0 = time.perf_counter()

    # Discover all .py files, skipping virtual envs and caches
    py_files = sorted(_discover_py_files(path))
    modules: list[ModuleInfo] = []
    for py_file in py_files:
        modules.append(extract_module_info(py_file))

    # Build dependency edges from internal imports
    dep_edges = _build_edges(modules, path)

    pkg = PackageInfo(
        name=path.name,
        root=path,
        modules=modules,
        dependency_edges=dep_edges,
    )

    elapsed = time.perf_counter() - t0
    logger.debug("Analyzed %s in %.2fs (%d modules)", path.name, elapsed, len(modules))

    return pkg

clear_cache()

Reset the global PackageCache, forcing re-parsing on next call.

Source code in packages/axm-ast/src/axm_ast/core/cache.py
def clear_cache() -> None:
    """Reset the global ``PackageCache``, forcing re-parsing on next call."""
    _cache.clear()

extract_module_info(path)

Extract full module information from a Python file.

Parses the file using tree-sitter and extracts all functions, classes, imports, variables, and the module docstring.

Parameters:

Name Type Description Default
path Path

Path to a .py file.

required

Returns:

Type Description
ModuleInfo

ModuleInfo with all extracted metadata.

Raises:

Type Description
FileNotFoundError

If the file does not exist.

ValueError

If the file is not a .py file.

Example

from pathlib import Path mod = extract_module_info(Path("my_module.py")) mod.path.name 'my_module.py'

Source code in packages/axm-ast/src/axm_ast/core/parser.py
def extract_module_info(path: Path) -> ModuleInfo:
    """Extract full module information from a Python file.

    Parses the file using tree-sitter and extracts all functions,
    classes, imports, variables, and the module docstring.

    Args:
        path: Path to a .py file.

    Returns:
        ModuleInfo with all extracted metadata.

    Raises:
        FileNotFoundError: If the file does not exist.
        ValueError: If the file is not a .py file.

    Example:
        >>> from pathlib import Path
        >>> mod = extract_module_info(Path("my_module.py"))
        >>> mod.path.name
        'my_module.py'
    """
    tree = parse_file(path)
    root = tree.root_node

    docstring = _extract_docstring(root)
    all_exports = _extract_all_exports(root)

    functions: list[FunctionInfo] = []
    classes: list[ClassInfo] = []
    imports: list[ImportInfo] = []
    variables: list[VariableInfo] = []

    for child in root.children:
        if child.type == "function_definition":
            functions.append(_extract_function(child))
        elif child.type == "decorated_definition":
            _process_decorated(child, functions, classes)
        elif child.type == "class_definition":
            classes.append(_extract_class(child))
        elif child.type in (
            "import_statement",
            "import_from_statement",
            "future_import_statement",
        ):
            imports.extend(_extract_imports(child))
        elif child.type == "expression_statement":
            var = _extract_variable(child)
            if var is not None and var.name != "__all__":
                variables.append(var)

    return ModuleInfo(
        path=path.resolve(),
        docstring=docstring,
        functions=functions,
        classes=classes,
        imports=imports,
        variables=variables,
        all_exports=all_exports,
    )

get_package(path)

Return PackageInfo for path, using the global cache.

Equivalent to analyze_package(path) but avoids re-parsing if the same path was already analyzed in this session.

Parameters:

Name Type Description Default
path Path

Path to the package root directory.

required

Returns:

Type Description
PackageInfo

Cached or freshly parsed PackageInfo.

Source code in packages/axm-ast/src/axm_ast/core/cache.py
def get_package(path: Path) -> PackageInfo:
    """Return ``PackageInfo`` for *path*, using the global cache.

    Equivalent to ``analyze_package(path)`` but avoids re-parsing
    if the same *path* was already analyzed in this session.

    Args:
        path: Path to the package root directory.

    Returns:
        Cached or freshly parsed ``PackageInfo``.
    """
    return _cache.get(path)

parse_file(path)

Parse a Python file into a tree-sitter Tree.

Parameters:

Name Type Description Default
path Path

Path to a .py file.

required

Returns:

Type Description
Tree

Parsed tree-sitter Tree.

Raises:

Type Description
FileNotFoundError

If the file does not exist.

ValueError

If the file is not a .py file.

Example

from pathlib import Path tree = parse_file(Path("setup.py")) tree.root_node.type 'module'

Source code in packages/axm-ast/src/axm_ast/core/parser.py
def parse_file(path: Path) -> Tree:
    """Parse a Python file into a tree-sitter Tree.

    Args:
        path: Path to a .py file.

    Returns:
        Parsed tree-sitter Tree.

    Raises:
        FileNotFoundError: If the file does not exist.
        ValueError: If the file is not a .py file.

    Example:
        >>> from pathlib import Path
        >>> tree = parse_file(Path("setup.py"))
        >>> tree.root_node.type
        'module'
    """
    path = Path(path).resolve()
    if not path.exists():
        msg = f"File not found: {path}"
        raise FileNotFoundError(msg)
    if path.suffix != ".py":
        msg = f"Not a Python file: {path}"
        raise ValueError(msg)
    source = path.read_text(encoding="utf-8")
    return parse_source(source)