256 lines
15 KiB
HTML
256 lines
15 KiB
HTML
<?xml version="1.0" encoding="us-ascii" ?>
|
|
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
|
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
|
|
<head>
|
|
<meta http-equiv="Content-Type" content="text/html; charset=us-ascii" />
|
|
<meta name="generator" content="Docutils 0.3.0: http://docutils.sourceforge.net/" />
|
|
<title>PTL: Python Template Language</title>
|
|
<link rel="stylesheet" href="default.css" type="text/css" />
|
|
</head>
|
|
<body>
|
|
<div class="document" id="ptl-python-template-language">
|
|
<h1 class="title">PTL: Python Template Language</h1>
|
|
<div class="section" id="introduction">
|
|
<h1><a name="introduction">Introduction</a></h1>
|
|
<p>PTL is the templating language used by Quixote. Most web templating
|
|
languages embed a real programming language in HTML, but PTL inverts
|
|
this model 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.</p>
|
|
<p>Specifically, a PTL template is designated by inserting a <tt class="literal"><span class="pre">[plain]</span></tt>
|
|
or <tt class="literal"><span class="pre">[html]</span></tt> modifier after the function name. The value of
|
|
expressions inside templates are kept, not discarded. If the type is
|
|
<tt class="literal"><span class="pre">[html]</span></tt> then non-literal strings are passed through a function that
|
|
escapes HTML special characters.</p>
|
|
</div>
|
|
<div class="section" id="plain-text-templates">
|
|
<h1><a name="plain-text-templates">Plain text templates</a></h1>
|
|
<p>Here's a sample plain text template:</p>
|
|
<pre class="literal-block">
|
|
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
|
|
z = x + y
|
|
"""You can plug in variables like x (%s)
|
|
in a variety of ways.""" % x
|
|
|
|
"\n\n"
|
|
"Whitespace is important in generated text.\n"
|
|
"z = "; z
|
|
", but y is "
|
|
y
|
|
"."
|
|
</pre>
|
|
<p>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.</p>
|
|
<p>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:</p>
|
|
<pre class="literal-block">
|
|
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
|
|
z = x + y
|
|
"""You can plug in variables like x (%s)
|
|
in a variety of ways.""" % x
|
|
</pre>
|
|
<p>Calling this template with <tt class="literal"><span class="pre">foo(1,</span> <span class="pre">2)</span></tt> results in the following
|
|
string:</p>
|
|
<pre class="literal-block">
|
|
This is a chunk of static text.You can plug in variables like x (1)
|
|
in a variety of ways.
|
|
</pre>
|
|
<p>Normally when Python evaluates expressions inside functions, it just
|
|
discards their values, but in a <tt class="literal"><span class="pre">[plain]</span></tt> PTL template the value is
|
|
converted to a string using <tt class="literal"><span class="pre">str()</span></tt> and appended to the template's
|
|
return value. There's a single exception to this rule: <tt class="literal"><span class="pre">None</span></tt> 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 <tt class="literal"><span class="pre">None</span></tt>
|
|
would require assigning their value to a variable. You'd have to write
|
|
<tt class="literal"><span class="pre">dummy</span> <span class="pre">=</span> <span class="pre">list.sort()</span></tt> in PTL code, which would be strange and
|
|
confusing.)</p>
|
|
<p>The initial string in a template isn't treated as a docstring, but is
|
|
just incorporated in the generated output; therefore, templates can't
|
|
have docstrings. No whitespace is ever automatically added to the
|
|
output, resulting in <tt class="literal"><span class="pre">...text.You</span> <span class="pre">can</span> <span class="pre">...</span></tt> from the example. You'd
|
|
have to add an extra space to one of the string literals to correct
|
|
this.</p>
|
|
<p>The assignment to the <tt class="literal"><span class="pre">greeting</span></tt> local variable is a statement, not an
|
|
expression, so it doesn't return a value and produces no output. The
|
|
output from the <tt class="literal"><span class="pre">print</span></tt> statement will be printed as usual, but won't
|
|
go into the string generated by the template. Quixote directs standard
|
|
output into Quixote's debugging log; if you're using PTL on its own, you
|
|
should consider doing something similar. <tt class="literal"><span class="pre">print</span></tt> should never be used
|
|
to generate output returned to the browser, only for adding debugging
|
|
traces to a template.</p>
|
|
<p>Inside templates, you can use all of Python's control-flow statements:</p>
|
|
<pre class="literal-block">
|
|
def numbers [plain] (n):
|
|
for i in range(n):
|
|
i
|
|
" " # PTL does not add any whitespace
|
|
</pre>
|
|
<p>Calling <tt class="literal"><span class="pre">numbers(5)</span></tt> will return the string <tt class="literal"><span class="pre">"1</span> <span class="pre">2</span> <span class="pre">3</span> <span class="pre">4</span> <span class="pre">5</span> <span class="pre">"</span></tt>. You can
|
|
also have conditional logic or exception blocks:</p>
|
|
<pre class="literal-block">
|
|
def international_hello [plain] (language):
|
|
if language == "english":
|
|
"hello"
|
|
elif language == "french":
|
|
"bonjour"
|
|
else:
|
|
raise ValueError, "I don't speak %s" % language
|
|
</pre>
|
|
</div>
|
|
<div class="section" id="html-templates">
|
|
<h1><a name="html-templates">HTML templates</a></h1>
|
|
<p>Since PTL is usually used to generate HTML documents, an <tt class="literal"><span class="pre">[html]</span></tt>
|
|
template type has been provided to make generating HTML easier.</p>
|
|
<p>A common error when generating HTML is to grab data from the browser
|
|
or from a database and incorporate the contents without escaping
|
|
special characters such as '<' and '&'. This leads to a class of
|
|
security bugs called "cross-site scripting" bugs, where a hostile user
|
|
can insert arbitrary HTML in your site's output that can link to other
|
|
sites or contain JavaScript code that does something nasty (say,
|
|
popping up 10,000 browser windows).</p>
|
|
<p>Such bugs occur because it's easy to forget to HTML-escape a string,
|
|
and forgetting it in just one location is enough to open a hole. PTL
|
|
offers a solution to this problem by being able to escape strings
|
|
automatically when generating HTML output, at the cost of slightly
|
|
diminished performance (a few percent).</p>
|
|
<p>Here's how this feature works. PTL defines a class called
|
|
<tt class="literal"><span class="pre">htmltext</span></tt> that represents a string that's already been HTML-escaped
|
|
and can be safely sent to the client. The function <tt class="literal"><span class="pre">htmlescape(string)</span></tt>
|
|
is used to escape data, and it always returns an <tt class="literal"><span class="pre">htmltext</span></tt>
|
|
instance. It does nothing if the argument is already <tt class="literal"><span class="pre">htmltext</span></tt>.</p>
|
|
<p>If a template function is declared <tt class="literal"><span class="pre">[html]</span></tt> instead of <tt class="literal"><span class="pre">[text]</span></tt>
|
|
then two things happen. First, all literal strings in the function
|
|
become instances of <tt class="literal"><span class="pre">htmltext</span></tt> instead of Python's <tt class="literal"><span class="pre">str</span></tt>. Second,
|
|
the values of expressions are passed through <tt class="literal"><span class="pre">htmlescape()</span></tt> instead
|
|
of <tt class="literal"><span class="pre">str()</span></tt>.</p>
|
|
<p><tt class="literal"><span class="pre">htmltext</span></tt> type is like the <tt class="literal"><span class="pre">str</span></tt> type except that operations
|
|
combining strings and <tt class="literal"><span class="pre">htmltext</span></tt> instances will result in the string
|
|
being passed through <tt class="literal"><span class="pre">htmlescape()</span></tt>. For example:</p>
|
|
<pre class="literal-block">
|
|
>>> 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: %s') % response
|
|
<htmltext 'The response was: green eggs &amp; ham'>
|
|
</pre>
|
|
<p>Note that calling <tt class="literal"><span class="pre">str()</span></tt> strips the <tt class="literal"><span class="pre">htmltext</span></tt> type and should be
|
|
avoided since it usually results in characters being escaped more than
|
|
once. While <tt class="literal"><span class="pre">htmltext</span></tt> behaves much like a regular string, it is
|
|
sometimes necessary to insert a <tt class="literal"><span class="pre">str()</span></tt> inside a template in order
|
|
to obtain a genuine string. For example, the <tt class="literal"><span class="pre">re</span></tt> module requires
|
|
genuine strings. We have found that explicit calls to <tt class="literal"><span class="pre">str()</span></tt> can
|
|
often be avoided by splitting some code out of the template into a
|
|
helper function written in regular Python.</p>
|
|
<p>It is also recommended that the <tt class="literal"><span class="pre">htmltext</span></tt> constructor be used as
|
|
sparingly as possible. The reason is that when using the htmltext
|
|
feature of PTL, explicit calls to <tt class="literal"><span class="pre">htmltext</span></tt> become the most likely
|
|
source of cross-site scripting holes. Calling <tt class="literal"><span class="pre">htmltext</span></tt> 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."</p>
|
|
<p>Note that literal strings in template functions declared with
|
|
<tt class="literal"><span class="pre">[html]</span></tt> are htmltext instances, and therefore won't be escaped.
|
|
You'll only need to use <tt class="literal"><span class="pre">htmltext</span></tt> when HTML markup comes from
|
|
outside the template. For example, if you want to include a file
|
|
containing HTML:</p>
|
|
<pre class="literal-block">
|
|
def output_file [html] ():
|
|
'<html><body>' # does not get escaped
|
|
htmltext(open("myfile.html").read())
|
|
'</body></html>'
|
|
</pre>
|
|
<p>In the common case, templates won't be dealing with HTML markup from
|
|
external sources, so you can write straightforward code. Consider
|
|
this function to generate the contents of the <tt class="literal"><span class="pre">HEAD</span></tt> element:</p>
|
|
<pre class="literal-block">
|
|
def meta_tags [html] (title, description):
|
|
'<title>%s</title>' % title
|
|
'<meta name="description" content="%s">\n' % description
|
|
</pre>
|
|
<p>There are no calls to <tt class="literal"><span class="pre">htmlescape()</span></tt> at all, but string literals
|
|
such as <tt class="literal"><span class="pre"><title>%s</title></span></tt> have all be turned into <tt class="literal"><span class="pre">htmltext</span></tt>
|
|
instances, so the string variables will be automatically escaped:</p>
|
|
<pre class="literal-block">
|
|
>>> t.meta_tags('Catalog', 'A catalog of our cool products')
|
|
<htmltext '<title>Catalog</title>
|
|
<meta name="description" content="A catalog of our cool products">\n'>
|
|
>>> t.meta_tags('Dissertation on <HEAD>',
|
|
... 'Discusses the "LINK" and "META" tags')
|
|
<htmltext '<title>Dissertation on &lt;HEAD&gt;</title>
|
|
<meta name="description"
|
|
content="Discusses the &quot;LINK&quot; and &quot;META&quot; tags">\n'>
|
|
>>>
|
|
</pre>
|
|
<p>Note how the title and description have had HTML-escaping applied to them.
|
|
(The output has been manually pretty-printed to be more readable.)</p>
|
|
<p>Once you start using <tt class="literal"><span class="pre">htmltext</span></tt> in one of your templates, mixing
|
|
plain and HTML templates is tricky because of <tt class="literal"><span class="pre">htmltext</span></tt>'s automatic
|
|
escaping; plain templates that generate HTML tags will be
|
|
double-escaped. One approach is to just use HTML templates throughout
|
|
your application. Alternatively you can use <tt class="literal"><span class="pre">str()</span></tt> to convert
|
|
<tt class="literal"><span class="pre">htmltext</span></tt> instances to regular Python strings; just be sure the
|
|
resulting string isn't HTML-escaped again.</p>
|
|
<p>Two implementations of <tt class="literal"><span class="pre">htmltext</span></tt> are provided, one written in pure
|
|
Python and a second one implemented as a C extension. Both versions
|
|
have seen production use.</p>
|
|
</div>
|
|
<div class="section" id="ptl-modules">
|
|
<h1><a name="ptl-modules">PTL modules</a></h1>
|
|
<p>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
|
|
a compiled file with the extension <tt class="literal"><span class="pre">.pyc</span></tt>. Since vanilla Python
|
|
doesn't know anything about PTL, Quixote provides an import hook to let
|
|
you import PTL files just like regular Python modules. The standard way
|
|
to install this import hook is by calling the <tt class="literal"><span class="pre">enable_ptl()</span></tt> function:</p>
|
|
<pre class="literal-block">
|
|
from quixote import enable_ptl
|
|
enable_ptl()
|
|
</pre>
|
|
<p>(Note: if you're using ZODB, always import ZODB <em>before</em> installing the
|
|
PTL import hook. There's some interaction which causes importing the
|
|
TimeStamp module to fail when the PTL import hook is installed; we
|
|
haven't debugged the problem. A similar problem has been reported for
|
|
BioPython and win32com.client imports.)</p>
|
|
<p>Once the import hook is installed, PTL files can be imported as if they
|
|
were Python modules. If all the example templates shown here were put
|
|
into a file named <tt class="literal"><span class="pre">foo.ptl</span></tt>, you could then write Python code that did
|
|
this:</p>
|
|
<pre class="literal-block">
|
|
from foo import numbers
|
|
def f():
|
|
return numbers(10)
|
|
</pre>
|
|
<p>You may want to keep this little function in your <tt class="literal"><span class="pre">PYTHONSTARTUP</span></tt>
|
|
file:</p>
|
|
<pre class="literal-block">
|
|
def ptl():
|
|
try:
|
|
import ZODB
|
|
except ImportError:
|
|
pass
|
|
from quixote import enable_ptl
|
|
enable_ptl()
|
|
</pre>
|
|
<p>This is useful if you want to interactively play with a PTL module.</p>
|
|
</div>
|
|
</div>
|
|
</body>
|
|
</html>
|