-
-
Notifications
You must be signed in to change notification settings - Fork 1.6k
/
loaders.txt
245 lines (184 loc) · 8.48 KB
/
loaders.txt
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
================
Template Loaders
================
This part of the documentation explains how to use and write a template loader.
Builtin Loaders
===============
This list contains the builtin loaders you can use without further
modification:
[[list_of_loaders]]
Loader Baseclasses
==================
With Jinja 1.1 onwards all the loaders have (except of the uncached)
baseclasses. You can use them to mix your own caching layer in. This technique
is described below. The `BaseLoader` itself is also a loader baseclass but
because it's the baseclass of all loaders it's covered in the "Developing
Loaders" section.
[[list_of_baseloaders]]
Developing Loaders
==================
Template loaders are just normal Python classes that have to provide some
functions used to load and translate templates. Because some of the tasks
a loader has to do are redundant there are some classes that make loader
development easier.
Here the implementation of a simple loader based on the `BaseLoader` from
`jinja.loaders`:
.. sourcecode:: python
import codecs
from os.path import join
from jinja.loaders import BaseLoader
from jinja.exceptions import TemplateNotFound
class SimpleLoader(BaseLoader):
def __init__(self, path):
self.path = path
def get_source(self, environment, name, parent):
filename = join(self.path, name)
if not path.exists(filename):
raise TemplateNotFound(name)
f = codecs.open(filename, 'r', environment.template_charset)
try:
return f.read()
finally:
f.close()
The functions `load` and `parse` which are a requirement for a loader are
added automatically by the `BaseLoader`. Instead of the normal `BaseLoader`
you can use one of the other base loaders that already come with a proper
`get_source` method for further modification. Those loaders however are
new in Jinja 1.1.
CachedLoaderMixin
-----------------
Additionally to the `BaseLoader` there is a mixin class called
`CachedLoaderMixin` that implements memory and disk caching of templates.
Note that you have to give it a higher priority in the MRO than the
`BaseLoader` which means that's the first base class when inheriting from it:
.. sourcecode:: python
import codecs
from os.path import join, getmtime, exists
from jinja.loaders import BaseLoaderCachedLoaderMixin
from jinja.exceptions import TemplateNotFound
class CachedLoader(CachedLoaderMixin, BaseLoader):
def __init__(self, path):
self.path = path
CachedLoaderMixin.__init__(self,
True, # use memory caching
40, # for up to 40 templates
'/tmp', # additionally save the compiled templates in /tmp
True, # and reload cached templates automatically if changed
'foo' # optional salt used to keep templates with the same
# name in the same cache folder, but from different
# loaders. New in Jinja 1.1 and can be omitted.
)
def get_source(self, environment, name, parent):
filename = join(self.path, name)
if not path.exists(filename):
raise TemplateNotFound(name)
f = codecs.open(filename, 'r', environment.template_charset)
try:
return f.read()
finally:
f.close()
def check_source_changed(self, environment, name):
fn = join(self.path, name)
if exists(fn):
return getmtime(fn)
return -1
You don't have to provide the `check_source_changed` method. If it doesn't
exist the option `auto_reload` won't have an effect. Also note that the
`check_source_changed` method must not raise an exception if the template
does not exist but return ``-1``. The return value ``-1`` is considered
"always reload" whereas ``0`` means "do not reload". The default return
value for not existing templates should be ``-1``.
For the default base classes that come with Jinja 1.1 onwards there exist
also concrete implementations that support caching. The implementation
just mixes in the `CachedLoaderMixin`.
MemcachedLoaderMixin
--------------------
*New in Jinja 1.1*
The `MemcachedLoaderMixin` class adds support for `memcached`_ caching.
There is only one builtin loader that mixes it in: The
`MemcachedFileSystemLoader`. If you need other loaders with this mixin
you can easily subclass one of the existing base loaders. Here an example
for the `FunctionLoader`:
.. sourcecode:: python
from jinja.loaders import FunctionLoader, MemcachedLoaderMixin
class MemcachedFunctionLoader(MemcachedLoaderMixin, FunctionLoader):
def __init__(self, loader_func):
BaseFunctionLoader.__init__(self, loader_func)
MemcachedLoaderMixin.__init__(self,
True, # use memcached
60 * 60 * 24 * 7, # 7 days expiration
['127.0.0.1:11211'], # the memcached hosts
'template/' # string prefix for the cache keys
)
This mixin requires the `python-memcached`_ library.
.. _memcached: http://www.danga.com/memcached/
.. _python-memcached: http://www.tummy.com/Community/software/python-memcached/
How Mixin Classes Work
======================
The idea of the cached loader mixins is that you override the `load`
method of the other base class so that it's only called to get the data
from the loader and put it into a cache and then bypass the original `load`.
This works because mixin classes, as well as the loaders are so called "new
style classes" with a MRO (method resolution order). So it's possible to
access the parent without actually knowing the name of it.
Here as small mixin class that stores everything after loading in a
dict:
.. sourcecode:: python
class SimpleCacheMixin(object):
def __init__(self):
self.__cache = {}
def load(self, environment, name, translator):
if name in self.__cache:
return self.__cache[name]
tmpl = super(SimpleCacheMixin, self).load(environment, name,
translator)
self.__cache[name] = tmpl
return tmpl
You can then mix the class in like any other mixin class. Note that
all non public attributes **must** be prefixed with two underscores to
enable the name mangling. Otherwise the mixin class could break the
internal structure of the loader.
The ``super(SimpleCacheMixin, self)`` call returns an object that looks
up all the attributes you request in all the parent classes. The
`SimpleCacheMixin` just has the `object` parent which makes it a new
style class, but as soon as a loader is mixed in it will call the
`load` method of the loader that is the other parent of the resulting
class. Here a full example.
Combining Everything
====================
Here a full example with a custom cache mixin and a custom base loader:
.. sourcecode:: python
import codecs
from os.path import join
from jinja.loaders import BaseLoader
from jinja.exceptions import TemplateNotFound
class SimpleBaseLoader(BaseLoader):
def __init__(self, path):
self.path = path
def get_source(self, environment, name, parent):
filename = join(self.path, name)
if not path.exists(filename):
raise TemplateNotFound(name)
f = codecs.open(filename, 'r', environment.template_charset)
try:
return f.read()
finally:
f.close()
class SimpleCacheMixin(object):
def __init__(self):
self.__cache = {}
def load(self, environment, name, translator):
if name in self.__cache:
return self.__cache[name]
tmpl = super(SimpleCacheMixin, self).load(environment, name,
translator)
self.__cache[name] = tmpl
return tmpl
class SimpleLoader(SimpleBaseLoader, SimpleCacheMixin):
def __init__(self, path):
SimpleBaseLoader.__init__(self, path)
SimpleCacheMixin.__init__()
You can of course put all the functionallity into the `SimpleLoader` but then
you cannot exchange parts of it without rewriting much code. In the example
above replacing the `SimpleCacheMixin` with a `MemcachedLoaderMixin` is a
matter of 20 seconds.