Ticket #190: match_unbuffered6.diff
| File match_unbuffered6.diff, 20.2 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, **vars): 258 258 """Apply the given directives to the stream. 259 259 260 260 :param stream: the stream the directives should be applied to 261 :param directives: the list of directives to apply 261 262 :param ctxt: the `Context` 262 :param directives: the list of directives to apply 263 :param vars: additional variables that should be available when Python 264 code is executed 263 265 :return: the stream with the given directives applied 264 266 """ 265 267 if directives: 266 stream = directives[0](iter(stream), ctxt, directives[1:])268 stream = directives[0](iter(stream), directives[1:], ctxt, **vars) 267 269 return stream 268 270 271 def _eval_expr(expr, ctxt, **vars): 272 """Evaluate the given `Expression` object. 273 274 :param expr: the expression to evaluate 275 :param ctxt: the `Context` 276 :param vars: additional variables that should be available to the 277 expression 278 :return: the result of the evaluation 279 """ 280 if vars: 281 ctxt.push(vars) 282 retval = expr.evaluate(ctxt) 283 if vars: 284 ctxt.pop() 285 return retval 269 286 287 def _exec_suite(suite, ctxt, **vars): 288 """Execute the given `Suite` object. 289 290 :param suite: the code suite to execute 291 :param ctxt: the `Context` 292 :param vars: additional variables that should be available to the 293 code 294 """ 295 if vars: 296 ctxt.push(vars) 297 ctxt.push({}) 298 suite.execute(_ctxt2dict(ctxt)) 299 if vars: 300 top = ctxt.pop() 301 ctxt.pop() 302 ctxt.frames[0].update(top) 303 304 270 305 class TemplateMeta(type): 271 306 """Meta class for templates.""" 272 307 … … 426 461 :return: a markup event stream representing the result of applying 427 462 the template to the context data. 428 463 """ 464 vars = {} 429 465 if args: 430 466 assert len(args) == 1 431 467 ctxt = args[0] 432 468 if ctxt is None: 433 469 ctxt = Context(**kwargs) 470 else: 471 vars = kwargs 434 472 assert isinstance(ctxt, Context) 435 473 else: 436 474 ctxt = Context(**kwargs) 437 475 438 476 stream = self.stream 439 477 for filter_ in self.filters: 440 stream = filter_(iter(stream), ctxt )478 stream = filter_(iter(stream), ctxt, **vars) 441 479 return Stream(stream, self.serializer) 442 480 443 def _eval(self, stream, ctxt ):481 def _eval(self, stream, ctxt, **vars): 444 482 """Internal stream filter that evaluates any expressions in `START` and 445 483 `TEXT` events. 446 484 """ … … 460 498 else: 461 499 values = [] 462 500 for subkind, subdata, subpos in self._eval(substream, 463 ctxt): 501 ctxt, 502 **vars): 464 503 if subkind is TEXT: 465 504 values.append(subdata) 466 505 value = [x for x in values if x is not None] … … 470 509 yield kind, (tag, Attrs(new_attrs)), pos 471 510 472 511 elif kind is EXPR: 473 result = data.evaluate(ctxt)512 result = _eval_expr(data, ctxt, **vars) 474 513 if result is not None: 475 514 # First check for a string, otherwise the iterable test 476 515 # below succeeds, and the string will be chopped up into … … 482 521 elif hasattr(result, '__iter__'): 483 522 substream = _ensure(result) 484 523 for filter_ in filters: 485 substream = filter_(substream, ctxt )524 substream = filter_(substream, ctxt, **vars) 486 525 for event in substream: 487 526 yield event 488 527 else: … … 491 530 else: 492 531 yield kind, data, pos 493 532 494 def _exec(self, stream, ctxt ):533 def _exec(self, stream, ctxt, **vars): 495 534 """Internal stream filter that executes Python code blocks.""" 496 535 for event in stream: 497 536 if event[0] is EXEC: 498 event[1].execute(_ctxt2dict(ctxt))537 _exec_suite(event[1], ctxt, **vars) 499 538 else: 500 539 yield event 501 540 502 def _flatten(self, stream, ctxt ):541 def _flatten(self, stream, ctxt, **vars): 503 542 """Internal stream filter that expands `SUB` events in the stream.""" 504 543 for event in stream: 505 544 if event[0] is SUB: 506 545 # This event is a list of directives and a list of nested 507 546 # events to which those directives should be applied 508 547 directives, substream = event[1] 509 substream = _apply_directives(substream, ctxt, directives) 510 for event in self._flatten(substream, ctxt): 548 substream = _apply_directives(substream, directives, ctxt, 549 **vars) 550 for event in self._flatten(substream, ctxt, **vars): 511 551 yield event 512 552 else: 513 553 yield event 514 554 515 def _include(self, stream, ctxt ):555 def _include(self, stream, ctxt, **vars): 516 556 """Internal stream filter that performs inclusion of external 517 557 template files. 518 558 """ … … 523 563 href, cls, fallback = event[1] 524 564 if not isinstance(href, basestring): 525 565 parts = [] 526 for subkind, subdata, subpos in self._eval(href, ctxt): 566 for subkind, subdata, subpos in self._eval(href, ctxt, 567 **vars): 527 568 if subkind is TEXT: 528 569 parts.append(subdata) 529 570 href = u''.join([x for x in parts if x is not None]) 530 571 try: 531 572 tmpl = self.loader.load(href, relative_to=event[2][0], 532 573 cls=cls or self.__class__) 533 for event in tmpl.generate(ctxt ):574 for event in tmpl.generate(ctxt, **vars): 534 575 yield event 535 576 except TemplateNotFound: 536 577 if fallback is None: 537 578 raise 538 579 for filter_ in self.filters: 539 fallback = filter_(iter(fallback), ctxt )580 fallback = filter_(iter(fallback), ctxt, **vars) 540 581 for event in fallback: 541 582 yield event 542 583 else: -
genshi/template/tests/markup.py
611 611 </body> 612 612 </html>""", tmpl.generate().render()) 613 613 614 def test_with_in_match(self): 615 xml = ("""<html xmlns:py="http://genshi.edgewall.org/"> 616 <py:match path="body/p"> 617 <h1>${select('text()')}</h1> 618 ${select('.')} 619 </py:match> 620 <body><p py:with="foo='bar'">${foo}</p></body> 621 </html>""") 622 tmpl = MarkupTemplate(xml, filename='test.html') 623 self.assertEqual("""<html> 624 <body> 625 <h1>bar</h1> 626 <p>bar</p> 627 </body> 628 </html>""", tmpl.generate().render()) 629 614 630 def test_nested_include_matches(self): 615 631 # See ticket #157 616 632 dirname = tempfile.mkdtemp(suffix='genshi_test') -
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, **vars): 229 229 """Internal stream filter that applies any defined match templates 230 230 to the stream. 231 231 """ … … 271 271 272 272 # Consume and store all events until an end event 273 273 # corresponding to this start event is encountered 274 inner = _strip(stream)275 274 pre_match_templates = match_templates[:idx + 1] 276 275 if 'match_once' not in hints and 'not_recursive' in hints: 277 276 pre_match_templates.pop() 278 inner = self._match(inner, ctxt, pre_match_templates) 279 content = list(self._include(chain([event], inner, tail), 280 ctxt)) 277 inner = _strip(stream) 278 if pre_match_templates: 279 inner = self._match(inner, ctxt, pre_match_templates) 280 content = self._include(chain([event], inner, tail), ctxt) 281 if 'not_buffered' not in hints: 282 content = list(content) 281 283 282 for test in [mt[0] for mt in match_templates]: 283 test(tail[0], namespaces, ctxt, updateonly=True) 284 if tail: 285 for test in [mt[0] for mt in match_templates]: 286 test(tail[0], namespaces, ctxt, updateonly=True) 284 287 285 288 # Make the select() function available in the body of the 286 289 # match template 287 290 def select(path): 288 291 return Stream(content).select(path, namespaces, ctxt) 289 ctxt.push(dict(select=select))292 vars = dict(select=select) 290 293 291 294 # Recursively process the output 292 template = _apply_directives(template, ctxt, directives) 293 remaining = match_templates 294 for event in self._match(self._exec( 295 self._eval(self._flatten(template, ctxt), 296 ctxt), ctxt), ctxt, match_templates[idx + 1:]): 295 template = _apply_directives(template, directives, ctxt, 296 **vars) 297 for event in self._match( 298 self._exec( 299 self._eval( 300 self._flatten(template, ctxt, **vars), 301 ctxt, **vars), 302 ctxt, **vars), 303 ctxt, match_templates[idx + 1:], **vars): 297 304 yield event 298 305 299 ctxt.pop()300 306 break 301 307 302 308 else: # no matches -
genshi/template/directives.py
22 22 from genshi.core import QName, Stream 23 23 from genshi.path import Path 24 24 from genshi.template.base import TemplateRuntimeError, TemplateSyntaxError, \ 25 EXPR, _apply_directives, _ctxt2dict 25 EXPR, _apply_directives, _eval_expr, \ 26 _exec_suite 26 27 from genshi.template.eval import Expression, Suite, ExpressionASTTransformer, \ 27 28 _parse 28 29 … … 88 89 return cls(value, template, namespaces, *pos[1:]), stream 89 90 attach = classmethod(attach) 90 91 91 def __call__(self, stream, ctxt, directives):92 def __call__(self, stream, directives, ctxt, **vars): 92 93 """Apply the directive to the given stream. 93 94 94 95 :param stream: the event stream 95 :param ctxt: the context data96 96 :param directives: a list of the remaining directives that should 97 97 process the stream 98 :param ctxt: the context data 99 :param vars: additional variables that should be made available when 100 Python code is executed 98 101 """ 99 102 raise NotImplementedError 100 103 … … 167 170 """ 168 171 __slots__ = [] 169 172 170 def __call__(self, stream, ctxt, directives):173 def __call__(self, stream, directives, ctxt, **vars): 171 174 def _generate(): 172 175 kind, (tag, attrib), pos = stream.next() 173 attrs = self.expr.evaluate(ctxt)176 attrs = _eval_expr(self.expr, ctxt, **vars) 174 177 if attrs: 175 178 if isinstance(attrs, Stream): 176 179 try: … … 186 189 for event in stream: 187 190 yield event 188 191 189 return _apply_directives(_generate(), ctxt, directives)192 return _apply_directives(_generate(), directives, ctxt, **vars) 190 193 191 194 192 195 class ContentDirective(Directive): … … 291 294 namespaces, pos) 292 295 attach = classmethod(attach) 293 296 294 def __call__(self, stream, ctxt, directives):297 def __call__(self, stream, directives, ctxt, **vars): 295 298 stream = list(stream) 296 299 297 300 def function(*args, **kwargs): … … 304 307 if name in kwargs: 305 308 val = kwargs.pop(name) 306 309 else: 307 val = self.defaults.get(name).evaluate(ctxt)310 val = _eval_expr(self.defaults.get(name), ctxt, **vars) 308 311 scope[name] = val 309 312 if not self.star_args is None: 310 313 scope[self.star_args] = args 311 314 if not self.dstar_args is None: 312 315 scope[self.dstar_args] = kwargs 313 316 ctxt.push(scope) 314 for event in _apply_directives(stream, ctxt, directives):317 for event in _apply_directives(stream, directives, ctxt, **vars): 315 318 yield event 316 319 ctxt.pop() 317 320 try: … … 364 367 namespaces, pos) 365 368 attach = classmethod(attach) 366 369 367 def __call__(self, stream, ctxt, directives):368 iterable = self.expr.evaluate(ctxt)370 def __call__(self, stream, directives, ctxt, **vars): 371 iterable = _eval_expr(self.expr, ctxt, **vars) 369 372 if iterable is None: 370 373 return 371 374 … … 375 378 for item in iterable: 376 379 assign(scope, item) 377 380 ctxt.push(scope) 378 for event in _apply_directives(stream, ctxt, directives):381 for event in _apply_directives(stream, directives, ctxt, **vars): 379 382 yield event 380 383 ctxt.pop() 381 384 … … 405 408 namespaces, pos) 406 409 attach = classmethod(attach) 407 410 408 def __call__(self, stream, ctxt, directives): 409 if self.expr.evaluate(ctxt): 410 return _apply_directives(stream, ctxt, directives) 411 def __call__(self, stream, directives, ctxt, **vars): 412 value = _eval_expr(self.expr, ctxt, **vars) 413 if value: 414 return _apply_directives(stream, directives, ctxt, **vars) 411 415 return [] 412 416 413 417 … … 440 444 def attach(cls, template, stream, value, namespaces, pos): 441 445 hints = [] 442 446 if type(value) is dict: 447 if value.get('buffer', '').lower() == 'false': 448 hints.append('not_buffered') 443 449 if value.get('once', '').lower() == 'true': 444 450 hints.append('match_once') 445 451 if value.get('recursive', '').lower() == 'false': … … 449 455 stream 450 456 attach = classmethod(attach) 451 457 452 def __call__(self, stream, ctxt, directives):458 def __call__(self, stream, directives, ctxt, **vars): 453 459 ctxt._match_templates.append((self.path.test(ignore_context=True), 454 460 self.path, list(stream), self.hints, 455 461 self.namespaces, directives)) … … 531 537 """ 532 538 __slots__ = [] 533 539 534 def __call__(self, stream, ctxt, directives):540 def __call__(self, stream, directives, ctxt, **vars): 535 541 def _generate(): 536 if self.expr.evaluate(ctxt):542 if _eval_expr(self.expr, ctxt, **vars): 537 543 stream.next() # skip start tag 538 544 previous = stream.next() 539 545 for event in stream: … … 542 548 else: 543 549 for event in stream: 544 550 yield event 545 return _apply_directives(_generate(), ctxt, directives)551 return _apply_directives(_generate(), directives, ctxt, **vars) 546 552 547 553 def attach(cls, template, stream, value, namespaces, pos): 548 554 if not value: … … 600 606 namespaces, pos) 601 607 attach = classmethod(attach) 602 608 603 def __call__(self, stream, ctxt, directives):609 def __call__(self, stream, directives, ctxt, **vars): 604 610 info = [False, bool(self.expr), None] 605 611 if self.expr: 606 info[2] = self.expr.evaluate(ctxt)612 info[2] = _eval_expr(self.expr, ctxt, **vars) 607 613 ctxt._choice_stack.append(info) 608 for event in _apply_directives(stream, ctxt, directives):614 for event in _apply_directives(stream, directives, ctxt, **vars): 609 615 yield event 610 616 ctxt._choice_stack.pop() 611 617 … … 629 635 namespaces, pos) 630 636 attach = classmethod(attach) 631 637 632 def __call__(self, stream, ctxt, directives):638 def __call__(self, stream, directives, ctxt, **vars): 633 639 info = ctxt._choice_stack and ctxt._choice_stack[-1] 634 640 if not info: 635 641 raise TemplateRuntimeError('"when" directives can only be used ' … … 644 650 if info[1]: 645 651 value = info[2] 646 652 if self.expr: 647 matched = value == self.expr.evaluate(ctxt)653 matched = value == _eval_expr(self.expr, ctxt, **vars) 648 654 else: 649 655 matched = bool(value) 650 656 else: 651 matched = bool( self.expr.evaluate(ctxt))657 matched = bool(_eval_expr(self.expr, ctxt, **vars)) 652 658 info[0] = matched 653 659 if not matched: 654 660 return [] 655 661 656 return _apply_directives(stream, ctxt, directives)662 return _apply_directives(stream, directives, ctxt, **vars) 657 663 658 664 659 665 class OtherwiseDirective(Directive): … … 668 674 Directive.__init__(self, None, template, namespaces, lineno, offset) 669 675 self.filename = template.filepath 670 676 671 def __call__(self, stream, ctxt, directives):677 def __call__(self, stream, directives, ctxt, **vars): 672 678 info = ctxt._choice_stack and ctxt._choice_stack[-1] 673 679 if not info: 674 680 raise TemplateRuntimeError('an "otherwise" directive can only be ' … … 678 684 return [] 679 685 info[0] = True 680 686 681 return _apply_directives(stream, ctxt, directives)687 return _apply_directives(stream, directives, ctxt, **vars) 682 688 683 689 684 690 class WithDirective(Directive): … … 722 728 namespaces, pos) 723 729 attach = classmethod(attach) 724 730 725 def __call__(self, stream, ctxt, directives): 726 frame = {} 727 ctxt.push(frame) 728 self.suite.execute(_ctxt2dict(ctxt)) 729 for event in _apply_directives(stream, ctxt, directives): 731 def __call__(self, stream, directives, ctxt, **vars): 732 ctxt.push({}) 733 _exec_suite(self.suite, ctxt, **vars) 734 for event in _apply_directives(stream, directives, ctxt, **vars): 730 735 yield event 731 736 ctxt.pop() 732 737 -
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']
