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 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 tempfile 16import types 17import weakref 18 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 26 27 28class Template(object): 29 30 r"""Represents a compiled template. 31 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. 37 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. 41 42 :param text: textual template source. This argument is mutually 43 exclusive versus the ``filename`` parameter. 44 45 :param filename: filename of the source template. This argument is 46 mutually exclusive versus the ``text`` parameter. 47 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. 55 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. 63 64 .. versionadded:: 0.4 65 Added to provide the same behavior as that of the previous series. 66 67 :param cache_args: Dictionary of cache configuration arguments that 68 will be passed to the :class:`.CacheImpl`. See :ref:`caching_toplevel`. 69 70 :param cache_dir: 71 72 .. deprecated:: 0.6 73 Use the ``'dir'`` argument in the ``cache_args`` dictionary. 74 See :ref:`caching_toplevel`. 75 76 :param cache_enabled: Boolean flag which enables caching of this 77 template. See :ref:`caching_toplevel`. 78 79 :param cache_impl: String name of a :class:`.CacheImpl` caching 80 implementation to use. Defaults to ``'beaker'``. 81 82 :param cache_type: 83 84 .. deprecated:: 0.6 85 Use the ``'type'`` argument in the ``cache_args`` dictionary. 86 See :ref:`caching_toplevel`. 87 88 :param cache_url: 89 90 .. deprecated:: 0.6 91 Use the ``'url'`` argument in the ``cache_args`` dictionary. 92 See :ref:`caching_toplevel`. 93 94 :param default_filters: List of string filter names that will 95 be applied to all expressions. See :ref:`filtering_default_filters`. 96 97 :param disable_unicode: Disables all awareness of Python Unicode 98 objects. See :ref:`unicode_disabled`. 99 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`. 106 107 :param encoding_errors: Error parameter passed to ``encode()`` when 108 string encoding is performed. See :ref:`usage_unicode`. 109 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. 117 118 .. seealso:: 119 120 :paramref:`.Template.include_error_handler` - include-specific 121 error handler function 122 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. 128 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`. 133 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. 142 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. 146 147 .. versionadded:: 1.0.6 148 149 .. seealso:: 150 151 :paramref:`.Template.error_handler` - top-level error handler function 152 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. 157 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`. 162 163 :param module_directory: Filesystem location where generated 164 Python module files will be placed. 165 166 :param module_filename: Overrides the filename of the generated 167 Python module file. For advanced usage only. 168 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: 176 177 .. sourcecode:: python 178 179 import tempfile 180 import os 181 import shutil 182 183 def module_writer(source, outputpath): 184 (dest, name) = \\ 185 tempfile.mkstemp( 186 dir=os.path.dirname(outputpath) 187 ) 188 189 os.write(dest, source) 190 os.close(dest) 191 shutil.move(name, outputpath) 192 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 ) 199 200 The function is provided for unusual configurations where 201 certain platform-specific permissions or other special 202 steps are needed. 203 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`. 207 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. 212 213 :param lexer_cls: A :class:`.Lexer` class used to parse 214 the template. The :class:`.Lexer` class is used by 215 default. 216 217 .. versionadded:: 0.7.4 218 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. 224 225 .. versionadded:: 0.3.6 226 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. 234 235 """ 236 237 lexer_cls = Lexer 238 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 282 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 ) 293 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 302 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 320 321 self.imports = imports 322 self.future_imports = future_imports 323 self.preprocessor = preprocessor 324 325 if lexer_cls is not None: 326 self.lexer_cls = lexer_cls 327 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 ) 352 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 360 361 self.module_directory = module_directory 362 363 self._setup_cache_args( 364 cache_impl, 365 cache_enabled, 366 cache_args, 367 cache_type, 368 cache_dir, 369 cache_url, 370 ) 371 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"]) 378 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 = {} 394 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 402 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 432 433 @property 434 def source(self): 435 """Return the template source code for this :class:`.Template`.""" 436 437 return _get_module_info_from_callable(self.callable_).source 438 439 @property 440 def code(self): 441 """Return the module source code for this :class:`.Template`.""" 442 443 return _get_module_info_from_callable(self.callable_).code 444 445 @util.memoized_property 446 def cache(self): 447 return cache.Cache(self) 448 449 @property 450 def cache_dir(self): 451 return self.cache_args["dir"] 452 453 @property 454 def cache_url(self): 455 return self.cache_args["url"] 456 457 @property 458 def cache_type(self): 459 return self.cache_args["type"] 460 461 def render(self, *args, **data): 462 """Render the output of this template as a string. 463 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. 471 472 """ 473 return runtime._render(self, self.callable_, args, data) 474 475 def render_unicode(self, *args, **data): 476 """Render the output of this template as a unicode object.""" 477 478 return runtime._render( 479 self, self.callable_, args, data, as_unicode=True 480 ) 481 482 def render_context(self, context, *args, **kwargs): 483 """Render this :class:`.Template` with the given context. 484 485 The data is written to the context's buffer. 486 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) 491 492 def has_def(self, name): 493 return hasattr(self.module, "render_%s" % name) 494 495 def get_def(self, name): 496 """Return a def of this template as a :class:`.DefTemplate`.""" 497 498 return DefTemplate(self, getattr(self.module, "render_%s" % name)) 499 500 def list_defs(self): 501 """return a list of defs in the template. 502 503 .. versionadded:: 1.0.4 504 505 """ 506 return [i[7:] for i in dir(self.module) if i[:7] == "render_"] 507 508 def _get_def_callable(self, name): 509 return getattr(self.module, "render_%s" % name) 510 511 @property 512 def last_modified(self): 513 return self.module._modified_time 514 515 516class ModuleTemplate(Template): 517 518 """A Template which is constructed given an existing Python module. 519 520 e.g.:: 521 522 t = Template("this is a template") 523 f = file("mymodule.py", "w") 524 f.write(t.code) 525 f.close() 526 527 import mymodule 528 529 t = ModuleTemplate(mymodule) 530 print(t.render()) 531 532 """ 533 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 565 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 ) 575 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 ) 587 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 ) 601 602 603class DefTemplate(Template): 604 605 """A :class:`.Template` which represents a callable def in a parent 606 template.""" 607 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 620 621 def get_def(self, name): 622 return self.parent.get_def(name) 623 624 625class ModuleInfo(object): 626 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. 630 631 """ 632 633 _modules = weakref.WeakValueDictionary() 634 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 654 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"] 667 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 674 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) 681 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 699 700 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 726 727 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 ) 736 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") 742 743 # this exec() works for 2.4->3.3. 744 exec(code, module.__dict__, module.__dict__) 745 return (source, module) 746 747 748def _compile_module_file(template, text, filename, outputpath, module_writer): 749 source, lexer = _compile( 750 template, text, filename, generate_magic_comment=True 751 ) 752 753 if isinstance(source, compat.text_type): 754 source = source.encode(lexer.encoding or "ascii") 755 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)) 763 764 os.write(dest, source) 765 os.close(dest) 766 shutil.move(name, outputpath) 767 768 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__"]) 774 775 776def _get_module_info(filename): 777 return ModuleInfo._modules[filename] 778