Mercurial > genshi > mirror
annotate markup/template.py @ 227:96a7e5011c69 trunk
Use list comprehension instead of `map()` in the AST transformer.
author | cmlenz |
---|---|
date | Fri, 08 Sep 2006 09:17:42 +0000 |
parents | 90d62225f411 |
children |
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 | |
24 import re | |
25 from StringIO import StringIO | |
26 | |
182
2f30ce3fb85e
Renamed `Attributes` to `Attrs` to reduce the verbosity.
cmlenz
parents:
181
diff
changeset
|
27 from markup.core import Attrs, Namespace, Stream, StreamEventKind, _ensure |
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 START, END, START_NS, END_NS, TEXT, COMMENT |
1 | 29 from markup.eval import Expression |
69 | 30 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
|
31 from markup.path import Path |
1 | 32 |
150
d35688d16831
Removed to many classes from the `__all__` list of `markup.template` in [191].
cmlenz
parents:
149
diff
changeset
|
33 __all__ = ['BadDirectiveError', 'TemplateError', 'TemplateSyntaxError', |
d35688d16831
Removed to many classes from the `__all__` list of `markup.template` in [191].
cmlenz
parents:
149
diff
changeset
|
34 'TemplateNotFound', 'Template', 'TemplateLoader'] |
1 | 35 |
36 | |
37 class TemplateError(Exception): | |
38 """Base exception class for errors related to template processing.""" | |
39 | |
40 | |
41 class TemplateSyntaxError(TemplateError): | |
42 """Exception raised when an expression in a template causes a Python syntax | |
43 error.""" | |
44 | |
45 def __init__(self, message, filename='<string>', lineno=-1, offset=-1): | |
46 if isinstance(message, SyntaxError) and message.lineno is not None: | |
47 message = str(message).replace(' (line %d)' % message.lineno, '') | |
213 | 48 self.msg = message |
49 message = '%s (%s, line %d)' % (self.msg, 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): | |
222
71741151e42b
`BadDirectiveError` no longer expects a `QName` as the directive name; a regular string should do.
cmlenz
parents:
221
diff
changeset
|
65 message = 'bad directive "%s"' % name |
213 | 66 TemplateSyntaxError.__init__(self, message, filename, lineno) |
1 | 67 |
68 | |
69 class TemplateNotFound(TemplateError): | |
70 """Exception raised when a specific template file could not be found.""" | |
71 | |
72 def __init__(self, name, search_path): | |
73 TemplateError.__init__(self, 'Template "%s" not found' % name) | |
74 self.search_path = search_path | |
75 | |
76 | |
77 class Context(object): | |
95
2fe86a99947f
Improve performance of push/pop operations on the context.
cmlenz
parents:
93
diff
changeset
|
78 """Container for template input data. |
1 | 79 |
95
2fe86a99947f
Improve performance of push/pop operations on the context.
cmlenz
parents:
93
diff
changeset
|
80 A context provides a stack of scopes (represented by dictionaries). |
2fe86a99947f
Improve performance of push/pop operations on the context.
cmlenz
parents:
93
diff
changeset
|
81 |
2fe86a99947f
Improve performance of push/pop operations on the context.
cmlenz
parents:
93
diff
changeset
|
82 Template directives such as loops can push a new scope on the stack with |
2fe86a99947f
Improve performance of push/pop operations on the context.
cmlenz
parents:
93
diff
changeset
|
83 data that should only be available inside the loop. When the loop |
2fe86a99947f
Improve performance of push/pop operations on the context.
cmlenz
parents:
93
diff
changeset
|
84 terminates, that scope can get popped off the stack again. |
1 | 85 |
86 >>> ctxt = Context(one='foo', other=1) | |
87 >>> ctxt.get('one') | |
88 'foo' | |
89 >>> ctxt.get('other') | |
90 1 | |
95
2fe86a99947f
Improve performance of push/pop operations on the context.
cmlenz
parents:
93
diff
changeset
|
91 >>> ctxt.push(dict(one='frost')) |
1 | 92 >>> ctxt.get('one') |
93 'frost' | |
94 >>> ctxt.get('other') | |
95 1 | |
96 >>> ctxt.pop() | |
95
2fe86a99947f
Improve performance of push/pop operations on the context.
cmlenz
parents:
93
diff
changeset
|
97 {'one': 'frost'} |
1 | 98 >>> ctxt.get('one') |
99 'foo' | |
100 """ | |
101 | |
102 def __init__(self, **data): | |
70
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
|
103 self.frames = deque([data]) |
95
2fe86a99947f
Improve performance of push/pop operations on the context.
cmlenz
parents:
93
diff
changeset
|
104 self.pop = self.frames.popleft |
2fe86a99947f
Improve performance of push/pop operations on the context.
cmlenz
parents:
93
diff
changeset
|
105 self.push = self.frames.appendleft |
157
47433aa69169
Fix for backwards compatibility proposed by cboos in #28.
cmlenz
parents:
154
diff
changeset
|
106 self._match_templates = [] |
1 | 107 |
108 def __repr__(self): | |
70
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 |
202
92353f28ae54
Remove the (hopefully) last instance where directives store state in instance variables, allowing templates to be cached and reused in a threadsafe manner. Closes #39. Many thanks to Christian Boos for the patch!
cmlenz
parents:
199
diff
changeset
|
115 def _find(self, key, default=None): |
220
f4943d2babda
Fix for #45 and #46: properly support assignment to nested tuples in `py:for` and `py:with` directives.
cmlenz
parents:
217
diff
changeset
|
116 """Retrieve a given variable's value and the frame it was found in. |
202
92353f28ae54
Remove the (hopefully) last instance where directives store state in instance variables, allowing templates to be cached and reused in a threadsafe manner. Closes #39. Many thanks to Christian Boos for the patch!
cmlenz
parents:
199
diff
changeset
|
117 |
92353f28ae54
Remove the (hopefully) last instance where directives store state in instance variables, allowing templates to be cached and reused in a threadsafe manner. Closes #39. Many thanks to Christian Boos for the patch!
cmlenz
parents:
199
diff
changeset
|
118 Intented for internal use by directives. |
92353f28ae54
Remove the (hopefully) last instance where directives store state in instance variables, allowing templates to be cached and reused in a threadsafe manner. Closes #39. Many thanks to Christian Boos for the patch!
cmlenz
parents:
199
diff
changeset
|
119 """ |
92353f28ae54
Remove the (hopefully) last instance where directives store state in instance variables, allowing templates to be cached and reused in a threadsafe manner. Closes #39. Many thanks to Christian Boos for the patch!
cmlenz
parents:
199
diff
changeset
|
120 for frame in self.frames: |
92353f28ae54
Remove the (hopefully) last instance where directives store state in instance variables, allowing templates to be cached and reused in a threadsafe manner. Closes #39. Many thanks to Christian Boos for the patch!
cmlenz
parents:
199
diff
changeset
|
121 if key in frame: |
92353f28ae54
Remove the (hopefully) last instance where directives store state in instance variables, allowing templates to be cached and reused in a threadsafe manner. Closes #39. Many thanks to Christian Boos for the patch!
cmlenz
parents:
199
diff
changeset
|
122 return frame[key], frame |
92353f28ae54
Remove the (hopefully) last instance where directives store state in instance variables, allowing templates to be cached and reused in a threadsafe manner. Closes #39. Many thanks to Christian Boos for the patch!
cmlenz
parents:
199
diff
changeset
|
123 return default, None |
92353f28ae54
Remove the (hopefully) last instance where directives store state in instance variables, allowing templates to be cached and reused in a threadsafe manner. Closes #39. Many thanks to Christian Boos for the patch!
cmlenz
parents:
199
diff
changeset
|
124 |
192
b64e36bc1100
Expression evaluation now differentiates between undefined variables and variables that are defined but set to `None`.
cmlenz
parents:
191
diff
changeset
|
125 def get(self, key, default=None): |
95
2fe86a99947f
Improve performance of push/pop operations on the context.
cmlenz
parents:
93
diff
changeset
|
126 """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
|
127 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
|
128 """ |
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
|
129 for frame in self.frames: |
1 | 130 if key in frame: |
131 return frame[key] | |
192
b64e36bc1100
Expression evaluation now differentiates between undefined variables and variables that are defined but set to `None`.
cmlenz
parents:
191
diff
changeset
|
132 return default |
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
|
133 __getitem__ = get |
1 | 134 |
95
2fe86a99947f
Improve performance of push/pop operations on the context.
cmlenz
parents:
93
diff
changeset
|
135 def push(self, data): |
2fe86a99947f
Improve performance of push/pop operations on the context.
cmlenz
parents:
93
diff
changeset
|
136 """Push a new scope on the stack.""" |
1 | 137 |
138 def pop(self): | |
95
2fe86a99947f
Improve performance of push/pop operations on the context.
cmlenz
parents:
93
diff
changeset
|
139 """Pop the top-most scope from the stack.""" |
1 | 140 |
141 | |
142 class Directive(object): | |
143 """Abstract base class for template directives. | |
144 | |
54 | 145 A directive is basically a callable that takes three positional arguments: |
146 `ctxt` is the template data context, `stream` is an iterable over the | |
147 events that the directive applies to, and `directives` is is a list of | |
148 other directives on the same stream that need to be applied. | |
1 | 149 |
150 Directives can be "anonymous" or "registered". Registered directives can be | |
151 applied by the template author using an XML attribute with the | |
152 corresponding name in the template. Such directives should be subclasses of | |
31 | 153 this base class that can be instantiated with the value of the directive |
154 attribute as parameter. | |
1 | 155 |
156 Anonymous directives are simply functions conforming to the protocol | |
157 described above, and can only be applied programmatically (for example by | |
158 template filters). | |
159 """ | |
160 __slots__ = ['expr'] | |
161 | |
81
d60486018004
Template expressions are now compiled to Python bytecode.
cmlenz
parents:
80
diff
changeset
|
162 def __init__(self, value, filename=None, lineno=-1, offset=-1): |
d60486018004
Template expressions are now compiled to Python bytecode.
cmlenz
parents:
80
diff
changeset
|
163 try: |
d60486018004
Template expressions are now compiled to Python bytecode.
cmlenz
parents:
80
diff
changeset
|
164 self.expr = value and Expression(value, filename, lineno) or None |
d60486018004
Template expressions are now compiled to Python bytecode.
cmlenz
parents:
80
diff
changeset
|
165 except SyntaxError, err: |
166
d43f50402cf2
Better error reporting for errors in directive expressions, and when `py:otherwise`/`py:when` are used outside a `py:choose` directive. Thanks to Christian Boos for the initial patch.
cmlenz
parents:
165
diff
changeset
|
166 err.msg += ' in expression "%s" of "%s" directive' % (value, |
172
ff4f0d89eef7
Fix for #30 (trouble using `py:def`inside a match template)
cmlenz
parents:
166
diff
changeset
|
167 self.tagname) |
81
d60486018004
Template expressions are now compiled to Python bytecode.
cmlenz
parents:
80
diff
changeset
|
168 raise TemplateSyntaxError(err, filename, lineno, |
d60486018004
Template expressions are now compiled to Python bytecode.
cmlenz
parents:
80
diff
changeset
|
169 offset + (err.offset or 0)) |
1 | 170 |
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
|
171 def __call__(self, stream, ctxt, directives): |
1 | 172 raise NotImplementedError |
173 | |
174 def __repr__(self): | |
175 expr = '' | |
176 if self.expr is not None: | |
177 expr = ' "%s"' % self.expr.source | |
178 return '<%s%s>' % (self.__class__.__name__, expr) | |
179 | |
172
ff4f0d89eef7
Fix for #30 (trouble using `py:def`inside a match template)
cmlenz
parents:
166
diff
changeset
|
180 def tagname(self): |
ff4f0d89eef7
Fix for #30 (trouble using `py:def`inside a match template)
cmlenz
parents:
166
diff
changeset
|
181 """Return the local tag name of the directive as it is used in |
ff4f0d89eef7
Fix for #30 (trouble using `py:def`inside a match template)
cmlenz
parents:
166
diff
changeset
|
182 templates. |
ff4f0d89eef7
Fix for #30 (trouble using `py:def`inside a match template)
cmlenz
parents:
166
diff
changeset
|
183 """ |
166
d43f50402cf2
Better error reporting for errors in directive expressions, and when `py:otherwise`/`py:when` are used outside a `py:choose` directive. Thanks to Christian Boos for the initial patch.
cmlenz
parents:
165
diff
changeset
|
184 return self.__class__.__name__.lower().replace('directive', '') |
172
ff4f0d89eef7
Fix for #30 (trouble using `py:def`inside a match template)
cmlenz
parents:
166
diff
changeset
|
185 tagname = property(tagname) |
166
d43f50402cf2
Better error reporting for errors in directive expressions, and when `py:otherwise`/`py:when` are used outside a `py:choose` directive. Thanks to Christian Boos for the initial patch.
cmlenz
parents:
165
diff
changeset
|
186 |
78
46fed54f23cd
Minor improvements to how directives are applied in template processing.
cmlenz
parents:
77
diff
changeset
|
187 |
46fed54f23cd
Minor improvements to how directives are applied in template processing.
cmlenz
parents:
77
diff
changeset
|
188 def _apply_directives(stream, ctxt, directives): |
161
7b1f07496bf7
Various docstring additions and other cosmetic changes.
cmlenz
parents:
157
diff
changeset
|
189 """Apply the given directives to the stream.""" |
78
46fed54f23cd
Minor improvements to how directives are applied in template processing.
cmlenz
parents:
77
diff
changeset
|
190 if directives: |
46fed54f23cd
Minor improvements to how directives are applied in template processing.
cmlenz
parents:
77
diff
changeset
|
191 stream = directives[0](iter(stream), ctxt, directives[1:]) |
46fed54f23cd
Minor improvements to how directives are applied in template processing.
cmlenz
parents:
77
diff
changeset
|
192 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
|
193 |
220
f4943d2babda
Fix for #45 and #46: properly support assignment to nested tuples in `py:for` and `py:with` directives.
cmlenz
parents:
217
diff
changeset
|
194 def _assignment(ast): |
f4943d2babda
Fix for #45 and #46: properly support assignment to nested tuples in `py:for` and `py:with` directives.
cmlenz
parents:
217
diff
changeset
|
195 """Takes the AST representation of an assignment, and returns a function |
f4943d2babda
Fix for #45 and #46: properly support assignment to nested tuples in `py:for` and `py:with` directives.
cmlenz
parents:
217
diff
changeset
|
196 that applies the assignment of a given value to a dictionary. |
f4943d2babda
Fix for #45 and #46: properly support assignment to nested tuples in `py:for` and `py:with` directives.
cmlenz
parents:
217
diff
changeset
|
197 """ |
f4943d2babda
Fix for #45 and #46: properly support assignment to nested tuples in `py:for` and `py:with` directives.
cmlenz
parents:
217
diff
changeset
|
198 def _names(node): |
f4943d2babda
Fix for #45 and #46: properly support assignment to nested tuples in `py:for` and `py:with` directives.
cmlenz
parents:
217
diff
changeset
|
199 if isinstance(node, (compiler.ast.AssTuple, compiler.ast.Tuple)): |
221 | 200 return tuple([_names(child) for child in node.nodes]) |
220
f4943d2babda
Fix for #45 and #46: properly support assignment to nested tuples in `py:for` and `py:with` directives.
cmlenz
parents:
217
diff
changeset
|
201 elif isinstance(node, (compiler.ast.AssName, compiler.ast.Name)): |
f4943d2babda
Fix for #45 and #46: properly support assignment to nested tuples in `py:for` and `py:with` directives.
cmlenz
parents:
217
diff
changeset
|
202 return node.name |
f4943d2babda
Fix for #45 and #46: properly support assignment to nested tuples in `py:for` and `py:with` directives.
cmlenz
parents:
217
diff
changeset
|
203 def _assign(data, value, names=_names(ast)): |
f4943d2babda
Fix for #45 and #46: properly support assignment to nested tuples in `py:for` and `py:with` directives.
cmlenz
parents:
217
diff
changeset
|
204 if type(names) is tuple: |
f4943d2babda
Fix for #45 and #46: properly support assignment to nested tuples in `py:for` and `py:with` directives.
cmlenz
parents:
217
diff
changeset
|
205 for idx in range(len(names)): |
f4943d2babda
Fix for #45 and #46: properly support assignment to nested tuples in `py:for` and `py:with` directives.
cmlenz
parents:
217
diff
changeset
|
206 _assign(data, value[idx], names[idx]) |
f4943d2babda
Fix for #45 and #46: properly support assignment to nested tuples in `py:for` and `py:with` directives.
cmlenz
parents:
217
diff
changeset
|
207 else: |
f4943d2babda
Fix for #45 and #46: properly support assignment to nested tuples in `py:for` and `py:with` directives.
cmlenz
parents:
217
diff
changeset
|
208 data[names] = value |
f4943d2babda
Fix for #45 and #46: properly support assignment to nested tuples in `py:for` and `py:with` directives.
cmlenz
parents:
217
diff
changeset
|
209 return _assign |
f4943d2babda
Fix for #45 and #46: properly support assignment to nested tuples in `py:for` and `py:with` directives.
cmlenz
parents:
217
diff
changeset
|
210 |
1 | 211 |
212 class AttrsDirective(Directive): | |
213 """Implementation of the `py:attrs` template directive. | |
214 | |
205
b700e5326421
Add doctest to verify that the `py:attrs` directive correctly handles a sequence of `(name, value)` tuples.
cmlenz
parents:
202
diff
changeset
|
215 The value of the `py:attrs` attribute should be a dictionary or a sequence |
b700e5326421
Add doctest to verify that the `py:attrs` directive correctly handles a sequence of `(name, value)` tuples.
cmlenz
parents:
202
diff
changeset
|
216 of `(name, value)` tuples. The items in that dictionary or sequence are |
b700e5326421
Add doctest to verify that the `py:attrs` directive correctly handles a sequence of `(name, value)` tuples.
cmlenz
parents:
202
diff
changeset
|
217 added as attributes to the element: |
1 | 218 |
61 | 219 >>> tmpl = Template('''<ul xmlns:py="http://markup.edgewall.org/"> |
1 | 220 ... <li py:attrs="foo">Bar</li> |
221 ... </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
|
222 >>> print tmpl.generate(foo={'class': 'collapse'}) |
1 | 223 <ul> |
224 <li class="collapse">Bar</li> | |
225 </ul> | |
205
b700e5326421
Add doctest to verify that the `py:attrs` directive correctly handles a sequence of `(name, value)` tuples.
cmlenz
parents:
202
diff
changeset
|
226 >>> print tmpl.generate(foo=[('class', 'collapse')]) |
b700e5326421
Add doctest to verify that the `py:attrs` directive correctly handles a sequence of `(name, value)` tuples.
cmlenz
parents:
202
diff
changeset
|
227 <ul> |
b700e5326421
Add doctest to verify that the `py:attrs` directive correctly handles a sequence of `(name, value)` tuples.
cmlenz
parents:
202
diff
changeset
|
228 <li class="collapse">Bar</li> |
b700e5326421
Add doctest to verify that the `py:attrs` directive correctly handles a sequence of `(name, value)` tuples.
cmlenz
parents:
202
diff
changeset
|
229 </ul> |
1 | 230 |
231 If the value evaluates to `None` (or any other non-truth value), no | |
232 attributes are added: | |
233 | |
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
|
234 >>> print tmpl.generate(foo=None) |
1 | 235 <ul> |
236 <li>Bar</li> | |
237 </ul> | |
238 """ | |
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
|
239 __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
|
240 |
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
|
241 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
|
242 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
|
243 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
|
244 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
|
245 if attrs: |
182
2f30ce3fb85e
Renamed `Attributes` to `Attrs` to reduce the verbosity.
cmlenz
parents:
181
diff
changeset
|
246 attrib = Attrs(attrib[:]) |
77
f5ec6d4a61e4
* Simplify implementation of the individual XPath tests (use closures instead of callable classes)
cmlenz
parents:
75
diff
changeset
|
247 if isinstance(attrs, Stream): |
f5ec6d4a61e4
* Simplify implementation of the individual XPath tests (use closures instead of callable classes)
cmlenz
parents:
75
diff
changeset
|
248 try: |
f5ec6d4a61e4
* Simplify implementation of the individual XPath tests (use closures instead of callable classes)
cmlenz
parents:
75
diff
changeset
|
249 attrs = iter(attrs).next() |
f5ec6d4a61e4
* Simplify implementation of the individual XPath tests (use closures instead of callable classes)
cmlenz
parents:
75
diff
changeset
|
250 except StopIteration: |
f5ec6d4a61e4
* Simplify implementation of the individual XPath tests (use closures instead of callable classes)
cmlenz
parents:
75
diff
changeset
|
251 attrs = [] |
f5ec6d4a61e4
* Simplify implementation of the individual XPath tests (use closures instead of callable classes)
cmlenz
parents:
75
diff
changeset
|
252 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
|
253 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
|
254 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
|
255 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
|
256 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
|
257 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
|
258 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
|
259 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
|
260 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
|
261 yield event |
77
f5ec6d4a61e4
* Simplify implementation of the individual XPath tests (use closures instead of callable classes)
cmlenz
parents:
75
diff
changeset
|
262 |
78
46fed54f23cd
Minor improvements to how directives are applied in template processing.
cmlenz
parents:
77
diff
changeset
|
263 return _apply_directives(_generate(), ctxt, directives) |
1 | 264 |
265 | |
266 class ContentDirective(Directive): | |
267 """Implementation of the `py:content` template directive. | |
268 | |
269 This directive replaces the content of the element with the result of | |
270 evaluating the value of the `py:content` attribute: | |
271 | |
61 | 272 >>> tmpl = Template('''<ul xmlns:py="http://markup.edgewall.org/"> |
1 | 273 ... <li py:content="bar">Hello</li> |
274 ... </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
|
275 >>> print tmpl.generate(bar='Bye') |
1 | 276 <ul> |
277 <li>Bye</li> | |
278 </ul> | |
279 """ | |
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
|
280 __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
|
281 |
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
|
282 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
|
283 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
|
284 kind, data, pos = stream.next() |
101 | 285 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
|
286 yield kind, data, pos # emit start tag |
69 | 287 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
|
288 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
|
289 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
|
290 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
|
291 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
|
292 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
|
293 |
78
46fed54f23cd
Minor improvements to how directives are applied in template processing.
cmlenz
parents:
77
diff
changeset
|
294 return _apply_directives(_generate(), ctxt, directives) |
1 | 295 |
296 | |
297 class DefDirective(Directive): | |
298 """Implementation of the `py:def` template directive. | |
299 | |
300 This directive can be used to create "Named Template Functions", which | |
301 are template snippets that are not actually output during normal | |
302 processing, but rather can be expanded from expressions in other places | |
303 in the template. | |
304 | |
305 A named template function can be used just like a normal Python function | |
306 from template expressions: | |
307 | |
61 | 308 >>> tmpl = Template('''<div xmlns:py="http://markup.edgewall.org/"> |
1 | 309 ... <p py:def="echo(greeting, name='world')" class="message"> |
310 ... ${greeting}, ${name}! | |
311 ... </p> | |
90
c835e81c50af
When an expression evaluates to a callable, it is called implicitly.
cmlenz
parents:
89
diff
changeset
|
312 ... ${echo('Hi', name='you')} |
1 | 313 ... </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
|
314 >>> print tmpl.generate(bar='Bye') |
1 | 315 <div> |
316 <p class="message"> | |
90
c835e81c50af
When an expression evaluates to a callable, it is called implicitly.
cmlenz
parents:
89
diff
changeset
|
317 Hi, you! |
1 | 318 </p> |
319 </div> | |
320 | |
90
c835e81c50af
When an expression evaluates to a callable, it is called implicitly.
cmlenz
parents:
89
diff
changeset
|
321 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
|
322 both when defining and when calling it: |
c835e81c50af
When an expression evaluates to a callable, it is called implicitly.
cmlenz
parents:
89
diff
changeset
|
323 |
61 | 324 >>> 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
|
325 ... <p py:def="helloworld" class="message"> |
c835e81c50af
When an expression evaluates to a callable, it is called implicitly.
cmlenz
parents:
89
diff
changeset
|
326 ... Hello, world! |
1 | 327 ... </p> |
90
c835e81c50af
When an expression evaluates to a callable, it is called implicitly.
cmlenz
parents:
89
diff
changeset
|
328 ... ${helloworld} |
1 | 329 ... </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
|
330 >>> print tmpl.generate(bar='Bye') |
1 | 331 <div> |
332 <p class="message"> | |
90
c835e81c50af
When an expression evaluates to a callable, it is called implicitly.
cmlenz
parents:
89
diff
changeset
|
333 Hello, world! |
1 | 334 </p> |
335 </div> | |
336 """ | |
154
8bd5c8cd33e0
* Make sure `py:def` macros don't go out of scope if they are defined inside another directive.
cmlenz
parents:
153
diff
changeset
|
337 __slots__ = ['name', 'args', 'defaults'] |
1 | 338 |
65
b3fdf93057ab
Support the use of directives as elements to reduce the need for using `py:strip`.
cmlenz
parents:
61
diff
changeset
|
339 ATTRIBUTE = 'function' |
b3fdf93057ab
Support the use of directives as elements to reduce the need for using `py:strip`.
cmlenz
parents:
61
diff
changeset
|
340 |
81
d60486018004
Template expressions are now compiled to Python bytecode.
cmlenz
parents:
80
diff
changeset
|
341 def __init__(self, args, filename=None, lineno=-1, offset=-1): |
d60486018004
Template expressions are now compiled to Python bytecode.
cmlenz
parents:
80
diff
changeset
|
342 Directive.__init__(self, None, filename, lineno, offset) |
1 | 343 ast = compiler.parse(args, 'eval').node |
344 self.args = [] | |
345 self.defaults = {} | |
346 if isinstance(ast, compiler.ast.CallFunc): | |
347 self.name = ast.node.name | |
348 for arg in ast.args: | |
349 if isinstance(arg, compiler.ast.Keyword): | |
350 self.args.append(arg.name) | |
165
54a4be707664
Fix handling of keyword arguments in `py:def` directive. Thanks to Christian Boos for reporting the problem and providing the basic patch for this change.
cmlenz
parents:
161
diff
changeset
|
351 self.defaults[arg.name] = Expression(arg.expr, filename, |
54a4be707664
Fix handling of keyword arguments in `py:def` directive. Thanks to Christian Boos for reporting the problem and providing the basic patch for this change.
cmlenz
parents:
161
diff
changeset
|
352 lineno) |
1 | 353 else: |
354 self.args.append(arg.name) | |
355 else: | |
356 self.name = ast.name | |
357 | |
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
|
358 def __call__(self, stream, ctxt, directives): |
154
8bd5c8cd33e0
* Make sure `py:def` macros don't go out of scope if they are defined inside another directive.
cmlenz
parents:
153
diff
changeset
|
359 stream = list(stream) |
1 | 360 |
154
8bd5c8cd33e0
* Make sure `py:def` macros don't go out of scope if they are defined inside another directive.
cmlenz
parents:
153
diff
changeset
|
361 def function(*args, **kwargs): |
8bd5c8cd33e0
* Make sure `py:def` macros don't go out of scope if they are defined inside another directive.
cmlenz
parents:
153
diff
changeset
|
362 scope = {} |
8bd5c8cd33e0
* Make sure `py:def` macros don't go out of scope if they are defined inside another directive.
cmlenz
parents:
153
diff
changeset
|
363 args = list(args) # make mutable |
8bd5c8cd33e0
* Make sure `py:def` macros don't go out of scope if they are defined inside another directive.
cmlenz
parents:
153
diff
changeset
|
364 for name in self.args: |
8bd5c8cd33e0
* Make sure `py:def` macros don't go out of scope if they are defined inside another directive.
cmlenz
parents:
153
diff
changeset
|
365 if args: |
8bd5c8cd33e0
* Make sure `py:def` macros don't go out of scope if they are defined inside another directive.
cmlenz
parents:
153
diff
changeset
|
366 scope[name] = args.pop(0) |
8bd5c8cd33e0
* Make sure `py:def` macros don't go out of scope if they are defined inside another directive.
cmlenz
parents:
153
diff
changeset
|
367 else: |
165
54a4be707664
Fix handling of keyword arguments in `py:def` directive. Thanks to Christian Boos for reporting the problem and providing the basic patch for this change.
cmlenz
parents:
161
diff
changeset
|
368 if name in kwargs: |
54a4be707664
Fix handling of keyword arguments in `py:def` directive. Thanks to Christian Boos for reporting the problem and providing the basic patch for this change.
cmlenz
parents:
161
diff
changeset
|
369 val = kwargs.pop(name) |
54a4be707664
Fix handling of keyword arguments in `py:def` directive. Thanks to Christian Boos for reporting the problem and providing the basic patch for this change.
cmlenz
parents:
161
diff
changeset
|
370 else: |
54a4be707664
Fix handling of keyword arguments in `py:def` directive. Thanks to Christian Boos for reporting the problem and providing the basic patch for this change.
cmlenz
parents:
161
diff
changeset
|
371 val = self.defaults.get(name).evaluate(ctxt) |
54a4be707664
Fix handling of keyword arguments in `py:def` directive. Thanks to Christian Boos for reporting the problem and providing the basic patch for this change.
cmlenz
parents:
161
diff
changeset
|
372 scope[name] = val |
154
8bd5c8cd33e0
* Make sure `py:def` macros don't go out of scope if they are defined inside another directive.
cmlenz
parents:
153
diff
changeset
|
373 ctxt.push(scope) |
8bd5c8cd33e0
* Make sure `py:def` macros don't go out of scope if they are defined inside another directive.
cmlenz
parents:
153
diff
changeset
|
374 for event in _apply_directives(stream, ctxt, directives): |
8bd5c8cd33e0
* Make sure `py:def` macros don't go out of scope if they are defined inside another directive.
cmlenz
parents:
153
diff
changeset
|
375 yield event |
8bd5c8cd33e0
* Make sure `py:def` macros don't go out of scope if they are defined inside another directive.
cmlenz
parents:
153
diff
changeset
|
376 ctxt.pop() |
8bd5c8cd33e0
* Make sure `py:def` macros don't go out of scope if they are defined inside another directive.
cmlenz
parents:
153
diff
changeset
|
377 try: |
8bd5c8cd33e0
* Make sure `py:def` macros don't go out of scope if they are defined inside another directive.
cmlenz
parents:
153
diff
changeset
|
378 function.__name__ = self.name |
8bd5c8cd33e0
* Make sure `py:def` macros don't go out of scope if they are defined inside another directive.
cmlenz
parents:
153
diff
changeset
|
379 except TypeError: |
8bd5c8cd33e0
* Make sure `py:def` macros don't go out of scope if they are defined inside another directive.
cmlenz
parents:
153
diff
changeset
|
380 # Function name can't be set in Python 2.3 |
8bd5c8cd33e0
* Make sure `py:def` macros don't go out of scope if they are defined inside another directive.
cmlenz
parents:
153
diff
changeset
|
381 pass |
8bd5c8cd33e0
* Make sure `py:def` macros don't go out of scope if they are defined inside another directive.
cmlenz
parents:
153
diff
changeset
|
382 |
8bd5c8cd33e0
* Make sure `py:def` macros don't go out of scope if they are defined inside another directive.
cmlenz
parents:
153
diff
changeset
|
383 # Store the function reference in the bottom context frame so that it |
8bd5c8cd33e0
* Make sure `py:def` macros don't go out of scope if they are defined inside another directive.
cmlenz
parents:
153
diff
changeset
|
384 # doesn't get popped off before processing the template has finished |
8bd5c8cd33e0
* Make sure `py:def` macros don't go out of scope if they are defined inside another directive.
cmlenz
parents:
153
diff
changeset
|
385 ctxt.frames[-1][self.name] = function |
8bd5c8cd33e0
* Make sure `py:def` macros don't go out of scope if they are defined inside another directive.
cmlenz
parents:
153
diff
changeset
|
386 |
8bd5c8cd33e0
* Make sure `py:def` macros don't go out of scope if they are defined inside another directive.
cmlenz
parents:
153
diff
changeset
|
387 return [] |
1 | 388 |
172
ff4f0d89eef7
Fix for #30 (trouble using `py:def`inside a match template)
cmlenz
parents:
166
diff
changeset
|
389 def __repr__(self): |
ff4f0d89eef7
Fix for #30 (trouble using `py:def`inside a match template)
cmlenz
parents:
166
diff
changeset
|
390 return '<%s "%s">' % (self.__class__.__name__, self.name) |
ff4f0d89eef7
Fix for #30 (trouble using `py:def`inside a match template)
cmlenz
parents:
166
diff
changeset
|
391 |
1 | 392 |
393 class ForDirective(Directive): | |
31 | 394 """Implementation of the `py:for` template directive for repeating an |
395 element based on an iterable in the context data. | |
1 | 396 |
61 | 397 >>> tmpl = Template('''<ul xmlns:py="http://markup.edgewall.org/"> |
1 | 398 ... <li py:for="item in items">${item}</li> |
399 ... </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
|
400 >>> print tmpl.generate(items=[1, 2, 3]) |
1 | 401 <ul> |
402 <li>1</li><li>2</li><li>3</li> | |
403 </ul> | |
404 """ | |
220
f4943d2babda
Fix for #45 and #46: properly support assignment to nested tuples in `py:for` and `py:with` directives.
cmlenz
parents:
217
diff
changeset
|
405 __slots__ = ['assign'] |
1 | 406 |
65
b3fdf93057ab
Support the use of directives as elements to reduce the need for using `py:strip`.
cmlenz
parents:
61
diff
changeset
|
407 ATTRIBUTE = 'each' |
b3fdf93057ab
Support the use of directives as elements to reduce the need for using `py:strip`.
cmlenz
parents:
61
diff
changeset
|
408 |
81
d60486018004
Template expressions are now compiled to Python bytecode.
cmlenz
parents:
80
diff
changeset
|
409 def __init__(self, value, filename=None, lineno=-1, offset=-1): |
181
e103b75a96ce
Some error message improvements for template directives. Thanks to Christian Boos for the patch!
cmlenz
parents:
179
diff
changeset
|
410 if ' in ' not in value: |
e103b75a96ce
Some error message improvements for template directives. Thanks to Christian Boos for the patch!
cmlenz
parents:
179
diff
changeset
|
411 raise TemplateSyntaxError('"in" keyword missing in "for" directive', |
e103b75a96ce
Some error message improvements for template directives. Thanks to Christian Boos for the patch!
cmlenz
parents:
179
diff
changeset
|
412 filename, lineno, offset) |
220
f4943d2babda
Fix for #45 and #46: properly support assignment to nested tuples in `py:for` and `py:with` directives.
cmlenz
parents:
217
diff
changeset
|
413 assign, value = value.split(' in ', 1) |
f4943d2babda
Fix for #45 and #46: properly support assignment to nested tuples in `py:for` and `py:with` directives.
cmlenz
parents:
217
diff
changeset
|
414 ast = compiler.parse(assign, 'exec') |
f4943d2babda
Fix for #45 and #46: properly support assignment to nested tuples in `py:for` and `py:with` directives.
cmlenz
parents:
217
diff
changeset
|
415 self.assign = _assignment(ast.node.nodes[0].expr) |
140
c1f4390d50f8
Fix bug in HTML serializer, plus some other minor tweaks.
cmlenz
parents:
139
diff
changeset
|
416 Directive.__init__(self, value.strip(), filename, lineno, offset) |
1 | 417 |
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
|
418 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
|
419 iterable = self.expr.evaluate(ctxt) |
101 | 420 if iterable is None: |
421 return | |
422 | |
220
f4943d2babda
Fix for #45 and #46: properly support assignment to nested tuples in `py:for` and `py:with` directives.
cmlenz
parents:
217
diff
changeset
|
423 assign = self.assign |
101 | 424 scope = {} |
425 stream = list(stream) | |
426 for item in iter(iterable): | |
220
f4943d2babda
Fix for #45 and #46: properly support assignment to nested tuples in `py:for` and `py:with` directives.
cmlenz
parents:
217
diff
changeset
|
427 assign(scope, item) |
101 | 428 ctxt.push(scope) |
429 for event in _apply_directives(stream, ctxt, directives): | |
430 yield event | |
431 ctxt.pop() | |
1 | 432 |
433 def __repr__(self): | |
434 return '<%s "%s in %s">' % (self.__class__.__name__, | |
435 ', '.join(self.targets), self.expr.source) | |
436 | |
437 | |
438 class IfDirective(Directive): | |
31 | 439 """Implementation of the `py:if` template directive for conditionally |
440 excluding elements from being output. | |
1 | 441 |
61 | 442 >>> tmpl = Template('''<div xmlns:py="http://markup.edgewall.org/"> |
1 | 443 ... <b py:if="foo">${bar}</b> |
444 ... </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
|
445 >>> print tmpl.generate(foo=True, bar='Hello') |
1 | 446 <div> |
447 <b>Hello</b> | |
448 </div> | |
449 """ | |
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
|
450 __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
|
451 |
65
b3fdf93057ab
Support the use of directives as elements to reduce the need for using `py:strip`.
cmlenz
parents:
61
diff
changeset
|
452 ATTRIBUTE = 'test' |
b3fdf93057ab
Support the use of directives as elements to reduce the need for using `py:strip`.
cmlenz
parents:
61
diff
changeset
|
453 |
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
|
454 def __call__(self, stream, ctxt, directives): |
1 | 455 if self.expr.evaluate(ctxt): |
78
46fed54f23cd
Minor improvements to how directives are applied in template processing.
cmlenz
parents:
77
diff
changeset
|
456 return _apply_directives(stream, ctxt, directives) |
1 | 457 return [] |
458 | |
459 | |
460 class MatchDirective(Directive): | |
461 """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
|
462 |
61 | 463 >>> 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
|
464 ... <span py:match="greeting"> |
1 | 465 ... Hello ${select('@name')} |
466 ... </span> | |
467 ... <greeting name="Dude" /> | |
468 ... </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
|
469 >>> print tmpl.generate() |
1 | 470 <div> |
471 <span> | |
472 Hello Dude | |
473 </span> | |
474 </div> | |
475 """ | |
199
71ce4b5335ba
* Removed storage of substream in `MatchDirective`, because it's already being stored in the match templates (related to #39).
cmlenz
parents:
193
diff
changeset
|
476 __slots__ = ['path'] |
1 | 477 |
65
b3fdf93057ab
Support the use of directives as elements to reduce the need for using `py:strip`.
cmlenz
parents:
61
diff
changeset
|
478 ATTRIBUTE = 'path' |
b3fdf93057ab
Support the use of directives as elements to reduce the need for using `py:strip`.
cmlenz
parents:
61
diff
changeset
|
479 |
81
d60486018004
Template expressions are now compiled to Python bytecode.
cmlenz
parents:
80
diff
changeset
|
480 def __init__(self, value, filename=None, lineno=-1, offset=-1): |
d60486018004
Template expressions are now compiled to Python bytecode.
cmlenz
parents:
80
diff
changeset
|
481 Directive.__init__(self, None, filename, lineno, offset) |
139
8332287b5508
Implement position reporting for XPath syntax errors. Closes #20.
cmlenz
parents:
134
diff
changeset
|
482 self.path = Path(value, filename, lineno) |
1 | 483 |
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
|
484 def __call__(self, stream, ctxt, directives): |
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
|
485 ctxt._match_templates.append((self.path.test(ignore_context=True), |
199
71ce4b5335ba
* Removed storage of substream in `MatchDirective`, because it's already being stored in the match templates (related to #39).
cmlenz
parents:
193
diff
changeset
|
486 self.path, list(stream), directives)) |
1 | 487 return [] |
488 | |
489 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
|
490 return '<%s "%s">' % (self.__class__.__name__, self.path.source) |
1 | 491 |
492 | |
493 class ReplaceDirective(Directive): | |
494 """Implementation of the `py:replace` template directive. | |
495 | |
31 | 496 This directive replaces the element with the result of evaluating the |
497 value of the `py:replace` attribute: | |
498 | |
61 | 499 >>> tmpl = Template('''<div xmlns:py="http://markup.edgewall.org/"> |
1 | 500 ... <span py:replace="bar">Hello</span> |
501 ... </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
|
502 >>> print tmpl.generate(bar='Bye') |
1 | 503 <div> |
504 Bye | |
505 </div> | |
506 | |
507 This directive is equivalent to `py:content` combined with `py:strip`, | |
508 providing a less verbose way to achieve the same effect: | |
509 | |
61 | 510 >>> tmpl = Template('''<div xmlns:py="http://markup.edgewall.org/"> |
1 | 511 ... <span py:content="bar" py:strip="">Hello</span> |
512 ... </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
|
513 >>> print tmpl.generate(bar='Bye') |
1 | 514 <div> |
515 Bye | |
516 </div> | |
517 """ | |
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
|
518 __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
|
519 |
54 | 520 def __call__(self, stream, ctxt, directives): |
1 | 521 kind, data, pos = stream.next() |
69 | 522 yield EXPR, self.expr, pos |
1 | 523 |
524 | |
525 class StripDirective(Directive): | |
526 """Implementation of the `py:strip` template directive. | |
527 | |
528 When the value of the `py:strip` attribute evaluates to `True`, the element | |
529 is stripped from the output | |
530 | |
61 | 531 >>> tmpl = Template('''<div xmlns:py="http://markup.edgewall.org/"> |
1 | 532 ... <div py:strip="True"><b>foo</b></div> |
533 ... </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
|
534 >>> print tmpl.generate() |
1 | 535 <div> |
536 <b>foo</b> | |
537 </div> | |
538 | |
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
|
539 Leaving the attribute value empty is equivalent to a truth value. |
1 | 540 |
541 This directive is particulary interesting for named template functions or | |
542 match templates that do not generate a top-level element: | |
543 | |
61 | 544 >>> tmpl = Template('''<div xmlns:py="http://markup.edgewall.org/"> |
1 | 545 ... <div py:def="echo(what)" py:strip=""> |
546 ... <b>${what}</b> | |
547 ... </div> | |
548 ... ${echo('foo')} | |
549 ... </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
|
550 >>> print tmpl.generate() |
1 | 551 <div> |
552 <b>foo</b> | |
553 </div> | |
554 """ | |
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
|
555 __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
|
556 |
54 | 557 def __call__(self, stream, ctxt, directives): |
78
46fed54f23cd
Minor improvements to how directives are applied in template processing.
cmlenz
parents:
77
diff
changeset
|
558 def _generate(): |
46fed54f23cd
Minor improvements to how directives are applied in template processing.
cmlenz
parents:
77
diff
changeset
|
559 if self.expr: |
46fed54f23cd
Minor improvements to how directives are applied in template processing.
cmlenz
parents:
77
diff
changeset
|
560 strip = self.expr.evaluate(ctxt) |
46fed54f23cd
Minor improvements to how directives are applied in template processing.
cmlenz
parents:
77
diff
changeset
|
561 else: |
46fed54f23cd
Minor improvements to how directives are applied in template processing.
cmlenz
parents:
77
diff
changeset
|
562 strip = True |
46fed54f23cd
Minor improvements to how directives are applied in template processing.
cmlenz
parents:
77
diff
changeset
|
563 if strip: |
46fed54f23cd
Minor improvements to how directives are applied in template processing.
cmlenz
parents:
77
diff
changeset
|
564 stream.next() # skip start tag |
46fed54f23cd
Minor improvements to how directives are applied in template processing.
cmlenz
parents:
77
diff
changeset
|
565 previous = stream.next() |
46fed54f23cd
Minor improvements to how directives are applied in template processing.
cmlenz
parents:
77
diff
changeset
|
566 for event in stream: |
46fed54f23cd
Minor improvements to how directives are applied in template processing.
cmlenz
parents:
77
diff
changeset
|
567 yield previous |
46fed54f23cd
Minor improvements to how directives are applied in template processing.
cmlenz
parents:
77
diff
changeset
|
568 previous = event |
46fed54f23cd
Minor improvements to how directives are applied in template processing.
cmlenz
parents:
77
diff
changeset
|
569 else: |
46fed54f23cd
Minor improvements to how directives are applied in template processing.
cmlenz
parents:
77
diff
changeset
|
570 for event in stream: |
46fed54f23cd
Minor improvements to how directives are applied in template processing.
cmlenz
parents:
77
diff
changeset
|
571 yield event |
46fed54f23cd
Minor improvements to how directives are applied in template processing.
cmlenz
parents:
77
diff
changeset
|
572 |
46fed54f23cd
Minor improvements to how directives are applied in template processing.
cmlenz
parents:
77
diff
changeset
|
573 return _apply_directives(_generate(), ctxt, directives) |
1 | 574 |
575 | |
44
436e30c8420b
implement `py:choose/when/otherwise` directives for conditionally selecting one of several blocks
mgood
parents:
38
diff
changeset
|
576 class ChooseDirective(Directive): |
436e30c8420b
implement `py:choose/when/otherwise` directives for conditionally selecting one of several blocks
mgood
parents:
38
diff
changeset
|
577 """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
|
578 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
|
579 |
44
436e30c8420b
implement `py:choose/when/otherwise` directives for conditionally selecting one of several blocks
mgood
parents:
38
diff
changeset
|
580 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
|
581 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
|
582 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
|
583 `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
|
584 |
61 | 585 >>> 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
|
586 ... py:choose=""> |
436e30c8420b
implement `py:choose/when/otherwise` directives for conditionally selecting one of several blocks
mgood
parents:
38
diff
changeset
|
587 ... <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
|
588 ... <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
|
589 ... <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
|
590 ... </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
|
591 >>> print tmpl.generate() |
44
436e30c8420b
implement `py:choose/when/otherwise` directives for conditionally selecting one of several blocks
mgood
parents:
38
diff
changeset
|
592 <div> |
436e30c8420b
implement `py:choose/when/otherwise` directives for conditionally selecting one of several blocks
mgood
parents:
38
diff
changeset
|
593 <span>1</span> |
436e30c8420b
implement `py:choose/when/otherwise` directives for conditionally selecting one of several blocks
mgood
parents:
38
diff
changeset
|
594 </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
|
595 |
44
436e30c8420b
implement `py:choose/when/otherwise` directives for conditionally selecting one of several blocks
mgood
parents:
38
diff
changeset
|
596 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
|
597 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
|
598 |
61 | 599 >>> 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
|
600 ... py:choose="2"> |
436e30c8420b
implement `py:choose/when/otherwise` directives for conditionally selecting one of several blocks
mgood
parents:
38
diff
changeset
|
601 ... <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
|
602 ... <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
|
603 ... </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
|
604 >>> print tmpl.generate() |
44
436e30c8420b
implement `py:choose/when/otherwise` directives for conditionally selecting one of several blocks
mgood
parents:
38
diff
changeset
|
605 <div> |
436e30c8420b
implement `py:choose/when/otherwise` directives for conditionally selecting one of several blocks
mgood
parents:
38
diff
changeset
|
606 <span>2</span> |
436e30c8420b
implement `py:choose/when/otherwise` directives for conditionally selecting one of several blocks
mgood
parents:
38
diff
changeset
|
607 </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
|
608 |
44
436e30c8420b
implement `py:choose/when/otherwise` directives for conditionally selecting one of several blocks
mgood
parents:
38
diff
changeset
|
609 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
|
610 `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
|
611 `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
|
612 """ |
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
|
613 __slots__ = ['matched', 'value'] |
44
436e30c8420b
implement `py:choose/when/otherwise` directives for conditionally selecting one of several blocks
mgood
parents:
38
diff
changeset
|
614 |
65
b3fdf93057ab
Support the use of directives as elements to reduce the need for using `py:strip`.
cmlenz
parents:
61
diff
changeset
|
615 ATTRIBUTE = 'test' |
b3fdf93057ab
Support the use of directives as elements to reduce the need for using `py:strip`.
cmlenz
parents:
61
diff
changeset
|
616 |
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
|
617 def __call__(self, stream, ctxt, directives): |
202
92353f28ae54
Remove the (hopefully) last instance where directives store state in instance variables, allowing templates to be cached and reused in a threadsafe manner. Closes #39. Many thanks to Christian Boos for the patch!
cmlenz
parents:
199
diff
changeset
|
618 frame = dict({'_choose.matched': False}) |
44
436e30c8420b
implement `py:choose/when/otherwise` directives for conditionally selecting one of several blocks
mgood
parents:
38
diff
changeset
|
619 if self.expr: |
202
92353f28ae54
Remove the (hopefully) last instance where directives store state in instance variables, allowing templates to be cached and reused in a threadsafe manner. Closes #39. Many thanks to Christian Boos for the patch!
cmlenz
parents:
199
diff
changeset
|
620 frame['_choose.value'] = self.expr.evaluate(ctxt) |
92353f28ae54
Remove the (hopefully) last instance where directives store state in instance variables, allowing templates to be cached and reused in a threadsafe manner. Closes #39. Many thanks to Christian Boos for the patch!
cmlenz
parents:
199
diff
changeset
|
621 ctxt.push(frame) |
78
46fed54f23cd
Minor improvements to how directives are applied in template processing.
cmlenz
parents:
77
diff
changeset
|
622 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
|
623 yield event |
436e30c8420b
implement `py:choose/when/otherwise` directives for conditionally selecting one of several blocks
mgood
parents:
38
diff
changeset
|
624 ctxt.pop() |
436e30c8420b
implement `py:choose/when/otherwise` directives for conditionally selecting one of several blocks
mgood
parents:
38
diff
changeset
|
625 |
436e30c8420b
implement `py:choose/when/otherwise` directives for conditionally selecting one of several blocks
mgood
parents:
38
diff
changeset
|
626 |
436e30c8420b
implement `py:choose/when/otherwise` directives for conditionally selecting one of several blocks
mgood
parents:
38
diff
changeset
|
627 class WhenDirective(Directive): |
436e30c8420b
implement `py:choose/when/otherwise` directives for conditionally selecting one of several blocks
mgood
parents:
38
diff
changeset
|
628 """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
|
629 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
|
630 |
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
|
631 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
|
632 """ |
65
b3fdf93057ab
Support the use of directives as elements to reduce the need for using `py:strip`.
cmlenz
parents:
61
diff
changeset
|
633 |
b3fdf93057ab
Support the use of directives as elements to reduce the need for using `py:strip`.
cmlenz
parents:
61
diff
changeset
|
634 ATTRIBUTE = 'test' |
b3fdf93057ab
Support the use of directives as elements to reduce the need for using `py:strip`.
cmlenz
parents:
61
diff
changeset
|
635 |
54 | 636 def __call__(self, stream, ctxt, directives): |
202
92353f28ae54
Remove the (hopefully) last instance where directives store state in instance variables, allowing templates to be cached and reused in a threadsafe manner. Closes #39. Many thanks to Christian Boos for the patch!
cmlenz
parents:
199
diff
changeset
|
637 matched, frame = ctxt._find('_choose.matched') |
92353f28ae54
Remove the (hopefully) last instance where directives store state in instance variables, allowing templates to be cached and reused in a threadsafe manner. Closes #39. Many thanks to Christian Boos for the patch!
cmlenz
parents:
199
diff
changeset
|
638 if not frame: |
181
e103b75a96ce
Some error message improvements for template directives. Thanks to Christian Boos for the patch!
cmlenz
parents:
179
diff
changeset
|
639 raise TemplateSyntaxError('"when" directives can only be used ' |
e103b75a96ce
Some error message improvements for template directives. Thanks to Christian Boos for the patch!
cmlenz
parents:
179
diff
changeset
|
640 'inside a "choose" directive', |
e103b75a96ce
Some error message improvements for template directives. Thanks to Christian Boos for the patch!
cmlenz
parents:
179
diff
changeset
|
641 *stream.next()[2]) |
202
92353f28ae54
Remove the (hopefully) last instance where directives store state in instance variables, allowing templates to be cached and reused in a threadsafe manner. Closes #39. Many thanks to Christian Boos for the patch!
cmlenz
parents:
199
diff
changeset
|
642 if matched: |
44
436e30c8420b
implement `py:choose/when/otherwise` directives for conditionally selecting one of several blocks
mgood
parents:
38
diff
changeset
|
643 return [] |
181
e103b75a96ce
Some error message improvements for template directives. Thanks to Christian Boos for the patch!
cmlenz
parents:
179
diff
changeset
|
644 if not self.expr: |
e103b75a96ce
Some error message improvements for template directives. Thanks to Christian Boos for the patch!
cmlenz
parents:
179
diff
changeset
|
645 raise TemplateSyntaxError('"when" directive has no test condition', |
e103b75a96ce
Some error message improvements for template directives. Thanks to Christian Boos for the patch!
cmlenz
parents:
179
diff
changeset
|
646 *stream.next()[2]) |
44
436e30c8420b
implement `py:choose/when/otherwise` directives for conditionally selecting one of several blocks
mgood
parents:
38
diff
changeset
|
647 value = self.expr.evaluate(ctxt) |
202
92353f28ae54
Remove the (hopefully) last instance where directives store state in instance variables, allowing templates to be cached and reused in a threadsafe manner. Closes #39. Many thanks to Christian Boos for the patch!
cmlenz
parents:
199
diff
changeset
|
648 if '_choose.value' in frame: |
92353f28ae54
Remove the (hopefully) last instance where directives store state in instance variables, allowing templates to be cached and reused in a threadsafe manner. Closes #39. Many thanks to Christian Boos for the patch!
cmlenz
parents:
199
diff
changeset
|
649 matched = (value == frame['_choose.value']) |
92353f28ae54
Remove the (hopefully) last instance where directives store state in instance variables, allowing templates to be cached and reused in a threadsafe manner. Closes #39. Many thanks to Christian Boos for the patch!
cmlenz
parents:
199
diff
changeset
|
650 else: |
92353f28ae54
Remove the (hopefully) last instance where directives store state in instance variables, allowing templates to be cached and reused in a threadsafe manner. Closes #39. Many thanks to Christian Boos for the patch!
cmlenz
parents:
199
diff
changeset
|
651 matched = bool(value) |
92353f28ae54
Remove the (hopefully) last instance where directives store state in instance variables, allowing templates to be cached and reused in a threadsafe manner. Closes #39. Many thanks to Christian Boos for the patch!
cmlenz
parents:
199
diff
changeset
|
652 frame['_choose.matched'] = matched |
92353f28ae54
Remove the (hopefully) last instance where directives store state in instance variables, allowing templates to be cached and reused in a threadsafe manner. Closes #39. Many thanks to Christian Boos for the patch!
cmlenz
parents:
199
diff
changeset
|
653 if not matched: |
92353f28ae54
Remove the (hopefully) last instance where directives store state in instance variables, allowing templates to be cached and reused in a threadsafe manner. Closes #39. Many thanks to Christian Boos for the patch!
cmlenz
parents:
199
diff
changeset
|
654 return [] |
92353f28ae54
Remove the (hopefully) last instance where directives store state in instance variables, allowing templates to be cached and reused in a threadsafe manner. Closes #39. Many thanks to Christian Boos for the patch!
cmlenz
parents:
199
diff
changeset
|
655 |
92353f28ae54
Remove the (hopefully) last instance where directives store state in instance variables, allowing templates to be cached and reused in a threadsafe manner. Closes #39. Many thanks to Christian Boos for the patch!
cmlenz
parents:
199
diff
changeset
|
656 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
|
657 |
436e30c8420b
implement `py:choose/when/otherwise` directives for conditionally selecting one of several blocks
mgood
parents:
38
diff
changeset
|
658 |
436e30c8420b
implement `py:choose/when/otherwise` directives for conditionally selecting one of several blocks
mgood
parents:
38
diff
changeset
|
659 class OtherwiseDirective(Directive): |
436e30c8420b
implement `py:choose/when/otherwise` directives for conditionally selecting one of several blocks
mgood
parents:
38
diff
changeset
|
660 """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
|
661 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
|
662 |
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
|
663 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
|
664 """ |
54 | 665 def __call__(self, stream, ctxt, directives): |
202
92353f28ae54
Remove the (hopefully) last instance where directives store state in instance variables, allowing templates to be cached and reused in a threadsafe manner. Closes #39. Many thanks to Christian Boos for the patch!
cmlenz
parents:
199
diff
changeset
|
666 matched, frame = ctxt._find('_choose.matched') |
92353f28ae54
Remove the (hopefully) last instance where directives store state in instance variables, allowing templates to be cached and reused in a threadsafe manner. Closes #39. Many thanks to Christian Boos for the patch!
cmlenz
parents:
199
diff
changeset
|
667 if not frame: |
181
e103b75a96ce
Some error message improvements for template directives. Thanks to Christian Boos for the patch!
cmlenz
parents:
179
diff
changeset
|
668 raise TemplateSyntaxError('an "otherwise" directive can only be ' |
e103b75a96ce
Some error message improvements for template directives. Thanks to Christian Boos for the patch!
cmlenz
parents:
179
diff
changeset
|
669 'used inside a "choose" directive', |
166
d43f50402cf2
Better error reporting for errors in directive expressions, and when `py:otherwise`/`py:when` are used outside a `py:choose` directive. Thanks to Christian Boos for the initial patch.
cmlenz
parents:
165
diff
changeset
|
670 *stream.next()[2]) |
202
92353f28ae54
Remove the (hopefully) last instance where directives store state in instance variables, allowing templates to be cached and reused in a threadsafe manner. Closes #39. Many thanks to Christian Boos for the patch!
cmlenz
parents:
199
diff
changeset
|
671 if matched: |
44
436e30c8420b
implement `py:choose/when/otherwise` directives for conditionally selecting one of several blocks
mgood
parents:
38
diff
changeset
|
672 return [] |
202
92353f28ae54
Remove the (hopefully) last instance where directives store state in instance variables, allowing templates to be cached and reused in a threadsafe manner. Closes #39. Many thanks to Christian Boos for the patch!
cmlenz
parents:
199
diff
changeset
|
673 frame['_choose.matched'] = True |
92353f28ae54
Remove the (hopefully) last instance where directives store state in instance variables, allowing templates to be cached and reused in a threadsafe manner. Closes #39. Many thanks to Christian Boos for the patch!
cmlenz
parents:
199
diff
changeset
|
674 |
78
46fed54f23cd
Minor improvements to how directives are applied in template processing.
cmlenz
parents:
77
diff
changeset
|
675 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
|
676 |
436e30c8420b
implement `py:choose/when/otherwise` directives for conditionally selecting one of several blocks
mgood
parents:
38
diff
changeset
|
677 |
104
f12e7987d7f4
Added `py:with` directive based on Jonas' patch in #17.
cmlenz
parents:
101
diff
changeset
|
678 class WithDirective(Directive): |
f12e7987d7f4
Added `py:with` directive based on Jonas' patch in #17.
cmlenz
parents:
101
diff
changeset
|
679 """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
|
680 shorthand access to variables and expressions. |
f12e7987d7f4
Added `py:with` directive based on Jonas' patch in #17.
cmlenz
parents:
101
diff
changeset
|
681 |
f12e7987d7f4
Added `py:with` directive based on Jonas' patch in #17.
cmlenz
parents:
101
diff
changeset
|
682 >>> 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
|
683 ... <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
|
684 ... </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
|
685 >>> print tmpl.generate(x=42) |
104
f12e7987d7f4
Added `py:with` directive based on Jonas' patch in #17.
cmlenz
parents:
101
diff
changeset
|
686 <div> |
f12e7987d7f4
Added `py:with` directive based on Jonas' patch in #17.
cmlenz
parents:
101
diff
changeset
|
687 <span>42 7 52</span> |
f12e7987d7f4
Added `py:with` directive based on Jonas' patch in #17.
cmlenz
parents:
101
diff
changeset
|
688 </div> |
f12e7987d7f4
Added `py:with` directive based on Jonas' patch in #17.
cmlenz
parents:
101
diff
changeset
|
689 """ |
f12e7987d7f4
Added `py:with` directive based on Jonas' patch in #17.
cmlenz
parents:
101
diff
changeset
|
690 __slots__ = ['vars'] |
f12e7987d7f4
Added `py:with` directive based on Jonas' patch in #17.
cmlenz
parents:
101
diff
changeset
|
691 |
f12e7987d7f4
Added `py:with` directive based on Jonas' patch in #17.
cmlenz
parents:
101
diff
changeset
|
692 ATTRIBUTE = 'vars' |
f12e7987d7f4
Added `py:with` directive based on Jonas' patch in #17.
cmlenz
parents:
101
diff
changeset
|
693 |
f12e7987d7f4
Added `py:with` directive based on Jonas' patch in #17.
cmlenz
parents:
101
diff
changeset
|
694 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
|
695 Directive.__init__(self, None, filename, lineno, offset) |
f12e7987d7f4
Added `py:with` directive based on Jonas' patch in #17.
cmlenz
parents:
101
diff
changeset
|
696 self.vars = [] |
190 | 697 value = value.strip() |
104
f12e7987d7f4
Added `py:with` directive based on Jonas' patch in #17.
cmlenz
parents:
101
diff
changeset
|
698 try: |
190 | 699 ast = compiler.parse(value, 'exec').node |
700 for node in ast.nodes: | |
701 if isinstance(node, compiler.ast.Discard): | |
702 continue | |
703 elif not isinstance(node, compiler.ast.Assign): | |
704 raise TemplateSyntaxError('only assignment allowed in ' | |
705 'value of the "with" directive', | |
706 filename, lineno, offset) | |
220
f4943d2babda
Fix for #45 and #46: properly support assignment to nested tuples in `py:for` and `py:with` directives.
cmlenz
parents:
217
diff
changeset
|
707 self.vars.append(([_assignment(n) for n in node.nodes], |
190 | 708 Expression(node.expr, filename, lineno))) |
104
f12e7987d7f4
Added `py:with` directive based on Jonas' patch in #17.
cmlenz
parents:
101
diff
changeset
|
709 except SyntaxError, err: |
190 | 710 err.msg += ' in expression "%s" of "%s" directive' % (value, |
711 self.tagname) | |
104
f12e7987d7f4
Added `py:with` directive based on Jonas' patch in #17.
cmlenz
parents:
101
diff
changeset
|
712 raise TemplateSyntaxError(err, filename, lineno, |
f12e7987d7f4
Added `py:with` directive based on Jonas' patch in #17.
cmlenz
parents:
101
diff
changeset
|
713 offset + (err.offset or 0)) |
f12e7987d7f4
Added `py:with` directive based on Jonas' patch in #17.
cmlenz
parents:
101
diff
changeset
|
714 |
f12e7987d7f4
Added `py:with` directive based on Jonas' patch in #17.
cmlenz
parents:
101
diff
changeset
|
715 def __call__(self, stream, ctxt, directives): |
190 | 716 frame = {} |
717 ctxt.push(frame) | |
220
f4943d2babda
Fix for #45 and #46: properly support assignment to nested tuples in `py:for` and `py:with` directives.
cmlenz
parents:
217
diff
changeset
|
718 for targets, expr in self.vars: |
190 | 719 value = expr.evaluate(ctxt, nocall=True) |
220
f4943d2babda
Fix for #45 and #46: properly support assignment to nested tuples in `py:for` and `py:with` directives.
cmlenz
parents:
217
diff
changeset
|
720 for assign in targets: |
f4943d2babda
Fix for #45 and #46: properly support assignment to nested tuples in `py:for` and `py:with` directives.
cmlenz
parents:
217
diff
changeset
|
721 assign(frame, value) |
104
f12e7987d7f4
Added `py:with` directive based on Jonas' patch in #17.
cmlenz
parents:
101
diff
changeset
|
722 for event in _apply_directives(stream, ctxt, directives): |
f12e7987d7f4
Added `py:with` directive based on Jonas' patch in #17.
cmlenz
parents:
101
diff
changeset
|
723 yield event |
f12e7987d7f4
Added `py:with` directive based on Jonas' patch in #17.
cmlenz
parents:
101
diff
changeset
|
724 ctxt.pop() |
f12e7987d7f4
Added `py:with` directive based on Jonas' patch in #17.
cmlenz
parents:
101
diff
changeset
|
725 |
f12e7987d7f4
Added `py:with` directive based on Jonas' patch in #17.
cmlenz
parents:
101
diff
changeset
|
726 def __repr__(self): |
f12e7987d7f4
Added `py:with` directive based on Jonas' patch in #17.
cmlenz
parents:
101
diff
changeset
|
727 return '<%s "%s">' % (self.__class__.__name__, |
f12e7987d7f4
Added `py:with` directive based on Jonas' patch in #17.
cmlenz
parents:
101
diff
changeset
|
728 '; '.join(['%s = %s' % (name, expr.source) |
f12e7987d7f4
Added `py:with` directive based on Jonas' patch in #17.
cmlenz
parents:
101
diff
changeset
|
729 for name, expr in self.vars])) |
f12e7987d7f4
Added `py:with` directive based on Jonas' patch in #17.
cmlenz
parents:
101
diff
changeset
|
730 |
f12e7987d7f4
Added `py:with` directive based on Jonas' patch in #17.
cmlenz
parents:
101
diff
changeset
|
731 |
1 | 732 class Template(object): |
733 """Can parse a template and transform it into the corresponding output | |
734 based on context data. | |
735 """ | |
61 | 736 NAMESPACE = Namespace('http://markup.edgewall.org/') |
1 | 737 |
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
|
738 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
|
739 SUB = StreamEventKind('SUB') # a "subprogram" |
10
f77f7a91aa46
Moved the template-specific stream event kinds into the template module.
cmlenz
parents:
6
diff
changeset
|
740 |
1 | 741 directives = [('def', DefDirective), |
742 ('match', MatchDirective), | |
120 | 743 ('when', WhenDirective), |
744 ('otherwise', OtherwiseDirective), | |
1 | 745 ('for', ForDirective), |
746 ('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
|
747 ('choose', ChooseDirective), |
104
f12e7987d7f4
Added `py:with` directive based on Jonas' patch in #17.
cmlenz
parents:
101
diff
changeset
|
748 ('with', WithDirective), |
1 | 749 ('replace', ReplaceDirective), |
750 ('content', ContentDirective), | |
751 ('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
|
752 ('strip', StripDirective)] |
1 | 753 _dir_by_name = dict(directives) |
754 _dir_order = [directive[1] for directive in directives] | |
755 | |
21
b4d17897d053
* Include paths are now interpreted relative to the path of the including template. Closes #3.
cmlenz
parents:
18
diff
changeset
|
756 def __init__(self, source, basedir=None, filename=None): |
1 | 757 """Initialize a template from either a string or a file-like object.""" |
758 if isinstance(source, basestring): | |
759 self.source = StringIO(source) | |
760 else: | |
761 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
|
762 self.basedir = basedir |
172
ff4f0d89eef7
Fix for #30 (trouble using `py:def`inside a match template)
cmlenz
parents:
166
diff
changeset
|
763 self.filename = filename |
21
b4d17897d053
* Include paths are now interpreted relative to the path of the including template. Closes #3.
cmlenz
parents:
18
diff
changeset
|
764 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
|
765 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
|
766 else: |
172
ff4f0d89eef7
Fix for #30 (trouble using `py:def`inside a match template)
cmlenz
parents:
166
diff
changeset
|
767 self.filepath = None |
1 | 768 |
23
d88358f719fa
Separate match and eval filters from the include and user-supplied filters.
cmlenz
parents:
22
diff
changeset
|
769 self.filters = [] |
214
ab407defd204
Cosmetic change to internal template initialization.
cmlenz
parents:
213
diff
changeset
|
770 self.stream = self.parse() |
1 | 771 |
772 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
|
773 return '<%s "%s">' % (self.__class__.__name__, self.filename) |
1 | 774 |
775 def parse(self): | |
776 """Parse the template. | |
777 | |
778 The parsing stage parses the XML template and constructs a list of | |
779 directives that will be executed in the render stage. The input is | |
780 split up into literal output (markup that does not depend on the | |
781 context data) and actual directives (commands or variable | |
782 substitution). | |
783 """ | |
784 stream = [] # list of events of the "compiled" template | |
785 dirmap = {} # temporary mapping of directives to elements | |
786 ns_prefix = {} | |
787 depth = 0 | |
788 | |
21
b4d17897d053
* Include paths are now interpreted relative to the path of the including template. Closes #3.
cmlenz
parents:
18
diff
changeset
|
789 for kind, data, pos in XMLParser(self.source, filename=self.filename): |
1 | 790 |
69 | 791 if kind is START_NS: |
1 | 792 # Strip out the namespace declaration for template directives |
793 prefix, uri = data | |
794 if uri == self.NAMESPACE: | |
795 ns_prefix[prefix] = uri | |
796 else: | |
797 stream.append((kind, data, pos)) | |
798 | |
69 | 799 elif kind is END_NS: |
1 | 800 if data in ns_prefix: |
801 del ns_prefix[data] | |
802 else: | |
803 stream.append((kind, data, pos)) | |
804 | |
69 | 805 elif kind is START: |
1 | 806 # Record any directive attributes in start tags |
807 tag, attrib = data | |
808 directives = [] | |
65
b3fdf93057ab
Support the use of directives as elements to reduce the need for using `py:strip`.
cmlenz
parents:
61
diff
changeset
|
809 strip = False |
b3fdf93057ab
Support the use of directives as elements to reduce the need for using `py:strip`.
cmlenz
parents:
61
diff
changeset
|
810 |
b3fdf93057ab
Support the use of directives as elements to reduce the need for using `py:strip`.
cmlenz
parents:
61
diff
changeset
|
811 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
|
812 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
|
813 if cls is None: |
222
71741151e42b
`BadDirectiveError` no longer expects a `QName` as the directive name; a regular string should do.
cmlenz
parents:
221
diff
changeset
|
814 raise BadDirectiveError(tag.localname, pos[0], pos[1]) |
66
59eb24184e9c
Switch copyright to Edgewall and URLs to markup.edgewall.org.
cmlenz
parents:
65
diff
changeset
|
815 value = attrib.get(getattr(cls, 'ATTRIBUTE', None), '') |
81
d60486018004
Template expressions are now compiled to Python bytecode.
cmlenz
parents:
80
diff
changeset
|
816 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
|
817 strip = True |
b3fdf93057ab
Support the use of directives as elements to reduce the need for using `py:strip`.
cmlenz
parents:
61
diff
changeset
|
818 |
1 | 819 new_attrib = [] |
820 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
|
821 if name in self.NAMESPACE: |
1 | 822 cls = self._dir_by_name.get(name.localname) |
823 if cls is None: | |
222
71741151e42b
`BadDirectiveError` no longer expects a `QName` as the directive name; a regular string should do.
cmlenz
parents:
221
diff
changeset
|
824 raise BadDirectiveError(name.localname, pos[0], |
71741151e42b
`BadDirectiveError` no longer expects a `QName` as the directive name; a regular string should do.
cmlenz
parents:
221
diff
changeset
|
825 pos[1]) |
81
d60486018004
Template expressions are now compiled to Python bytecode.
cmlenz
parents:
80
diff
changeset
|
826 directives.append(cls(value, *pos)) |
1 | 827 else: |
75
3722696d0343
Empty attributes in templates were being stripped out. Thanks to Jonas for the patch.
cmlenz
parents:
74
diff
changeset
|
828 if value: |
3722696d0343
Empty attributes in templates were being stripped out. Thanks to Jonas for the patch.
cmlenz
parents:
74
diff
changeset
|
829 value = list(self._interpolate(value, *pos)) |
81
d60486018004
Template expressions are now compiled to Python bytecode.
cmlenz
parents:
80
diff
changeset
|
830 if len(value) == 1 and value[0][0] is TEXT: |
d60486018004
Template expressions are now compiled to Python bytecode.
cmlenz
parents:
80
diff
changeset
|
831 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
|
832 else: |
3722696d0343
Empty attributes in templates were being stripped out. Thanks to Jonas for the patch.
cmlenz
parents:
74
diff
changeset
|
833 value = [(TEXT, u'', pos)] |
1 | 834 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
|
835 |
1 | 836 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
|
837 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
|
838 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
|
839 dirmap[(depth, tag)] = (directives, len(stream), strip) |
1 | 840 |
182
2f30ce3fb85e
Renamed `Attributes` to `Attrs` to reduce the verbosity.
cmlenz
parents:
181
diff
changeset
|
841 stream.append((kind, (tag, Attrs(new_attrib)), pos)) |
1 | 842 depth += 1 |
843 | |
69 | 844 elif kind is END: |
1 | 845 depth -= 1 |
846 stream.append((kind, data, pos)) | |
847 | |
848 # If there have have directive attributes with the corresponding | |
849 # start tag, move the events inbetween into a "subprogram" | |
850 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
|
851 directives, start_offset, strip = dirmap.pop((depth, data)) |
1 | 852 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
|
853 if strip: |
b3fdf93057ab
Support the use of directives as elements to reduce the need for using `py:strip`.
cmlenz
parents:
61
diff
changeset
|
854 substream = substream[1:-1] |
69 | 855 stream[start_offset:] = [(SUB, (directives, substream), |
856 pos)] | |
1 | 857 |
69 | 858 elif kind is TEXT: |
1 | 859 for kind, data, pos in self._interpolate(data, *pos): |
860 stream.append((kind, data, pos)) | |
861 | |
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
|
862 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
|
863 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
|
864 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
|
865 |
1 | 866 else: |
867 stream.append((kind, data, pos)) | |
868 | |
214
ab407defd204
Cosmetic change to internal template initialization.
cmlenz
parents:
213
diff
changeset
|
869 return stream |
1 | 870 |
184
181d292eafa2
Interpolate multiline expressions in templates. Thanks to Christian Boos for reporting the problem and providing the fix.
cmlenz
parents:
182
diff
changeset
|
871 _FULL_EXPR_RE = re.compile(r'(?<!\$)\$\{(.+?)\}', re.DOTALL) |
1 | 872 _SHORT_EXPR_RE = re.compile(r'(?<!\$)\$([a-zA-Z][a-zA-Z0-9_\.]*)') |
873 | |
21
b4d17897d053
* Include paths are now interpreted relative to the path of the including template. Closes #3.
cmlenz
parents:
18
diff
changeset
|
874 def _interpolate(cls, text, filename=None, lineno=-1, offset=-1): |
1 | 875 """Parse the given string and extract expressions. |
876 | |
877 This method returns a list containing both literal text and `Expression` | |
878 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
|
879 |
1 | 880 @param text: the text to parse |
881 @param lineno: the line number at which the text was found (optional) | |
882 @param offset: the column number at which the text starts in the source | |
883 (optional) | |
884 """ | |
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
|
885 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
|
886 offset=offset): |
191
3289055a8c32
Allow leading whitespace in expressions. Closes #38. Thanks to Christian Boos for the patch!
cmlenz
parents:
190
diff
changeset
|
887 for idx, grp in enumerate(patterns.pop(0).split(text)): |
1 | 888 if idx % 2: |
81
d60486018004
Template expressions are now compiled to Python bytecode.
cmlenz
parents:
80
diff
changeset
|
889 try: |
191
3289055a8c32
Allow leading whitespace in expressions. Closes #38. Thanks to Christian Boos for the patch!
cmlenz
parents:
190
diff
changeset
|
890 yield EXPR, Expression(grp.strip(), 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
|
891 (filename, lineno, offset) |
81
d60486018004
Template expressions are now compiled to Python bytecode.
cmlenz
parents:
80
diff
changeset
|
892 except SyntaxError, err: |
d60486018004
Template expressions are now compiled to Python bytecode.
cmlenz
parents:
80
diff
changeset
|
893 raise TemplateSyntaxError(err, filename, lineno, |
d60486018004
Template expressions are now compiled to Python bytecode.
cmlenz
parents:
80
diff
changeset
|
894 offset + (err.offset or 0)) |
191
3289055a8c32
Allow leading whitespace in expressions. Closes #38. Thanks to Christian Boos for the patch!
cmlenz
parents:
190
diff
changeset
|
895 elif grp: |
1 | 896 if patterns: |
191
3289055a8c32
Allow leading whitespace in expressions. Closes #38. Thanks to Christian Boos for the patch!
cmlenz
parents:
190
diff
changeset
|
897 for result in _interpolate(grp, patterns[:]): |
1 | 898 yield result |
899 else: | |
191
3289055a8c32
Allow leading whitespace in expressions. Closes #38. Thanks to Christian Boos for the patch!
cmlenz
parents:
190
diff
changeset
|
900 yield TEXT, grp.replace('$$', '$'), \ |
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
|
901 (filename, lineno, offset) |
191
3289055a8c32
Allow leading whitespace in expressions. Closes #38. Thanks to Christian Boos for the patch!
cmlenz
parents:
190
diff
changeset
|
902 if '\n' in grp: |
3289055a8c32
Allow leading whitespace in expressions. Closes #38. Thanks to Christian Boos for the patch!
cmlenz
parents:
190
diff
changeset
|
903 lines = grp.splitlines() |
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
|
904 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
|
905 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
|
906 else: |
191
3289055a8c32
Allow leading whitespace in expressions. Closes #38. Thanks to Christian Boos for the patch!
cmlenz
parents:
190
diff
changeset
|
907 offset += len(grp) |
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
|
908 return _interpolate(text, [cls._FULL_EXPR_RE, cls._SHORT_EXPR_RE]) |
1 | 909 _interpolate = classmethod(_interpolate) |
910 | |
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
|
911 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
|
912 """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
|
913 |
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
|
914 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
|
915 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
|
916 |
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
|
917 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
|
918 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
|
919 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
|
920 |
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
|
921 @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
|
922 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
|
923 """ |
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
|
924 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
|
925 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
|
926 ctxt = args[0] |
173 | 927 if ctxt is None: |
928 ctxt = Context(**kwargs) | |
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
|
929 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
|
930 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
|
931 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
|
932 |
69 | 933 stream = self.stream |
208
bc146e63c159
Cleanup the application of template processing steps (flatten, eval, match) so that they are only performed when necessary. Results in a small performance boost, and also fixes #35.
cmlenz
parents:
206
diff
changeset
|
934 for filter_ in [self._flatten, self._eval, self._match] + self.filters: |
35
35b9e9318fb1
Simplify template processing model by removing dynamically generated `SUB` events.
cmlenz
parents:
31
diff
changeset
|
935 stream = filter_(iter(stream), ctxt) |
35b9e9318fb1
Simplify template processing model by removing dynamically generated `SUB` events.
cmlenz
parents:
31
diff
changeset
|
936 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
|
937 |
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 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
|
939 """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
|
940 `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
|
941 """ |
208
bc146e63c159
Cleanup the application of template processing steps (flatten, eval, match) so that they are only performed when necessary. Results in a small performance boost, and also fixes #35.
cmlenz
parents:
206
diff
changeset
|
942 filters = (self._flatten, self._eval) |
81
d60486018004
Template expressions are now compiled to Python bytecode.
cmlenz
parents:
80
diff
changeset
|
943 |
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
|
944 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
|
945 |
81
d60486018004
Template expressions are now compiled to Python bytecode.
cmlenz
parents:
80
diff
changeset
|
946 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
|
947 # 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
|
948 # 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
|
949 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
|
950 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
|
951 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
|
952 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
|
953 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
|
954 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
|
955 values = [] |
185
95c3813a00de
Fix for #34: `py:def` macros can now be invoked from within expressions in attribute values.
cmlenz
parents:
184
diff
changeset
|
956 for subkind, subdata, subpos in self._eval(substream, |
95c3813a00de
Fix for #34: `py:def` macros can now be invoked from within expressions in attribute values.
cmlenz
parents:
184
diff
changeset
|
957 ctxt): |
95c3813a00de
Fix for #34: `py:def` macros can now be invoked from within expressions in attribute values.
cmlenz
parents:
184
diff
changeset
|
958 if subkind is TEXT: |
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 values.append(subdata) |
185
95c3813a00de
Fix for #34: `py:def` macros can now be invoked from within expressions in attribute values.
cmlenz
parents:
184
diff
changeset
|
960 value = [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
|
961 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
|
962 continue |
23
d88358f719fa
Separate match and eval filters from the include and user-supplied filters.
cmlenz
parents:
22
diff
changeset
|
963 new_attrib.append((name, u''.join(value))) |
182
2f30ce3fb85e
Renamed `Attributes` to `Attrs` to reduce the verbosity.
cmlenz
parents:
181
diff
changeset
|
964 yield kind, (tag, Attrs(new_attrib)), 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
|
965 |
69 | 966 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
|
967 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
|
968 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
|
969 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
|
970 |
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 # 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
|
972 # 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
|
973 # 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
|
974 if isinstance(result, basestring): |
69 | 975 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
|
976 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
|
977 # 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
|
978 # 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
|
979 try: |
206
75c9c019de88
`TypeError`s raised by `py:def` macros (and other expressions producing streams) are no longer silently ignored. Closes #44.
cmlenz
parents:
205
diff
changeset
|
980 substream = _ensure(iter(result)) |
75c9c019de88
`TypeError`s raised by `py:def` macros (and other expressions producing streams) are no longer silently ignored. Closes #44.
cmlenz
parents:
205
diff
changeset
|
981 except TypeError: |
75c9c019de88
`TypeError`s raised by `py:def` macros (and other expressions producing streams) are no longer silently ignored. Closes #44.
cmlenz
parents:
205
diff
changeset
|
982 # Neither a string nor an iterable, so just pass it |
75c9c019de88
`TypeError`s raised by `py:def` macros (and other expressions producing streams) are no longer silently ignored. Closes #44.
cmlenz
parents:
205
diff
changeset
|
983 # through |
75c9c019de88
`TypeError`s raised by `py:def` macros (and other expressions producing streams) are no longer silently ignored. Closes #44.
cmlenz
parents:
205
diff
changeset
|
984 yield TEXT, unicode(result), pos |
75c9c019de88
`TypeError`s raised by `py:def` macros (and other expressions producing streams) are no longer silently ignored. Closes #44.
cmlenz
parents:
205
diff
changeset
|
985 else: |
81
d60486018004
Template expressions are now compiled to Python bytecode.
cmlenz
parents:
80
diff
changeset
|
986 for filter_ in filters: |
77
f5ec6d4a61e4
* Simplify implementation of the individual XPath tests (use closures instead of callable classes)
cmlenz
parents:
75
diff
changeset
|
987 substream = filter_(substream, ctxt) |
f5ec6d4a61e4
* Simplify implementation of the individual XPath tests (use closures instead of callable classes)
cmlenz
parents:
75
diff
changeset
|
988 for event in substream: |
35
35b9e9318fb1
Simplify template processing model by removing dynamically generated `SUB` events.
cmlenz
parents:
31
diff
changeset
|
989 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
|
990 |
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
|
991 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
|
992 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
|
993 |
23
d88358f719fa
Separate match and eval filters from the include and user-supplied filters.
cmlenz
parents:
22
diff
changeset
|
994 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
|
995 """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
|
996 for kind, data, pos in stream: |
d60486018004
Template expressions are now compiled to Python bytecode.
cmlenz
parents:
80
diff
changeset
|
997 if kind is SUB: |
d60486018004
Template expressions are now compiled to Python bytecode.
cmlenz
parents:
80
diff
changeset
|
998 # 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
|
999 # events to which those directives should be applied |
d60486018004
Template expressions are now compiled to Python bytecode.
cmlenz
parents:
80
diff
changeset
|
1000 directives, substream = data |
d60486018004
Template expressions are now compiled to Python bytecode.
cmlenz
parents:
80
diff
changeset
|
1001 substream = _apply_directives(substream, ctxt, directives) |
208
bc146e63c159
Cleanup the application of template processing steps (flatten, eval, match) so that they are only performed when necessary. Results in a small performance boost, and also fixes #35.
cmlenz
parents:
206
diff
changeset
|
1002 for event in self._flatten(substream, ctxt): |
81
d60486018004
Template expressions are now compiled to Python bytecode.
cmlenz
parents:
80
diff
changeset
|
1003 yield event |
d60486018004
Template expressions are now compiled to Python bytecode.
cmlenz
parents:
80
diff
changeset
|
1004 else: |
d60486018004
Template expressions are now compiled to Python bytecode.
cmlenz
parents:
80
diff
changeset
|
1005 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
|
1006 |
36
ed370ebfa794
Fix for #7: match templates no longer process their own output.
cmlenz
parents:
35
diff
changeset
|
1007 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
|
1008 """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
|
1009 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
|
1010 """ |
36
ed370ebfa794
Fix for #7: match templates no longer process their own output.
cmlenz
parents:
35
diff
changeset
|
1011 if match_templates is None: |
ed370ebfa794
Fix for #7: match templates no longer process their own output.
cmlenz
parents:
35
diff
changeset
|
1012 match_templates = ctxt._match_templates |
224
90d62225f411
Implement support for namespace prefixes in XPath expressions.
cmlenz
parents:
222
diff
changeset
|
1013 nsprefix = {} # mapping of namespace prefixes to URIs |
36
ed370ebfa794
Fix for #7: match templates no longer process their own output.
cmlenz
parents:
35
diff
changeset
|
1014 |
217
f150cff4da18
Fix `py:match` directive which would screw up in some scenarios due to incorrect handling of the substream. Closes #49.
cmlenz
parents:
214
diff
changeset
|
1015 tail = [] |
f150cff4da18
Fix `py:match` directive which would screw up in some scenarios due to incorrect handling of the substream. Closes #49.
cmlenz
parents:
214
diff
changeset
|
1016 def _strip(stream): |
f150cff4da18
Fix `py:match` directive which would screw up in some scenarios due to incorrect handling of the substream. Closes #49.
cmlenz
parents:
214
diff
changeset
|
1017 depth = 1 |
f150cff4da18
Fix `py:match` directive which would screw up in some scenarios due to incorrect handling of the substream. Closes #49.
cmlenz
parents:
214
diff
changeset
|
1018 while 1: |
f150cff4da18
Fix `py:match` directive which would screw up in some scenarios due to incorrect handling of the substream. Closes #49.
cmlenz
parents:
214
diff
changeset
|
1019 kind, data, pos = stream.next() |
f150cff4da18
Fix `py:match` directive which would screw up in some scenarios due to incorrect handling of the substream. Closes #49.
cmlenz
parents:
214
diff
changeset
|
1020 if kind is START: |
f150cff4da18
Fix `py:match` directive which would screw up in some scenarios due to incorrect handling of the substream. Closes #49.
cmlenz
parents:
214
diff
changeset
|
1021 depth += 1 |
f150cff4da18
Fix `py:match` directive which would screw up in some scenarios due to incorrect handling of the substream. Closes #49.
cmlenz
parents:
214
diff
changeset
|
1022 elif kind is END: |
f150cff4da18
Fix `py:match` directive which would screw up in some scenarios due to incorrect handling of the substream. Closes #49.
cmlenz
parents:
214
diff
changeset
|
1023 depth -= 1 |
f150cff4da18
Fix `py:match` directive which would screw up in some scenarios due to incorrect handling of the substream. Closes #49.
cmlenz
parents:
214
diff
changeset
|
1024 if depth > 0: |
f150cff4da18
Fix `py:match` directive which would screw up in some scenarios due to incorrect handling of the substream. Closes #49.
cmlenz
parents:
214
diff
changeset
|
1025 yield kind, data, pos |
f150cff4da18
Fix `py:match` directive which would screw up in some scenarios due to incorrect handling of the substream. Closes #49.
cmlenz
parents:
214
diff
changeset
|
1026 else: |
f150cff4da18
Fix `py:match` directive which would screw up in some scenarios due to incorrect handling of the substream. Closes #49.
cmlenz
parents:
214
diff
changeset
|
1027 tail[:] = [(kind, data, pos)] |
f150cff4da18
Fix `py:match` directive which would screw up in some scenarios due to incorrect handling of the substream. Closes #49.
cmlenz
parents:
214
diff
changeset
|
1028 break |
f150cff4da18
Fix `py:match` directive which would screw up in some scenarios due to incorrect handling of the substream. Closes #49.
cmlenz
parents:
214
diff
changeset
|
1029 |
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
|
1030 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
|
1031 |
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
|
1032 # 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
|
1033 # 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
|
1034 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
|
1035 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
|
1036 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
|
1037 |
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
|
1038 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
|
1039 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
|
1040 |
224
90d62225f411
Implement support for namespace prefixes in XPath expressions.
cmlenz
parents:
222
diff
changeset
|
1041 if test(kind, data, pos, nsprefix, ctxt) is True: |
208
bc146e63c159
Cleanup the application of template processing steps (flatten, eval, match) so that they are only performed when necessary. Results in a small performance boost, and also fixes #35.
cmlenz
parents:
206
diff
changeset
|
1042 |
bc146e63c159
Cleanup the application of template processing steps (flatten, eval, match) so that they are only performed when necessary. Results in a small performance boost, and also fixes #35.
cmlenz
parents:
206
diff
changeset
|
1043 # Let the remaining match templates know about the event so |
bc146e63c159
Cleanup the application of template processing steps (flatten, eval, match) so that they are only performed when necessary. Results in a small performance boost, and also fixes #35.
cmlenz
parents:
206
diff
changeset
|
1044 # they get a chance to update their internal state |
bc146e63c159
Cleanup the application of template processing steps (flatten, eval, match) so that they are only performed when necessary. Results in a small performance boost, and also fixes #35.
cmlenz
parents:
206
diff
changeset
|
1045 for test in [mt[0] for mt in match_templates[idx + 1:]]: |
224
90d62225f411
Implement support for namespace prefixes in XPath expressions.
cmlenz
parents:
222
diff
changeset
|
1046 test(kind, data, pos, nsprefix, ctxt) |
208
bc146e63c159
Cleanup the application of template processing steps (flatten, eval, match) so that they are only performed when necessary. Results in a small performance boost, and also fixes #35.
cmlenz
parents:
206
diff
changeset
|
1047 |
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
|
1048 # 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
|
1049 # 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
|
1050 content = [(kind, data, pos)] |
217
f150cff4da18
Fix `py:match` directive which would screw up in some scenarios due to incorrect handling of the substream. Closes #49.
cmlenz
parents:
214
diff
changeset
|
1051 content += list(self._match(_strip(stream), ctxt)) + tail |
f150cff4da18
Fix `py:match` directive which would screw up in some scenarios due to incorrect handling of the substream. Closes #49.
cmlenz
parents:
214
diff
changeset
|
1052 |
f150cff4da18
Fix `py:match` directive which would screw up in some scenarios due to incorrect handling of the substream. Closes #49.
cmlenz
parents:
214
diff
changeset
|
1053 kind, data, pos = tail[0] |
f150cff4da18
Fix `py:match` directive which would screw up in some scenarios due to incorrect handling of the substream. Closes #49.
cmlenz
parents:
214
diff
changeset
|
1054 for test in [mt[0] for mt in match_templates]: |
224
90d62225f411
Implement support for namespace prefixes in XPath expressions.
cmlenz
parents:
222
diff
changeset
|
1055 test(kind, data, pos, nsprefix, ctxt) |
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
|
1056 |
208
bc146e63c159
Cleanup the application of template processing steps (flatten, eval, match) so that they are only performed when necessary. Results in a small performance boost, and also fixes #35.
cmlenz
parents:
206
diff
changeset
|
1057 # Make the select() function available in the body of the |
bc146e63c159
Cleanup the application of template processing steps (flatten, eval, match) so that they are only performed when necessary. Results in a small performance boost, and also fixes #35.
cmlenz
parents:
206
diff
changeset
|
1058 # match template |
bc146e63c159
Cleanup the application of template processing steps (flatten, eval, match) so that they are only performed when necessary. Results in a small performance boost, and also fixes #35.
cmlenz
parents:
206
diff
changeset
|
1059 def select(path): |
bc146e63c159
Cleanup the application of template processing steps (flatten, eval, match) so that they are only performed when necessary. Results in a small performance boost, and also fixes #35.
cmlenz
parents:
206
diff
changeset
|
1060 return Stream(content).select(path) |
95
2fe86a99947f
Improve performance of push/pop operations on the context.
cmlenz
parents:
93
diff
changeset
|
1061 ctxt.push(dict(select=select)) |
36
ed370ebfa794
Fix for #7: match templates no longer process their own output.
cmlenz
parents:
35
diff
changeset
|
1062 |
208
bc146e63c159
Cleanup the application of template processing steps (flatten, eval, match) so that they are only performed when necessary. Results in a small performance boost, and also fixes #35.
cmlenz
parents:
206
diff
changeset
|
1063 # Recursively process the output |
78
46fed54f23cd
Minor improvements to how directives are applied in template processing.
cmlenz
parents:
77
diff
changeset
|
1064 template = _apply_directives(template, ctxt, directives) |
210
9fd7535883f2
Fix regression introduced in [258]. More fixes needed?
cmlenz
parents:
208
diff
changeset
|
1065 for event in self._match(self._eval(self._flatten(template, |
9fd7535883f2
Fix regression introduced in [258]. More fixes needed?
cmlenz
parents:
208
diff
changeset
|
1066 ctxt), |
9fd7535883f2
Fix regression introduced in [258]. More fixes needed?
cmlenz
parents:
208
diff
changeset
|
1067 ctxt), ctxt, |
9fd7535883f2
Fix regression introduced in [258]. More fixes needed?
cmlenz
parents:
208
diff
changeset
|
1068 match_templates[:idx] + |
69 | 1069 match_templates[idx + 1:]): |
35
35b9e9318fb1
Simplify template processing model by removing dynamically generated `SUB` events.
cmlenz
parents:
31
diff
changeset
|
1070 yield event |
116 | 1071 |
35
35b9e9318fb1
Simplify template processing model by removing dynamically generated `SUB` events.
cmlenz
parents:
31
diff
changeset
|
1072 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
|
1073 break |
69 | 1074 |
1075 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
|
1076 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
|
1077 |
1 | 1078 |
69 | 1079 EXPR = Template.EXPR |
1080 SUB = Template.SUB | |
1081 | |
1082 | |
1 | 1083 class TemplateLoader(object): |
1084 """Responsible for loading templates from files on the specified search | |
1085 path. | |
1086 | |
1087 >>> import tempfile | |
1088 >>> fd, path = tempfile.mkstemp(suffix='.html', prefix='template') | |
1089 >>> os.write(fd, '<p>$var</p>') | |
1090 11 | |
1091 >>> os.close(fd) | |
1092 | |
1093 The template loader accepts a list of directory paths that are then used | |
1094 when searching for template files, in the given order: | |
1095 | |
1096 >>> loader = TemplateLoader([os.path.dirname(path)]) | |
1097 | |
1098 The `load()` method first checks the template cache whether the requested | |
1099 template has already been loaded. If not, it attempts to locate the | |
1100 template file, and returns the corresponding `Template` object: | |
1101 | |
1102 >>> template = loader.load(os.path.basename(path)) | |
1103 >>> isinstance(template, Template) | |
1104 True | |
1105 | |
1106 Template instances are cached: requesting a template with the same name | |
1107 results in the same instance being returned: | |
1108 | |
1109 >>> loader.load(os.path.basename(path)) is template | |
1110 True | |
152
cdb2a1f930e2
Add some tests for relative template includes (see #27).
cmlenz
parents:
150
diff
changeset
|
1111 |
cdb2a1f930e2
Add some tests for relative template includes (see #27).
cmlenz
parents:
150
diff
changeset
|
1112 >>> os.remove(path) |
1 | 1113 """ |
1114 def __init__(self, search_path=None, auto_reload=False): | |
1115 """Create the template laoder. | |
1116 | |
1117 @param search_path: a list of absolute path names that should be | |
1118 searched for template files | |
1119 @param auto_reload: whether to check the last modification time of | |
1120 template files, and reload them if they have changed | |
1121 """ | |
1122 self.search_path = search_path | |
1123 if self.search_path is None: | |
1124 self.search_path = [] | |
1125 self.auto_reload = auto_reload | |
1126 self._cache = {} | |
1127 self._mtime = {} | |
1128 | |
21
b4d17897d053
* Include paths are now interpreted relative to the path of the including template. Closes #3.
cmlenz
parents:
18
diff
changeset
|
1129 def load(self, filename, relative_to=None): |
1 | 1130 """Load the template with the given name. |
1131 | |
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
|
1132 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
|
1133 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
|
1134 name is an absolute path, the search path is not bypassed. |
1 | 1135 |
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
|
1136 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
|
1137 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
|
1138 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
|
1139 |
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
|
1140 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
|
1141 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
|
1142 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
|
1143 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
|
1144 changed since the last parse.) |
1 | 1145 |
21
b4d17897d053
* Include paths are now interpreted relative to the path of the including template. Closes #3.
cmlenz
parents:
18
diff
changeset
|
1146 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
|
1147 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
|
1148 |
1 | 1149 @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
|
1150 @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
|
1151 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
|
1152 directly |
1 | 1153 """ |
69 | 1154 from markup.filters import IncludeFilter |
1155 | |
21
b4d17897d053
* Include paths are now interpreted relative to the path of the including template. Closes #3.
cmlenz
parents:
18
diff
changeset
|
1156 if relative_to: |
153 | 1157 filename = os.path.join(os.path.dirname(relative_to), filename) |
1 | 1158 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
|
1159 |
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
|
1160 # First check the cache to avoid reparsing the same file |
1 | 1161 try: |
1162 tmpl = self._cache[filename] | |
1163 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
|
1164 os.path.getmtime(tmpl.filepath) == self._mtime[filename]: |
1 | 1165 return tmpl |
1166 except KeyError: | |
1167 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
|
1168 |
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
|
1169 # 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
|
1170 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
|
1171 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
|
1172 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
|
1173 |
176
00212b14903d
Fix control flow for error message when template search path is empty.
cmlenz
parents:
175
diff
changeset
|
1174 if not search_path: |
00212b14903d
Fix control flow for error message when template search path is empty.
cmlenz
parents:
175
diff
changeset
|
1175 raise TemplateError('Search path for templates not configured') |
00212b14903d
Fix control flow for error message when template search path is empty.
cmlenz
parents:
175
diff
changeset
|
1176 |
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
|
1177 for dirname in search_path: |
1 | 1178 filepath = os.path.join(dirname, filename) |
1179 try: | |
133
79f445396cd7
Minor cleanup and performance improvement for the builder module.
cmlenz
parents:
120
diff
changeset
|
1180 fileobj = open(filepath, 'U') |
1 | 1181 try: |
21
b4d17897d053
* Include paths are now interpreted relative to the path of the including template. Closes #3.
cmlenz
parents:
18
diff
changeset
|
1182 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
|
1183 tmpl.filters.append(IncludeFilter(self)) |
1 | 1184 finally: |
1185 fileobj.close() | |
1186 self._cache[filename] = tmpl | |
1187 self._mtime[filename] = os.path.getmtime(filepath) | |
1188 return tmpl | |
1189 except IOError: | |
1190 continue | |
175 | 1191 |
1 | 1192 raise TemplateNotFound(filename, self.search_path) |