feat/retry-mechanism #1
43
app/templates.py
Normal file
43
app/templates.py
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
import re
|
||||||
|
from typing import Any, Dict
|
||||||
|
|
||||||
|
import jinja2
|
||||||
|
|
||||||
|
# Create a minimal Jinja2 environment for text templates.
|
||||||
|
# Use StrictUndefined so missing variables raise exceptions during rendering.
|
||||||
|
ENV = jinja2.Environment(
|
||||||
|
undefined=jinja2.StrictUndefined,
|
||||||
|
autoescape=False,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def _convert_braced_format_to_jinja(template: str) -> str:
|
||||||
|
"""
|
||||||
|
Convert simple `{var}` style placeholders into Jinja `{{ var }}` so legacy templates keep working.
|
||||||
|
This will not touch existing `{{ }}` or `{% %}`.
|
||||||
|
"""
|
||||||
|
# Skip conversion if template already contains jinja markers
|
||||||
|
if ("{{" in template) or ("{%" in template):
|
||||||
|
return template
|
||||||
|
|
||||||
|
# Replace single-brace simple identifiers like {a_b} or {a.b} -> {{ a_b }} or {{ a.b }}
|
||||||
|
pattern = re.compile(r'(?<!\{)\{([A-Za-z0-9_\.]+)\}(?!\})')
|
||||||
|
return pattern.sub(r'{{ \1 }}', template)
|
||||||
|
|
||||||
|
|
||||||
|
def safe_render(template_str: str, context: Dict[str, Any]) -> str:
|
||||||
|
"""
|
||||||
|
Safely render a template string using Jinja2 with StrictUndefined.
|
||||||
|
- Accepts legacy `{var}` placeholders by converting them to Jinja style.
|
||||||
|
- Raises jinja2 exceptions when rendering fails.
|
||||||
|
"""
|
||||||
|
if template_str is None:
|
||||||
|
raise ValueError("template_str is None")
|
||||||
|
|
||||||
|
tpl = _convert_braced_format_to_jinja(template_str)
|
||||||
|
jtpl = ENV.from_string(tpl)
|
||||||
|
# Jinja expects normal Python types in context; if context contains Pydantic models,
|
||||||
|
# they should be converted by caller to plain dicts.
|
||||||
|
return jtpl.render(**context)
|
||||||
|
|
||||||
|
|
||||||
19
tests/test_templates.py
Normal file
19
tests/test_templates.py
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
import pytest
|
||||||
|
|
||||||
|
from app.templates import safe_render
|
||||||
|
|
||||||
|
|
||||||
|
def test_safe_render_legacy_and_nested():
|
||||||
|
tpl = "收到{actual_ref_amt}元 by {trans_order_info.remark}"
|
||||||
|
ctx = {"actual_ref_amt": 88.88, "trans_order_info": {"remark": "order123"}}
|
||||||
|
out = safe_render(tpl, ctx)
|
||||||
|
assert "88.88" in out
|
||||||
|
assert "order123" in out
|
||||||
|
|
||||||
|
|
||||||
|
def test_safe_render_missing_var_raises():
|
||||||
|
tpl = "Hello {missing_var}"
|
||||||
|
with pytest.raises(Exception):
|
||||||
|
safe_render(tpl, {})
|
||||||
|
|
||||||
|
|
||||||
Loading…
Reference in New Issue
Block a user