[svn] added support for translations (unstable api)
[jinja2.git] / jinja / loaders.py
1 # -*- coding: utf-8 -*-
2 """
3     jinja.loaders
4     ~~~~~~~~~~~~~
5
6     Jinja loader classes.
7
8     :copyright: 2007 by Armin Ronacher.
9     :license: BSD, see LICENSE for more details.
10 """
11
12 import codecs
13 from os import path
14 from jinja.parser import Parser
15 from jinja.translators.python import PythonTranslator
16 from jinja.exceptions import TemplateNotFound
17
18
19 __all__ = ['FileSystemLoader']
20
21
22 def get_template_filename(searchpath, name):
23     """
24     Return the filesystem filename wanted.
25     """
26     return path.join(searchpath, path.sep.join([p for p in name.split('/')
27                      if p and p[0] != '.']))
28
29
30 class LoaderWrapper(object):
31     """
32     Wraps a loader so that it's bound to an environment.
33     """
34
35     def __init__(self, environment, loader):
36         self.environment = environment
37         self.loader = loader
38         if self.loader is None:
39             self.get_source = self.parse = self.load = self._loader_missing
40             self.available = False
41         else:
42             self.available = True
43
44     def get_source(self, name, parent=None):
45         """Retrieve the sourcecode of a template."""
46         # just ascii chars are allowed as template names
47         name = str(name)
48         return self.loader.get_source(self.environment, name, parent)
49
50     def parse(self, name, parent=None):
51         """Retreive a template and parse it."""
52         # just ascii chars are allowed as template names
53         name = str(name)
54         return self.loader.parse(self.environment, name, parent)
55
56     def load(self, name, translator=PythonTranslator):
57         """
58         Translate a template and return it. This must not necesarily
59         be a template class. The javascript translator for example
60         will just output a string with the translated code.
61         """
62         # just ascii chars are allowed as template names
63         name = str(name)
64         return self.loader.load(self.environment, name, translator)
65
66     def _loader_missing(self, *args, **kwargs):
67         """Helper method that overrides all other methods if no
68         loader is defined."""
69         raise RuntimeError('no loader defined')
70
71     def __nonzero__(self):
72         return self.loader is not None
73
74
75 class FileSystemLoader(object):
76     """
77     Loads templates from the filesystem:
78
79     .. sourcecode:: python
80
81         from jinja import Environment, FileSystemLoader
82         e = Environment(loader=FileSystemLoader('templates/'))
83
84     You can pass the following keyword arguments to the loader on
85     initialisation:
86
87     =================== =================================================
88     ``searchpath``      String with the path to the templates on the
89                         filesystem.
90     ``use_cache``       Set this to ``True`` to enable memory caching.
91                         This is usually a good idea in production mode,
92                         but disable it during development since it won't
93                         reload template changes automatically.
94                         This only works in persistent environments like
95                         FastCGI.
96     ``cache_size``      Number of template instance you want to cache.
97                         Defaults to ``40``.
98     =================== =================================================
99     """
100
101     def __init__(self, searchpath, use_cache=False, cache_size=40):
102         self.searchpath = searchpath
103         self.use_cache = use_cache
104         self.cache_size = cache_size
105         self.cache = {}
106
107     def get_source(self, environment, name, parent):
108         filename = get_template_filename(self.searchpath, name)
109         if path.exists(filename):
110             f = codecs.open(filename, 'r', environment.template_charset)
111             try:
112                 return f.read()
113             finally:
114                 f.close()
115         else:
116             raise TemplateNotFound(name)
117
118     def parse(self, environment, name, parent):
119         source = self.get_source(environment, name, parent)
120         return Parser(environment, source, name).parse()
121
122     def load(self, environment, name, translator):
123         if self.use_cache:
124             key = (name, translator)
125             if key in self.cache:
126                 return self.cache[key]
127             if len(self.cache) >= self.cache_size:
128                 self.cache.clear()
129         rv = translator.process(environment, self.parse(environment, name, None))
130         if self.use_cache:
131             self.cache[key] = rv
132         return rv