Source code for kstlib.utils.text
"""Text manipulation helpers."""
from __future__ import annotations
import importlib
import re
from typing import TYPE_CHECKING, Any
if TYPE_CHECKING:
from collections.abc import Mapping
else: # pragma: no cover - runtime alias for delayed evaluation
Mapping = importlib.import_module("collections.abc").Mapping
_PLACEHOLDER_PATTERN = re.compile(r"{{\s*(?P<key>[\w.\-]+)\s*}}")
[docs]
def replace_placeholders(template: str, values: Mapping[str, Any] | None = None, /, **kwargs: Any) -> str:
"""Replace ``{{ placeholder }}`` tokens within *template*.
Args:
template: Raw template string containing placeholder patterns.
values: Optional mapping used to look up replacement values. When provided,
it is merged with the keyword arguments, giving precedence to the latter.
**kwargs: Additional placeholder values.
Returns:
Rendered template with matching placeholders substituted by their string
representation. Missing placeholders are left untouched to simplify
incremental rendering.
Examples:
>>> replace_placeholders("Hello {{ name }}!", name="Ada")
'Hello Ada!'
"""
combined: dict[str, Any] = {}
if values:
combined.update(dict(values))
if kwargs:
combined.update(kwargs)
def _replace(match: re.Match[str]) -> str:
key = match.group("key")
if key not in combined:
return match.group(0)
value = combined[key]
if value is None:
return ""
if isinstance(value, str | int | float | bool):
return str(value)
return "[object]"
return _PLACEHOLDER_PATTERN.sub(_replace, template)
__all__ = ["replace_placeholders"]