ics-simlab-config-gen-claude/services/validation/hil_init_validation.py

95 lines
3.2 KiB
Python

from __future__ import annotations
import ast
from dataclasses import dataclass
from pathlib import Path
from typing import Dict, List, Optional, Set
@dataclass
class HilInitIssue:
file: str
key: str
message: str
def _get_str_const(node: ast.AST) -> Optional[str]:
return node.value if isinstance(node, ast.Constant) and isinstance(node.value, str) else None
class _PhysicalValuesInitCollector(ast.NodeVisitor):
"""
Colleziona le chiavi inizializzate in vari modi:
- physical_values["x"] = ...
- physical_values["x"] += ...
- physical_values.setdefault("x", ...)
- physical_values.update({"x": ..., "y": ...})
"""
def __init__(self) -> None:
self.inits: Set[str] = set()
def visit_Assign(self, node: ast.Assign) -> None:
for tgt in node.targets:
k = self._key_from_physical_values_subscript(tgt)
if k:
self.inits.add(k)
self.generic_visit(node)
def visit_AugAssign(self, node: ast.AugAssign) -> None:
k = self._key_from_physical_values_subscript(node.target)
if k:
self.inits.add(k)
self.generic_visit(node)
def visit_Call(self, node: ast.Call) -> None:
# physical_values.setdefault("x", ...)
if isinstance(node.func, ast.Attribute) and isinstance(node.func.value, ast.Name):
if node.func.value.id == "physical_values":
if node.func.attr == "setdefault" and node.args:
k = _get_str_const(node.args[0])
if k:
self.inits.add(k)
# physical_values.update({...})
if node.func.attr == "update" and node.args:
arg0 = node.args[0]
if isinstance(arg0, ast.Dict):
for key_node in arg0.keys:
k = _get_str_const(key_node)
if k:
self.inits.add(k)
self.generic_visit(node)
@staticmethod
def _key_from_physical_values_subscript(node: ast.AST) -> Optional[str]:
# physical_values["x"] -> Subscript(Name("physical_values"), Constant("x"))
if not isinstance(node, ast.Subscript):
return None
if not (isinstance(node.value, ast.Name) and node.value.id == "physical_values"):
return None
return _get_str_const(node.slice)
def validate_hil_initialization(hil_logic_file: str, required_keys: Set[str]) -> List[HilInitIssue]:
"""
Verifica che nel file HIL ci sia almeno un'inizializzazione per ciascuna key richiesta.
Best-effort: guarda tutte le assegnazioni nel file (non solo dentro logic()).
"""
path = Path(hil_logic_file)
text = path.read_text(encoding="utf-8", errors="replace")
tree = ast.parse(text)
collector = _PhysicalValuesInitCollector()
collector.visit(tree)
missing = sorted(required_keys - collector.inits)
return [
HilInitIssue(
file=str(path),
key=k,
message=f"physical_values['{k}'] non sembra inizializzato nel file HIL (manca un assegnamento/setdefault/update).",
)
for k in missing
]