comparison markup/template.py @ 50:d3842cd76e92 trunk

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.
author cmlenz
date Tue, 04 Jul 2006 08:37:25 +0000
parents a5d585dd38c4
children b2383634ec04
comparison
equal deleted inserted replaced
49:6d1f79b2f7ef 50:d3842cd76e92
174 __slots__ = ['expr'] 174 __slots__ = ['expr']
175 175
176 def __init__(self, value): 176 def __init__(self, value):
177 self.expr = value and Expression(value) or None 177 self.expr = value and Expression(value) or None
178 178
179 def __call__(self, stream, ctxt): 179 def __call__(self, stream, ctxt, directives=None):
180 raise NotImplementedError 180 raise NotImplementedError
181 181
182 def __repr__(self): 182 def __repr__(self):
183 expr = '' 183 expr = ''
184 if self.expr is not None: 184 if self.expr is not None:
208 >>> print tmpl.generate(ctxt) 208 >>> print tmpl.generate(ctxt)
209 <ul> 209 <ul>
210 <li>Bar</li> 210 <li>Bar</li>
211 </ul> 211 </ul>
212 """ 212 """
213 def __call__(self, stream, ctxt): 213 __slots__ = []
214
215 def __call__(self, stream, ctxt, directives=None):
214 kind, (tag, attrib), pos = stream.next() 216 kind, (tag, attrib), pos = stream.next()
215 attrs = self.expr.evaluate(ctxt) 217 attrs = self.expr.evaluate(ctxt)
216 if attrs: 218 if attrs:
217 attrib = Attributes(attrib[:]) 219 attrib = Attributes(attrib[:])
218 if not isinstance(attrs, list): # assume it's a dict 220 if not isinstance(attrs, list): # assume it's a dict
240 >>> print tmpl.generate(ctxt) 242 >>> print tmpl.generate(ctxt)
241 <ul> 243 <ul>
242 <li>Bye</li> 244 <li>Bye</li>
243 </ul> 245 </ul>
244 """ 246 """
245 def __call__(self, stream, ctxt): 247 __slots__ = []
246 kind, data, pos = stream.next() 248
247 if kind is Stream.START: 249 def __call__(self, stream, ctxt, directives):
248 yield kind, data, pos # emit start tag 250 def generate():
249 yield Template.EXPR, self.expr, pos 251 kind, data, pos = stream.next()
250 previous = stream.next() 252 if kind is Stream.START:
251 for event in stream: 253 yield kind, data, pos # emit start tag
252 previous = event 254 yield Template.EXPR, self.expr, pos
253 if previous is not None: 255 previous = stream.next()
254 yield previous 256 for event in stream:
257 previous = event
258 if previous is not None:
259 yield previous
260 output = generate()
261 if directives:
262 output = directives[0](output, ctxt, directives[1:])
263 return output
255 264
256 265
257 class DefDirective(Directive): 266 class DefDirective(Directive):
258 """Implementation of the `py:def` template directive. 267 """Implementation of the `py:def` template directive.
259 268
291 <p class="message"> 300 <p class="message">
292 hello, world! 301 hello, world!
293 </p> 302 </p>
294 </div> 303 </div>
295 """ 304 """
296 __slots__ = ['name', 'args', 'defaults', 'stream'] 305 __slots__ = ['name', 'args', 'defaults', 'stream', 'directives']
297 306
298 def __init__(self, args): 307 def __init__(self, args):
299 Directive.__init__(self, None) 308 Directive.__init__(self, None)
300 ast = compiler.parse(args, 'eval').node 309 ast = compiler.parse(args, 'eval').node
301 self.args = [] 310 self.args = []
308 self.defaults[arg.name] = arg.expr.value 317 self.defaults[arg.name] = arg.expr.value
309 else: 318 else:
310 self.args.append(arg.name) 319 self.args.append(arg.name)
311 else: 320 else:
312 self.name = ast.name 321 self.name = ast.name
313 self.stream = [] 322 self.stream, self.directives = [], []
314 323
315 def __call__(self, stream, ctxt): 324 def __call__(self, stream, ctxt, directives):
316 self.stream = list(stream) 325 self.stream = list(stream)
326 self.directives = directives
317 ctxt[self.name] = lambda *args, **kwargs: self._exec(ctxt, *args, 327 ctxt[self.name] = lambda *args, **kwargs: self._exec(ctxt, *args,
318 **kwargs) 328 **kwargs)
319 return [] 329 return []
320 330
321 def _exec(self, ctxt, *args, **kwargs): 331 def _exec(self, ctxt, *args, **kwargs):
325 if args: 335 if args:
326 scope[name] = args.pop(0) 336 scope[name] = args.pop(0)
327 else: 337 else:
328 scope[name] = kwargs.pop(name, self.defaults.get(name)) 338 scope[name] = kwargs.pop(name, self.defaults.get(name))
329 ctxt.push(**scope) 339 ctxt.push(**scope)
330 for event in self.stream: 340 stream = iter(self.stream)
341 if self.directives:
342 stream = self.directives[0](stream, ctxt, self.directives[1:])
343 for event in stream:
331 yield event 344 yield event
332 ctxt.pop() 345 ctxt.pop()
333 346
334 347
335 class ForDirective(Directive): 348 class ForDirective(Directive):
350 def __init__(self, value): 363 def __init__(self, value):
351 targets, value = value.split(' in ', 1) 364 targets, value = value.split(' in ', 1)
352 self.targets = [str(name.strip()) for name in targets.split(',')] 365 self.targets = [str(name.strip()) for name in targets.split(',')]
353 Directive.__init__(self, value) 366 Directive.__init__(self, value)
354 367
355 def __call__(self, stream, ctxt): 368 def __call__(self, stream, ctxt, directives):
356 iterable = self.expr.evaluate(ctxt) or [] 369 iterable = self.expr.evaluate(ctxt) or []
357 if iterable is not None: 370 if iterable is not None:
358 stream = list(stream) 371 stream = list(stream)
359 for item in iter(iterable): 372 for item in iter(iterable):
360 if len(self.targets) == 1: 373 if len(self.targets) == 1:
361 item = [item] 374 item = [item]
362 scope = {} 375 scope = {}
363 for idx, name in enumerate(self.targets): 376 for idx, name in enumerate(self.targets):
364 scope[name] = item[idx] 377 scope[name] = item[idx]
365 ctxt.push(**scope) 378 ctxt.push(**scope)
379 if directives:
380 stream = list(directives[0](iter(stream), ctxt,
381 directives[1:]))
366 for event in stream: 382 for event in stream:
367 yield event 383 yield event
368 ctxt.pop() 384 ctxt.pop()
369 385
370 def __repr__(self): 386 def __repr__(self):
383 >>> print tmpl.generate(ctxt) 399 >>> print tmpl.generate(ctxt)
384 <div> 400 <div>
385 <b>Hello</b> 401 <b>Hello</b>
386 </div> 402 </div>
387 """ 403 """
388 def __call__(self, stream, ctxt): 404 __slots__ = []
405
406 def __call__(self, stream, ctxt, directives):
389 if self.expr.evaluate(ctxt): 407 if self.expr.evaluate(ctxt):
408 if directives:
409 stream = directives[0](stream, ctxt, directives[1:])
390 return stream 410 return stream
391 return [] 411 return []
392 412
393 413
394 class MatchDirective(Directive): 414 class MatchDirective(Directive):
412 def __init__(self, value): 432 def __init__(self, value):
413 Directive.__init__(self, None) 433 Directive.__init__(self, None)
414 self.path = Path(value) 434 self.path = Path(value)
415 self.stream = [] 435 self.stream = []
416 436
417 def __call__(self, stream, ctxt): 437 def __call__(self, stream, ctxt, directives):
418 self.stream = list(stream) 438 self.stream = list(stream)
419 ctxt._match_templates.append((self.path.test(ignore_context=True), 439 ctxt._match_templates.append((self.path.test(ignore_context=True),
420 self.path, self.stream)) 440 self.path, self.stream, directives))
421 return [] 441 return []
422 442
423 def __repr__(self): 443 def __repr__(self):
424 return '<%s "%s">' % (self.__class__.__name__, self.path.source) 444 return '<%s "%s">' % (self.__class__.__name__, self.path.source)
425 445
449 >>> print tmpl.generate(ctxt) 469 >>> print tmpl.generate(ctxt)
450 <div> 470 <div>
451 Bye 471 Bye
452 </div> 472 </div>
453 """ 473 """
454 def __call__(self, stream, ctxt): 474 __slots__ = []
475
476 def __call__(self, stream, ctxt, directives=None):
455 kind, data, pos = stream.next() 477 kind, data, pos = stream.next()
456 yield Template.EXPR, self.expr, pos 478 yield Template.EXPR, self.expr, pos
457 479
458 480
459 class StripDirective(Directive): 481 class StripDirective(Directive):
484 >>> print tmpl.generate() 506 >>> print tmpl.generate()
485 <div> 507 <div>
486 <b>foo</b> 508 <b>foo</b>
487 </div> 509 </div>
488 """ 510 """
489 def __call__(self, stream, ctxt): 511 __slots__ = []
512
513 def __call__(self, stream, ctxt, directives=None):
490 if self.expr: 514 if self.expr:
491 strip = self.expr.evaluate(ctxt) 515 strip = self.expr.evaluate(ctxt)
492 else: 516 else:
493 strip = True 517 strip = True
494 if strip: 518 if strip:
572 596
573 Behavior is undefined if a `py:choose` block contains content outside a 597 Behavior is undefined if a `py:choose` block contains content outside a
574 `py:when` or `py:otherwise` block. Behavior is also undefined if a 598 `py:when` or `py:otherwise` block. Behavior is also undefined if a
575 `py:otherwise` occurs before `py:when` blocks. 599 `py:otherwise` occurs before `py:when` blocks.
576 """ 600 """
577 601 __slots__ = ['matched', 'value']
578 def __call__(self, stream, ctxt): 602
603 def __call__(self, stream, ctxt, directives=None):
579 if self.expr: 604 if self.expr:
580 self.value = self.expr.evaluate(ctxt) 605 self.value = self.expr.evaluate(ctxt)
581 self.matched = False 606 self.matched = False
582 ctxt.push(__choose=self) 607 ctxt.push(_choose=self)
583 for event in stream: 608 for event in stream:
584 yield event 609 yield event
585 ctxt.pop() 610 ctxt.pop()
586 611
587 612
588 class WhenDirective(Directive): 613 class WhenDirective(Directive):
589 """Implementation of the `py:when` directive for nesting in a parent with 614 """Implementation of the `py:when` directive for nesting in a parent with
590 the `py:choose` directive. See the documentation of `py:choose` for 615 the `py:choose` directive.
591 usage. 616
592 """ 617 See the documentation of `py:choose` for usage.
593 def __call__(self, stream, ctxt): 618 """
594 choose = ctxt['__choose'] 619 def __call__(self, stream, ctxt, directives=None):
620 choose = ctxt['_choose']
595 if choose.matched: 621 if choose.matched:
596 return [] 622 return []
597 value = self.expr.evaluate(ctxt) 623 value = self.expr.evaluate(ctxt)
598 try: 624 try:
599 if value == choose.value: 625 if value == choose.value:
606 return [] 632 return []
607 633
608 634
609 class OtherwiseDirective(Directive): 635 class OtherwiseDirective(Directive):
610 """Implementation of the `py:otherwise` directive for nesting in a parent 636 """Implementation of the `py:otherwise` directive for nesting in a parent
611 with the `py:choose` directive. See the documentation of `py:choose` for 637 with the `py:choose` directive.
612 usage. 638
613 """ 639 See the documentation of `py:choose` for usage.
614 def __call__(self, stream, ctxt): 640 """
615 choose = ctxt['__choose'] 641 def __call__(self, stream, ctxt, directives=None):
642 choose = ctxt['_choose']
616 if choose.matched: 643 if choose.matched:
617 return [] 644 return []
618 choose.matched = True 645 choose.matched = True
619 return stream 646 return stream
620 647
630 657
631 directives = [('def', DefDirective), 658 directives = [('def', DefDirective),
632 ('match', MatchDirective), 659 ('match', MatchDirective),
633 ('for', ForDirective), 660 ('for', ForDirective),
634 ('if', IfDirective), 661 ('if', IfDirective),
662 ('choose', ChooseDirective),
663 ('when', WhenDirective),
664 ('otherwise', OtherwiseDirective),
635 ('replace', ReplaceDirective), 665 ('replace', ReplaceDirective),
636 ('content', ContentDirective), 666 ('content', ContentDirective),
637 ('attrs', AttrsDirective), 667 ('attrs', AttrsDirective),
638 ('strip', StripDirective), 668 ('strip', StripDirective)]
639 ('choose', ChooseDirective),
640 ('when', WhenDirective),
641 ('otherwise', OtherwiseDirective)]
642 _dir_by_name = dict(directives) 669 _dir_by_name = dict(directives)
643 _dir_order = [directive[1] for directive in directives] 670 _dir_order = [directive[1] for directive in directives]
644 671
645 def __init__(self, source, basedir=None, filename=None): 672 def __init__(self, source, basedir=None, filename=None):
646 """Initialize a template from either a string or a file-like object.""" 673 """Initialize a template from either a string or a file-like object."""
705 directives.append(cls(value)) 732 directives.append(cls(value))
706 else: 733 else:
707 value = list(self._interpolate(value, *pos)) 734 value = list(self._interpolate(value, *pos))
708 new_attrib.append((name, value)) 735 new_attrib.append((name, value))
709 if directives: 736 if directives:
710 directives.sort(lambda a, b: cmp(self._dir_order.index(b.__class__), 737 directives.sort(lambda a, b: cmp(self._dir_order.index(a.__class__),
711 self._dir_order.index(a.__class__))) 738 self._dir_order.index(b.__class__)))
712 dirmap[(depth, tag)] = (directives, len(stream)) 739 dirmap[(depth, tag)] = (directives, len(stream))
713 740
714 stream.append((kind, (tag, Attributes(new_attrib)), pos)) 741 stream.append((kind, (tag, Attributes(new_attrib)), pos))
715 depth += 1 742 depth += 1
716 743
840 for kind, data, pos in stream: 867 for kind, data, pos in stream:
841 if kind is Template.SUB: 868 if kind is Template.SUB:
842 # This event is a list of directives and a list of nested 869 # This event is a list of directives and a list of nested
843 # events to which those directives should be applied 870 # events to which those directives should be applied
844 directives, substream = data 871 directives, substream = data
845 for directive in directives: 872 substream = directives[0](iter(substream), ctxt, directives[1:])
846 substream = directive(iter(substream), ctxt)
847 substream = self._match(self._eval(substream, ctxt), ctxt) 873 substream = self._match(self._eval(substream, ctxt), ctxt)
848 for event in self._flatten(substream, ctxt): 874 for event in self._flatten(substream, ctxt):
849 yield event 875 yield event
850 continue 876 continue
851 else: 877 else:
867 # We might care about namespace events in the future, though 893 # We might care about namespace events in the future, though
868 if kind not in (Stream.START, Stream.END): 894 if kind not in (Stream.START, Stream.END):
869 yield kind, data, pos 895 yield kind, data, pos
870 continue 896 continue
871 897
872 for idx, (test, path, template) in enumerate(match_templates): 898 for idx, (test, path, template, directives) in \
899 enumerate(match_templates):
873 result = test(kind, data, pos) 900 result = test(kind, data, pos)
874 901
875 if result: 902 if result:
876 # Consume and store all events until an end event 903 # Consume and store all events until an end event
877 # corresponding to this start event is encountered 904 # corresponding to this start event is encountered
889 test(*event) 916 test(*event)
890 917
891 content = list(self._flatten(content, ctxt)) 918 content = list(self._flatten(content, ctxt))
892 ctxt.push(select=lambda path: Stream(content).select(path)) 919 ctxt.push(select=lambda path: Stream(content).select(path))
893 920
921 if directives:
922 template = directives[0](iter(template), ctxt,
923 directives[1:])
894 template = self._match(self._eval(iter(template), ctxt), 924 template = self._match(self._eval(iter(template), ctxt),
895 ctxt, match_templates[:idx] + 925 ctxt, match_templates[:idx] +
896 match_templates[idx + 1:]) 926 match_templates[idx + 1:])
897 for event in template: 927 for event in template:
898 yield event 928 yield event
Copyright (C) 2012-2017 Edgewall Software