jun2024's picture
Upload 2241 files
b4f2238 verified
Raw History Blame Contribute Delete
3.77 kB
"""占位符统一兜底工具。"""
from __future__ import annotations
from collections.abc import Iterable, Mapping
from typing import Any
PLACEHOLDER_FALLBACK_VALUE = "/"
def normalize_placeholder_value(value: Any, *, fallback_value: str = PLACEHOLDER_FALLBACK_VALUE) -> Any:
"""将 None 统一归一为兜底值。"""
return fallback_value if value is None else value
def get_service_placeholder_keys(service: Any) -> list[str]:
"""兼容不同服务实现,提取服务声明的占位符键。"""
getter = getattr(service, "get_placeholder_keys", None)
if callable(getter):
keys = getter()
else:
keys = getattr(service, "placeholder_keys", [])
if not isinstance(keys, Iterable) or isinstance(keys, (str, bytes)):
return []
result: list[str] = []
for key in keys:
if isinstance(key, str) and key.strip():
result.append(key)
return result
def normalize_service_result(
service_result: Mapping[str, Any] | None,
*,
expected_keys: Iterable[str] = (),
fallback_value: str = PLACEHOLDER_FALLBACK_VALUE,
) -> dict[str, Any]:
"""标准化单个服务的返回值,并补全服务声明但未返回的键。"""
normalized: dict[str, Any] = {}
if service_result:
for key, value in service_result.items():
normalized[str(key)] = normalize_placeholder_value(value, fallback_value=fallback_value)
for key in expected_keys:
normalized.setdefault(key, fallback_value)
return normalized
def ensure_required_placeholders(
context: Mapping[str, Any],
required_placeholders: Iterable[str] | None,
*,
fallback_value: str = PLACEHOLDER_FALLBACK_VALUE,
) -> dict[str, Any]:
"""确保 required_placeholders 中所有键都存在且非 None。"""
normalized: dict[str, Any] = {
str(key): normalize_placeholder_value(value, fallback_value=fallback_value)
for key, value in context.items()
}
if required_placeholders:
for key in required_placeholders:
if key not in normalized or normalized[key] is None:
normalized[key] = fallback_value
return normalized
def build_docx_render_context(
*,
doc: Any,
context: Mapping[str, Any],
fallback_value: str = PLACEHOLDER_FALLBACK_VALUE,
) -> dict[str, Any]:
"""在 docxtpl 渲染前补齐缺失变量,保证模板中未命中的变量也会落为兜底值。"""
normalized = ensure_required_placeholders(context, None, fallback_value=fallback_value)
missing_keys = _get_undeclared_template_variables(doc=doc, context=normalized)
for key in missing_keys:
normalized.setdefault(key, fallback_value)
return normalized
def resolve_render_variable(
variables: Mapping[str, Any],
key: str,
*,
fallback_value: str = PLACEHOLDER_FALLBACK_VALUE,
) -> tuple[bool, str]:
"""字符串模板渲染的统一变量解析。"""
if key in variables and variables[key] is not None:
return True, str(variables[key])
return False, fallback_value
def _get_undeclared_template_variables(*, doc: Any, context: Mapping[str, Any]) -> set[str]:
getter = getattr(doc, "get_undeclared_template_variables", None)
if not callable(getter):
return set()
values: Any
try:
values = getter(context=dict(context))
except TypeError:
try:
values = getter(dict(context))
except Exception:
return set()
except Exception:
return set()
if not isinstance(values, (set, list, tuple)):
return set()
result: set[str] = set()
for item in values:
if isinstance(item, str) and item.strip():
result.add(item)
return result