| 15 | === Simple pre-rendering === |
| 16 | |
| 17 | py:optimize directive was introduced. |
| 18 | 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. |
| 19 | |
| 20 | 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: |
| 21 | * matching tag inside optimized fragment; it also applies to first tag in |
| 22 | fragment so instead of |
| 23 | {{{ |
| 24 | <py:match path="foo"> |
| 25 | <bar> |
| 26 | ${select('.')} |
| 27 | </bar> |
| 28 | </py:match> |
| 29 | <foo py:optimize=""> |
| 30 | <p>Some text inside with <i>tags</i> which processing could be costful, |
| 31 | but <b>could be avoided</b> by caching.</p> |
| 32 | <p>Next paragraph.</p> |
| 33 | </foo> |
| 34 | }}} |
| 35 | one should use |
| 36 | {{{ |
| 37 | <py:match path="foo"> |
| 38 | <bar> |
| 39 | ${select('.')} |
| 40 | </bar> |
| 41 | </py:match> |
| 42 | <foo> |
| 43 | <py:optimize vars=""> |
| 44 | <p>Some text inside with <i>tags</i> which processing could be costful, |
| 45 | but <b>could be avoided</b> by caching.</p> |
| 46 | <p>Next paragraph.</p> |
| 47 | </py:optimize> |
| 48 | </foo> |
| 49 | }}} |
| 50 | * using Transformer filter; yeah it's really cool, but too cool to be cached - it is able to do nearly anything to the stream |
| 51 | * 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 |
| 52 | |
| 53 | Using filters: |
| 54 | * 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 |
| 55 | * HTMLFormFiller can be different object |
| 56 | |
| 57 | |
17 | | There are many templates where much of code doesn't depend on context. It can be pre-rendered once and then only pasted as result. |
18 | | Also common situation is when there are only few possible variable values - for example only True or False. We can pre-reder parts of code for each of values and use them later. We only have to check for given fragment of template which values does it use and find recurrences of this values. |
19 | | There's also need of making filters compatible with this changes - probably somehow mark fragment of code they change. |
20 | | |
21 | | Sometimes template creator will have to think about optimizations while creating template. For example in following code: |
22 | | |
| 60 | * 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 |
| 61 | rendered fragments will be the same |
31 | | The variable here is user. Creating pre-render dict user -> pre-rendered paragraph wont't help a lot, because there will be a lot of users. But this code can be also written as: |
32 | | {{{ |
33 | | <py:with vars="a = user.authorized()"> |
34 | | <a href="/edit/1" py:if="a">[edit]</a> |
35 | | <p>1111 11 11</p> |
36 | | <a href="/edit/2" py:if="a">[edit]</a> |
37 | | <p>222 22 22 2</p> |
38 | | <a href="/edit/3" py:if="a">[edit]</a> |
39 | | <p>333 33333 33 333</p> |
40 | | </py:with> |
41 | | }}} |
42 | | Now fragment depends only on variable "a", which can be only True or False, which gives optimization possibilities. |
43 | | |
44 | | Or it should be even vars = "a = bool(user.authorized())" to avoid things like that: |
45 | | |
46 | | {{{ |
47 | | >>> class Foo(object): |
48 | | ... def __len__(self): |
49 | | ... print "Sideeffect" |
50 | | ... return 1 |
51 | | ... |
52 | | >>> f = Foo() |
53 | | >>> if f: |
54 | | ... print "Something" |
55 | | ... |
56 | | Sideeffect |
57 | | Something |
58 | | }}} |
59 | | |
60 | | Foo can also change its internal state, and return different values in different ifs, which is not good. Only when variable is pure bool (a.__class__ is bool) we are safe. :-) |