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__)
Copyright (C) 2012-2017 Edgewall Software