Template Data Interface (TDI)

TDI (/ʹtedɪ/ [1]) is a markup templating system written in python with (optional but recommended) speedup code written in C.

Unlike most templating systems TDI does not invent its own language to provide functionality [2]. Instead you simply mark the nodes you want to manipulate within the template document. The template is parsed and the marked nodes are presented to your python code, where they can be modified in any way you want.

TDI is currently available for python2 versions only (starting with python 2.7). Python 3 is not supported yet. The following alternative implementations are supported:

  • PyPy (2.0) (Python only)
  • Jython (2.7) (Python only)

Documentation

Working Example

The following code renders a calendar, i.e. days of a month specified by the caller.

The code is intended to give you a good impression of how TDI looks like and feels like. It also tries to present many of TDI‘s features at once. When you read the documentation, feel free to pick it up and play around with it.

If you don’t want to review the example now, just skip it.

layout.html

<html>
<head>
    <title tdi="title"></title><tdi tdi:overlay="->css" /></head>
<body tdi:overlay=">body">
</body>
</html>

calendar.html

<html>
<head>
<tdi tdi:overlay="<-css">
    <link ref="stylesheet" type="text/css" href="calendar.css">
<tdi tdi:overlay="->css" /></tdi>
</head>
<body tdi:overlay="<body">
<h1 tdi="title">Heading</h1>
<table class="calendar">
    <tr class="legend"><th>Mo</th><th>Di</th><th>Mi</th><th>Do</th>
        <th>Fr</th><th>Sa</th><th>So</th>
    </tr>
    <tr tdi="row"><td tdi="col"></td><td tdi=":-col">
        </td></tr><tr tdi=":-row">
    </tr>
</table>
</body>
</html>

Python Code

import datetime as _dt

from tdi import html as _html
from tdi.tools import html as _html_tools


class Model(object):
    MONTHS = u"""
        Januar Februar M\xe4rz April Mai Juni Juli August September Oktober
        November Dezember
    """.split()

    def __init__(self, year, month):
        self.first = start = _dt.date(year, month, 1)
        self.end = (start + _dt.timedelta(31)).replace(day=1)
        self.name = self.MONTHS[month - 1]
        self.year = year
        self.start = start - _dt.timedelta(start.weekday())
        self.today = _dt.date.today()

    def render_title(self, node):
        node.content = u"%s %04d" % (self.name, self.year)

    def render_row(self, node):
        weeks, rest = divmod((self.end - self.start).days, 7)
        weeks += bool(rest)
        # repeat over each displayed week (each monday)
        node.repeat(None, (self.start + _dt.timedelta(week * 7)
            for week in xrange(weeks)
        ))

    def render_col(self, node):
        monday = node.ctx[1]
        # repeat over each weekday
        node.repeat(self.repeat_col, (monday + _dt.timedelta(day)
            for day in xrange(7)
        ))

    def repeat_col(self, node, date):
        node.content = date.day
        if date.weekday() >= 5:
            _html_tools.class_add(node, u'weekend')
        if not(self.first <= date < self.end):
            _html_tools.class_add(node, u'fill')
        if date == self.today:
            _html_tools.class_add(node, u'today')

    def separate_col(self, node):
        if node.ctx[0] != 3:
            node.remove()


tpl = _html.from_files(['layout.html', 'calendar.html'])
tpl.render(Model(2014, 1))

Result

<html>
<head>
    <title>Januar 2014</title>
    <link ref="stylesheet" type="text/css" href="calendar.css">
</head>
<body>
<h1>Januar 2014</h1>
<table class="calendar">
    <tr class="legend"><th>Mo</th><th>Di</th><th>Mi</th><th>Do</th>
        <th>Fr</th><th>Sa</th><th>So</th>
    </tr>
    <tr><td class="fill">30</td><td class="fill">31</td><td>1</td><td>2</td>
        <td>3</td><td class="weekend">4</td><td class="weekend">5</td></tr>
    <tr><td>6</td><td>7</td><td>8</td><td>9</td>
        <td>10</td><td class="weekend">11</td><td class="weekend">12</td></tr>
    <tr><td>13</td><td>14</td><td>15</td><td>16</td>
        <td>17</td><td class="weekend">18</td><td class="weekend">19</td></tr>
    <tr><td>20</td><td>21</td><td>22</td><td>23</td>
        <td>24</td><td class="weekend">25</td><td class="weekend">26</td></tr>
    <tr><td>27</td><td>28</td><td>29</td><td>30</td>
        <td>31</td><td class="weekend fill">1</td><td class="weekend fill">2</td></tr>
