The previous article, Web.py source code analysis: Templates (1), explained how a template for Web.py works in general. This article focuses on how the syntax supported by the web.py template translates to generate the __template__ function.
The correlation between the web.py template syntax and the __template__() function
This chapter lists the contents of the template as well as the contents of the converted __template__() function, along with any necessary text. The name of the template is always hello.html.
Pure string
Content of the template
hello, world
The function content
def __template__():
__lineoffset__ = -5
loop = ForLoop()
self = TemplateResult(); extend_ = self.extend
extend_([u'hello, world\n'])
return self
def with
Content of the template
$def with (name, value=[], *args, **kargs)
hello, $name
The function content
def __template__ (name, value=[], *args, **kargs):
__lineoffset__ = -4
loop = ForLoop()
self = TemplateResult(); extend_ = self.extend
extend_([u'hello, ', escape_(name, True), u'\n'])
return self
As you can see from the generated function, the def with syntax produces a list of arguments to the __template__() function.
Expression substitution
Content of the template
$def with (name, value)
$name
${name + value}
$(name + value)ing.
$name[value].function()
The function content
def __template__ (name, value):
__lineoffset__ = -4
loop = ForLoop()
self = TemplateResult(); extend_ = self.extend
extend_([escape_(name, True), u'\n'])
extend_([escape_((name + value), True), u'\n'])
extend_([escape_((name + value), True), u'ing.\n'])
extend_([escape_(name[value].function(), True), u'\n'])
return self
The replacement of an expression is the execution of the expression (the corresponding code for the expression) and the result is added to the TemplateResult instance.
The assignment
Content of the template
$def with (name, func)
$ name1 = name
$ name2 = func()
The function content
def __template__ (name, func):
__lineoffset__ = -4
loop = ForLoop()
self = TemplateResult(); extend_ = self.extend
name1 = name
name2 = func()
return self
In essence, the conversion is an assignment statement in Python code.
Content filtering
Content of the template
$def with (name)
$name
$:name
The function content
def __template__ (name):
__lineoffset__ = -4
loop = ForLoop()
self = TemplateResult(); extend_ = self.extend
extend_([escape_(name, True), u'\n'])
extend_([escape_(name, False), u'\n'])
return self
From the generated code, the difference between using the filtering syntax or not is passing in the second argument to the excape_ function, which is really just a string handling function, but more on that later.
(newline suppression)
Content of the template
$def with (name, func)
$name
hello, \
$name \
!
$func(1, 2, 3, 4, 5)
The function content
def __template__ (name, func):
__lineoffset__ = -4
loop = ForLoop()
self = TemplateResult(); extend_ = self.extend
extend_([escape_(name, True), u'\n'])
extend_([u'hello, '])
extend_([escape_(name, True), u' '])
extend_([u'!\n'])
extend_([escape_(func(1, 2, 3, 4, 5), True), u'\n'])
return self
From the point of view of the result, the line breaks in the template are simply to prevent the insertion of a redundant newline character into the result. In addition, there is no support for line breaking in the middle of an expression. For example, you can’t write a two-line argument list to the func function in a template.
The $sign
Content of the template
$$
The function content
def __template__():
__lineoffset__ = -5
loop = ForLoop()
self = TemplateResult(); extend_ = self.extend
extend_([u'$', u'\n'])
return self
annotation
Content of the template
$# comment line
hello, world.
The function content
def __template__():
__lineoffset__ = -5
loop = ForLoop()
self = TemplateResult(); extend_ = self.extend
extend_([u'\n'])
extend_([u'hello, world.\n'])
return self
Lines commented in the template have only one newline character in the generated function.
Control structure
The for loop
Content of the template
$for i in range(10):
I like $i
The function content
def __template__():
__lineoffset__ = -5
loop = ForLoop()
self = TemplateResult(); extend_ = self.extend
for i in loop.setup(range(10)):
extend_([u'I like ', escape_(i, True), u'\n'])
return self
The for loop in the template is converted to a for loop in the code, and the variable loop is used (we’ll look at the ForLoop object later).
The while loop
Content of the template
$def with (name_list)
$while name_list:
hello, $name_list.pop()
The function content
def __template__ (name_list):
__lineoffset__ = -4
loop = ForLoop()
self = TemplateResult(); extend_ = self.extend
extend_([u'\n'])
while name_list:
extend_([u'hello, ', escape_(name_list.pop(), True), u'\n'])
return self
Note the difference with the for loop, which does not use the loop variable.
The loop variable of the for loop
Content of the template
$def with (name_list)
$for name in name_list:
$loop.index
$loop.index0
$loop.first
$loop.last
$loop.odd
$loop.even
$loop.parity
$loop.parent
hello, $name
$for i in range(10):
$for name in name_list:
$loop.parent
hello, $name
The function content
def __template__ (name_list):
__lineoffset__ = -4
loop = ForLoop()
self = TemplateResult(); extend_ = self.extend
for name in loop.setup(name_list):
extend_([escape_(loop.index, True), u'\n'])
extend_([escape_(loop.index0, True), u'\n'])
extend_([escape_(loop.first, True), u'\n'])
extend_([escape_(loop.last, True), u'\n'])
extend_([escape_(loop.odd, True), u'\n'])
extend_([escape_(loop.even, True), u'\n'])
extend_([escape_(loop.parity, True), u'\n'])
extend_([escape_(loop.parent, True), u'\n'])
extend_([u'hello, ', escape_(name, True), u'\n'])
extend_([u'\n'])
for i in loop.setup(range(10)):
for name in loop.setup(name_list):
extend_([escape_(loop.parent, True), u'\n'])
extend_([u'hello, ', escape_(name, True), u'\n'])
return self
This shows the members of the loop variable and the use of nested loops.
if-else
Content of the template
$def with (name_list)
$if name_list:
$len(name_list)
$else:
0
The function content
def __template__ (name_list):
__lineoffset__ = -4
loop = ForLoop()
self = TemplateResult(); extend_ = self.extend
if name_list:
extend_([escape_(len(name_list), True), u'\n'])
else:
extend_([u'0\n'])
return self
The elif statement is also supported.
The function definitions
Content of the template
$def with (name_list)
$def hello(name):
hello, $name
$def hello_to_all(nlist):
$for each in nlist:
$hello(each)
$hello_to_all(name_list)
The function content
def __template__ (name_list):
__lineoffset__ = -4
loop = ForLoop()
self = TemplateResult(); extend_ = self.extend
extend_([u'\n'])
__lineoffset__ -= 3
def hello(name):
self = TemplateResult(); extend_ = self.extend
extend_([u'hello, ', escape_(name, True), u'\n'])
extend_([u'\n'])
return self
__lineoffset__ -= 3
def hello_to_all(nlist):
self = TemplateResult(); extend_ = self.extend
for each in loop.setup(nlist):
extend_([escape_(hello(each), True), u'\n'])
extend_([u'\n'])
return self
extend_([escape_(hello_to_all(name_list), True), u'\n'])
return self
Template support for functions is essentially defining inner functions and calling them, and each inner function returns an instance of the TemplateResult.
code
Content of the template
$def with (name_list)
$code:
new_list = [x.upper() for x in name_list]
def hello(name):
return "hello, %s" % (name)
more_new_list = []
for each in new_list:
more_new_list.append(hello(each))
$hello("everybody")
$len(more_new_list)
The function content
def __template__ (name_list):
__lineoffset__ = -4
loop = ForLoop()
self = TemplateResult(); extend_ = self.extend
extend_([u'\n'])
new_list = [x.upper() for x in name_list]
def hello(name):
return "hello, %s" % (name)
more_new_list = []
for each in new_list:
more_new_list.append(hello(each))
extend_([escape_(hello("everybody"), True), u'\n'])
extend_([escape_(len(more_new_list), True), u'\n'])
return self
The syntax of code is a bit complicated, but internally it is used to define the raw Python code, which has the following features:
-
Functions defined within code are also internal functions that can be called elsewhere in the template, but do not return the results stored in the TemplateResult instance.
-
Variables defined in code are treated as local variables to the __template__() function and can be called from elsewhere in the template.
Note that the print statement is not used in the code block to print output (although this is disabled by default).
var
Content of the template
$def with (name_list)
$var title: hi
$var title2: "hi"
$var name: $name_list[0]
$var name2: name_list[0]
The function content
def __template__ (name_list):
__lineoffset__ = -4
loop = ForLoop()
self = TemplateResult(); extend_ = self.extend
self['title'] = join_(u'hi')
self['title2'] = join_(u'"hi"')
self['name'] = join_(escape_(name_list[0], True))
self['name2'] = join_(u'name_list[0]')
return self
Var is used to set properties for the template, which, from the generated code, is for the TemplateResult instance. There are some key details in the template content above:
-
The content after the colon in var name: value is treated as a string by default and can be left out of quotation marks. If so, the quotation marks will also be part of the content.
-
If you want to access some variable values after the colon, you need to use the $prefix.
conclusion
I have written these correlations between the syntax supported by the web.py template and the generated code. I hope this will help you understand the syntax of the template, understand the purpose of each syntax, and avoid the pitfalls.