add new Template system, authorizing Django and ezt syntaxes (#19442)

This commit is contained in:
Thomas NOËL 2017-10-14 18:26:39 +02:00
parent 234f1499fa
commit f626c45cbb
2 changed files with 139 additions and 0 deletions

44
tests/test_templates.py Normal file
View File

@ -0,0 +1,44 @@
import pytest
from qommon.template import Template, TemplateError
def test_template():
tmpl = Template('')
assert tmpl.render() == ''
assert tmpl.render({'foo': 'bar'}) == ''
tmpl = Template('zoo')
assert tmpl.render() == 'zoo'
assert tmpl.render({'foo': 'bar'}) == 'zoo'
# django
tmpl = Template('{{ foo }}')
assert tmpl.render() == ''
assert tmpl.render({'foo': 'bar'}) == 'bar'
tmpl = Template('{% if foo %}{{ foo }}{% endif %}')
assert tmpl.render() == ''
assert tmpl.render({'foo': 'bar'}) == 'bar'
# ezt
tmpl = Template('[foo]')
assert tmpl.render() == '[foo]'
assert tmpl.render({'foo': 'bar'}) == 'bar'
tmpl = Template('[if-any foo][foo][end]')
assert tmpl.render() == ''
assert tmpl.render({'foo': 'bar'}) == 'bar'
# mix Django/ezt: Django wins
tmpl = Template('{% if foo %}{{ foo }}[foo]{% endif %}')
assert tmpl.render({'foo': 'bar'}) == 'bar[foo]'
# django syntax error
with pytest.raises(TemplateError):
tmpl = Template('{% if foo %}{{ foo }}{% end %}', raises=True)
tmpl = Template('{% if foo %}{{ foo }}{% end %}')
assert tmpl.render({'foo': 'bar'}) == '{% if foo %}{{ foo }}{% end %}'
# ezt syntax error
with pytest.raises(TemplateError):
tmpl = Template('[if-any foo][foo][endif]', raises=True)
tmpl = Template('[if-any foo][foo][endif]')
assert tmpl.render({'foo': 'bar'}) == '[if-any foo][foo][endif]'

View File

@ -19,6 +19,9 @@ import os
import glob
import xml.etree.ElementTree as ET
from django.template import (Context as DjangoContext, Template as DjangoTemplate,
TemplateSyntaxError as DjangoTemplateSyntaxError,
VariableDoesNotExist as DjangoVariableDoesNotExist)
from django.template.loader import render_to_string
from quixote import get_session, get_request, get_response, get_publisher
@ -411,3 +414,95 @@ def decorate(body, response):
def render(template_name, context):
return htmltext(render_to_string(template_name, context).encode('utf-8'))
class TemplateError(Exception):
def __init__(self, msg, params=()):
self.msg = msg
self.params = params
def __str__(self):
return self.msg % self.params
def ezt_raises(exception, on_parse=False):
from qommon import _
parts = []
parts.append({
ezt.ArgCountSyntaxError: _('wrong number of arguments'),
ezt.UnknownReference: _('unknown reference'),
ezt.NeedSequenceError: _('sequence required'),
ezt.UnclosedBlocksError: _('unclosed block'),
ezt.UnmatchedEndError: _('unmatched [end]'),
ezt.UnmatchedElseError: _('unmatched [else]'),
ezt.BaseUnavailableError: _('unavailable base location'),
ezt.BadFormatConstantError: _('bad format constant'),
ezt.UnknownFormatConstantError: _('unknown format constant'),
}.get(exception.__class__, _('unknown error')))
if exception.line is not None:
parts.append(_('at line %(line)d and column %(column)d') % {
'line': exception.line + 1, 'column': exception.column + 1})
if on_parse:
message = _('syntax error in ezt template: %s')
else:
message = _('failure to render ezt template: %s')
raise TemplateError(message % ' '.join(parts))
class Template(object):
def __init__(self, value, raises=False):
'''Guess kind of template (Django or ezt), and parse it'''
self.value = value
self.raises = raises
if '{{' in value or '{%' in value: # Django template
self.render = self.django_render
try:
self.template = DjangoTemplate(value)
except DjangoTemplateSyntaxError as e:
if raises:
from qommon import _
raise TemplateError(_('syntax error in Django template: %s'), e)
self.render = self.null_render
elif '[' in value: # ezt template
self.render = self.ezt_render
self.template = ezt.Template(compress_whitespace=False)
try:
self.template.parse(value)
except ezt.EZTException as e:
if raises:
ezt_raises(e, on_parse=True)
self.render = self.null_render
else:
self.render = self.null_render
def null_render(self, context={}):
return str(self.value)
def django_render(self, context={}):
context = DjangoContext(context)
try:
return self.template.render(context)
except (DjangoTemplateSyntaxError, DjangoVariableDoesNotExist) as e:
if self.raises:
from qommon import _
raise TemplateError(_('failure to render Django template: %s'), e)
else:
return self.value
def ezt_render(self, context={}):
fd = StringIO()
try:
self.template.generate(fd, context)
except ezt.EZTException as e:
if self.raises:
ezt_raises(e)
else:
return self.value
return fd.getvalue()
@classmethod
def is_template_string(cls, string):
return string and ('{{' in string or '{%' in string or '[' in string)