TEXT   61

PTL - Python Template Language

Guest on 27th June 2022 05:45:14 AM

  1. PTL: Python Template Language
  2. =============================
  3.  
  4. Introduction
  5. ------------
  6.  
  7. PTL is the templating language used by Quixote.  Most web templating
  8. languages embed a real programming language in HTML, but PTL inverts
  9. this model by merely tweaking Python to make it easier to generate
  10. HTML pages (or other forms of text).  In other words, PTL is basically
  11. Python with a novel way to specify function return values.
  12.  
  13. Specifically, a PTL template is designated by decorating a function with
  14. the `ptl_plain` or `ptl_html` decorator (from the `quixote.ptl` module).
  15. The value of expressions inside templates are kept, not discarded.  For
  16. HTML templates, the return value from the function is a special string
  17. type, which tracks HTML special character escaping (i.e.  special
  18. characters are escaped exactly once).
  19.  
  20.  
  21. Plain text templates
  22. --------------------
  23.  
  24. Here's a sample plain text template::
  25.  
  26.     from quixote.ptl import ptl_plain
  27.  
  28.     @ptl_plain
  29.     def foo(x, y = 5):
  30.         "This is a chunk of static text."
  31.         greeting = "hello world" # statement, no PTL output
  32.         print('Input values:', x, y)
  33.         z = x + y
  34.         """You can plug in variables like x (%s)
  35.     in a variety of ways.""" % x
  36.  
  37.         "\n\n"
  38.         "Whitespace is important in generated text.\n"
  39.         "z = "; z
  40.         ", but y is "
  41.         y
  42.         "."
  43.  
  44. Obviously, templates can't have docstrings, but otherwise they follow
  45. Python's syntactic rules: indentation indicates scoping, single-quoted
  46. and triple-quoted strings can be used, the same rules for continuing
  47. lines apply, and so forth.  PTL also follows all the expected semantics
  48. of normal Python code: so templates can have parameters, and the
  49. parameters can have default values, be treated as keyword arguments,
  50. etc.
  51.  
  52. The difference between a template and a regular Python function is that
  53. inside a template the result of expressions are saved as the return
  54. value of that template.  Look at the first part of the example again::
  55.  
  56.     @ptl_plain
  57.     def foo(x, y=5):
  58.         "This is a chunk of static text."
  59.         greeting = "hello world" # statement, no PTL output
  60.         print('Input values:', x, y)
  61.         z = x + y
  62.         """You can plug in variables like x (%s)
  63.     in a variety of ways.""" % x
  64.  
  65. Calling this template with ``foo(1, 2)`` results in the following
  66. string::
  67.  
  68.     This is a chunk of static text.You can plug in variables like x (1)
  69.     in a variety of ways.
  70.  
  71. Normally when Python evaluates expressions inside functions, it just
  72. discards their values, but in a plain text PTL template the value is
  73. converted to a string using ``str()`` and appended to the template's
  74. return value.  There's a single exception to this rule: ``None`` is the
  75. only value that's ever ignored, adding nothing to the output.  (If this
  76. weren't the case, calling methods or functions that return ``None``
  77. would require assigning their value to a variable.  You'd have to write
  78. ``dummy = list.sort()`` in PTL code, which would be strange and
  79. confusing.)
  80.  
  81. The initial string in a template isn't treated as a docstring, but is
  82. just incorporated in the generated output; therefore, templates can't
  83. have docstrings.  No whitespace is ever automatically added to the
  84. output, resulting in ``...text.You can ...`` from the example.  You'd
  85. have to add an extra space to one of the string literals to correct
  86. this.
  87.  
  88. The assignment to the ``greeting`` local variable is a statement, not an
  89. expression, so it doesn't return a value and produces no output.  The
  90. output from the ``print`` statement will be printed as usual, but won't
  91. go into the string generated by the template.  Quixote directs standard
  92. output into Quixote's debugging log; if you're using PTL on its own, you
  93. should consider doing something similar.  ``print`` should never be used
  94. to generate output returned to the browser, only for adding debugging
  95. traces to a template.
  96.  
  97. Inside templates, you can use all of Python's control-flow statements::
  98.  
  99.     @ptl_plain
  100.     def numbers(n):
  101.         for i in range(n):
  102.             i
  103.             " " # PTL does not add any whitespace
  104.  
  105. Calling ``numbers(5)`` will return the string ``"1 2 3 4 5 "``.  You can
  106. also have conditional logic or exception blocks::
  107.  
  108.     @ptl_plain
  109.     def international_hello(language):
  110.         if language == "english":
  111.             "hello"
  112.         elif language == "french":
  113.             "bonjour"
  114.         else:
  115.             raise ValueError("I don't speak %s" % language)
  116.  
  117.  
  118. HTML templates
  119. --------------
  120.  
  121. Since PTL is usually used to generate HTML documents, an HTML
  122. template type has been provided to make generating HTML easier.  
  123.  
  124. A common error when generating HTML is to grab data from the browser
  125. or from a database and incorporate the contents without escaping
  126. special characters such as '<' and '&'.  This leads to a class of
  127. security bugs called "cross-site scripting" bugs, where a hostile user
  128. can insert arbitrary HTML in your site's output that can link to other
  129. sites or contain JavaScript code that does something nasty (say,
  130. popping up 10,000 browser windows).
  131.  
  132. Such bugs occur because it's easy to forget to HTML-escape a string,
  133. and forgetting it in just one location is enough to open a hole.  PTL
  134. offers a solution to this problem by being able to escape strings
  135. automatically when generating HTML output, at the cost of slightly
  136. diminished performance (a few percent).
  137.  
  138. Here's how this feature works.  PTL defines a class called
  139. ``htmltext`` that represents a string that's already been HTML-escaped
  140. and can be safely sent to the client.  The function ``htmlescape(string)``
  141. is used to escape data, and it always returns an ``htmltext``
  142. instance.  It does nothing if the argument is already ``htmltext``.
  143.  
  144. If a template function is decorated with `ptl_html` instead of
  145. `ptl_plain` then the return value of the function becomes an 'htmltext'
  146. instance.  ``htmltext`` type is like the ``str`` type except that
  147. operations combining strings and ``htmltext`` instances will result in
  148. the string being passed through ``htmlescape()``.  For example::
  149.  
  150.     >>> from quixote.html import htmltext
  151.     >>> htmltext('a') + 'b'
  152.     <htmltext 'ab'>
  153.     >>> 'a' + htmltext('b')
  154.     <htmltext 'ab'>
  155.     >>> htmltext('a%s') % 'b'
  156.     <htmltext 'ab'>
  157.     >>> response = 'green eggs & ham'
  158.     >>> htmltext('The response was: %s') % response
  159.     <htmltext 'The response was: green eggs &amp; ham'>
  160.  
  161. Note that calling ``str()`` strips the ``htmltext`` type and should be
  162. avoided since it usually results in characters being escaped more than
  163. once.
  164.  
  165. It is recommended that the ``htmltext`` constructor be used as sparingly
  166. as possible.  The reason is that when using the htmltext feature of PTL,
  167. explicit calls to ``htmltext`` become the most likely source of
  168. cross-site scripting holes.  Calling ``htmltext`` is like saying "I am
  169. absolutely sure this piece of data cannot contain malicious HTML code
  170. injected by a user.  Don't escape HTML special characters because I want
  171. them."
  172.  
  173. To include literal 'htmltext' data in .ptl modules, use the HTML f-string
  174. notation (upper-case prefix).  For example::
  175.  
  176.     def format_title(title):
  177.         return F'<h1>{title}</h1>'
  178.  
  179. The literal strings using the HTML f-string notation are htmltext
  180. instances.  The ``htmltext`` type prevents their contents from being
  181. escaped by the ``htmlescape`` function.  You will only need to use
  182. ``htmltext`` when HTML markup comes from outside the template.  For
  183. example, if you want to include a file containing HTML::
  184.  
  185.     @ptl_html
  186.     def output_file():
  187.         '<html><body>' # does not get escaped
  188.         with open('myfile.html') as fp:
  189.             htmltext(fp.read())
  190.         '</body></html>'
  191.  
  192. In the common case, templates won't be dealing with HTML markup from
  193. external sources, so you can write straightforward code.  Consider
  194. this function to generate the contents of the ``HEAD`` element::
  195.  
  196.     @ptl_html
  197.     def meta_tags(title, description):
  198.         F'<title>{title}</title>'
  199.         F'<meta name="description" content="{description}">\n'
  200.  
  201. There are no calls to ``htmlescape()`` at all, but the HTML f-string
  202. literals are ``htmltext`` instances, so the data in the `title` and
  203. `description` variables will automatically be escaped::
  204.  
  205.     >>> t.meta_tags('Catalog', 'A catalog of our cool products')
  206.     <htmltext '<title>Catalog</title>
  207.       <meta name="description" content="A catalog of our cool products">\n'>
  208.     >>> t.meta_tags('Dissertation on <HEAD>',
  209.     ...             'Discusses the "LINK" and "META" tags')
  210.     <htmltext '<title>Dissertation on &lt;HEAD&gt;</title>
  211.       <meta name="description"
  212.        content="Discusses the &quot;LINK&quot; and &quot;META&quot; tags">\n'>
  213.     >>>
  214.  
  215. Note how the title and description have had HTML-escaping applied to them.
  216. (The output has been manually pretty-printed to be more readable.)
  217.  
  218. Two implementations of ``htmltext`` are provided, one written in pure
  219. Python and a second one implemented as a C extension.  Both versions
  220. have seen production use.
  221.  
  222.  
  223. PTL modules
  224. -----------
  225.  
  226. PTL templates are kept in files with the extension .ptl.  Like Python
  227. files, they are byte-compiled on import, and the byte-code is written to
  228. a compiled file with the extension ``.pyc``.  Since vanilla Python
  229. doesn't know anything about PTL, Quixote provides an import hook to let
  230. you import PTL files just like regular Python modules.  The standard way
  231. to install this import hook is by calling the ``enable_ptl()`` function::
  232.  
  233.     from quixote import enable_ptl
  234.     enable_ptl()
  235.  
  236. Once the import hook is installed, PTL files can be imported as if they
  237. were Python modules.  If all the example templates shown here were put
  238. into a file named ``foo.ptl``, you could then write Python code that did
  239. this::
  240.  
  241.     from foo import numbers
  242.     def f():
  243.         return numbers(10)
  244.  
  245. You may want to keep this little function in your ``PYTHONSTARTUP``
  246. file::
  247.  
  248.     def ptl():
  249.         from quixote import enable_ptl
  250.         enable_ptl()
  251.  
  252. This is useful if you want to interactively play with a PTL module.

Raw Paste


Login or Register to edit or fork this paste. It's free.