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