fix: less brittle meta parsing

This commit is contained in:
aclist 2026-06-11 15:11:38 +09:00
parent 450d54d7fd
commit 5a8d85ac92
9 changed files with 67 additions and 31 deletions

View File

@ -1,6 +1,6 @@
import hashlib
import logging
import shlex
import re
from concurrent.futures import wait
from concurrent.futures import ThreadPoolExecutor
@ -53,39 +53,25 @@ def is_mission(path: Path) -> bool:
file = path / "init.c"
return file.exists()
def parse_meta(file: Path) -> ModMeta | None:
mod = file / "meta.cpp"
if mod.exists() is False:
return None
def tokenize(file: str) -> dict[Any] | None:
delimiter=r"\s*=\s*"
modmeta = {}
try:
with open(file / "meta.cpp", "r") as f:
st = f.read()
lex = shlex.shlex(st)
lex.whitespace += "=;"
v = []
while True:
tok = lex.get_token()
if not tok:
break
if tok == "protocol" or tok == "publishedid":
ntok = lex.get_token()
elif tok == "timestamp":
# NOTE: some malformed .NET tick conversions result in numbers < 0
ntok = lex.get_token()
if ntok == "-":
ntok += str(lex.get_token())
elif tok == "name":
ntok = lex.get_token()
if ntok is not None:
ntok = ntok.split('"')[1]
if ntok is not None:
v.append(ntok)
return ModMeta(*v)
with open(file, "r", encoding="utf-8") as file:
for line in file:
line = line.strip().rstrip(";")
if not line:
continue
els = re.split(delimiter, line, maxsplit=1)
if len(els) == 2:
key, value = els
print(value)
modmeta[key.strip()] = value.strip('"')
return modmeta
except Exception as e:
logger.critical(e)
return None
def get_mod_size(path: Path) -> float:
s = 0
for f in path.rglob("*"):
@ -105,12 +91,12 @@ def parse_mods(mods: list[Path]) -> list[Any]:
for mod in mods:
mod_dir = mod.name
symlink = _hash(mod_dir)
meta = parse_meta(mod)
meta = tokenize(mod)
if meta is None:
continue
size = get_mod_size(mod)
# NOTE: final col is cell renderer highlight toggle
clean.append([meta.name, symlink, mod_dir, size, False])
clean.append([meta["name"], symlink, mod_dir, size, False])
clean.sort(key=lambda row: str(row[0]).casefold())
return clean

5
tests/fixtures/cpp/meta1.cpp vendored Normal file
View File

@ -0,0 +1,5 @@
protocol = 1;
publishedid = 1234567890;
name = "ModName";
timestamp = 638278452000000000;
hash = "a1b2c3d4e5f6g7h8i9j0";

5
tests/fixtures/cpp/meta2.cpp vendored Normal file
View File

@ -0,0 +1,5 @@
protocol = 1
publishedid = 1234567890
name = "ModName"
timestamp = 638278452000000000
hash = "a1b2c3d4e5f6g7h8i9j0"

5
tests/fixtures/cpp/meta3.cpp vendored Normal file
View File

@ -0,0 +1,5 @@
protocol=1
publishedid=1234567890
name="ModName"
timestamp=638278452000000000
hash="a1b2c3d4e5f6g7h8i9j0"

5
tests/fixtures/cpp/meta4.cpp vendored Normal file
View File

@ -0,0 +1,5 @@
protocol=1
publishedid=1234567890
name=ModName
timestamp=638278452000000000
hash=a1b2c3d4e5f6g7h8i9j0

5
tests/fixtures/cpp/meta5.cpp vendored Normal file
View File

@ -0,0 +1,5 @@
protocol=1;
publishedid=1234567890;
name=ModName;
timestamp=638278452000000000;
hash=a1b2c3d4e5f6g7h8i9j0;

5
tests/fixtures/cpp/meta6.cpp vendored Normal file
View File

@ -0,0 +1,5 @@
protocol=1
publishedid=1234567890;
name="ModName";
timestamp=638278452000000000;
hash= a1b2c3d4e5f6g7h8i9j0;

3
tests/fixtures/cpp/test.py vendored Normal file
View File

@ -0,0 +1,3 @@
from dzgui.api.mods import tokenize
print(tokenize("meta4.cpp"))

17
tests/test_modmeta.py Normal file
View File

@ -0,0 +1,17 @@
import pytest
from dzgui.api.mods import tokenize
from tests.fixtures import fixture_path
@pytest.mark.mods
@pytest.mark.parametrize("fixture", [
fixture_path("cpp/meta1.cpp"),
fixture_path("cpp/meta2.cpp"),
fixture_path("cpp/meta3.cpp"),
fixture_path("cpp/meta4.cpp"),
fixture_path("cpp/meta5.cpp"),
fixture_path("cpp/meta6.cpp"),
]
)
def test_modmeta(fixture: str) -> None:
meta = tokenize(fixture)
assert meta["name"] == "ModName"