added autoescaping
[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, 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         parser = Parser(self, source, name)
121         return parser.parse()
122
123     def lex(self, source, name=None):
124         """Lex the given sourcecode and return a generator that yields tokens.
125         The stream returned is not usable for Jinja but can be used if
126         Jinja templates should be processed by other tools (for example
127         syntax highlighting etc)
128
129         The tuples are returned in the form ``(lineno, token, value)``.
130         """
131         return self.lexer.tokeniter(source, name)
132
133     def compile(self, source, name=None, filename=None, raw=False,
134                 globals=None):
135         """Compile a node or source."""
136         if isinstance(source, basestring):
137             source = self.parse(source, name)
138         if self.optimized:
139             node = optimize(source, self, globals or {})
140         source = generate(node, self, name, filename)
141         if raw:
142             return source
143         if filename is None:
144             filename = '<from_string>'
145         elif isinstance(filename, unicode):
146             filename = filename.encode('utf-8')
147         return compile(source, filename, 'exec')
148
149     def join_path(self, template, parent):
150         """Join a template with the parent.  By default all the lookups are
151         relative to the loader root, but if the paths should be relative this
152         function can be used to calculate the real filename."""
153         return template
154
155     def get_template(self, name, parent=None, globals=None):
156         """Load a template."""
157         if self.loader is None:
158             raise TypeError('no loader for this environment specified')
159         if parent is not None:
160             name = self.join_path(name, parent)
161         globals = self.make_globals(globals)
162         return self.loader.load(self, name, globals)
163
164     def from_string(self, source, globals=None):
165         """Load a template from a string."""
166         globals = self.make_globals(globals)
167         return Template(self, self.compile(source, globals=globals),
168                         globals)
169
170     def make_globals(self, d):
171         """Return a dict for the globals."""
172         if d is None:
173             return self.globals
174         return dict(self.globals, **d)
175
176
177 class Template(object):
178     """Represents a template."""
179
180     def __init__(self, environment, code, globals):
181         namespace = {'environment': environment}
182         exec code in namespace
183         self.environment = environment
184         self.name = namespace['name']
185         self.filename = code.co_filename
186         self.root_render_func = namespace['root']
187         self.blocks = namespace['blocks']
188         self.globals = globals
189
190         # debug helpers
191         self._get_debug_info = namespace['get_debug_info']
192         namespace['__jinja_template__'] = self
193
194     def render(self, *args, **kwargs):
195         return u''.join(self.generate(*args, **kwargs))
196
197     def stream(self, *args, **kwargs):
198         return TemplateStream(self.generate(*args, **kwargs))
199
200     def generate(self, *args, **kwargs):
201         # assemble the context
202         context = dict(*args, **kwargs)
203
204         # if the environment is using the optimizer locals may never
205         # override globals as optimizations might have happened
206         # depending on values of certain globals.  This assertion goes
207         # away if the python interpreter is started with -O
208         if __debug__ and self.environment.optimized:
209             overrides = set(context) & set(self.globals)
210             if overrides:
211                 plural = len(overrides) != 1 and 's' or ''
212                 raise AssertionError('the per template variable%s %s '
213                                      'override%s global variable%s. '
214                                      'With an enabled optimizer this '
215                                      'will lead to unexpected results.' %
216                     (plural, ', '.join(overrides), plural or ' a', plural))
217         gen = self.root_render_func(dict(self.globals, **context))
218         # skip the first item which is a reference to the context
219         gen.next()
220
221         try:
222             for event in gen:
223                 yield event
224         except:
225             exc_info = translate_exception(sys.exc_info())
226             raise exc_info[0], exc_info[1], exc_info[2]
227
228     def get_corresponding_lineno(self, lineno):
229         """Return the source line number of a line number in the
230         generated bytecode as they are not in sync.
231         """
232         for template_line, code_line in reversed(self._get_debug_info()):
233             if code_line <= lineno:
234                 return template_line
235         return 1
236
237     def __repr__(self):
238         return '<%s %r>' % (
239             self.__class__.__name__,
240             self.name
241         )
242
243
244 class TemplateStream(object):
245     """Wraps a genererator for outputing template streams."""
246
247     def __init__(self, gen):
248         self._gen = gen
249         self._next = gen.next
250         self.buffered = False
251
252     def disable_buffering(self):
253         """Disable the output buffering."""
254         self._next = self._gen.next
255         self.buffered = False
256
257     def enable_buffering(self, size=5):
258         """Enable buffering. Buffer `size` items before yielding them."""
259         if size <= 1:
260             raise ValueError('buffer size too small')
261         self.buffered = True
262
263         def buffering_next():
264             buf = []
265             c_size = 0
266             push = buf.append
267             next = self._gen.next
268
269             try:
270                 while 1:
271                     item = next()
272                     if item:
273                         push(item)
274                         c_size += 1
275                     if c_size >= size:
276                         raise StopIteration()
277             except StopIteration:
278                 if not c_size:
279                     raise
280             return u''.join(buf)
281
282         self._next = buffering_next
283
284     def __iter__(self):
285         return self
286
287     def next(self):
288         return self._next()