Edgewall Software

Changeset 718


Ignore:
Timestamp:
Aug 23, 2007, 1:35:43 PM (16 years ago)
Author:
cmlenz
Message:

Add runtime optimization hints for match templates.

Location:
trunk
Files:
5 edited

Legend:

Unmodified
Added
Removed
  • trunk/doc/xml-templates.txt

    r614 r718  
    333333    <greeting name="Dude" />
    334334  </div>
     335
     336When used this way, the ``py:match`` directive can also be annotated with a
     337couple of optimization hints. For example, the following informs the matching
     338engine that the match should only be applied once:
     339
     340.. code-block:: genshi
     341
     342  <py:match path="body" once="true">
     343    <body py:attrs="select('@*')">
     344      <div id="header">...</div>
     345      ${select("*|text()")}
     346      <div id="footer">...</div>
     347    </body>
     348  </py:match>
     349
     350The following optimization hints are recognized:
     351
     352+---------------+-----------+-----------------------------------------------+
     353| Attribute     | Default   | Description                                   |
     354+===============+===========+===============================================+
     355| ``once``      | ``false`` | Whether the engine should stop looking for    |
     356|               |           | more matching elements after the first match. |
     357|               |           | Use this on match templates that match        |
     358|               |           | elements that can only occur once in the      |
     359|               |           | stream, such as the ``<head>`` or ``<body>``  |
     360|               |           | elements in an HTML template, or elements     |
     361|               |           | with a specific ID.                           |
     362+---------------+-----------+-----------------------------------------------+
     363| ``recursive`` | ``true``  | Whether the match template should be applied  |
     364|               |           | to its own output. Note that ``once`` implies |
     365|               |           | non-recursive behavior, so this attribute     |
     366|               |           | only needs to be set for match templates that |
     367|               |           | don't also have ``once`` set.                 |
     368+---------------+-----------+-----------------------------------------------+
     369
     370.. note:: The ``py:match`` optimization hints were added in the 0.5 release. In
     371          earlier versions, the attributes have no effect.
    335372
    336373
  • trunk/examples/bench/genshi/base.html

    r299 r718  
    77  </p>
    88
    9   <body py:match="body">
     9  <py:match path="body" once="true"><body>
    1010    <div id="header">
    1111      <h1>${title}</h1>
     
    1313    ${select('*')}
    1414    <div id="footer" />
    15   </body>
     15  </body></py:match>
    1616
    1717</html>
  • trunk/genshi/template/directives.py

    r717 r718  
    1515
    1616import compiler
     17try:
     18    frozenset
     19except NameError:
     20    from sets import ImmutableSet as frozenset
    1721
    1822from genshi.core import QName, Stream
     
    424428    </div>
    425429    """
    426     __slots__ = ['path', 'namespaces']
    427 
    428     def __init__(self, value, template, namespaces=None, lineno=-1, offset=-1):
     430    __slots__ = ['path', 'namespaces', 'hints']
     431
     432    def __init__(self, value, template, hints=None, namespaces=None,
     433                 lineno=-1, offset=-1):
    429434        Directive.__init__(self, None, template, namespaces, lineno, offset)
    430435        self.path = Path(value, template.filepath, lineno)
    431436        self.namespaces = namespaces or {}
    432 
    433     def attach(cls, template, stream, value, namespaces, pos):
     437        self.hints = hints or ()
     438
     439    def attach(cls, template, stream, value, namespaces, pos):
     440        hints = []
    434441        if type(value) is dict:
     442            if value.get('once', '').lower() == 'true':
     443                hints.append('match_once')
     444            if value.get('recursive', '').lower() == 'false':
     445                hints.append('not_recursive')
    435446            value = value.get('path')
    436         return super(MatchDirective, cls).attach(template, stream, value,
    437                                                  namespaces, pos)
     447        return cls(value, template, frozenset(hints), namespaces, *pos[1:]), \
     448               stream
    438449    attach = classmethod(attach)
    439450
    440451    def __call__(self, stream, ctxt, directives):
    441452        ctxt._match_templates.append((self.path.test(ignore_context=True),
    442                                       self.path, list(stream), self.namespaces,
    443                                       directives))
     453                                      self.path, list(stream), self.hints,
     454                                      self.namespaces, directives))
    444455        return []
    445456
  • trunk/genshi/template/markup.py

    r717 r718  
    262262                continue
    263263
    264             for idx, (test, path, template, namespaces, directives) in \
    265                     enumerate(match_templates):
     264            for idx, (test, path, template, hints, namespaces, directives) \
     265                    in enumerate(match_templates):
    266266
    267267                if test(event, namespaces, ctxt) is True:
     268                    if 'match_once' in hints:
     269                        del match_templates[idx]
     270                        idx -= 1
    268271
    269272                    # Let the remaining match templates know about the event so
     
    274277                    # Consume and store all events until an end event
    275278                    # corresponding to this start event is encountered
    276                     content = chain([event],
    277                                     self._match(_strip(stream), ctxt,
    278                                                 [match_templates[idx]]),
    279                                     tail)
    280                     content = list(self._include(content, ctxt))
     279                    inner = _strip(stream)
     280                    if 'match_once' not in hints \
     281                            and 'not_recursive' not in hints:
     282                        inner = self._match(inner, ctxt, [match_templates[idx]])
     283                    content = list(self._include(chain([event], inner, tail),
     284                                                 ctxt))
    281285
    282286                    for test in [mt[0] for mt in match_templates]:
     
    291295                    # Recursively process the output
    292296                    template = _apply_directives(template, ctxt, directives)
     297                    remaining = match_templates
     298                    if 'match_once' not in hints:
     299                        remaining = remaining[:idx] + remaining[idx + 1:]
    293300                    for event in self._match(self._eval(self._flatten(template,
    294301                                                                      ctxt),
    295                                                         ctxt), ctxt,
    296                                              match_templates[:idx] +
    297                                              match_templates[idx + 1:]):
     302                                                        ctxt), ctxt, remaining):
    298303                        yield event
    299304
  • trunk/genshi/template/tests/directives.py

    r579 r718  
    843843        </html>""", str(tmpl.generate()))
    844844
     845    def test_match_with_once_attribute(self):
     846        tmpl = MarkupTemplate("""<html xmlns:py="http://genshi.edgewall.org/">
     847          <py:match path="body" once="true"><body>
     848            <div id="wrap">
     849              ${select("*")}
     850            </div>
     851          </body></py:match>
     852          <body>
     853            <p>Foo</p>
     854          </body>
     855          <body>
     856            <p>Bar</p>
     857          </body>
     858        </html>""")
     859        self.assertEqual("""<html>
     860          <body>
     861            <div id="wrap">
     862              <p>Foo</p>
     863            </div>
     864          </body>
     865          <body>
     866            <p>Bar</p>
     867          </body>
     868        </html>""", str(tmpl.generate()))
     869
     870    def test_match_with_recursive_attribute(self):
     871        tmpl = MarkupTemplate("""<doc xmlns:py="http://genshi.edgewall.org/">
     872          <py:match path="elem" recursive="false"><elem>
     873            <div class="elem">
     874              ${select('*')}
     875            </div>
     876          </elem></py:match>
     877          <elem>
     878            <subelem>
     879              <elem/>
     880            </subelem>
     881          </elem>
     882        </doc>""")
     883        self.assertEqual("""<doc>
     884          <elem>
     885            <div class="elem">
     886              <subelem>
     887              <elem/>
     888            </subelem>
     889            </div>
     890          </elem>
     891        </doc>""", str(tmpl.generate()))
     892
    845893    # FIXME
    846894    #def test_match_after_step(self):
Note: See TracChangeset for help on using the changeset viewer.