orchestrata_tools.reviewer.code_reviewer

Reference › orchestrata_tools › reviewer › code_reviewer

CodeReviewer is a rule-based quality checker that ports the key logic of the old code-review.py script. It reads an ASTIndex and returns ReviewReport objects from models. The code_review wrapper prints its output.

review_method flags a missing docstring (medium), more than seven parameters (medium), a missing return annotation except on __init__ (low) and each parameter without a type annotation (low). review_class flags a missing docstring (medium), more than 20 methods as a god class (high), more than three bases (medium) and a protocol with no methods (low). review_module flags an empty module (info) and more than ten classes (low). review_codebase visits every module, its classes and each class's methods and returns all reports. Rules carry a suggestion text. The checks use metadata only, not method bodies.

At a glance

Source src/orchestrata_tools/reviewer/code_reviewer.py
Lines of code 174
Classes 1
Functions 0
Imports (internal) orchestrata_tools.models.ast_models, orchestrata_tools.parser.indexer, orchestrata_tools.reviewer.models
Imported by orchestrata_tools, orchestrata_tools.reviewer
Tests that import it none

Class diagram

Class diagram orchestrata_tools.reviewer.code_reviewer

Dependency diagram

Dependency diagram orchestrata_tools.reviewer.code_reviewer

API

Classes

CodeReviewer

Line 19

Review code quality using AST metadata.

Method Signature Summary
__init__ __init__(index: ASTIndex)
_location _location(method: MethodInfo) -> str
review_method review_method(method: MethodInfo) -> ReviewReport Review a single method.
review_class review_class() -> ReviewReport Review a single class.
review_module review_module(module: ModuleInfo) -> ReviewReport Review a module.
review_codebase review_codebase() -> list[ReviewReport] Review all modules, classes, and methods.

Source

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

"""Code quality review engine.

Ports key logic from code-review.py into a rule-based analyzer
that checks methods, classes, and modules for quality issues.
"""

from __future__ import annotations

from collections.abc import Iterator
from typing import TYPE_CHECKING

from orchestrata_tools.reviewer.models import Issue, ReviewReport, Severity

if TYPE_CHECKING:
    from orchestrata_tools.models.ast_models import ClassInfo, MethodInfo, ModuleInfo
    from orchestrata_tools.parser.indexer import ASTIndex


class CodeReviewer:
    """Review code quality using AST metadata."""

    def __init__(self, index: ASTIndex):
        self.index = index

    def _location(self, method: MethodInfo) -> str:
        return f"{method.module_path}.{method.class_name}.{method.name}"

    def review_method(self, method: MethodInfo) -> ReviewReport:
        """Review a single method."""
        report = ReviewReport(target=self._location(method))

        # Missing docstring
        if not method.docstring:
            report.add(
                Issue(
                    rule="missing-docstring",
                    message=f"Method '{method.name}' has no docstring",
                    severity=Severity.MEDIUM,
                    location=self._location(method),
                    suggestion="Add a docstring describing the method's purpose",
                )
            )

        # Too many parameters
        if len(method.parameters) > 7:
            report.add(
                Issue(
                    rule="too-many-params",
                    message=f"Method has {len(method.parameters)} parameters",
                    severity=Severity.MEDIUM,
                    location=self._location(method),
                    suggestion="Consider using a dataclass or config object",
                )
            )

        # Missing return type
        if not method.return_type and method.name != "__init__":
            report.add(
                Issue(
                    rule="missing-return-type",
                    message="Method lacks return type annotation",
                    severity=Severity.LOW,
                    location=self._location(method),
                    suggestion="Add -> return_type annotation",
                )
            )

        # Missing parameter types
        for param in method.parameters:
            if not param.type_annotation:
                report.add(
                    Issue(
                        rule="missing-param-type",
                        message=f"Parameter '{param.name}' lacks type annotation",
                        severity=Severity.LOW,
                        location=self._location(method),
                    )
                )

        return report

    def review_class(self, cls: ClassInfo) -> ReviewReport:
        """Review a single class."""
        report = ReviewReport(target=cls.qualified_name)

        # Missing docstring
        if not cls.docstring:
            report.add(
                Issue(
                    rule="missing-docstring",
                    message=f"Class '{cls.name}' has no docstring",
                    severity=Severity.MEDIUM,
                    location=cls.qualified_name,
                )
            )

        # God class
        if cls.method_count > 20:
            report.add(
                Issue(
                    rule="god-class",
                    message=f"Class has {cls.method_count} methods",
                    severity=Severity.HIGH,
                    location=cls.qualified_name,
                    suggestion="Split into smaller, focused classes",
                )
            )

        # Too many bases
        if len(cls.bases) > 3:
            report.add(
                Issue(
                    rule="too-many-bases",
                    message=f"Class inherits from {len(cls.bases)} bases",
                    severity=Severity.MEDIUM,
                    location=cls.qualified_name,
                )
            )

        # Protocol without methods
        if cls.is_protocol and not cls.methods:
            report.add(
                Issue(
                    rule="empty-protocol",
                    message="Protocol has no methods defined",
                    severity=Severity.LOW,
                    location=cls.qualified_name,
                )
            )

        return report

    def review_module(self, module: ModuleInfo) -> ReviewReport:
        """Review a module."""
        report = ReviewReport(target=module.path)

        # Empty module
        if not module.classes and not module.functions:
            report.add(
                Issue(
                    rule="empty-module",
                    message="Module has no classes or functions",
                    severity=Severity.INFO,
                    location=module.path,
                )
            )

        # Too many classes
        if module.class_count > 10:
            report.add(
                Issue(
                    rule="too-many-classes",
                    message=f"Module has {module.class_count} classes",
                    severity=Severity.LOW,
                    location=module.path,
                )
            )

        return report

    def review_codebase(self) -> list[ReviewReport]:
        """Review all modules, classes, and methods."""

        def reports_for(module: ModuleInfo) -> Iterator[ReviewReport]:
            yield self.review_module(module)
            for cls in module.classes:
                yield self.review_class(cls)
                yield from (self.review_method(method) for method in cls.methods)

        return [
            report
            for module in self.index.codebase.modules
            for report in reports_for(module)
        ]