Mercurial > genshi > genshi-test
comparison genshi/template/directives.py @ 830:de82830f8816 experimental-inline
inline branch: synced with trunk@1038.
author | cmlenz |
---|---|
date | Fri, 13 Mar 2009 20:04:26 +0000 |
parents | abb1f1d2f4f3 |
children | 09cc3627654c |
comparison
equal
deleted
inserted
replaced
828:eb8aa8690480 | 830:de82830f8816 |
---|---|
58 | 58 |
59 def __init__(self, value, template=None, namespaces=None, lineno=-1, | 59 def __init__(self, value, template=None, namespaces=None, lineno=-1, |
60 offset=-1): | 60 offset=-1): |
61 self.expr = self._parse_expr(value, template, lineno, offset) | 61 self.expr = self._parse_expr(value, template, lineno, offset) |
62 | 62 |
63 @classmethod | |
63 def attach(cls, template, stream, value, namespaces, pos): | 64 def attach(cls, template, stream, value, namespaces, pos): |
64 """Called after the template stream has been completely parsed. | 65 """Called after the template stream has been completely parsed. |
65 | 66 |
66 :param template: the `Template` object | 67 :param template: the `Template` object |
67 :param stream: the event stream associated with the directive | 68 :param stream: the event stream associated with the directive |
78 class, and gets added to the list of directives applied to the substream | 79 class, and gets added to the list of directives applied to the substream |
79 at runtime. `stream` is an event stream that replaces the original | 80 at runtime. `stream` is an event stream that replaces the original |
80 stream associated with the directive. | 81 stream associated with the directive. |
81 """ | 82 """ |
82 return cls(value, template, namespaces, *pos[1:]), stream | 83 return cls(value, template, namespaces, *pos[1:]), stream |
83 attach = classmethod(attach) | |
84 | 84 |
85 def __call__(self, stream, directives, ctxt, **vars): | 85 def __call__(self, stream, directives, ctxt, **vars): |
86 """Apply the directive to the given stream. | 86 """Apply the directive to the given stream. |
87 | 87 |
88 :param stream: the event stream | 88 :param stream: the event stream |
98 expr = '' | 98 expr = '' |
99 if getattr(self, 'expr', None) is not None: | 99 if getattr(self, 'expr', None) is not None: |
100 expr = ' "%s"' % self.expr.source | 100 expr = ' "%s"' % self.expr.source |
101 return '<%s%s>' % (self.__class__.__name__, expr) | 101 return '<%s%s>' % (self.__class__.__name__, expr) |
102 | 102 |
103 @classmethod | |
103 def _parse_expr(cls, expr, template, lineno=-1, offset=-1): | 104 def _parse_expr(cls, expr, template, lineno=-1, offset=-1): |
104 """Parses the given expression, raising a useful error message when a | 105 """Parses the given expression, raising a useful error message when a |
105 syntax error is encountered. | 106 syntax error is encountered. |
106 """ | 107 """ |
107 try: | 108 try: |
110 except SyntaxError, err: | 111 except SyntaxError, err: |
111 err.msg += ' in expression "%s" of "%s" directive' % (expr, | 112 err.msg += ' in expression "%s" of "%s" directive' % (expr, |
112 cls.tagname) | 113 cls.tagname) |
113 raise TemplateSyntaxError(err, template.filepath, lineno, | 114 raise TemplateSyntaxError(err, template.filepath, lineno, |
114 offset + (err.offset or 0)) | 115 offset + (err.offset or 0)) |
115 _parse_expr = classmethod(_parse_expr) | |
116 | 116 |
117 | 117 |
118 def _assignment(ast): | 118 def _assignment(ast): |
119 """Takes the AST representation of an assignment, and returns a | 119 """Takes the AST representation of an assignment, and returns a |
120 function that applies the assignment of a given value to a dictionary. | 120 function that applies the assignment of a given value to a dictionary. |
164 __slots__ = [] | 164 __slots__ = [] |
165 | 165 |
166 def __call__(self, stream, directives, ctxt, **vars): | 166 def __call__(self, stream, directives, ctxt, **vars): |
167 def _generate(): | 167 def _generate(): |
168 kind, (tag, attrib), pos = stream.next() | 168 kind, (tag, attrib), pos = stream.next() |
169 attrs = _eval_expr(self.expr, ctxt, **vars) | 169 attrs = _eval_expr(self.expr, ctxt, vars) |
170 if attrs: | 170 if attrs: |
171 if isinstance(attrs, Stream): | 171 if isinstance(attrs, Stream): |
172 try: | 172 try: |
173 attrs = iter(attrs).next() | 173 attrs = iter(attrs).next() |
174 except StopIteration: | 174 except StopIteration: |
180 in attrs if val is not None] | 180 in attrs if val is not None] |
181 yield kind, (tag, attrib), pos | 181 yield kind, (tag, attrib), pos |
182 for event in stream: | 182 for event in stream: |
183 yield event | 183 yield event |
184 | 184 |
185 return _apply_directives(_generate(), directives, ctxt, **vars) | 185 return _apply_directives(_generate(), directives, ctxt, vars) |
186 | 186 |
187 | 187 |
188 class ContentDirective(Directive): | 188 class ContentDirective(Directive): |
189 """Implementation of the ``py:content`` template directive. | 189 """Implementation of the ``py:content`` template directive. |
190 | 190 |
200 <li>Bye</li> | 200 <li>Bye</li> |
201 </ul> | 201 </ul> |
202 """ | 202 """ |
203 __slots__ = [] | 203 __slots__ = [] |
204 | 204 |
205 @classmethod | |
205 def attach(cls, template, stream, value, namespaces, pos): | 206 def attach(cls, template, stream, value, namespaces, pos): |
206 if type(value) is dict: | 207 if type(value) is dict: |
207 raise TemplateSyntaxError('The content directive can not be used ' | 208 raise TemplateSyntaxError('The content directive can not be used ' |
208 'as an element', template.filepath, | 209 'as an element', template.filepath, |
209 *pos[1:]) | 210 *pos[1:]) |
210 expr = cls._parse_expr(value, template, *pos[1:]) | 211 expr = cls._parse_expr(value, template, *pos[1:]) |
211 return None, [stream[0], (EXPR, expr, pos), stream[-1]] | 212 return None, [stream[0], (EXPR, expr, pos), stream[-1]] |
212 attach = classmethod(attach) | |
213 | 213 |
214 | 214 |
215 class DefDirective(Directive): | 215 class DefDirective(Directive): |
216 """Implementation of the ``py:def`` template directive. | 216 """Implementation of the ``py:def`` template directive. |
217 | 217 |
279 if getattr(ast, 'kwargs', None): | 279 if getattr(ast, 'kwargs', None): |
280 self.dstar_args = ast.kwargs.id | 280 self.dstar_args = ast.kwargs.id |
281 else: | 281 else: |
282 self.name = ast.id | 282 self.name = ast.id |
283 | 283 |
284 @classmethod | |
284 def attach(cls, template, stream, value, namespaces, pos): | 285 def attach(cls, template, stream, value, namespaces, pos): |
285 if type(value) is dict: | 286 if type(value) is dict: |
286 value = value.get('function') | 287 value = value.get('function') |
287 return super(DefDirective, cls).attach(template, stream, value, | 288 return super(DefDirective, cls).attach(template, stream, value, |
288 namespaces, pos) | 289 namespaces, pos) |
289 attach = classmethod(attach) | |
290 | 290 |
291 def __call__(self, stream, directives, ctxt, **vars): | 291 def __call__(self, stream, directives, ctxt, **vars): |
292 stream = list(stream) | 292 stream = list(stream) |
293 | 293 |
294 def function(*args, **kwargs): | 294 def function(*args, **kwargs): |
299 scope[name] = args.pop(0) | 299 scope[name] = args.pop(0) |
300 else: | 300 else: |
301 if name in kwargs: | 301 if name in kwargs: |
302 val = kwargs.pop(name) | 302 val = kwargs.pop(name) |
303 else: | 303 else: |
304 val = _eval_expr(self.defaults.get(name), ctxt, **vars) | 304 val = _eval_expr(self.defaults.get(name), ctxt, vars) |
305 scope[name] = val | 305 scope[name] = val |
306 if not self.star_args is None: | 306 if not self.star_args is None: |
307 scope[self.star_args] = args | 307 scope[self.star_args] = args |
308 if not self.dstar_args is None: | 308 if not self.dstar_args is None: |
309 scope[self.dstar_args] = kwargs | 309 scope[self.dstar_args] = kwargs |
310 ctxt.push(scope) | 310 ctxt.push(scope) |
311 for event in _apply_directives(stream, directives, ctxt, **vars): | 311 for event in _apply_directives(stream, directives, ctxt, vars): |
312 yield event | 312 yield event |
313 ctxt.pop() | 313 ctxt.pop() |
314 function.__name__ = self.name | 314 function.__name__ = self.name |
315 | 315 |
316 # Store the function reference in the bottom context frame so that it | 316 # Store the function reference in the bottom context frame so that it |
348 self.assign = _assignment(self.target) | 348 self.assign = _assignment(self.target) |
349 value = 'iter(%s)' % value.strip() | 349 value = 'iter(%s)' % value.strip() |
350 self.filename = template.filepath | 350 self.filename = template.filepath |
351 Directive.__init__(self, value, template, namespaces, lineno, offset) | 351 Directive.__init__(self, value, template, namespaces, lineno, offset) |
352 | 352 |
353 @classmethod | |
353 def attach(cls, template, stream, value, namespaces, pos): | 354 def attach(cls, template, stream, value, namespaces, pos): |
354 if type(value) is dict: | 355 if type(value) is dict: |
355 value = value.get('each') | 356 value = value.get('each') |
356 return super(ForDirective, cls).attach(template, stream, value, | 357 return super(ForDirective, cls).attach(template, stream, value, |
357 namespaces, pos) | 358 namespaces, pos) |
358 attach = classmethod(attach) | 359 |
359 | 360 def __call__(self, stream, directives, ctxt, **vars): |
360 def __call__(self, stream, directives, ctxt, **vars): | 361 iterable = _eval_expr(self.expr, ctxt, vars) |
361 iterable = _eval_expr(self.expr, ctxt, **vars) | |
362 if iterable is None: | 362 if iterable is None: |
363 return | 363 return |
364 | 364 |
365 assign = self.assign | 365 assign = self.assign |
366 scope = {} | 366 scope = {} |
367 stream = list(stream) | 367 stream = list(stream) |
368 for item in iterable: | 368 for item in iterable: |
369 assign(scope, item) | 369 assign(scope, item) |
370 ctxt.push(scope) | 370 ctxt.push(scope) |
371 for event in _apply_directives(stream, directives, ctxt, **vars): | 371 for event in _apply_directives(stream, directives, ctxt, vars): |
372 yield event | 372 yield event |
373 ctxt.pop() | 373 ctxt.pop() |
374 | 374 |
375 def __repr__(self): | 375 def __repr__(self): |
376 return '<%s>' % self.__class__.__name__ | 376 return '<%s>' % self.__class__.__name__ |
389 <b>Hello</b> | 389 <b>Hello</b> |
390 </div> | 390 </div> |
391 """ | 391 """ |
392 __slots__ = [] | 392 __slots__ = [] |
393 | 393 |
394 @classmethod | |
394 def attach(cls, template, stream, value, namespaces, pos): | 395 def attach(cls, template, stream, value, namespaces, pos): |
395 if type(value) is dict: | 396 if type(value) is dict: |
396 value = value.get('test') | 397 value = value.get('test') |
397 return super(IfDirective, cls).attach(template, stream, value, | 398 return super(IfDirective, cls).attach(template, stream, value, |
398 namespaces, pos) | 399 namespaces, pos) |
399 attach = classmethod(attach) | 400 |
400 | 401 def __call__(self, stream, directives, ctxt, **vars): |
401 def __call__(self, stream, directives, ctxt, **vars): | 402 value = _eval_expr(self.expr, ctxt, vars) |
402 value = _eval_expr(self.expr, ctxt, **vars) | |
403 if value: | 403 if value: |
404 return _apply_directives(stream, directives, ctxt, **vars) | 404 return _apply_directives(stream, directives, ctxt, vars) |
405 return [] | 405 return [] |
406 | 406 |
407 | 407 |
408 class MatchDirective(Directive): | 408 class MatchDirective(Directive): |
409 """Implementation of the ``py:match`` template directive. | 409 """Implementation of the ``py:match`` template directive. |
429 Directive.__init__(self, None, template, namespaces, lineno, offset) | 429 Directive.__init__(self, None, template, namespaces, lineno, offset) |
430 self.path = Path(value, template.filepath, lineno) | 430 self.path = Path(value, template.filepath, lineno) |
431 self.namespaces = namespaces or {} | 431 self.namespaces = namespaces or {} |
432 self.hints = hints or () | 432 self.hints = hints or () |
433 | 433 |
434 @classmethod | |
434 def attach(cls, template, stream, value, namespaces, pos): | 435 def attach(cls, template, stream, value, namespaces, pos): |
435 hints = [] | 436 hints = [] |
436 if type(value) is dict: | 437 if type(value) is dict: |
437 if value.get('buffer', '').lower() == 'false': | 438 if value.get('buffer', '').lower() == 'false': |
438 hints.append('not_buffered') | 439 hints.append('not_buffered') |
441 if value.get('recursive', '').lower() == 'false': | 442 if value.get('recursive', '').lower() == 'false': |
442 hints.append('not_recursive') | 443 hints.append('not_recursive') |
443 value = value.get('path') | 444 value = value.get('path') |
444 return cls(value, template, frozenset(hints), namespaces, *pos[1:]), \ | 445 return cls(value, template, frozenset(hints), namespaces, *pos[1:]), \ |
445 stream | 446 stream |
446 attach = classmethod(attach) | |
447 | 447 |
448 def __call__(self, stream, directives, ctxt, **vars): | 448 def __call__(self, stream, directives, ctxt, **vars): |
449 ctxt._match_templates.append((self.path.test(ignore_context=True), | 449 ctxt._match_templates.append((self.path.test(ignore_context=True), |
450 self.path, list(stream), self.hints, | 450 self.path, list(stream), self.hints, |
451 self.namespaces, directives)) | 451 self.namespaces, directives)) |
481 Bye | 481 Bye |
482 </div> | 482 </div> |
483 """ | 483 """ |
484 __slots__ = [] | 484 __slots__ = [] |
485 | 485 |
486 @classmethod | |
486 def attach(cls, template, stream, value, namespaces, pos): | 487 def attach(cls, template, stream, value, namespaces, pos): |
487 if type(value) is dict: | 488 if type(value) is dict: |
488 value = value.get('value') | 489 value = value.get('value') |
489 if not value: | 490 if not value: |
490 raise TemplateSyntaxError('missing value for "replace" directive', | 491 raise TemplateSyntaxError('missing value for "replace" directive', |
491 template.filepath, *pos[1:]) | 492 template.filepath, *pos[1:]) |
492 expr = cls._parse_expr(value, template, *pos[1:]) | 493 expr = cls._parse_expr(value, template, *pos[1:]) |
493 return None, [(EXPR, expr, pos)] | 494 return None, [(EXPR, expr, pos)] |
494 attach = classmethod(attach) | |
495 | 495 |
496 | 496 |
497 class StripDirective(Directive): | 497 class StripDirective(Directive): |
498 """Implementation of the ``py:strip`` template directive. | 498 """Implementation of the ``py:strip`` template directive. |
499 | 499 |
527 """ | 527 """ |
528 __slots__ = [] | 528 __slots__ = [] |
529 | 529 |
530 def __call__(self, stream, directives, ctxt, **vars): | 530 def __call__(self, stream, directives, ctxt, **vars): |
531 def _generate(): | 531 def _generate(): |
532 if _eval_expr(self.expr, ctxt, **vars): | 532 if _eval_expr(self.expr, ctxt, vars): |
533 stream.next() # skip start tag | 533 stream.next() # skip start tag |
534 previous = stream.next() | 534 previous = stream.next() |
535 for event in stream: | 535 for event in stream: |
536 yield previous | 536 yield previous |
537 previous = event | 537 previous = event |
538 else: | 538 else: |
539 for event in stream: | 539 for event in stream: |
540 yield event | 540 yield event |
541 return _apply_directives(_generate(), directives, ctxt, **vars) | 541 return _apply_directives(_generate(), directives, ctxt, vars) |
542 | 542 |
543 @classmethod | |
543 def attach(cls, template, stream, value, namespaces, pos): | 544 def attach(cls, template, stream, value, namespaces, pos): |
544 if not value: | 545 if not value: |
545 return None, stream[1:-1] | 546 return None, stream[1:-1] |
546 return super(StripDirective, cls).attach(template, stream, value, | 547 return super(StripDirective, cls).attach(template, stream, value, |
547 namespaces, pos) | 548 namespaces, pos) |
548 attach = classmethod(attach) | |
549 | 549 |
550 | 550 |
551 class ChooseDirective(Directive): | 551 class ChooseDirective(Directive): |
552 """Implementation of the ``py:choose`` directive for conditionally selecting | 552 """Implementation of the ``py:choose`` directive for conditionally selecting |
553 one of several body elements to display. | 553 one of several body elements to display. |
587 ``py:when`` or ``py:otherwise`` block. Behavior is also undefined if a | 587 ``py:when`` or ``py:otherwise`` block. Behavior is also undefined if a |
588 ``py:otherwise`` occurs before ``py:when`` blocks. | 588 ``py:otherwise`` occurs before ``py:when`` blocks. |
589 """ | 589 """ |
590 __slots__ = ['matched', 'value'] | 590 __slots__ = ['matched', 'value'] |
591 | 591 |
592 @classmethod | |
592 def attach(cls, template, stream, value, namespaces, pos): | 593 def attach(cls, template, stream, value, namespaces, pos): |
593 if type(value) is dict: | 594 if type(value) is dict: |
594 value = value.get('test') | 595 value = value.get('test') |
595 return super(ChooseDirective, cls).attach(template, stream, value, | 596 return super(ChooseDirective, cls).attach(template, stream, value, |
596 namespaces, pos) | 597 namespaces, pos) |
597 attach = classmethod(attach) | |
598 | 598 |
599 def __call__(self, stream, directives, ctxt, **vars): | 599 def __call__(self, stream, directives, ctxt, **vars): |
600 info = [False, bool(self.expr), None] | 600 info = [False, bool(self.expr), None] |
601 if self.expr: | 601 if self.expr: |
602 info[2] = _eval_expr(self.expr, ctxt, **vars) | 602 info[2] = _eval_expr(self.expr, ctxt, vars) |
603 ctxt._choice_stack.append(info) | 603 ctxt._choice_stack.append(info) |
604 for event in _apply_directives(stream, directives, ctxt, **vars): | 604 for event in _apply_directives(stream, directives, ctxt, vars): |
605 yield event | 605 yield event |
606 ctxt._choice_stack.pop() | 606 ctxt._choice_stack.pop() |
607 | 607 |
608 | 608 |
609 class WhenDirective(Directive): | 609 class WhenDirective(Directive): |
616 | 616 |
617 def __init__(self, value, template, namespaces=None, lineno=-1, offset=-1): | 617 def __init__(self, value, template, namespaces=None, lineno=-1, offset=-1): |
618 Directive.__init__(self, value, template, namespaces, lineno, offset) | 618 Directive.__init__(self, value, template, namespaces, lineno, offset) |
619 self.filename = template.filepath | 619 self.filename = template.filepath |
620 | 620 |
621 @classmethod | |
621 def attach(cls, template, stream, value, namespaces, pos): | 622 def attach(cls, template, stream, value, namespaces, pos): |
622 if type(value) is dict: | 623 if type(value) is dict: |
623 value = value.get('test') | 624 value = value.get('test') |
624 return super(WhenDirective, cls).attach(template, stream, value, | 625 return super(WhenDirective, cls).attach(template, stream, value, |
625 namespaces, pos) | 626 namespaces, pos) |
626 attach = classmethod(attach) | |
627 | 627 |
628 def __call__(self, stream, directives, ctxt, **vars): | 628 def __call__(self, stream, directives, ctxt, **vars): |
629 info = ctxt._choice_stack and ctxt._choice_stack[-1] | 629 info = ctxt._choice_stack and ctxt._choice_stack[-1] |
630 if not info: | 630 if not info: |
631 raise TemplateRuntimeError('"when" directives can only be used ' | 631 raise TemplateRuntimeError('"when" directives can only be used ' |
638 'must have a test expression', | 638 'must have a test expression', |
639 self.filename, *stream.next()[2][1:]) | 639 self.filename, *stream.next()[2][1:]) |
640 if info[1]: | 640 if info[1]: |
641 value = info[2] | 641 value = info[2] |
642 if self.expr: | 642 if self.expr: |
643 matched = value == _eval_expr(self.expr, ctxt, **vars) | 643 matched = value == _eval_expr(self.expr, ctxt, vars) |
644 else: | 644 else: |
645 matched = bool(value) | 645 matched = bool(value) |
646 else: | 646 else: |
647 matched = bool(_eval_expr(self.expr, ctxt, **vars)) | 647 matched = bool(_eval_expr(self.expr, ctxt, vars)) |
648 info[0] = matched | 648 info[0] = matched |
649 if not matched: | 649 if not matched: |
650 return [] | 650 return [] |
651 | 651 |
652 return _apply_directives(stream, directives, ctxt, **vars) | 652 return _apply_directives(stream, directives, ctxt, vars) |
653 | 653 |
654 | 654 |
655 class OtherwiseDirective(Directive): | 655 class OtherwiseDirective(Directive): |
656 """Implementation of the ``py:otherwise`` directive for nesting in a parent | 656 """Implementation of the ``py:otherwise`` directive for nesting in a parent |
657 with the ``py:choose`` directive. | 657 with the ``py:choose`` directive. |
672 self.filename, *stream.next()[2][1:]) | 672 self.filename, *stream.next()[2][1:]) |
673 if info[0]: | 673 if info[0]: |
674 return [] | 674 return [] |
675 info[0] = True | 675 info[0] = True |
676 | 676 |
677 return _apply_directives(stream, directives, ctxt, **vars) | 677 return _apply_directives(stream, directives, ctxt, vars) |
678 | 678 |
679 | 679 |
680 class WithDirective(Directive): | 680 class WithDirective(Directive): |
681 """Implementation of the ``py:with`` template directive, which allows | 681 """Implementation of the ``py:with`` template directive, which allows |
682 shorthand access to variables and expressions. | 682 shorthand access to variables and expressions. |
710 err.msg += ' in expression "%s" of "%s" directive' % (value, | 710 err.msg += ' in expression "%s" of "%s" directive' % (value, |
711 self.tagname) | 711 self.tagname) |
712 raise TemplateSyntaxError(err, template.filepath, lineno, | 712 raise TemplateSyntaxError(err, template.filepath, lineno, |
713 offset + (err.offset or 0)) | 713 offset + (err.offset or 0)) |
714 | 714 |
715 @classmethod | |
715 def attach(cls, template, stream, value, namespaces, pos): | 716 def attach(cls, template, stream, value, namespaces, pos): |
716 if type(value) is dict: | 717 if type(value) is dict: |
717 value = value.get('vars') | 718 value = value.get('vars') |
718 return super(WithDirective, cls).attach(template, stream, value, | 719 return super(WithDirective, cls).attach(template, stream, value, |
719 namespaces, pos) | 720 namespaces, pos) |
720 attach = classmethod(attach) | |
721 | 721 |
722 def __call__(self, stream, directives, ctxt, **vars): | 722 def __call__(self, stream, directives, ctxt, **vars): |
723 frame = {} | 723 frame = {} |
724 ctxt.push(frame) | 724 ctxt.push(frame) |
725 for targets, expr in self.vars: | 725 for targets, expr in self.vars: |
726 value = _eval_expr(expr, ctxt, **vars) | 726 value = _eval_expr(expr, ctxt, vars) |
727 for _, assign in targets: | 727 for _, assign in targets: |
728 assign(frame, value) | 728 assign(frame, value) |
729 for event in _apply_directives(stream, directives, ctxt, **vars): | 729 for event in _apply_directives(stream, directives, ctxt, vars): |
730 yield event | 730 yield event |
731 ctxt.pop() | 731 ctxt.pop() |
732 | 732 |
733 def __repr__(self): | 733 def __repr__(self): |
734 return '<%s>' % (self.__class__.__name__) | 734 return '<%s>' % (self.__class__.__name__) |