Edgewall Software

Ticket #190: match_unbuffered4.diff

File match_unbuffered4.diff, 18.2 kB (added by cmlenz, 7 months ago)

Improved patch

  • 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 
     
    263263    :return: the stream with the given directives applied 
    264264    """ 
    265265    if directives: 
    266         stream = directives[0](iter(stream), ctxt, directives[1:]) 
     266        stream = directives[0](iter(stream), directives[1:], ctxt, **bind) 
    267267    return stream 
    268268 
     269def _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() 
    269277 
     278def _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 
    270291class TemplateMeta(type): 
    271292    """Meta class for templates.""" 
    272293 
     
    426447        :return: a markup event stream representing the result of applying 
    427448                 the template to the context data. 
    428449        """ 
     450        bind = {} 
    429451        if args: 
    430452            assert len(args) == 1 
    431453            ctxt = args[0] 
    432454            if ctxt is None: 
    433455                ctxt = Context(**kwargs) 
     456            else: 
     457                bind = kwargs 
    434458            assert isinstance(ctxt, Context) 
    435459        else: 
    436460            ctxt = Context(**kwargs) 
    437461 
    438462        stream = self.stream 
    439463        for filter_ in self.filters: 
    440             stream = filter_(iter(stream), ctxt) 
     464            stream = filter_(iter(stream), ctxt, **bind) 
    441465        return Stream(stream, self.serializer) 
    442466 
    443     def _eval(self, stream, ctxt): 
     467    def _eval(self, stream, ctxt, **bind): 
    444468        """Internal stream filter that evaluates any expressions in `START` and 
    445469        `TEXT` events. 
    446470        """ 
     
    460484                    else: 
    461485                        values = [] 
    462486                        for subkind, subdata, subpos in self._eval(substream, 
    463                                                                    ctxt): 
     487                                                                   ctxt, 
     488                                                                   **bind): 
    464489                            if subkind is TEXT: 
    465490                                values.append(subdata) 
    466491                        value = [x for x in values if x is not None] 
     
    470495                yield kind, (tag, Attrs(new_attrs)), pos 
    471496 
    472497            elif kind is EXPR: 
    473                 result = data.evaluate(ctxt) 
     498                result = _eval_expr(data, ctxt, **bind) 
    474499                if result is not None: 
    475500                    # First check for a string, otherwise the iterable test 
    476501                    # below succeeds, and the string will be chopped up into 
     
    482507                    elif hasattr(result, '__iter__'): 
    483508                        substream = _ensure(result) 
    484509                        for filter_ in filters: 
    485                             substream = filter_(substream, ctxt) 
     510                            substream = filter_(substream, ctxt, **bind) 
    486511                        for event in substream: 
    487512                            yield event 
    488513                    else: 
     
    491516            else: 
    492517                yield kind, data, pos 
    493518 
    494     def _exec(self, stream, ctxt): 
     519    def _exec(self, stream, ctxt, **bind): 
    495520        """Internal stream filter that executes Python code blocks.""" 
    496521        for event in stream: 
    497522            if event[0] is EXEC: 
    498                 event[1].execute(_ctxt2dict(ctxt)) 
     523                _exec_suite(event[1], ctxt, **bind) 
    499524            else: 
    500525                yield event 
    501526 
    502     def _flatten(self, stream, ctxt): 
     527    def _flatten(self, stream, ctxt, **bind): 
    503528        """Internal stream filter that expands `SUB` events in the stream.""" 
    504529        for event in stream: 
    505530            if event[0] is SUB: 
    506531                # This event is a list of directives and a list of nested 
    507532                # events to which those directives should be applied 
    508533                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): 
    511537                    yield event 
    512538            else: 
    513539                yield event 
    514540 
    515     def _include(self, stream, ctxt): 
     541    def _include(self, stream, ctxt, **bind): 
    516542        """Internal stream filter that performs inclusion of external 
    517543        template files. 
    518544        """ 
     
    523549                href, cls, fallback = event[1] 
    524550                if not isinstance(href, basestring): 
    525551                    parts = [] 
    526                     for subkind, subdata, subpos in self._eval(href, ctxt): 
     552                    for subkind, subdata, subpos in self._eval(href, ctxt, 
     553                                                               **bind): 
    527554                        if subkind is TEXT: 
    528555                            parts.append(subdata) 
    529556                    href = u''.join([x for x in parts if x is not None]) 
    530557                try: 
    531558                    tmpl = self.loader.load(href, relative_to=event[2][0], 
    532559                                            cls=cls or self.__class__) 
    533                     for event in tmpl.generate(ctxt): 
     560                    for event in tmpl.generate(ctxt, **bind): 
    534561                        yield event 
    535562                except TemplateNotFound: 
    536563                    if fallback is None: 
    537564                        raise 
    538565                    for filter_ in self.filters: 
    539                         fallback = filter_(iter(fallback), ctxt) 
     566                        fallback = filter_(iter(fallback), ctxt, **bind) 
    540567                    for event in fallback: 
    541568                        yield event 
    542569            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 = 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) 
    281282 
    282283                    for test in [mt[0] for mt in match_templates]: 
    283284                        test(tail[0], namespaces, ctxt, updateonly=True) 
     
    286287                    # match template 
    287288                    def select(path): 
    288289                        return Stream(content).select(path, namespaces, ctxt) 
    289                     ctxt.push(dict(select=select)) 
     290                    bind = dict(select=select) 
    290291 
    291292                    # 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): 
    297302                        yield event 
    298303 
    299                     ctxt.pop() 
    300304                    break 
    301305 
    302306            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): 
    298299            scope = {} 
     300            if bind: ctxt.push(bind) 
    299301            args = list(args) # make mutable 
    300302            for name in self.args: 
    301303                if args: 
     
    306308                    else: 
    307309                        val = self.defaults.get(name).evaluate(ctxt) 
    308310                    scope[name] = val 
     311            if bind: ctxt.pop() 
    309312            if not self.star_args is None: 
    310313                scope[self.star_args] = args 
    311314            if not self.dstar_args is None: 
    312315                scope[self.dstar_args] = kwargs 
    313316            ctxt.push(scope) 
    314             for event in _apply_directives(stream, ctxt, directives): 
     317            for event in _apply_directives(stream, directives, ctxt, **bind): 
    315318                yield event 
    316319            ctxt.pop() 
    317320        try: 
     
    364367                                               namespaces, pos) 
    365368    attach = classmethod(attach) 
    366369 
    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) 
    369372        if iterable is None: 
    370373            return 
    371374 
     
    375378        for item in iterable: 
    376379            assign(scope, item) 
    377380            ctxt.push(scope) 
    378             for event in _apply_directives(stream, ctxt, directives): 
     381            for event in _apply_directives(stream, directives, ctxt, **bind): 
    379382                yield event 
    380383            ctxt.pop() 
    381384 
     
    405408                                              namespaces, pos) 
    406409    attach = classmethod(attach) 
    407410 
    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) 
    411415        return [] 
    412416 
    413417 
     
    440444    def attach(cls, template, stream, value, namespaces, pos): 
    441445        hints = [] 
    442446        if type(value) is dict: 
     447            if value.get('buffer', '').lower() == 'false': 
     448                hints.append('not_buffered') 
    443449            if value.get('once', '').lower() == 'true': 
    444450                hints.append('match_once') 
    445451            if value.get('recursive', '').lower() == 'false': 
     
    449455               stream 
    450456    attach = classmethod(attach) 
    451457 
    452     def __call__(self, stream, ctxt, directives): 
     458    def __call__(self, stream, directives, ctxt, **bind): 
    453459        ctxt._match_templates.append((self.path.test(ignore_context=True), 
    454460                                      self.path, list(stream), self.hints, 
    455461                                      self.namespaces, directives)) 
     
    531537    """ 
    532538    __slots__ = [] 
    533539 
    534     def __call__(self, stream, ctxt, directives): 
     540    def __call__(self, stream, directives, ctxt, **bind): 
    535541        def _generate(): 
    536             if self.expr.evaluate(ctxt): 
     542            if _eval_expr(self.expr, ctxt, **bind): 
    537543                stream.next() # skip start tag 
    538544                previous = stream.next() 
    539545                for event in stream: 
     
    542548            else: 
    543549                for event in stream: 
    544550                    yield event 
    545         return _apply_directives(_generate(), ctxt, directives) 
     551        return _apply_directives(_generate(), directives, ctxt, **bind) 
    546552 
    547553    def attach(cls, template, stream, value, namespaces, pos): 
    548554        if not value: 
     
    600606                                                  namespaces, pos) 
    601607    attach = classmethod(attach) 
    602608 
    603     def __call__(self, stream, ctxt, directives): 
     609    def __call__(self, stream, directives, ctxt, **bind): 
    604610        info = [False, bool(self.expr), None] 
    605611        if self.expr: 
    606             info[2] = self.expr.evaluate(ctxt) 
     612            info[2] = _eval_expr(self.expr, ctxt, **bind) 
    607613        ctxt._choice_stack.append(info) 
    608         for event in _apply_directives(stream, ctxt, directives): 
     614        for event in _apply_directives(stream, directives, ctxt, **bind): 
    609615            yield event 
    610616        ctxt._choice_stack.pop() 
    611617 
     
    629635                                                namespaces, pos) 
    630636    attach = classmethod(attach) 
    631637 
    632     def __call__(self, stream, ctxt, directives): 
     638    def __call__(self, stream, directives, ctxt, **bind): 
    633639        info = ctxt._choice_stack and ctxt._choice_stack[-1] 
    634640        if not info: 
    635641            raise TemplateRuntimeError('"when" directives can only be used ' 
     
    644650        if info[1]: 
    645651            value = info[2] 
    646652            if self.expr: 
    647                 matched = value == self.expr.evaluate(ctxt) 
     653                matched = value == _eval_expr(self.expr, ctxt, **bind) 
    648654            else: 
    649655                matched = bool(value) 
    650656        else: 
    651             matched = bool(self.expr.evaluate(ctxt)) 
     657            matched = bool(_eval_expr(self.expr, ctxt, **bind)) 
    652658        info[0] = matched 
    653659        if not matched: 
    654660            return [] 
    655661 
    656         return _apply_directives(stream, ctxt, directives) 
     662        return _apply_directives(stream, directives, ctxt, **bind) 
    657663 
    658664 
    659665class OtherwiseDirective(Directive): 
     
    668674        Directive.__init__(self, None, template, namespaces, lineno, offset) 
    669675        self.filename = template.filepath 
    670676 
    671     def __call__(self, stream, ctxt, directives): 
     677    def __call__(self, stream, directives, ctxt, **bind): 
    672678        info = ctxt._choice_stack and ctxt._choice_stack[-1] 
    673679        if not info: 
    674680            raise TemplateRuntimeError('an "otherwise" directive can only be ' 
     
    678684            return [] 
    679685        info[0] = True 
    680686 
    681         return _apply_directives(stream, ctxt, directives) 
     687        return _apply_directives(stream, directives, ctxt, **bind) 
    682688 
    683689 
    684690class WithDirective(Directive): 
     
    722728                                                namespaces, pos) 
    723729    attach = classmethod(attach) 
    724730 
    725     def __call__(self, stream, ctxt, directives): 
     731    def __call__(self, stream, directives, ctxt, **bind): 
    726732        frame = {} 
    727733        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): 
    730736            yield event 
    731737        ctxt.pop() 
    732738 
  • 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']