Edgewall Software
Version 9 (modified by mkurczych, 6 years ago)

--

Google Summer of Code 2008

The plan

  • Reimplement XPath
  • Add some kind of pre-rendering of parts that don't use variables
  • Try compiling templates to Python bytecode
  • Try adding some minor optimizations

What has been done

XPath reimplementation

Current XPath implementation in Genshi is rather buggy (for example http://genshi.edgewall.org/ticket/185). I've rewritten it. Implemented algorithm works in O(qn) time, where q is length of XPath expression, n is number of stream events and O(qh) memory complexity, where h is height of document XML tree. It computes for every node which places of XPath expression does it match. O(qn) is pessimistic complexity, I think algorithm will work like O(n) in most cases (the worst case is when nearly every node matches to nearly every place of expression, which is quite rare).

Still need to add some tests covering more situations.

Simple pre-rendering

py:optimize directive was introduced. It marks subtree of this tag as optimizable - which means that rendering code inside can be omited and instead replaced by inserting cached one. In particular it cannot have any Python code that has side effects and cannot depend on variables.

Fragment of stream will be replaced by one event saying it represents this code. It will not be unpacked untill it is needed. Following cases "need" unpacking of fragment:

  • matching tag inside optimized fragment; it also applies to first tag in fragment so instead of
    <py:match path="foo">                                                     
        <bar>                                                                 
            ${select('.')}                                                    
        </bar>                                                                
    </py:match>                                                               
    <foo py:optimize="">                                                      
        <p>Some text inside with <i>tags</i> which processing could be costful,
        but <b>could be avoided</b> by caching.</p>                            
        <p>Next paragraph.</p>                                                 
    </foo>                                                                     
    

one should use

<py:match path="foo">                                                      
    <bar>                                                                  
        ${select('.')}                                                     
    </bar>                                                                 
</py:match>                                                                
<foo>                                                                      
    <py:optimize vars="">                                                  
    <p>Some text inside with <i>tags</i> which processing could be costful,
    but <b>could be avoided</b> by caching.</p>
    <p>Next paragraph.</p>
    </py:optimize>
</foo>
  • using Transformer filter; yeah it's really cool, but too cool to be cached - it is able to do nearly anything to the stream
  • using form HTMLFormFiller; but here it's better than in Transformer case, because if filter sees there were no forms in fragment it returns just one event for fragment - does not return unpacked version

Using filters:

  • When filtering stream with filters defined in genshi.output filter object you have to use the same filter object (don't construct it every time), if you don't optimization engine won't treat result fragment as the same as generated with other filter object
  • HTMLFormFiller can be different object

Currently working at:

Template pre-processing

  • make py:optimize take argument -- expression that calculates some "digest" of context variables which this fragment depends on; it can be any hashable python object with assumption, that if results are the same, then the rendered fragments will be the same
    <div py:optimize="var%6">
        <py:choice ="var%3">
            <p py:when="0">It is divisible by three!</p>
            <p py:default="">It's remainder when dividing by three is
            ${spell(var%3)}.</p>
        </py:choice>
        <p py:if="var%2 == 0">It is odd.</p>
        <p py:if="var%2 == 1">It is even.</p>
    </div>