fix: a Batocera note stays with its own item

Notes were keyed by file path, so a comment before one of the items reading an archive came back before every item reading it: bk's '# MAME' twelve times. A comment after the list's opening bracket matched neither rule and was dropped (enterprise's ep128emu-core). Notes are now keyed by file and archive member, written once, and a head-line comment goes with the first item.
This commit is contained in:
Abdessamad Derraz committed 2026-10-20 01:22:27 +02:00
1 parent e74d182d32
commit cef6d943e2
2 files changed
+47 -17

No files matched your search

+27 -17
View File
@@ -23,6 +23,13 @@ _ENTRY_START = re.compile(r'^(\s{4})"([^"]+)":\s*\{')
_INDENT = " " * 4
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)
member = re.search(r'"zippedFile"\s*:\s*"([^"]+)"', text)
return (path.group(1) if path else "", member.group(1) if member else "")
class Exporter(BaseExporter):
"""Write Batocera's batocera-systems, corrected."""
@@ -208,16 +215,22 @@ class Exporter(BaseExporter):
)
@staticmethod
def _item_notes(lines: list[str]) -> tuple[dict[str, str], dict[str, list[str]]]:
"""The notes a maintainer wrote about each file, keyed by its path.
def _item_notes(
lines: list[str],
) -> tuple[dict[tuple[str, str], str], dict[tuple[str, str], list[str]]]:
"""The notes a maintainer wrote about each item, keyed by its file
and the member it names inside an archive.
Rewriting an entry as one line would drop them, and a note like
"ideally - 94bc50..." is the only record of why a hash is blank.
A comment on its own line belongs to the file below it, a comment at
the end of a line belongs to the file on it.
A comment on its own line belongs to the item below it, a comment at
the end of a line belongs to the item on it, and a comment after the
list's opening bracket (`"biosFiles": [# ep128emu-core`) to the first
item. Keyed by path alone, bk's '# MAME' came back before each of
twelve items reading bios/bk0010.zip.
"""
trailing: dict[str, str] = {}
leading: dict[str, list[str]] = {}
trailing: dict[tuple[str, str], str] = {}
leading: dict[tuple[str, str], list[str]] = {}
pending: list[str] = []
for line in lines:
stripped = line.strip()
@@ -225,17 +238,19 @@ class Exporter(BaseExporter):
if stripped.startswith("#"):
pending.append(stripped)
continue
path = re.search(r'"file"\s*:\s*"([^"]+)"', line)
if not path:
key = _note_key(line)
if not key[0]:
if 0 <= hash_at and "[" in line[:hash_at]:
pending.append(line[hash_at:].strip())
continue
key = path.group(1)
if pending:
leading[key] = pending
pending = []
if 0 <= hash_at and hash_at > line.index(key):
if 0 <= hash_at and hash_at > line.index(key[0]):
trailing[key] = line[hash_at:].rstrip()
return trailing, leading
def _entry_block(
self,
system: NativeSystem,
@@ -259,8 +274,8 @@ class Exporter(BaseExporter):
pad = " " * (len(_INDENT) + 4)
lines = [head]
for index, item in enumerate(items):
key = self._item_path(item)
lines.extend(f"{pad}{note}" for note in leading.get(key, []))
key = _note_key(item)
lines.extend(f"{pad}{note}" for note in leading.pop(key, []))
separator = "," if index < len(items) - 1 else ""
note = trailing.get(key, "")
suffix = f" {note}" if note else ""
@@ -268,11 +283,6 @@ class Exporter(BaseExporter):
lines.append(f"{_INDENT}] }},")
return lines
@staticmethod
def _item_path(item: str) -> str:
match = re.search(r'"file": "([^"]+)"', item)
return match.group(1) if match else ""
@staticmethod
def _split_original(original: str) -> tuple[list[str], list[str], list[str]]:
"""Cut the script into what precedes the dict, the dict, what follows."""
+20
View File
@@ -1744,5 +1744,25 @@ class RetroDeckAdditionsFollowTheirCore(unittest.TestCase):
self.assertEqual(read_by_armsx2.native("component", ""), "")
class BatoceraNotesStayWithTheirItem(unittest.TestCase):
"""Keyed by path, bk's '# MAME' came back before each of twelve items
reading bios/bk0010.zip, and enterprise's '"biosFiles": [# ep128emu-core'
was dropped."""
def test_notes_are_kept_once_per_item(self):
from exporter.batocera_exporter import Exporter
lines = [
' "bk": { "name": "BK", "biosFiles": [# head note',
" # MAME",
' { "md5": "a", "file": "bios/bk0010.zip", "zippedFile": "one.rom" },',
' { "md5": "b", "file": "bios/bk0010.zip", "zippedFile": "two.rom" }, # tail',
" ] },",
]
trailing, leading = Exporter._item_notes(lines)
self.assertEqual(leading, {("bios/bk0010.zip", "one.rom"): ["# head note", "# MAME"]})
self.assertEqual(trailing, {("bios/bk0010.zip", "two.rom"): "# tail"})
if __name__ == "__main__":
unittest.main()