scripts/audit_ast_rules.py
Rama main · réplica de lectura del repositorio original.
"""Shared AST helpers for small, deterministic repository-maintenance rules."""
from __future__ import annotations
import ast
from collections.abc import Iterator
def parse_source(source: str) -> ast.AST | None:
try:
return ast.parse(source)
except SyntaxError:
return None
def dotted_name(node: ast.AST | None) -> str | None:
if isinstance(node, ast.Name):
return node.id
if isinstance(node, ast.Attribute):
prefix = dotted_name(node.value)
return f"{prefix}.{node.attr}" if prefix else node.attr
return None
def iter_calls(source: str) -> Iterator[ast.Call]:
tree = parse_source(source)
if tree is None:
return
for node in ast.walk(tree):
if isinstance(node, ast.Call):
yield node
def iter_assignments(source: str) -> Iterator[ast.Assign | ast.AnnAssign | ast.AugAssign]:
tree = parse_source(source)
if tree is None:
return
for node in ast.walk(tree):
if isinstance(node, (ast.Assign, ast.AnnAssign, ast.AugAssign)):
yield node
def call_name(call: ast.Call) -> str | None:
return dotted_name(call.func)
def has_keyword(call: ast.Call, name: str) -> bool:
return any(keyword.arg == name for keyword in call.keywords)
def assignment_targets(node: ast.Assign | ast.AnnAssign | ast.AugAssign) -> list[str]:
raw_targets: list[ast.AST]
if isinstance(node, ast.Assign):
raw_targets = list(node.targets)
else:
raw_targets = [node.target]
names: list[str] = []
for target in raw_targets:
name = dotted_name(target)
if name:
names.append(name)
return names