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
Dependency diagram
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
"""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)
]