hopefully fixed `Template`
[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.defaults import DEFAULT_FILTERS, DEFAULT_TESTS, DEFAULT_NAMESPACE
16
17
18 class Environment(object):
19     """The Jinja environment.
20
21     The core component of Jinja is the `Environment`. It contains
22     important shared variables like configuration, filters, tests,
23     globals and others.
24     """
25
26     def __init__(self,
27                  block_start_string='{%',
28                  block_end_string='%}',
29                  variable_start_string='{{',
30                  variable_end_string='}}',
31                  comment_start_string='{#',
32                  comment_end_string='#}',
33                  line_statement_prefix=None,
34                  trim_blocks=False,
35                  optimized=True,
36                  loader=None):
37         """Here the possible initialization parameters:
38
39         ========================= ============================================
40         `block_start_string`      the string marking the begin of a block.
41                                   this defaults to ``'{%'``.
42         `block_end_string`        the string marking the end of a block.
43                                   defaults to ``'%}'``.
44         `variable_start_string`   the string marking the begin of a print
45                                   statement. defaults to ``'{{'``.
46         `comment_start_string`    the string marking the begin of a
47                                   comment. defaults to ``'{#'``.
48         `comment_end_string`      the string marking the end of a comment.
49                                   defaults to ``'#}'``.
50         `line_statement_prefix`   If given and a string, this will be used as
51                                   prefix for line based statements.  See the
52                                   documentation for more details.
53         `trim_blocks`             If this is set to ``True`` the first newline
54                                   after a block is removed (block, not
55                                   variable tag!). Defaults to ``False``.
56         `optimized`               should the optimizer be enabled?  Default is
57                                   ``True``.
58         `loader`                  the loader which should be used.
59         ========================= ============================================
60         """
61
62         # lexer / parser information
63         self.block_start_string = block_start_string
64         self.block_end_string = block_end_string
65         self.variable_start_string = variable_start_string
66         self.variable_end_string = variable_end_string
67         self.comment_start_string = comment_start_string
68         self.comment_end_string = comment_end_string
69         self.line_statement_prefix = line_statement_prefix
70         self.trim_blocks = trim_blocks
71         self.optimized = optimized
72
73         # defaults
74         self.filters = DEFAULT_FILTERS.copy()
75         self.tests = DEFAULT_TESTS.copy()
76         self.globals = DEFAULT_NAMESPACE.copy()
77
78         # if no finalize function/method exists we default to unicode.  The
79         # compiler check if the finalize attribute *is* unicode, if yes no
80         # finalizaion is written where it can be avoided.
81         if not hasattr(self, 'finalize'):
82             self.finalize = unicode
83
84         # set the loader provided
85         self.loader = loader
86
87         # create lexer
88         self.lexer = Lexer(self)
89
90     def parse(self, source, filename=None):
91         """Parse the sourcecode and return the abstract syntax tree. This tree
92         of nodes is used by the compiler to convert the template into
93         executable source- or bytecode.
94         """
95         parser = Parser(self, source, filename)
96         return parser.parse()
97
98     def lex(self, source, filename=None):
99         """Lex the given sourcecode and return a generator that yields tokens.
100         The stream returned is not usable for Jinja but can be used if
101         Jinja templates should be processed by other tools (for example
102         syntax highlighting etc)
103
104         The tuples are returned in the form ``(lineno, token, value)``.
105         """
106         return self.lexer.tokeniter(source, filename)
107
108     def compile(self, source, filename=None, raw=False, globals=None):
109         """Compile a node or source."""
110         if isinstance(source, basestring):
111             source = self.parse(source, filename)
112         if self.optimized:
113             node = optimize(source, self, globals or {})
114         source = generate(node, self, filename)
115         if raw:
116             return source
117         if isinstance(filename, unicode):
118             filename = filename.encode('utf-8')
119         return compile(source, filename, 'exec')
120
121     def join_path(self, template, parent):
122         """Join a template with the parent.  By default all the lookups are
123         relative to the loader root, but if the paths should be relative this
124         function can be used to calculate the real filename."""
125         return template
126
127     def get_template(self, name, parent=None, globals=None):
128         """Load a template."""
129         if self.loader is None:
130             raise TypeError('no loader for this environment specified')
131         if parent is not None:
132             name = self.join_path(name, parent)
133         globals = self.make_globals(globals)
134         return self.loader.load(self, name, globals)
135
136     def from_string(self, source, filename='<string>', globals=None):
137         """Load a template from a string."""
138         globals = self.make_globals(globals)
139         return Template(self, self.compile(source, filename), globals)
140
141     def make_globals(self, d):
142         """Return a dict for the globals."""
143         if d is None:
144             return self.globals
145         return dict(self.globals, **d)
146
147
148 class Template(object):
149     """Represents a template."""
150
151     def __init__(self, environment, code, globals):
152         namespace = {'environment': environment}
153         exec code in namespace
154         self.environment = environment
155         self.name = namespace['filename']
156         self.root_render_func = namespace['root']
157         self.blocks = namespace['blocks']
158         self.globals = globals
159
160     def render(self, *args, **kwargs):
161         return u''.join(self.generate(*args, **kwargs))
162
163     def stream(self, *args, **kwargs):
164         return TemplateStream(self.generate(*args, **kwargs))
165
166     def generate(self, *args, **kwargs):
167         # assemble the context
168         local_context = dict(*args, **kwargs)
169         context = self.globals.copy()
170         context.update(local_context)
171
172         # if the environment is using the optimizer locals may never
173         # override globals as optimizations might have happened
174         # depending on values of certain globals.  This assertion goes
175         # away if the python interpreter is started with -O
176         if __debug__ and self.environment.optimized:
177             overrides = set(local_context) & set(self.globals)
178             if overrides:
179                 plural = len(overrides) != 1 and 's' or ''
180                 raise AssertionError('the per template variable%s %s '
181                                      'override%s global variable%s. '
182                                      'With an enabled optimizer this '
183                                      'will lead to unexpected results.' %
184                     (plural, ', '.join(overrides), plural or ' a', plural))
185         gen = self.root_render_func(context)
186         # skip the first item which is a reference to the stream
187         gen.next()
188         return gen