lint_oop_policy
Validate and normalize TwinCAT 3 OOP policy configurations in .twincat-validator.json files to ensure consistent code quality standards for industrial automation projects.
Instructions
Lint nearest .twincat-validator.json policy keys/types and return normalized policy.
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| target_path | No | ||
| strict | No |
Output Schema
| Name | Required | Description | Default |
|---|---|---|---|
| result | Yes |
Implementation Reference
- MCP tool handler for `lint_oop_policy`, which invokes the underlying `ValidationConfig.lint_oop_policy` method.
@mcp.tool() def lint_oop_policy(target_path: str = "", strict: bool = True) -> str: """Lint nearest .twincat-validator.json policy keys/types and return normalized policy.""" _t0 = time.monotonic() try: policy_target = _resolve_policy_target_path(target_path) lint = config.lint_oop_policy(policy_target, strict=bool(strict)) result = { "success": True, "target_path": str(policy_target), "valid": bool(lint.get("valid", False)), "strict": bool(lint.get("strict", strict)), "source": lint.get("source", "defaults"), "policy_file": lint.get("policy_file"), "recognized_keys": lint.get("recognized_keys", []), "unknown_keys": lint.get("unknown_keys", []), "type_errors": lint.get("type_errors", []), "constraint_errors": lint.get("constraint_errors", []), "parse_error": lint.get("parse_error"), "normalized_policy": lint.get("normalized_policy", {}), } return _with_meta(result, _t0) except Exception as e: return _tool_error(str(e), file_path=target_path or None, start_time=_t0) - The actual implementation logic for `lint_oop_policy` within the `ValidationConfig` class.
def lint_oop_policy( self, target_path: Path | None = None, *, strict: bool = True, policy_file: Path | None = None, ) -> dict[str, Any]: """Lint .twincat-validator.json OOP policy keys/types and return normalized result.""" selected_policy_file = policy_file if selected_policy_file is None: selected_policy_file = self._locate_policy_file(target_path) expected_types = self._oop_policy_expected_types() known_keys = set(expected_types.keys()) defaults = dict(self.oop_policy_defaults) result: dict[str, Any] = { "valid": True, "strict": bool(strict), "source": "defaults", "policy_file": None, "recognized_keys": [], "unknown_keys": [], "type_errors": [], "constraint_errors": [], "parse_error": None, "normalized_policy": defaults, } if selected_policy_file is None: return result result["policy_file"] = str(selected_policy_file.resolve()) result["source"] = str(selected_policy_file.resolve()) try: with open(selected_policy_file, "r", encoding="utf-8") as f: data = json.load(f) except (OSError, json.JSONDecodeError) as exc: result["valid"] = False result["parse_error"] = str(exc) if strict: return result return result raw_policy = data.get("oop_policy", {}) if not isinstance(raw_policy, dict): result["valid"] = False result["type_errors"].append( {"key": "oop_policy", "expected": "object", "actual": type(raw_policy).__name__} ) if strict: return result raw_policy = {} recognized = sorted(k for k in raw_policy.keys() if k in known_keys) unknown = sorted(k for k in raw_policy.keys() if k not in known_keys) result["recognized_keys"] = recognized result["unknown_keys"] = unknown type_errors: list[dict[str, str]] = [] constraint_errors: list[dict[str, str]] = [] for key in recognized: value = raw_policy[key] expected = expected_types[key] if expected is bool and not isinstance(value, bool): type_errors.append({"key": key, "expected": "bool", "actual": type(value).__name__}) continue if expected is int and not isinstance(value, int): type_errors.append({"key": key, "expected": "int", "actual": type(value).__name__}) continue if expected is list and not isinstance(value, list): type_errors.append({"key": key, "expected": "list", "actual": type(value).__name__}) continue if key in ("required_super_methods", "cleanup_method_names") and isinstance( value, list ): bad_items = [type(v).__name__ for v in value if not isinstance(v, str)] if bad_items: type_errors.append( { "key": key, "expected": "list[str]", "actual": f"list[{bad_items[0]}]", } ) if key in ("max_inheritance_depth", "max_interface_methods", "max_methods_per_pou"): if isinstance(value, int) and value <= 0: constraint_errors.append({"key": key, "error": "must be > 0"}) result["type_errors"] = type_errors result["constraint_errors"] = constraint_errors # Reuse existing normalization for deterministic effective policy. result["normalized_policy"] = self._normalize_oop_policy(raw_policy) invalid = bool(unknown or type_errors or constraint_errors or result["parse_error"]) result["valid"] = not invalid if strict else not bool(result["parse_error"]) return result