Mercurial > genshi > genshi-test
annotate markup/template.py @ 157:40fc3d36f5b4
Fix for backwards compatibility proposed by cboos in #28.
author | cmlenz |
---|---|
date | Wed, 16 Aug 2006 18:31:56 +0000 |
parents | 1c404be518d1 |
children | a25f9fc5787d |
rev | line source |
---|---|
1 | 1 # -*- coding: utf-8 -*- |
2 # | |
66
822089ae65ce
Switch copyright to Edgewall and URLs to markup.edgewall.org.
cmlenz
parents:
65
diff
changeset
|
3 # Copyright (C) 2006 Edgewall Software |
1 | 4 # All rights reserved. |
5 # | |
6 # This software is licensed as described in the file COPYING, which | |
7 # you should have received as part of this distribution. The terms | |
66
822089ae65ce
Switch copyright to Edgewall and URLs to markup.edgewall.org.
cmlenz
parents:
65
diff
changeset
|
8 # are also available at http://markup.edgewall.org/wiki/License. |
1 | 9 # |
10 # This software consists of voluntary contributions made by many | |
11 # individuals. For the exact contribution history, see the revision | |
66
822089ae65ce
Switch copyright to Edgewall and URLs to markup.edgewall.org.
cmlenz
parents:
65
diff
changeset
|
12 # history and logs, available at http://markup.edgewall.org/log/. |
1 | 13 |
82 | 14 """Implementation of the template engine.""" |
1 | 15 |
70
0498da8e5de7
Use `collections.deque` for the template context stack on Python 2.4, which improves performance if there are many context frame pop/push operations.
cmlenz
parents:
69
diff
changeset
|
16 try: |
0498da8e5de7
Use `collections.deque` for the template context stack on Python 2.4, which improves performance if there are many context frame pop/push operations.
cmlenz
parents:
69
diff
changeset
|
17 from collections import deque |
0498da8e5de7
Use `collections.deque` for the template context stack on Python 2.4, which improves performance if there are many context frame pop/push operations.
cmlenz
parents:
69
diff
changeset
|
18 except ImportError: |
0498da8e5de7
Use `collections.deque` for the template context stack on Python 2.4, which improves performance if there are many context frame pop/push operations.
cmlenz
parents:
69
diff
changeset
|
19 class deque(list): |
0498da8e5de7
Use `collections.deque` for the template context stack on Python 2.4, which improves performance if there are many context frame pop/push operations.
cmlenz
parents:
69
diff
changeset
|
20 def appendleft(self, x): self.insert(0, x) |
0498da8e5de7
Use `collections.deque` for the template context stack on Python 2.4, which improves performance if there are many context frame pop/push operations.
cmlenz
parents:
69
diff
changeset
|
21 def popleft(self): return self.pop(0) |
1 | 22 import compiler |
23 import os | |
24 import re | |
25 from StringIO import StringIO | |
26 | |
145
56d534eb53f9
* Fix error in expression evaluation when the expression evaluates to an iterable that does not produce event tuples.
cmlenz
parents:
140
diff
changeset
|
27 from markup.core import Attributes, Namespace, Stream, StreamEventKind, _ensure |
56d534eb53f9
* Fix error in expression evaluation when the expression evaluates to an iterable that does not produce event tuples.
cmlenz
parents:
140
diff
changeset
|
28 from markup.core import START, END, START_NS, END_NS, TEXT, COMMENT |
1 | 29 from markup.eval import Expression |
69 | 30 from markup.input import XMLParser |
14
76b5d4b189e6
The `<py:match>` directive now protects itself against simple infinite recursion (see MatchDirective), while still allowing recursion in general.
cmlenz
parents:
13
diff
changeset
|
31 from markup.path import Path |
1 | 32 |
150
39a8012f60e3
Removed to many classes from the `__all__` list of `markup.template` in [191].
cmlenz
parents:
149
diff
changeset
|
33 __all__ = ['BadDirectiveError', 'TemplateError', 'TemplateSyntaxError', |
39a8012f60e3
Removed to many classes from the `__all__` list of `markup.template` in [191].
cmlenz
parents:
149
diff
changeset
|
34 'TemplateNotFound', 'Template', 'TemplateLoader'] |
1 | 35 |
36 | |
37 class TemplateError(Exception): | |
38 """Base exception class for errors related to template processing.""" | |
39 | |
40 | |
41 class TemplateSyntaxError(TemplateError): | |
42 """Exception raised when an expression in a template causes a Python syntax | |
43 error.""" | |
44 | |
45 def __init__(self, message, filename='<string>', lineno=-1, offset=-1): | |
46 if isinstance(message, SyntaxError) and message.lineno is not None: | |
47 message = str(message).replace(' (line %d)' % message.lineno, '') | |
80 | 48 message = '%s (%s, line %d)' % (message, filename, lineno) |
1 | 49 TemplateError.__init__(self, message) |
50 self.filename = filename | |
51 self.lineno = lineno | |
52 self.offset = offset | |
53 | |
54 | |
55 class BadDirectiveError(TemplateSyntaxError): | |
56 """Exception raised when an unknown directive is encountered when parsing | |
57 a template. | |
58 | |
59 An unknown directive is any attribute using the namespace for directives, | |
60 with a local name that doesn't match any registered directive. | |
61 """ | |
62 | |
63 def __init__(self, name, filename='<string>', lineno=-1): | |
80 | 64 msg = 'bad directive "%s" (%s, line %d)' % (name.localname, filename, |
65 lineno) | |
66 TemplateSyntaxError.__init__(self, msg, filename, lineno) | |
1 | 67 |
68 | |
69 class TemplateNotFound(TemplateError): | |
70 """Exception raised when a specific template file could not be found.""" | |
71 | |
72 def __init__(self, name, search_path): | |
73 TemplateError.__init__(self, 'Template "%s" not found' % name) | |
74 self.search_path = search_path | |
75 | |
76 | |
77 class Context(object): | |
95
7d6426183a90
Improve performance of push/pop operations on the context.
cmlenz
parents:
93
diff
changeset
|
78 """Container for template input data. |
1 | 79 |
95
7d6426183a90
Improve performance of push/pop operations on the context.
cmlenz
parents:
93
diff
changeset
|
80 A context provides a stack of scopes (represented by dictionaries). |
7d6426183a90
Improve performance of push/pop operations on the context.
cmlenz
parents:
93
diff
changeset
|
81 |
7d6426183a90
Improve performance of push/pop operations on the context.
cmlenz
parents:
93
diff
changeset
|
82 Template directives such as loops can push a new scope on the stack with |
7d6426183a90
Improve performance of push/pop operations on the context.
cmlenz
parents:
93
diff
changeset
|
83 data that should only be available inside the loop. When the loop |
7d6426183a90
Improve performance of push/pop operations on the context.
cmlenz
parents:
93
diff
changeset
|
84 terminates, that scope can get popped off the stack again. |
1 | 85 |
86 >>> ctxt = Context(one='foo', other=1) | |
87 >>> ctxt.get('one') | |
88 'foo' | |
89 >>> ctxt.get('other') | |
90 1 | |
95
7d6426183a90
Improve performance of push/pop operations on the context.
cmlenz
parents:
93
diff
changeset
|
91 >>> ctxt.push(dict(one='frost')) |
1 | 92 >>> ctxt.get('one') |
93 'frost' | |
94 >>> ctxt.get('other') | |
95 1 | |
96 >>> ctxt.pop() | |
95
7d6426183a90
Improve performance of push/pop operations on the context.
cmlenz
parents:
93
diff
changeset
|
97 {'one': 'frost'} |
1 | 98 >>> ctxt.get('one') |
99 'foo' | |
100 """ | |
101 | |
102 def __init__(self, **data): | |
70
0498da8e5de7
Use `collections.deque` for the template context stack on Python 2.4, which improves performance if there are many context frame pop/push operations.
cmlenz
parents:
69
diff
changeset
|
103 self.frames = deque([data]) |
95
7d6426183a90
Improve performance of push/pop operations on the context.
cmlenz
parents:
93
diff
changeset
|
104 self.pop = self.frames.popleft |
7d6426183a90
Improve performance of push/pop operations on the context.
cmlenz
parents:
93
diff
changeset
|
105 self.push = self.frames.appendleft |
157
40fc3d36f5b4
Fix for backwards compatibility proposed by cboos in #28.
cmlenz
parents:
154
diff
changeset
|
106 self._match_templates = [] |
1 | 107 |
108 def __repr__(self): | |
70
0498da8e5de7
Use `collections.deque` for the template context stack on Python 2.4, which improves performance if there are many context frame pop/push operations.
cmlenz
parents:
69
diff
changeset
|
109 return repr(self.frames) |
1 | 110 |
111 def __setitem__(self, key, value): | |
95
7d6426183a90
Improve performance of push/pop operations on the context.
cmlenz
parents:
93
diff
changeset
|
112 """Set a variable in the current scope.""" |
70
0498da8e5de7
Use `collections.deque` for the template context stack on Python 2.4, which improves performance if there are many context frame pop/push operations.
cmlenz
parents:
69
diff
changeset
|
113 self.frames[0][key] = value |
1 | 114 |
115 def get(self, key): | |
95
7d6426183a90
Improve performance of push/pop operations on the context.
cmlenz
parents:
93
diff
changeset
|
116 """Get a variable's value, starting at the current scope and going |
7d6426183a90
Improve performance of push/pop operations on the context.
cmlenz
parents:
93
diff
changeset
|
117 upward. |
29
4b6cee37ce62
* Minor simplification of template directives: they no longer get passed the template instance and the position, as no directive was actually using
cmlenz
parents:
27
diff
changeset
|
118 """ |
70
0498da8e5de7
Use `collections.deque` for the template context stack on Python 2.4, which improves performance if there are many context frame pop/push operations.
cmlenz
parents:
69
diff
changeset
|
119 for frame in self.frames: |
1 | 120 if key in frame: |
121 return frame[key] | |
70
0498da8e5de7
Use `collections.deque` for the template context stack on Python 2.4, which improves performance if there are many context frame pop/push operations.
cmlenz
parents:
69
diff
changeset
|
122 __getitem__ = get |
1 | 123 |
95
7d6426183a90
Improve performance of push/pop operations on the context.
cmlenz
parents:
93
diff
changeset
|
124 def push(self, data): |
7d6426183a90
Improve performance of push/pop operations on the context.
cmlenz
parents:
93
diff
changeset
|
125 """Push a new scope on the stack.""" |
1 | 126 |
127 def pop(self): | |
95
7d6426183a90
Improve performance of push/pop operations on the context.
cmlenz
parents:
93
diff
changeset
|
128 """Pop the top-most scope from the stack.""" |
1 | 129 |
130 | |
131 class Directive(object): | |
132 """Abstract base class for template directives. | |
133 | |
54 | 134 A directive is basically a callable that takes three positional arguments: |
135 `ctxt` is the template data context, `stream` is an iterable over the | |
136 events that the directive applies to, and `directives` is is a list of | |
137 other directives on the same stream that need to be applied. | |
1 | 138 |
139 Directives can be "anonymous" or "registered". Registered directives can be | |
140 applied by the template author using an XML attribute with the | |
141 corresponding name in the template. Such directives should be subclasses of | |
31 | 142 this base class that can be instantiated with the value of the directive |
143 attribute as parameter. | |
1 | 144 |
145 Anonymous directives are simply functions conforming to the protocol | |
146 described above, and can only be applied programmatically (for example by | |
147 template filters). | |
148 """ | |
149 __slots__ = ['expr'] | |
150 | |
81
cc034182061e
Template expressions are now compiled to Python bytecode.
cmlenz
parents:
80
diff
changeset
|
151 def __init__(self, value, filename=None, lineno=-1, offset=-1): |
cc034182061e
Template expressions are now compiled to Python bytecode.
cmlenz
parents:
80
diff
changeset
|
152 try: |
cc034182061e
Template expressions are now compiled to Python bytecode.
cmlenz
parents:
80
diff
changeset
|
153 self.expr = value and Expression(value, filename, lineno) or None |
cc034182061e
Template expressions are now compiled to Python bytecode.
cmlenz
parents:
80
diff
changeset
|
154 except SyntaxError, err: |
cc034182061e
Template expressions are now compiled to Python bytecode.
cmlenz
parents:
80
diff
changeset
|
155 raise TemplateSyntaxError(err, filename, lineno, |
cc034182061e
Template expressions are now compiled to Python bytecode.
cmlenz
parents:
80
diff
changeset
|
156 offset + (err.offset or 0)) |
1 | 157 |
53
60f1a556690e
* Add helper function to let directives apply any remaining directives, and use that helper consistently in every directive.
cmlenz
parents:
51
diff
changeset
|
158 def __call__(self, stream, ctxt, directives): |
1 | 159 raise NotImplementedError |
160 | |
161 def __repr__(self): | |
162 expr = '' | |
163 if self.expr is not None: | |
164 expr = ' "%s"' % self.expr.source | |
165 return '<%s%s>' % (self.__class__.__name__, expr) | |
166 | |
78
fa4bafcbe4c7
Minor improvements to how directives are applied in template processing.
cmlenz
parents:
77
diff
changeset
|
167 |
fa4bafcbe4c7
Minor improvements to how directives are applied in template processing.
cmlenz
parents:
77
diff
changeset
|
168 def _apply_directives(stream, ctxt, directives): |
fa4bafcbe4c7
Minor improvements to how directives are applied in template processing.
cmlenz
parents:
77
diff
changeset
|
169 if directives: |
fa4bafcbe4c7
Minor improvements to how directives are applied in template processing.
cmlenz
parents:
77
diff
changeset
|
170 stream = directives[0](iter(stream), ctxt, directives[1:]) |
fa4bafcbe4c7
Minor improvements to how directives are applied in template processing.
cmlenz
parents:
77
diff
changeset
|
171 return stream |
53
60f1a556690e
* Add helper function to let directives apply any remaining directives, and use that helper consistently in every directive.
cmlenz
parents:
51
diff
changeset
|
172 |
1 | 173 |
174 class AttrsDirective(Directive): | |
175 """Implementation of the `py:attrs` template directive. | |
176 | |
177 The value of the `py:attrs` attribute should be a dictionary. The keys and | |
178 values of that dictionary will be added as attributes to the element: | |
179 | |
61 | 180 >>> tmpl = Template('''<ul xmlns:py="http://markup.edgewall.org/"> |
1 | 181 ... <li py:attrs="foo">Bar</li> |
182 ... </ul>''') | |
149
7306bf730ff3
`Template.generate()` now accepts the context data as keyword arguments, so that you don't have to import the `Context` class every time you want to pass data into a template.
cmlenz
parents:
145
diff
changeset
|
183 >>> print tmpl.generate(foo={'class': 'collapse'}) |
1 | 184 <ul> |
185 <li class="collapse">Bar</li> | |
186 </ul> | |
187 | |
188 If the value evaluates to `None` (or any other non-truth value), no | |
189 attributes are added: | |
190 | |
149
7306bf730ff3
`Template.generate()` now accepts the context data as keyword arguments, so that you don't have to import the `Context` class every time you want to pass data into a template.
cmlenz
parents:
145
diff
changeset
|
191 >>> print tmpl.generate(foo=None) |
1 | 192 <ul> |
193 <li>Bar</li> | |
194 </ul> | |
195 """ | |
50
a053ffb834cb
Fix the way multiple directives are applied to a single `SUB` in many cases by making the directives themselves responsible for applying any remaining directives.
cmlenz
parents:
48
diff
changeset
|
196 __slots__ = [] |
a053ffb834cb
Fix the way multiple directives are applied to a single `SUB` in many cases by making the directives themselves responsible for applying any remaining directives.
cmlenz
parents:
48
diff
changeset
|
197 |
53
60f1a556690e
* Add helper function to let directives apply any remaining directives, and use that helper consistently in every directive.
cmlenz
parents:
51
diff
changeset
|
198 def __call__(self, stream, ctxt, directives): |
60f1a556690e
* Add helper function to let directives apply any remaining directives, and use that helper consistently in every directive.
cmlenz
parents:
51
diff
changeset
|
199 def _generate(): |
60f1a556690e
* Add helper function to let directives apply any remaining directives, and use that helper consistently in every directive.
cmlenz
parents:
51
diff
changeset
|
200 kind, (tag, attrib), pos = stream.next() |
60f1a556690e
* Add helper function to let directives apply any remaining directives, and use that helper consistently in every directive.
cmlenz
parents:
51
diff
changeset
|
201 attrs = self.expr.evaluate(ctxt) |
60f1a556690e
* Add helper function to let directives apply any remaining directives, and use that helper consistently in every directive.
cmlenz
parents:
51
diff
changeset
|
202 if attrs: |
60f1a556690e
* Add helper function to let directives apply any remaining directives, and use that helper consistently in every directive.
cmlenz
parents:
51
diff
changeset
|
203 attrib = Attributes(attrib[:]) |
77
f1aa49c759b2
* Simplify implementation of the individual XPath tests (use closures instead of callable classes)
cmlenz
parents:
75
diff
changeset
|
204 if isinstance(attrs, Stream): |
f1aa49c759b2
* Simplify implementation of the individual XPath tests (use closures instead of callable classes)
cmlenz
parents:
75
diff
changeset
|
205 try: |
f1aa49c759b2
* Simplify implementation of the individual XPath tests (use closures instead of callable classes)
cmlenz
parents:
75
diff
changeset
|
206 attrs = iter(attrs).next() |
f1aa49c759b2
* Simplify implementation of the individual XPath tests (use closures instead of callable classes)
cmlenz
parents:
75
diff
changeset
|
207 except StopIteration: |
f1aa49c759b2
* Simplify implementation of the individual XPath tests (use closures instead of callable classes)
cmlenz
parents:
75
diff
changeset
|
208 attrs = [] |
f1aa49c759b2
* Simplify implementation of the individual XPath tests (use closures instead of callable classes)
cmlenz
parents:
75
diff
changeset
|
209 elif not isinstance(attrs, list): # assume it's a dict |
53
60f1a556690e
* Add helper function to let directives apply any remaining directives, and use that helper consistently in every directive.
cmlenz
parents:
51
diff
changeset
|
210 attrs = attrs.items() |
60f1a556690e
* Add helper function to let directives apply any remaining directives, and use that helper consistently in every directive.
cmlenz
parents:
51
diff
changeset
|
211 for name, value in attrs: |
60f1a556690e
* Add helper function to let directives apply any remaining directives, and use that helper consistently in every directive.
cmlenz
parents:
51
diff
changeset
|
212 if value is None: |
60f1a556690e
* Add helper function to let directives apply any remaining directives, and use that helper consistently in every directive.
cmlenz
parents:
51
diff
changeset
|
213 attrib.remove(name) |
60f1a556690e
* Add helper function to let directives apply any remaining directives, and use that helper consistently in every directive.
cmlenz
parents:
51
diff
changeset
|
214 else: |
60f1a556690e
* Add helper function to let directives apply any remaining directives, and use that helper consistently in every directive.
cmlenz
parents:
51
diff
changeset
|
215 attrib.set(name, unicode(value).strip()) |
60f1a556690e
* Add helper function to let directives apply any remaining directives, and use that helper consistently in every directive.
cmlenz
parents:
51
diff
changeset
|
216 yield kind, (tag, attrib), pos |
60f1a556690e
* Add helper function to let directives apply any remaining directives, and use that helper consistently in every directive.
cmlenz
parents:
51
diff
changeset
|
217 for event in stream: |
60f1a556690e
* Add helper function to let directives apply any remaining directives, and use that helper consistently in every directive.
cmlenz
parents:
51
diff
changeset
|
218 yield event |
77
f1aa49c759b2
* Simplify implementation of the individual XPath tests (use closures instead of callable classes)
cmlenz
parents:
75
diff
changeset
|
219 |
78
fa4bafcbe4c7
Minor improvements to how directives are applied in template processing.
cmlenz
parents:
77
diff
changeset
|
220 return _apply_directives(_generate(), ctxt, directives) |
1 | 221 |
222 | |
223 class ContentDirective(Directive): | |
224 """Implementation of the `py:content` template directive. | |
225 | |
226 This directive replaces the content of the element with the result of | |
227 evaluating the value of the `py:content` attribute: | |
228 | |
61 | 229 >>> tmpl = Template('''<ul xmlns:py="http://markup.edgewall.org/"> |
1 | 230 ... <li py:content="bar">Hello</li> |
231 ... </ul>''') | |
149
7306bf730ff3
`Template.generate()` now accepts the context data as keyword arguments, so that you don't have to import the `Context` class every time you want to pass data into a template.
cmlenz
parents:
145
diff
changeset
|
232 >>> print tmpl.generate(bar='Bye') |
1 | 233 <ul> |
234 <li>Bye</li> | |
235 </ul> | |
236 """ | |
50
a053ffb834cb
Fix the way multiple directives are applied to a single `SUB` in many cases by making the directives themselves responsible for applying any remaining directives.
cmlenz
parents:
48
diff
changeset
|
237 __slots__ = [] |
a053ffb834cb
Fix the way multiple directives are applied to a single `SUB` in many cases by making the directives themselves responsible for applying any remaining directives.
cmlenz
parents:
48
diff
changeset
|
238 |
a053ffb834cb
Fix the way multiple directives are applied to a single `SUB` in many cases by making the directives themselves responsible for applying any remaining directives.
cmlenz
parents:
48
diff
changeset
|
239 def __call__(self, stream, ctxt, directives): |
53
60f1a556690e
* Add helper function to let directives apply any remaining directives, and use that helper consistently in every directive.
cmlenz
parents:
51
diff
changeset
|
240 def _generate(): |
50
a053ffb834cb
Fix the way multiple directives are applied to a single `SUB` in many cases by making the directives themselves responsible for applying any remaining directives.
cmlenz
parents:
48
diff
changeset
|
241 kind, data, pos = stream.next() |
101 | 242 if kind is START: |
50
a053ffb834cb
Fix the way multiple directives are applied to a single `SUB` in many cases by making the directives themselves responsible for applying any remaining directives.
cmlenz
parents:
48
diff
changeset
|
243 yield kind, data, pos # emit start tag |
69 | 244 yield EXPR, self.expr, pos |
50
a053ffb834cb
Fix the way multiple directives are applied to a single `SUB` in many cases by making the directives themselves responsible for applying any remaining directives.
cmlenz
parents:
48
diff
changeset
|
245 previous = stream.next() |
a053ffb834cb
Fix the way multiple directives are applied to a single `SUB` in many cases by making the directives themselves responsible for applying any remaining directives.
cmlenz
parents:
48
diff
changeset
|
246 for event in stream: |
a053ffb834cb
Fix the way multiple directives are applied to a single `SUB` in many cases by making the directives themselves responsible for applying any remaining directives.
cmlenz
parents:
48
diff
changeset
|
247 previous = event |
a053ffb834cb
Fix the way multiple directives are applied to a single `SUB` in many cases by making the directives themselves responsible for applying any remaining directives.
cmlenz
parents:
48
diff
changeset
|
248 if previous is not None: |
a053ffb834cb
Fix the way multiple directives are applied to a single `SUB` in many cases by making the directives themselves responsible for applying any remaining directives.
cmlenz
parents:
48
diff
changeset
|
249 yield previous |
87
c6f07b7cd3ea
Fix some problems in expression evaluation by transforming the AST and compiling that to bytecode, instead of generating bytecode directly. Invalidates #13.
cmlenz
parents:
82
diff
changeset
|
250 |
78
fa4bafcbe4c7
Minor improvements to how directives are applied in template processing.
cmlenz
parents:
77
diff
changeset
|
251 return _apply_directives(_generate(), ctxt, directives) |
1 | 252 |
253 | |
254 class DefDirective(Directive): | |
255 """Implementation of the `py:def` template directive. | |
256 | |
257 This directive can be used to create "Named Template Functions", which | |
258 are template snippets that are not actually output during normal | |
259 processing, but rather can be expanded from expressions in other places | |
260 in the template. | |
261 | |
262 A named template function can be used just like a normal Python function | |
263 from template expressions: | |
264 | |
61 | 265 >>> tmpl = Template('''<div xmlns:py="http://markup.edgewall.org/"> |
1 | 266 ... <p py:def="echo(greeting, name='world')" class="message"> |
267 ... ${greeting}, ${name}! | |
268 ... </p> | |
90
242610137d1f
When an expression evaluates to a callable, it is called implicitly.
cmlenz
parents:
89
diff
changeset
|
269 ... ${echo('Hi', name='you')} |
1 | 270 ... </div>''') |
149
7306bf730ff3
`Template.generate()` now accepts the context data as keyword arguments, so that you don't have to import the `Context` class every time you want to pass data into a template.
cmlenz
parents:
145
diff
changeset
|
271 >>> print tmpl.generate(bar='Bye') |
1 | 272 <div> |
273 <p class="message"> | |
90
242610137d1f
When an expression evaluates to a callable, it is called implicitly.
cmlenz
parents:
89
diff
changeset
|
274 Hi, you! |
1 | 275 </p> |
276 </div> | |
277 | |
90
242610137d1f
When an expression evaluates to a callable, it is called implicitly.
cmlenz
parents:
89
diff
changeset
|
278 If a function does not require parameters, the parenthesis can be omitted |
242610137d1f
When an expression evaluates to a callable, it is called implicitly.
cmlenz
parents:
89
diff
changeset
|
279 both when defining and when calling it: |
242610137d1f
When an expression evaluates to a callable, it is called implicitly.
cmlenz
parents:
89
diff
changeset
|
280 |
61 | 281 >>> tmpl = Template('''<div xmlns:py="http://markup.edgewall.org/"> |
90
242610137d1f
When an expression evaluates to a callable, it is called implicitly.
cmlenz
parents:
89
diff
changeset
|
282 ... <p py:def="helloworld" class="message"> |
242610137d1f
When an expression evaluates to a callable, it is called implicitly.
cmlenz
parents:
89
diff
changeset
|
283 ... Hello, world! |
1 | 284 ... </p> |
90
242610137d1f
When an expression evaluates to a callable, it is called implicitly.
cmlenz
parents:
89
diff
changeset
|
285 ... ${helloworld} |
1 | 286 ... </div>''') |
149
7306bf730ff3
`Template.generate()` now accepts the context data as keyword arguments, so that you don't have to import the `Context` class every time you want to pass data into a template.
cmlenz
parents:
145
diff
changeset
|
287 >>> print tmpl.generate(bar='Bye') |
1 | 288 <div> |
289 <p class="message"> | |
90
242610137d1f
When an expression evaluates to a callable, it is called implicitly.
cmlenz
parents:
89
diff
changeset
|
290 Hello, world! |
1 | 291 </p> |
292 </div> | |
293 """ | |
154
1c404be518d1
* Make sure `py:def` macros don't go out of scope if they are defined inside another directive.
cmlenz
parents:
153
diff
changeset
|
294 __slots__ = ['name', 'args', 'defaults'] |
1 | 295 |
65
5c024cf58ecb
Support the use of directives as elements to reduce the need for using `py:strip`.
cmlenz
parents:
61
diff
changeset
|
296 ATTRIBUTE = 'function' |
5c024cf58ecb
Support the use of directives as elements to reduce the need for using `py:strip`.
cmlenz
parents:
61
diff
changeset
|
297 |
81
cc034182061e
Template expressions are now compiled to Python bytecode.
cmlenz
parents:
80
diff
changeset
|
298 def __init__(self, args, filename=None, lineno=-1, offset=-1): |
cc034182061e
Template expressions are now compiled to Python bytecode.
cmlenz
parents:
80
diff
changeset
|
299 Directive.__init__(self, None, filename, lineno, offset) |
1 | 300 ast = compiler.parse(args, 'eval').node |
301 self.args = [] | |
302 self.defaults = {} | |
303 if isinstance(ast, compiler.ast.CallFunc): | |
304 self.name = ast.node.name | |
305 for arg in ast.args: | |
306 if isinstance(arg, compiler.ast.Keyword): | |
307 self.args.append(arg.name) | |
308 self.defaults[arg.name] = arg.expr.value | |
309 else: | |
310 self.args.append(arg.name) | |
311 else: | |
312 self.name = ast.name | |
313 | |
50
a053ffb834cb
Fix the way multiple directives are applied to a single `SUB` in many cases by making the directives themselves responsible for applying any remaining directives.
cmlenz
parents:
48
diff
changeset
|
314 def __call__(self, stream, ctxt, directives): |
154
1c404be518d1
* Make sure `py:def` macros don't go out of scope if they are defined inside another directive.
cmlenz
parents:
153
diff
changeset
|
315 stream = list(stream) |
1 | 316 |
154
1c404be518d1
* Make sure `py:def` macros don't go out of scope if they are defined inside another directive.
cmlenz
parents:
153
diff
changeset
|
317 def function(*args, **kwargs): |
1c404be518d1
* Make sure `py:def` macros don't go out of scope if they are defined inside another directive.
cmlenz
parents:
153
diff
changeset
|
318 scope = {} |
1c404be518d1
* Make sure `py:def` macros don't go out of scope if they are defined inside another directive.
cmlenz
parents:
153
diff
changeset
|
319 args = list(args) # make mutable |
1c404be518d1
* Make sure `py:def` macros don't go out of scope if they are defined inside another directive.
cmlenz
parents:
153
diff
changeset
|
320 for name in self.args: |
1c404be518d1
* Make sure `py:def` macros don't go out of scope if they are defined inside another directive.
cmlenz
parents:
153
diff
changeset
|
321 if args: |
1c404be518d1
* Make sure `py:def` macros don't go out of scope if they are defined inside another directive.
cmlenz
parents:
153
diff
changeset
|
322 scope[name] = args.pop(0) |
1c404be518d1
* Make sure `py:def` macros don't go out of scope if they are defined inside another directive.
cmlenz
parents:
153
diff
changeset
|
323 else: |
1c404be518d1
* Make sure `py:def` macros don't go out of scope if they are defined inside another directive.
cmlenz
parents:
153
diff
changeset
|
324 scope[name] = kwargs.pop(name, self.defaults.get(name)) |
1c404be518d1
* Make sure `py:def` macros don't go out of scope if they are defined inside another directive.
cmlenz
parents:
153
diff
changeset
|
325 ctxt.push(scope) |
1c404be518d1
* Make sure `py:def` macros don't go out of scope if they are defined inside another directive.
cmlenz
parents:
153
diff
changeset
|
326 for event in _apply_directives(stream, ctxt, directives): |
1c404be518d1
* Make sure `py:def` macros don't go out of scope if they are defined inside another directive.
cmlenz
parents:
153
diff
changeset
|
327 yield event |
1c404be518d1
* Make sure `py:def` macros don't go out of scope if they are defined inside another directive.
cmlenz
parents:
153
diff
changeset
|
328 ctxt.pop() |
1c404be518d1
* Make sure `py:def` macros don't go out of scope if they are defined inside another directive.
cmlenz
parents:
153
diff
changeset
|
329 try: |
1c404be518d1
* Make sure `py:def` macros don't go out of scope if they are defined inside another directive.
cmlenz
parents:
153
diff
changeset
|
330 function.__name__ = self.name |
1c404be518d1
* Make sure `py:def` macros don't go out of scope if they are defined inside another directive.
cmlenz
parents:
153
diff
changeset
|
331 except TypeError: |
1c404be518d1
* Make sure `py:def` macros don't go out of scope if they are defined inside another directive.
cmlenz
parents:
153
diff
changeset
|
332 # Function name can't be set in Python 2.3 |
1c404be518d1
* Make sure `py:def` macros don't go out of scope if they are defined inside another directive.
cmlenz
parents:
153
diff
changeset
|
333 pass |
1c404be518d1
* Make sure `py:def` macros don't go out of scope if they are defined inside another directive.
cmlenz
parents:
153
diff
changeset
|
334 |
1c404be518d1
* Make sure `py:def` macros don't go out of scope if they are defined inside another directive.
cmlenz
parents:
153
diff
changeset
|
335 # Store the function reference in the bottom context frame so that it |
1c404be518d1
* Make sure `py:def` macros don't go out of scope if they are defined inside another directive.
cmlenz
parents:
153
diff
changeset
|
336 # doesn't get popped off before processing the template has finished |
1c404be518d1
* Make sure `py:def` macros don't go out of scope if they are defined inside another directive.
cmlenz
parents:
153
diff
changeset
|
337 ctxt.frames[-1][self.name] = function |
1c404be518d1
* Make sure `py:def` macros don't go out of scope if they are defined inside another directive.
cmlenz
parents:
153
diff
changeset
|
338 |
1c404be518d1
* Make sure `py:def` macros don't go out of scope if they are defined inside another directive.
cmlenz
parents:
153
diff
changeset
|
339 return [] |
1 | 340 |
341 | |
342 class ForDirective(Directive): | |
31 | 343 """Implementation of the `py:for` template directive for repeating an |
344 element based on an iterable in the context data. | |
1 | 345 |
61 | 346 >>> tmpl = Template('''<ul xmlns:py="http://markup.edgewall.org/"> |
1 | 347 ... <li py:for="item in items">${item}</li> |
348 ... </ul>''') | |
149
7306bf730ff3
`Template.generate()` now accepts the context data as keyword arguments, so that you don't have to import the `Context` class every time you want to pass data into a template.
cmlenz
parents:
145
diff
changeset
|
349 >>> print tmpl.generate(items=[1, 2, 3]) |
1 | 350 <ul> |
351 <li>1</li><li>2</li><li>3</li> | |
352 </ul> | |
353 """ | |
354 __slots__ = ['targets'] | |
355 | |
65
5c024cf58ecb
Support the use of directives as elements to reduce the need for using `py:strip`.
cmlenz
parents:
61
diff
changeset
|
356 ATTRIBUTE = 'each' |
5c024cf58ecb
Support the use of directives as elements to reduce the need for using `py:strip`.
cmlenz
parents:
61
diff
changeset
|
357 |
81
cc034182061e
Template expressions are now compiled to Python bytecode.
cmlenz
parents:
80
diff
changeset
|
358 def __init__(self, value, filename=None, lineno=-1, offset=-1): |
29
4b6cee37ce62
* Minor simplification of template directives: they no longer get passed the template instance and the position, as no directive was actually using
cmlenz
parents:
27
diff
changeset
|
359 targets, value = value.split(' in ', 1) |
1 | 360 self.targets = [str(name.strip()) for name in targets.split(',')] |
140
a2edde90ad24
Fix bug in HTML serializer, plus some other minor tweaks.
cmlenz
parents:
139
diff
changeset
|
361 Directive.__init__(self, value.strip(), filename, lineno, offset) |
1 | 362 |
50
a053ffb834cb
Fix the way multiple directives are applied to a single `SUB` in many cases by making the directives themselves responsible for applying any remaining directives.
cmlenz
parents:
48
diff
changeset
|
363 def __call__(self, stream, ctxt, directives): |
53
60f1a556690e
* Add helper function to let directives apply any remaining directives, and use that helper consistently in every directive.
cmlenz
parents:
51
diff
changeset
|
364 iterable = self.expr.evaluate(ctxt) |
101 | 365 if iterable is None: |
366 return | |
367 | |
368 scope = {} | |
369 stream = list(stream) | |
370 targets = self.targets | |
140
a2edde90ad24
Fix bug in HTML serializer, plus some other minor tweaks.
cmlenz
parents:
139
diff
changeset
|
371 single = len(targets) == 1 |
101 | 372 for item in iter(iterable): |
140
a2edde90ad24
Fix bug in HTML serializer, plus some other minor tweaks.
cmlenz
parents:
139
diff
changeset
|
373 if single: |
101 | 374 scope[targets[0]] = item |
375 else: | |
376 for idx, name in enumerate(targets): | |
377 scope[name] = item[idx] | |
378 ctxt.push(scope) | |
379 for event in _apply_directives(stream, ctxt, directives): | |
380 yield event | |
381 ctxt.pop() | |
1 | 382 |
383 def __repr__(self): | |
384 return '<%s "%s in %s">' % (self.__class__.__name__, | |
385 ', '.join(self.targets), self.expr.source) | |
386 | |
387 | |
388 class IfDirective(Directive): | |
31 | 389 """Implementation of the `py:if` template directive for conditionally |
390 excluding elements from being output. | |
1 | 391 |
61 | 392 >>> tmpl = Template('''<div xmlns:py="http://markup.edgewall.org/"> |
1 | 393 ... <b py:if="foo">${bar}</b> |
394 ... </div>''') | |
149
7306bf730ff3
`Template.generate()` now accepts the context data as keyword arguments, so that you don't have to import the `Context` class every time you want to pass data into a template.
cmlenz
parents:
145
diff
changeset
|
395 >>> print tmpl.generate(foo=True, bar='Hello') |
1 | 396 <div> |
397 <b>Hello</b> | |
398 </div> | |
399 """ | |
50
a053ffb834cb
Fix the way multiple directives are applied to a single `SUB` in many cases by making the directives themselves responsible for applying any remaining directives.
cmlenz
parents:
48
diff
changeset
|
400 __slots__ = [] |
a053ffb834cb
Fix the way multiple directives are applied to a single `SUB` in many cases by making the directives themselves responsible for applying any remaining directives.
cmlenz
parents:
48
diff
changeset
|
401 |
65
5c024cf58ecb
Support the use of directives as elements to reduce the need for using `py:strip`.
cmlenz
parents:
61
diff
changeset
|
402 ATTRIBUTE = 'test' |
5c024cf58ecb
Support the use of directives as elements to reduce the need for using `py:strip`.
cmlenz
parents:
61
diff
changeset
|
403 |
50
a053ffb834cb
Fix the way multiple directives are applied to a single `SUB` in many cases by making the directives themselves responsible for applying any remaining directives.
cmlenz
parents:
48
diff
changeset
|
404 def __call__(self, stream, ctxt, directives): |
1 | 405 if self.expr.evaluate(ctxt): |
78
fa4bafcbe4c7
Minor improvements to how directives are applied in template processing.
cmlenz
parents:
77
diff
changeset
|
406 return _apply_directives(stream, ctxt, directives) |
1 | 407 return [] |
408 | |
409 | |
410 class MatchDirective(Directive): | |
411 """Implementation of the `py:match` template directive. | |
14
76b5d4b189e6
The `<py:match>` directive now protects itself against simple infinite recursion (see MatchDirective), while still allowing recursion in general.
cmlenz
parents:
13
diff
changeset
|
412 |
61 | 413 >>> tmpl = Template('''<div xmlns:py="http://markup.edgewall.org/"> |
17
ad63ad459524
Refactoring to address #6: all match templates are now processed by a single filter, which means that match templates added by included templates are properly applied. A side effect of this refactoring is that `Context` objects may not be reused across multiple template processing runs.
cmlenz
parents:
14
diff
changeset
|
414 ... <span py:match="greeting"> |
1 | 415 ... Hello ${select('@name')} |
416 ... </span> | |
417 ... <greeting name="Dude" /> | |
418 ... </div>''') | |
14
76b5d4b189e6
The `<py:match>` directive now protects itself against simple infinite recursion (see MatchDirective), while still allowing recursion in general.
cmlenz
parents:
13
diff
changeset
|
419 >>> print tmpl.generate() |
1 | 420 <div> |
421 <span> | |
422 Hello Dude | |
423 </span> | |
424 </div> | |
425 """ | |
426 __slots__ = ['path', 'stream'] | |
427 | |
65
5c024cf58ecb
Support the use of directives as elements to reduce the need for using `py:strip`.
cmlenz
parents:
61
diff
changeset
|
428 ATTRIBUTE = 'path' |
5c024cf58ecb
Support the use of directives as elements to reduce the need for using `py:strip`.
cmlenz
parents:
61
diff
changeset
|
429 |
81
cc034182061e
Template expressions are now compiled to Python bytecode.
cmlenz
parents:
80
diff
changeset
|
430 def __init__(self, value, filename=None, lineno=-1, offset=-1): |
cc034182061e
Template expressions are now compiled to Python bytecode.
cmlenz
parents:
80
diff
changeset
|
431 Directive.__init__(self, None, filename, lineno, offset) |
139
54131cbb91a5
Implement position reporting for XPath syntax errors. Closes #20.
cmlenz
parents:
134
diff
changeset
|
432 self.path = Path(value, filename, lineno) |
1 | 433 self.stream = [] |
434 | |
50
a053ffb834cb
Fix the way multiple directives are applied to a single `SUB` in many cases by making the directives themselves responsible for applying any remaining directives.
cmlenz
parents:
48
diff
changeset
|
435 def __call__(self, stream, ctxt, directives): |
1 | 436 self.stream = list(stream) |
38
fec9f4897415
Fix for #2 (incorrect context node in path expressions). Still some paths that produce incorrect results, but the common case seems to work now.
cmlenz
parents:
37
diff
changeset
|
437 ctxt._match_templates.append((self.path.test(ignore_context=True), |
50
a053ffb834cb
Fix the way multiple directives are applied to a single `SUB` in many cases by making the directives themselves responsible for applying any remaining directives.
cmlenz
parents:
48
diff
changeset
|
438 self.path, self.stream, directives)) |
1 | 439 return [] |
440 | |
441 def __repr__(self): | |
14
76b5d4b189e6
The `<py:match>` directive now protects itself against simple infinite recursion (see MatchDirective), while still allowing recursion in general.
cmlenz
parents:
13
diff
changeset
|
442 return '<%s "%s">' % (self.__class__.__name__, self.path.source) |
1 | 443 |
444 | |
445 class ReplaceDirective(Directive): | |
446 """Implementation of the `py:replace` template directive. | |
447 | |
31 | 448 This directive replaces the element with the result of evaluating the |
449 value of the `py:replace` attribute: | |
450 | |
61 | 451 >>> tmpl = Template('''<div xmlns:py="http://markup.edgewall.org/"> |
1 | 452 ... <span py:replace="bar">Hello</span> |
453 ... </div>''') | |
149
7306bf730ff3
`Template.generate()` now accepts the context data as keyword arguments, so that you don't have to import the `Context` class every time you want to pass data into a template.
cmlenz
parents:
145
diff
changeset
|
454 >>> print tmpl.generate(bar='Bye') |
1 | 455 <div> |
456 Bye | |
457 </div> | |
458 | |
459 This directive is equivalent to `py:content` combined with `py:strip`, | |
460 providing a less verbose way to achieve the same effect: | |
461 | |
61 | 462 >>> tmpl = Template('''<div xmlns:py="http://markup.edgewall.org/"> |
1 | 463 ... <span py:content="bar" py:strip="">Hello</span> |
464 ... </div>''') | |
149
7306bf730ff3
`Template.generate()` now accepts the context data as keyword arguments, so that you don't have to import the `Context` class every time you want to pass data into a template.
cmlenz
parents:
145
diff
changeset
|
465 >>> print tmpl.generate(bar='Bye') |
1 | 466 <div> |
467 Bye | |
468 </div> | |
469 """ | |
50
a053ffb834cb
Fix the way multiple directives are applied to a single `SUB` in many cases by making the directives themselves responsible for applying any remaining directives.
cmlenz
parents:
48
diff
changeset
|
470 __slots__ = [] |
a053ffb834cb
Fix the way multiple directives are applied to a single `SUB` in many cases by making the directives themselves responsible for applying any remaining directives.
cmlenz
parents:
48
diff
changeset
|
471 |
54 | 472 def __call__(self, stream, ctxt, directives): |
1 | 473 kind, data, pos = stream.next() |
69 | 474 yield EXPR, self.expr, pos |
1 | 475 |
476 | |
477 class StripDirective(Directive): | |
478 """Implementation of the `py:strip` template directive. | |
479 | |
480 When the value of the `py:strip` attribute evaluates to `True`, the element | |
481 is stripped from the output | |
482 | |
61 | 483 >>> tmpl = Template('''<div xmlns:py="http://markup.edgewall.org/"> |
1 | 484 ... <div py:strip="True"><b>foo</b></div> |
485 ... </div>''') | |
14
76b5d4b189e6
The `<py:match>` directive now protects itself against simple infinite recursion (see MatchDirective), while still allowing recursion in general.
cmlenz
parents:
13
diff
changeset
|
486 >>> print tmpl.generate() |
1 | 487 <div> |
488 <b>foo</b> | |
489 </div> | |
490 | |
37
224b0b41d1da
Moved some of the tests for the strip directive to a new unittest test case to not clutter up the documentation.
cmlenz
parents:
36
diff
changeset
|
491 Leaving the attribute value empty is equivalent to a truth value. |
1 | 492 |
493 This directive is particulary interesting for named template functions or | |
494 match templates that do not generate a top-level element: | |
495 | |
61 | 496 >>> tmpl = Template('''<div xmlns:py="http://markup.edgewall.org/"> |
1 | 497 ... <div py:def="echo(what)" py:strip=""> |
498 ... <b>${what}</b> | |
499 ... </div> | |
500 ... ${echo('foo')} | |
501 ... </div>''') | |
14
76b5d4b189e6
The `<py:match>` directive now protects itself against simple infinite recursion (see MatchDirective), while still allowing recursion in general.
cmlenz
parents:
13
diff
changeset
|
502 >>> print tmpl.generate() |
1 | 503 <div> |
504 <b>foo</b> | |
505 </div> | |
506 """ | |
50
a053ffb834cb
Fix the way multiple directives are applied to a single `SUB` in many cases by making the directives themselves responsible for applying any remaining directives.
cmlenz
parents:
48
diff
changeset
|
507 __slots__ = [] |
a053ffb834cb
Fix the way multiple directives are applied to a single `SUB` in many cases by making the directives themselves responsible for applying any remaining directives.
cmlenz
parents:
48
diff
changeset
|
508 |
54 | 509 def __call__(self, stream, ctxt, directives): |
78
fa4bafcbe4c7
Minor improvements to how directives are applied in template processing.
cmlenz
parents:
77
diff
changeset
|
510 def _generate(): |
fa4bafcbe4c7
Minor improvements to how directives are applied in template processing.
cmlenz
parents:
77
diff
changeset
|
511 if self.expr: |
fa4bafcbe4c7
Minor improvements to how directives are applied in template processing.
cmlenz
parents:
77
diff
changeset
|
512 strip = self.expr.evaluate(ctxt) |
fa4bafcbe4c7
Minor improvements to how directives are applied in template processing.
cmlenz
parents:
77
diff
changeset
|
513 else: |
fa4bafcbe4c7
Minor improvements to how directives are applied in template processing.
cmlenz
parents:
77
diff
changeset
|
514 strip = True |
fa4bafcbe4c7
Minor improvements to how directives are applied in template processing.
cmlenz
parents:
77
diff
changeset
|
515 if strip: |
fa4bafcbe4c7
Minor improvements to how directives are applied in template processing.
cmlenz
parents:
77
diff
changeset
|
516 stream.next() # skip start tag |
fa4bafcbe4c7
Minor improvements to how directives are applied in template processing.
cmlenz
parents:
77
diff
changeset
|
517 previous = stream.next() |
fa4bafcbe4c7
Minor improvements to how directives are applied in template processing.
cmlenz
parents:
77
diff
changeset
|
518 for event in stream: |
fa4bafcbe4c7
Minor improvements to how directives are applied in template processing.
cmlenz
parents:
77
diff
changeset
|
519 yield previous |
fa4bafcbe4c7
Minor improvements to how directives are applied in template processing.
cmlenz
parents:
77
diff
changeset
|
520 previous = event |
fa4bafcbe4c7
Minor improvements to how directives are applied in template processing.
cmlenz
parents:
77
diff
changeset
|
521 else: |
fa4bafcbe4c7
Minor improvements to how directives are applied in template processing.
cmlenz
parents:
77
diff
changeset
|
522 for event in stream: |
fa4bafcbe4c7
Minor improvements to how directives are applied in template processing.
cmlenz
parents:
77
diff
changeset
|
523 yield event |
fa4bafcbe4c7
Minor improvements to how directives are applied in template processing.
cmlenz
parents:
77
diff
changeset
|
524 |
fa4bafcbe4c7
Minor improvements to how directives are applied in template processing.
cmlenz
parents:
77
diff
changeset
|
525 return _apply_directives(_generate(), ctxt, directives) |
1 | 526 |
527 | |
44
42bcb91bf025
implement `py:choose/when/otherwise` directives for conditionally selecting one of several blocks
mgood
parents:
38
diff
changeset
|
528 class ChooseDirective(Directive): |
42bcb91bf025
implement `py:choose/when/otherwise` directives for conditionally selecting one of several blocks
mgood
parents:
38
diff
changeset
|
529 """Implementation of the `py:choose` directive for conditionally selecting |
42bcb91bf025
implement `py:choose/when/otherwise` directives for conditionally selecting one of several blocks
mgood
parents:
38
diff
changeset
|
530 one of several body elements to display. |
53
60f1a556690e
* Add helper function to let directives apply any remaining directives, and use that helper consistently in every directive.
cmlenz
parents:
51
diff
changeset
|
531 |
44
42bcb91bf025
implement `py:choose/when/otherwise` directives for conditionally selecting one of several blocks
mgood
parents:
38
diff
changeset
|
532 If the `py:choose` expression is empty the expressions of nested `py:when` |
42bcb91bf025
implement `py:choose/when/otherwise` directives for conditionally selecting one of several blocks
mgood
parents:
38
diff
changeset
|
533 directives are tested for truth. The first true `py:when` body is output. |
53
60f1a556690e
* Add helper function to let directives apply any remaining directives, and use that helper consistently in every directive.
cmlenz
parents:
51
diff
changeset
|
534 If no `py:when` directive is matched then the fallback directive |
60f1a556690e
* Add helper function to let directives apply any remaining directives, and use that helper consistently in every directive.
cmlenz
parents:
51
diff
changeset
|
535 `py:otherwise` will be used. |
60f1a556690e
* Add helper function to let directives apply any remaining directives, and use that helper consistently in every directive.
cmlenz
parents:
51
diff
changeset
|
536 |
61 | 537 >>> tmpl = Template('''<div xmlns:py="http://markup.edgewall.org/" |
44
42bcb91bf025
implement `py:choose/when/otherwise` directives for conditionally selecting one of several blocks
mgood
parents:
38
diff
changeset
|
538 ... py:choose=""> |
42bcb91bf025
implement `py:choose/when/otherwise` directives for conditionally selecting one of several blocks
mgood
parents:
38
diff
changeset
|
539 ... <span py:when="0 == 1">0</span> |
42bcb91bf025
implement `py:choose/when/otherwise` directives for conditionally selecting one of several blocks
mgood
parents:
38
diff
changeset
|
540 ... <span py:when="1 == 1">1</span> |
53
60f1a556690e
* Add helper function to let directives apply any remaining directives, and use that helper consistently in every directive.
cmlenz
parents:
51
diff
changeset
|
541 ... <span py:otherwise="">2</span> |
44
42bcb91bf025
implement `py:choose/when/otherwise` directives for conditionally selecting one of several blocks
mgood
parents:
38
diff
changeset
|
542 ... </div>''') |
149
7306bf730ff3
`Template.generate()` now accepts the context data as keyword arguments, so that you don't have to import the `Context` class every time you want to pass data into a template.
cmlenz
parents:
145
diff
changeset
|
543 >>> print tmpl.generate() |
44
42bcb91bf025
implement `py:choose/when/otherwise` directives for conditionally selecting one of several blocks
mgood
parents:
38
diff
changeset
|
544 <div> |
42bcb91bf025
implement `py:choose/when/otherwise` directives for conditionally selecting one of several blocks
mgood
parents:
38
diff
changeset
|
545 <span>1</span> |
42bcb91bf025
implement `py:choose/when/otherwise` directives for conditionally selecting one of several blocks
mgood
parents:
38
diff
changeset
|
546 </div> |
53
60f1a556690e
* Add helper function to let directives apply any remaining directives, and use that helper consistently in every directive.
cmlenz
parents:
51
diff
changeset
|
547 |
44
42bcb91bf025
implement `py:choose/when/otherwise` directives for conditionally selecting one of several blocks
mgood
parents:
38
diff
changeset
|
548 If the `py:choose` directive contains an expression, the nested `py:when` |
53
60f1a556690e
* Add helper function to let directives apply any remaining directives, and use that helper consistently in every directive.
cmlenz
parents:
51
diff
changeset
|
549 directives are tested for equality to the `py:choose` expression: |
60f1a556690e
* Add helper function to let directives apply any remaining directives, and use that helper consistently in every directive.
cmlenz
parents:
51
diff
changeset
|
550 |
61 | 551 >>> tmpl = Template('''<div xmlns:py="http://markup.edgewall.org/" |
44
42bcb91bf025
implement `py:choose/when/otherwise` directives for conditionally selecting one of several blocks
mgood
parents:
38
diff
changeset
|
552 ... py:choose="2"> |
42bcb91bf025
implement `py:choose/when/otherwise` directives for conditionally selecting one of several blocks
mgood
parents:
38
diff
changeset
|
553 ... <span py:when="1">1</span> |
42bcb91bf025
implement `py:choose/when/otherwise` directives for conditionally selecting one of several blocks
mgood
parents:
38
diff
changeset
|
554 ... <span py:when="2">2</span> |
42bcb91bf025
implement `py:choose/when/otherwise` directives for conditionally selecting one of several blocks
mgood
parents:
38
diff
changeset
|
555 ... </div>''') |
149
7306bf730ff3
`Template.generate()` now accepts the context data as keyword arguments, so that you don't have to import the `Context` class every time you want to pass data into a template.
cmlenz
parents:
145
diff
changeset
|
556 >>> print tmpl.generate() |
44
42bcb91bf025
implement `py:choose/when/otherwise` directives for conditionally selecting one of several blocks
mgood
parents:
38
diff
changeset
|
557 <div> |
42bcb91bf025
implement `py:choose/when/otherwise` directives for conditionally selecting one of several blocks
mgood
parents:
38
diff
changeset
|
558 <span>2</span> |
42bcb91bf025
implement `py:choose/when/otherwise` directives for conditionally selecting one of several blocks
mgood
parents:
38
diff
changeset
|
559 </div> |
53
60f1a556690e
* Add helper function to let directives apply any remaining directives, and use that helper consistently in every directive.
cmlenz
parents:
51
diff
changeset
|
560 |
44
42bcb91bf025
implement `py:choose/when/otherwise` directives for conditionally selecting one of several blocks
mgood
parents:
38
diff
changeset
|
561 Behavior is undefined if a `py:choose` block contains content outside a |
42bcb91bf025
implement `py:choose/when/otherwise` directives for conditionally selecting one of several blocks
mgood
parents:
38
diff
changeset
|
562 `py:when` or `py:otherwise` block. Behavior is also undefined if a |
42bcb91bf025
implement `py:choose/when/otherwise` directives for conditionally selecting one of several blocks
mgood
parents:
38
diff
changeset
|
563 `py:otherwise` occurs before `py:when` blocks. |
42bcb91bf025
implement `py:choose/when/otherwise` directives for conditionally selecting one of several blocks
mgood
parents:
38
diff
changeset
|
564 """ |
50
a053ffb834cb
Fix the way multiple directives are applied to a single `SUB` in many cases by making the directives themselves responsible for applying any remaining directives.
cmlenz
parents:
48
diff
changeset
|
565 __slots__ = ['matched', 'value'] |
44
42bcb91bf025
implement `py:choose/when/otherwise` directives for conditionally selecting one of several blocks
mgood
parents:
38
diff
changeset
|
566 |
65
5c024cf58ecb
Support the use of directives as elements to reduce the need for using `py:strip`.
cmlenz
parents:
61
diff
changeset
|
567 ATTRIBUTE = 'test' |
5c024cf58ecb
Support the use of directives as elements to reduce the need for using `py:strip`.
cmlenz
parents:
61
diff
changeset
|
568 |
53
60f1a556690e
* Add helper function to let directives apply any remaining directives, and use that helper consistently in every directive.
cmlenz
parents:
51
diff
changeset
|
569 def __call__(self, stream, ctxt, directives): |
44
42bcb91bf025
implement `py:choose/when/otherwise` directives for conditionally selecting one of several blocks
mgood
parents:
38
diff
changeset
|
570 if self.expr: |
42bcb91bf025
implement `py:choose/when/otherwise` directives for conditionally selecting one of several blocks
mgood
parents:
38
diff
changeset
|
571 self.value = self.expr.evaluate(ctxt) |
42bcb91bf025
implement `py:choose/when/otherwise` directives for conditionally selecting one of several blocks
mgood
parents:
38
diff
changeset
|
572 self.matched = False |
95
7d6426183a90
Improve performance of push/pop operations on the context.
cmlenz
parents:
93
diff
changeset
|
573 ctxt.push(dict(_choose=self)) |
78
fa4bafcbe4c7
Minor improvements to how directives are applied in template processing.
cmlenz
parents:
77
diff
changeset
|
574 for event in _apply_directives(stream, ctxt, directives): |
44
42bcb91bf025
implement `py:choose/when/otherwise` directives for conditionally selecting one of several blocks
mgood
parents:
38
diff
changeset
|
575 yield event |
42bcb91bf025
implement `py:choose/when/otherwise` directives for conditionally selecting one of several blocks
mgood
parents:
38
diff
changeset
|
576 ctxt.pop() |
42bcb91bf025
implement `py:choose/when/otherwise` directives for conditionally selecting one of several blocks
mgood
parents:
38
diff
changeset
|
577 |
42bcb91bf025
implement `py:choose/when/otherwise` directives for conditionally selecting one of several blocks
mgood
parents:
38
diff
changeset
|
578 |
42bcb91bf025
implement `py:choose/when/otherwise` directives for conditionally selecting one of several blocks
mgood
parents:
38
diff
changeset
|
579 class WhenDirective(Directive): |
42bcb91bf025
implement `py:choose/when/otherwise` directives for conditionally selecting one of several blocks
mgood
parents:
38
diff
changeset
|
580 """Implementation of the `py:when` directive for nesting in a parent with |
50
a053ffb834cb
Fix the way multiple directives are applied to a single `SUB` in many cases by making the directives themselves responsible for applying any remaining directives.
cmlenz
parents:
48
diff
changeset
|
581 the `py:choose` directive. |
a053ffb834cb
Fix the way multiple directives are applied to a single `SUB` in many cases by making the directives themselves responsible for applying any remaining directives.
cmlenz
parents:
48
diff
changeset
|
582 |
a053ffb834cb
Fix the way multiple directives are applied to a single `SUB` in many cases by making the directives themselves responsible for applying any remaining directives.
cmlenz
parents:
48
diff
changeset
|
583 See the documentation of `py:choose` for usage. |
44
42bcb91bf025
implement `py:choose/when/otherwise` directives for conditionally selecting one of several blocks
mgood
parents:
38
diff
changeset
|
584 """ |
65
5c024cf58ecb
Support the use of directives as elements to reduce the need for using `py:strip`.
cmlenz
parents:
61
diff
changeset
|
585 |
5c024cf58ecb
Support the use of directives as elements to reduce the need for using `py:strip`.
cmlenz
parents:
61
diff
changeset
|
586 ATTRIBUTE = 'test' |
5c024cf58ecb
Support the use of directives as elements to reduce the need for using `py:strip`.
cmlenz
parents:
61
diff
changeset
|
587 |
54 | 588 def __call__(self, stream, ctxt, directives): |
50
a053ffb834cb
Fix the way multiple directives are applied to a single `SUB` in many cases by making the directives themselves responsible for applying any remaining directives.
cmlenz
parents:
48
diff
changeset
|
589 choose = ctxt['_choose'] |
44
42bcb91bf025
implement `py:choose/when/otherwise` directives for conditionally selecting one of several blocks
mgood
parents:
38
diff
changeset
|
590 if choose.matched: |
42bcb91bf025
implement `py:choose/when/otherwise` directives for conditionally selecting one of several blocks
mgood
parents:
38
diff
changeset
|
591 return [] |
42bcb91bf025
implement `py:choose/when/otherwise` directives for conditionally selecting one of several blocks
mgood
parents:
38
diff
changeset
|
592 value = self.expr.evaluate(ctxt) |
42bcb91bf025
implement `py:choose/when/otherwise` directives for conditionally selecting one of several blocks
mgood
parents:
38
diff
changeset
|
593 try: |
42bcb91bf025
implement `py:choose/when/otherwise` directives for conditionally selecting one of several blocks
mgood
parents:
38
diff
changeset
|
594 if value == choose.value: |
42bcb91bf025
implement `py:choose/when/otherwise` directives for conditionally selecting one of several blocks
mgood
parents:
38
diff
changeset
|
595 choose.matched = True |
78
fa4bafcbe4c7
Minor improvements to how directives are applied in template processing.
cmlenz
parents:
77
diff
changeset
|
596 return _apply_directives(stream, ctxt, directives) |
44
42bcb91bf025
implement `py:choose/when/otherwise` directives for conditionally selecting one of several blocks
mgood
parents:
38
diff
changeset
|
597 except AttributeError: |
42bcb91bf025
implement `py:choose/when/otherwise` directives for conditionally selecting one of several blocks
mgood
parents:
38
diff
changeset
|
598 if value: |
42bcb91bf025
implement `py:choose/when/otherwise` directives for conditionally selecting one of several blocks
mgood
parents:
38
diff
changeset
|
599 choose.matched = True |
78
fa4bafcbe4c7
Minor improvements to how directives are applied in template processing.
cmlenz
parents:
77
diff
changeset
|
600 return _apply_directives(stream, ctxt, directives) |
44
42bcb91bf025
implement `py:choose/when/otherwise` directives for conditionally selecting one of several blocks
mgood
parents:
38
diff
changeset
|
601 return [] |
42bcb91bf025
implement `py:choose/when/otherwise` directives for conditionally selecting one of several blocks
mgood
parents:
38
diff
changeset
|
602 |
42bcb91bf025
implement `py:choose/when/otherwise` directives for conditionally selecting one of several blocks
mgood
parents:
38
diff
changeset
|
603 |
42bcb91bf025
implement `py:choose/when/otherwise` directives for conditionally selecting one of several blocks
mgood
parents:
38
diff
changeset
|
604 class OtherwiseDirective(Directive): |
42bcb91bf025
implement `py:choose/when/otherwise` directives for conditionally selecting one of several blocks
mgood
parents:
38
diff
changeset
|
605 """Implementation of the `py:otherwise` directive for nesting in a parent |
50
a053ffb834cb
Fix the way multiple directives are applied to a single `SUB` in many cases by making the directives themselves responsible for applying any remaining directives.
cmlenz
parents:
48
diff
changeset
|
606 with the `py:choose` directive. |
a053ffb834cb
Fix the way multiple directives are applied to a single `SUB` in many cases by making the directives themselves responsible for applying any remaining directives.
cmlenz
parents:
48
diff
changeset
|
607 |
a053ffb834cb
Fix the way multiple directives are applied to a single `SUB` in many cases by making the directives themselves responsible for applying any remaining directives.
cmlenz
parents:
48
diff
changeset
|
608 See the documentation of `py:choose` for usage. |
44
42bcb91bf025
implement `py:choose/when/otherwise` directives for conditionally selecting one of several blocks
mgood
parents:
38
diff
changeset
|
609 """ |
54 | 610 def __call__(self, stream, ctxt, directives): |
50
a053ffb834cb
Fix the way multiple directives are applied to a single `SUB` in many cases by making the directives themselves responsible for applying any remaining directives.
cmlenz
parents:
48
diff
changeset
|
611 choose = ctxt['_choose'] |
44
42bcb91bf025
implement `py:choose/when/otherwise` directives for conditionally selecting one of several blocks
mgood
parents:
38
diff
changeset
|
612 if choose.matched: |
42bcb91bf025
implement `py:choose/when/otherwise` directives for conditionally selecting one of several blocks
mgood
parents:
38
diff
changeset
|
613 return [] |
42bcb91bf025
implement `py:choose/when/otherwise` directives for conditionally selecting one of several blocks
mgood
parents:
38
diff
changeset
|
614 choose.matched = True |
78
fa4bafcbe4c7
Minor improvements to how directives are applied in template processing.
cmlenz
parents:
77
diff
changeset
|
615 return _apply_directives(stream, ctxt, directives) |
44
42bcb91bf025
implement `py:choose/when/otherwise` directives for conditionally selecting one of several blocks
mgood
parents:
38
diff
changeset
|
616 |
42bcb91bf025
implement `py:choose/when/otherwise` directives for conditionally selecting one of several blocks
mgood
parents:
38
diff
changeset
|
617 |
104
e9259920db05
Added `py:with` directive based on Jonas' patch in #17.
cmlenz
parents:
101
diff
changeset
|
618 class WithDirective(Directive): |
e9259920db05
Added `py:with` directive based on Jonas' patch in #17.
cmlenz
parents:
101
diff
changeset
|
619 """Implementation of the `py:with` template directive, which allows |
e9259920db05
Added `py:with` directive based on Jonas' patch in #17.
cmlenz
parents:
101
diff
changeset
|
620 shorthand access to variables and expressions. |
e9259920db05
Added `py:with` directive based on Jonas' patch in #17.
cmlenz
parents:
101
diff
changeset
|
621 |
e9259920db05
Added `py:with` directive based on Jonas' patch in #17.
cmlenz
parents:
101
diff
changeset
|
622 >>> tmpl = Template('''<div xmlns:py="http://markup.edgewall.org/"> |
e9259920db05
Added `py:with` directive based on Jonas' patch in #17.
cmlenz
parents:
101
diff
changeset
|
623 ... <span py:with="y=7; z=x+10">$x $y $z</span> |
e9259920db05
Added `py:with` directive based on Jonas' patch in #17.
cmlenz
parents:
101
diff
changeset
|
624 ... </div>''') |
149
7306bf730ff3
`Template.generate()` now accepts the context data as keyword arguments, so that you don't have to import the `Context` class every time you want to pass data into a template.
cmlenz
parents:
145
diff
changeset
|
625 >>> print tmpl.generate(x=42) |
104
e9259920db05
Added `py:with` directive based on Jonas' patch in #17.
cmlenz
parents:
101
diff
changeset
|
626 <div> |
e9259920db05
Added `py:with` directive based on Jonas' patch in #17.
cmlenz
parents:
101
diff
changeset
|
627 <span>42 7 52</span> |
e9259920db05
Added `py:with` directive based on Jonas' patch in #17.
cmlenz
parents:
101
diff
changeset
|
628 </div> |
e9259920db05
Added `py:with` directive based on Jonas' patch in #17.
cmlenz
parents:
101
diff
changeset
|
629 """ |
e9259920db05
Added `py:with` directive based on Jonas' patch in #17.
cmlenz
parents:
101
diff
changeset
|
630 __slots__ = ['vars'] |
e9259920db05
Added `py:with` directive based on Jonas' patch in #17.
cmlenz
parents:
101
diff
changeset
|
631 |
e9259920db05
Added `py:with` directive based on Jonas' patch in #17.
cmlenz
parents:
101
diff
changeset
|
632 ATTRIBUTE = 'vars' |
e9259920db05
Added `py:with` directive based on Jonas' patch in #17.
cmlenz
parents:
101
diff
changeset
|
633 |
e9259920db05
Added `py:with` directive based on Jonas' patch in #17.
cmlenz
parents:
101
diff
changeset
|
634 def __init__(self, value, filename=None, lineno=-1, offset=-1): |
e9259920db05
Added `py:with` directive based on Jonas' patch in #17.
cmlenz
parents:
101
diff
changeset
|
635 Directive.__init__(self, None, filename, lineno, offset) |
e9259920db05
Added `py:with` directive based on Jonas' patch in #17.
cmlenz
parents:
101
diff
changeset
|
636 self.vars = [] |
e9259920db05
Added `py:with` directive based on Jonas' patch in #17.
cmlenz
parents:
101
diff
changeset
|
637 try: |
e9259920db05
Added `py:with` directive based on Jonas' patch in #17.
cmlenz
parents:
101
diff
changeset
|
638 for stmt in value.split(';'): |
e9259920db05
Added `py:with` directive based on Jonas' patch in #17.
cmlenz
parents:
101
diff
changeset
|
639 name, value = stmt.split('=', 1) |
e9259920db05
Added `py:with` directive based on Jonas' patch in #17.
cmlenz
parents:
101
diff
changeset
|
640 self.vars.append((name.strip(), |
e9259920db05
Added `py:with` directive based on Jonas' patch in #17.
cmlenz
parents:
101
diff
changeset
|
641 Expression(value.strip(), filename, lineno))) |
e9259920db05
Added `py:with` directive based on Jonas' patch in #17.
cmlenz
parents:
101
diff
changeset
|
642 except SyntaxError, err: |
e9259920db05
Added `py:with` directive based on Jonas' patch in #17.
cmlenz
parents:
101
diff
changeset
|
643 raise TemplateSyntaxError(err, filename, lineno, |
e9259920db05
Added `py:with` directive based on Jonas' patch in #17.
cmlenz
parents:
101
diff
changeset
|
644 offset + (err.offset or 0)) |
e9259920db05
Added `py:with` directive based on Jonas' patch in #17.
cmlenz
parents:
101
diff
changeset
|
645 |
e9259920db05
Added `py:with` directive based on Jonas' patch in #17.
cmlenz
parents:
101
diff
changeset
|
646 def __call__(self, stream, ctxt, directives): |
120 | 647 ctxt.push(dict([(name, expr.evaluate(ctxt, nocall=True)) |
104
e9259920db05
Added `py:with` directive based on Jonas' patch in #17.
cmlenz
parents:
101
diff
changeset
|
648 for name, expr in self.vars])) |
e9259920db05
Added `py:with` directive based on Jonas' patch in #17.
cmlenz
parents:
101
diff
changeset
|
649 for event in _apply_directives(stream, ctxt, directives): |
e9259920db05
Added `py:with` directive based on Jonas' patch in #17.
cmlenz
parents:
101
diff
changeset
|
650 yield event |
e9259920db05
Added `py:with` directive based on Jonas' patch in #17.
cmlenz
parents:
101
diff
changeset
|
651 ctxt.pop() |
e9259920db05
Added `py:with` directive based on Jonas' patch in #17.
cmlenz
parents:
101
diff
changeset
|
652 |
e9259920db05
Added `py:with` directive based on Jonas' patch in #17.
cmlenz
parents:
101
diff
changeset
|
653 def __repr__(self): |
e9259920db05
Added `py:with` directive based on Jonas' patch in #17.
cmlenz
parents:
101
diff
changeset
|
654 return '<%s "%s">' % (self.__class__.__name__, |
e9259920db05
Added `py:with` directive based on Jonas' patch in #17.
cmlenz
parents:
101
diff
changeset
|
655 '; '.join(['%s = %s' % (name, expr.source) |
e9259920db05
Added `py:with` directive based on Jonas' patch in #17.
cmlenz
parents:
101
diff
changeset
|
656 for name, expr in self.vars])) |
e9259920db05
Added `py:with` directive based on Jonas' patch in #17.
cmlenz
parents:
101
diff
changeset
|
657 |
e9259920db05
Added `py:with` directive based on Jonas' patch in #17.
cmlenz
parents:
101
diff
changeset
|
658 |
1 | 659 class Template(object): |
660 """Can parse a template and transform it into the corresponding output | |
661 based on context data. | |
662 """ | |
61 | 663 NAMESPACE = Namespace('http://markup.edgewall.org/') |
1 | 664 |
17
ad63ad459524
Refactoring to address #6: all match templates are now processed by a single filter, which means that match templates added by included templates are properly applied. A side effect of this refactoring is that `Context` objects may not be reused across multiple template processing runs.
cmlenz
parents:
14
diff
changeset
|
665 EXPR = StreamEventKind('EXPR') # an expression |
ad63ad459524
Refactoring to address #6: all match templates are now processed by a single filter, which means that match templates added by included templates are properly applied. A side effect of this refactoring is that `Context` objects may not be reused across multiple template processing runs.
cmlenz
parents:
14
diff
changeset
|
666 SUB = StreamEventKind('SUB') # a "subprogram" |
10
c5890ef863ba
Moved the template-specific stream event kinds into the template module.
cmlenz
parents:
6
diff
changeset
|
667 |
1 | 668 directives = [('def', DefDirective), |
669 ('match', MatchDirective), | |
120 | 670 ('when', WhenDirective), |
671 ('otherwise', OtherwiseDirective), | |
1 | 672 ('for', ForDirective), |
673 ('if', IfDirective), | |
53
60f1a556690e
* Add helper function to let directives apply any remaining directives, and use that helper consistently in every directive.
cmlenz
parents:
51
diff
changeset
|
674 ('choose', ChooseDirective), |
104
e9259920db05
Added `py:with` directive based on Jonas' patch in #17.
cmlenz
parents:
101
diff
changeset
|
675 ('with', WithDirective), |
1 | 676 ('replace', ReplaceDirective), |
677 ('content', ContentDirective), | |
678 ('attrs', AttrsDirective), | |
50
a053ffb834cb
Fix the way multiple directives are applied to a single `SUB` in many cases by making the directives themselves responsible for applying any remaining directives.
cmlenz
parents:
48
diff
changeset
|
679 ('strip', StripDirective)] |
1 | 680 _dir_by_name = dict(directives) |
681 _dir_order = [directive[1] for directive in directives] | |
682 | |
21
eca77129518a
* Include paths are now interpreted relative to the path of the including template. Closes #3.
cmlenz
parents:
18
diff
changeset
|
683 def __init__(self, source, basedir=None, filename=None): |
1 | 684 """Initialize a template from either a string or a file-like object.""" |
685 if isinstance(source, basestring): | |
686 self.source = StringIO(source) | |
687 else: | |
688 self.source = source | |
21
eca77129518a
* Include paths are now interpreted relative to the path of the including template. Closes #3.
cmlenz
parents:
18
diff
changeset
|
689 self.basedir = basedir |
1 | 690 self.filename = filename or '<string>' |
21
eca77129518a
* Include paths are now interpreted relative to the path of the including template. Closes #3.
cmlenz
parents:
18
diff
changeset
|
691 if basedir and filename: |
eca77129518a
* Include paths are now interpreted relative to the path of the including template. Closes #3.
cmlenz
parents:
18
diff
changeset
|
692 self.filepath = os.path.join(basedir, filename) |
eca77129518a
* Include paths are now interpreted relative to the path of the including template. Closes #3.
cmlenz
parents:
18
diff
changeset
|
693 else: |
eca77129518a
* Include paths are now interpreted relative to the path of the including template. Closes #3.
cmlenz
parents:
18
diff
changeset
|
694 self.filepath = '<string>' |
1 | 695 |
23
00835401c8cc
Separate match and eval filters from the include and user-supplied filters.
cmlenz
parents:
22
diff
changeset
|
696 self.filters = [] |
1 | 697 self.parse() |
698 | |
699 def __repr__(self): | |
21
eca77129518a
* Include paths are now interpreted relative to the path of the including template. Closes #3.
cmlenz
parents:
18
diff
changeset
|
700 return '<%s "%s">' % (self.__class__.__name__, self.filename) |
1 | 701 |
702 def parse(self): | |
703 """Parse the template. | |
704 | |
705 The parsing stage parses the XML template and constructs a list of | |
706 directives that will be executed in the render stage. The input is | |
707 split up into literal output (markup that does not depend on the | |
708 context data) and actual directives (commands or variable | |
709 substitution). | |
710 """ | |
711 stream = [] # list of events of the "compiled" template | |
712 dirmap = {} # temporary mapping of directives to elements | |
713 ns_prefix = {} | |
714 depth = 0 | |
715 | |
21
eca77129518a
* Include paths are now interpreted relative to the path of the including template. Closes #3.
cmlenz
parents:
18
diff
changeset
|
716 for kind, data, pos in XMLParser(self.source, filename=self.filename): |
1 | 717 |
69 | 718 if kind is START_NS: |
1 | 719 # Strip out the namespace declaration for template directives |
720 prefix, uri = data | |
721 if uri == self.NAMESPACE: | |
722 ns_prefix[prefix] = uri | |
723 else: | |
724 stream.append((kind, data, pos)) | |
725 | |
69 | 726 elif kind is END_NS: |
1 | 727 if data in ns_prefix: |
728 del ns_prefix[data] | |
729 else: | |
730 stream.append((kind, data, pos)) | |
731 | |
69 | 732 elif kind is START: |
1 | 733 # Record any directive attributes in start tags |
734 tag, attrib = data | |
735 directives = [] | |
65
5c024cf58ecb
Support the use of directives as elements to reduce the need for using `py:strip`.
cmlenz
parents:
61
diff
changeset
|
736 strip = False |
5c024cf58ecb
Support the use of directives as elements to reduce the need for using `py:strip`.
cmlenz
parents:
61
diff
changeset
|
737 |
5c024cf58ecb
Support the use of directives as elements to reduce the need for using `py:strip`.
cmlenz
parents:
61
diff
changeset
|
738 if tag in self.NAMESPACE: |
5c024cf58ecb
Support the use of directives as elements to reduce the need for using `py:strip`.
cmlenz
parents:
61
diff
changeset
|
739 cls = self._dir_by_name.get(tag.localname) |
5c024cf58ecb
Support the use of directives as elements to reduce the need for using `py:strip`.
cmlenz
parents:
61
diff
changeset
|
740 if cls is None: |
5c024cf58ecb
Support the use of directives as elements to reduce the need for using `py:strip`.
cmlenz
parents:
61
diff
changeset
|
741 raise BadDirectiveError(tag, pos[0], pos[1]) |
66
822089ae65ce
Switch copyright to Edgewall and URLs to markup.edgewall.org.
cmlenz
parents:
65
diff
changeset
|
742 value = attrib.get(getattr(cls, 'ATTRIBUTE', None), '') |
81
cc034182061e
Template expressions are now compiled to Python bytecode.
cmlenz
parents:
80
diff
changeset
|
743 directives.append(cls(value, *pos)) |
65
5c024cf58ecb
Support the use of directives as elements to reduce the need for using `py:strip`.
cmlenz
parents:
61
diff
changeset
|
744 strip = True |
5c024cf58ecb
Support the use of directives as elements to reduce the need for using `py:strip`.
cmlenz
parents:
61
diff
changeset
|
745 |
1 | 746 new_attrib = [] |
747 for name, value in attrib: | |
18
4cbebb15a834
Actually make use of the `markup.core.Namespace` class, and add a couple of doctests.
cmlenz
parents:
17
diff
changeset
|
748 if name in self.NAMESPACE: |
1 | 749 cls = self._dir_by_name.get(name.localname) |
750 if cls is None: | |
65
5c024cf58ecb
Support the use of directives as elements to reduce the need for using `py:strip`.
cmlenz
parents:
61
diff
changeset
|
751 raise BadDirectiveError(name, pos[0], pos[1]) |
81
cc034182061e
Template expressions are now compiled to Python bytecode.
cmlenz
parents:
80
diff
changeset
|
752 directives.append(cls(value, *pos)) |
1 | 753 else: |
75
c3c26300a46d
Empty attributes in templates were being stripped out. Thanks to Jonas for the patch.
cmlenz
parents:
74
diff
changeset
|
754 if value: |
c3c26300a46d
Empty attributes in templates were being stripped out. Thanks to Jonas for the patch.
cmlenz
parents:
74
diff
changeset
|
755 value = list(self._interpolate(value, *pos)) |
81
cc034182061e
Template expressions are now compiled to Python bytecode.
cmlenz
parents:
80
diff
changeset
|
756 if len(value) == 1 and value[0][0] is TEXT: |
cc034182061e
Template expressions are now compiled to Python bytecode.
cmlenz
parents:
80
diff
changeset
|
757 value = value[0][1] |
75
c3c26300a46d
Empty attributes in templates were being stripped out. Thanks to Jonas for the patch.
cmlenz
parents:
74
diff
changeset
|
758 else: |
c3c26300a46d
Empty attributes in templates were being stripped out. Thanks to Jonas for the patch.
cmlenz
parents:
74
diff
changeset
|
759 value = [(TEXT, u'', pos)] |
1 | 760 new_attrib.append((name, value)) |
65
5c024cf58ecb
Support the use of directives as elements to reduce the need for using `py:strip`.
cmlenz
parents:
61
diff
changeset
|
761 |
1 | 762 if directives: |
50
a053ffb834cb
Fix the way multiple directives are applied to a single `SUB` in many cases by making the directives themselves responsible for applying any remaining directives.
cmlenz
parents:
48
diff
changeset
|
763 directives.sort(lambda a, b: cmp(self._dir_order.index(a.__class__), |
a053ffb834cb
Fix the way multiple directives are applied to a single `SUB` in many cases by making the directives themselves responsible for applying any remaining directives.
cmlenz
parents:
48
diff
changeset
|
764 self._dir_order.index(b.__class__))) |
65
5c024cf58ecb
Support the use of directives as elements to reduce the need for using `py:strip`.
cmlenz
parents:
61
diff
changeset
|
765 dirmap[(depth, tag)] = (directives, len(stream), strip) |
1 | 766 |
767 stream.append((kind, (tag, Attributes(new_attrib)), pos)) | |
768 depth += 1 | |
769 | |
69 | 770 elif kind is END: |
1 | 771 depth -= 1 |
772 stream.append((kind, data, pos)) | |
773 | |
774 # If there have have directive attributes with the corresponding | |
775 # start tag, move the events inbetween into a "subprogram" | |
776 if (depth, data) in dirmap: | |
65
5c024cf58ecb
Support the use of directives as elements to reduce the need for using `py:strip`.
cmlenz
parents:
61
diff
changeset
|
777 directives, start_offset, strip = dirmap.pop((depth, data)) |
1 | 778 substream = stream[start_offset:] |
65
5c024cf58ecb
Support the use of directives as elements to reduce the need for using `py:strip`.
cmlenz
parents:
61
diff
changeset
|
779 if strip: |
5c024cf58ecb
Support the use of directives as elements to reduce the need for using `py:strip`.
cmlenz
parents:
61
diff
changeset
|
780 substream = substream[1:-1] |
69 | 781 stream[start_offset:] = [(SUB, (directives, substream), |
782 pos)] | |
1 | 783 |
69 | 784 elif kind is TEXT: |
1 | 785 for kind, data, pos in self._interpolate(data, *pos): |
786 stream.append((kind, data, pos)) | |
787 | |
89
d4c7617900e3
Support comments in templates that are not included in the output, in the same way Kid does: if the comment text starts with a `!` character, it is stripped from the output.
cmlenz
parents:
87
diff
changeset
|
788 elif kind is COMMENT: |
d4c7617900e3
Support comments in templates that are not included in the output, in the same way Kid does: if the comment text starts with a `!` character, it is stripped from the output.
cmlenz
parents:
87
diff
changeset
|
789 if not data.lstrip().startswith('!'): |
d4c7617900e3
Support comments in templates that are not included in the output, in the same way Kid does: if the comment text starts with a `!` character, it is stripped from the output.
cmlenz
parents:
87
diff
changeset
|
790 stream.append((kind, data, pos)) |
d4c7617900e3
Support comments in templates that are not included in the output, in the same way Kid does: if the comment text starts with a `!` character, it is stripped from the output.
cmlenz
parents:
87
diff
changeset
|
791 |
1 | 792 else: |
793 stream.append((kind, data, pos)) | |
794 | |
795 self.stream = stream | |
796 | |
797 _FULL_EXPR_RE = re.compile(r'(?<!\$)\$\{(.+?)\}') | |
798 _SHORT_EXPR_RE = re.compile(r'(?<!\$)\$([a-zA-Z][a-zA-Z0-9_\.]*)') | |
799 | |
21
eca77129518a
* Include paths are now interpreted relative to the path of the including template. Closes #3.
cmlenz
parents:
18
diff
changeset
|
800 def _interpolate(cls, text, filename=None, lineno=-1, offset=-1): |
1 | 801 """Parse the given string and extract expressions. |
802 | |
803 This method returns a list containing both literal text and `Expression` | |
804 objects. | |
14
76b5d4b189e6
The `<py:match>` directive now protects itself against simple infinite recursion (see MatchDirective), while still allowing recursion in general.
cmlenz
parents:
13
diff
changeset
|
805 |
1 | 806 @param text: the text to parse |
807 @param lineno: the line number at which the text was found (optional) | |
808 @param offset: the column number at which the text starts in the source | |
809 (optional) | |
810 """ | |
134
df44110ca91d
* Improve the accuracy of line numbers for text nodes, so that reported errors about syntax or evaluation errors in expressions point to the right line (not quite perfect yet, though).
cmlenz
parents:
133
diff
changeset
|
811 def _interpolate(text, patterns, filename=filename, lineno=lineno, |
df44110ca91d
* Improve the accuracy of line numbers for text nodes, so that reported errors about syntax or evaluation errors in expressions point to the right line (not quite perfect yet, though).
cmlenz
parents:
133
diff
changeset
|
812 offset=offset): |
1 | 813 for idx, group in enumerate(patterns.pop(0).split(text)): |
814 if idx % 2: | |
81
cc034182061e
Template expressions are now compiled to Python bytecode.
cmlenz
parents:
80
diff
changeset
|
815 try: |
cc034182061e
Template expressions are now compiled to Python bytecode.
cmlenz
parents:
80
diff
changeset
|
816 yield EXPR, Expression(group, filename, lineno), \ |
134
df44110ca91d
* Improve the accuracy of line numbers for text nodes, so that reported errors about syntax or evaluation errors in expressions point to the right line (not quite perfect yet, though).
cmlenz
parents:
133
diff
changeset
|
817 (filename, lineno, offset) |
81
cc034182061e
Template expressions are now compiled to Python bytecode.
cmlenz
parents:
80
diff
changeset
|
818 except SyntaxError, err: |
cc034182061e
Template expressions are now compiled to Python bytecode.
cmlenz
parents:
80
diff
changeset
|
819 raise TemplateSyntaxError(err, filename, lineno, |
cc034182061e
Template expressions are now compiled to Python bytecode.
cmlenz
parents:
80
diff
changeset
|
820 offset + (err.offset or 0)) |
1 | 821 elif group: |
822 if patterns: | |
74
3c271699c398
Fix expression interpolation where both shorthand notation and full notation are used inside a single text node. Thanks Jonas.
cmlenz
parents:
73
diff
changeset
|
823 for result in _interpolate(group, patterns[:]): |
1 | 824 yield result |
825 else: | |
134
df44110ca91d
* Improve the accuracy of line numbers for text nodes, so that reported errors about syntax or evaluation errors in expressions point to the right line (not quite perfect yet, though).
cmlenz
parents:
133
diff
changeset
|
826 yield TEXT, group.replace('$$', '$'), \ |
df44110ca91d
* Improve the accuracy of line numbers for text nodes, so that reported errors about syntax or evaluation errors in expressions point to the right line (not quite perfect yet, though).
cmlenz
parents:
133
diff
changeset
|
827 (filename, lineno, offset) |
df44110ca91d
* Improve the accuracy of line numbers for text nodes, so that reported errors about syntax or evaluation errors in expressions point to the right line (not quite perfect yet, though).
cmlenz
parents:
133
diff
changeset
|
828 if '\n' in group: |
df44110ca91d
* Improve the accuracy of line numbers for text nodes, so that reported errors about syntax or evaluation errors in expressions point to the right line (not quite perfect yet, though).
cmlenz
parents:
133
diff
changeset
|
829 lines = group.splitlines() |
df44110ca91d
* Improve the accuracy of line numbers for text nodes, so that reported errors about syntax or evaluation errors in expressions point to the right line (not quite perfect yet, though).
cmlenz
parents:
133
diff
changeset
|
830 lineno += len(lines) - 1 |
df44110ca91d
* Improve the accuracy of line numbers for text nodes, so that reported errors about syntax or evaluation errors in expressions point to the right line (not quite perfect yet, though).
cmlenz
parents:
133
diff
changeset
|
831 offset += len(lines[-1]) |
df44110ca91d
* Improve the accuracy of line numbers for text nodes, so that reported errors about syntax or evaluation errors in expressions point to the right line (not quite perfect yet, though).
cmlenz
parents:
133
diff
changeset
|
832 else: |
df44110ca91d
* Improve the accuracy of line numbers for text nodes, so that reported errors about syntax or evaluation errors in expressions point to the right line (not quite perfect yet, though).
cmlenz
parents:
133
diff
changeset
|
833 offset += len(group) |
74
3c271699c398
Fix expression interpolation where both shorthand notation and full notation are used inside a single text node. Thanks Jonas.
cmlenz
parents:
73
diff
changeset
|
834 return _interpolate(text, [cls._FULL_EXPR_RE, cls._SHORT_EXPR_RE]) |
1 | 835 _interpolate = classmethod(_interpolate) |
836 | |
149
7306bf730ff3
`Template.generate()` now accepts the context data as keyword arguments, so that you don't have to import the `Context` class every time you want to pass data into a template.
cmlenz
parents:
145
diff
changeset
|
837 def generate(self, *args, **kwargs): |
29
4b6cee37ce62
* Minor simplification of template directives: they no longer get passed the template instance and the position, as no directive was actually using
cmlenz
parents:
27
diff
changeset
|
838 """Apply the template to the given context data. |
4b6cee37ce62
* Minor simplification of template directives: they no longer get passed the template instance and the position, as no directive was actually using
cmlenz
parents:
27
diff
changeset
|
839 |
149
7306bf730ff3
`Template.generate()` now accepts the context data as keyword arguments, so that you don't have to import the `Context` class every time you want to pass data into a template.
cmlenz
parents:
145
diff
changeset
|
840 Any keyword arguments are made available to the template as context |
7306bf730ff3
`Template.generate()` now accepts the context data as keyword arguments, so that you don't have to import the `Context` class every time you want to pass data into a template.
cmlenz
parents:
145
diff
changeset
|
841 data. |
7306bf730ff3
`Template.generate()` now accepts the context data as keyword arguments, so that you don't have to import the `Context` class every time you want to pass data into a template.
cmlenz
parents:
145
diff
changeset
|
842 |
7306bf730ff3
`Template.generate()` now accepts the context data as keyword arguments, so that you don't have to import the `Context` class every time you want to pass data into a template.
cmlenz
parents:
145
diff
changeset
|
843 Only one positional argument is accepted: if it is provided, it must be |
7306bf730ff3
`Template.generate()` now accepts the context data as keyword arguments, so that you don't have to import the `Context` class every time you want to pass data into a template.
cmlenz
parents:
145
diff
changeset
|
844 an instance of the `Context` class, and keyword arguments are ignored. |
7306bf730ff3
`Template.generate()` now accepts the context data as keyword arguments, so that you don't have to import the `Context` class every time you want to pass data into a template.
cmlenz
parents:
145
diff
changeset
|
845 This calling style is used for internal processing. |
7306bf730ff3
`Template.generate()` now accepts the context data as keyword arguments, so that you don't have to import the `Context` class every time you want to pass data into a template.
cmlenz
parents:
145
diff
changeset
|
846 |
29
4b6cee37ce62
* Minor simplification of template directives: they no longer get passed the template instance and the position, as no directive was actually using
cmlenz
parents:
27
diff
changeset
|
847 @return: a markup event stream representing the result of applying |
4b6cee37ce62
* Minor simplification of template directives: they no longer get passed the template instance and the position, as no directive was actually using
cmlenz
parents:
27
diff
changeset
|
848 the template to the context data. |
4b6cee37ce62
* Minor simplification of template directives: they no longer get passed the template instance and the position, as no directive was actually using
cmlenz
parents:
27
diff
changeset
|
849 """ |
149
7306bf730ff3
`Template.generate()` now accepts the context data as keyword arguments, so that you don't have to import the `Context` class every time you want to pass data into a template.
cmlenz
parents:
145
diff
changeset
|
850 if args: |
7306bf730ff3
`Template.generate()` now accepts the context data as keyword arguments, so that you don't have to import the `Context` class every time you want to pass data into a template.
cmlenz
parents:
145
diff
changeset
|
851 assert len(args) == 1 |
7306bf730ff3
`Template.generate()` now accepts the context data as keyword arguments, so that you don't have to import the `Context` class every time you want to pass data into a template.
cmlenz
parents:
145
diff
changeset
|
852 ctxt = args[0] |
7306bf730ff3
`Template.generate()` now accepts the context data as keyword arguments, so that you don't have to import the `Context` class every time you want to pass data into a template.
cmlenz
parents:
145
diff
changeset
|
853 assert isinstance(ctxt, Context) |
7306bf730ff3
`Template.generate()` now accepts the context data as keyword arguments, so that you don't have to import the `Context` class every time you want to pass data into a template.
cmlenz
parents:
145
diff
changeset
|
854 else: |
7306bf730ff3
`Template.generate()` now accepts the context data as keyword arguments, so that you don't have to import the `Context` class every time you want to pass data into a template.
cmlenz
parents:
145
diff
changeset
|
855 ctxt = Context(**kwargs) |
17
ad63ad459524
Refactoring to address #6: all match templates are now processed by a single filter, which means that match templates added by included templates are properly applied. A side effect of this refactoring is that `Context` objects may not be reused across multiple template processing runs.
cmlenz
parents:
14
diff
changeset
|
856 |
69 | 857 stream = self.stream |
858 for filter_ in [self._eval, self._match, self._flatten] + self.filters: | |
35
3bc4778787c5
Simplify template processing model by removing dynamically generated `SUB` events.
cmlenz
parents:
31
diff
changeset
|
859 stream = filter_(iter(stream), ctxt) |
3bc4778787c5
Simplify template processing model by removing dynamically generated `SUB` events.
cmlenz
parents:
31
diff
changeset
|
860 return Stream(stream) |
17
ad63ad459524
Refactoring to address #6: all match templates are now processed by a single filter, which means that match templates added by included templates are properly applied. A side effect of this refactoring is that `Context` objects may not be reused across multiple template processing runs.
cmlenz
parents:
14
diff
changeset
|
861 |
ad63ad459524
Refactoring to address #6: all match templates are now processed by a single filter, which means that match templates added by included templates are properly applied. A side effect of this refactoring is that `Context` objects may not be reused across multiple template processing runs.
cmlenz
parents:
14
diff
changeset
|
862 def _eval(self, stream, ctxt=None): |
29
4b6cee37ce62
* Minor simplification of template directives: they no longer get passed the template instance and the position, as no directive was actually using
cmlenz
parents:
27
diff
changeset
|
863 """Internal stream filter that evaluates any expressions in `START` and |
4b6cee37ce62
* Minor simplification of template directives: they no longer get passed the template instance and the position, as no directive was actually using
cmlenz
parents:
27
diff
changeset
|
864 `TEXT` events. |
4b6cee37ce62
* Minor simplification of template directives: they no longer get passed the template instance and the position, as no directive was actually using
cmlenz
parents:
27
diff
changeset
|
865 """ |
111
8a4d9064f363
Some fixes and more unit tests for the XPath engine.
cmlenz
parents:
104
diff
changeset
|
866 filters = (self._eval, self._match) |
81
cc034182061e
Template expressions are now compiled to Python bytecode.
cmlenz
parents:
80
diff
changeset
|
867 |
17
ad63ad459524
Refactoring to address #6: all match templates are now processed by a single filter, which means that match templates added by included templates are properly applied. A side effect of this refactoring is that `Context` objects may not be reused across multiple template processing runs.
cmlenz
parents:
14
diff
changeset
|
868 for kind, data, pos in stream: |
ad63ad459524
Refactoring to address #6: all match templates are now processed by a single filter, which means that match templates added by included templates are properly applied. A side effect of this refactoring is that `Context` objects may not be reused across multiple template processing runs.
cmlenz
parents:
14
diff
changeset
|
869 |
81
cc034182061e
Template expressions are now compiled to Python bytecode.
cmlenz
parents:
80
diff
changeset
|
870 if kind is START and data[1]: |
17
ad63ad459524
Refactoring to address #6: all match templates are now processed by a single filter, which means that match templates added by included templates are properly applied. A side effect of this refactoring is that `Context` objects may not be reused across multiple template processing runs.
cmlenz
parents:
14
diff
changeset
|
871 # Attributes may still contain expressions in start tags at |
ad63ad459524
Refactoring to address #6: all match templates are now processed by a single filter, which means that match templates added by included templates are properly applied. A side effect of this refactoring is that `Context` objects may not be reused across multiple template processing runs.
cmlenz
parents:
14
diff
changeset
|
872 # this point, so do some evaluation |
ad63ad459524
Refactoring to address #6: all match templates are now processed by a single filter, which means that match templates added by included templates are properly applied. A side effect of this refactoring is that `Context` objects may not be reused across multiple template processing runs.
cmlenz
parents:
14
diff
changeset
|
873 tag, attrib = data |
ad63ad459524
Refactoring to address #6: all match templates are now processed by a single filter, which means that match templates added by included templates are properly applied. A side effect of this refactoring is that `Context` objects may not be reused across multiple template processing runs.
cmlenz
parents:
14
diff
changeset
|
874 new_attrib = [] |
ad63ad459524
Refactoring to address #6: all match templates are now processed by a single filter, which means that match templates added by included templates are properly applied. A side effect of this refactoring is that `Context` objects may not be reused across multiple template processing runs.
cmlenz
parents:
14
diff
changeset
|
875 for name, substream in attrib: |
ad63ad459524
Refactoring to address #6: all match templates are now processed by a single filter, which means that match templates added by included templates are properly applied. A side effect of this refactoring is that `Context` objects may not be reused across multiple template processing runs.
cmlenz
parents:
14
diff
changeset
|
876 if isinstance(substream, basestring): |
ad63ad459524
Refactoring to address #6: all match templates are now processed by a single filter, which means that match templates added by included templates are properly applied. A side effect of this refactoring is that `Context` objects may not be reused across multiple template processing runs.
cmlenz
parents:
14
diff
changeset
|
877 value = substream |
ad63ad459524
Refactoring to address #6: all match templates are now processed by a single filter, which means that match templates added by included templates are properly applied. A side effect of this refactoring is that `Context` objects may not be reused across multiple template processing runs.
cmlenz
parents:
14
diff
changeset
|
878 else: |
ad63ad459524
Refactoring to address #6: all match templates are now processed by a single filter, which means that match templates added by included templates are properly applied. A side effect of this refactoring is that `Context` objects may not be reused across multiple template processing runs.
cmlenz
parents:
14
diff
changeset
|
879 values = [] |
ad63ad459524
Refactoring to address #6: all match templates are now processed by a single filter, which means that match templates added by included templates are properly applied. A side effect of this refactoring is that `Context` objects may not be reused across multiple template processing runs.
cmlenz
parents:
14
diff
changeset
|
880 for subkind, subdata, subpos in substream: |
69 | 881 if subkind is EXPR: |
17
ad63ad459524
Refactoring to address #6: all match templates are now processed by a single filter, which means that match templates added by included templates are properly applied. A side effect of this refactoring is that `Context` objects may not be reused across multiple template processing runs.
cmlenz
parents:
14
diff
changeset
|
882 values.append(subdata.evaluate(ctxt)) |
ad63ad459524
Refactoring to address #6: all match templates are now processed by a single filter, which means that match templates added by included templates are properly applied. A side effect of this refactoring is that `Context` objects may not be reused across multiple template processing runs.
cmlenz
parents:
14
diff
changeset
|
883 else: |
ad63ad459524
Refactoring to address #6: all match templates are now processed by a single filter, which means that match templates added by included templates are properly applied. A side effect of this refactoring is that `Context` objects may not be reused across multiple template processing runs.
cmlenz
parents:
14
diff
changeset
|
884 values.append(subdata) |
48
06c642ba2b08
convert the result of expressions in attributes to strings so that values like ints are output correctly
mgood
parents:
44
diff
changeset
|
885 value = [unicode(x) for x in values if x is not None] |
17
ad63ad459524
Refactoring to address #6: all match templates are now processed by a single filter, which means that match templates added by included templates are properly applied. A side effect of this refactoring is that `Context` objects may not be reused across multiple template processing runs.
cmlenz
parents:
14
diff
changeset
|
886 if not value: |
ad63ad459524
Refactoring to address #6: all match templates are now processed by a single filter, which means that match templates added by included templates are properly applied. A side effect of this refactoring is that `Context` objects may not be reused across multiple template processing runs.
cmlenz
parents:
14
diff
changeset
|
887 continue |
23
00835401c8cc
Separate match and eval filters from the include and user-supplied filters.
cmlenz
parents:
22
diff
changeset
|
888 new_attrib.append((name, u''.join(value))) |
17
ad63ad459524
Refactoring to address #6: all match templates are now processed by a single filter, which means that match templates added by included templates are properly applied. A side effect of this refactoring is that `Context` objects may not be reused across multiple template processing runs.
cmlenz
parents:
14
diff
changeset
|
889 yield kind, (tag, Attributes(new_attrib)), pos |
ad63ad459524
Refactoring to address #6: all match templates are now processed by a single filter, which means that match templates added by included templates are properly applied. A side effect of this refactoring is that `Context` objects may not be reused across multiple template processing runs.
cmlenz
parents:
14
diff
changeset
|
890 |
69 | 891 elif kind is EXPR: |
17
ad63ad459524
Refactoring to address #6: all match templates are now processed by a single filter, which means that match templates added by included templates are properly applied. A side effect of this refactoring is that `Context` objects may not be reused across multiple template processing runs.
cmlenz
parents:
14
diff
changeset
|
892 result = data.evaluate(ctxt) |
ad63ad459524
Refactoring to address #6: all match templates are now processed by a single filter, which means that match templates added by included templates are properly applied. A side effect of this refactoring is that `Context` objects may not be reused across multiple template processing runs.
cmlenz
parents:
14
diff
changeset
|
893 if result is None: |
ad63ad459524
Refactoring to address #6: all match templates are now processed by a single filter, which means that match templates added by included templates are properly applied. A side effect of this refactoring is that `Context` objects may not be reused across multiple template processing runs.
cmlenz
parents:
14
diff
changeset
|
894 continue |
ad63ad459524
Refactoring to address #6: all match templates are now processed by a single filter, which means that match templates added by included templates are properly applied. A side effect of this refactoring is that `Context` objects may not be reused across multiple template processing runs.
cmlenz
parents:
14
diff
changeset
|
895 |
ad63ad459524
Refactoring to address #6: all match templates are now processed by a single filter, which means that match templates added by included templates are properly applied. A side effect of this refactoring is that `Context` objects may not be reused across multiple template processing runs.
cmlenz
parents:
14
diff
changeset
|
896 # First check for a string, otherwise the iterable test below |
ad63ad459524
Refactoring to address #6: all match templates are now processed by a single filter, which means that match templates added by included templates are properly applied. A side effect of this refactoring is that `Context` objects may not be reused across multiple template processing runs.
cmlenz
parents:
14
diff
changeset
|
897 # succeeds, and the string will be chopped up into individual |
ad63ad459524
Refactoring to address #6: all match templates are now processed by a single filter, which means that match templates added by included templates are properly applied. A side effect of this refactoring is that `Context` objects may not be reused across multiple template processing runs.
cmlenz
parents:
14
diff
changeset
|
898 # characters |
ad63ad459524
Refactoring to address #6: all match templates are now processed by a single filter, which means that match templates added by included templates are properly applied. A side effect of this refactoring is that `Context` objects may not be reused across multiple template processing runs.
cmlenz
parents:
14
diff
changeset
|
899 if isinstance(result, basestring): |
69 | 900 yield TEXT, result, pos |
17
ad63ad459524
Refactoring to address #6: all match templates are now processed by a single filter, which means that match templates added by included templates are properly applied. A side effect of this refactoring is that `Context` objects may not be reused across multiple template processing runs.
cmlenz
parents:
14
diff
changeset
|
901 else: |
ad63ad459524
Refactoring to address #6: all match templates are now processed by a single filter, which means that match templates added by included templates are properly applied. A side effect of this refactoring is that `Context` objects may not be reused across multiple template processing runs.
cmlenz
parents:
14
diff
changeset
|
902 # Test if the expression evaluated to an iterable, in which |
ad63ad459524
Refactoring to address #6: all match templates are now processed by a single filter, which means that match templates added by included templates are properly applied. A side effect of this refactoring is that `Context` objects may not be reused across multiple template processing runs.
cmlenz
parents:
14
diff
changeset
|
903 # case we yield the individual items |
ad63ad459524
Refactoring to address #6: all match templates are now processed by a single filter, which means that match templates added by included templates are properly applied. A side effect of this refactoring is that `Context` objects may not be reused across multiple template processing runs.
cmlenz
parents:
14
diff
changeset
|
904 try: |
111
8a4d9064f363
Some fixes and more unit tests for the XPath engine.
cmlenz
parents:
104
diff
changeset
|
905 substream = _ensure(result) |
81
cc034182061e
Template expressions are now compiled to Python bytecode.
cmlenz
parents:
80
diff
changeset
|
906 for filter_ in filters: |
77
f1aa49c759b2
* Simplify implementation of the individual XPath tests (use closures instead of callable classes)
cmlenz
parents:
75
diff
changeset
|
907 substream = filter_(substream, ctxt) |
f1aa49c759b2
* Simplify implementation of the individual XPath tests (use closures instead of callable classes)
cmlenz
parents:
75
diff
changeset
|
908 for event in substream: |
35
3bc4778787c5
Simplify template processing model by removing dynamically generated `SUB` events.
cmlenz
parents:
31
diff
changeset
|
909 yield event |
17
ad63ad459524
Refactoring to address #6: all match templates are now processed by a single filter, which means that match templates added by included templates are properly applied. A side effect of this refactoring is that `Context` objects may not be reused across multiple template processing runs.
cmlenz
parents:
14
diff
changeset
|
910 except TypeError: |
ad63ad459524
Refactoring to address #6: all match templates are now processed by a single filter, which means that match templates added by included templates are properly applied. A side effect of this refactoring is that `Context` objects may not be reused across multiple template processing runs.
cmlenz
parents:
14
diff
changeset
|
911 # Neither a string nor an iterable, so just pass it |
ad63ad459524
Refactoring to address #6: all match templates are now processed by a single filter, which means that match templates added by included templates are properly applied. A side effect of this refactoring is that `Context` objects may not be reused across multiple template processing runs.
cmlenz
parents:
14
diff
changeset
|
912 # through |
69 | 913 yield TEXT, unicode(result), pos |
17
ad63ad459524
Refactoring to address #6: all match templates are now processed by a single filter, which means that match templates added by included templates are properly applied. A side effect of this refactoring is that `Context` objects may not be reused across multiple template processing runs.
cmlenz
parents:
14
diff
changeset
|
914 |
ad63ad459524
Refactoring to address #6: all match templates are now processed by a single filter, which means that match templates added by included templates are properly applied. A side effect of this refactoring is that `Context` objects may not be reused across multiple template processing runs.
cmlenz
parents:
14
diff
changeset
|
915 else: |
ad63ad459524
Refactoring to address #6: all match templates are now processed by a single filter, which means that match templates added by included templates are properly applied. A side effect of this refactoring is that `Context` objects may not be reused across multiple template processing runs.
cmlenz
parents:
14
diff
changeset
|
916 yield kind, data, pos |
ad63ad459524
Refactoring to address #6: all match templates are now processed by a single filter, which means that match templates added by included templates are properly applied. A side effect of this refactoring is that `Context` objects may not be reused across multiple template processing runs.
cmlenz
parents:
14
diff
changeset
|
917 |
23
00835401c8cc
Separate match and eval filters from the include and user-supplied filters.
cmlenz
parents:
22
diff
changeset
|
918 def _flatten(self, stream, ctxt=None): |
29
4b6cee37ce62
* Minor simplification of template directives: they no longer get passed the template instance and the position, as no directive was actually using
cmlenz
parents:
27
diff
changeset
|
919 """Internal stream filter that expands `SUB` events in the stream.""" |
81
cc034182061e
Template expressions are now compiled to Python bytecode.
cmlenz
parents:
80
diff
changeset
|
920 for kind, data, pos in stream: |
cc034182061e
Template expressions are now compiled to Python bytecode.
cmlenz
parents:
80
diff
changeset
|
921 if kind is SUB: |
cc034182061e
Template expressions are now compiled to Python bytecode.
cmlenz
parents:
80
diff
changeset
|
922 # This event is a list of directives and a list of nested |
cc034182061e
Template expressions are now compiled to Python bytecode.
cmlenz
parents:
80
diff
changeset
|
923 # events to which those directives should be applied |
cc034182061e
Template expressions are now compiled to Python bytecode.
cmlenz
parents:
80
diff
changeset
|
924 directives, substream = data |
cc034182061e
Template expressions are now compiled to Python bytecode.
cmlenz
parents:
80
diff
changeset
|
925 substream = _apply_directives(substream, ctxt, directives) |
cc034182061e
Template expressions are now compiled to Python bytecode.
cmlenz
parents:
80
diff
changeset
|
926 for filter_ in (self._eval, self._match, self._flatten): |
cc034182061e
Template expressions are now compiled to Python bytecode.
cmlenz
parents:
80
diff
changeset
|
927 substream = filter_(substream, ctxt) |
cc034182061e
Template expressions are now compiled to Python bytecode.
cmlenz
parents:
80
diff
changeset
|
928 for event in substream: |
cc034182061e
Template expressions are now compiled to Python bytecode.
cmlenz
parents:
80
diff
changeset
|
929 yield event |
cc034182061e
Template expressions are now compiled to Python bytecode.
cmlenz
parents:
80
diff
changeset
|
930 else: |
cc034182061e
Template expressions are now compiled to Python bytecode.
cmlenz
parents:
80
diff
changeset
|
931 yield kind, data, pos |
17
ad63ad459524
Refactoring to address #6: all match templates are now processed by a single filter, which means that match templates added by included templates are properly applied. A side effect of this refactoring is that `Context` objects may not be reused across multiple template processing runs.
cmlenz
parents:
14
diff
changeset
|
932 |
36
57d607f25484
Fix for #7: match templates no longer process their own output.
cmlenz
parents:
35
diff
changeset
|
933 def _match(self, stream, ctxt=None, match_templates=None): |
29
4b6cee37ce62
* Minor simplification of template directives: they no longer get passed the template instance and the position, as no directive was actually using
cmlenz
parents:
27
diff
changeset
|
934 """Internal stream filter that applies any defined match templates |
4b6cee37ce62
* Minor simplification of template directives: they no longer get passed the template instance and the position, as no directive was actually using
cmlenz
parents:
27
diff
changeset
|
935 to the stream. |
4b6cee37ce62
* Minor simplification of template directives: they no longer get passed the template instance and the position, as no directive was actually using
cmlenz
parents:
27
diff
changeset
|
936 """ |
36
57d607f25484
Fix for #7: match templates no longer process their own output.
cmlenz
parents:
35
diff
changeset
|
937 if match_templates is None: |
57d607f25484
Fix for #7: match templates no longer process their own output.
cmlenz
parents:
35
diff
changeset
|
938 match_templates = ctxt._match_templates |
57d607f25484
Fix for #7: match templates no longer process their own output.
cmlenz
parents:
35
diff
changeset
|
939 |
17
ad63ad459524
Refactoring to address #6: all match templates are now processed by a single filter, which means that match templates added by included templates are properly applied. A side effect of this refactoring is that `Context` objects may not be reused across multiple template processing runs.
cmlenz
parents:
14
diff
changeset
|
940 for kind, data, pos in stream: |
ad63ad459524
Refactoring to address #6: all match templates are now processed by a single filter, which means that match templates added by included templates are properly applied. A side effect of this refactoring is that `Context` objects may not be reused across multiple template processing runs.
cmlenz
parents:
14
diff
changeset
|
941 |
ad63ad459524
Refactoring to address #6: all match templates are now processed by a single filter, which means that match templates added by included templates are properly applied. A side effect of this refactoring is that `Context` objects may not be reused across multiple template processing runs.
cmlenz
parents:
14
diff
changeset
|
942 # We (currently) only care about start and end events for matching |
ad63ad459524
Refactoring to address #6: all match templates are now processed by a single filter, which means that match templates added by included templates are properly applied. A side effect of this refactoring is that `Context` objects may not be reused across multiple template processing runs.
cmlenz
parents:
14
diff
changeset
|
943 # We might care about namespace events in the future, though |
92
3b75c6730b29
More performance improvements... this time for whitespace normalization and template loops.
cmlenz
parents:
90
diff
changeset
|
944 if not match_templates or kind not in (START, END): |
17
ad63ad459524
Refactoring to address #6: all match templates are now processed by a single filter, which means that match templates added by included templates are properly applied. A side effect of this refactoring is that `Context` objects may not be reused across multiple template processing runs.
cmlenz
parents:
14
diff
changeset
|
945 yield kind, data, pos |
ad63ad459524
Refactoring to address #6: all match templates are now processed by a single filter, which means that match templates added by included templates are properly applied. A side effect of this refactoring is that `Context` objects may not be reused across multiple template processing runs.
cmlenz
parents:
14
diff
changeset
|
946 continue |
ad63ad459524
Refactoring to address #6: all match templates are now processed by a single filter, which means that match templates added by included templates are properly applied. A side effect of this refactoring is that `Context` objects may not be reused across multiple template processing runs.
cmlenz
parents:
14
diff
changeset
|
947 |
50
a053ffb834cb
Fix the way multiple directives are applied to a single `SUB` in many cases by making the directives themselves responsible for applying any remaining directives.
cmlenz
parents:
48
diff
changeset
|
948 for idx, (test, path, template, directives) in \ |
a053ffb834cb
Fix the way multiple directives are applied to a single `SUB` in many cases by making the directives themselves responsible for applying any remaining directives.
cmlenz
parents:
48
diff
changeset
|
949 enumerate(match_templates): |
17
ad63ad459524
Refactoring to address #6: all match templates are now processed by a single filter, which means that match templates added by included templates are properly applied. A side effect of this refactoring is that `Context` objects may not be reused across multiple template processing runs.
cmlenz
parents:
14
diff
changeset
|
950 |
116 | 951 if test(kind, data, pos) is True: |
17
ad63ad459524
Refactoring to address #6: all match templates are now processed by a single filter, which means that match templates added by included templates are properly applied. A side effect of this refactoring is that `Context` objects may not be reused across multiple template processing runs.
cmlenz
parents:
14
diff
changeset
|
952 # Consume and store all events until an end event |
ad63ad459524
Refactoring to address #6: all match templates are now processed by a single filter, which means that match templates added by included templates are properly applied. A side effect of this refactoring is that `Context` objects may not be reused across multiple template processing runs.
cmlenz
parents:
14
diff
changeset
|
953 # corresponding to this start event is encountered |
ad63ad459524
Refactoring to address #6: all match templates are now processed by a single filter, which means that match templates added by included templates are properly applied. A side effect of this refactoring is that `Context` objects may not be reused across multiple template processing runs.
cmlenz
parents:
14
diff
changeset
|
954 content = [(kind, data, pos)] |
ad63ad459524
Refactoring to address #6: all match templates are now processed by a single filter, which means that match templates added by included templates are properly applied. A side effect of this refactoring is that `Context` objects may not be reused across multiple template processing runs.
cmlenz
parents:
14
diff
changeset
|
955 depth = 1 |
ad63ad459524
Refactoring to address #6: all match templates are now processed by a single filter, which means that match templates added by included templates are properly applied. A side effect of this refactoring is that `Context` objects may not be reused across multiple template processing runs.
cmlenz
parents:
14
diff
changeset
|
956 while depth > 0: |
73 | 957 kind, data, pos = stream.next() |
958 if kind is START: | |
959 depth += 1 | |
960 elif kind is END: | |
961 depth -= 1 | |
962 content.append((kind, data, pos)) | |
963 test(kind, data, pos) | |
17
ad63ad459524
Refactoring to address #6: all match templates are now processed by a single filter, which means that match templates added by included templates are properly applied. A side effect of this refactoring is that `Context` objects may not be reused across multiple template processing runs.
cmlenz
parents:
14
diff
changeset
|
964 |
23
00835401c8cc
Separate match and eval filters from the include and user-supplied filters.
cmlenz
parents:
22
diff
changeset
|
965 content = list(self._flatten(content, ctxt)) |
95
7d6426183a90
Improve performance of push/pop operations on the context.
cmlenz
parents:
93
diff
changeset
|
966 select = lambda path: Stream(content).select(path) |
7d6426183a90
Improve performance of push/pop operations on the context.
cmlenz
parents:
93
diff
changeset
|
967 ctxt.push(dict(select=select)) |
36
57d607f25484
Fix for #7: match templates no longer process their own output.
cmlenz
parents:
35
diff
changeset
|
968 |
78
fa4bafcbe4c7
Minor improvements to how directives are applied in template processing.
cmlenz
parents:
77
diff
changeset
|
969 template = _apply_directives(template, ctxt, directives) |
69 | 970 for event in self._match(self._eval(template, ctxt), |
971 ctxt, match_templates[:idx] + | |
972 match_templates[idx + 1:]): | |
35
3bc4778787c5
Simplify template processing model by removing dynamically generated `SUB` events.
cmlenz
parents:
31
diff
changeset
|
973 yield event |
116 | 974 |
35
3bc4778787c5
Simplify template processing model by removing dynamically generated `SUB` events.
cmlenz
parents:
31
diff
changeset
|
975 ctxt.pop() |
17
ad63ad459524
Refactoring to address #6: all match templates are now processed by a single filter, which means that match templates added by included templates are properly applied. A side effect of this refactoring is that `Context` objects may not be reused across multiple template processing runs.
cmlenz
parents:
14
diff
changeset
|
976 break |
69 | 977 |
978 else: # no matches | |
17
ad63ad459524
Refactoring to address #6: all match templates are now processed by a single filter, which means that match templates added by included templates are properly applied. A side effect of this refactoring is that `Context` objects may not be reused across multiple template processing runs.
cmlenz
parents:
14
diff
changeset
|
979 yield kind, data, pos |
ad63ad459524
Refactoring to address #6: all match templates are now processed by a single filter, which means that match templates added by included templates are properly applied. A side effect of this refactoring is that `Context` objects may not be reused across multiple template processing runs.
cmlenz
parents:
14
diff
changeset
|
980 |
1 | 981 |
69 | 982 EXPR = Template.EXPR |
983 SUB = Template.SUB | |
984 | |
985 | |
1 | 986 class TemplateLoader(object): |
987 """Responsible for loading templates from files on the specified search | |
988 path. | |
989 | |
990 >>> import tempfile | |
991 >>> fd, path = tempfile.mkstemp(suffix='.html', prefix='template') | |
992 >>> os.write(fd, '<p>$var</p>') | |
993 11 | |
994 >>> os.close(fd) | |
995 | |
996 The template loader accepts a list of directory paths that are then used | |
997 when searching for template files, in the given order: | |
998 | |
999 >>> loader = TemplateLoader([os.path.dirname(path)]) | |
1000 | |
1001 The `load()` method first checks the template cache whether the requested | |
1002 template has already been loaded. If not, it attempts to locate the | |
1003 template file, and returns the corresponding `Template` object: | |
1004 | |
1005 >>> template = loader.load(os.path.basename(path)) | |
1006 >>> isinstance(template, Template) | |
1007 True | |
1008 | |
1009 Template instances are cached: requesting a template with the same name | |
1010 results in the same instance being returned: | |
1011 | |
1012 >>> loader.load(os.path.basename(path)) is template | |
1013 True | |
152
064ba1078f92
Add some tests for relative template includes (see #27).
cmlenz
parents:
150
diff
changeset
|
1014 |
064ba1078f92
Add some tests for relative template includes (see #27).
cmlenz
parents:
150
diff
changeset
|
1015 >>> os.remove(path) |
1 | 1016 """ |
1017 def __init__(self, search_path=None, auto_reload=False): | |
1018 """Create the template laoder. | |
1019 | |
1020 @param search_path: a list of absolute path names that should be | |
1021 searched for template files | |
1022 @param auto_reload: whether to check the last modification time of | |
1023 template files, and reload them if they have changed | |
1024 """ | |
1025 self.search_path = search_path | |
1026 if self.search_path is None: | |
1027 self.search_path = [] | |
1028 self.auto_reload = auto_reload | |
1029 self._cache = {} | |
1030 self._mtime = {} | |
1031 | |
21
eca77129518a
* Include paths are now interpreted relative to the path of the including template. Closes #3.
cmlenz
parents:
18
diff
changeset
|
1032 def load(self, filename, relative_to=None): |
1 | 1033 """Load the template with the given name. |
1034 | |
22
31b13ddf9f53
Fix for the template engine plugin: the search path is now ignored if the requested template path is absolute.
cmlenz
parents:
21
diff
changeset
|
1035 If the `filename` parameter is relative, this method searches the search |
31b13ddf9f53
Fix for the template engine plugin: the search path is now ignored if the requested template path is absolute.
cmlenz
parents:
21
diff
changeset
|
1036 path trying to locate a template matching the given name. If the file |
31b13ddf9f53
Fix for the template engine plugin: the search path is now ignored if the requested template path is absolute.
cmlenz
parents:
21
diff
changeset
|
1037 name is an absolute path, the search path is not bypassed. |
1 | 1038 |
22
31b13ddf9f53
Fix for the template engine plugin: the search path is now ignored if the requested template path is absolute.
cmlenz
parents:
21
diff
changeset
|
1039 If requested template is not found, a `TemplateNotFound` exception is |
31b13ddf9f53
Fix for the template engine plugin: the search path is now ignored if the requested template path is absolute.
cmlenz
parents:
21
diff
changeset
|
1040 raised. Otherwise, a `Template` object is returned that represents the |
31b13ddf9f53
Fix for the template engine plugin: the search path is now ignored if the requested template path is absolute.
cmlenz
parents:
21
diff
changeset
|
1041 parsed template. |
31b13ddf9f53
Fix for the template engine plugin: the search path is now ignored if the requested template path is absolute.
cmlenz
parents:
21
diff
changeset
|
1042 |
31b13ddf9f53
Fix for the template engine plugin: the search path is now ignored if the requested template path is absolute.
cmlenz
parents:
21
diff
changeset
|
1043 Template instances are cached to avoid having to parse the same |
31b13ddf9f53
Fix for the template engine plugin: the search path is now ignored if the requested template path is absolute.
cmlenz
parents:
21
diff
changeset
|
1044 template file more than once. Thus, subsequent calls of this method |
31b13ddf9f53
Fix for the template engine plugin: the search path is now ignored if the requested template path is absolute.
cmlenz
parents:
21
diff
changeset
|
1045 with the same template file name will return the same `Template` |
31b13ddf9f53
Fix for the template engine plugin: the search path is now ignored if the requested template path is absolute.
cmlenz
parents:
21
diff
changeset
|
1046 object (unless the `auto_reload` option is enabled and the file was |
31b13ddf9f53
Fix for the template engine plugin: the search path is now ignored if the requested template path is absolute.
cmlenz
parents:
21
diff
changeset
|
1047 changed since the last parse.) |
1 | 1048 |
21
eca77129518a
* Include paths are now interpreted relative to the path of the including template. Closes #3.
cmlenz
parents:
18
diff
changeset
|
1049 If the `relative_to` parameter is provided, the `filename` is |
eca77129518a
* Include paths are now interpreted relative to the path of the including template. Closes #3.
cmlenz
parents:
18
diff
changeset
|
1050 interpreted as being relative to that path. |
eca77129518a
* Include paths are now interpreted relative to the path of the including template. Closes #3.
cmlenz
parents:
18
diff
changeset
|
1051 |
1 | 1052 @param filename: the relative path of the template file to load |
21
eca77129518a
* Include paths are now interpreted relative to the path of the including template. Closes #3.
cmlenz
parents:
18
diff
changeset
|
1053 @param relative_to: the filename of the template from which the new |
eca77129518a
* Include paths are now interpreted relative to the path of the including template. Closes #3.
cmlenz
parents:
18
diff
changeset
|
1054 template is being loaded, or `None` if the template is being loaded |
eca77129518a
* Include paths are now interpreted relative to the path of the including template. Closes #3.
cmlenz
parents:
18
diff
changeset
|
1055 directly |
1 | 1056 """ |
69 | 1057 from markup.filters import IncludeFilter |
1058 | |
21
eca77129518a
* Include paths are now interpreted relative to the path of the including template. Closes #3.
cmlenz
parents:
18
diff
changeset
|
1059 if relative_to: |
153 | 1060 filename = os.path.join(os.path.dirname(relative_to), filename) |
1 | 1061 filename = os.path.normpath(filename) |
22
31b13ddf9f53
Fix for the template engine plugin: the search path is now ignored if the requested template path is absolute.
cmlenz
parents:
21
diff
changeset
|
1062 |
31b13ddf9f53
Fix for the template engine plugin: the search path is now ignored if the requested template path is absolute.
cmlenz
parents:
21
diff
changeset
|
1063 # First check the cache to avoid reparsing the same file |
1 | 1064 try: |
1065 tmpl = self._cache[filename] | |
1066 if not self.auto_reload or \ | |
21
eca77129518a
* Include paths are now interpreted relative to the path of the including template. Closes #3.
cmlenz
parents:
18
diff
changeset
|
1067 os.path.getmtime(tmpl.filepath) == self._mtime[filename]: |
1 | 1068 return tmpl |
1069 except KeyError: | |
1070 pass | |
22
31b13ddf9f53
Fix for the template engine plugin: the search path is now ignored if the requested template path is absolute.
cmlenz
parents:
21
diff
changeset
|
1071 |
31b13ddf9f53
Fix for the template engine plugin: the search path is now ignored if the requested template path is absolute.
cmlenz
parents:
21
diff
changeset
|
1072 # Bypass the search path if the filename is absolute |
31b13ddf9f53
Fix for the template engine plugin: the search path is now ignored if the requested template path is absolute.
cmlenz
parents:
21
diff
changeset
|
1073 search_path = self.search_path |
31b13ddf9f53
Fix for the template engine plugin: the search path is now ignored if the requested template path is absolute.
cmlenz
parents:
21
diff
changeset
|
1074 if os.path.isabs(filename): |
31b13ddf9f53
Fix for the template engine plugin: the search path is now ignored if the requested template path is absolute.
cmlenz
parents:
21
diff
changeset
|
1075 search_path = [os.path.dirname(filename)] |
31b13ddf9f53
Fix for the template engine plugin: the search path is now ignored if the requested template path is absolute.
cmlenz
parents:
21
diff
changeset
|
1076 |
31b13ddf9f53
Fix for the template engine plugin: the search path is now ignored if the requested template path is absolute.
cmlenz
parents:
21
diff
changeset
|
1077 for dirname in search_path: |
1 | 1078 filepath = os.path.join(dirname, filename) |
1079 try: | |
133
b9a0031d4bbb
Minor cleanup and performance improvement for the builder module.
cmlenz
parents:
120
diff
changeset
|
1080 fileobj = open(filepath, 'U') |
1 | 1081 try: |
21
eca77129518a
* Include paths are now interpreted relative to the path of the including template. Closes #3.
cmlenz
parents:
18
diff
changeset
|
1082 tmpl = Template(fileobj, basedir=dirname, filename=filename) |
17
ad63ad459524
Refactoring to address #6: all match templates are now processed by a single filter, which means that match templates added by included templates are properly applied. A side effect of this refactoring is that `Context` objects may not be reused across multiple template processing runs.
cmlenz
parents:
14
diff
changeset
|
1083 tmpl.filters.append(IncludeFilter(self)) |
1 | 1084 finally: |
1085 fileobj.close() | |
1086 self._cache[filename] = tmpl | |
1087 self._mtime[filename] = os.path.getmtime(filepath) | |
1088 return tmpl | |
1089 except IOError: | |
1090 continue | |
1091 raise TemplateNotFound(filename, self.search_path) |