Mercurial > genshi > genshi-test
comparison genshi/template/inline.py @ 826:8ebccfa9a9fe experimental-inline
inline branch: Add code block support to the template inliner, and some tweaks/cleanup.
author | cmlenz |
---|---|
date | Fri, 13 Mar 2009 09:34:50 +0000 |
parents | abb1f1d2f4f3 |
children | eb8aa8690480 |
comparison
equal
deleted
inserted
replaced
821:abb1f1d2f4f3 | 826:8ebccfa9a9fe |
---|---|
13 | 13 |
14 import imp | 14 import imp |
15 | 15 |
16 from genshi.core import Attrs, Stream, _ensure, START, END, TEXT | 16 from genshi.core import Attrs, Stream, _ensure, START, END, TEXT |
17 from genshi.template.astutil import _ast | 17 from genshi.template.astutil import _ast |
18 from genshi.template.base import EXPR, SUB | 18 from genshi.template.base import EXEC, EXPR, SUB |
19 from genshi.template.directives import * | 19 from genshi.template.directives import * |
20 | 20 |
21 | 21 |
22 class CodeWriter(object): | 22 class CodeWriter(object): |
23 | 23 |
43 # First check for a string, otherwise the iterable test below | 43 # First check for a string, otherwise the iterable test below |
44 # succeeds, and the string will be chopped up into individual | 44 # succeeds, and the string will be chopped up into individual |
45 # characters | 45 # characters |
46 if isinstance(obj, basestring): | 46 if isinstance(obj, basestring): |
47 yield TEXT, obj, pos | 47 yield TEXT, obj, pos |
48 elif isinstance(obj, (int, float, long)): | |
49 yield TEXT, unicode(obj), pos | |
48 elif hasattr(obj, '__iter__'): | 50 elif hasattr(obj, '__iter__'): |
49 for event in _ensure(obj): | 51 for event in _ensure(obj): |
50 yield event | 52 yield event |
51 else: | 53 else: |
52 yield TEXT, unicode(obj), pos | 54 yield TEXT, unicode(obj), pos |
53 | 55 |
56 | |
54 def _expand_text(obj): | 57 def _expand_text(obj): |
55 if obj is not None: | 58 if obj is not None: |
56 if isinstance(obj, basestring): | 59 if isinstance(obj, basestring): |
57 return [obj] | 60 return [obj] |
61 elif isinstance(obj, (int, float, long)): | |
62 return [unicode(result)] | |
58 elif hasattr(obj, '__iter__'): | 63 elif hasattr(obj, '__iter__'): |
59 return [e[1] for e in _ensure(obj) if e[0] is TEXT] | 64 return [e[1] for e in _ensure(obj) if e[0] is TEXT] |
60 else: | 65 else: |
61 return [unicode(obj)] | 66 return [unicode(obj)] |
62 return [] | 67 return [] |
68 | |
63 | 69 |
64 def _assign(ast): | 70 def _assign(ast): |
65 buf = [] | 71 buf = [] |
66 def _build(node, indices): | 72 def _build(node, indices): |
67 if isinstance(node, _ast.Tuple): | 73 if isinstance(node, _ast.Tuple): |
70 elif isinstance(node, _ast.Name): | 76 elif isinstance(node, _ast.Name): |
71 buf.append('%r: v%s' % (node.id, ''.join(['[%s]' % i for i in indices]))) | 77 buf.append('%r: v%s' % (node.id, ''.join(['[%s]' % i for i in indices]))) |
72 _build(ast, ()) | 78 _build(ast, ()) |
73 return '{%s}' % ', '.join(buf) | 79 return '{%s}' % ', '.join(buf) |
74 | 80 |
81 | |
75 def inline(template): | 82 def inline(template): |
76 w = CodeWriter() | 83 w = CodeWriter() |
77 | 84 |
78 yield w('from genshi.core import Attrs, QName') | 85 yield w('from genshi.core import Attrs, QName') |
79 yield w('from genshi.core import START, START_NS, END, END_NS, DOCTYPE, TEXT') | 86 yield w('from genshi.core import START, START_CDATA, START_NS, END, ' |
87 'END_CDATA, END_NS, DOCTYPE, TEXT') | |
80 yield w('from genshi.path import Path') | 88 yield w('from genshi.path import Path') |
81 yield w('from genshi.template.eval import Expression') | 89 yield w('from genshi.template.eval import Expression, Suite') |
82 yield w('from genshi.template.inline import _expand, _expand_text') | 90 yield w('from genshi.template.inline import _expand, _expand_text') |
83 yield w() | 91 yield w() |
84 | 92 |
85 def _predecl_vars(stream): | 93 def _declare_vars(stream): |
86 for kind, data, pos in stream: | 94 for kind, data, pos in stream: |
87 | 95 |
88 if kind is START: | 96 if kind is START: |
89 tagname, attrs = data | 97 tagname, attrs = data |
90 yield 'Q', tagname, tagname | 98 yield 'Q', tagname, tagname |
101 yield 'A', tuple(sattrs), sattrs | 109 yield 'A', tuple(sattrs), sattrs |
102 | 110 |
103 elif kind is EXPR: | 111 elif kind is EXPR: |
104 yield 'E', data, data | 112 yield 'E', data, data |
105 | 113 |
114 elif kind is EXEC: | |
115 yield 'S', data, data | |
116 | |
106 elif kind is SUB: | 117 elif kind is SUB: |
107 directives, substream = data | 118 directives, substream = data |
108 for directive in directives: | 119 for directive in directives: |
109 | 120 |
110 if directive.expr: | 121 if directive.expr: |
115 yield 'E', expr, expr | 126 yield 'E', expr, expr |
116 | 127 |
117 elif hasattr(directive, 'path') and directive.path: | 128 elif hasattr(directive, 'path') and directive.path: |
118 yield 'P', directive.path, directive.path | 129 yield 'P', directive.path, directive.path |
119 | 130 |
120 for line in _predecl_vars(substream): | 131 for line in _declare_vars(substream): |
121 yield line | 132 yield line |
122 | 133 |
123 def _predecl_defs(stream): | 134 def _declare_functions(stream, names): |
124 for kind, data, pos in stream: | 135 for kind, data, pos in stream: |
125 if kind is SUB: | 136 if kind is SUB: |
126 directives, substream = data | 137 directives, substream = data |
127 for idx, directive in enumerate(directives): | 138 for idx, directive in enumerate(directives): |
128 if isinstance(directive, DefDirective): | 139 if isinstance(directive, DefDirective): |
129 defs.append(directive.name) | 140 names.append(directive.name) |
130 yield w('def %s:', directive.signature) | 141 yield w('def %s:', directive.signature) |
131 w.shift() | 142 w.shift() |
132 args = ['%r: %s' % (name, name) for name | 143 args = ['%r: %s' % (name, name) for name |
133 in directive.args] | 144 in directive.args] |
134 yield w('ctxt.push({%s})', ', '.join(args)) | 145 yield w('push({%s})', ', '.join(args)) |
135 for line in _apply(directives[idx + 1:], substream): | 146 for line in _apply(directives[idx + 1:], substream): |
136 yield line | 147 yield line |
137 yield w('ctxt.pop()') | 148 yield w('pop()') |
138 w.unshift() | 149 w.unshift() |
139 | 150 |
140 # Recursively apply directives | 151 # Recursively apply directives |
141 def _apply(directives, stream): | 152 def _apply(directives, stream): |
142 if not directives: | 153 if not directives: |
154 yield w('# Applying %r', d) | 165 yield w('# Applying %r', d) |
155 | 166 |
156 if isinstance(d, ForDirective): | 167 if isinstance(d, ForDirective): |
157 yield w('for v in e[%d].evaluate(ctxt):', index['E'][d.expr]) | 168 yield w('for v in e[%d].evaluate(ctxt):', index['E'][d.expr]) |
158 w.shift() | 169 w.shift() |
159 yield w('ctxt.push(%s)', _assign(d.target)) | 170 yield w('push(%s)', _assign(d.target)) |
160 for line in _apply(rest, stream): | 171 for line in _apply(rest, stream): |
161 yield line | 172 yield line |
162 yield w('ctxt.pop()') | 173 yield w('pop()') |
163 w.unshift() | 174 w.unshift() |
164 | 175 |
165 elif isinstance(d, IfDirective): | 176 elif isinstance(d, IfDirective): |
166 yield w('if e[%d].evaluate(ctxt):', index['E'][d.expr]) | 177 yield w('if e[%d].evaluate(ctxt):', index['E'][d.expr]) |
167 w.shift() | 178 w.shift() |
179 for kind, data, pos in stream: | 190 for kind, data, pos in stream: |
180 | 191 |
181 if kind is EXPR: | 192 if kind is EXPR: |
182 yield w('for evt in _expand(e[%d].evaluate(ctxt), (f, %d, %d)): yield evt', | 193 yield w('for evt in _expand(e[%d].evaluate(ctxt), (f, %d, %d)): yield evt', |
183 index['E'][data], *pos[1:]) | 194 index['E'][data], *pos[1:]) |
195 | |
196 elif kind is EXEC: | |
197 yield w('s[%d].execute(ctxt)', index['S'][data]) | |
184 | 198 |
185 elif kind is START: | 199 elif kind is START: |
186 tagname, attrs = data | 200 tagname, attrs = data |
187 qn = index['Q'][tagname] | 201 qn = index['Q'][tagname] |
188 | 202 |
223 yield w('yield %s, %r, (f, %d, %d)', kind, data, *pos[1:]) | 237 yield w('yield %s, %r, (f, %d, %d)', kind, data, *pos[1:]) |
224 | 238 |
225 yield w('_F = %r', template.filename) | 239 yield w('_F = %r', template.filename) |
226 yield w() | 240 yield w() |
227 | 241 |
228 yield '# predeclare qnames, attributes, and expressions' | 242 yield '# Create qnames, attributes, expressions, and suite objects' |
229 index, counter, values = {}, {}, {} | 243 index, counter, values = {}, {}, {} |
230 for prefix, key, value in _predecl_vars(template.stream): | 244 for prefix, key, value in _declare_vars(template.stream): |
231 if not prefix in counter: | 245 if not prefix in counter: |
232 counter[prefix] = 0 | 246 counter[prefix] = 0 |
233 if key not in index.get(prefix, ()): | 247 if key not in index.get(prefix, ()): |
234 index.setdefault(prefix, {})[key] = counter[prefix] | 248 index.setdefault(prefix, {})[key] = counter[prefix] |
235 counter[prefix] += 1 | 249 counter[prefix] += 1 |
241 yield w(')') | 255 yield w(')') |
242 yield w() | 256 yield w() |
243 | 257 |
244 yield w('def generate(ctxt, %s):', | 258 yield w('def generate(ctxt, %s):', |
245 ', '.join(['f=_F'] + ['%s=_%s' % (n.lower(), n) for n in index])) | 259 ', '.join(['f=_F'] + ['%s=_%s' % (n.lower(), n) for n in index])) |
260 w.shift() | |
261 yield w('push = ctxt.push; pop = ctxt.pop') | |
246 yield w() | 262 yield w() |
247 w.shift() | |
248 | 263 |
249 # Define macro functions | 264 # Define macro functions |
250 defs = [] | 265 defs = [] |
251 for line in _predecl_defs(template.stream): | 266 for line in _declare_functions(template.stream, names=defs): |
252 yield line | 267 yield line |
253 if defs: | 268 if defs: |
254 yield w() | 269 yield w() |
255 yield w('ctxt.push({%s})', ', '.join(['%r: %s' % (n, n) for n in defs])) | 270 yield w('push({%s})', ', '.join('%r: %s' % (n, n) for n in defs)) |
256 yield w() | 271 yield w() |
257 | 272 |
258 ei, pi = [0], [0] | 273 ei, pi = [0], [0] |
259 for line in _generate(template.stream): | 274 for line in _generate(template.stream): |
260 yield line | 275 yield line |
262 | 277 |
263 if __name__ == '__main__': | 278 if __name__ == '__main__': |
264 import timeit | 279 import timeit |
265 from genshi.template import Context, MarkupTemplate | 280 from genshi.template import Context, MarkupTemplate |
266 | 281 |
267 text = """<!DOCTYPE html | 282 text = """<html xmlns="http://www.w3.org/1999/xhtml" |
268 PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" | |
269 "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> | |
270 <html xmlns="http://www.w3.org/1999/xhtml" | |
271 xmlns:py="http://genshi.edgewall.org/" | 283 xmlns:py="http://genshi.edgewall.org/" |
272 lang="en"> | 284 lang="en"> |
285 <?python | |
286 def foo(x): | |
287 return x*x | |
288 ?> | |
273 <body> | 289 <body> |
274 <h1 py:def="sayhi(name='world')" py:strip=""> | 290 <h1 py:def="sayhi(name='world')" py:strip=""> |
275 Hello, $name! | 291 Hello, $name! |
276 </h1> | 292 </h1> |
277 ${sayhi()} | 293 ${sayhi()} |
291 for idx, line in enumerate(inline(tmpl)): | 307 for idx, line in enumerate(inline(tmpl)): |
292 print '%3d %s' % (idx + 1, line) | 308 print '%3d %s' % (idx + 1, line) |
293 | 309 |
294 print | 310 print |
295 print 'Interpreted template:' | 311 print 'Interpreted template:' |
296 print tmpl.generate(ctxt) | 312 print tmpl.generate(ctxt).render('html') |
297 | 313 |
298 print | 314 print |
299 print 'Executed module:' | 315 print 'Executed module:' |
300 module = tmpl.compile() | 316 module = tmpl.compile() |
301 print Stream(module.generate(ctxt)) | 317 print Stream(module.generate(ctxt)).render('html') |
302 | 318 |
303 print | 319 print |
304 print | 320 print |
305 t = timeit.Timer('list(tmpl.generate(**data))', ''' | 321 t = timeit.Timer('list(tmpl.generate(**data))', ''' |
306 from genshi.template import Context, MarkupTemplate | 322 from genshi.template import Context, MarkupTemplate |