</table>
</body>
</html>

With some simple CSS applied the table might look like this:

MoDiMiDo FrSaSo
303112 345
6789 101112
13141516 171819
20212223 242526
27282930 3112

Learn More

Kess held an introductory talk about TDI at PyCon DE 2011, which is available on youtube (German, about 30 minutes).

Rationale, Advocacy, Key Features

Here is the short variant why you should consider reading on:

TDI is pure, simple, fast and powerful.

And here’s a bit more (for the long variant have a look at the documentation):

Pure

  • Template and Logic are strictly separated. TDI is the bridge between template and rendering logic.
  • There is no templating language at all, just your markup language (e.g. HTML) and python. (There’s a markup language for plain text templates, though).
  • TDI is non-intrusive - it blends in, both on template and python side.

Simple

  • TDI has a steep learning curve. It’s easy to learn and easy to master.
  • Knowing about your programming language (Python) and knowing about your template language (like HTML) is entirely sufficient.
  • You can even use your usual tools to document and validate your code (like pylint, epydoc or an HTML validator).
  • TDI escapes your content automatically - you are safe by default.
  • TDI comes as a stand-alone package. Don’t worry about dependencies.

Fast

  • Use TDI for rapid development. It’s easy to parallelize the work on layout and logic. You can even use mock-ups as templates.
  • TDI runs with ludicrous speed.
  • Pre-rendering the less dynamic parts makes it even faster.

Powerful

  • Semantically partition your code both on template and logic side using overlays and scopes.
  • Partial rendering is supported (useful for dynamic web sites).
  • Process your templates during loading time with parser filters.
  • TDI is unicode aware and automatically emits the desired encoding.
  • TDI is designed to be highly customizable and extendable. Nearly every component can be modified or replaced.

Development Status

TDI has proven its performance and stability in high load production environments, which means, it works and it’s stable. There are still some kinks to solve and a few missing minor features to add until TDI reaches version 1.0. So, development wise it’s effectively a beta release right now.

Download

Change Log

CHANGES file

Integrity Check

There are hashes (MD5, SHA1 and SHA256) of the download packages stored in the digests file. In order to check the integrity of the downloaded file, use a tool like md5sum (or sha1sum, sha256sum accordingly), e.g.:

$ md5sum -c tdi-0.9.9.9.digests
tdi-0.9.9.9.tar.bz2: OK
tdi-0.9.9.9.tar.gz: OK
tdi-0.9.9.9.tar.lzma: OK
tdi-0.9.9.9.zip: OK

In order to check the integrity of the digest file itself, you can check the PGP signature of that file. The file is signed by André Malo, Key-ID 0x8103A37E:

$ gpg --verify tdi-0.9.9.9.digests
gpg: Signature made Thu Dec 31 19:02:04 2015 CET using DSA key ID 8103A37E
gpg: Good signature from "Andre Malo <nd@apache.org>"
gpg:                 aka "Andr\xe9\x20Malo <nd@perlig.de>"
gpg:                 aka "Andre Malo <ndparker@gmx.net>"

Vendor Packages

There’s a Gentoo Linux Ebuild available.

Get Informed About New Releases

If you want to get informed about new releases of the TDI, you may use the subscription service provided by freecode.com.

License

TDI is available under the terms and conditions of the “Apache License, Version 2.0.” You’ll find the detailed licensing terms in the root directory of the source distribution package or online at http://www.apache.org/licenses/LICENSE-2.0.

Bugs

No bugs, of course. ;-) But if you’ve found one or have an idea how to improve TDI, feel free to send a pull request on github or send a mail to <tdi-bugs@perlig.de>.

Author Information

TDI was written and is maintained by André Malo.

Acknowledgements

Various people helped developing and improving TDI itself. They are listed in alphabetical order:

  • Lumes
  • Sebastian Dauß
  • Astrid ‘Kess’ Malo
  • Jens Michlo
  • Javier ‘Jay’ Rodriguez-Stegmaier
  • Roland Sommer

Last but not least I’d like to thank the author of HTMLTemplate. At the time I started experimenting with document tree based template systems, HTMLTemplate was short-listed. However, it had a different license back then, which was unacceptable for my purposes and also missing some features, so TDI was grown in the first place.


[1]The pronunciation came up while discussing TDI at the breakfast table. Once our little daughter picked it up and asked for her “teddy...?”. [3]
[2]It does invent some markup, though. Especially for plain text templates, because they naturally do not provide their own markup system.
[3]Well, the joke is easier to understand, if you’re speaking German at your breakfast table.