Mercurial > genshi > mirror
comparison markup/template.py @ 220:f4943d2babda trunk
Fix for #45 and #46: properly support assignment to nested tuples in `py:for` and `py:with` directives.
author | cmlenz |
---|---|
date | Tue, 05 Sep 2006 16:33:13 +0000 |
parents | f150cff4da18 |
children | 04d260487b9a |
comparison
equal
deleted
inserted
replaced
219:ebceef564b79 | 220:f4943d2babda |
---|---|
111 def __setitem__(self, key, value): | 111 def __setitem__(self, key, value): |
112 """Set a variable in the current scope.""" | 112 """Set a variable in the current scope.""" |
113 self.frames[0][key] = value | 113 self.frames[0][key] = value |
114 | 114 |
115 def _find(self, key, default=None): | 115 def _find(self, key, default=None): |
116 """Retrieve a given variable's value and frame it was found in. | 116 """Retrieve a given variable's value and the frame it was found in. |
117 | 117 |
118 Intented for internal use by directives. | 118 Intented for internal use by directives. |
119 """ | 119 """ |
120 for frame in self.frames: | 120 for frame in self.frames: |
121 if key in frame: | 121 if key in frame: |
188 def _apply_directives(stream, ctxt, directives): | 188 def _apply_directives(stream, ctxt, directives): |
189 """Apply the given directives to the stream.""" | 189 """Apply the given directives to the stream.""" |
190 if directives: | 190 if directives: |
191 stream = directives[0](iter(stream), ctxt, directives[1:]) | 191 stream = directives[0](iter(stream), ctxt, directives[1:]) |
192 return stream | 192 return stream |
193 | |
194 def _assignment(ast): | |
195 """Takes the AST representation of an assignment, and returns a function | |
196 that applies the assignment of a given value to a dictionary. | |
197 """ | |
198 def _names(node): | |
199 if isinstance(node, (compiler.ast.AssTuple, compiler.ast.Tuple)): | |
200 return tuple([_names(child) for child in node]) | |
201 elif isinstance(node, (compiler.ast.AssName, compiler.ast.Name)): | |
202 return node.name | |
203 def _assign(data, value, names=_names(ast)): | |
204 if type(names) is tuple: | |
205 for idx in range(len(names)): | |
206 _assign(data, value[idx], names[idx]) | |
207 else: | |
208 data[names] = value | |
209 return _assign | |
193 | 210 |
194 | 211 |
195 class AttrsDirective(Directive): | 212 class AttrsDirective(Directive): |
196 """Implementation of the `py:attrs` template directive. | 213 """Implementation of the `py:attrs` template directive. |
197 | 214 |
383 >>> print tmpl.generate(items=[1, 2, 3]) | 400 >>> print tmpl.generate(items=[1, 2, 3]) |
384 <ul> | 401 <ul> |
385 <li>1</li><li>2</li><li>3</li> | 402 <li>1</li><li>2</li><li>3</li> |
386 </ul> | 403 </ul> |
387 """ | 404 """ |
388 __slots__ = ['targets'] | 405 __slots__ = ['assign'] |
389 | 406 |
390 ATTRIBUTE = 'each' | 407 ATTRIBUTE = 'each' |
391 | 408 |
392 def __init__(self, value, filename=None, lineno=-1, offset=-1): | 409 def __init__(self, value, filename=None, lineno=-1, offset=-1): |
393 if ' in ' not in value: | 410 if ' in ' not in value: |
394 raise TemplateSyntaxError('"in" keyword missing in "for" directive', | 411 raise TemplateSyntaxError('"in" keyword missing in "for" directive', |
395 filename, lineno, offset) | 412 filename, lineno, offset) |
396 targets, value = value.split(' in ', 1) | 413 assign, value = value.split(' in ', 1) |
397 self.targets = [str(name.strip()) for name in targets.split(',')] | 414 ast = compiler.parse(assign, 'exec') |
415 self.assign = _assignment(ast.node.nodes[0].expr) | |
398 Directive.__init__(self, value.strip(), filename, lineno, offset) | 416 Directive.__init__(self, value.strip(), filename, lineno, offset) |
399 | 417 |
400 def __call__(self, stream, ctxt, directives): | 418 def __call__(self, stream, ctxt, directives): |
401 iterable = self.expr.evaluate(ctxt) | 419 iterable = self.expr.evaluate(ctxt) |
402 if iterable is None: | 420 if iterable is None: |
403 return | 421 return |
404 | 422 |
423 assign = self.assign | |
405 scope = {} | 424 scope = {} |
406 stream = list(stream) | 425 stream = list(stream) |
407 targets = self.targets | |
408 single = len(targets) == 1 | |
409 for item in iter(iterable): | 426 for item in iter(iterable): |
410 if single: | 427 assign(scope, item) |
411 scope[targets[0]] = item | |
412 else: | |
413 for idx, name in enumerate(targets): | |
414 scope[name] = item[idx] | |
415 ctxt.push(scope) | 428 ctxt.push(scope) |
416 for event in _apply_directives(stream, ctxt, directives): | 429 for event in _apply_directives(stream, ctxt, directives): |
417 yield event | 430 yield event |
418 ctxt.pop() | 431 ctxt.pop() |
419 | 432 |
689 continue | 702 continue |
690 elif not isinstance(node, compiler.ast.Assign): | 703 elif not isinstance(node, compiler.ast.Assign): |
691 raise TemplateSyntaxError('only assignment allowed in ' | 704 raise TemplateSyntaxError('only assignment allowed in ' |
692 'value of the "with" directive', | 705 'value of the "with" directive', |
693 filename, lineno, offset) | 706 filename, lineno, offset) |
694 self.vars.append(([n.name for n in node.nodes], | 707 self.vars.append(([_assignment(n) for n in node.nodes], |
695 Expression(node.expr, filename, lineno))) | 708 Expression(node.expr, filename, lineno))) |
696 except SyntaxError, err: | 709 except SyntaxError, err: |
697 err.msg += ' in expression "%s" of "%s" directive' % (value, | 710 err.msg += ' in expression "%s" of "%s" directive' % (value, |
698 self.tagname) | 711 self.tagname) |
699 raise TemplateSyntaxError(err, filename, lineno, | 712 raise TemplateSyntaxError(err, filename, lineno, |
700 offset + (err.offset or 0)) | 713 offset + (err.offset or 0)) |
701 | 714 |
702 def __call__(self, stream, ctxt, directives): | 715 def __call__(self, stream, ctxt, directives): |
703 frame = {} | 716 frame = {} |
704 ctxt.push(frame) | 717 ctxt.push(frame) |
705 for names, expr in self.vars: | 718 for targets, expr in self.vars: |
706 value = expr.evaluate(ctxt, nocall=True) | 719 value = expr.evaluate(ctxt, nocall=True) |
707 frame.update(dict([(name, value) for name in names])) | 720 for assign in targets: |
721 assign(frame, value) | |
708 for event in _apply_directives(stream, ctxt, directives): | 722 for event in _apply_directives(stream, ctxt, directives): |
709 yield event | 723 yield event |
710 ctxt.pop() | 724 ctxt.pop() |
711 | 725 |
712 def __repr__(self): | 726 def __repr__(self): |