A first crack at documenting the new PTL syntax. (quixote/doc/PTL.txt)

Neil Schemenauer <nascheme-fVcApmY9cLvQ3/1i3zOLAti2O/[email protected]> Thu, 02 Jan 2003 14:38:30 -0500
Newsgroups gmane.comp.web.quixote.cvs
Message-ID <[email protected]>
Update of /home/cvs/quixote/doc
In directory hewson:/tmp/cvs-serv13109

Modified Files:
	PTL.txt 
Log Message:
A first crack at documenting the new PTL syntax.


Index: PTL.txt
===================================================================
RCS file: /home/cvs/quixote/doc/PTL.txt,v
retrieving revision 1.13
retrieving revision 1.14
diff -u -d -r1.13 -r1.14
--- PTL.txt	2 Oct 2002 14:52:47 -0000	1.13
+++ PTL.txt	2 Jan 2003 19:38:28 -0000	1.14
@@ -1,16 +1,27 @@
 PTL: Python Template Language
 =============================
 
+Introduction
+------------
+
 PTL is the templating language used by Quixote.  PTL inverts the usual
 model used by web templating languages -- embed a real programming
 language in HTML -- by merely tweaking Python to make it easier to
 generate HTML pages (or other forms of text).  In other words, PTL is
 basically Python with a novel way to specify function return values.
-Specifically, PTL has one extra keyword -- ``template`` -- and the value
-of expressions inside templates are kept, not discarded.  Here's a
-sample template::
+Specifically, a PTL template is designated by inserting a ``[plain]`` or
+``[html]`` modifier after the function name.  The value of expressions
+inside templates are kept, not discarded.  If the type is ``[html]``
+then non-literal strings are passed through a function that escapes HTML
+special characters.
 
-    template foo (x, y = 5):
+
+Plain text templates
+--------------------
+
+Here's a sample plain text template::
+
+    def foo [plain] (x, y = 5):
         "This is a chunk of static text."
         greeting = "hello world" # statement, no PTL output
         print 'Input values:', x, y
@@ -20,26 +31,24 @@
 
         "\n\n"
         "Whitespace is important in generated text.\n"
-        "z = "
-        str(z)
+        "z = "; z
         ", but y is "
         y
         "."
 
-Templates are the PTL analogue to Python functions.  Templates are
-defined using the ``template`` keyword, obviously, and they can't have
-docstrings, but otherwise they follow Python's syntactic rules:
-indentation indicates scoping, single-quoted and triple-quoted strings
-can be used, the same rules for continuing lines apply, and so forth.
-PTL also follows all the expected semantics of normal Python code: so
-templates can have parameters, and the parameters can have default
-values, be treated as keyword arguments, etc.
+Obviously, templates can't have docstrings, but otherwise they follow
+Python's syntactic rules: indentation indicates scoping, single-quoted
+and triple-quoted strings can be used, the same rules for continuing
+lines apply, and so forth.  PTL also follows all the expected semantics
+of normal Python code: so templates can have parameters, and the
+parameters can have default values, be treated as keyword arguments,
+etc.
 
 The difference between a template and a regular Python function is that
 inside a template the result of expressions are saved as the return
 value of that template.  Look at the first part of the example again::
 
-    template foo (x, y = 5):
+    def foo [plain] (x, y = 5):
         "This is a chunk of static text."
         greeting = "hello world" # statement, no PTL output
         print 'Input values:', x, y
@@ -47,20 +56,21 @@
         """You can plug in variables like x (%s)
     in a variety of ways.""" % x
 
-Calling this template with ``foo(1,2)`` results in the following
+Calling this template with ``foo(1, 2)`` results in the following
 string::
 
     This is a chunk of static text.You can plug in variables like x (1)
     in a variety of ways.
 
 Normally when Python evaluates expressions inside functions, it just
