Ticket #190: match_unbuffered5.diff
| File match_unbuffered5.diff, 19.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, **bind): 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 bind: 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, **bind) 267 269 return stream 268 270 271 def _eval_expr(expr, ctxt, **bind): 272 """Evaluate the given `Expression` object. 273 274 :param expr: the expression to evaluate 275 :param ctxt: the `Context` 276 :param bind: additional variables that should be available to the 277 expression 278 :return: the result of the evaluation 279 """ 280 if bind: 281 ctxt.push(bind) 282 try: 283 return expr.evaluate(ctxt) 284 finally: 285 if bind: 286 ctxt.pop() 269 287 288 def _exec_suite(suite, ctxt, **bind): 289 """Execute the given `Suite` object. 290 291 :param suite: the code suite to execute 292 :param ctxt: the `Context` 293 :param bind: additional variables that should be available to the 294 code 295 """ 296 if bind: 297 ctxt.push(bind) 298 ctxt.push({}) 299 try: 300 suite.execute(_ctxt2dict(ctxt)) 301 finally: 302 if bind: 303 top = ctxt.pop() 304 ctxt.pop() 305 ctxt.frames[0].update(top) 306 307 270 308 class TemplateMeta(type): 271 309 """Meta class for templates.""" 272 310 … … 426 464 :return: a markup event stream representing the result of applying 427 465 the template to the context data. 428 466 """ 467 bind = {} 429 468 if args: 430 469 assert len(args) == 1 431 470 ctxt = args[0] 432 471 if ctxt is None: 433 472 ctxt = Context(**kwargs) 473 else: 474 bind = kwargs 434 475 assert isinstance(ctxt, Context) 435 476 else: 436 477 ctxt = Context(**kwargs) 437 478 438 479 stream = self.stream 439 480 for filter_ in self.filters: 440 stream = filter_(iter(stream), ctxt )481 stream = filter_(iter(stream), ctxt, **bind) 441 482 return Stream(stream, self.serializer) 442 483 443 def _eval(self, stream, ctxt ):484 def _eval(self, stream, ctxt, **bind): 444 485 """Internal stream filter that evaluates any expressions in `START` and 445 486 `TEXT` events. 446 487 """ … … 460 501 else: 461 502 values = [] 462 503 for subkind, subdata, subpos in self._eval(substream, 463 ctxt): 504 ctxt, 505 **bind): 464 506 if subkind is TEXT: 465 507 values.append(subdata) 466 508 value = [x for x in values if x is not None] … … 470 512 yield kind, (tag, Attrs(new_attrs)), pos 471 513 472 514 elif kind is EXPR: 473 result = data.evaluate(ctxt)515 result = _eval_expr(data, ctxt, **bind) 474 516 if result is not None: 475 517 # First check for a string, otherwise the iterable test 476 518 # below succeeds, and the string will be chopped up into … … 482 524 elif hasattr(result, '__iter__'): 483 525 substream = _ensure(result) 484 526 for filter_ in filters: 485 substream = filter_(substream, ctxt )527 substream = filter_(substream, ctxt, **bind) 486 528 for event in substream: 487 529 yield event 488 530 else: … … 491 533 else: 492 534 yield kind, data, pos 493 535 494 def _exec(self, stream, ctxt ):536 def _exec(self, stream, ctxt, **bind): 495 537 """Internal stream filter that executes Python code blocks.""" 496 538 for event in stream: 497 539 if event[0] is EXEC: 498 event[1].execute(_ctxt2dict(ctxt))540 _exec_suite(event[1], ctxt, **bind) 499 541 else: 500 542 yield event 501 543 502 def _flatten(self, stream, ctxt ):544 def _flatten(self, stream, ctxt, **bind): 503 545 """Internal stream filter that expands `SUB` events in the stream.""" 504 546 for event in stream: 505 547 if event[0] is SUB: 506 548 # This event is a list of directives and a list of nested 507 549 # events to which those directives should be applied 508 550 directives, substream = event[1] 509 substream = _apply_directives(substream, ctxt, directives) 510 for event in self._flatten(substream, ctxt): 551 substream = _apply_directives(substream, directives, ctxt, 552 **bind) 553 for event in self._flatten(substream, ctxt, **bind): 511 554 yield event 512 555 else: 513 556 yield event 514 557 515 def _include(self, stream, ctxt ):558 def _include(self, stream, ctxt, **bind): 516 559 """Internal stream filter that performs inclusion of external 517 560 template files. 518 561 """ … … 523 566 href, cls, fallback = event[1] 524 567 if not isinstance(href, basestring): 525 568 parts = [] 526 for subkind, subdata, subpos in self._eval(href, ctxt): 569 for subkind, subdata, subpos in self._eval(href, ctxt, 570 **bind): 527 571 if subkind is TEXT: 528 572 parts.append(subdata) 529 573 href = u''.join([x for x in parts if x is not None]) 530 574 try: 531 575 tmpl = self.loader.load(href, relative_to=event[2][0], 532 576 cls=cls or self.__class__) 533 for event in tmpl.generate(ctxt ):577 for event in tmpl.generate(ctxt, **bind): 534 578 yield event 535 579 except TemplateNotFound: 536 580 if fallback is None: 537 581 raise 538 582 for filter_ in self.filters: 539 fallback = filter_(iter(fallback), ctxt )583 fallback = filter_(iter(fallback), ctxt, **bind) 540 584 for event in fallback: 541 585 yield event 542 586 else: -
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 """ … … 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 bind = 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 **bind) 297 for event in self._match( 298 self._exec( 299 self._eval( 300 self._flatten(template, ctxt, **bind), 301 ctxt, **bind), 302 ctxt, **bind), 303 ctxt, match_templates[idx + 1:], **bind): 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, **bind): 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 98 99 """ 99 100 raise NotImplementedError 100 101 … … 167 168 """ 168 169 __slots__ = [] 169 170 170 def __call__(self, stream, ctxt, directives):171 def __call__(self, stream, directives, ctxt, **bind): 171 172 def _generate(): 172 173 kind, (tag, attrib), pos = stream.next() 173 attrs = self.expr.evaluate(ctxt)174 attrs = _eval_expr(self.expr, ctxt, **bind) 174 175 if attrs: 175 176 if isinstance(attrs, Stream): 176 177 try: … … 186 187 for event in stream: 187 188 yield event 188 189 189 return _apply_directives(_generate(), ctxt, directives)190 return _apply_directives(_generate(), directives, ctxt, **bind) 190 191 191 192 192 193 class ContentDirective(Directive): … … 291 292 namespaces, pos) 292 293 attach = classmethod(attach) 293 294 294 def __call__(self, stream, ctxt, directives):295 def __call__(self, stream, directives, ctxt, **bind): 295 296 stream = list(stream) 296 297 297 298 def function(*args, **kwargs): … … 304 305 if name in kwargs: 305 306 val = kwargs.pop(name) 306 307 else: 307 val = self.defaults.get(name).evaluate(ctxt)308 val = _eval_expr(self.defaults.get(name), ctxt, **bind) 308 309 scope[name] = val 309 310 if not self.star_args is None: 310 311 scope[self.star_args] = args 311 312 if not self.dstar_args is None: 312 313 scope[self.dstar_args] = kwargs 313 314 ctxt.push(scope) 314 for event in _apply_directives(stream, ctxt, directives):315 for event in _apply_directives(stream, directives, ctxt, **bind): 315 316 yield event 316 317 ctxt.pop() 317 318 try: … … 364 365 namespaces, pos) 365 366 attach = classmethod(attach) 366 367 367 def __call__(self, stream, ctxt, directives):368 iterable = self.expr.evaluate(ctxt)368 def __call__(self, stream, directives, ctxt, **bind): 369 iterable = _eval_expr(self.expr, ctxt, **bind) 369 370 if iterable is None: 370 371 return 371 372 … … 375 376 for item in iterable: 376 377 assign(scope, item) 377 378 ctxt.push(scope) 378 for event in _apply_directives(stream, ctxt, directives):379 for event in _apply_directives(stream, directives, ctxt, **bind): 379 380 yield event 380 381 ctxt.pop() 381 382 … … 405 406 namespaces, pos) 406 407 attach = classmethod(attach) 407 408 408 def __call__(self, stream, ctxt, directives): 409 if self.expr.evaluate(ctxt): 410 return _apply_directives(stream, ctxt, directives) 409 def __call__(self, stream, directives, ctxt, **bind): 410 value = _eval_expr(self.expr, ctxt, **bind) 411 if value: 412 return _apply_directives(stream, directives, ctxt, **bind) 411 413 return [] 412 414 413 415 … … 440 442 def attach(cls, template, stream, value, namespaces, pos): 441 443 hints = [] 442 444 if type(value) is dict: 445 if value.get('buffer', '').lower() == 'false': 446 hints.append('not_buffered') 443 447 if value.get('once', '').lower() == 'true': 444 448 hints.append('match_once') 445 449 if value.get('recursive', '').lower() == 'false': … … 449 453 stream 450 454 attach = classmethod(attach) 451 455 452 def __call__(self, stream, ctxt, directives):456 def __call__(self, stream, directives, ctxt, **bind): 453 457 ctxt._match_templates.append((self.path.test(ignore_context=True), 454 458 self.path, list(stream), self.hints, 455 459 self.namespaces, directives)) … … 531 535 """ 532 536 __slots__ = [] 533 537 534 def __call__(self, stream, ctxt, directives):538 def __call__(self, stream, directives, ctxt, **bind): 535 539 def _generate(): 536 if self.expr.evaluate(ctxt):540 if _eval_expr(self.expr, ctxt, **bind): 537 541 stream.next() # skip start tag 538 542 previous = stream.next() 539 543 for event in stream: … … 542 546 else: 543 547 for event in stream: 544 548 yield event 545 return _apply_directives(_generate(), ctxt, directives)549 return _apply_directives(_generate(), directives, ctxt, **bind) 546 550 547 551 def attach(cls, template, stream, value, namespaces, pos): 548 552 if not value: … … 600 604 namespaces, pos) 601 605 attach = classmethod(attach) 602 606 603 def __call__(self, stream, ctxt, directives):607 def __call__(self, stream, directives, ctxt, **bind): 604 608 info = [False, bool(self.expr), None] 605 609 if self.expr: 606 info[2] = self.expr.evaluate(ctxt)610 info[2] = _eval_expr(self.expr, ctxt, **bind) 607 611 ctxt._choice_stack.append(info) 608 for event in _apply_directives(stream, ctxt, directives):612 for event in _apply_directives(stream, directives, ctxt, **bind): 609 613 yield event 610 614 ctxt._choice_stack.pop() 611 615 … … 629 633 namespaces, pos) 630 634 attach = classmethod(attach) 631 635 632 def __call__(self, stream, ctxt, directives):636 def __call__(self, stream, directives, ctxt, **bind): 633 637 info = ctxt._choice_stack and ctxt._choice_stack[-1] 634 638 if not info: 635 639 raise TemplateRuntimeError('"when" directives can only be used ' … … 644 648 if info[1]: 645 649 value = info[2] 646 650 if self.expr: 647 matched = value == self.expr.evaluate(ctxt)651 matched = value == _eval_expr(self.expr, ctxt, **bind) 648 652 else: 649 653 matched = bool(value) 650 654 else: 651 matched = bool( self.expr.evaluate(ctxt))655 matched = bool(_eval_expr(self.expr, ctxt, **bind)) 652 656 info[0] = matched 653 657 if not matched: 654 658 return [] 655 659 656 return _apply_directives(stream, ctxt, directives)660 return _apply_directives(stream, directives, ctxt, **bind) 657 661 658 662 659 663 class OtherwiseDirective(Directive): … … 668 672 Directive.__init__(self, None, template, namespaces, lineno, offset) 669 673 self.filename = template.filepath 670 674 671 def __call__(self, stream, ctxt, directives):675 def __call__(self, stream, directives, ctxt, **bind): 672 676 info = ctxt._choice_stack and ctxt._choice_stack[-1] 673 677 if not info: 674 678 raise TemplateRuntimeError('an "otherwise" directive can only be ' … … 678 682 return [] 679 683 info[0] = True 680 684 681 return _apply_directives(stream, ctxt, directives)685 return _apply_directives(stream, directives, ctxt, **bind) 682 686 683 687 684 688 class WithDirective(Directive): … … 722 726 namespaces, pos) 723 727 attach = classmethod(attach) 724 728 725 def __call__(self, stream, ctxt, directives):729 def __call__(self, stream, directives, ctxt, **bind): 726 730 frame = {} 727 731 ctxt.push(frame) 728 self.suite.execute(_ctxt2dict(ctxt))729 for event in _apply_directives(stream, ctxt, directives):732 _exec_suite(self.suite, ctxt, **bind) 733 for event in _apply_directives(stream, directives, ctxt, **bind): 730 734 yield event 731 735 ctxt.pop() 732 736 -
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']
