Viewing files at commit 25881039a2bbCommit detailsView latest

scripts/oakbench/configio.py 79 lines · 2.4 KB · python
1
"""Dependency-free config loading (ADR-0001).
2
3
One TOML-subset parser for the shapes this repo writes: dotted [section.name]
4
tables, string/boolean/string-list values. If a config outgrows this subset,
5
simplify the config, not the parser.
6
"""
7
8
from __future__ import annotations
9
10
from pathlib import Path
11
from typing import Any
12
13
try:
14
    import tomllib
15
except ModuleNotFoundError:  # Python 3.9/3.10 on stock macOS.
16
    tomllib = None  # type: ignore[assignment]
17
18
19
def load_toml(path: Path) -> dict[str, Any]:
20
    text = path.read_text()
21
    if tomllib is not None:
22
        return tomllib.loads(text)
23
24
    root: dict[str, Any] = {}
25
    current: dict[str, Any] | None = None
26
    for raw_line in text.splitlines():
27
        line = _strip_toml_comment(raw_line).strip()
28
        if not line:
29
            continue
30
        if line.startswith("[") and line.endswith("]"):
31
            parts = line[1:-1].split(".")
32
            current = root
33
            for part in parts:
34
                current = current.setdefault(part, {})
35
            continue
36
        if current is None or "=" not in line:
37
            continue
38
        key, value = [part.strip() for part in line.split("=", 1)]
39
        if value in {"true", "false"}:
40
            current[key] = value == "true"
41
        elif value.startswith('"') and value.endswith('"'):
42
            current[key] = value[1:-1]
43
        elif value.startswith("'") and value.endswith("'"):
44
            current[key] = value[1:-1]
45
        elif value.startswith("[") and value.endswith("]"):
46
            items = [item.strip().strip('"').strip("'") for item in value[1:-1].split(",") if item.strip()]
47
            current[key] = items
48
        else:
49
            current[key] = value
50
    return root
51
52
53
def _strip_toml_comment(raw_line: str) -> str:
54
    quote: str | None = None
55
    escaped = False
56
    for index, char in enumerate(raw_line):
57
        if escaped:
58
            escaped = False
59
            continue
60
        if char == "\\":
61
            escaped = quote == '"'
62
            continue
63
        if quote is not None:
64
            if char == quote:
65
                quote = None
66
            continue
67
        if char in {"'", '"'}:
68
            quote = char
69
            continue
70
        if char == "#":
71
            return raw_line[:index]
72
    return raw_line
73
74
75
def load_subject_config(path: Path) -> dict[str, Any]:
76
    data = load_toml(path)
77
    if "subjects" not in data:
78
        return {"subjects": {}}
79
    return {"subjects": data["subjects"]}