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 & ham'>
+ >>> htmltext('The response was: %s') % response
+ <htmltext 'The response was: green eggs & 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