Genshi caching filter

7 July 2008
20:00

By cmlenz as Python

Possible genshi.filters.cache module

1 """Filter for caching parts of templates.
2
3 >>> from genshi.core import Stream
4 >>> from genshi.template import MarkupTemplate
5 >>> tmpl = MarkupTemplate('''<html xmlns:py="http://genshi.edgewall.org/"
6 ... xmlns:cache="http://genshi.edgewall.org/cache">
7 ... <ul cache:key="list">
8 ... <li py:for="item in items">Item ${item}</li>
9 ... </ul>
10 ... </html>''')
11 >>> cache = Cache()
12 >>> tmpl.filters.insert(0, cache.retrieve)
13 >>> print tmpl.generate(items=range(3)) | cache.store
14 <html>
15 <ul>
16 <li>Item 0</li><li>Item 1</li><li>Item 2</li>
17 </ul>
18 </html>
19 >>> 'list' in cache.cache
20 True
21
22 :since: version 0.5
23 """
24
25 from datetime import datetime, timedelta
26
27 from genshi.core import Attrs, Namespace, QName, StreamEventKind, START, END, \
28 START_NS, END_NS
29 from genshi.util import LRUCache
30
31 __all__ = ['Cache']
32 __docformat__ = 'restructuredtext en'
33
34
35 class Cache(object):
36
37 NAMESPACE = Namespace('http://genshi.edgewall.org/cache')
38
39 def __init__(self, capacity=20):
40 self.cache = LRUCache(capacity)
41
42 def retrieve(self, stream, ctxt=None):
43 cache_key = self.NAMESPACE['key']
44 cache_maxage = self.NAMESPACE['maxage']
45 now = datetime.utcnow()
46 skip = 0
47
48 for kind, data, pos in stream:
49
50 # skip parts that have been retrieved from the cache
51 if skip:
52 if kind is START:
53 skip += 1
54 elif kind is END:
55 skip -= 1
56 continue
57
58 if kind is START:
59 if cache_key in data[1]:
60 tag, attrs = data
61 key = attrs.get(cache_key)
62 maxage = attrs.get(cache_maxage, 9999999999)
63 entry = None
64 if key in self.cache:
65 entry = self.cache[key]
66 if not entry or entry[0] < now - timedelta(seconds=maxage):
67 if entry:
68 del self.cache[key]
69 yield kind, (tag, attrs), pos
70 else:
71 attrs -= (cache_key, cache_maxage)
72 for event in entry[1]:
73 yield event
74 skip = 1
75 else:
76 yield kind, data, pos
77
78 else:
79 yield kind, data, pos
80
81 def store(self, stream, ctxt=None):
82 cache_key = self.NAMESPACE['key']
83 cache_maxage = self.NAMESPACE['maxage']
84 ns_prefixes = []
85 now = datetime.utcnow()
86 key = None
87 entry = None
88 depth = 0
89
90 for kind, data, pos in stream:
91
92 if kind is START:
93 tag, attrs = data
94 if depth:
95 depth += 1
96 entry.append((kind, data, pos))
97 elif cache_key in data[1]:
98 key = attrs.get(cache_key)
99 attrs -= (cache_key, cache_maxage)
100 entry = [(kind, data, pos)]
101 depth = 1
102 yield kind, (tag, attrs), pos
103
104 elif kind is END and depth:
105 depth -= 1
106 entry.append((kind, data, pos))
107 if not depth:
108 self.cache[key] = (now, entry)
109 yield kind, data, pos
110
111 elif kind is START_NS and data[1] == self.NAMESPACE:
112 ns_prefixes.append(data[0])
113
114 elif kind is END_NS and data in ns_prefixes:
115 ns_prefixes.remove(data)
116
117 else:
118 if depth:
119 entry.append((kind, data, pos))
120 yield kind, data, pos
121

Download Raw Source

Comments

No comments so far.