Armin Ronacher | 07bc684 | 2008-03-31 14:18:49 +0200 | [diff] [blame] | 1 | # -*- coding: utf-8 -*- |
| 2 | """ |
Armin Ronacher | 82b3f3d | 2008-03-31 20:01:08 +0200 | [diff] [blame] | 3 | jinja2.environment |
| 4 | ~~~~~~~~~~~~~~~~~~ |
Armin Ronacher | 07bc684 | 2008-03-31 14:18:49 +0200 | [diff] [blame] | 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 | """ |
Armin Ronacher | ba3757b | 2008-04-16 19:43:16 +0200 | [diff] [blame] | 11 | import sys |
Armin Ronacher | 82b3f3d | 2008-03-31 20:01:08 +0200 | [diff] [blame] | 12 | from jinja2.lexer import Lexer |
Armin Ronacher | 0553093 | 2008-04-20 13:27:49 +0200 | [diff] [blame] | 13 | from jinja2.parser import Parser |
Armin Ronacher | bcb7c53 | 2008-04-11 16:30:34 +0200 | [diff] [blame] | 14 | from jinja2.optimizer import optimize |
| 15 | from jinja2.compiler import generate |
Armin Ronacher | de6bf71 | 2008-04-26 01:44:14 +0200 | [diff] [blame] | 16 | from jinja2.runtime import Undefined, TemplateContext, concat |
Armin Ronacher | ba3757b | 2008-04-16 19:43:16 +0200 | [diff] [blame] | 17 | from jinja2.debug import translate_exception |
Armin Ronacher | 6ce170c | 2008-04-25 12:32:36 +0200 | [diff] [blame] | 18 | from jinja2.utils import import_string, LRUCache, Markup |
Armin Ronacher | 82b3f3d | 2008-03-31 20:01:08 +0200 | [diff] [blame] | 19 | from jinja2.defaults import DEFAULT_FILTERS, DEFAULT_TESTS, DEFAULT_NAMESPACE |
Armin Ronacher | 07bc684 | 2008-03-31 14:18:49 +0200 | [diff] [blame] | 20 | |
| 21 | |
Armin Ronacher | 203bfcb | 2008-04-24 21:54:44 +0200 | [diff] [blame] | 22 | # for direct template usage we have up to ten living environments |
| 23 | _spontaneous_environments = LRUCache(10) |
| 24 | |
| 25 | |
Armin Ronacher | b5124e6 | 2008-04-25 00:36:14 +0200 | [diff] [blame] | 26 | def get_spontaneous_environment(*args): |
Armin Ronacher | 203bfcb | 2008-04-24 21:54:44 +0200 | [diff] [blame] | 27 | """Return a new spontaneus environment. A spontaneus environment is an |
| 28 | unnamed and unaccessable (in theory) environment that is used for |
| 29 | template generated from a string and not from the file system. |
| 30 | """ |
| 31 | try: |
| 32 | env = _spontaneous_environments.get(args) |
| 33 | except TypeError: |
| 34 | return Environment(*args) |
| 35 | if env is not None: |
| 36 | return env |
| 37 | _spontaneous_environments[args] = env = Environment(*args) |
Armin Ronacher | c9705c2 | 2008-04-27 21:28:03 +0200 | [diff] [blame] | 38 | env.shared = True |
Armin Ronacher | 203bfcb | 2008-04-24 21:54:44 +0200 | [diff] [blame] | 39 | return env |
| 40 | |
| 41 | |
| 42 | def template_from_code(environment, code, globals, uptodate=None, |
| 43 | template_class=None): |
| 44 | """Generate a new template object from code. It's used in the |
| 45 | template constructor and the loader `load` implementation. |
| 46 | """ |
| 47 | t = object.__new__(template_class or environment.template_class) |
| 48 | namespace = { |
| 49 | 'environment': environment, |
| 50 | '__jinja_template__': t |
| 51 | } |
| 52 | exec code in namespace |
| 53 | t.environment = environment |
| 54 | t.name = namespace['name'] |
| 55 | t.filename = code.co_filename |
| 56 | t.root_render_func = namespace['root'] |
| 57 | t.blocks = namespace['blocks'] |
| 58 | t.globals = globals |
| 59 | |
| 60 | # debug and loader helpers |
| 61 | t._debug_info = namespace['debug_info'] |
| 62 | t._uptodate = uptodate |
| 63 | |
| 64 | return t |
| 65 | |
| 66 | |
Armin Ronacher | 07bc684 | 2008-03-31 14:18:49 +0200 | [diff] [blame] | 67 | class Environment(object): |
Armin Ronacher | d134231 | 2008-04-28 12:20:12 +0200 | [diff] [blame^] | 68 | """The core component of Jinja is the `Environment`. It contains |
Armin Ronacher | 07bc684 | 2008-03-31 14:18:49 +0200 | [diff] [blame] | 69 | important shared variables like configuration, filters, tests, |
Armin Ronacher | d134231 | 2008-04-28 12:20:12 +0200 | [diff] [blame^] | 70 | globals and others. Instances of this class may be modified if |
| 71 | they are not shared and if no template was loaded so far. |
| 72 | Modifications on environments after the first template was loaded |
| 73 | will lead to surprising effects and undefined behavior. |
| 74 | |
| 75 | Here the possible initialization parameters: |
| 76 | |
| 77 | `block_start_string` |
| 78 | The string marking the begin of a block. Defaults to ``'{%'``. |
| 79 | |
| 80 | `block_end_string` |
| 81 | The string marking the end of a block. Defaults to ``'%}'``. |
| 82 | |
| 83 | `variable_start_string` |
| 84 | The string marking the begin of a print statement. |
| 85 | Defaults to ``'{{'``. |
| 86 | |
| 87 | `comment_start_string` |
| 88 | The string marking the begin of a comment. Defaults to ``'{#'``. |
| 89 | |
| 90 | `comment_end_string` |
| 91 | The string marking the end of a comment. Defaults to ``'#}'``. |
| 92 | |
| 93 | `line_statement_prefix` |
| 94 | If given and a string, this will be used as prefix for line based |
| 95 | statements. |
| 96 | |
| 97 | `trim_blocks` |
| 98 | If this is set to ``True`` the first newline after a block is |
| 99 | removed (block, not variable tag!). Defaults to `False`. |
| 100 | |
| 101 | `extensions` |
| 102 | List of Jinja extensions to use. This can either be import paths |
| 103 | as strings or extension classes. |
| 104 | |
| 105 | `optimized` |
| 106 | should the optimizer be enabled? Default is `True`. |
| 107 | |
| 108 | `undefined` |
| 109 | :class:`Undefined` or a subclass of it that is used to represent |
| 110 | undefined values in the template. |
| 111 | |
| 112 | `finalize` |
| 113 | A callable that finalizes the variable. Per default no finalizing |
| 114 | is applied. |
| 115 | |
| 116 | `autoescape` |
| 117 | If set to true the XML/HTML autoescaping feature is enabled. |
| 118 | |
| 119 | `loader` |
| 120 | The template loader for this environment. |
Armin Ronacher | 07bc684 | 2008-03-31 14:18:49 +0200 | [diff] [blame] | 121 | """ |
| 122 | |
Armin Ronacher | c63243e | 2008-04-14 22:53:58 +0200 | [diff] [blame] | 123 | #: if this environment is sandboxed. Modifying this variable won't make |
| 124 | #: the environment sandboxed though. For a real sandboxed environment |
| 125 | #: have a look at jinja2.sandbox |
| 126 | sandboxed = False |
| 127 | |
Armin Ronacher | c9705c2 | 2008-04-27 21:28:03 +0200 | [diff] [blame] | 128 | #: shared environments have this set to `True`. A shared environment |
| 129 | #: must not be modified |
| 130 | shared = False |
| 131 | |
Armin Ronacher | 07bc684 | 2008-03-31 14:18:49 +0200 | [diff] [blame] | 132 | def __init__(self, |
| 133 | block_start_string='{%', |
| 134 | block_end_string='%}', |
| 135 | variable_start_string='{{', |
| 136 | variable_end_string='}}', |
| 137 | comment_start_string='{#', |
| 138 | comment_end_string='#}', |
Armin Ronacher | bf7c4ad | 2008-04-12 12:02:36 +0200 | [diff] [blame] | 139 | line_statement_prefix=None, |
Armin Ronacher | 07bc684 | 2008-03-31 14:18:49 +0200 | [diff] [blame] | 140 | trim_blocks=False, |
Armin Ronacher | b5124e6 | 2008-04-25 00:36:14 +0200 | [diff] [blame] | 141 | extensions=(), |
Armin Ronacher | fed44b5 | 2008-04-13 19:42:53 +0200 | [diff] [blame] | 142 | optimized=True, |
Armin Ronacher | c63243e | 2008-04-14 22:53:58 +0200 | [diff] [blame] | 143 | undefined=Undefined, |
Armin Ronacher | d134231 | 2008-04-28 12:20:12 +0200 | [diff] [blame^] | 144 | finalize=None, |
| 145 | autoescape=False, |
Armin Ronacher | b5124e6 | 2008-04-25 00:36:14 +0200 | [diff] [blame] | 146 | loader=None): |
| 147 | # !!Important notice!! |
| 148 | # The constructor accepts quite a few arguments that should be |
| 149 | # passed by keyword rather than position. However it's important to |
| 150 | # not change the order of arguments because it's used at least |
| 151 | # internally in those cases: |
| 152 | # - spontaneus environments (i18n extension and Template) |
| 153 | # - unittests |
| 154 | # If parameter changes are required only add parameters at the end |
| 155 | # and don't change the arguments (or the defaults!) of the arguments |
| 156 | # up to (but excluding) loader. |
Armin Ronacher | 2e9396b | 2008-04-16 14:21:57 +0200 | [diff] [blame] | 157 | |
| 158 | # santity checks |
Armin Ronacher | c63243e | 2008-04-14 22:53:58 +0200 | [diff] [blame] | 159 | assert issubclass(undefined, Undefined), 'undefined must be ' \ |
| 160 | 'a subclass of undefined because filters depend on it.' |
Armin Ronacher | 2e9396b | 2008-04-16 14:21:57 +0200 | [diff] [blame] | 161 | assert block_start_string != variable_start_string != \ |
| 162 | comment_start_string, 'block, variable and comment ' \ |
| 163 | 'start strings must be different' |
Armin Ronacher | 07bc684 | 2008-03-31 14:18:49 +0200 | [diff] [blame] | 164 | |
| 165 | # lexer / parser information |
| 166 | self.block_start_string = block_start_string |
| 167 | self.block_end_string = block_end_string |
| 168 | self.variable_start_string = variable_start_string |
| 169 | self.variable_end_string = variable_end_string |
| 170 | self.comment_start_string = comment_start_string |
| 171 | self.comment_end_string = comment_end_string |
Armin Ronacher | bf7c4ad | 2008-04-12 12:02:36 +0200 | [diff] [blame] | 172 | self.line_statement_prefix = line_statement_prefix |
Armin Ronacher | 07bc684 | 2008-03-31 14:18:49 +0200 | [diff] [blame] | 173 | self.trim_blocks = trim_blocks |
Armin Ronacher | 203bfcb | 2008-04-24 21:54:44 +0200 | [diff] [blame] | 174 | |
Armin Ronacher | f59bac2 | 2008-04-20 13:11:43 +0200 | [diff] [blame] | 175 | # runtime information |
Armin Ronacher | c63243e | 2008-04-14 22:53:58 +0200 | [diff] [blame] | 176 | self.undefined = undefined |
Armin Ronacher | fed44b5 | 2008-04-13 19:42:53 +0200 | [diff] [blame] | 177 | self.optimized = optimized |
Armin Ronacher | 18c6ca0 | 2008-04-17 10:03:29 +0200 | [diff] [blame] | 178 | self.finalize = finalize |
Armin Ronacher | d134231 | 2008-04-28 12:20:12 +0200 | [diff] [blame^] | 179 | self.autoescape = autoescape |
Armin Ronacher | 07bc684 | 2008-03-31 14:18:49 +0200 | [diff] [blame] | 180 | |
| 181 | # defaults |
| 182 | self.filters = DEFAULT_FILTERS.copy() |
| 183 | self.tests = DEFAULT_TESTS.copy() |
| 184 | self.globals = DEFAULT_NAMESPACE.copy() |
| 185 | |
Armin Ronacher | bcb7c53 | 2008-04-11 16:30:34 +0200 | [diff] [blame] | 186 | # set the loader provided |
| 187 | self.loader = loader |
| 188 | |
Armin Ronacher | 07bc684 | 2008-03-31 14:18:49 +0200 | [diff] [blame] | 189 | # create lexer |
| 190 | self.lexer = Lexer(self) |
| 191 | |
Armin Ronacher | b5124e6 | 2008-04-25 00:36:14 +0200 | [diff] [blame] | 192 | # load extensions |
| 193 | self.extensions = [] |
| 194 | for extension in extensions: |
| 195 | if isinstance(extension, basestring): |
| 196 | extension = import_string(extension) |
| 197 | self.extensions.append(extension(self)) |
Armin Ronacher | 203bfcb | 2008-04-24 21:54:44 +0200 | [diff] [blame] | 198 | |
Armin Ronacher | c63243e | 2008-04-14 22:53:58 +0200 | [diff] [blame] | 199 | def subscribe(self, obj, argument): |
| 200 | """Get an item or attribute of an object.""" |
| 201 | try: |
| 202 | return getattr(obj, str(argument)) |
| 203 | except (AttributeError, UnicodeError): |
| 204 | try: |
| 205 | return obj[argument] |
| 206 | except (TypeError, LookupError): |
Armin Ronacher | 9a82205 | 2008-04-17 18:44:07 +0200 | [diff] [blame] | 207 | return self.undefined(obj=obj, name=argument) |
Armin Ronacher | c63243e | 2008-04-14 22:53:58 +0200 | [diff] [blame] | 208 | |
Armin Ronacher | ba3757b | 2008-04-16 19:43:16 +0200 | [diff] [blame] | 209 | def parse(self, source, name=None): |
Armin Ronacher | d134231 | 2008-04-28 12:20:12 +0200 | [diff] [blame^] | 210 | """Parse the sourcecode and return the abstract syntax tree. This |
| 211 | tree of nodes is used by the compiler to convert the template into |
| 212 | executable source- or bytecode. This is useful for debugging or to |
| 213 | extract information from templates. |
Armin Ronacher | 07bc684 | 2008-03-31 14:18:49 +0200 | [diff] [blame] | 214 | """ |
Armin Ronacher | 9a82205 | 2008-04-17 18:44:07 +0200 | [diff] [blame] | 215 | return Parser(self, source, name).parse() |
Armin Ronacher | 07bc684 | 2008-03-31 14:18:49 +0200 | [diff] [blame] | 216 | |
Armin Ronacher | ba3757b | 2008-04-16 19:43:16 +0200 | [diff] [blame] | 217 | def lex(self, source, name=None): |
Armin Ronacher | d134231 | 2008-04-28 12:20:12 +0200 | [diff] [blame^] | 218 | """Lex the given sourcecode and return a generator that yields |
| 219 | tokens as tuples in the form ``(lineno, token_type, value)``. |
Armin Ronacher | 07bc684 | 2008-03-31 14:18:49 +0200 | [diff] [blame] | 220 | """ |
Armin Ronacher | ba3757b | 2008-04-16 19:43:16 +0200 | [diff] [blame] | 221 | return self.lexer.tokeniter(source, name) |
Armin Ronacher | bcb7c53 | 2008-04-11 16:30:34 +0200 | [diff] [blame] | 222 | |
Armin Ronacher | 814f6c2 | 2008-04-17 15:52:23 +0200 | [diff] [blame] | 223 | def compile(self, source, name=None, filename=None, globals=None, |
| 224 | raw=False): |
Armin Ronacher | d134231 | 2008-04-28 12:20:12 +0200 | [diff] [blame^] | 225 | """Compile a node or template source code. The `name` parameter is |
| 226 | the load name of the template after it was joined using |
| 227 | :meth:`join_path` if necessary, not the filename on the file system. |
| 228 | the `filename` parameter is the estimated filename of the template on |
| 229 | the file system. If the template came from a database or memory this |
| 230 | can be omitted. The `globals` parameter can be used to provide extra |
| 231 | variables at compile time for the template. In the future the |
| 232 | optimizer will be able to evaluate parts of the template at compile |
| 233 | time based on those variables. |
| 234 | |
| 235 | The return value of this method is a python code object. If the `raw` |
| 236 | parameter is `True` the return value will be a string with python |
| 237 | code equivalent to the bytecode returned otherwise. This method is |
| 238 | mainly used internally. |
Armin Ronacher | 68f7767 | 2008-04-17 11:50:39 +0200 | [diff] [blame] | 239 | """ |
Armin Ronacher | bcb7c53 | 2008-04-11 16:30:34 +0200 | [diff] [blame] | 240 | if isinstance(source, basestring): |
Armin Ronacher | 8e8d071 | 2008-04-16 23:10:49 +0200 | [diff] [blame] | 241 | source = self.parse(source, name) |
Armin Ronacher | fed44b5 | 2008-04-13 19:42:53 +0200 | [diff] [blame] | 242 | if self.optimized: |
| 243 | node = optimize(source, self, globals or {}) |
Armin Ronacher | 8e8d071 | 2008-04-16 23:10:49 +0200 | [diff] [blame] | 244 | source = generate(node, self, name, filename) |
Armin Ronacher | bcb7c53 | 2008-04-11 16:30:34 +0200 | [diff] [blame] | 245 | if raw: |
| 246 | return source |
Armin Ronacher | 2e9396b | 2008-04-16 14:21:57 +0200 | [diff] [blame] | 247 | if filename is None: |
Armin Ronacher | 68f7767 | 2008-04-17 11:50:39 +0200 | [diff] [blame] | 248 | filename = '<template>' |
Armin Ronacher | 2e9396b | 2008-04-16 14:21:57 +0200 | [diff] [blame] | 249 | elif isinstance(filename, unicode): |
Armin Ronacher | bcb7c53 | 2008-04-11 16:30:34 +0200 | [diff] [blame] | 250 | filename = filename.encode('utf-8') |
| 251 | return compile(source, filename, 'exec') |
| 252 | |
| 253 | def join_path(self, template, parent): |
| 254 | """Join a template with the parent. By default all the lookups are |
Armin Ronacher | d134231 | 2008-04-28 12:20:12 +0200 | [diff] [blame^] | 255 | relative to the loader root so this method returns the `template` |
| 256 | parameter unchanged, but if the paths should be relative to the |
| 257 | parent template, this function can be used to calculate the real |
| 258 | template name. |
| 259 | |
| 260 | Subclasses may override this method and implement template path |
| 261 | joining here. |
| 262 | """ |
Armin Ronacher | bcb7c53 | 2008-04-11 16:30:34 +0200 | [diff] [blame] | 263 | return template |
| 264 | |
Armin Ronacher | fed44b5 | 2008-04-13 19:42:53 +0200 | [diff] [blame] | 265 | def get_template(self, name, parent=None, globals=None): |
Armin Ronacher | d134231 | 2008-04-28 12:20:12 +0200 | [diff] [blame^] | 266 | """Load a template from the loader. If a loader is configured this |
| 267 | method ask the loader for the template and returns a :class:`Template`. |
| 268 | If the `parent` parameter is not `None`, :meth:`join_path` is called |
| 269 | to get the real template name before loading. |
| 270 | |
| 271 | The `globals` parameter can be used to provide compile-time globals. |
| 272 | In the future this will allow the optimizer to render parts of the |
| 273 | templates at compile-time. |
| 274 | |
| 275 | If the template does not exist a :exc:`TemplateNotFound` exception is |
| 276 | raised. |
| 277 | """ |
Armin Ronacher | bcb7c53 | 2008-04-11 16:30:34 +0200 | [diff] [blame] | 278 | if self.loader is None: |
| 279 | raise TypeError('no loader for this environment specified') |
| 280 | if parent is not None: |
| 281 | name = self.join_path(name, parent) |
Armin Ronacher | 32a910f | 2008-04-26 23:21:03 +0200 | [diff] [blame] | 282 | return self.loader.load(self, name, self.make_globals(globals)) |
Armin Ronacher | bcb7c53 | 2008-04-11 16:30:34 +0200 | [diff] [blame] | 283 | |
Armin Ronacher | 203bfcb | 2008-04-24 21:54:44 +0200 | [diff] [blame] | 284 | def from_string(self, source, globals=None, template_class=None): |
Armin Ronacher | d134231 | 2008-04-28 12:20:12 +0200 | [diff] [blame^] | 285 | """Load a template from a string. This parses the source given and |
| 286 | returns a :class:`Template` object. |
| 287 | """ |
Armin Ronacher | fed44b5 | 2008-04-13 19:42:53 +0200 | [diff] [blame] | 288 | globals = self.make_globals(globals) |
Armin Ronacher | 203bfcb | 2008-04-24 21:54:44 +0200 | [diff] [blame] | 289 | return template_from_code(self, self.compile(source, globals=globals), |
Armin Ronacher | 5304229 | 2008-04-26 18:30:19 +0200 | [diff] [blame] | 290 | globals, None, template_class) |
Armin Ronacher | fed44b5 | 2008-04-13 19:42:53 +0200 | [diff] [blame] | 291 | |
| 292 | def make_globals(self, d): |
| 293 | """Return a dict for the globals.""" |
| 294 | if d is None: |
| 295 | return self.globals |
| 296 | return dict(self.globals, **d) |
Armin Ronacher | 46f5f98 | 2008-04-11 16:40:09 +0200 | [diff] [blame] | 297 | |
Armin Ronacher | bcb7c53 | 2008-04-11 16:30:34 +0200 | [diff] [blame] | 298 | |
| 299 | class Template(object): |
Armin Ronacher | 203bfcb | 2008-04-24 21:54:44 +0200 | [diff] [blame] | 300 | """The central template object. This class represents a compiled template |
| 301 | and is used to evaluate it. |
Armin Ronacher | bcb7c53 | 2008-04-11 16:30:34 +0200 | [diff] [blame] | 302 | |
Armin Ronacher | d134231 | 2008-04-28 12:20:12 +0200 | [diff] [blame^] | 303 | Normally the template object is generated from an :class:`Environment` but |
| 304 | it also has a constructor that makes it possible to create a template |
Armin Ronacher | 203bfcb | 2008-04-24 21:54:44 +0200 | [diff] [blame] | 305 | instance directly using the constructor. It takes the same arguments as |
| 306 | the environment constructor but it's not possible to specify a loader. |
Armin Ronacher | bcb7c53 | 2008-04-11 16:30:34 +0200 | [diff] [blame] | 307 | |
Armin Ronacher | 203bfcb | 2008-04-24 21:54:44 +0200 | [diff] [blame] | 308 | Every template object has a few methods and members that are guaranteed |
| 309 | to exist. However it's important that a template object should be |
| 310 | considered immutable. Modifications on the object are not supported. |
| 311 | |
| 312 | Template objects created from the constructor rather than an environment |
| 313 | do have an `environment` attribute that points to a temporary environment |
| 314 | that is probably shared with other templates created with the constructor |
| 315 | and compatible settings. |
| 316 | |
| 317 | >>> template = Template('Hello {{ name }}!') |
| 318 | >>> template.render(name='John Doe') |
| 319 | u'Hello John Doe!' |
| 320 | |
| 321 | >>> stream = template.stream(name='John Doe') |
| 322 | >>> stream.next() |
| 323 | u'Hello John Doe!' |
| 324 | >>> stream.next() |
| 325 | Traceback (most recent call last): |
| 326 | ... |
| 327 | StopIteration |
| 328 | """ |
| 329 | |
| 330 | def __new__(cls, source, |
| 331 | block_start_string='{%', |
| 332 | block_end_string='%}', |
| 333 | variable_start_string='{{', |
| 334 | variable_end_string='}}', |
| 335 | comment_start_string='{#', |
| 336 | comment_end_string='#}', |
| 337 | line_statement_prefix=None, |
| 338 | trim_blocks=False, |
Armin Ronacher | b5124e6 | 2008-04-25 00:36:14 +0200 | [diff] [blame] | 339 | extensions=(), |
Armin Ronacher | 203bfcb | 2008-04-24 21:54:44 +0200 | [diff] [blame] | 340 | optimized=True, |
| 341 | undefined=Undefined, |
Armin Ronacher | d134231 | 2008-04-28 12:20:12 +0200 | [diff] [blame^] | 342 | finalize=None, |
| 343 | autoescape=False): |
Armin Ronacher | b5124e6 | 2008-04-25 00:36:14 +0200 | [diff] [blame] | 344 | env = get_spontaneous_environment( |
Armin Ronacher | 203bfcb | 2008-04-24 21:54:44 +0200 | [diff] [blame] | 345 | block_start_string, block_end_string, variable_start_string, |
| 346 | variable_end_string, comment_start_string, comment_end_string, |
Armin Ronacher | b5124e6 | 2008-04-25 00:36:14 +0200 | [diff] [blame] | 347 | line_statement_prefix, trim_blocks, tuple(extensions), optimized, |
Armin Ronacher | d134231 | 2008-04-28 12:20:12 +0200 | [diff] [blame^] | 348 | undefined, finalize, autoescape) |
Armin Ronacher | 203bfcb | 2008-04-24 21:54:44 +0200 | [diff] [blame] | 349 | return env.from_string(source, template_class=cls) |
Armin Ronacher | ba3757b | 2008-04-16 19:43:16 +0200 | [diff] [blame] | 350 | |
Armin Ronacher | bcb7c53 | 2008-04-11 16:30:34 +0200 | [diff] [blame] | 351 | def render(self, *args, **kwargs): |
Armin Ronacher | d134231 | 2008-04-28 12:20:12 +0200 | [diff] [blame^] | 352 | """This method accepts the same arguments as the `dict` constructor: |
| 353 | A dict, a dict subclass or some keyword arguments. If no arguments |
| 354 | are given the context will be empty. These two calls do the same:: |
| 355 | |
| 356 | template.render(knights='that say nih') |
| 357 | template.render({'knights': 'that say nih'}) |
| 358 | |
| 359 | This will return the rendered template as unicode string. |
| 360 | """ |
Armin Ronacher | f41d139 | 2008-04-18 16:41:52 +0200 | [diff] [blame] | 361 | try: |
Armin Ronacher | de6bf71 | 2008-04-26 01:44:14 +0200 | [diff] [blame] | 362 | return concat(self.generate(*args, **kwargs)) |
Armin Ronacher | f41d139 | 2008-04-18 16:41:52 +0200 | [diff] [blame] | 363 | except: |
| 364 | # hide the `generate` frame |
| 365 | exc_type, exc_value, tb = sys.exc_info() |
| 366 | raise exc_type, exc_value, tb.tb_next |
Armin Ronacher | bcb7c53 | 2008-04-11 16:30:34 +0200 | [diff] [blame] | 367 | |
| 368 | def stream(self, *args, **kwargs): |
Armin Ronacher | d134231 | 2008-04-28 12:20:12 +0200 | [diff] [blame^] | 369 | """Works exactly like :meth:`generate` but returns a |
| 370 | :class:`TemplateStream`. |
| 371 | """ |
Armin Ronacher | f41d139 | 2008-04-18 16:41:52 +0200 | [diff] [blame] | 372 | try: |
| 373 | return TemplateStream(self.generate(*args, **kwargs)) |
| 374 | except: |
| 375 | # hide the `generate` frame |
| 376 | exc_type, exc_value, tb = sys.exc_info() |
| 377 | raise exc_type, exc_value, tb.tb_next |
Armin Ronacher | fed44b5 | 2008-04-13 19:42:53 +0200 | [diff] [blame] | 378 | |
| 379 | def generate(self, *args, **kwargs): |
Armin Ronacher | d134231 | 2008-04-28 12:20:12 +0200 | [diff] [blame^] | 380 | """For very large templates it can be useful to not render the whole |
| 381 | template at once but evaluate each statement after another and yield |
| 382 | piece for piece. This method basically does exactly that and returns |
| 383 | a generator that yields one item after another as unicode strings. |
| 384 | |
| 385 | It accepts the same arguments as :meth:`render`. |
| 386 | """ |
Armin Ronacher | fed44b5 | 2008-04-13 19:42:53 +0200 | [diff] [blame] | 387 | # assemble the context |
Armin Ronacher | 2e9396b | 2008-04-16 14:21:57 +0200 | [diff] [blame] | 388 | context = dict(*args, **kwargs) |
Armin Ronacher | fed44b5 | 2008-04-13 19:42:53 +0200 | [diff] [blame] | 389 | |
| 390 | # if the environment is using the optimizer locals may never |
| 391 | # override globals as optimizations might have happened |
| 392 | # depending on values of certain globals. This assertion goes |
| 393 | # away if the python interpreter is started with -O |
| 394 | if __debug__ and self.environment.optimized: |
Armin Ronacher | 2e9396b | 2008-04-16 14:21:57 +0200 | [diff] [blame] | 395 | overrides = set(context) & set(self.globals) |
Armin Ronacher | fed44b5 | 2008-04-13 19:42:53 +0200 | [diff] [blame] | 396 | if overrides: |
| 397 | plural = len(overrides) != 1 and 's' or '' |
| 398 | raise AssertionError('the per template variable%s %s ' |
| 399 | 'override%s global variable%s. ' |
| 400 | 'With an enabled optimizer this ' |
| 401 | 'will lead to unexpected results.' % |
| 402 | (plural, ', '.join(overrides), plural or ' a', plural)) |
Armin Ronacher | ba3757b | 2008-04-16 19:43:16 +0200 | [diff] [blame] | 403 | |
| 404 | try: |
Armin Ronacher | 203bfcb | 2008-04-24 21:54:44 +0200 | [diff] [blame] | 405 | for event in self.root_render_func(self.new_context(context)): |
Armin Ronacher | ba3757b | 2008-04-16 19:43:16 +0200 | [diff] [blame] | 406 | yield event |
| 407 | except: |
Armin Ronacher | 203bfcb | 2008-04-24 21:54:44 +0200 | [diff] [blame] | 408 | exc_type, exc_value, tb = translate_exception(sys.exc_info()) |
| 409 | raise exc_type, exc_value, tb |
| 410 | |
Armin Ronacher | c9705c2 | 2008-04-27 21:28:03 +0200 | [diff] [blame] | 411 | def new_context(self, vars=None, shared=False): |
| 412 | """Create a new template context for this template. The vars |
| 413 | provided will be passed to the template. Per default the globals |
| 414 | are added to the context, if shared is set to `True` the data |
| 415 | provided is used as parent namespace. This is used to share the |
| 416 | same globals in multiple contexts without consuming more memory. |
| 417 | (This works because the context does not modify the parent dict) |
| 418 | """ |
| 419 | if vars is None: |
| 420 | vars = {} |
| 421 | if shared: |
| 422 | parent = vars |
| 423 | else: |
| 424 | parent = dict(self.globals, **vars) |
| 425 | return TemplateContext(self.environment, parent, self.name, |
| 426 | self.blocks) |
Armin Ronacher | ba3757b | 2008-04-16 19:43:16 +0200 | [diff] [blame] | 427 | |
Armin Ronacher | c9705c2 | 2008-04-27 21:28:03 +0200 | [diff] [blame] | 428 | def include(self, vars=None): |
Armin Ronacher | d134231 | 2008-04-28 12:20:12 +0200 | [diff] [blame^] | 429 | """Some templates may export macros or other variables. It's possible |
| 430 | to access those variables by "including" the template. This is mainly |
| 431 | used internally but may also be useful on the Python layer. If passed |
| 432 | a context, the template is evaluated in it, otherwise an empty context |
| 433 | with just the globals is used. |
| 434 | |
| 435 | The return value is an included template object. Converting it to |
| 436 | unicode returns the rendered contents of the template, the exported |
| 437 | variables are accessable via the attribute syntax. |
| 438 | |
| 439 | This example shows how it can be used: |
| 440 | |
| 441 | >>> t = Template('{% say_hello(name) %}Hello {{ name }}!{% endmacro %}42') |
| 442 | >>> i = t.include() |
| 443 | >>> unicode(i) |
| 444 | u'42' |
| 445 | >>> i.say_hello('John') |
| 446 | u'Hello John!' |
Armin Ronacher | 6ce170c | 2008-04-25 12:32:36 +0200 | [diff] [blame] | 447 | """ |
Armin Ronacher | c9705c2 | 2008-04-27 21:28:03 +0200 | [diff] [blame] | 448 | if isinstance(vars, TemplateContext): |
| 449 | context = TemplateContext(self.environment, vars.parent, |
| 450 | self.name, self.blocks) |
Armin Ronacher | 963f97d | 2008-04-25 11:44:59 +0200 | [diff] [blame] | 451 | else: |
Armin Ronacher | c9705c2 | 2008-04-27 21:28:03 +0200 | [diff] [blame] | 452 | context = self.new_context(vars) |
Armin Ronacher | 963f97d | 2008-04-25 11:44:59 +0200 | [diff] [blame] | 453 | return IncludedTemplate(self, context) |
| 454 | |
Armin Ronacher | ba3757b | 2008-04-16 19:43:16 +0200 | [diff] [blame] | 455 | def get_corresponding_lineno(self, lineno): |
| 456 | """Return the source line number of a line number in the |
| 457 | generated bytecode as they are not in sync. |
| 458 | """ |
Armin Ronacher | 203bfcb | 2008-04-24 21:54:44 +0200 | [diff] [blame] | 459 | for template_line, code_line in reversed(self.debug_info): |
Armin Ronacher | ba3757b | 2008-04-16 19:43:16 +0200 | [diff] [blame] | 460 | if code_line <= lineno: |
| 461 | return template_line |
| 462 | return 1 |
Armin Ronacher | c63243e | 2008-04-14 22:53:58 +0200 | [diff] [blame] | 463 | |
Armin Ronacher | 9a82205 | 2008-04-17 18:44:07 +0200 | [diff] [blame] | 464 | @property |
Armin Ronacher | 814f6c2 | 2008-04-17 15:52:23 +0200 | [diff] [blame] | 465 | def is_up_to_date(self): |
Armin Ronacher | 203bfcb | 2008-04-24 21:54:44 +0200 | [diff] [blame] | 466 | """If this variable is `False` there is a newer version available.""" |
Armin Ronacher | 814f6c2 | 2008-04-17 15:52:23 +0200 | [diff] [blame] | 467 | if self._uptodate is None: |
| 468 | return True |
| 469 | return self._uptodate() |
| 470 | |
Armin Ronacher | 203bfcb | 2008-04-24 21:54:44 +0200 | [diff] [blame] | 471 | @property |
| 472 | def debug_info(self): |
| 473 | """The debug info mapping.""" |
| 474 | return [tuple(map(int, x.split('='))) for x in |
| 475 | self._debug_info.split('&')] |
| 476 | |
Armin Ronacher | c63243e | 2008-04-14 22:53:58 +0200 | [diff] [blame] | 477 | def __repr__(self): |
Armin Ronacher | 5304229 | 2008-04-26 18:30:19 +0200 | [diff] [blame] | 478 | if self.name is None: |
| 479 | name = 'memory:%x' % id(self) |
| 480 | else: |
| 481 | name = repr(self.name) |
| 482 | return '<%s %s>' % (self.__class__.__name__, name) |
Armin Ronacher | c63243e | 2008-04-14 22:53:58 +0200 | [diff] [blame] | 483 | |
| 484 | |
Armin Ronacher | 963f97d | 2008-04-25 11:44:59 +0200 | [diff] [blame] | 485 | class IncludedTemplate(object): |
Armin Ronacher | 5304229 | 2008-04-26 18:30:19 +0200 | [diff] [blame] | 486 | """Represents an included template. All the exported names of the |
| 487 | template are available as attributes on this object. Additionally |
| 488 | converting it into an unicode- or bytestrings renders the contents. |
| 489 | """ |
Armin Ronacher | 963f97d | 2008-04-25 11:44:59 +0200 | [diff] [blame] | 490 | |
| 491 | def __init__(self, template, context): |
Armin Ronacher | 5304229 | 2008-04-26 18:30:19 +0200 | [diff] [blame] | 492 | self.__body_stream = tuple(template.root_render_func(context)) |
Armin Ronacher | 6ce170c | 2008-04-25 12:32:36 +0200 | [diff] [blame] | 493 | self.__dict__.update(context.get_exported()) |
Armin Ronacher | 2feed1d | 2008-04-26 16:26:52 +0200 | [diff] [blame] | 494 | self.__name__ = template.name |
Armin Ronacher | 963f97d | 2008-04-25 11:44:59 +0200 | [diff] [blame] | 495 | |
Armin Ronacher | 5304229 | 2008-04-26 18:30:19 +0200 | [diff] [blame] | 496 | __html__ = lambda x: Markup(concat(x.__body_stream)) |
| 497 | __unicode__ = lambda x: unicode(concat(x.__body_stream)) |
Armin Ronacher | 6ce170c | 2008-04-25 12:32:36 +0200 | [diff] [blame] | 498 | |
| 499 | def __str__(self): |
Armin Ronacher | 2feed1d | 2008-04-26 16:26:52 +0200 | [diff] [blame] | 500 | return unicode(self).encode('utf-8') |
Armin Ronacher | 963f97d | 2008-04-25 11:44:59 +0200 | [diff] [blame] | 501 | |
| 502 | def __repr__(self): |
Armin Ronacher | 5304229 | 2008-04-26 18:30:19 +0200 | [diff] [blame] | 503 | if self.__name__ is None: |
| 504 | name = 'memory:%x' % id(self) |
| 505 | else: |
| 506 | name = repr(self.name) |
| 507 | return '<%s %s>' % (self.__class__.__name__, name) |
Armin Ronacher | 963f97d | 2008-04-25 11:44:59 +0200 | [diff] [blame] | 508 | |
| 509 | |
Armin Ronacher | c63243e | 2008-04-14 22:53:58 +0200 | [diff] [blame] | 510 | class TemplateStream(object): |
Armin Ronacher | d134231 | 2008-04-28 12:20:12 +0200 | [diff] [blame^] | 511 | """A template stream works pretty much like an ordinary python generator |
| 512 | but it can buffer multiple items to reduce the number of total iterations. |
| 513 | Per default the output is unbuffered which means that for every unbuffered |
| 514 | instruction in the template one unicode string is yielded. |
| 515 | |
| 516 | If buffering is enabled with a buffer size of 5, five items are combined |
| 517 | into a new unicode string. This is mainly useful if you are streaming |
| 518 | big templates to a client via WSGI which flushes after each iteration. |
Armin Ronacher | 203bfcb | 2008-04-24 21:54:44 +0200 | [diff] [blame] | 519 | """ |
Armin Ronacher | c63243e | 2008-04-14 22:53:58 +0200 | [diff] [blame] | 520 | |
| 521 | def __init__(self, gen): |
| 522 | self._gen = gen |
| 523 | self._next = gen.next |
| 524 | self.buffered = False |
| 525 | |
| 526 | def disable_buffering(self): |
| 527 | """Disable the output buffering.""" |
| 528 | self._next = self._gen.next |
| 529 | self.buffered = False |
| 530 | |
| 531 | def enable_buffering(self, size=5): |
Armin Ronacher | d134231 | 2008-04-28 12:20:12 +0200 | [diff] [blame^] | 532 | """Enable buffering. Buffer `size` items before yielding them.""" |
Armin Ronacher | c63243e | 2008-04-14 22:53:58 +0200 | [diff] [blame] | 533 | if size <= 1: |
| 534 | raise ValueError('buffer size too small') |
Armin Ronacher | c63243e | 2008-04-14 22:53:58 +0200 | [diff] [blame] | 535 | |
Armin Ronacher | 203bfcb | 2008-04-24 21:54:44 +0200 | [diff] [blame] | 536 | def generator(): |
Armin Ronacher | c63243e | 2008-04-14 22:53:58 +0200 | [diff] [blame] | 537 | buf = [] |
| 538 | c_size = 0 |
| 539 | push = buf.append |
| 540 | next = self._gen.next |
| 541 | |
Armin Ronacher | 203bfcb | 2008-04-24 21:54:44 +0200 | [diff] [blame] | 542 | while 1: |
| 543 | try: |
Armin Ronacher | b5124e6 | 2008-04-25 00:36:14 +0200 | [diff] [blame] | 544 | while c_size < size: |
Armin Ronacher | 203bfcb | 2008-04-24 21:54:44 +0200 | [diff] [blame] | 545 | push(next()) |
Armin Ronacher | c63243e | 2008-04-14 22:53:58 +0200 | [diff] [blame] | 546 | c_size += 1 |
Armin Ronacher | 203bfcb | 2008-04-24 21:54:44 +0200 | [diff] [blame] | 547 | except StopIteration: |
| 548 | if not c_size: |
| 549 | raise |
Armin Ronacher | de6bf71 | 2008-04-26 01:44:14 +0200 | [diff] [blame] | 550 | yield concat(buf) |
Armin Ronacher | 203bfcb | 2008-04-24 21:54:44 +0200 | [diff] [blame] | 551 | del buf[:] |
| 552 | c_size = 0 |
Armin Ronacher | c63243e | 2008-04-14 22:53:58 +0200 | [diff] [blame] | 553 | |
Armin Ronacher | 203bfcb | 2008-04-24 21:54:44 +0200 | [diff] [blame] | 554 | self.buffered = True |
| 555 | self._next = generator().next |
Armin Ronacher | c63243e | 2008-04-14 22:53:58 +0200 | [diff] [blame] | 556 | |
| 557 | def __iter__(self): |
| 558 | return self |
| 559 | |
| 560 | def next(self): |
| 561 | return self._next() |
Armin Ronacher | 203bfcb | 2008-04-24 21:54:44 +0200 | [diff] [blame] | 562 | |
| 563 | |
| 564 | # hook in default template class. if anyone reads this comment: ignore that |
| 565 | # it's possible to use custom templates ;-) |
| 566 | Environment.template_class = Template |