From 9ae3919d3432aecb965959d8a59994d34598557c Mon Sep 17 00:00:00 2001 From: Abdessamad Derraz <3028866+Abdess@users.noreply.github.com> Date: Thu, 5 Nov 2026 19:19:04 +0100 Subject: [PATCH] fix: spell cited commands so a paste runs them Messages in check_freshness, generate_pack, common, profile_sync and fileless_audit named commands as export_native.py --platform x, which fails from the repository root. They now read python scripts/.py like the rest of the report. --- scripts/check_freshness.py | 12 +++++------ scripts/common.py | 2 +- scripts/fileless_audit.py | 2 +- scripts/generate_pack.py | 3 ++- scripts/profile_sync.py | 4 ++-- tests/test_tool_messages.py | 41 +++++++++++++++++++++++++++++++++++++ 6 files changed, 53 insertions(+), 11 deletions(-) diff --git a/scripts/check_freshness.py b/scripts/check_freshness.py index ec799b866..c90ec334c 100644 --- a/scripts/check_freshness.py +++ b/scripts/check_freshness.py @@ -524,7 +524,7 @@ def check_native( ) if reason: reason += ( - f", export_native.py --platform {name} --refresh-cache" + f", python scripts/export_native.py --platform {name} --refresh-cache" " fetches it, then the export is written again" ) else: @@ -532,7 +532,7 @@ def check_native( name, exporter_class, truth_dir, platforms_dir, cache_dir, published ) if reason: - reason += f", export_native.py --platform {name} writes it again" + reason += f", python scripts/export_native.py --platform {name} writes it again" findings.append( Finding( "native", @@ -974,7 +974,7 @@ def check_coreinfo( if recorded is None: findings.append(Finding( "coreinfo", "reviewed .info", UNKNOWN, - detail=f"no {record_path.name}: run check_freshness.py --record-coreinfo", + detail=f"no {record_path.name}: run python scripts/check_freshness.py --record-coreinfo", )) return findings findings.extend( @@ -982,7 +982,7 @@ def check_coreinfo( "coreinfo", name, STALE, local=(recorded.get(name) or "not recorded")[:12], remote=blobs[name][:12], detail=(".info changed since reviewed: read its firmware, then " - f"check_freshness.py --record-coreinfo {name}"), + f"python scripts/check_freshness.py --record-coreinfo {name}"), ) for name in coreinfo_drift(blobs, recorded, profiles) ) @@ -1456,7 +1456,7 @@ def check_source_paths(profiles: dict) -> Finding: "source-paths.json", STALE if problems else OK, local=f"{len(recorded)} trees", - detail=(", ".join(problems) + ": run scripts/sitesources.py --fetch") if problems + detail=(", ".join(problems) + ": run python scripts/sitesources.py --fetch") if problems else "every pinned tree is recorded, and only those", ) @@ -1468,7 +1468,7 @@ def check_profiles(run: bool, offline: bool) -> list[Finding]: "profiles", "profile_sync", SKIPPED, - detail="pass --profiles, or run profile_sync.py --all --triage", + detail="pass --profiles, or run python scripts/profile_sync.py --all --triage", ) ] cmd = [ diff --git a/scripts/common.py b/scripts/common.py index d3e445fec..9309e5d99 100644 --- a/scripts/common.py +++ b/scripts/common.py @@ -691,7 +691,7 @@ def load_target_config( # --target but have no --list-targets of their own. + suggest_names( [target], sorted(alias_index), - f"verify.py --platform {platform_name} --list-targets", len(targets), + f"python scripts/verify.py --platform {platform_name} --list-targets", len(targets), ) ) diff --git a/scripts/fileless_audit.py b/scripts/fileless_audit.py index 5baf27206..deea6b65b 100644 --- a/scripts/fileless_audit.py +++ b/scripts/fileless_audit.py @@ -128,7 +128,7 @@ def main() -> None: if unknown: raise SystemExit( "ERROR: unknown profile " + ", ".join(unknown) + ". " - + suggest_names(unknown, known, "verify.py --list-emulators") + + suggest_names(unknown, known, "python scripts/verify.py --list-emulators") ) names = args.emulators if args.all: diff --git a/scripts/generate_pack.py b/scripts/generate_pack.py index 2a69cda7a..6cfaddd6d 100644 --- a/scripts/generate_pack.py +++ b/scripts/generate_pack.py @@ -2725,7 +2725,8 @@ def _run_verify_packs(args: argparse.Namespace) -> None: if args.platform: print( f" {platform_name}: FAIL (no pack in {args.output_dir}; build it with " - f"generate_pack.py --platform {platform_name} --output-dir {args.output_dir})" + f"python scripts/generate_pack.py --platform {platform_name} " + f"--output-dir {args.output_dir})" ) all_ok = False failing.append(f"{platform_name} (no pack)") diff --git a/scripts/profile_sync.py b/scripts/profile_sync.py index 3245ab3b0..4a911a71d 100644 --- a/scripts/profile_sync.py +++ b/scripts/profile_sync.py @@ -4414,7 +4414,7 @@ def select_profiles(profiles: dict, args: argparse.Namespace) -> dict: ) raise SystemExit( f"ERROR: unknown profile {', '.join(unknown)}. " - + suggest_names(unknown, listed, "verify.py --list-emulators") + + suggest_names(unknown, listed, "python scripts/verify.py --list-emulators") ) return {name: profiles[name] for name in dict.fromkeys(names)} @@ -4451,7 +4451,7 @@ def select_profiles(profiles: dict, args: argparse.Namespace) -> dict: if args.system: # Suggested from the spellings the named listing prints. systems = sorted(spelling for spelling, _ in system_index(profiles).values()) - hint = " " + suggest_names([args.system], systems, "verify.py --list-systems") + hint = " " + suggest_names([args.system], systems, "python scripts/verify.py --list-systems") elif args.type_: kinds = sorted({str(p.get("type")) for p in profiles.values() if p.get("type")}) hint = f" Types: {', '.join(kinds)}." diff --git a/tests/test_tool_messages.py b/tests/test_tool_messages.py index 872d1302d..c478f997a 100644 --- a/tests/test_tool_messages.py +++ b/tests/test_tool_messages.py @@ -5,6 +5,7 @@ from __future__ import annotations import contextlib import io import os +import re import subprocess import sys import tempfile @@ -635,5 +636,45 @@ class TheGitHubTokenComesFromGh(unittest.TestCase): run.assert_not_called() +class CitedCommandsRunWhenPasted(unittest.TestCase): + """check_freshness wrote "run python scripts/generate_truth.py" on one + line and "export_native.py --platform bizhawk writes it again" two lines + below: pasted from the repository root, the second is command not found. + A message that hands the reader a command spells the whole command.""" + + CITED = re.compile(r"(?:^|run |with |then |, )([a-z_]+\.py) --[a-z]") + + def test_no_message_cites_a_bare_script(self): + import ast # noqa: PLC0415 + + offenders = [] + for path in sorted((REPO_ROOT / "scripts").rglob("*.py")): + tree = ast.parse(path.read_text(encoding="utf-8")) + docs = { + id(node.body[0].value) + for node in ast.walk(tree) + if isinstance(node, (ast.Module, ast.ClassDef, ast.FunctionDef, + ast.AsyncFunctionDef)) + and node.body and isinstance(node.body[0], ast.Expr) + } + offenders += [ + f"{path.relative_to(REPO_ROOT)}:{node.lineno} {node.value.strip()[:60]}" + for node in ast.walk(tree) + if isinstance(node, ast.Constant) and isinstance(node.value, str) + and id(node) not in docs and self.CITED.search(node.value) + ] + self.assertEqual(offenders, []) + + def test_the_target_hint_is_a_whole_command(self): + with tempfile.TemporaryDirectory(dir=REPO_ROOT / "tmp") as tmp: + targets = Path(tmp) / "targets" + targets.mkdir() + (targets / "p.yml").write_text("targets:\n switch:\n cores: [a]\n") + with self.assertRaises(ValueError) as caught: + common.load_target_config("p", "swich", tmp) + self.assertIn("python scripts/verify.py --platform p --list-targets", + str(caught.exception)) + + if __name__ == "__main__": unittest.main()