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