🐛 fix(playbook): support toml without tomllib
This commit is contained in:
parent
ab0dd11afc
commit
ea00d43bb8
|
|
@ -39,10 +39,9 @@
|
|||
*.cppm text eol=lf
|
||||
*.mpp text eol=lf
|
||||
*.cmake text eol=lf
|
||||
*.clangd text eol=lf
|
||||
CMakeLists.txt text eol=lf
|
||||
CMakePresets.json text eol=lf
|
||||
*.clangd text eol=lf
|
||||
.clangd text eol=lf
|
||||
|
||||
# Binary files (no line-ending conversion).
|
||||
*.png binary
|
||||
|
|
|
|||
|
|
@ -5,7 +5,10 @@ from pathlib import Path
|
|||
from shutil import copy2, copytree, which
|
||||
import subprocess
|
||||
|
||||
import tomllib
|
||||
try:
|
||||
import tomllib
|
||||
except ModuleNotFoundError: # Python < 3.11
|
||||
tomllib = None
|
||||
|
||||
ORDER = ["vendor", "sync_templates", "sync_standards", "install_skills", "format_md"]
|
||||
SCRIPT_DIR = Path(__file__).resolve().parent
|
||||
|
|
@ -16,8 +19,125 @@ def usage() -> str:
|
|||
return "Usage:\n python scripts/playbook.py -config <path>\n python scripts/playbook.py -h"
|
||||
|
||||
|
||||
def strip_inline_comment(value: str) -> str:
|
||||
in_single = False
|
||||
in_double = False
|
||||
escape = False
|
||||
for idx, ch in enumerate(value):
|
||||
if escape:
|
||||
escape = False
|
||||
continue
|
||||
if in_double and ch == "\\":
|
||||
escape = True
|
||||
continue
|
||||
if ch == "'" and not in_double:
|
||||
in_single = not in_single
|
||||
continue
|
||||
if ch == '"' and not in_single:
|
||||
in_double = not in_double
|
||||
continue
|
||||
if ch == "#" and not in_single and not in_double:
|
||||
return value[:idx].rstrip()
|
||||
return value
|
||||
|
||||
|
||||
def split_list_items(raw: str) -> list[str]:
|
||||
items: list[str] = []
|
||||
buf: list[str] = []
|
||||
in_single = False
|
||||
in_double = False
|
||||
escape = False
|
||||
for ch in raw:
|
||||
if escape:
|
||||
buf.append(ch)
|
||||
escape = False
|
||||
continue
|
||||
if in_double and ch == "\\":
|
||||
buf.append(ch)
|
||||
escape = True
|
||||
continue
|
||||
if ch == "'" and not in_double:
|
||||
in_single = not in_single
|
||||
buf.append(ch)
|
||||
continue
|
||||
if ch == '"' and not in_single:
|
||||
in_double = not in_double
|
||||
buf.append(ch)
|
||||
continue
|
||||
if ch == "," and not in_single and not in_double:
|
||||
items.append("".join(buf).strip())
|
||||
buf = []
|
||||
continue
|
||||
buf.append(ch)
|
||||
tail = "".join(buf).strip()
|
||||
if tail:
|
||||
items.append(tail)
|
||||
return items
|
||||
|
||||
|
||||
def parse_toml_value(raw: str) -> object:
|
||||
value = raw.strip()
|
||||
if not value:
|
||||
return ""
|
||||
if value.startswith("[") and value.endswith("]"):
|
||||
inner = value[1:-1].strip()
|
||||
if not inner:
|
||||
return []
|
||||
return [parse_toml_value(item) for item in split_list_items(inner)]
|
||||
lowered = value.lower()
|
||||
if lowered == "true":
|
||||
return True
|
||||
if lowered == "false":
|
||||
return False
|
||||
if value[0] in ("'", '"') and value[-1] == value[0]:
|
||||
if value[0] == "'":
|
||||
return value[1:-1]
|
||||
import ast
|
||||
|
||||
try:
|
||||
return ast.literal_eval(value)
|
||||
except (ValueError, SyntaxError):
|
||||
return value[1:-1]
|
||||
try:
|
||||
if "." in value:
|
||||
return float(value)
|
||||
return int(value)
|
||||
except ValueError:
|
||||
return value
|
||||
|
||||
|
||||
def loads_toml_minimal(raw: str) -> dict:
|
||||
data: dict[str, dict] = {}
|
||||
current = None
|
||||
for line in raw.splitlines():
|
||||
stripped = line.strip()
|
||||
if not stripped or stripped.startswith("#"):
|
||||
continue
|
||||
if stripped.startswith("[") and stripped.endswith("]"):
|
||||
section = stripped[1:-1].strip()
|
||||
if not section:
|
||||
raise ValueError("empty section header")
|
||||
current = data.setdefault(section, {})
|
||||
if not isinstance(current, dict):
|
||||
raise ValueError(f"invalid section: {section}")
|
||||
continue
|
||||
if "=" not in stripped:
|
||||
raise ValueError(f"invalid line: {line}")
|
||||
key, value = stripped.split("=", 1)
|
||||
key = key.strip()
|
||||
if not key:
|
||||
raise ValueError("missing key")
|
||||
value = strip_inline_comment(value.strip())
|
||||
target = current if current is not None else data
|
||||
target[key] = parse_toml_value(value)
|
||||
return data
|
||||
|
||||
|
||||
def load_config(path: Path) -> dict:
|
||||
return tomllib.loads(path.read_text(encoding="utf-8"))
|
||||
raw = path.read_text(encoding="utf-8")
|
||||
if tomllib is not None:
|
||||
return tomllib.loads(raw)
|
||||
return loads_toml_minimal(raw)
|
||||
|
||||
|
||||
def log(message: str) -> None:
|
||||
|
|
|
|||
Loading…
Reference in New Issue