Ticket #190: match_unbuffered4.diff
| File match_unbuffered4.diff, 18.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 … … 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 def _eval_expr(expr, ctxt, **bind): 270 if bind: 271 ctxt.push(bind) 272 try: 273 return expr.evaluate(ctxt) 274 finally: 275 if bind: 276 ctxt.pop() 269 277 278 def _exec_suite(suite, ctxt, **bind): 279 if bind: 280 ctxt.push(bind) 281 ctxt.push({}) 282 try: 283 suite.execute(_ctxt2dict(ctxt)) 284 finally: 285 if bind: 286 top = ctxt.pop() 287 ctxt.pop() 288 ctxt.frames[0].update(top) 289 290 270 291 class TemplateMeta(type): 271 292 """Meta class for templates.""" 272 293 … … 426 447 :return: a markup event stream representing the result of applying 427 448 the template to the context data. 428 449 """ 450 bind = {} 429 451 if args: 430 452 assert len(args) == 1 431 453 ctxt = args[0] 432 454 if ctxt is None: 433 455 ctxt = Context(**kwargs) 456 else: 457 bind = kwargs 434 458 assert isinstance(ctxt, Context) 435 459 else: 436 460 ctxt = Context(**kwargs) 437 461 438 462 stream = self.stream 439 463 for filter_ in self.filters: 440 stream = filter_(iter(stream), ctxt )464 stream = filter_(iter(stream), ctxt, **bind) 441 465 return Stream(stream, self.serializer) 442 466 443 def _eval(self, stream, ctxt ):467 def _eval(self, stream, ctxt, **bind): 444 468 """Internal stream filter that evaluates any expressions in `START` and 445 469 `TEXT` events. 446 470 """ … … 460 484 else: 461 485 values = [] 462 486 for subkind, subdata, subpos in self._eval(substream, 463 ctxt): 487 ctxt, 488 **bind): 464 489 if subkind is TEXT: 465 490 values.append(subdata) 466 491 value = [x for x in values if x is not None] … … 470 495 yield kind, (tag, Attrs(new_attrs)), pos 471 496 472 497 elif kind is EXPR: 473 result = data.evaluate(ctxt)498 result = _eval_expr(data, ctxt, **bind) 474 499 if result is not None: 475 500 # First check for a string, otherwise the iterable test 476 501 # below succeeds, and the string will be chopped up into … … 482 507 elif hasattr(result, '__iter__'): 483 508 substream = _ensure(result) 484 509 for filter_ in filters: 485 substream = filter_(substream, ctxt )510 substream = filter_(substream, ctxt, **bind) 486 511 for event in substream: 487 512 yield event 488 513 else: … … 491 516 else: 492 517 yield kind, data, pos 493 518 494 def _exec(self, stream, ctxt ):519 def _exec(self, stream, ctxt, **bind): 495 520 """Internal stream filter that executes Python code blocks.""" 496 521 for event in stream: 497 522 if event[0] is EXEC: 498 event[1].execute(_ctxt2dict(ctxt))523 _exec_suite(event[1], ctxt, **bind) 499 524 else: 500 525 yield event 501 526 502 def _flatten(self, stream, ctxt ):527 def _flatten(self, stream, ctxt, **bind): 503 528 """Internal stream filter that expands `SUB` events in the stream.""" 504 529 for event in stream: 505 530 if event[0] is SUB: 506 531 # This event is a list of directives and a list of nested 507 532 # events to which those directives should be applied 508 533 directives, substream = event[1] 509 substream = _apply_directives(substream, ctxt, directives) 510 for event in self._flatten(substream, ctxt): 534 substream = _apply_directives(substream, directives, ctxt, 535 **bind) 536 for event in self._flatten(substream, ctxt, **bind): 511 537 yield event 512 538 else: 513 539 yield event 514 540 515 def _include(self, stream, ctxt ):541 def _include(self, stream, ctxt, **bind): 516 542 """Internal stream filter that performs inclusion of external 517 543 template files. 518 544 """ … … 523 549 href, cls, fallback = event[1] 524 550 if not isinstance(href, basestring): 525 551 parts = [] 526 for subkind, subdata, subpos in self._eval(href, ctxt): 552 for subkind, subdata, subpos in self._eval(href, ctxt, 553 **bind): 527 554 if subkind is TEXT: 528 555 parts.append(subdata) 529 556 href = u''.join([x for x in parts if x is not None]) 530 557 try: 531 558 tmpl = self.loader.load(href, relative_to=event[2][0], 532 559 cls=cls or self.__class__) 533 for event in tmpl.generate(ctxt ):560 for event in tmpl.generate(ctxt, **bind): 534 561 yield event 535 562 except TemplateNotFound: 536 563 if fallback is None: 537 564 raise 538 565 for filter_ in self.filters: 539 fallback = filter_(iter(fallback), ctxt )566 fallback = filter_(iter(fallback), ctxt, **bind) 540 567 for event in fallback: 541 568 yield event 542 569 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 = self._match(_strip(stream), ctxt, 278 pre_match_templates) 279 content = self._include(chain([event], inner, tail), ctxt) 280 if 'not_buffered' not in hints: 281 content = list(content) 281 282 282 283 for test in [mt[0] for mt in match_templates]: 283 284 test(tail[0], namespaces, ctxt, updateonly=True) … … 286 287 # match template 287 288 def select(path): 288 289 return Stream(content).select(path, namespaces, ctxt) 289 ctxt.push(dict(select=select))290 bind = dict(select=select) 290 291 291 292 # 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:]): 293 template = _apply_directives(template, directives, ctxt, 294 **bind) 295 for event in self._match( 296 self._exec( 297 self._eval( 298 self._flatten(template, ctxt, **bind), 299 ctxt, **bind), 300 ctxt, **bind), 301 ctxt, match_templates[idx + 1:], **bind): 297 302 yield event 298 303 299 ctxt.pop()300 304 break 301 305 302 306 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): 298 299 scope = {} 300 if bind: ctxt.push(bind) 299 301 args = list(args) # make mutable 300 302 for name in self.args: 301 303 if args: … … 306 308 else: 307 309 val = self.defaults.get(name).evaluate(ctxt) 308 310 scope[name] = val 311 if bind: ctxt.pop() 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, **bind): 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, **bind): 371 iterable = _eval_expr(self.expr, ctxt, **bind) 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, **bind): 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, **bind): 412 value = _eval_expr(self.expr, ctxt, **bind) 413 if value: 414 return _apply_directives(stream, directives, ctxt, **bind) 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, **bind): 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, **bind): 535 541 def _generate(): 536 if self.expr.evaluate(ctxt):542 if _eval_expr(self.expr, ctxt, **bind): 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, **bind) 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, **bind): 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, **bind) 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, **bind): 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, **bind): 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, **bind) 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, **bind)) 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, **bind) 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, **bind): 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, **bind) 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):731 def __call__(self, stream, directives, ctxt, **bind): 726 732 frame = {} 727 733 ctxt.push(frame) 728 self.suite.execute(_ctxt2dict(ctxt))729 for event in _apply_directives(stream, ctxt, directives):734 _exec_suite(self.suite, ctxt, **bind) 735 for event in _apply_directives(stream, directives, ctxt, **bind): 730 736 yield event 731 737 ctxt.pop() 732 738 -
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']
