Edgewall Software

Ticket #190: match_unbuffered5.diff

File match_unbuffered5.diff, 19.2 kB (added by cmlenz, 7 months ago)

And a couple more fixes

  • genshi/template/base.py

     
    254254        """Pop the top-most scope from the stack.""" 
    255255 
    256256 
    257 def _apply_directives(stream, ctxt, directives): 
     257def _apply_directives(stream, directives, ctxt, **bind): 
    258258    """Apply the given directives to the stream. 
    259259     
    260260    :param stream: the stream the directives should be applied to 
     261    :param directives: the list of directives to apply 
    261262    :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 
    263265    :return: the stream with the given directives applied 
    264266    """ 
    265267    if directives: 
    266         stream = directives[0](iter(stream), ctxt, directives[1:]) 
     268        stream = directives[0](iter(stream), directives[1:], ctxt, **bind) 
    267269    return stream 
    268270 
     271def _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() 
    269287 
     288def _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 
    270308class TemplateMeta(type): 
    271309    """Meta class for templates.""" 
    272310 
     
    426464        :return: a markup event stream representing the result of applying 
    427465                 the template to the context data. 
    428466        """ 
     467        bind = {} 
    429468        if args: 
    430469            assert len(args) == 1 
    431470            ctxt = args[0] 
    432471            if ctxt is None: 
    433472                ctxt = Context(**kwargs) 
     473            else: 
     474                bind = kwargs 
    434475            assert isinstance(ctxt, Context) 
    435476        else: 
    436477            ctxt = Context(**kwargs) 
    437478 
    438479        stream = self.stream 
    439480        for filter_ in self.filters: 
    440             stream = filter_(iter(stream), ctxt) 
     481            stream = filter_(iter(stream), ctxt, **bind) 
    441482        return Stream(stream, self.serializer) 
    442483 
    443     def _eval(self, stream, ctxt): 
     484    def _eval(self, stream, ctxt, **bind): 
    444485        """Internal stream filter that evaluates any expressions in `START` and 
    445486        `TEXT` events. 
    446487        """ 
     
    460501                    else: 
    461502                        values = [] 
    462503                        for subkind, subdata, subpos in self._eval(substream, 
    463                                                                    ctxt): 
     504                                                                   ctxt, 
     505                                                                   **bind): 
    464506                            if subkind is TEXT: 
    465507                                values.append(subdata) 
    466508                        value = [x for x in values if x is not None] 
     
    470512                yield kind, (tag, Attrs(new_attrs)), pos 
    471513 
    472514            elif kind is EXPR: 
    473                 result = data.evaluate(ctxt) 
     515                result = _eval_expr(data, ctxt, **bind) 
    474516                if result is not None: 
    475517                    # First check for a string, otherwise the iterable test 
    476518                    # below succeeds, and the string will be chopped up into 
     
    482524                    elif hasattr(result, '__iter__'): 
    483525                        substream = _ensure(result) 
    484526                        for filter_ in filters: 
    485                             substream = filter_(substream, ctxt) 
     527                            substream = filter_(substream, ctxt, **bind) 
    486528                        for event in substream: 
    487529                            yield event 
    488530                    else: 
     
    491533            else: 
    492534                yield kind, data, pos 
    493535 
    494     def _exec(self, stream, ctxt): 
     536    def _exec(self, stream, ctxt, **bind): 
    495537        """Internal stream filter that executes Python code blocks.""" 
    496538        for event in stream: 
    497539            if event[0] is EXEC: 
    498                 event[1].execute(_ctxt2dict(ctxt)) 
     540                _exec_suite(event[1], ctxt, **bind) 
    499541            else: 
    500542                yield event 
    501543 
    502     def _flatten(self, stream, ctxt): 
     544    def _flatten(self, stream, ctxt, **bind): 
    503545        """Internal stream filter that expands `SUB` events in the stream.""" 
    504546        for event in stream: 
    505547            if event[0] is SUB: 
    506548                # This event is a list of directives and a list of nested 
    507549                # events to which those directives should be applied 
    508550                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): 
    511554                    yield event 
    512555            else: 
    513556                yield event 
    514557 
    515     def _include(self, stream, ctxt): 
     558    def _include(self, stream, ctxt, **bind): 
    516559        """Internal stream filter that performs inclusion of external 
    517560        template files. 
    518561        """ 
     
    523566                href, cls, fallback = event[1] 
    524567                if not isinstance(href, basestring): 
    525568                    parts = [] 
    526                     for subkind, subdata, subpos in self._eval(href, ctxt): 
     569                    for subkind, subdata, subpos in self._eval(href, ctxt, 
     570                                                               **bind): 
    527571                        if subkind is TEXT: 
    528572                            parts.append(subdata) 
    529573                    href = u''.join([x for x in parts if x is not None]) 
    530574                try: 
    531575                    tmpl = self.loader.load(href, relative_to=event[2][0], 
    532576                                            cls=cls or self.__class__) 
    533                     for event in tmpl.generate(ctxt): 
     577                    for event in tmpl.generate(ctxt, **bind): 
    534578                        yield event 
    535579                except TemplateNotFound: 
    536580                    if fallback is None: 
    537581                        raise 
    538582                    for filter_ in self.filters: 
    539                         fallback = filter_(iter(fallback), ctxt) 
     583                        fallback = filter_(iter(fallback), ctxt, **bind) 
    540584                    for event in fallback: 
    541585                        yield event 
    542586            else: 
  • genshi/template/markup.py

     
    225225        assert len(streams) == 1 
    226226        return streams[0] 
    227227 
    228     def _match(self, stream, ctxt, match_templates=None): 
     228    def _match(self, stream, ctxt, match_templates=None, **bind): 
    229229        """Internal stream filter that applies any defined match templates 
    230230        to the stream. 
    231231        """ 
     
    271271 
    272272                    # Consume and store all events until an end event 
    273273                    # corresponding to this start event is encountered 
    274                     inner = _strip(stream) 
    275274                    pre_match_templates = match_templates[:idx + 1] 
    276275                    if 'match_once' not in hints and 'not_recursive' in hints: 
    277276                        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) 
    281283 
    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) 
    284287 
    285288                    # Make the select() function available in the body of the 
    286289                    # match template 
    287290                    def select(path): 
    288291                        return Stream(content).select(path, namespaces, ctxt) 
    289                     ctxt.push(dict(select=select)) 
     292                    bind = dict(select=select) 
    290293 
    291294                    # 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): 
    297304                        yield event 
    298305 
    299                     ctxt.pop() 
    300306                    break 
    301307 
    302308            else: # no matches 
  • genshi/template/directives.py

     
    2222from genshi.core import QName, Stream 
    2323from genshi.path import Path 
    2424from genshi.template.base import TemplateRuntimeError, TemplateSyntaxError, \ 
    25                                  EXPR, _apply_directives, _ctxt2dict 
     25                                 EXPR, _apply_directives, _eval_expr, \ 
     26                                 _exec_suite 
    2627from genshi.template.eval import Expression, Suite, ExpressionASTTransformer, \ 
    2728                                 _parse 
    2829 
     
    8889        return cls(value, template, namespaces, *pos[1:]), stream 
    8990    attach = classmethod(attach) 
    9091 
    91     def __call__(self, stream, ctxt, directives): 
     92    def __call__(self, stream, directives, ctxt, **bind): 
    9293        """Apply the directive to the given stream. 
    9394         
    9495        :param stream: the event stream 
    95         :param ctxt: the context data 
    9696        :param directives: a list of the remaining directives that should 
    9797                           process the stream 
     98        :param ctxt: the context data 
    9899        """ 
    99100        raise NotImplementedError 
    100101 
     
    167168    """ 
    168169    __slots__ = [] 
    169170 
    170     def __call__(self, stream, ctxt, directives): 
     171    def __call__(self, stream, directives, ctxt, **bind): 
    171172        def _generate(): 
    172173            kind, (tag, attrib), pos  = stream.next() 
    173             attrs = self.expr.evaluate(ctxt) 
     174            attrs = _eval_expr(self.expr, ctxt, **bind) 
    174175            if attrs: 
    175176                if isinstance(attrs, Stream): 
    176177                    try: 
     
    186187            for event in stream: 
    187188                yield event 
    188189 
    189         return _apply_directives(_generate(), ctxt, directives) 
     190        return _apply_directives(_generate(), directives, ctxt, **bind) 
    190191 
    191192 
    192193class ContentDirective(Directive): 
     
    291292                                               namespaces, pos) 
    292293    attach = classmethod(attach) 
    293294 
    294     def __call__(self, stream, ctxt, directives): 
     295    def __call__(self, stream, directives, ctxt, **bind): 
    295296        stream = list(stream) 
    296297 
    297298        def function(*args, **kwargs): 
     
    304305                    if name in kwargs: 
    305306                        val = kwargs.pop(name) 
    306307                    else: 
    307                         val = self.defaults.get(name).evaluate(ctxt) 
     308                        val = _eval_expr(self.defaults.get(name), ctxt, **bind) 
    308309                    scope[name] = val 
    309310            if not self.star_args is None: 
    310311                scope[self.star_args] = args 
    311312            if not self.dstar_args is None: 
    312313                scope[self.dstar_args] = kwargs 
    313314            ctxt.push(scope) 
    314             for event in _apply_directives(stream, ctxt, directives): 
     315            for event in _apply_directives(stream, directives, ctxt, **bind): 
    315316                yield event 
    316317            ctxt.pop() 
    317318        try: 
     
    364365                                               namespaces, pos) 
    365366    attach = classmethod(attach) 
    366367 
    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) 
    369370        if iterable is None: 
    370371            return 
    371372 
     
    375376        for item in iterable: 
    376377            assign(scope, item) 
    377378            ctxt.push(scope) 
    378             for event in _apply_directives(stream, ctxt, directives): 
     379            for event in _apply_directives(stream, directives, ctxt, **bind): 
    379380                yield event 
    380381            ctxt.pop() 
    381382 
     
    405406                                              namespaces, pos) 
    406407    attach = classmethod(attach) 
    407408 
    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) 
    411413        return [] 
    412414 
    413415 
     
    440442    def attach(cls, template, stream, value, namespaces, pos): 
    441443        hints = [] 
    442444        if type(value) is dict: 
     445            if value.get('buffer', '').lower() == 'false': 
     446                hints.append('not_buffered') 
    443447            if value.get('once', '').lower() == 'true': 
    444448                hints.append('match_once') 
    445449            if value.get('recursive', '').lower() == 'false': 
     
    449453               stream 
    450454    attach = classmethod(attach) 
    451455 
    452     def __call__(self, stream, ctxt, directives): 
     456    def __call__(self, stream, directives, ctxt, **bind): 
    453457        ctxt._match_templates.append((self.path.test(ignore_context=True), 
    454458                                      self.path, list(stream), self.hints, 
    455459                                      self.namespaces, directives)) 
     
    531535    """ 
    532536    __slots__ = [] 
    533537 
    534     def __call__(self, stream, ctxt, directives): 
     538    def __call__(self, stream, directives, ctxt, **bind): 
    535539        def _generate(): 
    536             if self.expr.evaluate(ctxt): 
     540            if _eval_expr(self.expr, ctxt, **bind): 
    537541                stream.next() # skip start tag 
    538542                previous = stream.next() 
    539543                for event in stream: 
     
    542546            else: 
    543547                for event in stream: 
    544548                    yield event 
    545         return _apply_directives(_generate(), ctxt, directives) 
     549        return _apply_directives(_generate(), directives, ctxt, **bind) 
    546550 
    547551    def attach(cls, template, stream, value, namespaces, pos): 
    548552        if not value: 
     
    600604                                                  namespaces, pos) 
    601605    attach = classmethod(attach) 
    602606 
    603     def __call__(self, stream, ctxt, directives): 
     607    def __call__(self, stream, directives, ctxt, **bind): 
    604608        info = [False, bool(self.expr), None] 
    605609        if self.expr: 
    606             info[2] = self.expr.evaluate(ctxt) 
     610            info[2] = _eval_expr(self.expr, ctxt, **bind) 
    607611        ctxt._choice_stack.append(info) 
    608         for event in _apply_directives(stream, ctxt, directives): 
     612        for event in _apply_directives(stream, directives, ctxt, **bind): 
    609613            yield event 
    610614        ctxt._choice_stack.pop() 
    611615 
     
    629633                                                namespaces, pos) 
    630634    attach = classmethod(attach) 
    631635 
    632     def __call__(self, stream, ctxt, directives): 
     636    def __call__(self, stream, directives, ctxt, **bind): 
    633637        info = ctxt._choice_stack and ctxt._choice_stack[-1] 
    634638        if not info: 
    635639            raise TemplateRuntimeError('"when" directives can only be used ' 
     
    644648        if info[1]: 
    645649            value = info[2] 
    646650            if self.expr: 
    647                 matched = value == self.expr.evaluate(ctxt) 
     651                matched = value == _eval_expr(self.expr, ctxt, **bind) 
    648652            else: 
    649653                matched = bool(value) 
    650654        else: 
    651             matched = bool(self.expr.evaluate(ctxt)) 
     655            matched = bool(_eval_expr(self.expr, ctxt, **bind)) 
    652656        info[0] = matched 
    653657        if not matched: 
    654658            return [] 
    655659 
    656         return _apply_directives(stream, ctxt, directives) 
     660        return _apply_directives(stream, directives, ctxt, **bind) 
    657661 
    658662 
    659663class OtherwiseDirective(Directive): 
     
    668672        Directive.__init__(self, None, template, namespaces, lineno, offset) 
    669673        self.filename = template.filepath 
    670674 
    671     def __call__(self, stream, ctxt, directives): 
     675    def __call__(self, stream, directives, ctxt, **bind): 
    672676        info = ctxt._choice_stack and ctxt._choice_stack[-1] 
    673677        if not info: 
    674678            raise TemplateRuntimeError('an "otherwise" directive can only be ' 
     
    678682            return [] 
    679683        info[0] = True 
    680684 
    681         return _apply_directives(stream, ctxt, directives) 
     685        return _apply_directives(stream, directives, ctxt, **bind) 
    682686 
    683687 
    684688class WithDirective(Directive): 
     
    722726                                                namespaces, pos) 
    723727    attach = classmethod(attach) 
    724728 
    725     def __call__(self, stream, ctxt, directives): 
     729    def __call__(self, stream, directives, ctxt, **bind): 
    726730        frame = {} 
    727731        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): 
    730734            yield event 
    731735        ctxt.pop() 
    732736 
  • genshi/template/text.py

     
    3333                                 TemplateSyntaxError, EXEC, INCLUDE, SUB 
    3434from genshi.template.eval import Suite 
    3535from genshi.template.directives import * 
    36 from genshi.template.directives import Directive, _apply_directives 
     36from genshi.template.directives import Directive 
    3737from genshi.template.interpolation import interpolate 
    3838 
    3939__all__ = ['NewTextTemplate', 'OldTextTemplate', 'TextTemplate']