Merge branch 'main' of https://git.yumee.top/laily/.pouch
This commit is contained in:
@@ -264,6 +264,93 @@ def parse_ids(values: list[str]) -> list[str]:
|
||||
return out
|
||||
|
||||
|
||||
def public_case(case: dict[str, Any]) -> dict[str, Any]:
|
||||
return {k: v for k, v in case.items() if not str(k).startswith("_")}
|
||||
|
||||
|
||||
def run_together_groups(meta: dict[str, Any]) -> list[dict[str, Any]]:
|
||||
raw = meta.get("run_together") or []
|
||||
if not isinstance(raw, list):
|
||||
return []
|
||||
groups: list[dict[str, Any]] = []
|
||||
for item in raw:
|
||||
if not isinstance(item, dict):
|
||||
continue
|
||||
ids = [str(x) for x in (item.get("ids") or []) if str(x).strip()]
|
||||
if len(ids) < 2:
|
||||
continue
|
||||
groups.append(
|
||||
{
|
||||
"ids": ids,
|
||||
"continue_on_fail": bool(item.get("continue_on_fail")),
|
||||
}
|
||||
)
|
||||
return groups
|
||||
|
||||
|
||||
def expand_run_together(
|
||||
meta: dict[str, Any],
|
||||
all_cases: list[dict[str, Any]],
|
||||
selected: list[dict[str, Any]],
|
||||
skipped: list[dict[str, str]],
|
||||
max_i: int,
|
||||
) -> tuple[list[dict[str, Any]], list[dict[str, Any]]]:
|
||||
groups = run_together_groups(meta)
|
||||
if not groups:
|
||||
return selected, []
|
||||
by_id = {str(c.get("id") or ""): c for c in all_cases}
|
||||
selected_ids = [str(c.get("id") or "") for c in selected]
|
||||
selected_map = {str(c.get("id") or ""): c for c in selected}
|
||||
hit_groups: list[dict[str, Any]] = []
|
||||
|
||||
for group in groups:
|
||||
ids = group["ids"]
|
||||
if not any(cid in selected_map for cid in ids):
|
||||
continue
|
||||
hit_groups.append(group)
|
||||
first_pos = min(
|
||||
(selected_ids.index(cid) for cid in ids if cid in selected_ids),
|
||||
default=len(selected_ids),
|
||||
)
|
||||
members: list[dict[str, Any]] = []
|
||||
for cid in ids:
|
||||
if cid in selected_map:
|
||||
members.append(selected_map[cid])
|
||||
continue
|
||||
case = by_id.get(cid)
|
||||
if not case or case.get("status", "active") != "active":
|
||||
skipped.append({"id": cid, "reason": "run_together 搭档不是 active"})
|
||||
continue
|
||||
risk = str(case.get("risk") or "safe")
|
||||
try:
|
||||
r_i = risk_index(meta, risk)
|
||||
except SystemExit:
|
||||
skipped.append({"id": cid, "reason": f"未知风险 {risk}"})
|
||||
continue
|
||||
if r_i > max_i:
|
||||
skipped.append({"id": cid, "reason": f"risk={risk} 超过上限"})
|
||||
continue
|
||||
members.append(public_case(case))
|
||||
drop = set(ids)
|
||||
new_selected: list[dict[str, Any]] = []
|
||||
inserted = False
|
||||
for case in selected:
|
||||
cid = str(case.get("id") or "")
|
||||
if cid in drop:
|
||||
if not inserted:
|
||||
new_selected.extend(members)
|
||||
inserted = True
|
||||
continue
|
||||
new_selected.append(case)
|
||||
if not inserted:
|
||||
new_selected.extend(members)
|
||||
selected = new_selected
|
||||
selected_ids = [str(c.get("id") or "") for c in selected]
|
||||
selected_map = {str(c.get("id") or ""): c for c in selected}
|
||||
|
||||
return selected, hit_groups
|
||||
|
||||
|
||||
def cmd_select(args: argparse.Namespace, root: Path) -> int:
|
||||
meta = load_catalog_meta(root)
|
||||
cases = load_all_cases(root)
|
||||
@@ -305,8 +392,7 @@ def cmd_select(args: argparse.Namespace, root: Path) -> int:
|
||||
if r_i > max_i:
|
||||
skipped.append({"id": cid, "reason": f"risk={risk} 超过上限 {max_risk}"})
|
||||
continue
|
||||
public = {k: v for k, v in case.items() if not str(k).startswith("_")}
|
||||
selected.append(public)
|
||||
selected.append(public_case(case))
|
||||
|
||||
if wanted and missing:
|
||||
print(
|
||||
@@ -315,12 +401,15 @@ def cmd_select(args: argparse.Namespace, root: Path) -> int:
|
||||
)
|
||||
return 2
|
||||
|
||||
selected, together = expand_run_together(meta, cases, selected, skipped, max_i)
|
||||
|
||||
print(
|
||||
json.dumps(
|
||||
{
|
||||
"max_risk": max_risk,
|
||||
"count": len(selected),
|
||||
"skipped": skipped,
|
||||
"run_together": together,
|
||||
"cases": selected,
|
||||
},
|
||||
ensure_ascii=False,
|
||||
@@ -405,6 +494,28 @@ def validate(root: Path) -> list[str]:
|
||||
except SystemExit as exc:
|
||||
return errors + [str(exc)]
|
||||
|
||||
together_raw = meta.get("run_together")
|
||||
if together_raw is None:
|
||||
together_raw = []
|
||||
if not isinstance(together_raw, list):
|
||||
errors.append("catalog.run_together 必须是列表")
|
||||
together_raw = []
|
||||
together_seen: set[str] = set()
|
||||
for i, item in enumerate(together_raw):
|
||||
where = f"catalog.run_together[{i}]"
|
||||
if not isinstance(item, dict):
|
||||
errors.append(f"{where}: 必须是 mapping,含 ids")
|
||||
continue
|
||||
ids = item.get("ids")
|
||||
if not isinstance(ids, list) or len(ids) < 2:
|
||||
errors.append(f"{where}: ids 至少两个")
|
||||
continue
|
||||
for cid in ids:
|
||||
cid_s = str(cid)
|
||||
if cid_s in together_seen:
|
||||
errors.append(f"{where}: id {cid_s} 已在另一组 run_together")
|
||||
together_seen.add(cid_s)
|
||||
|
||||
for case in cases:
|
||||
cid = str(case.get("id") or "")
|
||||
where = f"{case.get('_file')}:{cid or '?'}"
|
||||
@@ -416,6 +527,8 @@ def validate(root: Path) -> list[str]:
|
||||
if cid in seen:
|
||||
errors.append(f"{where}: id 重复(也出现在 {seen[cid]})")
|
||||
seen[cid] = str(case.get("_file"))
|
||||
if cid in together_seen:
|
||||
together_seen.discard(cid)
|
||||
cat = str(case.get("category") or "")
|
||||
file_cat = str(case.get("_file_category") or "")
|
||||
if file_cat and cat and cat != file_cat:
|
||||
@@ -471,6 +584,8 @@ def validate(root: Path) -> list[str]:
|
||||
errors.append(f"{where}: expected[{j}] 需要 kind 与 value")
|
||||
elif sig.get("kind") not in EXPECTED_KINDS:
|
||||
errors.append(f"{where}: expected[{j}].kind 非法")
|
||||
if together_seen:
|
||||
errors.append("catalog.run_together 引用了不存在的 id: " + ", ".join(sorted(together_seen)))
|
||||
return errors
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user