Skip to content

Commit a0c7d52

Browse files
committed
update docs for templetor
1 parent cbd51cb commit a0c7d52

File tree

1 file changed

+257
-29
lines changed

1 file changed

+257
-29
lines changed

docs/templating.rst

Lines changed: 257 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,64 +1,292 @@
1-
Templates
2-
=========
1+
Templating
2+
==========
33

4-
Writing HTML from inside Python can get cumbersome; it's much more fun to write Python from inside HTML. Luckily, web.py makes that pretty easy.
4+
There are almost as many Python templating systems as there are web
5+
frameworks (and, indeed, it seems like many templating systems are
6+
adopting web framework-like features). The following are the goals of `templetor`, which is the (codename of) templating system of web.py.
57

6-
Let's make a new directory for our templates (we'll call it templates). Inside, make a new file whose name ends with HTML (we'll call it index.html). Now, inside, you can just write normal HTML:
78

8-
<em>Hello</em>, world!
9-
Or you can use web.py's templating language to add code to your HTML:
9+
1. The templating system has to *look* decent. No ``<%#foo#%>`` crud.
10+
2. Reuse Python terms and semantics as much as possible.
11+
3. Expressive enough to do real computation.
12+
4. Usable for any text language, not just HTML and XML.
13+
14+
And requirements for the implementation as well:
15+
16+
4. Sandboxable so that you can let untrusted users write templates.
17+
5. Simple and fast implementation.
18+
19+
So here it is.
20+
21+
Variable substitution
22+
---------------------
23+
24+
::
25+
26+
Look, a $string.
27+
Hark, an ${arbitrary + expression}.
28+
Gawk, a $dictionary[key].function('argument').
29+
Cool, a $(limit)ing.
30+
31+
Stop, \$money isn't evaluated.
32+
33+
We use basically the same semantics as (rejected) `PEP
34+
215 <http://www.python.org/peps/pep-0215.html>`__. Variables can go
35+
anywhere in a document.
36+
37+
Newline suppression
38+
-------------------
39+
40+
::
41+
42+
If you put a backslash \
43+
at the end of a line \
44+
(like these) \
45+
then there will be no newline.
46+
47+
renders as all one line.
48+
49+
Expressions
50+
-----------
1051

1152
::
1253

13-
$def with (name)
54+
Here are some expressions:
55+
56+
$for var in iterator: I like $var!
1457

15-
$if name:
16-
I just wanted to say <em>hello</em> to $name.
58+
$if times > max:
59+
Stop! In the name of love.
1760
$else:
18-
<em>Hello</em>, world!
61+
Keep on, you can do it.
62+
63+
That's all, folks.
64+
65+
All your old Python friends are here: ``if``, ``while``, ``for``,
66+
``else``, ``break``, ``continue``, and ``pass`` also act as you'd
67+
expect. (Obviously, you can't have variables named any of these.) The
68+
Python code starts at the ``$`` and ends at the ``:``. The ``$`` has to
69+
be at the beginning of the line, but that's not such a burden because of
70+
newline suppression (above).
71+
72+
Also, we're very careful about spacing -- all the lines will render with
73+
no spaces at the beginning. (Open question: what if you want spaces at
74+
the beginning?) Also, a trailing space might break your code.
75+
76+
There are a couple changes from Python: ``for`` and ``while`` now take
77+
an ``else`` clause that gets called if the loop is never evaluated.
78+
79+
(Possible feature to add: Django-style for loop variables.)
80+
81+
Comments
82+
--------
83+
84+
::
85+
86+
$# Here's where we hoodwink the folks at home:
87+
88+
Please enter in your deets:
89+
90+
CC: [ ] $#this is the important one
91+
SSN: $#Social Security Number#$ [ ]
92+
93+
Comments start with ``$#`` and go to ``#$`` or the end of the line,
94+
whichever is first.
95+
96+
Code
97+
----
98+
99+
**NOTE: This feature has not been implemented in the current web.py
100+
implementation of templetor.**
101+
102+
::
103+
104+
Sometimes you just need to break out the Python.
105+
106+
$ mapping = {
107+
$ 'cool': ['nice', 'sweet', 'hot'],
108+
$ 'suck': ['bad', 'evil', 'awful']
109+
$ }
110+
111+
Isn't that $mapping[thought]?
112+
That's$ del mapping $ fine with me.
113+
114+
$ complicatedfunc()
115+
116+
$ for x in bugs:
117+
$ if bug.level == 'severe':
118+
Ooh, this one is bad.
119+
$ continue
120+
And there's $x...
121+
122+
**Body of loops have to be indented with exactly 4 spaces.**
123+
124+
Code begins with a ``$`` and a space and goes until the next ``$`` or
125+
the end of the line, whichever comes first. Nothing ever gets output if
126+
the first character after the ``$`` is a space (so ``complicatedfunc``
127+
above doesn't write anything to the screen like it might without the
128+
space).
129+
130+
Python integration
131+
------------------
132+
133+
A template begins with a line like this:
134+
135+
::
136+
137+
$def with (name, title, company='BigCo')
19138

20-
As you can see, the templates look a lot like Python files except for the def with statement at the top (saying what the template gets called with) and the $s placed in front of any code. Currently, the templetor requires the $def statement to be the first line of the file.
139+
which declares that the template takes those arguments. (The ``with``
140+
keyword is special, like ``def`` or ``if``.)
21141

142+
**Don't forget to put spaces in the definition**
22143

23-
Now go back to code.py. Under the first line, add:
144+
The following *will not work*:
24145

25146
::
26147

27-
render = web.template.render('templates/')
148+
$def with (name,title,company='BigCo')
28149

29-
This tells web.py to look for templates in your templates directory. Then change index.GET to:
150+
Inside Python, the template looks like a function that takes these
151+
arguments. It returns a storage object with the special property that
152+
evaluating it as a string returns the value of the body of the template.
153+
The elements in the storage object are the results of the ``def``\ s and
154+
the ``set``\ s.
155+
156+
Perhaps an example will make this clearer. Here's a template, "entry":
157+
158+
::
159+
160+
$def with (post)
161+
162+
$var title: $post.title
163+
164+
<p>$markdown(post.body)</p>
165+
166+
<p class="byline">by $post.author</p>
167+
168+
Here's another; "base":
169+
170+
::
171+
172+
$def with (self)
173+
<html><head>
174+
<title>$self.title</title>
175+
</head><body>
176+
<h1>$self.title</h1>
177+
178+
$:self
179+
</body></html>
180+
181+
Now let's say we compile both from within Python, the first as
182+
``entry``, the second as ``base``. Here's how we might use them:
183+
184+
::
185+
186+
print base( entry( post ) )
187+
188+
``entry`` takes the argument post and returns an object whose string
189+
value is a bit of HTML showing the post with its title in the property
190+
``title``. ``base`` takes this object and places the title in the
191+
appropriate place and displays the page itself in the body of the page.
192+
The Python code prints out the result.
193+
194+
*Where did ``markdown`` come from? It wasn't passed as an argument.* You
195+
can pass a list of functions and variables to the template compiler to
196+
be made globally available to templates. *Why $:self?* See below
197+
198+
Here's an example:
199+
200+
::
201+
202+
import template
203+
render = template.render('templates/')
204+
template.Template.globals['len'] = len
205+
206+
print render.base(render.message('Hello, world!'))
207+
208+
The first line imports templetor. The second says that our templates are
209+
in the directory ``templates/``. The third give all our templates access
210+
to the ``len`` function. The fourth grabs the template ``message.html``,
211+
passes it the argument ``'Hello, world!'``, passes the result of
212+
rendering it to `mcitp <http://www.buyitcert.com/mcitp.html>`__ the
213+
template ``base.html`` and prints the result. (If your templates don't
214+
end in ``.html`` or ``.xml``, templetor will still find them, but it
215+
won't do its automatic HTML-encoding.)
216+
217+
Turning Off Filter
218+
------------------
219+
220+
By default ``template.render`` will use ``web.websafe`` filter to do
221+
HTML-encoding. To turn it off, put a : after the $ as in:
222+
223+
::
224+
225+
$:form.render()
226+
227+
Output from form.render() will be displayed as is.
228+
229+
::
230+
231+
$:fooBar $# fooBar = <span>lorem ipsum</span>
232+
233+
Output from variable in template will be displayed as is.
234+
235+
Including / nesting templates
236+
-----------------------------
237+
238+
If you want to nest one template within another, you nest the
239+
``render()`` calls, and then include the variable (unfiltered) in the
240+
page. In your handler:
30241

31242
::
32243

33-
name = 'Bob'
34-
return render.index(name)
244+
print render.foo(render.bar())
35245

36-
('index' is the name of the template and 'name' is the argument passed to it)
246+
or (to make things a little more clear):
37247

38-
Visit your site and it should say hello to Bob.
248+
::
249+
250+
barhtml = render.bar()
251+
print render.foo(barhtml)
39252

40-
But let's say we want to let people enter their own name in. Replace the two lines we added above with:
253+
Then in the template ``foo.html``:
41254

42255
::
43256

44-
i = web.input(name=None)
45-
return render.index(i.name)
257+
$def with (bar)
258+
html goes here
259+
$:bar
260+
more html
46261

47-
Visit / and it should say hello to the world. Visit /?name=Joe and it should say hello to Joe.
262+
This replaces the ``$:bar`` with the output of the ``render.bar()`` call
263+
(which is why it must be ``$:``/unfiltered, so
264+
`ccnp <http://www.buyitcert.com/ccnp.html>`__ that you get un-encoded
265+
HTML (unless you want something else of course)). You can pass variables
266+
in, in the same way:
48267

49-
Of course, having that ? in the URL is kind of ugly. Instead, change your URL line at the top to:
268+
::
50269

51-
'/(.*)', 'index'
52-
and change the definition of index.GET to:
270+
print render.foo(render.bar(baz), qux)
271+
272+
In the template bar (``bar.html``):
53273

54274
::
55275

56-
def GET(self, name):
57-
return render.index(name)
276+
$def with (baz)
277+
bar stuff goes here + baz
278+
279+
In template foo (``foo.html``):
58280

59-
and delete the line setting name. Now visit /Joe and it should say hello to Joe.
281+
::
282+
283+
$def with (bar, qux)
284+
html goes here
285+
$:bar
286+
Value of qux is $qux
60287

61288
Escaping
62-
````````
289+
--------
290+
63291
web.py automatically escapes any variables used in templates, so that if for some reason name is set to a value containing some HTML, it will get properly escaped and appear as plain text. If you want to turn this off, write $:name instead of $name.
64292

0 commit comments

Comments
 (0)