4 Small, simple and powerful template-engine for Python.
6 A template-engine for Python, which is very simple, easy to use, small,
7 fast, powerful, modular, extensible, well documented and pythonic.
9 See documentation for a list of features, template-syntax etc.
12 :Requires: Python >=2.6 / 3.x
15 see class ``Template`` and examples below.
19 Note that the examples are in Python 2; they also work in
20 Python 3 if you replace u"..." by "...", unicode() by str()
21 and partly "..." by b"...".
24 >>> t = Template("hello @!name!@")
25 >>> print(t(name="marvin"))
28 quickstart with a template-file::
29 # >>> t = Template(filename="mytemplate.tmpl")
30 # >>> print(t(name="marvin"))
34 >>> t = Template(u"output is in Unicode \\xe4\\xf6\\xfc\\u20ac")
35 >>> t #doctest: +ELLIPSIS
36 <...Template instance at 0x...>
38 u'output is in Unicode \\xe4\\xf6\\xfc\\u20ac'
40 u'output is in Unicode \\xe4\\xf6\\xfc\\u20ac'
43 >>> t = Template("hello @!name!@", data={"name":"world"})
49 # >>> t(note="data must be Unicode or ASCII", name=u"\\xe4")
53 >>> t = Template("hello escaped: @!name!@, unescaped: $!name!$")
54 >>> t(name='''<>&'"''')
55 u'hello escaped: <>&'", unescaped: <>&\\'"'
58 # encode the unicode-object to your encoding with encode()
59 >>> t = Template(u"hello \\xe4\\xf6\\xfc\\u20ac")
62 u'hello \\xe4\\xf6\\xfc\\u20ac'
63 >>> result.encode("utf-8")
64 'hello \\xc3\\xa4\\xc3\\xb6\\xc3\\xbc\\xe2\\x82\\xac'
65 >>> result.encode("ascii")
66 Traceback (most recent call last):
68 UnicodeEncodeError: 'ascii' codec can't encode characters in position 6-9: ordinal not in range(128)
69 >>> result.encode("ascii", 'xmlcharrefreplace')
70 'hello äöü€'
73 >>> Template('formatted: @! "%8.5f" % value !@')(value=3.141592653)
75 >>> Template("hello --@!name.upper().center(20)!@--")(name="world")
77 >>> Template("calculate @!var*5+7!@")(var=7)
80 blocks (if/for/macros/...)::
81 >>> t = Template("<!--(if foo == 1)-->bar<!--(elif foo == 2)-->baz<!--(else)-->unknown(@!foo!@)<!--(end)-->")
87 >>> t = Template("<!--(for i in mylist)-->@!i!@ <!--(else)-->(empty)<!--(end)-->")
93 >>> t = Template("<!--(for i,elem in enumerate(mylist))--> - @!i!@: @!elem!@<!--(end)-->")
94 >>> t(mylist=["a","b","c"])
95 u' - 0: a - 1: b - 2: c'
97 >>> t = Template('<!--(macro greetings)-->hello <strong>@!name!@</strong><!--(end)--> @!greetings(name=user)!@')
99 u' hello <strong>monty</strong>'
102 >>> t = Template('<!--(if exists("foo"))-->YES<!--(else)-->NO<!--(end)-->')
107 >>> t(foo=None) # note this difference to 'default()'
111 # non-existing variables raise an error
112 >>> Template('hi @!optional!@')()
113 Traceback (most recent call last):
115 TemplateRenderError: Cannot eval expression 'optional'. (NameError: name 'optional' is not defined)
117 >>> t = Template('hi @!default("optional","anyone")!@')
122 >>> t(optional="there")
125 # the 1st parameter can be any eval-expression
126 >>> t = Template('@!default("5*var1+var2","missing variable")!@')
129 >>> t(var1=10, var2=2)
133 >>> t = Template('<!--(if default("opt1+opt2",0) > 0)-->yes<!--(else)-->no<!--(end)-->')
136 >>> t(opt1=23, opt2=42)
139 >>> t = Template('<!--(for i in default("optional_list",[]))-->@!i!@<!--(end)-->')
142 >>> t(optional_list=[1,2,3])
146 # but make sure to put the expression in quotation marks, otherwise:
147 >>> Template('@!default(optional,"fallback")!@')()
148 Traceback (most recent call last):
150 TemplateRenderError: Cannot eval expression 'default(optional,"fallback")'. (NameError: name 'optional' is not defined)
153 >>> t = Template('$!setvar("i", "i+1")!$@!i!@')
157 >>> t = Template('''<!--(if isinstance(s, (list,tuple)))-->$!setvar("s", '"\\\\\\\\n".join(s)')!$<!--(end)-->@!s!@''')
158 >>> t(isinstance=isinstance, s="123")
160 >>> t(isinstance=isinstance, s=["123", "456"])
163 :Author: Roland Koebler (rk at simple-is-better dot org)
164 :Copyright: Roland Koebler
165 :License: MIT/X11-like, see __license__
167 :RCS: $Id: pyratemp.py,v 1.12 2013/04/02 20:26:06 rk Exp $
169 from __future__
import unicode_literals
171 __version__ =
"0.3.0"
172 __author__ =
"Roland Koebler <rk at simple-is-better dot org>"
173 __license__ =
"""Copyright (c) Roland Koebler, 2007-2013
175 Permission is hereby granted, free of charge, to any person obtaining a copy
176 of this software and associated documentation files (the "Software"), to deal
177 in the Software without restriction, including without limitation the rights
178 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
179 copies of the Software, and to permit persons to whom the Software is
180 furnished to do so, subject to the following conditions:
182 The above copyright notice and this permission notice shall be included in
183 all copies or substantial portions of the Software.
185 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
186 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
187 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
188 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
189 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
190 FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
196 if sys.version_info[0] >= 3:
201 import __builtin__
as builtins
202 from codecs
import open
211 """Get line numer of ``string[i]`` in `string`.
213 :Returns: row, starting at 1
214 :Note: This works for text-strings with ``\\n`` or ``\\r\\n``.
216 return string.count(
'\n', 0,
max(0, i)) + 1
219 """Get column number of ``string[i]`` in `string`.
221 :Returns: column, starting at 1 (but may be <1 if i<0)
222 :Note: This works for text-strings with ``\\n`` or ``\\r\\n``.
224 return i - string.rfind(
'\n', 0,
max(0, i))
227 """Get index of the character at `row`/`col` in `string`.
230 - `row`: row number, starting at 1.
231 - `col`: column number, starting at 1.
232 :Returns: ``i``, starting at 0 (but may be <1 if row/col<0)
233 :Note: This works for text-strings with '\\n' or '\\r\\n'.
236 for _
in range(row-1):
237 n = string.find(
'\n', n) + 1
243 """Convert all keys of the dict `d` to strings.
246 for k, v
in d.items():
253 """Dummy function, doing nothing.
258 """Create an exception-raising dummy function.
260 :Returns: dummy function, raising ``exception(value)``
262 def mydummy(*_, **__):
263 raise exception(value)
269 (NONE, HTML, LATEX, MAIL_HEADER) = range(0, 4)
270 ESCAPE_SUPPORTED = {
"NONE":
None,
"HTML":HTML,
"LATEX":LATEX,
"MAIL_HEADER":MAIL_HEADER}
273 """Replace special characters by their escape sequence.
276 - `s`: unicode-string to escape
279 - `NONE`: nothing is replaced
280 - `HTML`: replace &<>'" by &...;
281 - `LATEX`: replace \#$%&_{}~^
282 - `MAIL_HEADER`: escape non-ASCII mail-header-contents
284 the escaped string in unicode
286 - `ValueError`: if `format` is invalid.
289 MAIL_HEADER uses module email
296 if format
is None or format == NONE:
299 s = s.replace(
"&",
"&")
300 s = s.replace(
"<",
"<")
301 s = s.replace(
">",
">")
302 s = s.replace(
'"',
""")
303 s = s.replace(
"'",
"'")
304 elif format == LATEX:
305 s = s.replace(
"\\",
"\\x")
306 s = s.replace(
"#",
"\\#")
307 s = s.replace(
"$",
"\\$")
308 s = s.replace(
"%",
"\\%")
309 s = s.replace(
"&",
"\\&")
310 s = s.replace(
"_",
"\\_")
311 s = s.replace(
"{",
"\\{")
312 s = s.replace(
"}",
"\\}")
313 s = s.replace(
"\\x",
"\\textbackslash{}")
314 s = s.replace(
"~",
"\\textasciitilde{}")
315 s = s.replace(
"^",
"\\textasciicircum{}")
316 elif format == MAIL_HEADER:
321 except UnicodeEncodeError:
322 return email.header.make_header([(s,
"utf-8")]).encode()
324 raise ValueError(
'Invalid format (only None, HTML, LATEX and MAIL_HEADER are supported).')
333 """Base class for template-exceptions."""
336 class TemplateParseError(TemplateException):
337 """Template parsing failed."""
341 - `err`: error-message or exception to wrap
342 - `errpos`: ``(filename,row,col)`` where the error occured.
345 self.filename, self.row, self.
col = errpos
346 TemplateException.__init__(self)
348 if not self.filename:
349 return "line %d, col %d: %s" % (self.row, self.
col,
str(self.
err))
351 return "file %s, line %d, col %d: %s" % (self.filename, self.row, self.
col,
str(self.
err))
354 """Template syntax-error."""
357 class TemplateIncludeError(TemplateParseError):
358 """Template 'include' failed."""
362 """Template rendering failed."""
369 """Load template from a string/unicode.
371 Note that 'include' is not possible in such templates.
377 """Return template-string as unicode.
379 if isinstance(s, unicode):
386 """Load template from a file.
388 When loading a template from a file, it's possible to including other
389 templates (by using 'include' in the template). But for simplicity
390 and security, all included templates have to be in the same directory!
391 (see ``allowed_path``)
393 def __init__(self, allowed_path=None, encoding='utf-8'):
397 - `allowed_path`: path of the template-files
398 - `encoding`: encoding of the template-files
400 - `ValueError`: if `allowed_path` is not a directory
402 if allowed_path
and not os.path.isdir(allowed_path):
403 raise ValueError(
"'allowed_path' has to be a directory.")
408 """Load a template from a file.
410 Check if filename is allowed and return its contens in unicode.
413 - `filename`: filename of the template without path
415 the contents of the template-file in unicode
417 - `ValueError`: if `filename` contains a path
419 if filename != os.path.basename(filename):
420 raise ValueError(
"No path allowed in filename. (%s)" %(filename))
421 filename = os.path.join(self.
path, filename)
423 f = open(filename,
'r', encoding=self.
encoding)
433 """Parse a template into a parse-tree.
435 Includes a syntax-check, an optional expression-check and verbose
438 See documentation for a description of the parse-tree.
441 _comment_start =
"#!"
447 _block_start =
"<!--("
453 _strComment =
r"""%s(?P<content>.*?)(?P<end>%s|\n|$)""" \
454 % (re.escape(_comment_start), re.escape(_comment_end))
455 _reComment = re.compile(_strComment, re.M)
459 _strSubstitution =
r"""
461 %s\s*(?P<sub>.*?)\s*(?P<end>%s|$) #substitution
463 %s\s*(?P<escsub>.*?)\s*(?P<escend>%s|$) #escaped substitution
465 """ % (re.escape(_sub_start), re.escape(_sub_end),
466 re.escape(_subesc_start), re.escape(_subesc_end))
467 _reSubstitution = re.compile(_strSubstitution, re.X|re.M)
479 _s = re.escape(_block_start)
480 _e = re.escape(_block_end)
482 ^(?P<mEnd>[ \t]*)%send%s(?P<meIgnored>.*)\r?\n? # multi-line end (^ <!--(end)-->IGNORED_TEXT\n)
484 (?P<sEnd>)%send%s # single-line end (<!--(end)-->)
486 (?P<sSpace>[ \t]*) # single-line tag (no nesting)
487 %s(?P<sKeyw>\w+)[ \t]*(?P<sParam>.*?)%s
489 (?=(?:%s.*?%s.*?)??%send%s) # (match until end or i.e. <!--(elif/else...)-->)
491 # multi-line tag, nested by whitespace indentation
492 ^(?P<indent>[ \t]*) # save indentation of start tag
493 %s(?P<mKeyw>\w+)\s*(?P<mParam>.*?)%s(?P<mIgnored>.*)\r?\n
494 (?P<mContent>(?:.*\n)*?)
495 (?=(?P=indent)%s(?:.|\s)*?%s) # match indentation
498 _s, _e, _s, _e, _s, _e,
500 _reBlock = re.compile(_strBlock, re.X|re.M)
503 _strForParam =
r"""^(?P<names>\w+(?:\s*,\s*\w+)*)\s+in\s+(?P<iter>.+)$"""
504 _reForParam = re.compile(_strForParam)
507 _reMacroParam = re.compile(
r"""^\w+$""")
510 def __init__(self, loadfunc=None, testexpr=None, escape=HTML):
514 - `loadfunc`: function to load included templates
515 (i.e. ``LoaderFile(...).load``)
516 - `testexpr`: function to test if a template-expressions is valid
517 (i.e. ``EvalPseudoSandbox().compile``)
518 - `escape`: default-escaping (may be modified by the template)
520 - `ValueError`: if `testexpr` or `escape` is invalid.
523 self.
_load =
dummy_raise(NotImplementedError,
"'include' not supported, since no 'loadfunc' was given.")
525 self.
_load = loadfunc
532 except Exception
as err:
533 raise ValueError(
"Invalid 'testexpr'. (%s)" %(err))
536 if escape
not in ESCAPE_SUPPORTED.values():
537 raise ValueError(
"Unsupported 'escape'. (%s)" %(escape))
545 - `template`: template-unicode-string
546 :Returns: the resulting parse-tree
548 - `TemplateSyntaxError`: for template-syntax-errors
549 - `TemplateIncludeError`: if template-inclusion failed
550 - `TemplateException`
553 return self.
_parse(template)
556 """Convert `fpos` to ``(filename,row,column)`` for error-messages."""
558 return filename,
srow(string, fpos),
scol(string, fpos)
561 """Test a template-expression to detect errors."""
564 except SyntaxError
as err:
568 """Parse substitutions, and append them to the parse-tree.
570 Additionally, remove comments.
574 start = match.start()
576 parsetree.append((
"str", self.
_reComment.sub(
'', text[curr:start])))
578 if match.group(
"sub")
is not None:
579 if not match.group(
"end"):
582 if len(match.group(
"sub")) > 0:
583 self.
_testexpr(match.group(
"sub"), fpos+start)
584 parsetree.append((
"sub", match.group(
"sub")))
586 assert(match.group(
"escsub")
is not None)
587 if not match.group(
"escend"):
590 if len(match.group(
"escsub")) > 0:
591 self.
_testexpr(match.group(
"escsub"), fpos+start)
592 parsetree.append((
"esc", self.
escape, match.group(
"escsub")))
597 parsetree.append((
"str", self.
_reComment.sub(
'', text[curr:])))
600 """Recursive part of `parse()`.
604 - fpos: position of ``template`` in the complete template (for error-messages)
619 for match
in self.
_reBlock.finditer(template):
620 start = match.start()
623 self.
_parse_sub(parsetree, template[curr:start], fpos)
627 block = match.groupdict()
629 if block[
"sKeyw"]
is not None:
631 keyword = block[
"sKeyw"]
632 param = block[
"sParam"]
633 content = block[
"sContent"]
635 if len(parsetree) > 0
and parsetree[-1][0] ==
"str":
636 parsetree[-1] = (
"str", parsetree[-1][1] + block[
"sSpace"])
638 parsetree.append((
"str", block[
"sSpace"]))
639 pos_p = fpos + match.start(
"sParam")
640 pos_c = fpos + match.start(
"sContent")
641 elif block[
"mKeyw"]
is not None:
642 block_indent = len(block[
"indent"])
643 keyword = block[
"mKeyw"]
644 param = block[
"mParam"]
645 content = block[
"mContent"]
646 pos_p = fpos + match.start(
"mParam")
647 pos_c = fpos + match.start(
"mContent")
648 ignored = block[
"mIgnored"].strip()
651 elif block[
"mEnd"]
is not None:
652 if block_type
is None:
654 if block_indent != len(block[
"mEnd"]):
656 ignored = block[
"meIgnored"].strip()
660 elif block[
"sEnd"]
is not None:
661 if block_type
is None:
663 if block_indent
is not None:
667 raise TemplateException(
"FATAL: Block regexp error. Please contact the author. (%s)" % match.group())
671 keyword = keyword.lower()
673 if block_type
is not None:
679 names = tuple(n.strip()
for n
in cond.group(
"names").split(
","))
680 self.
_testexpr(cond.group(
"iter"), pos_p+cond.start(
"iter"))
681 parsetree.append((
"for", names, cond.group(
"iter"), self.
_parse(content, pos_c)))
682 elif 'if' == keyword:
683 if block_type
is not None:
689 parsetree.append((
"if", param, self.
_parse(content, pos_c)))
690 elif 'elif' == keyword:
691 if block_type !=
'if':
696 parsetree.append((
"elif", param, self.
_parse(content, pos_c)))
697 elif 'else' == keyword:
698 if block_type
not in (
'if',
'for'):
702 parsetree.append((
"else", self.
_parse(content, pos_c)))
703 elif 'macro' == keyword:
704 if block_type
is not None:
713 if len(content) > 0
and content[-1] ==
'\n':
714 content = content[:-1]
715 if len(content) > 0
and content[-1] ==
'\r':
716 content = content[:-1]
717 parsetree.append((
"macro", param, self.
_parse(content, pos_c)))
720 elif 'raw' == keyword:
721 if block_type
is not None:
726 parsetree.append((
"str", content))
727 elif 'include' == keyword:
728 if block_type
is not None:
732 block_type =
'include'
734 u = self.
_load(content.strip())
735 except Exception
as err:
741 elif 'set_escape' == keyword:
742 if block_type
is not None:
746 block_type =
'set_escape'
747 esc = content.strip().upper()
748 if esc
not in ESCAPE_SUPPORTED:
750 self.
escape = ESCAPE_SUPPORTED[esc]
755 if block_type
is not None:
758 if len(template) > curr:
759 self.
_parse_sub(parsetree, template[curr:], fpos+curr)
767 assert len(eval(
"dir()", {
'__builtins__':{
'dir':dir}})) == 1, \
768 "FATAL: 'eval' does not work as expected (%s)."
769 assert compile(
"0 .__class__",
"<string>",
"eval").co_names == (
'__class__',), \
770 "FATAL: 'compile' does not work as expected."
773 """An eval-pseudo-sandbox.
775 The pseudo-sandbox restricts the available functions/objects, so the
776 code can only access:
778 - some of the builtin Python-functions, which are considered "safe"
780 - some additional functions (exists(), default(), setvar(), escape())
781 - the passed objects incl. their methods.
783 Additionally, names beginning with "_" are forbidden.
784 This is to prevent things like '0 .__class__', with which you could
785 easily break out of a "sandbox".
787 Be careful to only pass "safe" objects/functions to the template,
788 because any unsafe function/method could break the sandbox!
789 For maximum security, restrict the access to as few objects/functions
793 Note that this is no real sandbox! (And although I don't know any
794 way to break out of the sandbox without passing-in an unsafe object,
795 I cannot guarantee that there is no such way. So use with care.)
797 Take care if you want to use it for untrusted code!!
805 "abs" : builtins.abs,
806 "chr" : builtins.chr,
807 "divmod" : builtins.divmod,
808 "hash" : builtins.hash,
809 "hex" : builtins.hex,
810 "len" : builtins.len,
811 "max" : builtins.max,
812 "min" : builtins.min,
813 "oct" : builtins.oct,
814 "ord" : builtins.ord,
815 "pow" : builtins.pow,
816 "range" : builtins.range,
817 "round" : builtins.round,
818 "sorted" : builtins.sorted,
819 "sum" : builtins.sum,
820 "unichr" : builtins.chr,
821 "zip" : builtins.zip,
823 "bool" : builtins.bool,
824 "bytes" : builtins.bytes,
825 "complex" : builtins.complex,
826 "dict" : builtins.dict,
827 "enumerate" : builtins.enumerate,
828 "float" : builtins.float,
829 "int" : builtins.int,
830 "list" : builtins.list,
832 "reversed" : builtins.reversed,
833 "str" : builtins.str,
834 "tuple" : builtins.tuple,
837 if sys.version_info[0] < 3:
838 safe_builtins[
"unichr"] = builtins.unichr
851 """Add an object to the "allowed eval-globals".
853 Mainly useful to add user-defined functions to the pseudo-sandbox.
858 """Compile a Python-eval-expression.
860 - Use a compile-cache.
861 - Raise a `NameError` if `expr` contains a name beginning with ``_``.
863 :Returns: the compiled `expr`
865 - `SyntaxError`: for compile-errors
866 - `NameError`: if expr contains a name beginning with ``_``
872 raise NameError(
"Name '%s' is not allowed." % i)
877 """Eval a Python-eval-expression.
879 Sets ``self.locals_ptr`` to ``locales`` and compiles the code
889 """``import``/``__import__()`` for the sandboxed code.
891 Since "import" is insecure, the PseudoSandbox does not allow to
892 import other modules. But since some functions need to import
893 other modules (e.g. "datetime.datetime.strftime" imports "time"),
894 this function replaces the builtin "import" and allows to use
895 modules which are already accessible by the sandboxed code.
898 - This probably only works for rather simple imports.
899 - For security, it may be better to avoid such (complex) modules
900 which import other modules. (e.g. use time.localtime and
901 time.strftime instead of datetime.datetime.strftime,
902 or write a small wrapper.)
906 >>> from datetime import datetime
908 >>> t = pyratemp.Template('@!mytime.strftime("%H:%M:%S")!@')
910 # >>> print(t(mytime=datetime.now()))
911 # Traceback (most recent call last):
913 # ImportError: import not allowed in pseudo-sandbox; try to import 'time' yourself and pass it to the sandbox/template
916 >>> print(t(mytime=datetime.strptime("13:40:54", "%H:%M:%S"), time=time))
919 # >>> print(t(mytime=datetime.now(), time=time))
926 raise ImportError(
"import not allowed in pseudo-sandbox; try to import '%s' yourself (and maybe pass it to the sandbox/template)" % name)
929 """``exists()`` for the sandboxed code.
931 Test if the variable `varname` exists in the current locals-namespace.
933 This only works for single variable names. If you want to test
934 complicated expressions, use i.e. `default`.
935 (i.e. `default("expr",False)`)
937 :Note: the variable-name has to be quoted! (like in eval)
938 :Example: see module-docstring
943 """``default()`` for the sandboxed code.
945 Try to evaluate an expression and return the result or a
946 fallback-/default-value; the `default`-value is used
947 if `expr` does not exist/is invalid/results in None.
949 This is very useful for optional data.
952 - expr: eval-expression
953 - default: fallback-falue if eval(expr) fails or is None.
955 the eval-result or the "fallback"-value.
957 :Note: the eval-expression has to be quoted! (like in eval)
958 :Example: see module-docstring
966 except (NameError, LookupError, TypeError):
970 """``setvar()`` for the sandboxed code.
974 :Example: see module-docstring
980 """``escape()`` for the sandboxed code.
982 if isinstance(format, (str, unicode)):
983 format = ESCAPE_SUPPORTED[format.upper()]
990 """Basic template-class.
992 Used both for the template itself and for 'macro's ("subtemplates") in
996 def __init__(self, parsetree, renderfunc, data=None):
997 """Create the Template/Subtemplate/Macro.
1000 - `parsetree`: parse-tree of the template/subtemplate/macro
1001 - `renderfunc`: render-function
1002 - `data`: data to fill into the template by default (dictionary).
1003 This data may later be overridden when rendering the template.
1005 - `TypeError`: if `data` is not a dictionary
1009 if isinstance(data, dict):
1014 raise TypeError(
'"data" must be a dict (or None).')
1019 """Fill out/render the template.
1022 - `override`: objects to add to the data-namespace, overriding
1024 :Returns: the filled template (in unicode)
1025 :Note: This is also called when invoking macros
1026 (i.e. ``$!mymacro()!$``).
1035 """Alias for __call__()."""
1038 """Alias for __call__()."""
1045 """Unicode-string which should not be escaped.
1047 If ``isinstance(object,_dontescape)``, then don't escape the object in
1048 ``@!...!@``. It's useful for not double-escaping macros, and it's
1049 automatically used for macros/subtemplates.
1051 :Note: This only works if the object is used on its own in ``@!...!@``.
1052 It i.e. does not work in ``@!object*2!@`` or ``@!object + "hi"!@``.
1058 """Render a template-parse-tree.
1060 :Uses: `TemplateBase` for macros
1064 """Init the renderer.
1067 - `evalfunc`: function for template-expression-evaluation
1068 (i.e. ``EvalPseudoSandbox().eval``)
1069 - `escapefunc`: function for escaping special characters
1077 """evalfunc with error-messages"""
1081 except (TypeError,NameError,LookupError,AttributeError, SyntaxError)
as err:
1082 raise TemplateRenderError(
"Cannot eval expression '%s'. (%s: %s)" %(expr, err.__class__.__name__, err))
1085 """Render a parse-tree of a template.
1088 - `parsetree`: the parse-tree
1089 - `data`: the data to fill into the template (dictionary)
1090 :Returns: the rendered output-unicode-string
1092 - `TemplateRenderError`
1098 if parsetree
is None:
1100 for elem
in parsetree:
1101 if "str" == elem[0]:
1102 output.append(elem[1])
1103 elif "sub" == elem[0]:
1105 elif "esc" == elem[0]:
1106 obj =
_eval(elem[2], data)
1108 if isinstance(obj, _dontescape)
or isinstance(obj, TemplateBase):
1112 elif "for" == elem[0]:
1114 (names, iterable) = elem[1:3]
1116 loop_iter = iter(
_eval(iterable, data))
1124 data.update(zip(names, i))
1125 output.extend(self.
render(elem[3], data))
1126 elif "if" == elem[0]:
1128 if _eval(elem[1], data):
1130 output.extend(self.
render(elem[2], data))
1131 elif "elif" == elem[0]:
1132 if do_else
and _eval(elem[1], data):
1134 output.extend(self.
render(elem[2], data))
1135 elif "else" == elem[0]:
1138 output.extend(self.
render(elem[1], data))
1139 elif "macro" == elem[0]:
1150 """Template-User-Interface.
1154 t = Template(...) (<- see __init__)
1155 output = t(...) (<- see TemplateBase.__call__)
1158 see module-docstring
1161 def __init__(self, string=None,filename=None,parsetree=None, encoding='utf-8', data=None, escape=HTML,
1162 loader_class=LoaderFile,
1163 parser_class=Parser,
1164 renderer_class=Renderer,
1165 eval_class=EvalPseudoSandbox,
1166 escape_func=escape):
1167 """Load (+parse) a template.
1170 - `string,filename,parsetree`: a template-string,
1171 filename of a template to load,
1172 or a template-parsetree.
1173 (only one of these 3 is allowed)
1174 - `encoding`: encoding of the template-files (only used for "filename")
1175 - `data`: data to fill into the template by default (dictionary).
1176 This data may later be overridden when rendering the template.
1177 - `escape`: default-escaping for the template, may be overwritten by the template!
1184 if [string, filename, parsetree].count(
None) != 2:
1185 raise ValueError(
'Exactly 1 of string,filename,parsetree is necessary.')
1189 if filename
is not None:
1190 incl_load = loader_class(os.path.dirname(filename), encoding).load
1191 tmpl = incl_load(os.path.basename(filename))
1192 if string
is not None:
1193 incl_load =
dummy_raise(NotImplementedError,
"'include' not supported for template-strings.")
1197 templateeval = eval_class()
1200 if tmpl
is not None:
1201 p = parser_class(loadfunc=incl_load, testexpr=templateeval.compile, escape=escape)
1202 parsetree = p.parse(tmpl)
1206 renderfunc = renderer_class(templateeval.eval, escape_func).render
1209 TemplateBase.__init__(self, parsetree, renderfunc, data)
1216 """doctest this module."""
1221 if __name__ ==
'__main__':
1222 if sys.version_info[0] <= 2: