♻️ refactor(tsl-api-reference): split lookup workflows and maintenance tooling
This commit is contained in:
@@ -1,15 +1,10 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Resolve framework-class profiles without replacing ordinary API lookup."""
|
||||
"""Library for validating and resolving evidence-backed framework profiles."""
|
||||
|
||||
import argparse
|
||||
import json
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
from lookup import (
|
||||
DEFAULT_TSV,
|
||||
codegen_root_for_tsv,
|
||||
load_rows,
|
||||
normalize,
|
||||
search_exact,
|
||||
slice_entry,
|
||||
@@ -230,33 +225,6 @@ def validate_index(data, rows, codegen_root):
|
||||
return errors
|
||||
|
||||
|
||||
def find_framework(data, name):
|
||||
requested = normalize(name)
|
||||
return next(
|
||||
(
|
||||
framework
|
||||
for framework in data.get("frameworks", [])
|
||||
if isinstance(framework, dict)
|
||||
and normalize(framework.get("qualified_name", "")) == requested
|
||||
),
|
||||
None,
|
||||
)
|
||||
|
||||
|
||||
def parse_config(values):
|
||||
config = {}
|
||||
errors = []
|
||||
for value in values:
|
||||
key, separator, raw_value = value.partition("=")
|
||||
if not separator or not key.strip() or not raw_value.strip():
|
||||
errors.append(
|
||||
f"invalid --config {value!r}; expected a non-empty key=value pair"
|
||||
)
|
||||
continue
|
||||
config[key.strip()] = raw_value.strip()
|
||||
return config, errors
|
||||
|
||||
|
||||
def scaffold_packet(framework, config):
|
||||
configured_names = {
|
||||
entry["api"].rsplit(".", 1)[-1]
|
||||
@@ -320,135 +288,3 @@ def scaffold_packet(framework, config):
|
||||
"diagnostics": diagnostics,
|
||||
"evidence": framework.get("evidence", []),
|
||||
}
|
||||
|
||||
|
||||
def print_text(packet):
|
||||
print(f"framework: {packet['framework']}")
|
||||
print(f"scope/module: {packet['scope']}/{packet['module']}")
|
||||
print(f"scaffold_status: {packet['scaffold_status']}")
|
||||
print(f"contract_status: {packet['contract_status']}")
|
||||
print("lifecycle: " + " -> ".join(item["phase"] for item in packet["lifecycle"]))
|
||||
print("configuration: " + ", ".join(item["api"] for item in packet["configuration"]))
|
||||
required_hooks = [item["api"] for item in packet["hooks"] if item.get("required")]
|
||||
print("required_hooks: " + (", ".join(required_hooks) or "none"))
|
||||
print("state_apis: " + ", ".join(item["api"] for item in packet["state_apis"]))
|
||||
print("execution: " + ", ".join(item["api"] for item in packet["execution"]))
|
||||
print("result_apis: " + ", ".join(item["api"] for item in packet["result_apis"]))
|
||||
if packet["configured_values"]:
|
||||
print("configured_values: " + ", ".join(
|
||||
f"{key}={value}" for key, value in packet["configured_values"].items()
|
||||
))
|
||||
if packet["diagnostics"]:
|
||||
print("diagnostics:")
|
||||
for diagnostic in packet["diagnostics"]:
|
||||
print(f"- {diagnostic['code']}: {diagnostic['message']}")
|
||||
|
||||
|
||||
def main(argv=None):
|
||||
if hasattr(sys.stdout, "reconfigure"):
|
||||
sys.stdout.reconfigure(encoding="utf-8")
|
||||
parser = argparse.ArgumentParser(
|
||||
description="Resolve framework-class scaffolds for TSL API consumers.",
|
||||
allow_abbrev=False,
|
||||
)
|
||||
action = parser.add_mutually_exclusive_group(required=True)
|
||||
action.add_argument("--framework", help="exact framework qualified name")
|
||||
action.add_argument(
|
||||
"--check", action="store_true", help="validate all framework profile references"
|
||||
)
|
||||
parser.add_argument(
|
||||
"--config",
|
||||
action="append",
|
||||
default=[],
|
||||
metavar="KEY=VALUE",
|
||||
help="raw framework configuration; may be repeated",
|
||||
)
|
||||
parser.add_argument("--format", choices=("text", "json"), default="text")
|
||||
parser.add_argument("--index", metavar="PATH", help="framework_index.json path")
|
||||
parser.add_argument("--tsv", metavar="PATH", help="function_index.tsv path")
|
||||
args = parser.parse_args(argv)
|
||||
|
||||
index_path = Path(args.index) if args.index else DEFAULT_INDEX
|
||||
tsv_path = Path(args.tsv) if args.tsv else DEFAULT_TSV
|
||||
try:
|
||||
data = load_index(index_path)
|
||||
rows = load_rows(tsv_path)
|
||||
except (OSError, UnicodeError, ValueError) as error:
|
||||
print(f"ERROR: {error}", file=sys.stderr)
|
||||
return 1
|
||||
if not rows:
|
||||
print(f"ERROR: {tsv_path} has no entries", file=sys.stderr)
|
||||
return 1
|
||||
codegen_root = codegen_root_for_tsv(tsv_path)
|
||||
|
||||
errors = validate_index(data, rows, codegen_root)
|
||||
if args.check:
|
||||
if errors:
|
||||
for error in errors:
|
||||
print(f"ERROR: {error}", file=sys.stderr)
|
||||
return 1
|
||||
print(f"OK: {len(data['frameworks'])} framework profile(s) validated")
|
||||
return 0
|
||||
|
||||
if errors:
|
||||
packet = {
|
||||
"status": "data_error",
|
||||
"framework": args.framework,
|
||||
"diagnostics": [
|
||||
{
|
||||
"code": "PROFILE_REFERENCE_ERROR",
|
||||
"severity": "error",
|
||||
"message": error,
|
||||
"next_action": "Fix framework_index.json or rebuild the API index.",
|
||||
}
|
||||
for error in errors
|
||||
],
|
||||
}
|
||||
if args.format == "json":
|
||||
print(json.dumps(packet, ensure_ascii=False, indent=2))
|
||||
else:
|
||||
print(f"Framework profile data is invalid for {args.framework!r}.")
|
||||
for diagnostic in packet["diagnostics"]:
|
||||
print(f"- {diagnostic['code']}: {diagnostic['message']}")
|
||||
return 1
|
||||
|
||||
framework = find_framework(data, args.framework)
|
||||
if framework is None:
|
||||
packet = {
|
||||
"status": "not_found",
|
||||
"framework": args.framework,
|
||||
"diagnostics": [
|
||||
{
|
||||
"code": "FRAMEWORK_NOT_FOUND",
|
||||
"severity": "error",
|
||||
"message": f"No framework profile named {args.framework!r}.",
|
||||
"next_action": "Use --check or add a framework profile.",
|
||||
}
|
||||
],
|
||||
}
|
||||
if args.format == "json":
|
||||
print(json.dumps(packet, ensure_ascii=False, indent=2))
|
||||
else:
|
||||
print(f"No framework profile named {args.framework!r}.")
|
||||
return 0
|
||||
|
||||
config, config_errors = parse_config(args.config)
|
||||
packet = scaffold_packet(framework, config)
|
||||
for error in config_errors:
|
||||
packet["diagnostics"].append(
|
||||
{
|
||||
"code": "INVALID_CONFIGURATION",
|
||||
"severity": "error",
|
||||
"message": error,
|
||||
"next_action": "Use KEY=VALUE syntax with non-empty values.",
|
||||
}
|
||||
)
|
||||
if args.format == "json":
|
||||
print(json.dumps(packet, ensure_ascii=False, indent=2))
|
||||
else:
|
||||
print_text(packet)
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
raise SystemExit(main())
|
||||
|
||||
Reference in New Issue
Block a user