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.