feat: add safe_render using Jinja2; add tests
This commit is contained in:
parent
a5678de78b
commit
ec58508476
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