orchestrata_tools.backcompat.ast_explorer

Reference › orchestrata_tools › backcompat › ast_explorer

This module is a command-line wrapper that keeps the old ast_explorer.py interface alive on top of the orchestrata_tools library. Nothing imports it, and tests/test_orchestrata_tools_backcompat.py covers it.

main parses the legacy flags and loads a serialized AST file (default plans/ast-ultimate.json) with json_loader, then builds an ASTIndex. Each flag prints one report: --summary prints module, class, method and line totals and the counts of abstract and protocol classes; --untested lists untested public methods and --coverage and --gaps print coverage figures and gaps, both using the matcher and analyzer from test_analyzer against a second AST file (default plans/ast-tests.json); --no-docstring, --largest-modules and --class NAME print index queries. --limit caps list length. The flags --stubs and --readiness are declared but have no handler in main. The current command line is __main__.

At a glance

Source src/orchestrata_tools/backcompat/ast_explorer.py
Lines of code 89
Classes 0
Functions 1
Imports (internal) orchestrata_tools.parser, orchestrata_tools.test_analyzer
Imported by none
Tests that import it tests/test_orchestrata_tools_backcompat.py

Class diagram

No classes; see Functions.

Dependency diagram

Dependency diagram orchestrata_tools.backcompat.ast_explorer

API

Functions

Function Signature Summary
main main(argv: list[str] | None = None) -> int

Source

Permalink: https://gitlab.com/MSamy/orchestrata-ultimate-architecture/-/blob/a4cbc2bdf/src/orchestrata_tools/backcompat/ast_explorer.py

#!/usr/bin/env python3
"""Backward-compatible wrapper for ast_explorer.py."""

from __future__ import annotations

import argparse
import sys

from orchestrata_tools.parser import ASTIndex, JSONASTLoader
from orchestrata_tools.test_analyzer import CoverageAnalyzer, TestMatcher


def main(argv: list[str] | None = None) -> int:
    parser = argparse.ArgumentParser(description="AST Codebase Explorer")
    parser.add_argument("--ast", default="plans/ast-ultimate.json")
    parser.add_argument("--summary", action="store_true")
    parser.add_argument("--untested", action="store_true")
    parser.add_argument("--test-ast", default="plans/ast-tests.json")
    parser.add_argument("--coverage", action="store_true")
    parser.add_argument("--gaps", action="store_true")
    parser.add_argument("--no-docstring", action="store_true")
    parser.add_argument("--largest-modules", action="store_true")
    parser.add_argument("--class", dest="class_name")
    parser.add_argument("--limit", type=int, default=20)
    parser.add_argument("--stubs", action="store_true")
    parser.add_argument("--readiness", action="store_true")

    args = parser.parse_args(argv)

    ast = JSONASTLoader().load(args.ast)
    index = ASTIndex(ast)
    if args.summary:
        print(f"Modules: {len(ast.modules)}")
        print(f"Classes: {len(ast.all_classes)}")
        print(f"Methods: {len(ast.all_methods)}")
        print(f"LOC: {ast.total_loc}")
        print(f"Abstract classes: {len(index.abstract_classes())}")
        print(f"Protocol classes: {len(index.protocol_classes())}")

    if args.untested:
        test_ast = JSONASTLoader().load(args.test_ast)
        matcher = TestMatcher(test_ast)
        untested = matcher.find_untested_methods(index)
        print(f"\nUNTESTED PUBLIC METHODS: {len(untested)}")
        for m in untested[: args.limit]:
            print(f"  {m.module_path}.{m.class_name}.{m.name} (line {m.line})")

    if args.coverage:
        test_ast = JSONASTLoader().load(args.test_ast)
        matcher = TestMatcher(test_ast)
        analyzer = CoverageAnalyzer(matcher, index)
        report = analyzer.analyze()
        print(f"\nMethod coverage: {report.method_coverage_pct}%")
        print(f"Class coverage: {report.class_coverage_pct}%")

    if args.gaps:
        test_ast = JSONASTLoader().load(args.test_ast)
        matcher = TestMatcher(test_ast)
        analyzer = CoverageAnalyzer(matcher, index)
        report = analyzer.analyze()
        for gap in report.gaps[: args.limit]:
            print(f"  {gap.module_path}.{gap.class_name}.{gap.method.name}")

    if args.no_docstring:
        methods = index.methods_without_docstrings()
        print(f"\nMethods without docstrings: {len(methods)}")
        for m in methods[: args.limit]:
            print(f"  {m.qualified_name}")

    if args.largest_modules:
        print("\nLargest modules:")
        for path, loc, cc in index.largest_modules(args.limit):
            print(f"  {path}: {loc} lines, {cc} classes")

    if args.class_name:
        cls = index.find_class(args.class_name)
        if cls:
            print(f"\nClass: {cls.name}")
            print(f"  Module: {cls.module_path}")
            print(f"  Methods: {cls.method_count}")
            print(f"  Bases: {cls.bases}")
            for m in cls.methods:
                print(f"    {m.name}()")

    return 0


if __name__ == "__main__":
    sys.exit(main())