Project

General

Profile

TemplateParser » History » Version 25

Elmer de Looff, 2012-02-20 17:53

1 1 Elmer de Looff
h1. TemplateParser
2 25 Elmer de Looff
3 25 Elmer de Looff
{{>toc}}
4 1 Elmer de Looff
5 1 Elmer de Looff
The µWeb TemplateParser is a in-house developed templating engine that provides tag replacement, tag-functions and template control functions. This document will describe the following:
6 21 Elmer de Looff
* The *[[TemplateParser#template|Template class]]*, used to parse the templating language
7 21 Elmer de Looff
* The *[[TemplateParser#parser|Parser class]]*, which provides template loading and caching
8 21 Elmer de Looff
* *[[TemplateParser#using|Using TemplateParser]]* inside a µWeb PageMaker
9 21 Elmer de Looff
* *[[TemplateParser#syntax|Template syntax]]*, an overview of the language's constructs and behaviors
10 1 Elmer de Looff
11 1 Elmer de Looff
First though, to help with understanding the TemplateParser, a minimal size template document:
12 1 Elmer de Looff
13 1 Elmer de Looff
<pre><code class="html">
14 1 Elmer de Looff
Hello [title] [name]
15 1 Elmer de Looff
</code></pre>
16 4 Elmer de Looff
17 1 Elmer de Looff
The above document contains two simple template tags. These tags are delimited by square brackets, and they will be replaced by the named argument provided during parsing. If this name is not present, then the literal presentation of the tag will remain in the output.
18 1 Elmer de Looff
19 5 Elmer de Looff
h1(#template). Template class
20 4 Elmer de Looff
21 4 Elmer de Looff
The @Template@ class provides the interface for pre-parsing templates, loading them from files and parsing single templates to completion. During pre-parsing, constructs such as loops and conditional statements are converted to @TemplateLoop@ and @TemplateConditional@ objects, and their scopes nested appropriately in the @Template@. Tags are replaced by @TemplateTag@ instances, and text is captured in @TemplateText@. All of these provide @Parse@ methods, which together result in the combined parsed template output.
22 4 Elmer de Looff
23 4 Elmer de Looff
h2. Creating a template
24 4 Elmer de Looff
25 4 Elmer de Looff
A template is created simple by providing a string input to the @Template@'s constructor. This will return a valid Template instance (or raise an error if there is a problem with the [[TemplateParser#syntax|syntax]]:
26 4 Elmer de Looff
27 4 Elmer de Looff
<pre><code class="python">
28 21 Elmer de Looff
>>> import templateparser
29 4 Elmer de Looff
>>> template = templateparser.Template('Hello [title] [name]')
30 4 Elmer de Looff
>>> template
31 4 Elmer de Looff
Template([TemplateText('Hello '), TemplateTag('[title]'), TemplateText(' '), TemplateTag('[name]')])
32 4 Elmer de Looff
</code></pre>
33 4 Elmer de Looff
34 4 Elmer de Looff
Above can be seen the various parts of the template, which will be combined to output once parsed.
35 4 Elmer de Looff
36 4 Elmer de Looff
h2. Loading a template from file
37 4 Elmer de Looff
38 4 Elmer de Looff
The @Template@ class provides a @classmethod@ called @FromFile@, which loads the template at the path.
39 4 Elmer de Looff
40 4 Elmer de Looff
Loading a template named @example.utp@ from the current working directory:
41 4 Elmer de Looff
42 4 Elmer de Looff
<pre><code class="python">
43 21 Elmer de Looff
>>> import templateparser
44 4 Elmer de Looff
>>> template = templateparser.Template.FromFile('example.utp')
45 4 Elmer de Looff
>>> template
46 4 Elmer de Looff
Template([TemplateText('Hello '), TemplateTag('[title]'), TemplateText(' '), TemplateTag('[name]')])
47 4 Elmer de Looff
</code></pre>
48 4 Elmer de Looff
49 5 Elmer de Looff
h2. Parsing a template
50 4 Elmer de Looff
51 4 Elmer de Looff
Parsing a template can be done by calling the @Template@'s @Parse@ method. The keyword arguments provided to this call will from the replacement mapping for the template. In the following example, we will provide one such keyword, and leave the other undefined to show the (basic) behavior of the @Template.Parse@ method.
52 4 Elmer de Looff
53 4 Elmer de Looff
<pre><code class="python">
54 21 Elmer de Looff
>>> import templateparser
55 4 Elmer de Looff
>>> template = templateparser.Template('Hello [title] [name]')
56 8 Elmer de Looff
>>> template.Parse(title='sir')
57 8 Elmer de Looff
'Hello sir [name]'
58 4 Elmer de Looff
</code></pre>
59 1 Elmer de Looff
60 1 Elmer de Looff
h1(#parser). Parser class
61 6 Elmer de Looff
62 1 Elmer de Looff
The @Parser@ class provides simple management of multiple @Template@ objects. It is mainly used to load templates from disk. When initiating a @Parser@, the first argument provides the search path from where templates should be loaded (the default is the current working directory). An optional second argument can be provided to preload the template cache: a mapping of names and @Template@ objects.
63 1 Elmer de Looff
64 8 Elmer de Looff
h2. Loading templates
65 8 Elmer de Looff
66 6 Elmer de Looff
Creating a parser object, and loading the 'example.utp' template from the 'templates' directory works like this:
67 6 Elmer de Looff
68 6 Elmer de Looff
<pre><code class="python">
69 21 Elmer de Looff
>>> import templateparser
70 7 Elmer de Looff
>>> # This sets the 'templates' directory as the search path for AddTemplate
71 7 Elmer de Looff
>>> parser = templateparser.Parser('templates')
72 7 Elmer de Looff
>>> # Loads the 'templates/example.utp' and stores it as 'example.utp':
73 7 Elmer de Looff
>>> template = parser.AddTemplate('example.utp')
74 1 Elmer de Looff
>>> template.Parse(title='mister', name='Bob Dobalina')
75 1 Elmer de Looff
'Hello mister Bob Dobalina'
76 6 Elmer de Looff
</code></pre>
77 1 Elmer de Looff
78 8 Elmer de Looff
The @AddTemplate@ method takes a second optional argument, which allows us to give the template a different name in the cache, which we will now explain.
79 1 Elmer de Looff
80 8 Elmer de Looff
h2. Template cache and auto-loading
81 8 Elmer de Looff
82 8 Elmer de Looff
The @Parser@ object behaves like a slightly modified dictionary to achieve this. Retrieving keys yields the associated template. Keys that are not present in the cache are _automatically_ retrieved from the filesystem:
83 8 Elmer de Looff
84 1 Elmer de Looff
<pre><code class="python">
85 21 Elmer de Looff
>>> import templateparser
86 7 Elmer de Looff
>>> parser = templateparser.Parser('templates')
87 7 Elmer de Looff
>>> parser
88 7 Elmer de Looff
Parser({})  # The parser is empty (has no cached templates)
89 1 Elmer de Looff
>>> # Automatically loads the named template from the 'templates' directory:
90 1 Elmer de Looff
>>> parser['example.utp'].Parse(title='mister', name='Bob Dobalina')
91 7 Elmer de Looff
'Hello mister Bob Dobalina'
92 1 Elmer de Looff
>>> parser
93 8 Elmer de Looff
Parser({'example.utp': Template([TemplateText('Hello '), TemplateTag('[title]'),
94 8 Elmer de Looff
                                 TemplateText(' '), TemplateTag('[name]')])})
95 7 Elmer de Looff
</code></pre>
96 7 Elmer de Looff
97 7 Elmer de Looff
If these cannot be found, @TemplateReadError@ is raised:
98 7 Elmer de Looff
99 7 Elmer de Looff
<pre><code class="python">
100 21 Elmer de Looff
>>> import templateparser
101 7 Elmer de Looff
>>> parser = templateparser.Parser('templates')
102 6 Elmer de Looff
>>> parser['bad_template.utp'].Parse(failure='imminent')
103 6 Elmer de Looff
Traceback (most recent call last):
104 6 Elmer de Looff
  File "<stdin>", line 1, in <module>
105 7 Elmer de Looff
  File "/var/lib/underdark/libs/uweb/templateparser.py", line 147, in __getitem__
106 6 Elmer de Looff
    self.AddTemplate(template)
107 1 Elmer de Looff
  File "/var/lib/underdark/libs/uweb/templateparser.py", line 171, in AddTemplate
108 1 Elmer de Looff
    raise TemplateReadError('Could not load template %r' % template_path)
109 1 Elmer de Looff
underdark.libs.uweb.templateparser.TemplateReadError: Could not load template 'templates/bad_template.utp'
110 1 Elmer de Looff
</code></pre>
111 8 Elmer de Looff
112 18 Elmer de Looff
h2. @Parse@ and @ParseString@ methods
113 8 Elmer de Looff
114 8 Elmer de Looff
For convencience and consistency, the @Parser@ comes with two handy methods to provide parsing of @Template@ objects, one from its cache, one from raw template strings. It is recommended to use these over the previously shown direct key-based access:
115 8 Elmer de Looff
116 8 Elmer de Looff
<pre><code class="python">
117 21 Elmer de Looff
>>> import templateparser
118 8 Elmer de Looff
>>> parser = templateparser.Parser('templates')
119 8 Elmer de Looff
>>> parser.Parse('example.utp', title='mister', name='Bob Dobalina')
120 8 Elmer de Looff
'Hello mister Bob Dobalina'
121 8 Elmer de Looff
>>> parser.ParseString('Hello [title] [name]', title='mister', name='Bob Dobalina')
122 8 Elmer de Looff
'Hello mister Bob Dobalina'</code></pre>
123 6 Elmer de Looff
124 1 Elmer de Looff
h1(#using). Using TemplateParser inside µWeb
125 10 Elmer de Looff
126 10 Elmer de Looff
Within the default µWeb @PageMaker@, there is a @parser@ property, which provides a [[TemplateParser#parser|Parser]] object. The class constant @TEMPLATE_DIR@ provides the template search directory. The default template directory is @'templates'@. *N.B.* This path is relative to the file that contains the PageMaker class.
127 10 Elmer de Looff
128 10 Elmer de Looff
An example of TemplateParser to create a complete response:
129 10 Elmer de Looff
<pre><code class="python">
130 10 Elmer de Looff
from underdark.libs import uweb
131 10 Elmer de Looff
import time
132 10 Elmer de Looff
133 10 Elmer de Looff
class PageMaker(uweb.PageMaker):
134 10 Elmer de Looff
  def VersionPage(self):
135 10 Elmer de Looff
    return self.parser.Parse(
136 10 Elmer de Looff
      'version.utp', year=time.strftime('%Y'), version=uweb.__version__)
137 10 Elmer de Looff
</code></pre>
138 10 Elmer de Looff
139 10 Elmer de Looff
The example template for the above file could look something like this:
140 10 Elmer de Looff
141 10 Elmer de Looff
142 10 Elmer de Looff
<pre><code class="html">
143 10 Elmer de Looff
<!DOCTYPE html>
144 10 Elmer de Looff
<html>
145 10 Elmer de Looff
  <head>
146 10 Elmer de Looff
    <title>µWeb version info</title>
147 10 Elmer de Looff
  </head>
148 10 Elmer de Looff
  <body>
149 10 Elmer de Looff
    <p>µWeb version [version] - Copyright 2010-[year] Underdark</p>
150 10 Elmer de Looff
  </body>
151 10 Elmer de Looff
</html>
152 10 Elmer de Looff
</code></pre>
153 1 Elmer de Looff
154 5 Elmer de Looff
h1(#syntax). Templating language syntax
155 11 Elmer de Looff
156 11 Elmer de Looff
The templating syntax is relatively limited, but with the limited syntax it provides a flexible and rich system to create templates. Covered in these examples are:
157 11 Elmer de Looff
* Simple tags (used in various examples above)
158 11 Elmer de Looff
* Tag indexing
159 11 Elmer de Looff
* Tag functions
160 11 Elmer de Looff
* Template language constructs
161 11 Elmer de Looff
162 11 Elmer de Looff
All examples will consist of three parts:
163 11 Elmer de Looff
# The example template
164 11 Elmer de Looff
# The python invocation string (the template will be named 'example.utp')
165 11 Elmer de Looff
# The resulting output (as source, not as parsed HTML)
166 11 Elmer de Looff
167 11 Elmer de Looff
h2. Simple tags
168 11 Elmer de Looff
169 11 Elmer de Looff
This is an example for the most basic form of template tags. The tag is enclosed by square brackets as such: @[tag]@. Tags that match a provided argument to the Parse call get replaced. If there is no argument that matches the tag name, it is returned in the output verbatim. This is also demonstrated in the below example
170 11 Elmer de Looff
171 11 Elmer de Looff
The example below is a repeat of the example how to use TemplateParser inside µWeb, and shows the template result:
172 11 Elmer de Looff
173 11 Elmer de Looff
<pre><code class="html">
174 11 Elmer de Looff
<!DOCTYPE html>
175 11 Elmer de Looff
<html>
176 11 Elmer de Looff
  <head>
177 11 Elmer de Looff
    <title>µWeb version info</title>
178 11 Elmer de Looff
  </head>
179 11 Elmer de Looff
  <body>
180 11 Elmer de Looff
    <p>µWeb version [version] - Copyright 2010-[year] Underdark</p>
181 11 Elmer de Looff
    <p>
182 11 Elmer de Looff
      This [paragraph] is not replaced because there is no
183 11 Elmer de Looff
      paragraph argument provided to the parser.
184 11 Elmer de Looff
    </p>
185 11 Elmer de Looff
  </body>
186 11 Elmer de Looff
</html>
187 11 Elmer de Looff
</code></pre>
188 11 Elmer de Looff
189 11 Elmer de Looff
<pre><code class="python">
190 11 Elmer de Looff
>>> parser.Parse('version.utp', year=time.strftime('%Y'), version=uweb.__version__)
191 11 Elmer de Looff
</code></pre>
192 11 Elmer de Looff
193 11 Elmer de Looff
<pre><code class="html">
194 11 Elmer de Looff
<!DOCTYPE html>
195 11 Elmer de Looff
<html>
196 11 Elmer de Looff
  <head>
197 11 Elmer de Looff
    <title>µWeb version info</title>
198 11 Elmer de Looff
  </head>
199 11 Elmer de Looff
  <body>
200 11 Elmer de Looff
    <p>µWeb version 0.11 - Copyright 2010-212 Underdark</p>
201 11 Elmer de Looff
    <p>
202 11 Elmer de Looff
      This [paragraph] is not replaced because there is no
203 11 Elmer de Looff
      paragraph argument provided to the parser.
204 11 Elmer de Looff
    </p>
205 11 Elmer de Looff
  </body>
206 11 Elmer de Looff
</html>
207 11 Elmer de Looff
</code></pre>
208 11 Elmer de Looff
209 11 Elmer de Looff
h2. Tag indexing
210 11 Elmer de Looff
211 16 Elmer de Looff
Something about tax indexing
212 16 Elmer de Looff
213 11 Elmer de Looff
h2. Tag functions
214 11 Elmer de Looff
215 15 Elmer de Looff
Tag functions
216 15 Elmer de Looff
217 21 Elmer de Looff
h3. Default html escaping
218 11 Elmer de Looff
219 11 Elmer de Looff
h3. Adding custom functions
220 11 Elmer de Looff
221 23 Elmer de Looff
h2. TemplateLoop
222 11 Elmer de Looff
223 23 Elmer de Looff
As a language construct, TemplateParser has an understanding of iteration. The @TemplateLoop@ can be compared to the Python @for@-loop, or the @foreach@ construct in other languages (lazy iteration over the values of an iterable).
224 1 Elmer de Looff
225 23 Elmer de Looff
h3. Syntax and properties
226 23 Elmer de Looff
227 23 Elmer de Looff
*Syntax: @{{ for local_var in [collection] }}@*
228 20 Elmer de Looff
* The double accolades (curly braces) indicate the beginning and end of the construct;
229 20 Elmer de Looff
* The @for@ keyword indicates the structure to execute;
230 20 Elmer de Looff
* @local_var@ is the name which references the loop variable;
231 20 Elmer de Looff
* @[collection]@ is the tag that provides the iteratable.
232 20 Elmer de Looff
233 20 Elmer de Looff
*Properties*
234 20 Elmer de Looff
* The local name is stated without brackets (as it's no tag itself)
235 1 Elmer de Looff
* When it needs to be placed in the output, the local name should have brackets (like any other tag)
236 20 Elmer de Looff
* *N.B.* The local variable does _not_ bleed into the outer scope after the loop has completed.
237 20 Elmer de Looff
 It is therefore possible (though not recommended) to name the loop variable after the iterable: @{{ for collection in [collection] }}@.
238 20 Elmer de Looff
239 23 Elmer de Looff
h3. Example of a @TemplateLoop@
240 20 Elmer de Looff
241 20 Elmer de Looff
<pre><code class="html">
242 20 Elmer de Looff
<html>
243 20 Elmer de Looff
  <body>
244 20 Elmer de Looff
    <ul>
245 20 Elmer de Looff
    {{ for name in [presidents] }}
246 20 Elmer de Looff
      <li>President [name]</li>
247 20 Elmer de Looff
    {{ endfor }}
248 20 Elmer de Looff
    </ul>
249 20 Elmer de Looff
  </body>
250 20 Elmer de Looff
</html>
251 20 Elmer de Looff
</code></pre>
252 20 Elmer de Looff
253 20 Elmer de Looff
<pre><code class="python">
254 20 Elmer de Looff
>>> parser.Parse('rushmore.utp', presidents=['Washington', 'Jefferson', 'Roosevelt', 'Lincoln'])
255 20 Elmer de Looff
</code></pre>
256 20 Elmer de Looff
257 20 Elmer de Looff
<pre><code class="html">
258 20 Elmer de Looff
<html>
259 20 Elmer de Looff
  <body>
260 1 Elmer de Looff
    <ul>
261 1 Elmer de Looff
      <li>President Washington</li>
262 1 Elmer de Looff
      <li>President Jefferson</li>
263 1 Elmer de Looff
      <li>President Roosevelt</li>
264 1 Elmer de Looff
      <li>President Lincoln</li>
265 1 Elmer de Looff
    </ul>
266 1 Elmer de Looff
  </body>
267 1 Elmer de Looff
</html>
268 1 Elmer de Looff
</code></pre>
269 1 Elmer de Looff
270 1 Elmer de Looff
h2. Inlining templates
271 21 Elmer de Looff
272 21 Elmer de Looff
Often, there will be snippets of a template that will see a lot of reuse. Page headers and footers are often the same on many pages, and having several redundant copies means that changes will have to be replicated to each of these occurrances. To reduce the need for this, TemplateParser has an @inline@ statement. Using this you can specify a template that is available in the @[[TemplateParser#Parser]]@ instance and the statement will be replaced by the template.
273 21 Elmer de Looff
274 21 Elmer de Looff
Of course, if the inlined template is not already in the @Parser@ instance, the autoloading mechanism will trigger, and the named template will be search for in the @Parser@'s template directory.
275 21 Elmer de Looff
276 21 Elmer de Looff
First, we will define our inline template, @'inline_hello.utp'@:
277 21 Elmer de Looff
278 21 Elmer de Looff
<pre><code class="html">
279 21 Elmer de Looff
<p>Hello [name]</p>
280 21 Elmer de Looff
</code></pre>
281 21 Elmer de Looff
282 21 Elmer de Looff
Secondly, our main template, @'hello.utp'@:
283 21 Elmer de Looff
284 21 Elmer de Looff
<pre><code class="html">
285 21 Elmer de Looff
<h1>Greetings</h1>
286 21 Elmer de Looff
{{ inline inline_hello.utp }}
287 21 Elmer de Looff
</code></pre>
288 21 Elmer de Looff
289 21 Elmer de Looff
Then we parse the template:
290 21 Elmer de Looff
291 21 Elmer de Looff
<pre><code class="python">
292 21 Elmer de Looff
>>> parser.Parse('hello.utp', name='Dr John')
293 21 Elmer de Looff
</code></pre>
294 21 Elmer de Looff
295 21 Elmer de Looff
<pre><code class="html">
296 21 Elmer de Looff
<h1>Greetings</h1>
297 21 Elmer de Looff
<p>Hello Dr John</p>
298 21 Elmer de Looff
</code></pre>
299 11 Elmer de Looff
300 11 Elmer de Looff
h2. Conditional statements
301 11 Elmer de Looff
302 22 Elmer de Looff
Often, you'll want the output of your template to be dependent on the value, presence, or boolean value of another tag. For instance, we may want a print a list of attendees to a party. We start the @if@ conditional by checking the boolean value of the @attendees@ tag. If this list if not-empty, we will print the attendee names, but if it's empty (or contains only a single entry), we'll tell the user in more intelligent ways than giving them a list with zero entries:
303 22 Elmer de Looff
304 22 Elmer de Looff
<pre><code class="html">
305 22 Elmer de Looff
<h1>Party attendees</h1>
306 22 Elmer de Looff
{{ if len([attendees]) > 1 }}
307 22 Elmer de Looff
  <ol>
308 22 Elmer de Looff
    {{ for attendee in [attendees] }}
309 22 Elmer de Looff
    <li>[attendee:name]</li>
310 22 Elmer de Looff
    {{ endfor }}
311 22 Elmer de Looff
  </ol>
312 22 Elmer de Looff
{{ elif [attendees] }}
313 22 Elmer de Looff
  <p>only [attendees:0:name] is attending.</p>
314 22 Elmer de Looff
{{ else }}
315 22 Elmer de Looff
  <p>There are no registered attendees yet.</p>
316 22 Elmer de Looff
{{ endif }}
317 22 Elmer de Looff
</code></pre>
318 22 Elmer de Looff
319 22 Elmer de Looff
For the case where there are several attendees:
320 22 Elmer de Looff
321 22 Elmer de Looff
<pre><code class="python">
322 22 Elmer de Looff
>>> parser.Parse('party.utp', attendees=[
323 22 Elmer de Looff
...    {'name': 'Livingstone'},
324 22 Elmer de Looff
...    {'name': 'Cook'},
325 22 Elmer de Looff
...    {'name': 'Drake'}])
326 22 Elmer de Looff
</code></pre>
327 22 Elmer de Looff
328 22 Elmer de Looff
<pre><code class="html">
329 22 Elmer de Looff
<h1>Party attendees</h1>
330 22 Elmer de Looff
<ol>
331 22 Elmer de Looff
  <li>Livingstone</li>
332 22 Elmer de Looff
  <li>Cook</li>
333 22 Elmer de Looff
  <li>Drake</li>
334 22 Elmer de Looff
</ol>
335 22 Elmer de Looff
</code></pre>
336 22 Elmer de Looff
337 22 Elmer de Looff
For the case where there is one attendee:
338 22 Elmer de Looff
339 22 Elmer de Looff
<pre><code class="python">
340 22 Elmer de Looff
>>> parser.Parse('party.utp', attendees=[{'name': 'Johnny'}])
341 22 Elmer de Looff
</code></pre>
342 22 Elmer de Looff
343 22 Elmer de Looff
<pre><code class="html">
344 22 Elmer de Looff
<h1>Party attendees</h1>
345 22 Elmer de Looff
<p>Only Johnny is attending.</p>
346 22 Elmer de Looff
</code></pre>
347 22 Elmer de Looff
348 22 Elmer de Looff
And in the case where there are no attendees:
349 22 Elmer de Looff
350 22 Elmer de Looff
<pre><code class="python">
351 22 Elmer de Looff
>>> parser.Parse('party.utp', attendees=[])
352 22 Elmer de Looff
</code></pre>
353 22 Elmer de Looff
354 22 Elmer de Looff
<pre><code class="html">
355 22 Elmer de Looff
<h1>Party attendees</h1>
356 22 Elmer de Looff
<p>There are no registered attendees yet.</p>
357 22 Elmer de Looff
</code></pre>
358 22 Elmer de Looff
359 22 Elmer de Looff
h3. Properties of conditional statements
360 22 Elmer de Looff
361 22 Elmer de Looff
* *All template keys must be referenced as proper tag*
362 22 Elmer de Looff
 This is to prevent mixing of the template variables with the functions and reserved names of Python itself. Conditional expressions are evaluated using @eval()@, and proper tags are replaced by temporary names, the values of which are stored in a retrieve-on-demand dictionary. This makes them perfectly safe with regard to the value of template replacements, but some care should be taken with the writing of the conditional expressions.
363 22 Elmer de Looff
* *It is possible to index tags in conditional statements*
364 22 Elmer de Looff
 This allows for decisions based on the values in those indexes/keys. For instance, @Person@ objects can be checked for gender, so that the correct gender-based icon can be displayed next to them.
365 22 Elmer de Looff
* *Referencing a tag or index that doesn't exist raises @TemplateNameError*
366 22 Elmer de Looff
 Unlike in regular template text, there is no suitable fallback value for a tag or index that cannot be retrieved. However, in most cases this can be prevented by making use of the following property:
367 22 Elmer de Looff
* *Statement evaluation is lazy*
368 22 Elmer de Looff
 Template conditions are processed left to right, and short-circuited where possible. If the first member of an @or@ group succeeds, the return value is already known. Similarly, if the first member of an @and@ group fails, the second part need not be evaluated. This way @TemplateNameErrors@ can often be prevented, as in most cases, presence of indexes can be confirmed before accessing.
369 22 Elmer de Looff
370 22 Elmer de Looff
371 11 Elmer de Looff
h2. Template unicode handling
372 11 Elmer de Looff
373 11 Elmer de Looff
Any @unicode@ object found while parsing, will automatically be encoded to UTF-8:
374 11 Elmer de Looff
375 11 Elmer de Looff
<pre><code class="python">
376 11 Elmer de Looff
>>> template = 'Underdark [love] [app]'
377 11 Elmer de Looff
>>> output = parser.ParseString(template, love=u'\u2665', app=u'\N{micro sign}Web')
378 11 Elmer de Looff
>>> output
379 12 Elmer de Looff
'Underdark \xe2\x99\xa5 \xc2\xb5Web'  # The output in its raw UTF-8 representation
380 11 Elmer de Looff
>>> output.decode('UTF8')
381 12 Elmer de Looff
u'Underdark \u2665 \xb5Web'           # The output converted to a Unicode object
382 19 Elmer de Looff
>>> print output
383 19 Elmer de Looff
Underdark ♥ µWeb                      # And the printed UTF-8 as we desired it.
384 14 Elmer de Looff
</code></pre>