Project

General

Profile

TemplateParser » History » Version 13

Elmer de Looff, 2012-02-10 15:15
Aaargh the heart won't work inside <code>

1 1 Elmer de Looff
h1. TemplateParser
2 1 Elmer de Looff
3 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:
4 3 Elmer de Looff
* The [[TemplateParser#template|Template class]], used to parse the templating language
5 3 Elmer de Looff
* The [[TemplateParser#parser|Parser class]], which provides template loading and caching
6 3 Elmer de Looff
* [[TemplateParser#using|Using TemplateParser]] inside a µWeb PageMaker
7 9 Elmer de Looff
* [[TemplateParser#syntax|Template syntax]], an overview of the language's constructs and behaviors
8 1 Elmer de Looff
9 1 Elmer de Looff
First though, to help with understanding the TemplateParser, a minimal size template document:
10 1 Elmer de Looff
11 1 Elmer de Looff
<pre><code class="html">
12 1 Elmer de Looff
Hello [title] [name]
13 1 Elmer de Looff
</code></pre>
14 4 Elmer de Looff
15 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.
16 1 Elmer de Looff
17 5 Elmer de Looff
h1(#template). Template class
18 4 Elmer de Looff
19 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.
20 4 Elmer de Looff
21 4 Elmer de Looff
h2. Creating a template
22 4 Elmer de Looff
23 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]]:
24 4 Elmer de Looff
25 4 Elmer de Looff
<pre><code class="python">
26 4 Elmer de Looff
import templateparser
27 4 Elmer de Looff
>>> template = templateparser.Template('Hello [title] [name]')
28 4 Elmer de Looff
>>> template
29 4 Elmer de Looff
Template([TemplateText('Hello '), TemplateTag('[title]'), TemplateText(' '), TemplateTag('[name]')])
30 4 Elmer de Looff
</code></pre>
31 4 Elmer de Looff
32 4 Elmer de Looff
Above can be seen the various parts of the template, which will be combined to output once parsed.
33 4 Elmer de Looff
34 4 Elmer de Looff
h2. Loading a template from file
35 4 Elmer de Looff
36 4 Elmer de Looff
The @Template@ class provides a @classmethod@ called @FromFile@, which loads the template at the path.
37 4 Elmer de Looff
38 4 Elmer de Looff
Loading a template named @example.utp@ from the current working directory:
39 4 Elmer de Looff
40 4 Elmer de Looff
<pre><code class="python">
41 4 Elmer de Looff
import templateparser
42 4 Elmer de Looff
>>> template = templateparser.Template.FromFile('example.utp')
43 4 Elmer de Looff
>>> template
44 4 Elmer de Looff
Template([TemplateText('Hello '), TemplateTag('[title]'), TemplateText(' '), TemplateTag('[name]')])
45 4 Elmer de Looff
</code></pre>
46 4 Elmer de Looff
47 5 Elmer de Looff
h2. Parsing a template
48 4 Elmer de Looff
49 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.
50 4 Elmer de Looff
51 4 Elmer de Looff
<pre><code class="python">
52 4 Elmer de Looff
import templateparser
53 4 Elmer de Looff
>>> template = templateparser.Template('Hello [title] [name]')
54 8 Elmer de Looff
>>> template.Parse(title='sir')
55 8 Elmer de Looff
'Hello sir [name]'
56 4 Elmer de Looff
</code></pre>
57 1 Elmer de Looff
58 1 Elmer de Looff
h1(#parser). Parser class
59 6 Elmer de Looff
60 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.
61 1 Elmer de Looff
62 8 Elmer de Looff
h2. Loading templates
63 8 Elmer de Looff
64 6 Elmer de Looff
Creating a parser object, and loading the 'example.utp' template from the 'templates' directory works like this:
65 6 Elmer de Looff
66 6 Elmer de Looff
<pre><code class="python">
67 6 Elmer de Looff
import templateparser
68 7 Elmer de Looff
>>> # This sets the 'templates' directory as the search path for AddTemplate
69 7 Elmer de Looff
>>> parser = templateparser.Parser('templates')
70 7 Elmer de Looff
>>> # Loads the 'templates/example.utp' and stores it as 'example.utp':
71 7 Elmer de Looff
>>> template = parser.AddTemplate('example.utp')
72 1 Elmer de Looff
>>> template.Parse(title='mister', name='Bob Dobalina')
73 1 Elmer de Looff
'Hello mister Bob Dobalina'
74 6 Elmer de Looff
</code></pre>
75 1 Elmer de Looff
76 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.
77 1 Elmer de Looff
78 8 Elmer de Looff
h2. Template cache and auto-loading
79 8 Elmer de Looff
80 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:
81 8 Elmer de Looff
82 1 Elmer de Looff
<pre><code class="python">
83 7 Elmer de Looff
import templateparser
84 7 Elmer de Looff
>>> parser = templateparser.Parser('templates')
85 7 Elmer de Looff
>>> parser
86 7 Elmer de Looff
Parser({})  # The parser is empty (has no cached templates)
87 1 Elmer de Looff
>>> # Automatically loads the named template from the 'templates' directory:
88 1 Elmer de Looff
>>> parser['example.utp'].Parse(title='mister', name='Bob Dobalina')
89 7 Elmer de Looff
'Hello mister Bob Dobalina'
90 1 Elmer de Looff
>>> parser
91 8 Elmer de Looff
Parser({'example.utp': Template([TemplateText('Hello '), TemplateTag('[title]'),
92 8 Elmer de Looff
                                 TemplateText(' '), TemplateTag('[name]')])})
93 7 Elmer de Looff
</code></pre>
94 7 Elmer de Looff
95 7 Elmer de Looff
If these cannot be found, @TemplateReadError@ is raised:
96 7 Elmer de Looff
97 7 Elmer de Looff
<pre><code class="python">
98 7 Elmer de Looff
import templateparser
99 7 Elmer de Looff
>>> parser = templateparser.Parser('templates')
100 6 Elmer de Looff
>>> parser['bad_template.utp'].Parse(failure='imminent')
101 6 Elmer de Looff
Traceback (most recent call last):
102 6 Elmer de Looff
  File "<stdin>", line 1, in <module>
103 7 Elmer de Looff
  File "/var/lib/underdark/libs/uweb/templateparser.py", line 147, in __getitem__
104 6 Elmer de Looff
    self.AddTemplate(template)
105 1 Elmer de Looff
  File "/var/lib/underdark/libs/uweb/templateparser.py", line 171, in AddTemplate
106 1 Elmer de Looff
    raise TemplateReadError('Could not load template %r' % template_path)
107 1 Elmer de Looff
underdark.libs.uweb.templateparser.TemplateReadError: Could not load template 'templates/bad_template.utp'
108 1 Elmer de Looff
</code></pre>
109 8 Elmer de Looff
110 8 Elmer de Looff
h2. @Parse@ and @ParseString@
111 8 Elmer de Looff
112 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:
113 8 Elmer de Looff
114 8 Elmer de Looff
<pre><code class="python">
115 8 Elmer de Looff
import templateparser
116 8 Elmer de Looff
>>> parser = templateparser.Parser('templates')
117 8 Elmer de Looff
>>> parser.Parse('example.utp', title='mister', name='Bob Dobalina')
118 8 Elmer de Looff
'Hello mister Bob Dobalina'
119 8 Elmer de Looff
>>> parser.ParseString('Hello [title] [name]', title='mister', name='Bob Dobalina')
120 8 Elmer de Looff
'Hello mister Bob Dobalina'</code></pre>
121 6 Elmer de Looff
122 1 Elmer de Looff
h1(#using). Using TemplateParser inside µWeb
123 10 Elmer de Looff
124 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.
125 10 Elmer de Looff
126 10 Elmer de Looff
An example of TemplateParser to create a complete response:
127 10 Elmer de Looff
<pre><code class="python">
128 10 Elmer de Looff
from underdark.libs import uweb
129 10 Elmer de Looff
import time
130 10 Elmer de Looff
131 10 Elmer de Looff
class PageMaker(uweb.PageMaker):
132 10 Elmer de Looff
  def VersionPage(self):
133 10 Elmer de Looff
    return self.parser.Parse(
134 10 Elmer de Looff
      'version.utp', year=time.strftime('%Y'), version=uweb.__version__)
135 10 Elmer de Looff
</code></pre>
136 10 Elmer de Looff
137 10 Elmer de Looff
The example template for the above file could look something like this:
138 10 Elmer de Looff
139 10 Elmer de Looff
140 10 Elmer de Looff
<pre><code class="html">
141 10 Elmer de Looff
<!DOCTYPE html>
142 10 Elmer de Looff
<html>
143 10 Elmer de Looff
  <head>
144 10 Elmer de Looff
    <title>µWeb version info</title>
145 10 Elmer de Looff
  </head>
146 10 Elmer de Looff
  <body>
147 10 Elmer de Looff
    <p>µWeb version [version] - Copyright 2010-[year] Underdark</p>
148 10 Elmer de Looff
  </body>
149 10 Elmer de Looff
</html>
150 10 Elmer de Looff
</code></pre>
151 1 Elmer de Looff
152 5 Elmer de Looff
h1(#syntax). Templating language syntax
153 11 Elmer de Looff
154 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:
155 11 Elmer de Looff
* Simple tags (used in various examples above)
156 11 Elmer de Looff
* Tag indexing
157 11 Elmer de Looff
* Tag functions
158 11 Elmer de Looff
* Template language constructs
159 11 Elmer de Looff
160 11 Elmer de Looff
All examples will consist of three parts:
161 11 Elmer de Looff
# The example template
162 11 Elmer de Looff
# The python invocation string (the template will be named 'example.utp')
163 11 Elmer de Looff
# The resulting output (as source, not as parsed HTML)
164 11 Elmer de Looff
165 11 Elmer de Looff
h2. Simple tags
166 11 Elmer de Looff
167 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
168 11 Elmer de Looff
169 11 Elmer de Looff
The example below is a repeat of the example how to use TemplateParser inside µWeb, and shows the template result:
170 11 Elmer de Looff
171 11 Elmer de Looff
<pre><code class="html">
172 11 Elmer de Looff
<!DOCTYPE html>
173 11 Elmer de Looff
<html>
174 11 Elmer de Looff
  <head>
175 11 Elmer de Looff
    <title>µWeb version info</title>
176 11 Elmer de Looff
  </head>
177 11 Elmer de Looff
  <body>
178 11 Elmer de Looff
    <p>µWeb version [version] - Copyright 2010-[year] Underdark</p>
179 11 Elmer de Looff
    <p>
180 11 Elmer de Looff
      This [paragraph] is not replaced because there is no
181 11 Elmer de Looff
      paragraph argument provided to the parser.
182 11 Elmer de Looff
    </p>
183 11 Elmer de Looff
  </body>
184 11 Elmer de Looff
</html>
185 11 Elmer de Looff
</code></pre>
186 11 Elmer de Looff
187 11 Elmer de Looff
<pre><code class="python">
188 11 Elmer de Looff
>>> parser.Parse('version.utp', year=time.strftime('%Y'), version=uweb.__version__)
189 11 Elmer de Looff
</code></pre>
190 11 Elmer de Looff
191 11 Elmer de Looff
<pre><code class="html">
192 11 Elmer de Looff
<!DOCTYPE html>
193 11 Elmer de Looff
<html>
194 11 Elmer de Looff
  <head>
195 11 Elmer de Looff
    <title>µWeb version info</title>
196 11 Elmer de Looff
  </head>
197 11 Elmer de Looff
  <body>
198 11 Elmer de Looff
    <p>µWeb version 0.11 - Copyright 2010-212 Underdark</p>
199 11 Elmer de Looff
    <p>
200 11 Elmer de Looff
      This [paragraph] is not replaced because there is no
201 11 Elmer de Looff
      paragraph argument provided to the parser.
202 11 Elmer de Looff
    </p>
203 11 Elmer de Looff
  </body>
204 11 Elmer de Looff
</html>
205 11 Elmer de Looff
</code></pre>
206 11 Elmer de Looff
207 11 Elmer de Looff
h2. Tag indexing
208 11 Elmer de Looff
209 11 Elmer de Looff
h2. Tag functions
210 11 Elmer de Looff
211 11 Elmer de Looff
h3. Default html escaping 
212 11 Elmer de Looff
213 11 Elmer de Looff
h3. Adding custom functions
214 11 Elmer de Looff
215 11 Elmer de Looff
h2. For loops
216 11 Elmer de Looff
217 11 Elmer de Looff
h2. Inlining templates
218 11 Elmer de Looff
219 11 Elmer de Looff
h2. Conditional statements
220 11 Elmer de Looff
221 11 Elmer de Looff
h2. Template unicode handling
222 11 Elmer de Looff
223 11 Elmer de Looff
Any @unicode@ object found while parsing, will automatically be encoded to UTF-8:
224 11 Elmer de Looff
225 11 Elmer de Looff
<pre><code class="python">
226 11 Elmer de Looff
>>> template = 'Underdark [love] [app]'
227 11 Elmer de Looff
>>> output = parser.ParseString(template, love=u'\u2665', app=u'\N{micro sign}Web')
228 11 Elmer de Looff
>>> output
229 12 Elmer de Looff
'Underdark \xe2\x99\xa5 \xc2\xb5Web'  # The output in its raw UTF-8 representation
230 11 Elmer de Looff
>>> output.decode('UTF8')
231 12 Elmer de Looff
u'Underdark \u2665 \xb5Web'           # The output converted to a Unicode object
232 1 Elmer de Looff
</code></pre>
233 13 Elmer de Looff
234 13 Elmer de Looff
Printint the output in a UTF-8 aware environment yields the proper @Underdark ♥ µWeb@.