updated a few filters and improved i18n
[jinja2.git] / jinja2 / environment.py
1 # -*- coding: utf-8 -*-
2 """
3     jinja2.environment
4     ~~~~~~~~~~~~~~~~~~
5
6     Provides a class that holds runtime and parsing time options.
7
8     :copyright: 2007 by Armin Ronacher.
9     :license: BSD, see LICENSE for more details.
10 """
11 from jinja2.lexer import Lexer
12 from jinja2.parser import Parser
13 from jinja2.optimizer import optimize
14 from jinja2.compiler import generate
15 from jinja2.runtime import Undefined
16 from jinja2.defaults import DEFAULT_FILTERS, DEFAULT_TESTS, DEFAULT_NAMESPACE
17
18
19 class Environment(object):
20     """The Jinja environment.
21
22     The core component of Jinja is the `Environment`. It contains
23     important shared variables like configuration, filters, tests,
24     globals and others.
25     """
26
27     #: if this environment is sandboxed.  Modifying this variable won't make
28     #: the environment sandboxed though.  For a real sandboxed environment
29     #: have a look at jinja2.sandbox
30     sandboxed = False
31
32     def __init__(self,
33                  block_start_string='{%',
34                  block_end_string='%}',
35                  variable_start_string='{{',
36                  variable_end_string='}}',
37                  comment_start_string='{#',
38                  comment_end_string='#}',
39                  line_statement_prefix=None,
40                  trim_blocks=False,
41                  optimized=True,
42                  undefined=Undefined,
43                  loader=None):
44         """Here the possible initialization parameters:
45
46         ========================= ============================================
47         `block_start_string`      the string marking the begin of a block.
48                                   this defaults to ``'{%'``.
49         `block_end_string`        the string marking the end of a block.
50                                   defaults to ``'%}'``.
51         `variable_start_string`   the string marking the begin of a print
52                                   statement. defaults to ``'{{'``.
53         `comment_start_string`    the string marking the begin of a
54                                   comment. defaults to ``'{#'``.
55         `comment_end_string`      the string marking the end of a comment.
56                                   defaults to ``'#}'``.
57         `line_statement_prefix`   If given and a string, this will be used as
58                                   prefix for line based statements.  See the
59                                   documentation for more details.
60         `trim_blocks`             If this is set to ``True`` the first newline
61                                   after a block is removed (block, not
62                                   variable tag!). Defaults to ``False``.
63         `optimized`               should the optimizer be enabled?  Default is
64                                   ``True``.
65         `undefined`               a subclass of `Undefined` that is used to
66                                   represent undefined variables.
67         `loader`                  the loader which should be used.
68         ========================= ============================================
69         """
70
71         # santity checks
72         assert issubclass(undefined, Undefined), 'undefined must be ' \
73                'a subclass of undefined because filters depend on it.'
74         assert block_start_string != variable_start_string != \
75                comment_start_string, 'block, variable and comment ' \
76                'start strings must be different'
77
78         # lexer / parser information
79         self.block_start_string = block_start_string
80         self.block_end_string = block_end_string
81         self.variable_start_string = variable_start_string
82         self.variable_end_string = variable_end_string
83         self.comment_start_string = comment_start_string
84         self.comment_end_string = comment_end_string
85         self.line_statement_prefix = line_statement_prefix
86         self.trim_blocks = trim_blocks
87         self.undefined = undefined
88         self.optimized = optimized
89
90         # defaults
91         self.filters = DEFAULT_FILTERS.copy()
92         self.tests = DEFAULT_TESTS.copy()
93         self.globals = DEFAULT_NAMESPACE.copy()
94
95         # if no finalize function/method exists we default to unicode.  The
96         # compiler check if the finalize attribute *is* unicode, if yes no
97         # finalizaion is written where it can be avoided.
98         if not hasattr(self, 'finalize'):
99             self.finalize = unicode
100
101         # set the loader provided
102         self.loader = loader
103
104         # create lexer
105         self.lexer = Lexer(self)
106
107     def subscribe(self, obj, argument):
108         """Get an item or attribute of an object."""
109         try:
110             return getattr(obj, str(argument))
111         except (AttributeError, UnicodeError):
112             try:
113                 return obj[argument]
114             except (TypeError, LookupError):
115                 return self.undefined(obj, argument)
116
117     def parse(self, source, filename=None):
118         """Parse the sourcecode and return the abstract syntax tree. This tree
119         of nodes is used by the compiler to convert the template into
120         executable source- or bytecode.
121         """
122         parser = Parser(self, source, filename)
123         return parser.parse()
124
125     def lex(self, source, filename=None):
126         """Lex the given sourcecode and return a generator that yields tokens.
127         The stream returned is not usable for Jinja but can be used if
128         Jinja templates should be processed by other tools (for example
129         syntax highlighting etc)
130
131         The tuples are returned in the form ``(lineno, token, value)``.
132         """
133         return self.lexer.tokeniter(source, filename)
134
135     def compile(self, source, filename=None, raw=False, globals=None):
136         """Compile a node or source."""
137         if isinstance(source, basestring):
138             source = self.parse(source, filename)
139         if self.optimized:
140             node = optimize(source, self, globals or {})
141         source = generate(node, self, filename)
142         print source
143         if raw:
144             return source
145         if filename is None:
146             filename = '<from_string>'
147         elif isinstance(filename, unicode):
148             filename = filename.encode('utf-8')
149         return compile(source, filename, 'exec')
150
151     def join_path(self, template, parent):
152         """Join a template with the parent.  By default all the lookups are
153         relative to the loader root, but if the paths should be relative this
154         function can be used to calculate the real filename."""
155         return template
156
157     def get_template(self, name, parent=None, globals=None):
158         """Load a template."""
159         if self.loader is None:
160             raise TypeError('no loader for this environment specified')
161         if parent is not None:
162             name = self.join_path(name, parent)
163         globals = self.make_globals(globals)
164         return self.loader.load(self, name, globals)
165
166     def from_string(self, source, filename='<string>', globals=None):
167         """Load a template from a string."""
168         globals = self.make_globals(globals)
169         return Template(self, self.compile(source, filename, globals=globals),
170                         globals)
171
172     def make_globals(self, d):
173         """Return a dict for the globals."""
174         if d is None:
175             return self.globals
176         return dict(self.globals, **d)
177
178
179 class Template(object):
180     """Represents a template."""
181
182     def __init__(self, environment, code, globals):
183         namespace = {'environment': environment}
184         exec code in namespace
185         self.environment = environment
186         self.name = namespace['filename']
187         self.root_render_func = namespace['root']
188         self.blocks = namespace['blocks']
189         self.globals = globals
190
191     def render(self, *args, **kwargs):
192         return u''.join(self.generate(*args, **kwargs))
193
194     def stream(self, *args, **kwargs):
195         return TemplateStream(self.generate(*args, **kwargs))
196
197     def generate(self, *args, **kwargs):
198         # assemble the context
199         context = dict(*args, **kwargs)
200
201         # if the environment is using the optimizer locals may never
202         # override globals as optimizations might have happened
203         # depending on values of certain globals.  This assertion goes
204         # away if the python interpreter is started with -O
205         if __debug__ and self.environment.optimized:
206             overrides = set(context) & set(self.globals)
207             if overrides:
208                 plural = len(overrides) != 1 and 's' or ''
209                 raise AssertionError('the per template variable%s %s '
210                                      'override%s global variable%s. '
211                                      'With an enabled optimizer this '
212                                      'will lead to unexpected results.' %
213                     (plural, ', '.join(overrides), plural or ' a', plural))
214         gen = self.root_render_func(dict(self.globals, **context))
215         # skip the first item which is a reference to the context
216         gen.next()
217         return gen
218
219     def __repr__(self):
220         return '<%s %r>' % (
221             self.__class__.__name__,
222             self.name
223         )
224
225
226 class TemplateStream(object):
227     """Wraps a genererator for outputing template streams."""
228
229     def __init__(self, gen):
230         self._gen = gen
231         self._next = gen.next
232         self.buffered = False
233
234     def disable_buffering(self):
235         """Disable the output buffering."""
236         self._next = self._gen.next
237         self.buffered = False
238
239     def enable_buffering(self, size=5):
240         """Enable buffering. Buffer `size` items before yielding them."""
241         if size <= 1:
242             raise ValueError('buffer size too small')
243         self.buffered = True
244
245         def buffering_next():
246             buf = []
247             c_size = 0
248             push = buf.append
249             next = self._gen.next
250
251             try:
252                 while 1:
253                     item = next()
254                     if item:
255                         push(item)
256                         c_size += 1
257                     if c_size >= size:
258                         raise StopIteration()
259             except StopIteration:
260                 if not c_size:
261                     raise
262             return u''.join(buf)
263
264         self._next = buffering_next
265
266     def __iter__(self):
267         return self
268
269     def next(self):
270         return self._next()