Coverage for /home/martinb/.local/share/virtualenvs/camcops/lib/python3.6/site-packages/mako/template.py : 21%

Hot-keys on this page
r m x p toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
1# mako/template.py
2# Copyright 2006-2020 the Mako authors and contributors <see AUTHORS file>
3#
4# This module is part of Mako and is released under
5# the MIT License: http://www.opensource.org/licenses/mit-license.php
7"""Provides the Template class, a facade for parsing, generating and executing
8template strings, as well as template runtime operations."""
10import json
11import os
12import re
13import shutil
14import stat
15import tempfile
16import types
17import weakref
19from mako import cache
20from mako import codegen
21from mako import compat
22from mako import exceptions
23from mako import runtime
24from mako import util
25from mako.lexer import Lexer
28class Template(object):
30 r"""Represents a compiled template.
32 :class:`.Template` includes a reference to the original
33 template source (via the :attr:`.source` attribute)
34 as well as the source code of the
35 generated Python module (i.e. the :attr:`.code` attribute),
36 as well as a reference to an actual Python module.
38 :class:`.Template` is constructed using either a literal string
39 representing the template text, or a filename representing a filesystem
40 path to a source file.
42 :param text: textual template source. This argument is mutually
43 exclusive versus the ``filename`` parameter.
45 :param filename: filename of the source template. This argument is
46 mutually exclusive versus the ``text`` parameter.
48 :param buffer_filters: string list of filters to be applied
49 to the output of ``%def``\ s which are buffered, cached, or otherwise
50 filtered, after all filters
51 defined with the ``%def`` itself have been applied. Allows the
52 creation of default expression filters that let the output
53 of return-valued ``%def``\ s "opt out" of that filtering via
54 passing special attributes or objects.
56 :param bytestring_passthrough: When ``True``, and ``output_encoding`` is
57 set to ``None``, and :meth:`.Template.render` is used to render,
58 the `StringIO` or `cStringIO` buffer will be used instead of the
59 default "fast" buffer. This allows raw bytestrings in the
60 output stream, such as in expressions, to pass straight
61 through to the buffer. This flag is forced
62 to ``True`` if ``disable_unicode`` is also configured.
64 .. versionadded:: 0.4
65 Added to provide the same behavior as that of the previous series.
67 :param cache_args: Dictionary of cache configuration arguments that
68 will be passed to the :class:`.CacheImpl`. See :ref:`caching_toplevel`.
70 :param cache_dir:
72 .. deprecated:: 0.6
73 Use the ``'dir'`` argument in the ``cache_args`` dictionary.
74 See :ref:`caching_toplevel`.
76 :param cache_enabled: Boolean flag which enables caching of this
77 template. See :ref:`caching_toplevel`.
79 :param cache_impl: String name of a :class:`.CacheImpl` caching
80 implementation to use. Defaults to ``'beaker'``.
82 :param cache_type:
84 .. deprecated:: 0.6
85 Use the ``'type'`` argument in the ``cache_args`` dictionary.
86 See :ref:`caching_toplevel`.
88 :param cache_url:
90 .. deprecated:: 0.6
91 Use the ``'url'`` argument in the ``cache_args`` dictionary.
92 See :ref:`caching_toplevel`.
94 :param default_filters: List of string filter names that will
95 be applied to all expressions. See :ref:`filtering_default_filters`.
97 :param disable_unicode: Disables all awareness of Python Unicode
98 objects. See :ref:`unicode_disabled`.
100 :param enable_loop: When ``True``, enable the ``loop`` context variable.
101 This can be set to ``False`` to support templates that may
102 be making usage of the name "``loop``". Individual templates can
103 re-enable the "loop" context by placing the directive
104 ``enable_loop="True"`` inside the ``<%page>`` tag -- see
105 :ref:`migrating_loop`.
107 :param encoding_errors: Error parameter passed to ``encode()`` when
108 string encoding is performed. See :ref:`usage_unicode`.
110 :param error_handler: Python callable which is called whenever
111 compile or runtime exceptions occur. The callable is passed
112 the current context as well as the exception. If the
113 callable returns ``True``, the exception is considered to
114 be handled, else it is re-raised after the function
115 completes. Is used to provide custom error-rendering
116 functions.
118 .. seealso::
120 :paramref:`.Template.include_error_handler` - include-specific
121 error handler function
123 :param format_exceptions: if ``True``, exceptions which occur during
124 the render phase of this template will be caught and
125 formatted into an HTML error page, which then becomes the
126 rendered result of the :meth:`.render` call. Otherwise,
127 runtime exceptions are propagated outwards.
129 :param imports: String list of Python statements, typically individual
130 "import" lines, which will be placed into the module level
131 preamble of all generated Python modules. See the example
132 in :ref:`filtering_default_filters`.
134 :param future_imports: String list of names to import from `__future__`.
135 These will be concatenated into a comma-separated string and inserted
136 into the beginning of the template, e.g. ``futures_imports=['FOO',
137 'BAR']`` results in ``from __future__ import FOO, BAR``. If you're
138 interested in using features like the new division operator, you must
139 use future_imports to convey that to the renderer, as otherwise the
140 import will not appear as the first executed statement in the generated
141 code and will therefore not have the desired effect.
143 :param include_error_handler: An error handler that runs when this template
144 is included within another one via the ``<%include>`` tag, and raises an
145 error. Compare to the :paramref:`.Template.error_handler` option.
147 .. versionadded:: 1.0.6
149 .. seealso::
151 :paramref:`.Template.error_handler` - top-level error handler function
153 :param input_encoding: Encoding of the template's source code. Can
154 be used in lieu of the coding comment. See
155 :ref:`usage_unicode` as well as :ref:`unicode_toplevel` for
156 details on source encoding.
158 :param lookup: a :class:`.TemplateLookup` instance that will be used
159 for all file lookups via the ``<%namespace>``,
160 ``<%include>``, and ``<%inherit>`` tags. See
161 :ref:`usage_templatelookup`.
163 :param module_directory: Filesystem location where generated
164 Python module files will be placed.
166 :param module_filename: Overrides the filename of the generated
167 Python module file. For advanced usage only.
169 :param module_writer: A callable which overrides how the Python
170 module is written entirely. The callable is passed the
171 encoded source content of the module and the destination
172 path to be written to. The default behavior of module writing
173 uses a tempfile in conjunction with a file move in order
174 to make the operation atomic. So a user-defined module
175 writing function that mimics the default behavior would be:
177 .. sourcecode:: python
179 import tempfile
180 import os
181 import shutil
183 def module_writer(source, outputpath):
184 (dest, name) = \\
185 tempfile.mkstemp(
186 dir=os.path.dirname(outputpath)
187 )
189 os.write(dest, source)
190 os.close(dest)
191 shutil.move(name, outputpath)
193 from mako.template import Template
194 mytemplate = Template(
195 filename="index.html",
196 module_directory="/path/to/modules",
197 module_writer=module_writer
198 )
200 The function is provided for unusual configurations where
201 certain platform-specific permissions or other special
202 steps are needed.
204 :param output_encoding: The encoding to use when :meth:`.render`
205 is called.
206 See :ref:`usage_unicode` as well as :ref:`unicode_toplevel`.
208 :param preprocessor: Python callable which will be passed
209 the full template source before it is parsed. The return
210 result of the callable will be used as the template source
211 code.
213 :param lexer_cls: A :class:`.Lexer` class used to parse
214 the template. The :class:`.Lexer` class is used by
215 default.
217 .. versionadded:: 0.7.4
219 :param strict_undefined: Replaces the automatic usage of
220 ``UNDEFINED`` for any undeclared variables not located in
221 the :class:`.Context` with an immediate raise of
222 ``NameError``. The advantage is immediate reporting of
223 missing variables which include the name.
225 .. versionadded:: 0.3.6
227 :param uri: string URI or other identifier for this template.
228 If not provided, the ``uri`` is generated from the filesystem
229 path, or from the in-memory identity of a non-file-based
230 template. The primary usage of the ``uri`` is to provide a key
231 within :class:`.TemplateLookup`, as well as to generate the
232 file path of the generated Python module file, if
233 ``module_directory`` is specified.
235 """
237 lexer_cls = Lexer
239 def __init__(
240 self,
241 text=None,
242 filename=None,
243 uri=None,
244 format_exceptions=False,
245 error_handler=None,
246 lookup=None,
247 output_encoding=None,
248 encoding_errors="strict",
249 module_directory=None,
250 cache_args=None,
251 cache_impl="beaker",
252 cache_enabled=True,
253 cache_type=None,
254 cache_dir=None,
255 cache_url=None,
256 module_filename=None,
257 input_encoding=None,
258 disable_unicode=False,
259 module_writer=None,
260 bytestring_passthrough=False,
261 default_filters=None,
262 buffer_filters=(),
263 strict_undefined=False,
264 imports=None,
265 future_imports=None,
266 enable_loop=True,
267 preprocessor=None,
268 lexer_cls=None,
269 include_error_handler=None,
270 ):
271 if uri:
272 self.module_id = re.sub(r"\W", "_", uri)
273 self.uri = uri
274 elif filename:
275 self.module_id = re.sub(r"\W", "_", filename)
276 drive, path = os.path.splitdrive(filename)
277 path = os.path.normpath(path).replace(os.path.sep, "/")
278 self.uri = path
279 else:
280 self.module_id = "memory:" + hex(id(self))
281 self.uri = self.module_id
283 u_norm = self.uri
284 if u_norm.startswith("/"):
285 u_norm = u_norm[1:]
286 u_norm = os.path.normpath(u_norm)
287 if u_norm.startswith(".."):
288 raise exceptions.TemplateLookupException(
289 'Template uri "%s" is invalid - '
290 "it cannot be relative outside "
291 "of the root path." % self.uri
292 )
294 self.input_encoding = input_encoding
295 self.output_encoding = output_encoding
296 self.encoding_errors = encoding_errors
297 self.disable_unicode = disable_unicode
298 self.bytestring_passthrough = bytestring_passthrough or disable_unicode
299 self.enable_loop = enable_loop
300 self.strict_undefined = strict_undefined
301 self.module_writer = module_writer
303 if compat.py3k and disable_unicode:
304 raise exceptions.UnsupportedError(
305 "Mako for Python 3 does not " "support disabling Unicode"
306 )
307 elif output_encoding and disable_unicode:
308 raise exceptions.UnsupportedError(
309 "output_encoding must be set to "
310 "None when disable_unicode is used."
311 )
312 if default_filters is None:
313 if compat.py3k or self.disable_unicode:
314 self.default_filters = ["str"]
315 else:
316 self.default_filters = ["unicode"]
317 else:
318 self.default_filters = default_filters
319 self.buffer_filters = buffer_filters
321 self.imports = imports
322 self.future_imports = future_imports
323 self.preprocessor = preprocessor
325 if lexer_cls is not None:
326 self.lexer_cls = lexer_cls
328 # if plain text, compile code in memory only
329 if text is not None:
330 (code, module) = _compile_text(self, text, filename)
331 self._code = code
332 self._source = text
333 ModuleInfo(module, None, self, filename, code, text, uri)
334 elif filename is not None:
335 # if template filename and a module directory, load
336 # a filesystem-based module file, generating if needed
337 if module_filename is not None:
338 path = module_filename
339 elif module_directory is not None:
340 path = os.path.abspath(
341 os.path.join(
342 os.path.normpath(module_directory), u_norm + ".py"
343 )
344 )
345 else:
346 path = None
347 module = self._compile_from_file(path, filename)
348 else:
349 raise exceptions.RuntimeException(
350 "Template requires text or filename"
351 )
353 self.module = module
354 self.filename = filename
355 self.callable_ = self.module.render_body
356 self.format_exceptions = format_exceptions
357 self.error_handler = error_handler
358 self.include_error_handler = include_error_handler
359 self.lookup = lookup
361 self.module_directory = module_directory
363 self._setup_cache_args(
364 cache_impl,
365 cache_enabled,
366 cache_args,
367 cache_type,
368 cache_dir,
369 cache_url,
370 )
372 @util.memoized_property
373 def reserved_names(self):
374 if self.enable_loop:
375 return codegen.RESERVED_NAMES
376 else:
377 return codegen.RESERVED_NAMES.difference(["loop"])
379 def _setup_cache_args(
380 self,
381 cache_impl,
382 cache_enabled,
383 cache_args,
384 cache_type,
385 cache_dir,
386 cache_url,
387 ):
388 self.cache_impl = cache_impl
389 self.cache_enabled = cache_enabled
390 if cache_args:
391 self.cache_args = cache_args
392 else:
393 self.cache_args = {}
395 # transfer deprecated cache_* args
396 if cache_type:
397 self.cache_args["type"] = cache_type
398 if cache_dir:
399 self.cache_args["dir"] = cache_dir
400 if cache_url:
401 self.cache_args["url"] = cache_url
403 def _compile_from_file(self, path, filename):
404 if path is not None:
405 util.verify_directory(os.path.dirname(path))
406 filemtime = os.stat(filename)[stat.ST_MTIME]
407 if (
408 not os.path.exists(path)
409 or os.stat(path)[stat.ST_MTIME] < filemtime
410 ):
411 data = util.read_file(filename)
412 _compile_module_file(
413 self, data, filename, path, self.module_writer
414 )
415 module = compat.load_module(self.module_id, path)
416 if module._magic_number != codegen.MAGIC_NUMBER:
417 data = util.read_file(filename)
418 _compile_module_file(
419 self, data, filename, path, self.module_writer
420 )
421 module = compat.load_module(self.module_id, path)
422 ModuleInfo(module, path, self, filename, None, None, None)
423 else:
424 # template filename and no module directory, compile code
425 # in memory
426 data = util.read_file(filename)
427 code, module = _compile_text(self, data, filename)
428 self._source = None
429 self._code = code
430 ModuleInfo(module, None, self, filename, code, None, None)
431 return module
433 @property
434 def source(self):
435 """Return the template source code for this :class:`.Template`."""
437 return _get_module_info_from_callable(self.callable_).source
439 @property
440 def code(self):
441 """Return the module source code for this :class:`.Template`."""
443 return _get_module_info_from_callable(self.callable_).code
445 @util.memoized_property
446 def cache(self):
447 return cache.Cache(self)
449 @property
450 def cache_dir(self):
451 return self.cache_args["dir"]
453 @property
454 def cache_url(self):
455 return self.cache_args["url"]
457 @property
458 def cache_type(self):
459 return self.cache_args["type"]
461 def render(self, *args, **data):
462 """Render the output of this template as a string.
464 If the template specifies an output encoding, the string
465 will be encoded accordingly, else the output is raw (raw
466 output uses `cStringIO` and can't handle multibyte
467 characters). A :class:`.Context` object is created corresponding
468 to the given data. Arguments that are explicitly declared
469 by this template's internal rendering method are also
470 pulled from the given ``*args``, ``**data`` members.
472 """
473 return runtime._render(self, self.callable_, args, data)
475 def render_unicode(self, *args, **data):
476 """Render the output of this template as a unicode object."""
478 return runtime._render(
479 self, self.callable_, args, data, as_unicode=True
480 )
482 def render_context(self, context, *args, **kwargs):
483 """Render this :class:`.Template` with the given context.
485 The data is written to the context's buffer.
487 """
488 if getattr(context, "_with_template", None) is None:
489 context._set_with_template(self)
490 runtime._render_context(self, self.callable_, context, *args, **kwargs)
492 def has_def(self, name):
493 return hasattr(self.module, "render_%s" % name)
495 def get_def(self, name):
496 """Return a def of this template as a :class:`.DefTemplate`."""
498 return DefTemplate(self, getattr(self.module, "render_%s" % name))
500 def list_defs(self):
501 """return a list of defs in the template.
503 .. versionadded:: 1.0.4
505 """
506 return [i[7:] for i in dir(self.module) if i[:7] == "render_"]
508 def _get_def_callable(self, name):
509 return getattr(self.module, "render_%s" % name)
511 @property
512 def last_modified(self):
513 return self.module._modified_time
516class ModuleTemplate(Template):
518 """A Template which is constructed given an existing Python module.
520 e.g.::
522 t = Template("this is a template")
523 f = file("mymodule.py", "w")
524 f.write(t.code)
525 f.close()
527 import mymodule
529 t = ModuleTemplate(mymodule)
530 print(t.render())
532 """
534 def __init__(
535 self,
536 module,
537 module_filename=None,
538 template=None,
539 template_filename=None,
540 module_source=None,
541 template_source=None,
542 output_encoding=None,
543 encoding_errors="strict",
544 disable_unicode=False,
545 bytestring_passthrough=False,
546 format_exceptions=False,
547 error_handler=None,
548 lookup=None,
549 cache_args=None,
550 cache_impl="beaker",
551 cache_enabled=True,
552 cache_type=None,
553 cache_dir=None,
554 cache_url=None,
555 include_error_handler=None,
556 ):
557 self.module_id = re.sub(r"\W", "_", module._template_uri)
558 self.uri = module._template_uri
559 self.input_encoding = module._source_encoding
560 self.output_encoding = output_encoding
561 self.encoding_errors = encoding_errors
562 self.disable_unicode = disable_unicode
563 self.bytestring_passthrough = bytestring_passthrough or disable_unicode
564 self.enable_loop = module._enable_loop
566 if compat.py3k and disable_unicode:
567 raise exceptions.UnsupportedError(
568 "Mako for Python 3 does not " "support disabling Unicode"
569 )
570 elif output_encoding and disable_unicode:
571 raise exceptions.UnsupportedError(
572 "output_encoding must be set to "
573 "None when disable_unicode is used."
574 )
576 self.module = module
577 self.filename = template_filename
578 ModuleInfo(
579 module,
580 module_filename,
581 self,
582 template_filename,
583 module_source,
584 template_source,
585 module._template_uri,
586 )
588 self.callable_ = self.module.render_body
589 self.format_exceptions = format_exceptions
590 self.error_handler = error_handler
591 self.include_error_handler = include_error_handler
592 self.lookup = lookup
593 self._setup_cache_args(
594 cache_impl,
595 cache_enabled,
596 cache_args,
597 cache_type,
598 cache_dir,
599 cache_url,
600 )
603class DefTemplate(Template):
605 """A :class:`.Template` which represents a callable def in a parent
606 template."""
608 def __init__(self, parent, callable_):
609 self.parent = parent
610 self.callable_ = callable_
611 self.output_encoding = parent.output_encoding
612 self.module = parent.module
613 self.encoding_errors = parent.encoding_errors
614 self.format_exceptions = parent.format_exceptions
615 self.error_handler = parent.error_handler
616 self.include_error_handler = parent.include_error_handler
617 self.enable_loop = parent.enable_loop
618 self.lookup = parent.lookup
619 self.bytestring_passthrough = parent.bytestring_passthrough
621 def get_def(self, name):
622 return self.parent.get_def(name)
625class ModuleInfo(object):
627 """Stores information about a module currently loaded into
628 memory, provides reverse lookups of template source, module
629 source code based on a module's identifier.
631 """
633 _modules = weakref.WeakValueDictionary()
635 def __init__(
636 self,
637 module,
638 module_filename,
639 template,
640 template_filename,
641 module_source,
642 template_source,
643 template_uri,
644 ):
645 self.module = module
646 self.module_filename = module_filename
647 self.template_filename = template_filename
648 self.module_source = module_source
649 self.template_source = template_source
650 self.template_uri = template_uri
651 self._modules[module.__name__] = template._mmarker = self
652 if module_filename:
653 self._modules[module_filename] = self
655 @classmethod
656 def get_module_source_metadata(cls, module_source, full_line_map=False):
657 source_map = re.search(
658 r"__M_BEGIN_METADATA(.+?)__M_END_METADATA", module_source, re.S
659 ).group(1)
660 source_map = json.loads(source_map)
661 source_map["line_map"] = dict(
662 (int(k), int(v)) for k, v in source_map["line_map"].items()
663 )
664 if full_line_map:
665 f_line_map = source_map["full_line_map"] = []
666 line_map = source_map["line_map"]
668 curr_templ_line = 1
669 for mod_line in range(1, max(line_map)):
670 if mod_line in line_map:
671 curr_templ_line = line_map[mod_line]
672 f_line_map.append(curr_templ_line)
673 return source_map
675 @property
676 def code(self):
677 if self.module_source is not None:
678 return self.module_source
679 else:
680 return util.read_python_file(self.module_filename)
682 @property
683 def source(self):
684 if self.template_source is not None:
685 if self.module._source_encoding and not isinstance(
686 self.template_source, compat.text_type
687 ):
688 return self.template_source.decode(
689 self.module._source_encoding
690 )
691 else:
692 return self.template_source
693 else:
694 data = util.read_file(self.template_filename)
695 if self.module._source_encoding:
696 return data.decode(self.module._source_encoding)
697 else:
698 return data
701def _compile(template, text, filename, generate_magic_comment):
702 lexer = template.lexer_cls(
703 text,
704 filename,
705 disable_unicode=template.disable_unicode,
706 input_encoding=template.input_encoding,
707 preprocessor=template.preprocessor,
708 )
709 node = lexer.parse()
710 source = codegen.compile(
711 node,
712 template.uri,
713 filename,
714 default_filters=template.default_filters,
715 buffer_filters=template.buffer_filters,
716 imports=template.imports,
717 future_imports=template.future_imports,
718 source_encoding=lexer.encoding,
719 generate_magic_comment=generate_magic_comment,
720 disable_unicode=template.disable_unicode,
721 strict_undefined=template.strict_undefined,
722 enable_loop=template.enable_loop,
723 reserved_names=template.reserved_names,
724 )
725 return source, lexer
728def _compile_text(template, text, filename):
729 identifier = template.module_id
730 source, lexer = _compile(
731 template,
732 text,
733 filename,
734 generate_magic_comment=template.disable_unicode,
735 )
737 cid = identifier
738 if not compat.py3k and isinstance(cid, compat.text_type):
739 cid = cid.encode()
740 module = types.ModuleType(cid)
741 code = compile(source, cid, "exec")
743 # this exec() works for 2.4->3.3.
744 exec(code, module.__dict__, module.__dict__)
745 return (source, module)
748def _compile_module_file(template, text, filename, outputpath, module_writer):
749 source, lexer = _compile(
750 template, text, filename, generate_magic_comment=True
751 )
753 if isinstance(source, compat.text_type):
754 source = source.encode(lexer.encoding or "ascii")
756 if module_writer:
757 module_writer(source, outputpath)
758 else:
759 # make tempfiles in the same location as the ultimate
760 # location. this ensures they're on the same filesystem,
761 # avoiding synchronization issues.
762 (dest, name) = tempfile.mkstemp(dir=os.path.dirname(outputpath))
764 os.write(dest, source)
765 os.close(dest)
766 shutil.move(name, outputpath)
769def _get_module_info_from_callable(callable_):
770 if compat.py3k:
771 return _get_module_info(callable_.__globals__["__name__"])
772 else:
773 return _get_module_info(callable_.func_globals["__name__"])
776def _get_module_info(filename):
777 return ModuleInfo._modules[filename]