fix: patch batocera entries past commented items

_patched_entry read '#{ ... }' lines as live items, found no match in the model and gave up, so atarist was written again whole (52 lines changed for 3 additions). A commented item is now left as written.
This commit is contained in:
Abdessamad Derraz committed 2026-10-21 09:52:34 +02:00
1 parent 5ec1208a83
commit f7b758a41d
2 files changed
+34 -2

No files matched your search

+13
View File
@@ -26,6 +26,15 @@ _ITEM = re.compile(r"\{[^{}\n]*\}")
_MD5_VALUE = re.compile(r'("md5"\s*:\s*")[^"]*(")')
_STRING = re.compile(r'"(?:[^"\\\n]|\\.)*"')
def _commented(text: str, position: int) -> bool:
"""Whether a '#' outside a string precedes `position` on its line."""
line_start = text.rfind("\n", 0, position) + 1
return "#" in _STRING.sub("", text[line_start:position])
def _note_key(text: str) -> tuple[str, str]:
"""(file, zippedFile) of the item a line holds, ("", "") for none."""
path = re.search(r'"file"\s*:\s*"([^"]+)"', text)
@@ -316,6 +325,10 @@ class Exporter(BaseExporter):
last: re.Match[str] | None = None
consumed: dict[tuple, int] = {}
for match in _ITEM.finditer(joined):
if _commented(joined, match.start()):
# '#{ "md5": ..., "file": "bios/tos102us.img"},' is a note
# Python never reads, and it stays as written.
continue
try:
theirs = ast.literal_eval(match.group(0))
except (SyntaxError, ValueError):
+21 -2
View File
@@ -1919,7 +1919,7 @@ class BatoceraPatchesValuesNotEntries(unittest.TestCase):
" return {}\n"
)
def render(self, truth_files):
def render(self, truth_files, original=None):
scraped = {"cores": ["hatari"], "systems": {"atari-st": {"native_id": "atarist", "files": [
{"name": "tos102.img", "destination": "tos102.img", "native_system": "atarist",
"native_name": "Atari ST", "native_path": "bios/tos102.img", "md5": "1" * 32},
@@ -1929,7 +1929,8 @@ class BatoceraPatchesValuesNotEntries(unittest.TestCase):
systems, report = build_native_model({"systems": {"atari-st": {"files": truth_files}}},
scraped)
exporter = discover_exporters()["batocera"]()
return exporter.render(systems, report, {"batocera-systems": self.ORIGINAL}, scraped)[
original = original or self.ORIGINAL
return exporter.render(systems, report, {"batocera-systems": original}, scraped)[
"batocera-systems"]
def test_a_corrected_md5_changes_that_value_only(self):
@@ -1948,6 +1949,24 @@ class BatoceraPatchesValuesNotEntries(unittest.TestCase):
self.assertIn("# ST\n", produced)
self.assertIn('"file": "bios/tos102.img"},\n\n', produced)
def test_a_commented_item_does_not_stop_the_patch(self):
"""atarist keeps seven TOS items behind '#'. Read as live items they
matched nothing in the model, and the entry was written again whole
for three additions."""
commented = ' #{ "md5": "' + "5" * 32 + '", "file": "bios/tos102us.img"},\n'
original = self.ORIGINAL.replace("\n\n", "\n" + commented + "\n", 1)
produced = self.render([{"name": "tos206.img", "path": "tos206.img",
"md5": "4" * 32, "_cores": ["hatari"]}], original)
self.assertEqual(
produced,
original.replace(
'"file": "bios/tos104.img"}\n',
'"file": "bios/tos104.img"},\n'
' { "md5": "' + "4" * 32 + '", "file": "bios/tos206.img",'
' "emulator": "libretro", "core": "hatari"}\n',
),
)
class BatoceraExport(unittest.TestCase):
def _render(self, originals):