Ticket #190: match_unbuffered3.diff
| File match_unbuffered3.diff, 18.7 kB (added by cmlenz, 7 months ago) |
|---|
-
genshi/template/base.py
254 254 """Pop the top-most scope from the stack.""" 255 255 256 256 257 def _apply_directives(stream, ctxt, directives):257 def _apply_directives(stream, directives, ctxt, **bind): 258 258 """Apply the given directives to the stream. 259 259 260 260 :param stream: the stream the directives should be applied to … … 263 263 :return: the stream with the given directives applied 264 264 """ 265 265 if directives: 266 stream = directives[0](iter(stream), ctxt, directives[1:])266 stream = directives[0](iter(stream), directives[1:], ctxt, **bind) 267 267 return stream 268 268 269 269 … … 426 426 :return: a markup event stream representing the result of applying 427 427 the template to the context data. 428 428 """ 429 bind = {} 429 430 if args: 430 431 assert len(args) == 1 431 432 ctxt = args[0] 432 433 if ctxt is None: 433 434 ctxt = Context(**kwargs) 435 else: 436 bind = kwargs 434 437 assert isinstance(ctxt, Context) 435 438 else: 436 439 ctxt = Context(**kwargs) 437 440 438 441 stream = self.stream 439 442 for filter_ in self.filters: 440 stream = filter_(iter(stream), ctxt )443 stream = filter_(iter(stream), ctxt, **bind) 441 444 return Stream(stream, self.serializer) 442 445 443 def _eval(self, stream, ctxt ):446 def _eval(self, stream, ctxt, **bind): 444 447 """Internal stream filter that evaluates any expressions in `START` and 445 448 `TEXT` events. 446 449 """ … … 460 463 else: 461 464 values = [] 462 465 for subkind, subdata, subpos in self._eval(substream, 463 ctxt): 466 ctxt, 467 **bind): 464 468 if subkind is TEXT: 465 469 values.append(subdata) 466 470 value = [x for x in values if x is not None] … … 470 474 yield kind, (tag, Attrs(new_attrs)), pos 471 475 472 476 elif kind is EXPR: 477 if bind: ctxt.push(bind) 473 478 result = data.evaluate(ctxt) 479 if bind: ctxt.pop() 474 480 if result is not None: 475 481 # First check for a string, otherwise the iterable test 476 482 # below succeeds, and the string will be chopped up into … … 482 488 elif hasattr(result, '__iter__'): 483 489 substream = _ensure(result) 484 490 for filter_ in filters: 485 substream = filter_(substream, ctxt )491 substream = filter_(substream, ctxt, **bind) 486 492 for event in substream: 487 493 yield event 488 494 else: … … 491 497 else: 492 498 yield kind, data, pos 493 499 494 def _exec(self, stream, ctxt ):500 def _exec(self, stream, ctxt, **bind): 495 501 """Internal stream filter that executes Python code blocks.""" 496 502 for event in stream: 497 503 if event[0] is EXEC: 504 if bind: ctxt.push(bind) 498 505 event[1].execute(_ctxt2dict(ctxt)) 506 if bind: ctxt.pop() 499 507 else: 500 508 yield event 501 509 502 def _flatten(self, stream, ctxt ):510 def _flatten(self, stream, ctxt, **bind): 503 511 """Internal stream filter that expands `SUB` events in the stream.""" 504 512 for event in stream: 505 513 if event[0] is SUB: 506 514 # This event is a list of directives and a list of nested 507 515 # events to which those directives should be applied 508 516 directives, substream = event[1] 509 substream = _apply_directives(substream, ctxt, directives) 510 for event in self._flatten(substream, ctxt): 517 substream = _apply_directives(substream, directives, ctxt, 518 **bind) 519 for event in self._flatten(substream, ctxt, **bind): 511 520 yield event 512 521 else: 513 522 yield event 514 523 515 def _include(self, stream, ctxt ):524 def _include(self, stream, ctxt, **bind): 516 525 """Internal stream filter that performs inclusion of external 517 526 template files. 518 527 """ … … 523 532 href, cls, fallback = event[1] 524 533 if not isinstance(href, basestring): 525 534 parts = [] 526 for subkind, subdata, subpos in self._eval(href, ctxt): 535 for subkind, subdata, subpos in self._eval(href, ctxt, 536 **bind): 527 537 if subkind is TEXT: 528 538 parts.append(subdata) 529 539 href = u''.join([x for x in parts if x is not None]) 530 540 try: 531 541 tmpl = self.loader.load(href, relative_to=event[2][0], 532 542 cls=cls or self.__class__) 533 for event in tmpl.generate(ctxt ):543 for event in tmpl.generate(ctxt, **bind): 534 544 yield event 535 545 except TemplateNotFound: 536 546 if fallback is None: 537 547 raise 538 548 for filter_ in self.filters: 539 fallback = filter_(iter(fallback), ctxt )549 fallback = filter_(iter(fallback), ctxt, **bind) 540 550 for event in fallback: 541 551 yield event 542 552 else: -
genshi/template/tests/directives.py
631 631 </body> 632 632 </html>""", str(tmpl.generate())) 633 633 634 def test_recursive_match_3(self): 635 tmpl = MarkupTemplate("""<test xmlns:py="http://genshi.edgewall.org/"> 636 <py:match path="b[@type='bullet']"> 637 <bullet>${select('*|text()')}</bullet> 638 </py:match> 639 <py:match path="group[@type='bullet']"> 640 <ul>${select('*')}</ul> 641 </py:match> 642 <py:match path="b"> 643 <generic>${select('*|text()')}</generic> 644 </py:match> 645 646 <b> 647 <group type="bullet"> 648 <b type="bullet">1</b> 649 <b type="bullet">2</b> 650 </group> 651 </b> 652 </test> 653 """) 654 self.assertEqual("""<test> 655 <generic> 656 <ul><bullet>1</bullet><bullet>2</bullet></ul> 657 </generic> 658 </test>""", str(tmpl.generate())) 659 634 660 def test_not_match_self(self): 635 661 """ 636 662 See http://genshi.edgewall.org/ticket/77 -
genshi/template/markup.py
225 225 assert len(streams) == 1 226 226 return streams[0] 227 227 228 def _match(self, stream, ctxt, match_templates=None ):228 def _match(self, stream, ctxt, match_templates=None, **bind): 229 229 """Internal stream filter that applies any defined match templates 230 230 to the stream. 231 231 """ … … 272 272 # Consume and store all events until an end event 273 273 # corresponding to this start event is encountered 274 274 inner = _strip(stream) 275 if 'match_once' not in hints \ 276 and 'not_recursive' not in hints: 277 inner = self._match(inner, ctxt, [match_templates[idx]]) 278 content = list(self._include(chain([event], inner, tail), 279 ctxt)) 275 pre_match_templates = match_templates[:idx + 1] 276 if 'match_once' not in hints and 'not_recursive' in hints: 277 pre_match_templates.pop() 278 inner = self._match(inner, ctxt, pre_match_templates, 279 **bind) 280 content = self._include(chain([event], inner, tail), 281 ctxt, **bind) 282 if 'not_buffered' not in hints: 283 content = list(content) 280 284 281 285 for test in [mt[0] for mt in match_templates]: 282 286 test(tail[0], namespaces, ctxt, updateonly=True) … … 285 289 # match template 286 290 def select(path): 287 291 return Stream(content).select(path, namespaces, ctxt) 288 ctxt.push(dict(select=select))292 bind = dict(select=select) 289 293 290 294 # Recursively process the output 291 template = _apply_directives(template, ctxt, directives) 295 template = _apply_directives(template, directives, ctxt, 296 **bind) 292 297 remaining = match_templates 293 if 'match_once' not in hints: 294 remaining = remaining[:idx] + remaining[idx + 1:] 295 for event in self._match(self._exec( 296 self._eval(self._flatten(template, ctxt), 297 ctxt), ctxt), ctxt, remaining): 298 for event in self._match( 299 self._exec( 300 self._eval( 301 self._flatten(template, ctxt, **bind), 302 ctxt, **bind), 303 ctxt, **bind), 304 ctxt, match_templates[idx + 1:], **bind): 298 305 yield event 299 306 300 ctxt.pop()301 307 break 302 308 303 309 else: # no matches -
genshi/template/directives.py
88 88 return cls(value, template, namespaces, *pos[1:]), stream 89 89 attach = classmethod(attach) 90 90 91 def __call__(self, stream, ctxt, directives):91 def __call__(self, stream, directives, ctxt, **bind): 92 92 """Apply the directive to the given stream. 93 93 94 94 :param stream: the event stream 95 :param ctxt: the context data96 95 :param directives: a list of the remaining directives that should 97 96 process the stream 97 :param ctxt: the context data 98 98 """ 99 99 raise NotImplementedError 100 100 … … 167 167 """ 168 168 __slots__ = [] 169 169 170 def __call__(self, stream, ctxt, directives):170 def __call__(self, stream, directives, ctxt, **bind): 171 171 def _generate(): 172 172 kind, (tag, attrib), pos = stream.next() 173 if bind: ctxt.push(bind) 173 174 attrs = self.expr.evaluate(ctxt) 175 if bind: ctxt.pop() 174 176 if attrs: 175 177 if isinstance(attrs, Stream): 176 178 try: … … 186 188 for event in stream: 187 189 yield event 188 190 189 return _apply_directives(_generate(), ctxt, directives)191 return _apply_directives(_generate(), directives, ctxt, **bind) 190 192 191 193 192 194 class ContentDirective(Directive): … … 291 293 namespaces, pos) 292 294 attach = classmethod(attach) 293 295 294 def __call__(self, stream, ctxt, directives):296 def __call__(self, stream, directives, ctxt, **bind): 295 297 stream = list(stream) 296 298 297 299 def function(*args, **kwargs): 298 300 scope = {} 301 if bind: ctxt.push(bind) 299 302 args = list(args) # make mutable 300 303 for name in self.args: 301 304 if args: … … 306 309 else: 307 310 val = self.defaults.get(name).evaluate(ctxt) 308 311 scope[name] = val 312 if bind: ctxt.pop() 309 313 if not self.star_args is None: 310 314 scope[self.star_args] = args 311 315 if not self.dstar_args is None: 312 316 scope[self.dstar_args] = kwargs 313 317 ctxt.push(scope) 314 for event in _apply_directives(stream, ctxt, directives):318 for event in _apply_directives(stream, directives, ctxt, **bind): 315 319 yield event 316 320 ctxt.pop() 317 321 try: … … 364 368 namespaces, pos) 365 369 attach = classmethod(attach) 366 370 367 def __call__(self, stream, ctxt, directives): 371 def __call__(self, stream, directives, ctxt, **bind): 372 if bind: ctxt.push(bind) 368 373 iterable = self.expr.evaluate(ctxt) 374 if bind: ctxt.pop() 369 375 if iterable is None: 370 376 return 371 377 … … 375 381 for item in iterable: 376 382 assign(scope, item) 377 383 ctxt.push(scope) 378 for event in _apply_directives(stream, ctxt, directives):384 for event in _apply_directives(stream, directives, ctxt, **bind): 379 385 yield event 380 386 ctxt.pop() 381 387 … … 405 411 namespaces, pos) 406 412 attach = classmethod(attach) 407 413 408 def __call__(self, stream, ctxt, directives): 409 if self.expr.evaluate(ctxt): 410 return _apply_directives(stream, ctxt, directives) 414 def __call__(self, stream, directives, ctxt, **bind): 415 if bind: ctxt.push(bind) 416 value = self.expr.evaluate(ctxt) 417 if bind: ctxt.pop() 418 if value: 419 return _apply_directives(stream, directives, ctxt, **bind) 411 420 return [] 412 421 413 422 … … 440 449 def attach(cls, template, stream, value, namespaces, pos): 441 450 hints = [] 442 451 if type(value) is dict: 452 if value.get('buffer', '').lower() == 'false': 453 hints.append('not_buffered') 443 454 if value.get('once', '').lower() == 'true': 444 455 hints.append('match_once') 445 456 if value.get('recursive', '').lower() == 'false': … … 449 460 stream 450 461 attach = classmethod(attach) 451 462 452 def __call__(self, stream, ctxt, directives):463 def __call__(self, stream, directives, ctxt, **bind): 453 464 ctxt._match_templates.append((self.path.test(ignore_context=True), 454 465 self.path, list(stream), self.hints, 455 466 self.namespaces, directives)) … … 531 542 """ 532 543 __slots__ = [] 533 544 534 def __call__(self, stream, ctxt, directives):545 def __call__(self, stream, directives, ctxt, **bind): 535 546 def _generate(): 536 547 if self.expr.evaluate(ctxt): 537 548 stream.next() # skip start tag … … 542 553 else: 543 554 for event in stream: 544 555 yield event 545 return _apply_directives(_generate(), ctxt, directives)556 return _apply_directives(_generate(), directives, ctxt, **bind) 546 557 547 558 def attach(cls, template, stream, value, namespaces, pos): 548 559 if not value: … … 600 611 namespaces, pos) 601 612 attach = classmethod(attach) 602 613 603 def __call__(self, stream, ctxt, directives):614 def __call__(self, stream, directives, ctxt, **bind): 604 615 info = [False, bool(self.expr), None] 605 616 if self.expr: 617 if bind: ctxt.push(bind) 606 618 info[2] = self.expr.evaluate(ctxt) 619 if bind: ctxt.pop() 607 620 ctxt._choice_stack.append(info) 608 for event in _apply_directives(stream, ctxt, directives):621 for event in _apply_directives(stream, directives, ctxt, **bind): 609 622 yield event 610 623 ctxt._choice_stack.pop() 611 624 … … 629 642 namespaces, pos) 630 643 attach = classmethod(attach) 631 644 632 def __call__(self, stream, ctxt, directives):645 def __call__(self, stream, directives, ctxt, **bind): 633 646 info = ctxt._choice_stack and ctxt._choice_stack[-1] 634 647 if not info: 635 648 raise TemplateRuntimeError('"when" directives can only be used ' … … 644 657 if info[1]: 645 658 value = info[2] 646 659 if self.expr: 660 if bind: ctxt.push(bind) 647 661 matched = value == self.expr.evaluate(ctxt) 662 if bind: ctxt.pop() 648 663 else: 649 664 matched = bool(value) 650 665 else: … … 653 668 if not matched: 654 669 return [] 655 670 656 return _apply_directives(stream, ctxt, directives)671 return _apply_directives(stream, directives, ctxt, **bind) 657 672 658 673 659 674 class OtherwiseDirective(Directive): … … 668 683 Directive.__init__(self, None, template, namespaces, lineno, offset) 669 684 self.filename = template.filepath 670 685 671 def __call__(self, stream, ctxt, directives):686 def __call__(self, stream, directives, ctxt, **bind): 672 687 info = ctxt._choice_stack and ctxt._choice_stack[-1] 673 688 if not info: 674 689 raise TemplateRuntimeError('an "otherwise" directive can only be ' … … 678 693 return [] 679 694 info[0] = True 680 695 681 return _apply_directives(stream, ctxt, directives)696 return _apply_directives(stream, directives, ctxt, **bind) 682 697 683 698 684 699 class WithDirective(Directive): … … 722 737 namespaces, pos) 723 738 attach = classmethod(attach) 724 739 725 def __call__(self, stream, ctxt, directives):740 def __call__(self, stream, directives, ctxt, **bind): 726 741 frame = {} 727 742 ctxt.push(frame) 743 if bind: ctxt.push(bind) 728 744 self.suite.execute(_ctxt2dict(ctxt)) 729 for event in _apply_directives(stream, ctxt, directives): 745 if bind: ctxt.pop() 746 for event in _apply_directives(stream, directives, ctxt, **bind): 730 747 yield event 731 748 ctxt.pop() 732 749 -
genshi/template/text.py
33 33 TemplateSyntaxError, EXEC, INCLUDE, SUB 34 34 from genshi.template.eval import Suite 35 35 from genshi.template.directives import * 36 from genshi.template.directives import Directive , _apply_directives36 from genshi.template.directives import Directive 37 37 from genshi.template.interpolation import interpolate 38 38 39 39 __all__ = ['NewTextTemplate', 'OldTextTemplate', 'TextTemplate']
