noone was using these _verbose flags
[libfirm] / scripts / jinja2 / loaders.py
1 # -*- coding: utf-8 -*-
2 """
3     jinja2.loaders
4     ~~~~~~~~~~~~~~
5
6     Jinja loader classes.
7
8     :copyright: 2008 by Armin Ronacher.
9     :license: BSD, see LICENSE for more details.
10 """
11 from os import path
12 try:
13     from hashlib import sha1
14 except ImportError:
15     from sha import new as sha1
16 from jinja2.exceptions import TemplateNotFound
17 from jinja2.utils import LRUCache, open_if_exists
18
19
20 def split_template_path(template):
21     """Split a path into segments and perform a sanity check.  If it detects
22     '..' in the path it will raise a `TemplateNotFound` error.
23     """
24     pieces = []
25     for piece in template.split('/'):
26         if path.sep in piece \
27            or (path.altsep and path.altsep in piece) or \
28            piece == path.pardir:
29             raise TemplateNotFound(template)
30         elif piece and piece != '.':
31             pieces.append(piece)
32     return pieces
33
34
35 class BaseLoader(object):
36     """Baseclass for all loaders.  Subclass this and override `get_source` to
37     implement a custom loading mechanism.  The environment provides a
38     `get_template` method that calls the loader's `load` method to get the
39     :class:`Template` object.
40
41     A very basic example for a loader that looks up templates on the file
42     system could look like this::
43
44         from jinja2 import BaseLoader, TemplateNotFound
45         from os.path import join, exists, getmtime
46
47         class MyLoader(BaseLoader):
48
49             def __init__(self, path):
50                 self.path = path
51
52             def get_source(self, environment, template):
53                 path = join(self.path, template)
54                 if not exists(path):
55                     raise TemplateNotFound(template)
56                 mtime = getmtime(path)
57                 with file(path) as f:
58                     source = f.read().decode('utf-8')
59                 return source, path, lambda: mtime == getmtime(path)
60     """
61
62     def get_source(self, environment, template):
63         """Get the template source, filename and reload helper for a template.
64         It's passed the environment and template name and has to return a
65         tuple in the form ``(source, filename, uptodate)`` or raise a
66         `TemplateNotFound` error if it can't locate the template.
67
68         The source part of the returned tuple must be the source of the
69         template as unicode string or a ASCII bytestring.  The filename should
70         be the name of the file on the filesystem if it was loaded from there,
71         otherwise `None`.  The filename is used by python for the tracebacks
72         if no loader extension is used.
73
74         The last item in the tuple is the `uptodate` function.  If auto
75         reloading is enabled it's always called to check if the template
76         changed.  No arguments are passed so the function must store the
77         old state somewhere (for example in a closure).  If it returns `False`
78         the template will be reloaded.
79         """
80         raise TemplateNotFound(template)
81
82     def load(self, environment, name, globals=None):
83         """Loads a template.  This method looks up the template in the cache
84         or loads one by calling :meth:`get_source`.  Subclasses should not
85         override this method as loaders working on collections of other
86         loaders (such as :class:`PrefixLoader` or :class:`ChoiceLoader`)
87         will not call this method but `get_source` directly.
88         """
89         code = None
90         if globals is None:
91             globals = {}
92
93         # first we try to get the source for this template together
94         # with the filename and the uptodate function.
95         source, filename, uptodate = self.get_source(environment, name)
96
97         # try to load the code from the bytecode cache if there is a
98         # bytecode cache configured.
99         bcc = environment.bytecode_cache
100         if bcc is not None:
101             bucket = bcc.get_bucket(environment, name, filename, source)
102             code = bucket.code
103
104         # if we don't have code so far (not cached, no longer up to
105         # date) etc. we compile the template
106         if code is None:
107             code = environment.compile(source, name, filename)
108
109         # if the bytecode cache is available and the bucket doesn't
110         # have a code so far, we give the bucket the new code and put
111         # it back to the bytecode cache.
112         if bcc is not None and bucket.code is None:
113             bucket.code = code
114             bcc.set_bucket(bucket)
115
116         return environment.template_class.from_code(environment, code,
117                                                     globals, uptodate)
118
119
120 class FileSystemLoader(BaseLoader):
121     """Loads templates from the file system.  This loader can find templates
122     in folders on the file system and is the preferred way to load them.
123
124     The loader takes the path to the templates as string, or if multiple
125     locations are wanted a list of them which is then looked up in the
126     given order:
127
128     >>> loader = FileSystemLoader('/path/to/templates')
129     >>> loader = FileSystemLoader(['/path/to/templates', '/other/path'])
130
131     Per default the template encoding is ``'utf-8'`` which can be changed
132     by setting the `encoding` parameter to something else.
133     """
134
135     def __init__(self, searchpath, encoding='utf-8'):
136         if isinstance(searchpath, basestring):
137             searchpath = [searchpath]
138         self.searchpath = list(searchpath)
139         self.encoding = encoding
140
141     def get_source(self, environment, template):
142         pieces = split_template_path(template)
143         for searchpath in self.searchpath:
144             filename = path.join(searchpath, *pieces)
145             f = open_if_exists(filename)
146             if f is None:
147                 continue
148             try:
149                 contents = f.read().decode(self.encoding)
150             finally:
151                 f.close()
152
153             mtime = path.getmtime(filename)
154             def uptodate():
155                 try:
156                     return path.getmtime(filename) == mtime
157                 except OSError:
158                     return False
159             return contents, filename, uptodate
160         raise TemplateNotFound(template)
161
162
163 class PackageLoader(BaseLoader):
164     """Load templates from python eggs or packages.  It is constructed with
165     the name of the python package and the path to the templates in that
166     package:
167
168     >>> loader = PackageLoader('mypackage', 'views')
169
170     If the package path is not given, ``'templates'`` is assumed.
171
172     Per default the template encoding is ``'utf-8'`` which can be changed
173     by setting the `encoding` parameter to something else.  Due to the nature
174     of eggs it's only possible to reload templates if the package was loaded
175     from the file system and not a zip file.
176     """
177
178     def __init__(self, package_name, package_path='templates',
179                  encoding='utf-8'):
180         from pkg_resources import DefaultProvider, ResourceManager, \
181                                   get_provider
182         provider = get_provider(package_name)
183         self.encoding = encoding
184         self.manager = ResourceManager()
185         self.filesystem_bound = isinstance(provider, DefaultProvider)
186         self.provider = provider
187         self.package_path = package_path
188
189     def get_source(self, environment, template):
190         pieces = split_template_path(template)
191         p = '/'.join((self.package_path,) + tuple(pieces))
192         if not self.provider.has_resource(p):
193             raise TemplateNotFound(template)
194
195         filename = uptodate = None
196         if self.filesystem_bound:
197             filename = self.provider.get_resource_filename(self.manager, p)
198             mtime = path.getmtime(filename)
199             def uptodate():
200                 try:
201                     return path.getmtime(filename) == mtime
202                 except OSError:
203                     return False
204
205         source = self.provider.get_resource_string(self.manager, p)
206         return source.decode(self.encoding), filename, uptodate
207
208
209 class DictLoader(BaseLoader):
210     """Loads a template from a python dict.  It's passed a dict of unicode
211     strings bound to template names.  This loader is useful for unittesting:
212
213     >>> loader = DictLoader({'index.html': 'source here'})
214
215     Because auto reloading is rarely useful this is disabled per default.
216     """
217
218     def __init__(self, mapping):
219         self.mapping = mapping
220
221     def get_source(self, environment, template):
222         if template in self.mapping:
223             source = self.mapping[template]
224             return source, None, lambda: source != self.mapping.get(template)
225         raise TemplateNotFound(template)
226
227
228 class FunctionLoader(BaseLoader):
229     """A loader that is passed a function which does the loading.  The
230     function becomes the name of the template passed and has to return either
231     an unicode string with the template source, a tuple in the form ``(source,
232     filename, uptodatefunc)`` or `None` if the template does not exist.
233
234     >>> def load_template(name):
235     ...     if name == 'index.html'
236     ...         return '...'
237     ...
238     >>> loader = FunctionLoader(load_template)
239
240     The `uptodatefunc` is a function that is called if autoreload is enabled
241     and has to return `True` if the template is still up to date.  For more
242     details have a look at :meth:`BaseLoader.get_source` which has the same
243     return value.
244     """
245
246     def __init__(self, load_func):
247         self.load_func = load_func
248
249     def get_source(self, environment, template):
250         rv = self.load_func(template)
251         if rv is None:
252             raise TemplateNotFound(template)
253         elif isinstance(rv, basestring):
254             return rv, None, None
255         return rv
256
257
258 class PrefixLoader(BaseLoader):
259     """A loader that is passed a dict of loaders where each loader is bound
260     to a prefix.  The prefix is delimited from the template by a slash per
261     default, which can be changed by setting the `delimiter` argument to
262     something else.
263
264     >>> loader = PrefixLoader({
265     ...     'app1':     PackageLoader('mypackage.app1'),
266     ...     'app2':     PackageLoader('mypackage.app2')
267     ... })
268
269     By loading ``'app1/index.html'`` the file from the app1 package is loaded,
270     by loading ``'app2/index.html'`` the file from the second.
271     """
272
273     def __init__(self, mapping, delimiter='/'):
274         self.mapping = mapping
275         self.delimiter = delimiter
276
277     def get_source(self, environment, template):
278         try:
279             prefix, template = template.split(self.delimiter, 1)
280             loader = self.mapping[prefix]
281         except (ValueError, KeyError):
282             raise TemplateNotFound(template)
283         return loader.get_source(environment, template)
284
285
286 class ChoiceLoader(BaseLoader):
287     """This loader works like the `PrefixLoader` just that no prefix is
288     specified.  If a template could not be found by one loader the next one
289     is tried.
290
291     >>> loader = ChoiceLoader([
292     ...     FileSystemLoader('/path/to/user/templates'),
293     ...     PackageLoader('mypackage')
294     ... ])
295
296     This is useful if you want to allow users to override builtin templates
297     from a different location.
298     """
299
300     def __init__(self, loaders):
301         self.loaders = loaders
302
303     def get_source(self, environment, template):
304         for loader in self.loaders:
305             try:
306                 return loader.get_source(environment, template)
307             except TemplateNotFound:
308                 pass
309         raise TemplateNotFound(template)