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@. |