004ef74888c47c2a2d4b43dbdde648a9c2f30773
[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 import sys
12 from jinja2.lexer import Lexer
13 from jinja2.parser import Parser, ParserExtension
14 from jinja2.optimizer import optimize
15 from jinja2.compiler import generate
16 from jinja2.runtime import Undefined
17 from jinja2.debug import translate_exception
18 from jinja2.utils import import_string
19 from jinja2.defaults import DEFAULT_FILTERS, DEFAULT_TESTS, DEFAULT_NAMESPACE
20
21
22 class Environment(object):
23     """The Jinja environment.
24
25     The core component of Jinja is the `Environment`. It contains
26     important shared variables like configuration, filters, tests,
27     globals and others.
28     """
29
30     #: if this environment is sandboxed.  Modifying this variable won't make
31     #: the environment sandboxed though.  For a real sandboxed environment
32     #: have a look at jinja2.sandbox
33     sandboxed = False
34
35     def __init__(self,
36                  block_start_string='{%',
37                  block_end_string='%}',
38                  variable_start_string='{{',
39                  variable_end_string='}}',
40                  comment_start_string='{#',
41                  comment_end_string='#}',
42                  line_statement_prefix=None,
43                  trim_blocks=False,
44                  optimized=True,
45                  undefined=Undefined,
46                  loader=None,
47                  parser_extensions=(),
48                  finalize=unicode):
49         """Here the possible initialization parameters:
50
51         ========================= ============================================
52         `block_start_string`      the string marking the begin of a block.
53                                   this defaults to ``'{%'``.
54         `block_end_string`        the string marking the end of a block.
55                                   defaults to ``'%}'``.
56         `variable_start_string`   the string marking the begin of a print
57                                   statement. defaults to ``'{{'``.
58         `comment_start_string`    the string marking the begin of a
59                                   comment. defaults to ``'{#'``.
60         `comment_end_string`      the string marking the end of a comment.
61                                   defaults to ``'#}'``.
62         `line_statement_prefix`   If given and a string, this will be used as
63                                   prefix for line based statements.  See the
64                                   documentation for more details.
65         `trim_blocks`             If this is set to ``True`` the first newline
66                                   after a block is removed (block, not
67                                   variable tag!). Defaults to ``False``.
68         `optimized`               should the optimizer be enabled?  Default is
69                                   ``True``.
70         `undefined`               a subclass of `Undefined` that is used to
71                                   represent undefined variables.
72         `loader`                  the loader which should be used.
73         `parser_extensions`       List of parser extensions to use.
74         `finalize`                A callable that finalizes the variable.  Per
75                                   default this is `unicode`, other useful
76                                   builtin finalizers are `escape`.
77         ========================= ============================================
78         """
79
80         # santity checks
81         assert issubclass(undefined, Undefined), 'undefined must be ' \
82                'a subclass of undefined because filters depend on it.'
83         assert block_start_string != variable_start_string != \
84                comment_start_string, 'block, variable and comment ' \
85                'start strings must be different'
86
87         # lexer / parser information
88         self.block_start_string = block_start_string
89         self.block_end_string = block_end_string
90         self.variable_start_string = variable_start_string
91         self.variable_end_string = variable_end_string
92         self.comment_start_string = comment_start_string
93         self.comment_end_string = comment_end_string
94         self.line_statement_prefix = line_statement_prefix
95         self.trim_blocks = trim_blocks
96         self.parser_extensions = {}
97         for extension in parser_extensions:
98             if isinstance(extension, basestring):
99                 extension = import_string(extension)
100             self.parser_extensions[extension.tag] = extension
101
102
103         # runtime information
104         self.undefined = undefined
105         self.optimized = optimized
106         self.finalize = finalize
107
108         # defaults
109         self.filters = DEFAULT_FILTERS.copy()
110         self.tests = DEFAULT_TESTS.copy()
111         self.globals = DEFAULT_NAMESPACE.copy()
112
113         # set the loader provided
114         self.loader = loader
115
116         # create lexer
117         self.lexer = Lexer(self)
118
119     def subscribe(self, obj, argument):
120         """Get an item or attribute of an object."""
121         try:
122             return getattr(obj, str(argument))
123         except (AttributeError, UnicodeError):
124             try:
125                 return obj[argument]
126             except (TypeError, LookupError):
127                 return self.undefined(obj=obj, name=argument)
128
129     def parse(self, source, name=None):
130         """Parse the sourcecode and return the abstract syntax tree. This tree
131         of nodes is used by the compiler to convert the template into
132         executable source- or bytecode.
133         """
134         return Parser(self, source, name).parse()
135
136     def lex(self, source, name=None):
137         """Lex the given sourcecode and return a generator that yields tokens.
138         The stream returned is not usable for Jinja but can be used if
139         Jinja templates should be processed by other tools (for example
140         syntax highlighting etc)
141
142         The tuples are returned in the form ``(lineno, token, value)``.
143         """
144         return self.lexer.tokeniter(source, name)
145
146     def compile(self, source, name=None, filename=None, globals=None,
147                 raw=False):
148         """Compile a node or source.  The name is the load name of the
149         template after it was joined using `join_path` if necessary,
150         filename is the estimated filename of the template on the file
151         system.  If the template came from a database or memory this
152         can be omitted.
153         """
154         if isinstance(source, basestring):
155             source = self.parse(source, name)
156         if self.optimized:
157             node = optimize(source, self, globals or {})
158         source = generate(node, self, name, filename)
159         if raw:
160             return source
161         if filename is None:
162             filename = '<template>'
163         elif isinstance(filename, unicode):
164             filename = filename.encode('utf-8')
165         return compile(source, filename, 'exec')
166
167     def join_path(self, template, parent):
168         """Join a template with the parent.  By default all the lookups are
169         relative to the loader root, but if the paths should be relative this
170         function can be used to calculate the real filename."""
171         return template
172
173     def get_template(self, name, parent=None, globals=None):
174         """Load a template."""
175         if self.loader is None:
176             raise TypeError('no loader for this environment specified')
177         if parent is not None:
178             name = self.join_path(name, parent)
179         globals = self.make_globals(globals)
180         return self.loader.load(self, name, globals)
181
182     def from_string(self, source, globals=None):
183         """Load a template from a string."""
184         globals = self.make_globals(globals)
185         return Template(self, self.compile(source, globals=globals),
186                         globals)
187
188     def make_globals(self, d):
189         """Return a dict for the globals."""
190         if d is None:
191             return self.globals
192         return dict(self.globals, **d)
193
194
195 class Template(object):
196     """Represents a template."""
197
198     def __init__(self, environment, code, globals, uptodate=None):
199         namespace = {
200             'environment':          environment,
201             '__jinja_template__':   self
202         }
203         exec code in namespace
204         self.environment = environment
205         self.name = namespace['name']
206         self.filename = code.co_filename
207         self.root_render_func = namespace['root']
208         self.blocks = namespace['blocks']
209         self.globals = globals
210
211         # debug and loader helpers
212         self._get_debug_info = namespace['get_debug_info']
213         self._uptodate = uptodate
214
215     def render(self, *args, **kwargs):
216         """Render the template into a string."""
217         try:
218             return u''.join(self.generate(*args, **kwargs))
219         except:
220             # hide the `generate` frame
221             exc_type, exc_value, tb = sys.exc_info()
222             raise exc_type, exc_value, tb.tb_next
223
224     def stream(self, *args, **kwargs):
225         """Return a `TemplateStream` that generates the template."""
226         try:
227             return TemplateStream(self.generate(*args, **kwargs))
228         except:
229             # hide the `generate` frame
230             exc_type, exc_value, tb = sys.exc_info()
231             raise exc_type, exc_value, tb.tb_next
232
233     def generate(self, *args, **kwargs):
234         """Return a generator that generates the template."""
235         # assemble the context
236         context = dict(*args, **kwargs)
237
238         # if the environment is using the optimizer locals may never
239         # override globals as optimizations might have happened
240         # depending on values of certain globals.  This assertion goes
241         # away if the python interpreter is started with -O
242         if __debug__ and self.environment.optimized:
243             overrides = set(context) & set(self.globals)
244             if overrides:
245                 plural = len(overrides) != 1 and 's' or ''
246                 raise AssertionError('the per template variable%s %s '
247                                      'override%s global variable%s. '
248                                      'With an enabled optimizer this '
249                                      'will lead to unexpected results.' %
250                     (plural, ', '.join(overrides), plural or ' a', plural))
251         gen = self.root_render_func(dict(self.globals, **context))
252         # skip the first item which is a reference to the context
253         gen.next()
254
255         try:
256             for event in gen:
257                 yield event
258         except:
259             exc_info = translate_exception(sys.exc_info())
260             raise exc_info[0], exc_info[1], exc_info[2]
261
262     def get_corresponding_lineno(self, lineno):
263         """Return the source line number of a line number in the
264         generated bytecode as they are not in sync.
265         """
266         for template_line, code_line in reversed(self._get_debug_info()):
267             if code_line <= lineno:
268                 return template_line
269         return 1
270
271     @property
272     def is_up_to_date(self):
273         """Check if the template is still up to date."""
274         if self._uptodate is None:
275             return True
276         return self._uptodate()
277
278     def __repr__(self):
279         return '<%s %r>' % (
280             self.__class__.__name__,
281             self.name
282         )
283
284
285 class TemplateStream(object):
286     """Wraps a genererator for outputing template streams."""
287
288     def __init__(self, gen):
289         self._gen = gen
290         self._next = gen.next
291         self.buffered = False
292
293     def disable_buffering(self):
294         """Disable the output buffering."""
295         self._next = self._gen.next
296         self.buffered = False
297
298     def enable_buffering(self, size=5):
299         """Enable buffering. Buffer `size` items before yielding them."""
300         if size <= 1:
301             raise ValueError('buffer size too small')
302         self.buffered = True
303
304         def buffering_next():
305             buf = []
306             c_size = 0
307             push = buf.append
308             next = self._gen.next
309
310             try:
311                 while 1:
312                     item = next()
313                     if item:
314                         push(item)
315                         c_size += 1
316                     if c_size >= size:
317                         raise StopIteration()
318             except StopIteration:
319                 if not c_size:
320                     raise
321             return u''.join(buf)
322
323         self._next = buffering_next
324
325     def __iter__(self):
326         return self
327
328     def next(self):
329         return self._next()