from __future__ import annotations from html import escape import re from pathlib import Path from typing import Any _PLACEHOLDER_RE = re.compile(r"\{\{\s*([A-Za-z0-9_.-]+)\s*\}\}") def html_escape(value: Any) -> str: return escape("" if value is None else str(value), quote=True) def render_template(template: str, context: dict[str, Any]) -> str: def replace(match: re.Match[str]) -> str: key = match.group(1) value = context.get(key, "") return "" if value is None else str(value) return _PLACEHOLDER_RE.sub(replace, template) def read_text(path: str | Path) -> str: return Path(path).read_text(encoding="utf-8") def write_text(path: str | Path, text: str) -> None: Path(path).parent.mkdir(parents=True, exist_ok=True) Path(path).write_text(text, encoding="utf-8")