Edgewall Software

Ticket #190: match_unbuffered6.diff

File match_unbuffered6.diff, 20.2 kB (added by cmlenz, 7 months ago)

More improvements

  • 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, **vars): 
    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 vars: 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, **vars) 
    267269    return stream 
    268270 
     271def _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 
    269286 
     287def _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 
    270305class TemplateMeta(type): 
    271306    """Meta class for templates.""" 
    272307 
     
    426461        :return: a markup event stream representing the result of applying 
    427462                 the template to the context data. 
    428463        """ 
     464        vars = {} 
    429465        if args: 
    430466            assert len(args) == 1 
    431467            ctxt = args[0] 
    432468            if ctxt is None: 
    433469                ctxt = Context(**kwargs) 
     470            else: 
     471                vars = kwargs 
    434472            assert isinstance(ctxt, Context) 
    435473        else: 
    436474            ctxt = Context(**kwargs) 
    437475 
    438476        stream = self.stream 
    439477        for filter_ in self.filters: 
    440             stream = filter_(iter(stream), ctxt) 
     478            stream = filter_(iter(stream), ctxt, **vars) 
    441479        return Stream(stream, self.serializer) 
    442480 
    443     def _eval(self, stream, ctxt): 
     481    def _eval(self, stream, ctxt, **vars): 
    444482        """Internal stream filter that evaluates any expressions in `START` and 
    445483        `TEXT` events. 
    446484        """ 
     
    460498                    else: 
    461499                        values = [] 
    462500                        for subkind, subdata, subpos in self._eval(substream, 
    463                                                                    ctxt): 
     501                                                                   ctxt, 
     502                                                                   **vars): 
    464503                            if subkind is TEXT: 
    465504                                values.append(subdata) 
    466505                        value = [x for x in values if x is not None] 
     
    470509                yield kind, (tag, Attrs(new_attrs)), pos 
    471510 
    472511            elif kind is EXPR: 
    473                 result = data.evaluate(ctxt) 
     512                result = _eval_expr(data, ctxt, **vars) 
    474513                if result is not None: 
    475514                    # First check for a string, otherwise the iterable test 
    476515                    # below succeeds, and the string will be chopped up into 
     
    482521                    elif hasattr(result, '__iter__'): 
    483522                        substream = _ensure(result) 
    484523                        for filter_ in filters: 
    485                             substream = filter_(substream, ctxt) 
     524                            substream = filter_(substream, ctxt, **vars) 
    486525                        for event in substream: 
    487526                            yield event 
    488527                    else: 
     
    491530            else: 
    492531                yield kind, data, pos 
    493532 
    494     def _exec(self, stream, ctxt): 
     533    def _exec(self, stream, ctxt, **vars): 
    495534        """Internal stream filter that executes Python code blocks.""" 
    496535        for event in stream: 
    497536            if event[0] is EXEC: 
    498                 event[1].execute(_ctxt2dict(ctxt)) 
     537                _exec_suite(event[1], ctxt, **vars) 
    499538            else: 
    500539                yield event 
    501540 
    502     def _flatten(self, stream, ctxt): 
     541    def _flatten(self, stream, ctxt, **vars): 
    503542        """Internal stream filter that expands `SUB` events in the stream.""" 
    504543        for event in stream: 
    505544            if event[0] is SUB: 
    506545                # This event is a list of directives and a list of nested 
    507546                # events to which those directives should be applied 
    508547                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): 
    511551                    yield event 
    512552            else: 
    513553                yield event 
    514554 
    515     def _include(self, stream, ctxt): 
     555    def _include(self, stream, ctxt, **vars): 
    516556        """Internal stream filter that performs inclusion of external 
    517557        template files. 
    518558        """ 
     
    523563                href, cls, fallback = event[1] 
    524564                if not isinstance(href, basestring): 
    525565                    parts = [] 
    526                     for subkind, subdata, subpos in self._eval(href, ctxt): 
     566                    for subkind, subdata, subpos in self._eval(href, ctxt, 
     567                                                               **vars): 
    527568                        if subkind is TEXT: 
    528569                            parts.append(subdata) 
    529570                    href = u''.join([x for x in parts if x is not None]) 
    530571                try: 
    531572                    tmpl = self.loader.load(href, relative_to=event[2][0], 
    532573                                            cls=cls or self.__class__) 
    533                     for event in tmpl.generate(ctxt): 
     574                    for event in tmpl.generate(ctxt, **vars): 
    534575                        yield event 
    535576                except TemplateNotFound: 
    536577                    if fallback is None: 
    537578                        raise 
    538579                    for filter_ in self.filters: 
    539                         fallback = filter_(iter(fallback), ctxt) 
     580                        fallback = filter_(iter(fallback), ctxt, **vars) 
    540581                    for event in fallback: 
    541582                        yield event 
    542583            else: 
  • genshi/template/tests/markup.py

     
    611611          </body> 
    612612        </html>""", tmpl.generate().render()) 
    613613 
     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 
    614630    def test_nested_include_matches(self): 
    615631        # See ticket #157 
    616632        dirname = tempfile.mkdtemp(suffix='genshi_test') 
  • 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, **vars): 
    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                    vars = 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                                                 **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): 
    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, **vars): 
    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 
     99        :param vars: additional variables that should be made available when 
     100                     Python code is executed 
    98101        """ 
    99102        raise NotImplementedError 
    100103 
     
    167170    """ 
    168171    __slots__ = [] 
    169172 
    170     def __call__(self, stream, ctxt, directives): 
     173    def __call__(self, stream, directives, ctxt, **vars): 
    171174        def _generate(): 
    172175            kind, (tag, attrib), pos  = stream.next() 
    173             attrs = self.expr.evaluate(ctxt) 
     176            attrs = _eval_expr(self.expr, ctxt, **vars) 
    174177            if attrs: 
    175178                if isinstance(attrs, Stream): 
    176179                    try: 
     
    186189            for event in stream: 
    187190                yield event 
    188191 
    189         return _apply_directives(_generate(), ctxt, directives) 
     192        return _apply_directives(_generate(), directives, ctxt, **vars) 
    190193 
    191194 
    192195class ContentDirective(Directive): 
     
    291294                                               namespaces, pos) 
    292295    attach = classmethod(attach) 
    293296 
    294     def __call__(self, stream, ctxt, directives): 
     297    def __call__(self, stream, directives, ctxt, **vars): 
    295298        stream = list(stream) 
    296299 
    297300        def function(*args, **kwargs): 
     
    304307                    if name in kwargs: 
    305308                        val = kwargs.pop(name) 
    306309                    else: 
    307                         val = self.defaults.get(name).evaluate(ctxt) 
     310                        val = _eval_expr(self.defaults.get(name), ctxt, **vars) 
    308311                    scope[name] = val 
    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, **vars): 
    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, **vars): 
     371        iterable = _eval_expr(self.expr, ctxt, **vars) 
    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, **vars): 
    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, **vars): 
     412        value = _eval_expr(self.expr, ctxt, **vars) 
     413        if value: 
     414            return _apply_directives(stream, directives, ctxt, **vars) 
    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, **vars): 
    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, **vars): 
    535541        def _generate(): 
    536             if self.expr.evaluate(ctxt): 
     542            if _eval_expr(self.expr, ctxt, **vars): 
    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, **vars) 
    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, **vars): 
    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, **vars) 
    607613        ctxt._choice_stack.append(info) 
    608         for event in _apply_directives(stream, ctxt, directives): 
     614        for event in _apply_directives(stream, directives, ctxt, **vars): 
    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, **vars): 
    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, **vars) 
    648654            else: 
    649655                matched = bool(value) 
    650656        else: 
    651             matched = bool(self.expr.evaluate(ctxt)) 
     657            matched = bool(_eval_expr(self.expr, ctxt, **vars)) 
    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, **vars) 
    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, **vars): 
    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, **vars) 
    682688 
    683689 
    684690class WithDirective(Directive): 
     
    722728                                                namespaces, pos) 
    723729    attach = classmethod(attach) 
    724730 
    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): 
    730735            yield event 
    731736        ctxt.pop() 
    732737 
  • 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']