-discards their values, but in PTL the value is converted to a string
-using ``str()`` and appended to the template's return value.  There's a
-single exception to this rule: ``None`` is the only value that's ever
-ignored, adding nothing to the output.  (If this weren't the case,
-calling methods or functions that return ``None`` would require
-assigning their value to a variable.  You'd have to write ``dummy =
-list.sort()`` in PTL code, which would be strange and confusing.)
+discards their values, but in a ``[plain]`` PTL template the value is
+converted to a string using ``str()`` and appended to the template's
+return value.  There's a single exception to this rule: ``None`` is the
+only value that's ever ignored, adding nothing to the output.  (If this
+weren't the case, calling methods or functions that return ``None``
+would require assigning their value to a variable.  You'd have to write
+``dummy = list.sort()`` in PTL code, which would be strange and
+confusing.)
 
 The initial string in a template isn't treated as a docstring, but is
 just incorporated in the generated output; therefore, templates can't
@@ -80,7 +90,7 @@
 
 Inside templates, you can use all of Python's control-flow statements::
 
-    template numbers(n):
+    def numbers [plain] (n):
         for i in range(n):
             i
             " " # PTL does not add any whitespace
@@ -88,13 +98,74 @@
 Calling ``numbers(5)`` will return the string ``"1 2 3 4 5 "``.  You can
 also have conditional logic or exception blocks::
 
-    template international_hello(language):
+    def international_hello [plain] (language):
         if language == "english":
             "hello"
         elif language == "french":
             "bonjour"
         else:
             raise ValueError, "I don't speak %s" % language
+
+
+HTML templates
+--------------
+
+Since PTL is usually used to generate HTML documents, a ``[html]``
+template type has been provided to make generating HTML easier.  When
+generating HTML, it is extremely difficult to correctly escape special
+characters.  The PTL solution to this problem is to use a separate data
+type for data that does not need to be escaped.  Any data that is not of
+this type will be converted to this type after escaping any special
+characters.
+
+In PTL, the separate data type is ``htmltext``.  The function
+``htmlescape()`` is used to escape data and it returns a ``htmltext``
+instance.   It does nothing if the argument is already ``htmltext``.
+Both ``htmltext`` and ``htmlescape`` are available in the global
+namespace of PTL modules.
+
+If a template function is declared ``[html]`` instead of ``[text]`` then
+two things happen.  First, all literal strings in the function become
+instances of ``htmltext`` instead of ``str``.  Second, the values of
+expressions are passed through ``htmlescape()`` instead of ``str()``.
+
+``htmltext`` type is like the ``str`` type except that operations
+combining strings and ``htmltext`` instances will result in the string
+being passed through ``htmlescape()``.  For example::
+
+    >>> from quixote.html import htmltext
+    >>> htmltext('a') + 'b'
+    <htmltext 'ab'>
+    >>> 'a' + htmltext('b')
+    <htmltext 'ab'>
+    >>> htmltext('a%s') % 'b'
+    <htmltext 'ab'>
+    >>> response = 'green eggs & ham'
+    <htmltext 'The response was: green eggs &amp; ham'>
+    >>> htmltext('The response was: %s') % response
+    <htmltext 'The response was: green eggs &amp; ham'>
+
+Note that calling ``str()`` strips the ``htmltext`` type and should be
+avoided since it usually results in characters being escaped more than
+once.  While ``htmltext`` behaves much like a regular string, it is
+sometimes necessary to insert a ``str()`` inside a template in order to
+obtain a genuine string.  For example, the ``re`` module requires
+genuine strings.  We have found that explict calls to ``str()`` can
+often be avoided by splitting some code out of the template into a
+helper function.
+
+It is also recommended that the ``htmltext`` constructor be used as
+sparingly as possible.  The reason is that when using the htmltext
+feature of PTL, explict calls to `htmltext`` become the most likely
+source of cross-site scripting holes.  Calling ``htmltext`` is like
+saying "I am absolutely sure this piece of data cannot contain malicious
+HTML code injected by a user.  Don't escape HTML special characters
+because I want them."
+
+
+
+PTL modules
+-----------
 
 PTL templates are kept in files with the extension .ptl.  Like Python
 files, they are byte-compiled on import, and the byte-code is written to