Plain Text Templates

Plain text – naturally – does not provide any markup by itself. TDI accepts some simple markup, which can be used to annotate your text templates. If you’re new to TDI, please go back two chapters and learn about the basics first (annotations and basic content editing). If you’re not interested in plain text markup right now, just skip this chapter for now.

A text template looks like this:

from tdi import text
template = text.from_string("""
Hello [[name]],

[item]* some stuff[/item][:item]
[/:item]
""".strip())
template.render()

template.render() streams the result to sys.stdout by default. In contrast to HTML/XML templates, it is expected, that the markup should not be contained in the final output. Therefore, with text templates all nodes have the '-' flag set by default (which means, they are stripped).

So the output of this snippet would be:

Hello ,

* some stuff

We have no render logic specified, so the only difference to the original template is, that the markup is gone.

Basic Syntax

The text markup looks similar to HTML. The most obvious difference is, that the tags use square brackets ([ and ]) instead of angle brackets (< and >) as delimiters. Standalone tags (self-closing tags in HTML) are defined using doubled square-brackets ([[ and ]]).

Literal square brackets in the template content obviously have to be escaped. They are written as [].

Attributes

Attributes are specified using a key="value" syntax and are separated by whitespace. You can also use single quotes or no quotes at all, if the value does not contain quotes, backslashes or whitespace. Quotes within attributes need to be escaped using a backslash (\" or \'), if they match the surrounding quotes. The backslash itself is also quoted using a backslash (\\):

from tdi import text
template = text.from_string(r"""
[tdi="xxx" tdi:overlay="foo" myown="hey\\!\"" another=yo!],
""".strip())

class Model(object):
    def render_xxx(self, node):
        print node['myown']
        print node['another']

template.render_string(Model())

Note that TDI is generally very lazy, when it comes to unescaping content. So you get the raw output, when you ask for the attributes:

hey\\!\"
yo!

Use the template’s raw attribute decoder explicitly if you need the attribute value decoded and unescaped:

class Model(object):
    def render_xxx(self, node):
        decode = node.raw.decoder.attribute
        print decode(node.raw['myown']).encode('utf-8')
        print decode(node.raw['another']).encode('utf-8')

The decoder produces unicode, so for printing we encode it again. And there it is:

hey\!"
yo!

Node Name

Since the markup has no meaning otherwise, the nodes don’t have meaningful names per se. TDI uses that to allow shortening the markup. If a tag defines a name, it’s the same as defining a “tdi” attribute. That’s what we did in the initial example:

Hello [[name]],

[item]* some stuff[/item][:item]
[/:item]

If you use that feature, note that the endtags must use the same name (including flags in the same order). If you do not use that feature, the nodes simply don’t have a name and the endtags have to be empty ([/]).

Standalone Tags

Also in the initial example, we used a standalone tag already ([[name]]). Standalone tags do not have an endtag (in HTML that would be, for example, <br />). Any content you set on such a node is formally placed after the tag. The tag itself is removed by default, so effectively the tag is replaced by the content assigned later:

from tdi import text
template = text.from_string(r"""
Hello [[name]]!
""".strip())

class Model(object):
    def render_name(self, node):
        node.content = u"Andr\xe9"

template.render(Model())
Hello André!

Comments And Processing Instructions

TDI allows you to place comments inside your text templates. They begin with [# and end with #]. Comments cannot be nested. They are stripped from the output by default.

TDI also allows for generic “processing instructions”. They are delimited by [? and ?] and cannot be nested either. Processing instructions are stripped from the output by default.

Both comments and processing instructions are mechanisms to place some meta data in your template. Comments are for humans or external tools (e.g. editor tags). Processing instructions are intended for internal processing, i.e. template filters.

Character Encoding

TDI assumes the UTF-8 encoding for text templates by default. This can be changed either by passing a different encoding to the factory method or by placing an appropriate processing instruction in the template:

from tdi import text
template = text.from_string("""
[? encoding = cp1252 ?]Hello [[name]]!

Euro Sign: [[euro]]
""".strip())

class Model(object):
    def render_name(self, node):
        node.content = u"Andr\xe9"

    def render_euro(self, node):
        node.content = u"\u20ac"

print repr(template.render_string(Model()))
'Hello Andr\xe9!\n\nEuro Sign: \x80'

If you assign content, which cannot be encoded using the desired character encoding, you will get an error.