From 8f0b992c115c2c65bf653111fca801faa077511e Mon Sep 17 00:00:00 2001 From: Abdessamad Derraz <3028866+Abdess@users.noreply.github.com> Date: Sat, 17 Oct 2026 13:27:15 +0200 Subject: [PATCH] fix: name each archive romset_recipes cannot build --- scripts/romset_recipes.py | 56 ++++++++++++++++++++++-------------- tests/test_romset_recipes.py | 27 +++++++++++++++++ 2 files changed, 61 insertions(+), 22 deletions(-) diff --git a/scripts/romset_recipes.py b/scripts/romset_recipes.py index d082d728..f44a8840 100644 --- a/scripts/romset_recipes.py +++ b/scripts/romset_recipes.py @@ -329,6 +329,30 @@ def write_reconstructions( return written +def print_missing(gap: dict) -> None: + """The pinned archives the collection lacks, each one named.""" + print( + f"\nPinned archives not held: " + f"{len(gap['constructible'])} buildable, " + f"{len(gap['unreproducible'])} not reproducible, " + f"{len(gap['no_recipe'])} without a recipe" + ) + for record in gap["constructible"]: + print( + f" BUILDABLE: {record['archive']} md5={record['md5'][:8]} " + f"via {record['recipe']} ({record['size']} bytes)" + ) + # The acquisition targets: an archive pinned under several md5s is + # one line, the platforms' own bundles rather than romsets. + by_archive: dict[str, list[str]] = defaultdict(list) + for record in gap["unreproducible"]: + by_archive[record["archive"]].append(record["md5"][:8]) + for archive, md5s in sorted(by_archive.items(), key=lambda kv: (-len(kv[1]), kv[0])): + print(f" NOT REPRODUCIBLE: {archive} ({len(md5s)} pinned: {', '.join(sorted(md5s))})") + for record in gap["no_recipe"]: + print(f" NO RECIPE: {record['archive']} md5={record['md5'][:8]}") + + def main() -> int: parser = argparse.ArgumentParser(description=__doc__) parser.add_argument("--identify", action="store_true", help="label local archives") @@ -384,14 +408,14 @@ def main() -> int: return 0 print( - f"{result['archives_with_recipes']} archives documentees par " - f"{result['recipes']} recettes " - f"({result['profile_recipes']} de profils, {result['dat_recipes']} de DAT), " - f"{result['atoms']} atomes ROM disponibles" + f"{result['archives_with_recipes']} archives described by " + f"{result['recipes']} recipes " + f"({result['profile_recipes']} from profiles, {result['dat_recipes']} from DATs), " + f"{result['atoms']} ROM atoms available" ) if not result["dat_recipes"]: print( - " Aucune recette DAT: importer avec " + " No DAT recipe: import one with " "`python -m scripts.scraper.romset_dat_importer --source mame --fetch mame0289`" ) for problem in result.get("unreadable_archives", []): @@ -400,13 +424,12 @@ def main() -> int: if args.identify: ident = result["identify"] print( - f"\nIdentification: {len(ident['identified'])} archives reproduites " - f"par une recette, {len(ident['unidentified'])} non identifiees" + f"\nIdentification: {len(ident['identified'])} archives reproduced " + f"by a recipe, {len(ident['unidentified'])} unidentified" ) print( - " L'etiquette est la plus ancienne version produisant ces octets: " - "une recette inchangee depuis 0.190 est comptee la, pas a sa version " - "d'origine." + " The label is the oldest version producing these bytes: a recipe " + "unchanged since 0.190 is counted there, not at its own version." ) by_recipe: dict[str, int] = defaultdict(int) for record in ident["identified"]: @@ -417,18 +440,7 @@ def main() -> int: print(f" UNIDENTIFIED: {record['path']}") if args.missing: - gap = result["missing"] - print( - f"\nArchives epinglees absentes: " - f"{len(gap['constructible'])} reconstructibles, " - f"{len(gap['unreproducible'])} irreproductibles, " - f"{len(gap['no_recipe'])} sans recette" - ) - for record in gap["constructible"]: - print( - f" BUILDABLE: {record['archive']} md5={record['md5'][:8]} " - f"via {record['recipe']} ({record['size']} octets)" - ) + print_missing(result["missing"]) for path in result.get("written", []): print(f" WROTE: {path}") diff --git a/tests/test_romset_recipes.py b/tests/test_romset_recipes.py index 1cf85c26..8e9baf11 100644 --- a/tests/test_romset_recipes.py +++ b/tests/test_romset_recipes.py @@ -530,5 +530,32 @@ class RepositoryRun(unittest.TestCase): self.assertIn(record["recipe"], recipes[record["archive"]]) + +class TheMissingReportNamesItsTargets(unittest.TestCase): + """--missing printed three counters; the archives to go and find were + only in --json.""" + + def test_every_target_is_named(self): + import contextlib # noqa: PLC0415 + import io # noqa: PLC0415 + + import romset_recipes # noqa: PLC0415 + + gap = { + "constructible": [], + "unreproducible": [{"archive": "neogeo.zip", "md5": "a" * 32}, + {"archive": "neogeo.zip", "md5": "b" * 32}, + {"archive": "aes.zip", "md5": "c" * 32}], + "no_recipe": [{"archive": "scummclassic.zip", "md5": "d" * 32}], + } + out = io.StringIO() + with contextlib.redirect_stdout(out): + romset_recipes.print_missing(gap) + text = out.getvalue() + self.assertIn("NOT REPRODUCIBLE: neogeo.zip (2 pinned: aaaaaaaa, bbbbbbbb)", text) + self.assertIn("NOT REPRODUCIBLE: aes.zip (1 pinned: cccccccc)", text) + self.assertIn("NO RECIPE: scummclassic.zip md5=dddddddd", text) + self.assertLess(text.index("neogeo.zip"), text.index("aes.zip")) + if __name__ == "__main__": unittest.main()