Files
cluster-knaster/core/src/knaster/util.py
T

57 lines
1.5 KiB
Python

"""Kleine Hilfsfunktionen ohne Fachlogik."""
from __future__ import annotations
import re
import time
from pathlib import Path
from typing import Any
import yaml
_DURATION = re.compile(r"^\s*(\d+(?:\.\d+)?)\s*([smhd]?)\s*$")
_FACTORS = {"": 1, "s": 1, "m": 60, "h": 3600, "d": 86400}
_PLACEHOLDER = re.compile(r"\{([a-zA-Z0-9_]+)\}")
def now() -> float:
return time.time()
def load_yaml(path: str | Path) -> Any:
with open(path, encoding="utf-8") as fh:
return yaml.safe_load(fh) or {}
def parse_duration(value: str | int | float | None) -> float:
"""'5m' -> 300.0; Zahlen gelten als Sekunden."""
if value is None:
return 0.0
if isinstance(value, int | float):
return float(value)
match = _DURATION.match(value)
if not match:
raise ValueError(f"Ungültige Dauer: {value!r}")
return float(match.group(1)) * _FACTORS[match.group(2)]
class MissingLabel(KeyError):
pass
def fill_template(value: Any, labels: dict[str, str]) -> Any:
"""Ersetzt {label} rekursiv; fehlende Labels lösen MissingLabel aus."""
if isinstance(value, str):
def repl(m: re.Match[str]) -> str:
key = m.group(1)
if key not in labels:
raise MissingLabel(key)
return str(labels[key])
return _PLACEHOLDER.sub(repl, value)
if isinstance(value, dict):
return {k: fill_template(v, labels) for k, v in value.items()}
if isinstance(value, list):
return [fill_template(v, labels) for v in value]
return value