Package web :: Package web :: Module template
[hide private]
[frames] | no frames]

Module template

source code


simple, elegant templating
(part of web.py)

Template design:

Template string is split into tokens and the tokens are combined into nodes. 
Parse tree is a nodelist. TextNode and ExpressionNode are simple nodes and 
for-loop, if-loop etc are block nodes, which contain multiple child nodes. 

Each node can emit some python string. python string emitted by the 
root node is validated for safeeval and executed using python in the given environment.

Enough care is taken to make sure the generated code and the template has line to line match, 
so that the error messages can point to exact line number in template. (It doesn't work in some cases still.)

Grammar:

    template -> defwith sections 
    defwith -> '$def with (' arguments ')' | ''
    sections -> section*
    section -> block | assignment | line

    assignment -> '$ ' <assignment expression>
    line -> (text|expr)*
    text -> <any characters other than $>
    expr -> '$' pyexpr | '$(' pyexpr ')' | '${' pyexpr '}'
    pyexpr -> <python expression>

Classes [hide private]
  Parser
Parser Base.
  PythonTokenizer
Utility wrapper over python tokenizer.
  DefwithNode
  TextNode
  ExpressionNode
  AssignmentNode
  LineNode
  BlockNode
  ForNode
  CodeNode
  StatementNode
  IfNode
  ElseNode
  ElifNode
  DefNode
  VarNode
  SuiteNode
Suite is a list of sections.
  ForLoop
Wrapper for expression in for stament to support loop.xxx helpers.
  ForLoopContext
Stackable context for ForLoop to support nested for loops.
  BaseTemplate
  Template
  CompiledTemplate
  GAE_Render
  Render
The most preferred way of using templates.
  render
The most preferred way of using templates.
  ParseError
  SecurityError
The template seems to be trying to do something naughty.
  SafeVisitor
Make sure code is safe by walking through the AST.
  TemplateResult
Dictionary like object for storing template output.
Functions [hide private]
 
splitline(text)
Splits the given text at newline.
source code
 
websafe(text)
Encodes `text` for raw use in HTML.
source code
 
frender(path, **keywords)
Creates a template from the given file path.
source code
 
compile_templates(root)
Compiles templates to python code.
source code
 
test()
Doctest for testing template module.
source code
Variables [hide private]
  INDENT = u' '
  STATEMENT_NODES = {'for': ForNode, 'while': BlockNode, 'if': I...
  KEYWORDS = ['pass', 'break', 'continue', 'return']
  TEMPLATE_BUILTIN_NAMES = ['dict', 'enumerate', 'float', 'int',...
  TEMPLATE_BUILTINS = {'False': False, 'None': None, 'True': Tru...
  _htmlquote_re = re.compile(r'[&<>"\']')
  _htmlquote_d = {u'"': u'&quot;', u'&': u'&amp;', u''': u'&#39;...
  ALLOWED_AST_NODES = ['Add', 'And', 'AssList', 'AssName', 'AssT...
  __package__ = 'web.web'
  name = '__import__'
Function Details [hide private]

splitline(text)

source code 

Splits the given text at newline.

>>> splitline('foo\nbar')
('foo\n', 'bar')
>>> splitline('foo')
('foo', '')
>>> splitline('')
('', '')

websafe(text)

source code 

Encodes `text` for raw use in HTML.

>>> websafe(u"<'&\">")
u'&lt;&#39;&amp;&quot;&gt;'

Unlike the websafe function in utils.py, this works with unicode text.

test()

source code 

Doctest for testing template module.

Define a utility function to run template test.

>>> class TestResult:
...     def __init__(self, t): self.t = t
...     def __getattr__(self, name): return getattr(self.t, name)
...     def __repr__(self): return repr(unicode(self))
...
>>> def t(code, **keywords):
...     tmpl = Template(code, **keywords)
...     return lambda *a, **kw: TestResult(tmpl(*a, **kw))
...

Simple tests.

>>> t('1')()
u'1\n'
>>> t('$def with ()\n1')()
u'1\n'
>>> t('$def with (a)\n$a')(1)
u'1\n'
>>> t('$def with (a=0)\n$a')(1)
u'1\n'
>>> t('$def with (a=0)\n$a')(a=1)
u'1\n'

Test complicated expressions.

>>> t('$def with (x)\n$x.upper()')('hello')
u'HELLO\n'
>>> t('$(2 * 3 + 4 * 5)')()
u'26\n'
>>> t('${2 * 3 + 4 * 5}')()
u'26\n'
>>> t('$def with (limit)\nkeep $(limit)ing.')('go')
u'keep going.\n'
>>> t('$def with (a)\n$a.b[0]')(storage(b=[1]))
u'1\n'

Test html escaping.

>>> t('$def with (x)\n$x', filename='a.html')('<html>')
u'&lt;html&gt;\n'
>>> t('$def with (x)\n$x', filename='a.txt')('<html>')
u'<html>\n'

Test if, for and while.

>>> t('$if 1: 1')()
u'1\n'
>>> t('$if 1:\n    1')()
u'1\n'
>>> t('$if 1:\n    1\\')()
u'1'
>>> t('$if 0: 0\n$elif 1: 1')()
u'1\n'
>>> t('$if 0: 0\n$elif None: 0\n$else: 1')()
u'1\n'
>>> t('$if 0 < 1 and 1 < 2: 1')()
u'1\n'
>>> t('$for x in [1, 2, 3]: $x')()
u'1\n2\n3\n'
>>> t('$def with (d)\n$for k, v in d.iteritems(): $k')({1: 1})
u'1\n'
>>> t('$for x in [1, 2, 3]:\n\t$x')()
u'    1\n    2\n    3\n'
>>> t('$def with (a)\n$while a and a.pop():1')([1, 2, 3])
u'1\n1\n1\n'

The space after : must be ignored.

>>> t('$if True: foo')()
u'foo\n'

Test loop.xxx.

>>> t("$for i in range(5):$loop.index, $loop.parity")()
u'1, odd\n2, even\n3, odd\n4, even\n5, odd\n'
>>> t("$for i in range(2):\n    $for j in range(2):$loop.parent.parity $loop.parity")()
u'odd odd\nodd even\neven odd\neven even\n'

Test assignment.

>>> t('$ a = 1\n$a')()
u'1\n'
>>> t('$ a = [1]\n$a[0]')()
u'1\n'
>>> t('$ a = {1: 1}\n$a.keys()[0]')()
u'1\n'
>>> t('$ a = []\n$if not a: 1')()
u'1\n'
>>> t('$ a = {}\n$if not a: 1')()
u'1\n'
>>> t('$ a = -1\n$a')()
u'-1\n'
>>> t('$ a = "1"\n$a')()
u'1\n'

Test comments.

>>> t('$# 0')()
u'\n'
>>> t('hello$#comment1\nhello$#comment2')()
u'hello\nhello\n'
>>> t('$#comment0\nhello$#comment1\nhello$#comment2')()
u'\nhello\nhello\n'

Test unicode.

>>> t('$def with (a)\n$a')(u'\u203d')
u'\u203d\n'
>>> t('$def with (a)\n$a')(u'\u203d'.encode('utf-8'))
u'\u203d\n'
>>> t(u'$def with (a)\n$a $:a')(u'\u203d')
u'\u203d \u203d\n'
>>> t(u'$def with ()\nfoo')()
u'foo\n'
>>> def f(x): return x
...
>>> t(u'$def with (f)\n$:f("x")')(f)
u'x\n'
>>> t('$def with (f)\n$:f("x")')(f)
u'x\n'

Test dollar escaping.

>>> t("Stop, $$money isn't evaluated.")()
u"Stop, $money isn't evaluated.\n"
>>> t("Stop, \$money isn't evaluated.")()
u"Stop, $money isn't evaluated.\n"

Test space sensitivity.

>>> t('$def with (x)\n$x')(1)
u'1\n'
>>> t('$def with(x ,y)\n$x')(1, 1)
u'1\n'
>>> t('$(1 + 2*3 + 4)')()
u'11\n'

Make sure globals are working.

>>> t('$x')()
Traceback (most recent call last):
    ...
NameError: global name 'x' is not defined
>>> t('$x', globals={'x': 1})()
u'1\n'

Can't change globals.

>>> t('$ x = 2\n$x', globals={'x': 1})()
u'2\n'
>>> t('$ x = x + 1\n$x', globals={'x': 1})()
Traceback (most recent call last):
    ...
UnboundLocalError: local variable 'x' referenced before assignment

Make sure builtins are customizable.

>>> t('$min(1, 2)')()
u'1\n'
>>> t('$min(1, 2)', builtins={})()
Traceback (most recent call last):
    ...
NameError: global name 'min' is not defined

Test vars.

>>> x = t('$var x: 1')()
>>> x.x
u'1'
>>> x = t('$var x = 1')()
>>> x.x
1
>>> x = t('$var x:  \n    foo\n    bar')()
>>> x.x
u'foo\nbar\n'

Test BOM chars.

>>> t('\xef\xbb\xbf$def with(x)\n$x')('foo')
u'foo\n'

Test for with weird cases.

>>> t('$for i in range(10)[1:5]:\n    $i')()
u'1\n2\n3\n4\n'
>>> t("$for k, v in {'a': 1, 'b': 2}.items():\n    $k $v")()
u'a 1\nb 2\n'
>>> t("$for k, v in ({'a': 1, 'b': 2}.items():\n    $k $v")()
Traceback (most recent call last):
    ...
SyntaxError: invalid syntax

Test datetime.

>>> import datetime
>>> t("$def with (date)\n$date.strftime('%m %Y')")(datetime.datetime(2009, 1, 1))
u'01 2009\n'

Variables Details [hide private]

STATEMENT_NODES

Value:
{'for': ForNode, 'while': BlockNode, 'if': IfNode, 'elif': ElifNode, '\
else': ElseNode, 'def': DefNode, 'code': CodeNode}

TEMPLATE_BUILTIN_NAMES

Value:
['dict',
 'enumerate',
 'float',
 'int',
 'bool',
 'list',
 'long',
 'reversed',
...

TEMPLATE_BUILTINS

Value:
{'False': False,
 'None': None,
 'True': True,
 '__import__': <built-in function __import__>,
 'abs': <built-in function abs>,
 'all': <built-in function all>,
 'any': <built-in function any>,
 'bool': <type 'bool'>,
...

_htmlquote_d

Value:
{u'"': u'&quot;',
 u'&': u'&amp;',
 u''': u'&#39;',
 u'<': u'&lt;',
 u'>': u'&gt;'}

ALLOWED_AST_NODES

Value:
['Add',
 'And',
 'AssList',
 'AssName',
 'AssTuple',
 'Assign',
 'AugAssign',
 'Bitand',
...