Mercurial > genshi > mirror
annotate markup/template.py @ 152:cdb2a1f930e2 trunk
Add some tests for relative template includes (see #27).
author | cmlenz |
---|---|
date | Wed, 16 Aug 2006 10:25:15 +0000 |
parents | d35688d16831 |
children | fc2ff46d1ae3 |
rev | line source |
---|---|
1 | 1 # -*- coding: utf-8 -*- |
2 # | |
66
59eb24184e9c
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
59eb24184e9c
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
59eb24184e9c
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
dd73921530e8
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: |
dd73921530e8
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 |
dd73921530e8
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: |
dd73921530e8
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): |
dd73921530e8
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) |
dd73921530e8
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 | |
21
b4d17897d053
* Include paths are now interpreted relative to the path of the including template. Closes #3.
cmlenz
parents:
18
diff
changeset
|
24 import posixpath |
1 | 25 import re |
26 from StringIO import StringIO | |
27 | |
145
47bbd9d2a5af
* 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 Attributes, Namespace, Stream, StreamEventKind, _ensure |
47bbd9d2a5af
* Fix error in expression evaluation when the expression evaluates to an iterable that does not produce event tuples.
cmlenz
parents:
140
diff
changeset
|
29 from markup.core import START, END, START_NS, END_NS, TEXT, COMMENT |
1 | 30 from markup.eval import Expression |
69 | 31 from markup.input import XMLParser |
14
c7d33e0c9839
The `<py:match>` directive now protects itself against simple infinite recursion (see MatchDirective), while still allowing recursion in general.
cmlenz
parents:
13
diff
changeset
|
32 from markup.path import Path |
1 | 33 |
150
d35688d16831
Removed to many classes from the `__all__` list of `markup.template` in [191].
cmlenz
parents:
149
diff
changeset
|
34 __all__ = ['BadDirectiveError', 'TemplateError', 'TemplateSyntaxError', |
d35688d16831
Removed to many classes from the `__all__` list of `markup.template` in [191].
cmlenz
parents:
149
diff
changeset
|
35 'TemplateNotFound', 'Template', 'TemplateLoader'] |
1 | 36 |
37 | |
38 class TemplateError(Exception): | |
39 """Base exception class for errors related to template processing.""" | |
40 | |
41 | |
42 class TemplateSyntaxError(TemplateError): | |
43 """Exception raised when an expression in a template causes a Python syntax | |
44 error.""" | |
45 | |
46 def __init__(self, message, filename='<string>', lineno=-1, offset=-1): | |
47 if isinstance(message, SyntaxError) and message.lineno is not None: | |
48 message = str(message).replace(' (line %d)' % message.lineno, '') | |
80 | 49 message = '%s (%s, line %d)' % (message, filename, lineno) |
1 | 50 TemplateError.__init__(self, message) |
51 self.filename = filename | |
52 self.lineno = lineno | |
53 self.offset = offset | |
54 | |
55 | |
56 class BadDirectiveError(TemplateSyntaxError): | |
57 """Exception raised when an unknown directive is encountered when parsing | |
58 a template. | |
59 | |
60 An unknown directive is any attribute using the namespace for directives, | |
61 with a local name that doesn't match any registered directive. | |
62 """ | |
63 | |
64 def __init__(self, name, filename='<string>', lineno=-1): | |
80 | 65 msg = 'bad directive "%s" (%s, line %d)' % (name.localname, filename, |
66 lineno) | |
67 TemplateSyntaxError.__init__(self, msg, filename, lineno) | |
1 | 68 |
69 | |
70 class TemplateNotFound(TemplateError): | |
71 """Exception raised when a specific template file could not be found.""" | |
72 | |
73 def __init__(self, name, search_path): | |
74 TemplateError.__init__(self, 'Template "%s" not found' % name) | |
75 self.search_path = search_path | |
76 | |
77 | |
78 class Context(object): | |
95
2fe86a99947f
Improve performance of push/pop operations on the context.
cmlenz
parents:
93
diff
changeset
|
79 """Container for template input data. |
1 | 80 |
95
2fe86a99947f
Improve performance of push/pop operations on the context.
cmlenz
parents:
93
diff
changeset
|
81 A context provides a stack of scopes (represented by dictionaries). |
2fe86a99947f
Improve performance of push/pop operations on the context.
cmlenz
parents:
93
diff
changeset
|
82 |
2fe86a99947f
Improve performance of push/pop operations on the context.
cmlenz
parents:
93
diff
changeset
|
83 Template directives such as loops can push a new scope on the stack with |
2fe86a99947f
Improve performance of push/pop operations on the context.
cmlenz
parents:
93
diff
changeset
|
84 data that should only be available inside the loop. When the loop |
2fe86a99947f
Improve performance of push/pop operations on the context.
cmlenz
parents:
93
diff
changeset
|
85 terminates, that scope can get popped off the stack again. |
1 | 86 |
87 >>> ctxt = Context(one='foo', other=1) | |
88 >>> ctxt.get('one') | |
89 'foo' | |
90 >>> ctxt.get('other') | |
91 1 | |
95
2fe86a99947f
Improve performance of push/pop operations on the context.
cmlenz
parents:
93
diff
changeset
|
92 >>> ctxt.push(dict(one='frost')) |
1 | 93 >>> ctxt.get('one') |
94 'frost' | |
95 >>> ctxt.get('other') | |
96 1 | |
97 >>> ctxt.pop() | |
95
2fe86a99947f
Improve performance of push/pop operations on the context.
cmlenz
parents:
93
diff
changeset
|
98 {'one': 'frost'} |
1 | 99 >>> ctxt.get('one') |
100 'foo' | |
101 """ | |
102 | |
103 def __init__(self, **data): | |
70
dd73921530e8
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
|
104 self.frames = deque([data]) |
95
2fe86a99947f
Improve performance of push/pop operations on the context.
cmlenz
parents:
93
diff
changeset
|
105 self.pop = self.frames.popleft |
2fe86a99947f
Improve performance of push/pop operations on the context.
cmlenz
parents:
93
diff
changeset
|
106 self.push = self.frames.appendleft |
1 | 107 |
108 def __repr__(self): | |
70
dd73921530e8
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
2fe86a99947f
Improve performance of push/pop operations on the context.
cmlenz
parents:
93
diff
changeset
|
112 """Set a variable in the current scope.""" |
70
dd73921530e8
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
2fe86a99947f
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 |
2fe86a99947f
Improve performance of push/pop operations on the context.
cmlenz
parents:
93
diff
changeset
|
117 upward. |
29
ab8703fa68b8
* 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
dd73921530e8
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
dd73921530e8
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
2fe86a99947f
Improve performance of push/pop operations on the context.
cmlenz
parents:
93
diff
changeset
|
124 def push(self, data): |
2fe86a99947f
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
2fe86a99947f
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
d60486018004
Template expressions are now compiled to Python bytecode.
cmlenz
parents:
80
diff
changeset
|
151 def __init__(self, value, filename=None, lineno=-1, offset=-1): |
d60486018004
Template expressions are now compiled to Python bytecode.
cmlenz
parents:
80
diff
changeset
|
152 try: |
d60486018004
Template expressions are now compiled to Python bytecode.
cmlenz
parents:
80
diff
changeset
|
153 self.expr = value and Expression(value, filename, lineno) or None |
d60486018004
Template expressions are now compiled to Python bytecode.
cmlenz
parents:
80
diff
changeset
|
154 except SyntaxError, err: |
d60486018004
Template expressions are now compiled to Python bytecode.
cmlenz
parents:
80
diff
changeset
|
155 raise TemplateSyntaxError(err, filename, lineno, |
d60486018004
Template expressions are now compiled to Python bytecode.
cmlenz
parents:
80
diff
changeset
|
156 offset + (err.offset or 0)) |
1 | 157 |
53
512eb72dbb19
* 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
46fed54f23cd
Minor improvements to how directives are applied in template processing.
cmlenz
parents:
77
diff
changeset
|
167 |
46fed54f23cd
Minor improvements to how directives are applied in template processing.
cmlenz
parents:
77
diff
changeset
|
168 def _apply_directives(stream, ctxt, directives): |
46fed54f23cd
Minor improvements to how directives are applied in template processing.
cmlenz
parents:
77
diff
changeset
|
169 if directives: |
46fed54f23cd
Minor improvements to how directives are applied in template processing.
cmlenz
parents:
77
diff
changeset
|
170 stream = directives[0](iter(stream), ctxt, directives[1:]) |
46fed54f23cd
Minor improvements to how directives are applied in template processing.
cmlenz
parents:
77
diff
changeset
|
171 return stream |
53
512eb72dbb19
* 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
537f819c547b
`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
537f819c547b
`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
d3842cd76e92
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__ = [] |
d3842cd76e92
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
512eb72dbb19
* 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): |
512eb72dbb19
* 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(): |
512eb72dbb19
* 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() |
512eb72dbb19
* 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) |
512eb72dbb19
* 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: |
512eb72dbb19
* 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
f5ec6d4a61e4
* Simplify implementation of the individual XPath tests (use closures instead of callable classes)
cmlenz
parents:
75
diff
changeset
|
204 if isinstance(attrs, Stream): |
f5ec6d4a61e4
* Simplify implementation of the individual XPath tests (use closures instead of callable classes)
cmlenz
parents:
75
diff
changeset
|
205 try: |
f5ec6d4a61e4
* Simplify implementation of the individual XPath tests (use closures instead of callable classes)
cmlenz
parents:
75
diff
changeset
|
206 attrs = iter(attrs).next() |
f5ec6d4a61e4
* Simplify implementation of the individual XPath tests (use closures instead of callable classes)
cmlenz
parents:
75
diff
changeset
|
207 except StopIteration: |
f5ec6d4a61e4
* Simplify implementation of the individual XPath tests (use closures instead of callable classes)
cmlenz
parents:
75
diff
changeset
|
208 attrs = [] |
f5ec6d4a61e4
* 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
512eb72dbb19
* 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() |
512eb72dbb19
* 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: |
512eb72dbb19
* 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: |
512eb72dbb19
* 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) |
512eb72dbb19
* 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: |
512eb72dbb19
* 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()) |
512eb72dbb19
* 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 |
512eb72dbb19
* 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: |
512eb72dbb19
* 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
f5ec6d4a61e4
* Simplify implementation of the individual XPath tests (use closures instead of callable classes)
cmlenz
parents:
75
diff
changeset
|
219 |
78
46fed54f23cd
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
537f819c547b
`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
d3842cd76e92
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__ = [] |
d3842cd76e92
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 |
d3842cd76e92
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
512eb72dbb19
* 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
d3842cd76e92
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
d3842cd76e92
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
d3842cd76e92
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() |
d3842cd76e92
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: |
d3842cd76e92
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 |
d3842cd76e92
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: |
d3842cd76e92
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
1b874f032bde
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
46fed54f23cd
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
c835e81c50af
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
537f819c547b
`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
c835e81c50af
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
c835e81c50af
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 |
c835e81c50af
When an expression evaluates to a callable, it is called implicitly.
cmlenz
parents:
89
diff
changeset
|
279 both when defining and when calling it: |
c835e81c50af
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
c835e81c50af
When an expression evaluates to a callable, it is called implicitly.
cmlenz
parents:
89
diff
changeset
|
282 ... <p py:def="helloworld" class="message"> |
c835e81c50af
When an expression evaluates to a callable, it is called implicitly.
cmlenz
parents:
89
diff
changeset
|
283 ... Hello, world! |
1 | 284 ... </p> |
90
c835e81c50af
When an expression evaluates to a callable, it is called implicitly.
cmlenz
parents:
89
diff
changeset
|
285 ... ${helloworld} |
1 | 286 ... </div>''') |
149
537f819c547b
`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
c835e81c50af
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 """ | |
50
d3842cd76e92
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
|
294 __slots__ = ['name', 'args', 'defaults', 'stream', 'directives'] |
1 | 295 |
65
b3fdf93057ab
Support the use of directives as elements to reduce the need for using `py:strip`.
cmlenz
parents:
61
diff
changeset
|
296 ATTRIBUTE = 'function' |
b3fdf93057ab
Support the use of directives as elements to reduce the need for using `py:strip`.
cmlenz
parents:
61
diff
changeset
|
297 |
81
d60486018004
Template expressions are now compiled to Python bytecode.
cmlenz
parents:
80
diff
changeset
|
298 def __init__(self, args, filename=None, lineno=-1, offset=-1): |
d60486018004
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 | |
50
d3842cd76e92
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
|
313 self.stream, self.directives = [], [] |
1 | 314 |
50
d3842cd76e92
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
|
315 def __call__(self, stream, ctxt, directives): |
1 | 316 self.stream = list(stream) |
50
d3842cd76e92
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
|
317 self.directives = directives |
1 | 318 ctxt[self.name] = lambda *args, **kwargs: self._exec(ctxt, *args, |
319 **kwargs) | |
320 return [] | |
321 | |
322 def _exec(self, ctxt, *args, **kwargs): | |
323 scope = {} | |
324 args = list(args) # make mutable | |
325 for name in self.args: | |
326 if args: | |
327 scope[name] = args.pop(0) | |
328 else: | |
329 scope[name] = kwargs.pop(name, self.defaults.get(name)) | |
95
2fe86a99947f
Improve performance of push/pop operations on the context.
cmlenz
parents:
93
diff
changeset
|
330 ctxt.push(scope) |
78
46fed54f23cd
Minor improvements to how directives are applied in template processing.
cmlenz
parents:
77
diff
changeset
|
331 for event in _apply_directives(self.stream, ctxt, self.directives): |
1 | 332 yield event |
333 ctxt.pop() | |
334 | |
335 | |
336 class ForDirective(Directive): | |
31 | 337 """Implementation of the `py:for` template directive for repeating an |
338 element based on an iterable in the context data. | |
1 | 339 |
61 | 340 >>> tmpl = Template('''<ul xmlns:py="http://markup.edgewall.org/"> |
1 | 341 ... <li py:for="item in items">${item}</li> |
342 ... </ul>''') | |
149
537f819c547b
`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
|
343 >>> print tmpl.generate(items=[1, 2, 3]) |
1 | 344 <ul> |
345 <li>1</li><li>2</li><li>3</li> | |
346 </ul> | |
347 """ | |
348 __slots__ = ['targets'] | |
349 | |
65
b3fdf93057ab
Support the use of directives as elements to reduce the need for using `py:strip`.
cmlenz
parents:
61
diff
changeset
|
350 ATTRIBUTE = 'each' |
b3fdf93057ab
Support the use of directives as elements to reduce the need for using `py:strip`.
cmlenz
parents:
61
diff
changeset
|
351 |
81
d60486018004
Template expressions are now compiled to Python bytecode.
cmlenz
parents:
80
diff
changeset
|
352 def __init__(self, value, filename=None, lineno=-1, offset=-1): |
29
ab8703fa68b8
* 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
|
353 targets, value = value.split(' in ', 1) |
1 | 354 self.targets = [str(name.strip()) for name in targets.split(',')] |
140
c1f4390d50f8
Fix bug in HTML serializer, plus some other minor tweaks.
cmlenz
parents:
139
diff
changeset
|
355 Directive.__init__(self, value.strip(), filename, lineno, offset) |
1 | 356 |
50
d3842cd76e92
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
|
357 def __call__(self, stream, ctxt, directives): |
53
512eb72dbb19
* Add helper function to let directives apply any remaining directives, and use that helper consistently in every directive.
cmlenz
parents:
51
diff
changeset
|
358 iterable = self.expr.evaluate(ctxt) |
101 | 359 if iterable is None: |
360 return | |
361 | |
362 scope = {} | |
363 stream = list(stream) | |
364 targets = self.targets | |
140
c1f4390d50f8
Fix bug in HTML serializer, plus some other minor tweaks.
cmlenz
parents:
139
diff
changeset
|
365 single = len(targets) == 1 |
101 | 366 for item in iter(iterable): |
140
c1f4390d50f8
Fix bug in HTML serializer, plus some other minor tweaks.
cmlenz
parents:
139
diff
changeset
|
367 if single: |
101 | 368 scope[targets[0]] = item |
369 else: | |
370 for idx, name in enumerate(targets): | |
371 scope[name] = item[idx] | |
372 ctxt.push(scope) | |
373 for event in _apply_directives(stream, ctxt, directives): | |
374 yield event | |
375 ctxt.pop() | |
1 | 376 |
377 def __repr__(self): | |
378 return '<%s "%s in %s">' % (self.__class__.__name__, | |
379 ', '.join(self.targets), self.expr.source) | |
380 | |
381 | |
382 class IfDirective(Directive): | |
31 | 383 """Implementation of the `py:if` template directive for conditionally |
384 excluding elements from being output. | |
1 | 385 |
61 | 386 >>> tmpl = Template('''<div xmlns:py="http://markup.edgewall.org/"> |
1 | 387 ... <b py:if="foo">${bar}</b> |
388 ... </div>''') | |
149
537f819c547b
`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
|
389 >>> print tmpl.generate(foo=True, bar='Hello') |
1 | 390 <div> |
391 <b>Hello</b> | |
392 </div> | |
393 """ | |
50
d3842cd76e92
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
|
394 __slots__ = [] |
d3842cd76e92
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
|
395 |
65
b3fdf93057ab
Support the use of directives as elements to reduce the need for using `py:strip`.
cmlenz
parents:
61
diff
changeset
|
396 ATTRIBUTE = 'test' |
b3fdf93057ab
Support the use of directives as elements to reduce the need for using `py:strip`.
cmlenz
parents:
61
diff
changeset
|
397 |
50
d3842cd76e92
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
|
398 def __call__(self, stream, ctxt, directives): |
1 | 399 if self.expr.evaluate(ctxt): |
78
46fed54f23cd
Minor improvements to how directives are applied in template processing.
cmlenz
parents:
77
diff
changeset
|
400 return _apply_directives(stream, ctxt, directives) |
1 | 401 return [] |
402 | |
403 | |
404 class MatchDirective(Directive): | |
405 """Implementation of the `py:match` template directive. | |
14
c7d33e0c9839
The `<py:match>` directive now protects itself against simple infinite recursion (see MatchDirective), while still allowing recursion in general.
cmlenz
parents:
13
diff
changeset
|
406 |
61 | 407 >>> tmpl = Template('''<div xmlns:py="http://markup.edgewall.org/"> |
17
74cc70129d04
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
|
408 ... <span py:match="greeting"> |
1 | 409 ... Hello ${select('@name')} |
410 ... </span> | |
411 ... <greeting name="Dude" /> | |
412 ... </div>''') | |
14
c7d33e0c9839
The `<py:match>` directive now protects itself against simple infinite recursion (see MatchDirective), while still allowing recursion in general.
cmlenz
parents:
13
diff
changeset
|
413 >>> print tmpl.generate() |
1 | 414 <div> |
415 <span> | |
416 Hello Dude | |
417 </span> | |
418 </div> | |
419 """ | |
420 __slots__ = ['path', 'stream'] | |
421 | |
65
b3fdf93057ab
Support the use of directives as elements to reduce the need for using `py:strip`.
cmlenz
parents:
61
diff
changeset
|
422 ATTRIBUTE = 'path' |
b3fdf93057ab
Support the use of directives as elements to reduce the need for using `py:strip`.
cmlenz
parents:
61
diff
changeset
|
423 |
81
d60486018004
Template expressions are now compiled to Python bytecode.
cmlenz
parents:
80
diff
changeset
|
424 def __init__(self, value, filename=None, lineno=-1, offset=-1): |
d60486018004
Template expressions are now compiled to Python bytecode.
cmlenz
parents:
80
diff
changeset
|
425 Directive.__init__(self, None, filename, lineno, offset) |
139
8332287b5508
Implement position reporting for XPath syntax errors. Closes #20.
cmlenz
parents:
134
diff
changeset
|
426 self.path = Path(value, filename, lineno) |
1 | 427 self.stream = [] |
428 | |
50
d3842cd76e92
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
|
429 def __call__(self, stream, ctxt, directives): |
1 | 430 self.stream = list(stream) |
38
ee669cb9cccc
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
|
431 ctxt._match_templates.append((self.path.test(ignore_context=True), |
50
d3842cd76e92
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
|
432 self.path, self.stream, directives)) |
1 | 433 return [] |
434 | |
435 def __repr__(self): | |
14
c7d33e0c9839
The `<py:match>` directive now protects itself against simple infinite recursion (see MatchDirective), while still allowing recursion in general.
cmlenz
parents:
13
diff
changeset
|
436 return '<%s "%s">' % (self.__class__.__name__, self.path.source) |
1 | 437 |
438 | |
439 class ReplaceDirective(Directive): | |
440 """Implementation of the `py:replace` template directive. | |
441 | |
31 | 442 This directive replaces the element with the result of evaluating the |
443 value of the `py:replace` attribute: | |
444 | |
61 | 445 >>> tmpl = Template('''<div xmlns:py="http://markup.edgewall.org/"> |
1 | 446 ... <span py:replace="bar">Hello</span> |
447 ... </div>''') | |
149
537f819c547b
`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
|
448 >>> print tmpl.generate(bar='Bye') |
1 | 449 <div> |
450 Bye | |
451 </div> | |
452 | |
453 This directive is equivalent to `py:content` combined with `py:strip`, | |
454 providing a less verbose way to achieve the same effect: | |
455 | |
61 | 456 >>> tmpl = Template('''<div xmlns:py="http://markup.edgewall.org/"> |
1 | 457 ... <span py:content="bar" py:strip="">Hello</span> |
458 ... </div>''') | |
149
537f819c547b
`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
|
459 >>> print tmpl.generate(bar='Bye') |
1 | 460 <div> |
461 Bye | |
462 </div> | |
463 """ | |
50
d3842cd76e92
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
|
464 __slots__ = [] |
d3842cd76e92
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
|
465 |
54 | 466 def __call__(self, stream, ctxt, directives): |
1 | 467 kind, data, pos = stream.next() |
69 | 468 yield EXPR, self.expr, pos |
1 | 469 |
470 | |
471 class StripDirective(Directive): | |
472 """Implementation of the `py:strip` template directive. | |
473 | |
474 When the value of the `py:strip` attribute evaluates to `True`, the element | |
475 is stripped from the output | |
476 | |
61 | 477 >>> tmpl = Template('''<div xmlns:py="http://markup.edgewall.org/"> |
1 | 478 ... <div py:strip="True"><b>foo</b></div> |
479 ... </div>''') | |
14
c7d33e0c9839
The `<py:match>` directive now protects itself against simple infinite recursion (see MatchDirective), while still allowing recursion in general.
cmlenz
parents:
13
diff
changeset
|
480 >>> print tmpl.generate() |
1 | 481 <div> |
482 <b>foo</b> | |
483 </div> | |
484 | |
37
37557b8fb925
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
|
485 Leaving the attribute value empty is equivalent to a truth value. |
1 | 486 |
487 This directive is particulary interesting for named template functions or | |
488 match templates that do not generate a top-level element: | |
489 | |
61 | 490 >>> tmpl = Template('''<div xmlns:py="http://markup.edgewall.org/"> |
1 | 491 ... <div py:def="echo(what)" py:strip=""> |
492 ... <b>${what}</b> | |
493 ... </div> | |
494 ... ${echo('foo')} | |
495 ... </div>''') | |
14
c7d33e0c9839
The `<py:match>` directive now protects itself against simple infinite recursion (see MatchDirective), while still allowing recursion in general.
cmlenz
parents:
13
diff
changeset
|
496 >>> print tmpl.generate() |
1 | 497 <div> |
498 <b>foo</b> | |
499 </div> | |
500 """ | |
50
d3842cd76e92
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
|
501 __slots__ = [] |
d3842cd76e92
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
|
502 |
54 | 503 def __call__(self, stream, ctxt, directives): |
78
46fed54f23cd
Minor improvements to how directives are applied in template processing.
cmlenz
parents:
77
diff
changeset
|
504 def _generate(): |
46fed54f23cd
Minor improvements to how directives are applied in template processing.
cmlenz
parents:
77
diff
changeset
|
505 if self.expr: |
46fed54f23cd
Minor improvements to how directives are applied in template processing.
cmlenz
parents:
77
diff
changeset
|
506 strip = self.expr.evaluate(ctxt) |
46fed54f23cd
Minor improvements to how directives are applied in template processing.
cmlenz
parents:
77
diff
changeset
|
507 else: |
46fed54f23cd
Minor improvements to how directives are applied in template processing.
cmlenz
parents:
77
diff
changeset
|
508 strip = True |
46fed54f23cd
Minor improvements to how directives are applied in template processing.
cmlenz
parents:
77
diff
changeset
|
509 if strip: |
46fed54f23cd
Minor improvements to how directives are applied in template processing.
cmlenz
parents:
77
diff
changeset
|
510 stream.next() # skip start tag |
46fed54f23cd
Minor improvements to how directives are applied in template processing.
cmlenz
parents:
77
diff
changeset
|
511 previous = stream.next() |
46fed54f23cd
Minor improvements to how directives are applied in template processing.
cmlenz
parents:
77
diff
changeset
|
512 for event in stream: |
46fed54f23cd
Minor improvements to how directives are applied in template processing.
cmlenz
parents:
77
diff
changeset
|
513 yield previous |
46fed54f23cd
Minor improvements to how directives are applied in template processing.
cmlenz
parents:
77
diff
changeset
|
514 previous = event |
46fed54f23cd
Minor improvements to how directives are applied in template processing.
cmlenz
parents:
77
diff
changeset
|
515 else: |
46fed54f23cd
Minor improvements to how directives are applied in template processing.
cmlenz
parents:
77
diff
changeset
|
516 for event in stream: |
46fed54f23cd
Minor improvements to how directives are applied in template processing.
cmlenz
parents:
77
diff
changeset
|
517 yield event |
46fed54f23cd
Minor improvements to how directives are applied in template processing.
cmlenz
parents:
77
diff
changeset
|
518 |
46fed54f23cd
Minor improvements to how directives are applied in template processing.
cmlenz
parents:
77
diff
changeset
|
519 return _apply_directives(_generate(), ctxt, directives) |
1 | 520 |
521 | |
44
436e30c8420b
implement `py:choose/when/otherwise` directives for conditionally selecting one of several blocks
mgood
parents:
38
diff
changeset
|
522 class ChooseDirective(Directive): |
436e30c8420b
implement `py:choose/when/otherwise` directives for conditionally selecting one of several blocks
mgood
parents:
38
diff
changeset
|
523 """Implementation of the `py:choose` directive for conditionally selecting |
436e30c8420b
implement `py:choose/when/otherwise` directives for conditionally selecting one of several blocks
mgood
parents:
38
diff
changeset
|
524 one of several body elements to display. |
53
512eb72dbb19
* Add helper function to let directives apply any remaining directives, and use that helper consistently in every directive.
cmlenz
parents:
51
diff
changeset
|
525 |
44
436e30c8420b
implement `py:choose/when/otherwise` directives for conditionally selecting one of several blocks
mgood
parents:
38
diff
changeset
|
526 If the `py:choose` expression is empty the expressions of nested `py:when` |
436e30c8420b
implement `py:choose/when/otherwise` directives for conditionally selecting one of several blocks
mgood
parents:
38
diff
changeset
|
527 directives are tested for truth. The first true `py:when` body is output. |
53
512eb72dbb19
* Add helper function to let directives apply any remaining directives, and use that helper consistently in every directive.
cmlenz
parents:
51
diff
changeset
|
528 If no `py:when` directive is matched then the fallback directive |
512eb72dbb19
* Add helper function to let directives apply any remaining directives, and use that helper consistently in every directive.
cmlenz
parents:
51
diff
changeset
|
529 `py:otherwise` will be used. |
512eb72dbb19
* Add helper function to let directives apply any remaining directives, and use that helper consistently in every directive.
cmlenz
parents:
51
diff
changeset
|
530 |
61 | 531 >>> tmpl = Template('''<div xmlns:py="http://markup.edgewall.org/" |
44
436e30c8420b
implement `py:choose/when/otherwise` directives for conditionally selecting one of several blocks
mgood
parents:
38
diff
changeset
|
532 ... py:choose=""> |
436e30c8420b
implement `py:choose/when/otherwise` directives for conditionally selecting one of several blocks
mgood
parents:
38
diff
changeset
|
533 ... <span py:when="0 == 1">0</span> |
436e30c8420b
implement `py:choose/when/otherwise` directives for conditionally selecting one of several blocks
mgood
parents:
38
diff
changeset
|
534 ... <span py:when="1 == 1">1</span> |
53
512eb72dbb19
* Add helper function to let directives apply any remaining directives, and use that helper consistently in every directive.
cmlenz
parents:
51
diff
changeset
|
535 ... <span py:otherwise="">2</span> |
44
436e30c8420b
implement `py:choose/when/otherwise` directives for conditionally selecting one of several blocks
mgood
parents:
38
diff
changeset
|
536 ... </div>''') |
149
537f819c547b
`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
|
537 >>> print tmpl.generate() |
44
436e30c8420b
implement `py:choose/when/otherwise` directives for conditionally selecting one of several blocks
mgood
parents:
38
diff
changeset
|
538 <div> |
436e30c8420b
implement `py:choose/when/otherwise` directives for conditionally selecting one of several blocks
mgood
parents:
38
diff
changeset
|
539 <span>1</span> |
436e30c8420b
implement `py:choose/when/otherwise` directives for conditionally selecting one of several blocks
mgood
parents:
38
diff
changeset
|
540 </div> |
53
512eb72dbb19
* Add helper function to let directives apply any remaining directives, and use that helper consistently in every directive.
cmlenz
parents:
51
diff
changeset
|
541 |
44
436e30c8420b
implement `py:choose/when/otherwise` directives for conditionally selecting one of several blocks
mgood
parents:
38
diff
changeset
|
542 If the `py:choose` directive contains an expression, the nested `py:when` |
53
512eb72dbb19
* Add helper function to let directives apply any remaining directives, and use that helper consistently in every directive.
cmlenz
parents:
51
diff
changeset
|
543 directives are tested for equality to the `py:choose` expression: |
512eb72dbb19
* Add helper function to let directives apply any remaining directives, and use that helper consistently in every directive.
cmlenz
parents:
51
diff
changeset
|
544 |
61 | 545 >>> tmpl = Template('''<div xmlns:py="http://markup.edgewall.org/" |
44
436e30c8420b
implement `py:choose/when/otherwise` directives for conditionally selecting one of several blocks
mgood
parents:
38
diff
changeset
|
546 ... py:choose="2"> |
436e30c8420b
implement `py:choose/when/otherwise` directives for conditionally selecting one of several blocks
mgood
parents:
38
diff
changeset
|
547 ... <span py:when="1">1</span> |
436e30c8420b
implement `py:choose/when/otherwise` directives for conditionally selecting one of several blocks
mgood
parents:
38
diff
changeset
|
548 ... <span py:when="2">2</span> |
436e30c8420b
implement `py:choose/when/otherwise` directives for conditionally selecting one of several blocks
mgood
parents:
38
diff
changeset
|
549 ... </div>''') |
149
537f819c547b
`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
|
550 >>> print tmpl.generate() |
44
436e30c8420b
implement `py:choose/when/otherwise` directives for conditionally selecting one of several blocks
mgood
parents:
38
diff
changeset
|
551 <div> |
436e30c8420b
implement `py:choose/when/otherwise` directives for conditionally selecting one of several blocks
mgood
parents:
38
diff
changeset
|
552 <span>2</span> |
436e30c8420b
implement `py:choose/when/otherwise` directives for conditionally selecting one of several blocks
mgood
parents:
38
diff
changeset
|
553 </div> |
53
512eb72dbb19
* Add helper function to let directives apply any remaining directives, and use that helper consistently in every directive.
cmlenz
parents:
51
diff
changeset
|
554 |
44
436e30c8420b
implement `py:choose/when/otherwise` directives for conditionally selecting one of several blocks
mgood
parents:
38
diff
changeset
|
555 Behavior is undefined if a `py:choose` block contains content outside a |
436e30c8420b
implement `py:choose/when/otherwise` directives for conditionally selecting one of several blocks
mgood
parents:
38
diff
changeset
|
556 `py:when` or `py:otherwise` block. Behavior is also undefined if a |
436e30c8420b
implement `py:choose/when/otherwise` directives for conditionally selecting one of several blocks
mgood
parents:
38
diff
changeset
|
557 `py:otherwise` occurs before `py:when` blocks. |
436e30c8420b
implement `py:choose/when/otherwise` directives for conditionally selecting one of several blocks
mgood
parents:
38
diff
changeset
|
558 """ |
50
d3842cd76e92
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
|
559 __slots__ = ['matched', 'value'] |
44
436e30c8420b
implement `py:choose/when/otherwise` directives for conditionally selecting one of several blocks
mgood
parents:
38
diff
changeset
|
560 |
65
b3fdf93057ab
Support the use of directives as elements to reduce the need for using `py:strip`.
cmlenz
parents:
61
diff
changeset
|
561 ATTRIBUTE = 'test' |
b3fdf93057ab
Support the use of directives as elements to reduce the need for using `py:strip`.
cmlenz
parents:
61
diff
changeset
|
562 |
53
512eb72dbb19
* Add helper function to let directives apply any remaining directives, and use that helper consistently in every directive.
cmlenz
parents:
51
diff
changeset
|
563 def __call__(self, stream, ctxt, directives): |
44
436e30c8420b
implement `py:choose/when/otherwise` directives for conditionally selecting one of several blocks
mgood
parents:
38
diff
changeset
|
564 if self.expr: |
436e30c8420b
implement `py:choose/when/otherwise` directives for conditionally selecting one of several blocks
mgood
parents:
38
diff
changeset
|
565 self.value = self.expr.evaluate(ctxt) |
436e30c8420b
implement `py:choose/when/otherwise` directives for conditionally selecting one of several blocks
mgood
parents:
38
diff
changeset
|
566 self.matched = False |
95
2fe86a99947f
Improve performance of push/pop operations on the context.
cmlenz
parents:
93
diff
changeset
|
567 ctxt.push(dict(_choose=self)) |
78
46fed54f23cd
Minor improvements to how directives are applied in template processing.
cmlenz
parents:
77
diff
changeset
|
568 for event in _apply_directives(stream, ctxt, directives): |
44
436e30c8420b
implement `py:choose/when/otherwise` directives for conditionally selecting one of several blocks
mgood
parents:
38
diff
changeset
|
569 yield event |
436e30c8420b
implement `py:choose/when/otherwise` directives for conditionally selecting one of several blocks
mgood
parents:
38
diff
changeset
|
570 ctxt.pop() |
436e30c8420b
implement `py:choose/when/otherwise` directives for conditionally selecting one of several blocks
mgood
parents:
38
diff
changeset
|
571 |
436e30c8420b
implement `py:choose/when/otherwise` directives for conditionally selecting one of several blocks
mgood
parents:
38
diff
changeset
|
572 |
436e30c8420b
implement `py:choose/when/otherwise` directives for conditionally selecting one of several blocks
mgood
parents:
38
diff
changeset
|
573 class WhenDirective(Directive): |
436e30c8420b
implement `py:choose/when/otherwise` directives for conditionally selecting one of several blocks
mgood
parents:
38
diff
changeset
|
574 """Implementation of the `py:when` directive for nesting in a parent with |
50
d3842cd76e92
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
|
575 the `py:choose` directive. |
d3842cd76e92
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
|
576 |
d3842cd76e92
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
|
577 See the documentation of `py:choose` for usage. |
44
436e30c8420b
implement `py:choose/when/otherwise` directives for conditionally selecting one of several blocks
mgood
parents:
38
diff
changeset
|
578 """ |
65
b3fdf93057ab
Support the use of directives as elements to reduce the need for using `py:strip`.
cmlenz
parents:
61
diff
changeset
|
579 |
b3fdf93057ab
Support the use of directives as elements to reduce the need for using `py:strip`.
cmlenz
parents:
61
diff
changeset
|
580 ATTRIBUTE = 'test' |
b3fdf93057ab
Support the use of directives as elements to reduce the need for using `py:strip`.
cmlenz
parents:
61
diff
changeset
|
581 |
54 | 582 def __call__(self, stream, ctxt, directives): |
50
d3842cd76e92
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 choose = ctxt['_choose'] |
44
436e30c8420b
implement `py:choose/when/otherwise` directives for conditionally selecting one of several blocks
mgood
parents:
38
diff
changeset
|
584 if choose.matched: |
436e30c8420b
implement `py:choose/when/otherwise` directives for conditionally selecting one of several blocks
mgood
parents:
38
diff
changeset
|
585 return [] |
436e30c8420b
implement `py:choose/when/otherwise` directives for conditionally selecting one of several blocks
mgood
parents:
38
diff
changeset
|
586 value = self.expr.evaluate(ctxt) |
436e30c8420b
implement `py:choose/when/otherwise` directives for conditionally selecting one of several blocks
mgood
parents:
38
diff
changeset
|
587 try: |
436e30c8420b
implement `py:choose/when/otherwise` directives for conditionally selecting one of several blocks
mgood
parents:
38
diff
changeset
|
588 if value == choose.value: |
436e30c8420b
implement `py:choose/when/otherwise` directives for conditionally selecting one of several blocks
mgood
parents:
38
diff
changeset
|
589 choose.matched = True |
78
46fed54f23cd
Minor improvements to how directives are applied in template processing.
cmlenz
parents:
77
diff
changeset
|
590 return _apply_directives(stream, ctxt, directives) |
44
436e30c8420b
implement `py:choose/when/otherwise` directives for conditionally selecting one of several blocks
mgood
parents:
38
diff
changeset
|
591 except AttributeError: |
436e30c8420b
implement `py:choose/when/otherwise` directives for conditionally selecting one of several blocks
mgood
parents:
38
diff
changeset
|
592 if value: |
436e30c8420b
implement `py:choose/when/otherwise` directives for conditionally selecting one of several blocks
mgood
parents:
38
diff
changeset
|
593 choose.matched = True |
78
46fed54f23cd
Minor improvements to how directives are applied in template processing.
cmlenz
parents:
77
diff
changeset
|
594 return _apply_directives(stream, ctxt, directives) |
44
436e30c8420b
implement `py:choose/when/otherwise` directives for conditionally selecting one of several blocks
mgood
parents:
38
diff
changeset
|
595 return [] |
436e30c8420b
implement `py:choose/when/otherwise` directives for conditionally selecting one of several blocks
mgood
parents:
38
diff
changeset
|
596 |
436e30c8420b
implement `py:choose/when/otherwise` directives for conditionally selecting one of several blocks
mgood
parents:
38
diff
changeset
|
597 |
436e30c8420b
implement `py:choose/when/otherwise` directives for conditionally selecting one of several blocks
mgood
parents:
38
diff
changeset
|
598 class OtherwiseDirective(Directive): |
436e30c8420b
implement `py:choose/when/otherwise` directives for conditionally selecting one of several blocks
mgood
parents:
38
diff
changeset
|
599 """Implementation of the `py:otherwise` directive for nesting in a parent |
50
d3842cd76e92
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
|
600 with the `py:choose` directive. |
d3842cd76e92
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
|
601 |
d3842cd76e92
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
|
602 See the documentation of `py:choose` for usage. |
44
436e30c8420b
implement `py:choose/when/otherwise` directives for conditionally selecting one of several blocks
mgood
parents:
38
diff
changeset
|
603 """ |
54 | 604 def __call__(self, stream, ctxt, directives): |
50
d3842cd76e92
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
|
605 choose = ctxt['_choose'] |
44
436e30c8420b
implement `py:choose/when/otherwise` directives for conditionally selecting one of several blocks
mgood
parents:
38
diff
changeset
|
606 if choose.matched: |
436e30c8420b
implement `py:choose/when/otherwise` directives for conditionally selecting one of several blocks
mgood
parents:
38
diff
changeset
|
607 return [] |
436e30c8420b
implement `py:choose/when/otherwise` directives for conditionally selecting one of several blocks
mgood
parents:
38
diff
changeset
|
608 choose.matched = True |
78
46fed54f23cd
Minor improvements to how directives are applied in template processing.
cmlenz
parents:
77
diff
changeset
|
609 return _apply_directives(stream, ctxt, directives) |
44
436e30c8420b
implement `py:choose/when/otherwise` directives for conditionally selecting one of several blocks
mgood
parents:
38
diff
changeset
|
610 |
436e30c8420b
implement `py:choose/when/otherwise` directives for conditionally selecting one of several blocks
mgood
parents:
38
diff
changeset
|
611 |
104
f12e7987d7f4
Added `py:with` directive based on Jonas' patch in #17.
cmlenz
parents:
101
diff
changeset
|
612 class WithDirective(Directive): |
f12e7987d7f4
Added `py:with` directive based on Jonas' patch in #17.
cmlenz
parents:
101
diff
changeset
|
613 """Implementation of the `py:with` template directive, which allows |
f12e7987d7f4
Added `py:with` directive based on Jonas' patch in #17.
cmlenz
parents:
101
diff
changeset
|
614 shorthand access to variables and expressions. |
f12e7987d7f4
Added `py:with` directive based on Jonas' patch in #17.
cmlenz
parents:
101
diff
changeset
|
615 |
f12e7987d7f4
Added `py:with` directive based on Jonas' patch in #17.
cmlenz
parents:
101
diff
changeset
|
616 >>> tmpl = Template('''<div xmlns:py="http://markup.edgewall.org/"> |
f12e7987d7f4
Added `py:with` directive based on Jonas' patch in #17.
cmlenz
parents:
101
diff
changeset
|
617 ... <span py:with="y=7; z=x+10">$x $y $z</span> |
f12e7987d7f4
Added `py:with` directive based on Jonas' patch in #17.
cmlenz
parents:
101
diff
changeset
|
618 ... </div>''') |
149
537f819c547b
`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
|
619 >>> print tmpl.generate(x=42) |
104
f12e7987d7f4
Added `py:with` directive based on Jonas' patch in #17.
cmlenz
parents:
101
diff
changeset
|
620 <div> |
f12e7987d7f4
Added `py:with` directive based on Jonas' patch in #17.
cmlenz
parents:
101
diff
changeset
|
621 <span>42 7 52</span> |
f12e7987d7f4
Added `py:with` directive based on Jonas' patch in #17.
cmlenz
parents:
101
diff
changeset
|
622 </div> |
f12e7987d7f4
Added `py:with` directive based on Jonas' patch in #17.
cmlenz
parents:
101
diff
changeset
|
623 """ |
f12e7987d7f4
Added `py:with` directive based on Jonas' patch in #17.
cmlenz
parents:
101
diff
changeset
|
624 __slots__ = ['vars'] |
f12e7987d7f4
Added `py:with` directive based on Jonas' patch in #17.
cmlenz
parents:
101
diff
changeset
|
625 |
f12e7987d7f4
Added `py:with` directive based on Jonas' patch in #17.
cmlenz
parents:
101
diff
changeset
|
626 ATTRIBUTE = 'vars' |
f12e7987d7f4
Added `py:with` directive based on Jonas' patch in #17.
cmlenz
parents:
101
diff
changeset
|
627 |
f12e7987d7f4
Added `py:with` directive based on Jonas' patch in #17.
cmlenz
parents:
101
diff
changeset
|
628 def __init__(self, value, filename=None, lineno=-1, offset=-1): |
f12e7987d7f4
Added `py:with` directive based on Jonas' patch in #17.
cmlenz
parents:
101
diff
changeset
|
629 Directive.__init__(self, None, filename, lineno, offset) |
f12e7987d7f4
Added `py:with` directive based on Jonas' patch in #17.
cmlenz
parents:
101
diff
changeset
|
630 self.vars = [] |
f12e7987d7f4
Added `py:with` directive based on Jonas' patch in #17.
cmlenz
parents:
101
diff
changeset
|
631 try: |
f12e7987d7f4
Added `py:with` directive based on Jonas' patch in #17.
cmlenz
parents:
101
diff
changeset
|
632 for stmt in value.split(';'): |
f12e7987d7f4
Added `py:with` directive based on Jonas' patch in #17.
cmlenz
parents:
101
diff
changeset
|
633 name, value = stmt.split('=', 1) |
f12e7987d7f4
Added `py:with` directive based on Jonas' patch in #17.
cmlenz
parents:
101
diff
changeset
|
634 self.vars.append((name.strip(), |
f12e7987d7f4
Added `py:with` directive based on Jonas' patch in #17.
cmlenz
parents:
101
diff
changeset
|
635 Expression(value.strip(), filename, lineno))) |
f12e7987d7f4
Added `py:with` directive based on Jonas' patch in #17.
cmlenz
parents:
101
diff
changeset
|
636 except SyntaxError, err: |
f12e7987d7f4
Added `py:with` directive based on Jonas' patch in #17.
cmlenz
parents:
101
diff
changeset
|
637 raise TemplateSyntaxError(err, filename, lineno, |
f12e7987d7f4
Added `py:with` directive based on Jonas' patch in #17.
cmlenz
parents:
101
diff
changeset
|
638 offset + (err.offset or 0)) |
f12e7987d7f4
Added `py:with` directive based on Jonas' patch in #17.
cmlenz
parents:
101
diff
changeset
|
639 |
f12e7987d7f4
Added `py:with` directive based on Jonas' patch in #17.
cmlenz
parents:
101
diff
changeset
|
640 def __call__(self, stream, ctxt, directives): |
120 | 641 ctxt.push(dict([(name, expr.evaluate(ctxt, nocall=True)) |
104
f12e7987d7f4
Added `py:with` directive based on Jonas' patch in #17.
cmlenz
parents:
101
diff
changeset
|
642 for name, expr in self.vars])) |
f12e7987d7f4
Added `py:with` directive based on Jonas' patch in #17.
cmlenz
parents:
101
diff
changeset
|
643 for event in _apply_directives(stream, ctxt, directives): |
f12e7987d7f4
Added `py:with` directive based on Jonas' patch in #17.
cmlenz
parents:
101
diff
changeset
|
644 yield event |
f12e7987d7f4
Added `py:with` directive based on Jonas' patch in #17.
cmlenz
parents:
101
diff
changeset
|
645 ctxt.pop() |
f12e7987d7f4
Added `py:with` directive based on Jonas' patch in #17.
cmlenz
parents:
101
diff
changeset
|
646 |
f12e7987d7f4
Added `py:with` directive based on Jonas' patch in #17.
cmlenz
parents:
101
diff
changeset
|
647 def __repr__(self): |
f12e7987d7f4
Added `py:with` directive based on Jonas' patch in #17.
cmlenz
parents:
101
diff
changeset
|
648 return '<%s "%s">' % (self.__class__.__name__, |
f12e7987d7f4
Added `py:with` directive based on Jonas' patch in #17.
cmlenz
parents:
101
diff
changeset
|
649 '; '.join(['%s = %s' % (name, expr.source) |
f12e7987d7f4
Added `py:with` directive based on Jonas' patch in #17.
cmlenz
parents:
101
diff
changeset
|
650 for name, expr in self.vars])) |
f12e7987d7f4
Added `py:with` directive based on Jonas' patch in #17.
cmlenz
parents:
101
diff
changeset
|
651 |
f12e7987d7f4
Added `py:with` directive based on Jonas' patch in #17.
cmlenz
parents:
101
diff
changeset
|
652 |
1 | 653 class Template(object): |
654 """Can parse a template and transform it into the corresponding output | |
655 based on context data. | |
656 """ | |
61 | 657 NAMESPACE = Namespace('http://markup.edgewall.org/') |
1 | 658 |
17
74cc70129d04
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
|
659 EXPR = StreamEventKind('EXPR') # an expression |
74cc70129d04
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
|
660 SUB = StreamEventKind('SUB') # a "subprogram" |
10
f77f7a91aa46
Moved the template-specific stream event kinds into the template module.
cmlenz
parents:
6
diff
changeset
|
661 |
1 | 662 directives = [('def', DefDirective), |
663 ('match', MatchDirective), | |
120 | 664 ('when', WhenDirective), |
665 ('otherwise', OtherwiseDirective), | |
1 | 666 ('for', ForDirective), |
667 ('if', IfDirective), | |
53
512eb72dbb19
* Add helper function to let directives apply any remaining directives, and use that helper consistently in every directive.
cmlenz
parents:
51
diff
changeset
|
668 ('choose', ChooseDirective), |
104
f12e7987d7f4
Added `py:with` directive based on Jonas' patch in #17.
cmlenz
parents:
101
diff
changeset
|
669 ('with', WithDirective), |
1 | 670 ('replace', ReplaceDirective), |
671 ('content', ContentDirective), | |
672 ('attrs', AttrsDirective), | |
50
d3842cd76e92
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
|
673 ('strip', StripDirective)] |
1 | 674 _dir_by_name = dict(directives) |
675 _dir_order = [directive[1] for directive in directives] | |
676 | |
21
b4d17897d053
* Include paths are now interpreted relative to the path of the including template. Closes #3.
cmlenz
parents:
18
diff
changeset
|
677 def __init__(self, source, basedir=None, filename=None): |
1 | 678 """Initialize a template from either a string or a file-like object.""" |
679 if isinstance(source, basestring): | |
680 self.source = StringIO(source) | |
681 else: | |
682 self.source = source | |
21
b4d17897d053
* Include paths are now interpreted relative to the path of the including template. Closes #3.
cmlenz
parents:
18
diff
changeset
|
683 self.basedir = basedir |
1 | 684 self.filename = filename or '<string>' |
21
b4d17897d053
* Include paths are now interpreted relative to the path of the including template. Closes #3.
cmlenz
parents:
18
diff
changeset
|
685 if basedir and filename: |
b4d17897d053
* Include paths are now interpreted relative to the path of the including template. Closes #3.
cmlenz
parents:
18
diff
changeset
|
686 self.filepath = os.path.join(basedir, filename) |
b4d17897d053
* Include paths are now interpreted relative to the path of the including template. Closes #3.
cmlenz
parents:
18
diff
changeset
|
687 else: |
b4d17897d053
* Include paths are now interpreted relative to the path of the including template. Closes #3.
cmlenz
parents:
18
diff
changeset
|
688 self.filepath = '<string>' |
1 | 689 |
23
d88358f719fa
Separate match and eval filters from the include and user-supplied filters.
cmlenz
parents:
22
diff
changeset
|
690 self.filters = [] |
1 | 691 self.parse() |
692 | |
693 def __repr__(self): | |
21
b4d17897d053
* Include paths are now interpreted relative to the path of the including template. Closes #3.
cmlenz
parents:
18
diff
changeset
|
694 return '<%s "%s">' % (self.__class__.__name__, self.filename) |
1 | 695 |
696 def parse(self): | |
697 """Parse the template. | |
698 | |
699 The parsing stage parses the XML template and constructs a list of | |
700 directives that will be executed in the render stage. The input is | |
701 split up into literal output (markup that does not depend on the | |
702 context data) and actual directives (commands or variable | |
703 substitution). | |
704 """ | |
705 stream = [] # list of events of the "compiled" template | |
706 dirmap = {} # temporary mapping of directives to elements | |
707 ns_prefix = {} | |
708 depth = 0 | |
709 | |
21
b4d17897d053
* Include paths are now interpreted relative to the path of the including template. Closes #3.
cmlenz
parents:
18
diff
changeset
|
710 for kind, data, pos in XMLParser(self.source, filename=self.filename): |
1 | 711 |
69 | 712 if kind is START_NS: |
1 | 713 # Strip out the namespace declaration for template directives |
714 prefix, uri = data | |
715 if uri == self.NAMESPACE: | |
716 ns_prefix[prefix] = uri | |
717 else: | |
718 stream.append((kind, data, pos)) | |
719 | |
69 | 720 elif kind is END_NS: |
1 | 721 if data in ns_prefix: |
722 del ns_prefix[data] | |
723 else: | |
724 stream.append((kind, data, pos)) | |
725 | |
69 | 726 elif kind is START: |
1 | 727 # Record any directive attributes in start tags |
728 tag, attrib = data | |
729 directives = [] | |
65
b3fdf93057ab
Support the use of directives as elements to reduce the need for using `py:strip`.
cmlenz
parents:
61
diff
changeset
|
730 strip = False |
b3fdf93057ab
Support the use of directives as elements to reduce the need for using `py:strip`.
cmlenz
parents:
61
diff
changeset
|
731 |
b3fdf93057ab
Support the use of directives as elements to reduce the need for using `py:strip`.
cmlenz
parents:
61
diff
changeset
|
732 if tag in self.NAMESPACE: |
b3fdf93057ab
Support the use of directives as elements to reduce the need for using `py:strip`.
cmlenz
parents:
61
diff
changeset
|
733 cls = self._dir_by_name.get(tag.localname) |
b3fdf93057ab
Support the use of directives as elements to reduce the need for using `py:strip`.
cmlenz
parents:
61
diff
changeset
|
734 if cls is None: |
b3fdf93057ab
Support the use of directives as elements to reduce the need for using `py:strip`.
cmlenz
parents:
61
diff
changeset
|
735 raise BadDirectiveError(tag, pos[0], pos[1]) |
66
59eb24184e9c
Switch copyright to Edgewall and URLs to markup.edgewall.org.
cmlenz
parents:
65
diff
changeset
|
736 value = attrib.get(getattr(cls, 'ATTRIBUTE', None), '') |
81
d60486018004
Template expressions are now compiled to Python bytecode.
cmlenz
parents:
80
diff
changeset
|
737 directives.append(cls(value, *pos)) |
65
b3fdf93057ab
Support the use of directives as elements to reduce the need for using `py:strip`.
cmlenz
parents:
61
diff
changeset
|
738 strip = True |
b3fdf93057ab
Support the use of directives as elements to reduce the need for using `py:strip`.
cmlenz
parents:
61
diff
changeset
|
739 |
1 | 740 new_attrib = [] |
741 for name, value in attrib: | |
18
5420cfe42d36
Actually make use of the `markup.core.Namespace` class, and add a couple of doctests.
cmlenz
parents:
17
diff
changeset
|
742 if name in self.NAMESPACE: |
1 | 743 cls = self._dir_by_name.get(name.localname) |
744 if cls is None: | |
65
b3fdf93057ab
Support the use of directives as elements to reduce the need for using `py:strip`.
cmlenz
parents:
61
diff
changeset
|
745 raise BadDirectiveError(name, pos[0], pos[1]) |
81
d60486018004
Template expressions are now compiled to Python bytecode.
cmlenz
parents:
80
diff
changeset
|
746 directives.append(cls(value, *pos)) |
1 | 747 else: |
75
3722696d0343
Empty attributes in templates were being stripped out. Thanks to Jonas for the patch.
cmlenz
parents:
74
diff
changeset
|
748 if value: |
3722696d0343
Empty attributes in templates were being stripped out. Thanks to Jonas for the patch.
cmlenz
parents:
74
diff
changeset
|
749 value = list(self._interpolate(value, *pos)) |
81
d60486018004
Template expressions are now compiled to Python bytecode.
cmlenz
parents:
80
diff
changeset
|
750 if len(value) == 1 and value[0][0] is TEXT: |
d60486018004
Template expressions are now compiled to Python bytecode.
cmlenz
parents:
80
diff
changeset
|
751 value = value[0][1] |
75
3722696d0343
Empty attributes in templates were being stripped out. Thanks to Jonas for the patch.
cmlenz
parents:
74
diff
changeset
|
752 else: |
3722696d0343
Empty attributes in templates were being stripped out. Thanks to Jonas for the patch.
cmlenz
parents:
74
diff
changeset
|
753 value = [(TEXT, u'', pos)] |
1 | 754 new_attrib.append((name, value)) |
65
b3fdf93057ab
Support the use of directives as elements to reduce the need for using `py:strip`.
cmlenz
parents:
61
diff
changeset
|
755 |
1 | 756 if directives: |
50
d3842cd76e92
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
|
757 directives.sort(lambda a, b: cmp(self._dir_order.index(a.__class__), |
d3842cd76e92
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
|
758 self._dir_order.index(b.__class__))) |
65
b3fdf93057ab
Support the use of directives as elements to reduce the need for using `py:strip`.
cmlenz
parents:
61
diff
changeset
|
759 dirmap[(depth, tag)] = (directives, len(stream), strip) |
1 | 760 |
761 stream.append((kind, (tag, Attributes(new_attrib)), pos)) | |
762 depth += 1 | |
763 | |
69 | 764 elif kind is END: |
1 | 765 depth -= 1 |
766 stream.append((kind, data, pos)) | |
767 | |
768 # If there have have directive attributes with the corresponding | |
769 # start tag, move the events inbetween into a "subprogram" | |
770 if (depth, data) in dirmap: | |
65
b3fdf93057ab
Support the use of directives as elements to reduce the need for using `py:strip`.
cmlenz
parents:
61
diff
changeset
|
771 directives, start_offset, strip = dirmap.pop((depth, data)) |
1 | 772 substream = stream[start_offset:] |
65
b3fdf93057ab
Support the use of directives as elements to reduce the need for using `py:strip`.
cmlenz
parents:
61
diff
changeset
|
773 if strip: |
b3fdf93057ab
Support the use of directives as elements to reduce the need for using `py:strip`.
cmlenz
parents:
61
diff
changeset
|
774 substream = substream[1:-1] |
69 | 775 stream[start_offset:] = [(SUB, (directives, substream), |
776 pos)] | |
1 | 777 |
69 | 778 elif kind is TEXT: |
1 | 779 for kind, data, pos in self._interpolate(data, *pos): |
780 stream.append((kind, data, pos)) | |
781 | |
89
80386d62814f
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
|
782 elif kind is COMMENT: |
80386d62814f
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
|
783 if not data.lstrip().startswith('!'): |
80386d62814f
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
|
784 stream.append((kind, data, pos)) |
80386d62814f
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
|
785 |
1 | 786 else: |
787 stream.append((kind, data, pos)) | |
788 | |
789 self.stream = stream | |
790 | |
791 _FULL_EXPR_RE = re.compile(r'(?<!\$)\$\{(.+?)\}') | |
792 _SHORT_EXPR_RE = re.compile(r'(?<!\$)\$([a-zA-Z][a-zA-Z0-9_\.]*)') | |
793 | |
21
b4d17897d053
* Include paths are now interpreted relative to the path of the including template. Closes #3.
cmlenz
parents:
18
diff
changeset
|
794 def _interpolate(cls, text, filename=None, lineno=-1, offset=-1): |
1 | 795 """Parse the given string and extract expressions. |
796 | |
797 This method returns a list containing both literal text and `Expression` | |
798 objects. | |
14
c7d33e0c9839
The `<py:match>` directive now protects itself against simple infinite recursion (see MatchDirective), while still allowing recursion in general.
cmlenz
parents:
13
diff
changeset
|
799 |
1 | 800 @param text: the text to parse |
801 @param lineno: the line number at which the text was found (optional) | |
802 @param offset: the column number at which the text starts in the source | |
803 (optional) | |
804 """ | |
134
d681d2c3cd8d
* 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
|
805 def _interpolate(text, patterns, filename=filename, lineno=lineno, |
d681d2c3cd8d
* 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
|
806 offset=offset): |
1 | 807 for idx, group in enumerate(patterns.pop(0).split(text)): |
808 if idx % 2: | |
81
d60486018004
Template expressions are now compiled to Python bytecode.
cmlenz
parents:
80
diff
changeset
|
809 try: |
d60486018004
Template expressions are now compiled to Python bytecode.
cmlenz
parents:
80
diff
changeset
|
810 yield EXPR, Expression(group, filename, lineno), \ |
134
d681d2c3cd8d
* 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 (filename, lineno, offset) |
81
d60486018004
Template expressions are now compiled to Python bytecode.
cmlenz
parents:
80
diff
changeset
|
812 except SyntaxError, err: |
d60486018004
Template expressions are now compiled to Python bytecode.
cmlenz
parents:
80
diff
changeset
|
813 raise TemplateSyntaxError(err, filename, lineno, |
d60486018004
Template expressions are now compiled to Python bytecode.
cmlenz
parents:
80
diff
changeset
|
814 offset + (err.offset or 0)) |
1 | 815 elif group: |
816 if patterns: | |
74
d54b5fd60b52
Fix expression interpolation where both shorthand notation and full notation are used inside a single text node. Thanks Jonas.
cmlenz
parents:
73
diff
changeset
|
817 for result in _interpolate(group, patterns[:]): |
1 | 818 yield result |
819 else: | |
134
d681d2c3cd8d
* 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
|
820 yield TEXT, group.replace('$$', '$'), \ |
d681d2c3cd8d
* 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
|
821 (filename, lineno, offset) |
d681d2c3cd8d
* 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
|
822 if '\n' in group: |
d681d2c3cd8d
* 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
|
823 lines = group.splitlines() |
d681d2c3cd8d
* 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
|
824 lineno += len(lines) - 1 |
d681d2c3cd8d
* 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
|
825 offset += len(lines[-1]) |
d681d2c3cd8d
* 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 else: |
d681d2c3cd8d
* 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 offset += len(group) |
74
d54b5fd60b52
Fix expression interpolation where both shorthand notation and full notation are used inside a single text node. Thanks Jonas.
cmlenz
parents:
73
diff
changeset
|
828 return _interpolate(text, [cls._FULL_EXPR_RE, cls._SHORT_EXPR_RE]) |
1 | 829 _interpolate = classmethod(_interpolate) |
830 | |
149
537f819c547b
`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
|
831 def generate(self, *args, **kwargs): |
29
ab8703fa68b8
* 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
|
832 """Apply the template to the given context data. |
ab8703fa68b8
* 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
|
833 |
149
537f819c547b
`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
|
834 Any keyword arguments are made available to the template as context |
537f819c547b
`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
|
835 data. |
537f819c547b
`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
|
836 |
537f819c547b
`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 Only one positional argument is accepted: if it is provided, it must be |
537f819c547b
`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
|
838 an instance of the `Context` class, and keyword arguments are ignored. |
537f819c547b
`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
|
839 This calling style is used for internal processing. |
537f819c547b
`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 |
29
ab8703fa68b8
* 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
|
841 @return: a markup event stream representing the result of applying |
ab8703fa68b8
* 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
|
842 the template to the context data. |
ab8703fa68b8
* 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
|
843 """ |
149
537f819c547b
`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 if args: |
537f819c547b
`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 assert len(args) == 1 |
537f819c547b
`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 ctxt = args[0] |
537f819c547b
`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
|
847 assert isinstance(ctxt, Context) |
537f819c547b
`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
|
848 else: |
537f819c547b
`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
|
849 ctxt = Context(**kwargs) |
17
74cc70129d04
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
|
850 ctxt._match_templates = [] |
74cc70129d04
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
|
851 |
69 | 852 stream = self.stream |
853 for filter_ in [self._eval, self._match, self._flatten] + self.filters: | |
35
35b9e9318fb1
Simplify template processing model by removing dynamically generated `SUB` events.
cmlenz
parents:
31
diff
changeset
|
854 stream = filter_(iter(stream), ctxt) |
35b9e9318fb1
Simplify template processing model by removing dynamically generated `SUB` events.
cmlenz
parents:
31
diff
changeset
|
855 return Stream(stream) |
17
74cc70129d04
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 |
74cc70129d04
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
|
857 def _eval(self, stream, ctxt=None): |
29
ab8703fa68b8
* 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
|
858 """Internal stream filter that evaluates any expressions in `START` and |
ab8703fa68b8
* 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
|
859 `TEXT` events. |
ab8703fa68b8
* 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
|
860 """ |
111
2368c3becc52
Some fixes and more unit tests for the XPath engine.
cmlenz
parents:
104
diff
changeset
|
861 filters = (self._eval, self._match) |
81
d60486018004
Template expressions are now compiled to Python bytecode.
cmlenz
parents:
80
diff
changeset
|
862 |
17
74cc70129d04
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
|
863 for kind, data, pos in stream: |
74cc70129d04
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
|
864 |
81
d60486018004
Template expressions are now compiled to Python bytecode.
cmlenz
parents:
80
diff
changeset
|
865 if kind is START and data[1]: |
17
74cc70129d04
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
|
866 # Attributes may still contain expressions in start tags at |
74cc70129d04
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
|
867 # this point, so do some evaluation |
74cc70129d04
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 tag, attrib = data |
74cc70129d04
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 new_attrib = [] |
74cc70129d04
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
|
870 for name, substream in attrib: |
74cc70129d04
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 if isinstance(substream, basestring): |
74cc70129d04
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 value = substream |
74cc70129d04
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 else: |
74cc70129d04
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 values = [] |
74cc70129d04
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 subkind, subdata, subpos in substream: |
69 | 876 if subkind is EXPR: |
17
74cc70129d04
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 values.append(subdata.evaluate(ctxt)) |
74cc70129d04
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: |
74cc70129d04
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.append(subdata) |
48
a5d585dd38c4
convert the result of expressions in attributes to strings so that values like ints are output correctly
mgood
parents:
44
diff
changeset
|
880 value = [unicode(x) for x in values if x is not None] |
17
74cc70129d04
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
|
881 if not value: |
74cc70129d04
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 continue |
23
d88358f719fa
Separate match and eval filters from the include and user-supplied filters.
cmlenz
parents:
22
diff
changeset
|
883 new_attrib.append((name, u''.join(value))) |
17
74cc70129d04
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 yield kind, (tag, Attributes(new_attrib)), pos |
74cc70129d04
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
|
885 |
69 | 886 elif kind is EXPR: |
17
74cc70129d04
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 result = data.evaluate(ctxt) |
74cc70129d04
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
|
888 if result is None: |
74cc70129d04
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 continue |
74cc70129d04
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 |
74cc70129d04
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
|
891 # First check for a string, otherwise the iterable test below |
74cc70129d04
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 # succeeds, and the string will be chopped up into individual |
74cc70129d04
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 # characters |
74cc70129d04
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 if isinstance(result, basestring): |
69 | 895 yield TEXT, result, pos |
17
74cc70129d04
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 else: |
74cc70129d04
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 # Test if the expression evaluated to an iterable, in which |
74cc70129d04
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 # case we yield the individual items |
74cc70129d04
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 try: |
111
2368c3becc52
Some fixes and more unit tests for the XPath engine.
cmlenz
parents:
104
diff
changeset
|
900 substream = _ensure(result) |
81
d60486018004
Template expressions are now compiled to Python bytecode.
cmlenz
parents:
80
diff
changeset
|
901 for filter_ in filters: |
77
f5ec6d4a61e4
* Simplify implementation of the individual XPath tests (use closures instead of callable classes)
cmlenz
parents:
75
diff
changeset
|
902 substream = filter_(substream, ctxt) |
f5ec6d4a61e4
* Simplify implementation of the individual XPath tests (use closures instead of callable classes)
cmlenz
parents:
75
diff
changeset
|
903 for event in substream: |
35
35b9e9318fb1
Simplify template processing model by removing dynamically generated `SUB` events.
cmlenz
parents:
31
diff
changeset
|
904 yield event |
17
74cc70129d04
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
|
905 except TypeError: |
74cc70129d04
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
|
906 # Neither a string nor an iterable, so just pass it |
74cc70129d04
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
|
907 # through |
69 | 908 yield TEXT, unicode(result), pos |
17
74cc70129d04
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
|
909 |
74cc70129d04
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 else: |
74cc70129d04
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 yield kind, data, pos |
74cc70129d04
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 |
23
d88358f719fa
Separate match and eval filters from the include and user-supplied filters.
cmlenz
parents:
22
diff
changeset
|
913 def _flatten(self, stream, ctxt=None): |
29
ab8703fa68b8
* 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
|
914 """Internal stream filter that expands `SUB` events in the stream.""" |
81
d60486018004
Template expressions are now compiled to Python bytecode.
cmlenz
parents:
80
diff
changeset
|
915 for kind, data, pos in stream: |
d60486018004
Template expressions are now compiled to Python bytecode.
cmlenz
parents:
80
diff
changeset
|
916 if kind is SUB: |
d60486018004
Template expressions are now compiled to Python bytecode.
cmlenz
parents:
80
diff
changeset
|
917 # This event is a list of directives and a list of nested |
d60486018004
Template expressions are now compiled to Python bytecode.
cmlenz
parents:
80
diff
changeset
|
918 # events to which those directives should be applied |
d60486018004
Template expressions are now compiled to Python bytecode.
cmlenz
parents:
80
diff
changeset
|
919 directives, substream = data |
d60486018004
Template expressions are now compiled to Python bytecode.
cmlenz
parents:
80
diff
changeset
|
920 substream = _apply_directives(substream, ctxt, directives) |
d60486018004
Template expressions are now compiled to Python bytecode.
cmlenz
parents:
80
diff
changeset
|
921 for filter_ in (self._eval, self._match, self._flatten): |
d60486018004
Template expressions are now compiled to Python bytecode.
cmlenz
parents:
80
diff
changeset
|
922 substream = filter_(substream, ctxt) |
d60486018004
Template expressions are now compiled to Python bytecode.
cmlenz
parents:
80
diff
changeset
|
923 for event in substream: |
d60486018004
Template expressions are now compiled to Python bytecode.
cmlenz
parents:
80
diff
changeset
|
924 yield event |
d60486018004
Template expressions are now compiled to Python bytecode.
cmlenz
parents:
80
diff
changeset
|
925 else: |
d60486018004
Template expressions are now compiled to Python bytecode.
cmlenz
parents:
80
diff
changeset
|
926 yield kind, data, pos |
17
74cc70129d04
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
|
927 |
36
ed370ebfa794
Fix for #7: match templates no longer process their own output.
cmlenz
parents:
35
diff
changeset
|
928 def _match(self, stream, ctxt=None, match_templates=None): |
29
ab8703fa68b8
* 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
|
929 """Internal stream filter that applies any defined match templates |
ab8703fa68b8
* 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
|
930 to the stream. |
ab8703fa68b8
* 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
|
931 """ |
36
ed370ebfa794
Fix for #7: match templates no longer process their own output.
cmlenz
parents:
35
diff
changeset
|
932 if match_templates is None: |
ed370ebfa794
Fix for #7: match templates no longer process their own output.
cmlenz
parents:
35
diff
changeset
|
933 match_templates = ctxt._match_templates |
ed370ebfa794
Fix for #7: match templates no longer process their own output.
cmlenz
parents:
35
diff
changeset
|
934 |
17
74cc70129d04
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
|
935 for kind, data, pos in stream: |
74cc70129d04
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
|
936 |
74cc70129d04
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
|
937 # We (currently) only care about start and end events for matching |
74cc70129d04
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
|
938 # We might care about namespace events in the future, though |
92
01d36818bb3d
More performance improvements... this time for whitespace normalization and template loops.
cmlenz
parents:
90
diff
changeset
|
939 if not match_templates or kind not in (START, END): |
17
74cc70129d04
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 yield kind, data, pos |
74cc70129d04
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 continue |
74cc70129d04
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 |
50
d3842cd76e92
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
|
943 for idx, (test, path, template, directives) in \ |
d3842cd76e92
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
|
944 enumerate(match_templates): |
17
74cc70129d04
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 |
116 | 946 if test(kind, data, pos) is True: |
17
74cc70129d04
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 # Consume and store all events until an end event |
74cc70129d04
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
|
948 # corresponding to this start event is encountered |
74cc70129d04
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
|
949 content = [(kind, data, pos)] |
74cc70129d04
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 depth = 1 |
74cc70129d04
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
|
951 while depth > 0: |
73 | 952 kind, data, pos = stream.next() |
953 if kind is START: | |
954 depth += 1 | |
955 elif kind is END: | |
956 depth -= 1 | |
957 content.append((kind, data, pos)) | |
958 test(kind, data, pos) | |
17
74cc70129d04
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
|
959 |
23
d88358f719fa
Separate match and eval filters from the include and user-supplied filters.
cmlenz
parents:
22
diff
changeset
|
960 content = list(self._flatten(content, ctxt)) |
95
2fe86a99947f
Improve performance of push/pop operations on the context.
cmlenz
parents:
93
diff
changeset
|
961 select = lambda path: Stream(content).select(path) |
2fe86a99947f
Improve performance of push/pop operations on the context.
cmlenz
parents:
93
diff
changeset
|
962 ctxt.push(dict(select=select)) |
36
ed370ebfa794
Fix for #7: match templates no longer process their own output.
cmlenz
parents:
35
diff
changeset
|
963 |
78
46fed54f23cd
Minor improvements to how directives are applied in template processing.
cmlenz
parents:
77
diff
changeset
|
964 template = _apply_directives(template, ctxt, directives) |
69 | 965 for event in self._match(self._eval(template, ctxt), |
966 ctxt, match_templates[:idx] + | |
967 match_templates[idx + 1:]): | |
35
35b9e9318fb1
Simplify template processing model by removing dynamically generated `SUB` events.
cmlenz
parents:
31
diff
changeset
|
968 yield event |
116 | 969 |
35
35b9e9318fb1
Simplify template processing model by removing dynamically generated `SUB` events.
cmlenz
parents:
31
diff
changeset
|
970 ctxt.pop() |
17
74cc70129d04
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
|
971 break |
69 | 972 |
973 else: # no matches | |
17
74cc70129d04
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
|
974 yield kind, data, pos |
74cc70129d04
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
|
975 |
1 | 976 |
69 | 977 EXPR = Template.EXPR |
978 SUB = Template.SUB | |
979 | |
980 | |
1 | 981 class TemplateLoader(object): |
982 """Responsible for loading templates from files on the specified search | |
983 path. | |
984 | |
985 >>> import tempfile | |
986 >>> fd, path = tempfile.mkstemp(suffix='.html', prefix='template') | |
987 >>> os.write(fd, '<p>$var</p>') | |
988 11 | |
989 >>> os.close(fd) | |
990 | |
991 The template loader accepts a list of directory paths that are then used | |
992 when searching for template files, in the given order: | |
993 | |
994 >>> loader = TemplateLoader([os.path.dirname(path)]) | |
995 | |
996 The `load()` method first checks the template cache whether the requested | |
997 template has already been loaded. If not, it attempts to locate the | |
998 template file, and returns the corresponding `Template` object: | |
999 | |
1000 >>> template = loader.load(os.path.basename(path)) | |
1001 >>> isinstance(template, Template) | |
1002 True | |
1003 | |
1004 Template instances are cached: requesting a template with the same name | |
1005 results in the same instance being returned: | |
1006 | |
1007 >>> loader.load(os.path.basename(path)) is template | |
1008 True | |
152
cdb2a1f930e2
Add some tests for relative template includes (see #27).
cmlenz
parents:
150
diff
changeset
|
1009 |
cdb2a1f930e2
Add some tests for relative template includes (see #27).
cmlenz
parents:
150
diff
changeset
|
1010 >>> os.remove(path) |
1 | 1011 """ |
1012 def __init__(self, search_path=None, auto_reload=False): | |
1013 """Create the template laoder. | |
1014 | |
1015 @param search_path: a list of absolute path names that should be | |
1016 searched for template files | |
1017 @param auto_reload: whether to check the last modification time of | |
1018 template files, and reload them if they have changed | |
1019 """ | |
1020 self.search_path = search_path | |
1021 if self.search_path is None: | |
1022 self.search_path = [] | |
1023 self.auto_reload = auto_reload | |
1024 self._cache = {} | |
1025 self._mtime = {} | |
1026 | |
21
b4d17897d053
* Include paths are now interpreted relative to the path of the including template. Closes #3.
cmlenz
parents:
18
diff
changeset
|
1027 def load(self, filename, relative_to=None): |
1 | 1028 """Load the template with the given name. |
1029 | |
22
2483fe549959
Fix for the template engine plugin: the search path is now ignored if the requested template path is absolute.
cmlenz
parents:
21
diff
changeset
|
1030 If the `filename` parameter is relative, this method searches the search |
2483fe549959
Fix for the template engine plugin: the search path is now ignored if the requested template path is absolute.
cmlenz
parents:
21
diff
changeset
|
1031 path trying to locate a template matching the given name. If the file |
2483fe549959
Fix for the template engine plugin: the search path is now ignored if the requested template path is absolute.
cmlenz
parents:
21
diff
changeset
|
1032 name is an absolute path, the search path is not bypassed. |
1 | 1033 |
22
2483fe549959
Fix for the template engine plugin: the search path is now ignored if the requested template path is absolute.
cmlenz
parents:
21
diff
changeset
|
1034 If requested template is not found, a `TemplateNotFound` exception is |
2483fe549959
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 raised. Otherwise, a `Template` object is returned that represents the |
2483fe549959
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 parsed template. |
2483fe549959
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 |
2483fe549959
Fix for the template engine plugin: the search path is now ignored if the requested template path is absolute.
cmlenz
parents:
21
diff
changeset
|
1038 Template instances are cached to avoid having to parse the same |
2483fe549959
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 template file more than once. Thus, subsequent calls of this method |
2483fe549959
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 with the same template file name will return the same `Template` |
2483fe549959
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 object (unless the `auto_reload` option is enabled and the file was |
2483fe549959
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 changed since the last parse.) |
1 | 1043 |
21
b4d17897d053
* Include paths are now interpreted relative to the path of the including template. Closes #3.
cmlenz
parents:
18
diff
changeset
|
1044 If the `relative_to` parameter is provided, the `filename` is |
b4d17897d053
* Include paths are now interpreted relative to the path of the including template. Closes #3.
cmlenz
parents:
18
diff
changeset
|
1045 interpreted as being relative to that path. |
b4d17897d053
* Include paths are now interpreted relative to the path of the including template. Closes #3.
cmlenz
parents:
18
diff
changeset
|
1046 |
1 | 1047 @param filename: the relative path of the template file to load |
21
b4d17897d053
* Include paths are now interpreted relative to the path of the including template. Closes #3.
cmlenz
parents:
18
diff
changeset
|
1048 @param relative_to: the filename of the template from which the new |
b4d17897d053
* Include paths are now interpreted relative to the path of the including template. Closes #3.
cmlenz
parents:
18
diff
changeset
|
1049 template is being loaded, or `None` if the template is being loaded |
b4d17897d053
* Include paths are now interpreted relative to the path of the including template. Closes #3.
cmlenz
parents:
18
diff
changeset
|
1050 directly |
1 | 1051 """ |
69 | 1052 from markup.filters import IncludeFilter |
1053 | |
21
b4d17897d053
* Include paths are now interpreted relative to the path of the including template. Closes #3.
cmlenz
parents:
18
diff
changeset
|
1054 if relative_to: |
b4d17897d053
* Include paths are now interpreted relative to the path of the including template. Closes #3.
cmlenz
parents:
18
diff
changeset
|
1055 filename = posixpath.join(posixpath.dirname(relative_to), filename) |
1 | 1056 filename = os.path.normpath(filename) |
22
2483fe549959
Fix for the template engine plugin: the search path is now ignored if the requested template path is absolute.
cmlenz
parents:
21
diff
changeset
|
1057 |
2483fe549959
Fix for the template engine plugin: the search path is now ignored if the requested template path is absolute.
cmlenz
parents:
21
diff
changeset
|
1058 # First check the cache to avoid reparsing the same file |
1 | 1059 try: |
1060 tmpl = self._cache[filename] | |
1061 if not self.auto_reload or \ | |
21
b4d17897d053
* Include paths are now interpreted relative to the path of the including template. Closes #3.
cmlenz
parents:
18
diff
changeset
|
1062 os.path.getmtime(tmpl.filepath) == self._mtime[filename]: |
1 | 1063 return tmpl |
1064 except KeyError: | |
1065 pass | |
22
2483fe549959
Fix for the template engine plugin: the search path is now ignored if the requested template path is absolute.
cmlenz
parents:
21
diff
changeset
|
1066 |
2483fe549959
Fix for the template engine plugin: the search path is now ignored if the requested template path is absolute.
cmlenz
parents:
21
diff
changeset
|
1067 # Bypass the search path if the filename is absolute |
2483fe549959
Fix for the template engine plugin: the search path is now ignored if the requested template path is absolute.
cmlenz
parents:
21
diff
changeset
|
1068 search_path = self.search_path |
2483fe549959
Fix for the template engine plugin: the search path is now ignored if the requested template path is absolute.
cmlenz
parents:
21
diff
changeset
|
1069 if os.path.isabs(filename): |
2483fe549959
Fix for the template engine plugin: the search path is now ignored if the requested template path is absolute.
cmlenz
parents:
21
diff
changeset
|
1070 search_path = [os.path.dirname(filename)] |
2483fe549959
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 |
2483fe549959
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 for dirname in search_path: |
1 | 1073 filepath = os.path.join(dirname, filename) |
1074 try: | |
133
79f445396cd7
Minor cleanup and performance improvement for the builder module.
cmlenz
parents:
120
diff
changeset
|
1075 fileobj = open(filepath, 'U') |
1 | 1076 try: |
21
b4d17897d053
* Include paths are now interpreted relative to the path of the including template. Closes #3.
cmlenz
parents:
18
diff
changeset
|
1077 tmpl = Template(fileobj, basedir=dirname, filename=filename) |
17
74cc70129d04
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
|
1078 tmpl.filters.append(IncludeFilter(self)) |
1 | 1079 finally: |
1080 fileobj.close() | |
1081 self._cache[filename] = tmpl | |
1082 self._mtime[filename] = os.path.getmtime(filepath) | |
1083 return tmpl | |
1084 except IOError: | |
1085 continue | |
1086 raise TemplateNotFound(filename, self.search_path) |