pyTenjin User's Guide

> # pyTenjin User's Guide

> release: 0.8.0
> last update: $Date$

> Table of Contents:

> - Introduction
>   - Overview
>   - Features
>   - Comparison with other solutions
>   - Benchmark
> - Installation
> - Designer's Guide
>   - Notation
>   - Embedded Statement Styles
>   - Convert into Python Code
>   - Syntax Checking
>   - HTML Helper Functions
>   - Context Data File
>   - Command-line Context Data
>   - Nested Template
>   - Layout Template
>   - Capturing
>   - Template Arguments
>   - Preprocessing
>   - Template Encoding
>   - Other Options
> - Developer's Guide
>   - An Example
>   - Class tenjin.Template
>   - Class tenjin.Engine
>   - Template Caching
>   - Variables and Functions
>   - HTML Helper Functions
>   - Template Encoding
>     - Python 2.x
>     - Python 3.0
>   - Add Your Helper Functions
>   - Other Topics

> ## Introduction

> ### Overview

> pyTenjin is a very fast and lightweight template engine based on embedded Python. You can embed Python statements and expressions into your text file. pyTenjin converts it into Python script and evaluate it.

> The following is an example of pyTenjin.

> File 'ex.pyhtml':

>     Hello #{name}!
>     <ul>
>     <?py for item in items: ?>
>      <li>${item}</li>
>     <?py #end ?>
>     </ul>

> Here is the notation:

> `<?py ... ?>`
>   Python statements.
> `#{...}`
>   Python expression.
> `${...}`
>   Python expression (HTML escaped)
> Result of covertion into Python script:

>     $ pytenjin -s ex.pyhtml
>     _buf = []; _buf.extend(('''Hello ''', to_str(name), '''!
>     <ul>\n''', ));
>     for item in items:
>         _buf.extend((''' <li>''', escape(to_str(item)), '''</li>\n''', ));
>     #end
>     _buf.extend(('''</ul>\n''', ));
>     print ''.join(_buf)

> Output of execution with context data:

>     $ pytenjin -c "name='World'; items=['<AAA>','B&B','\"CCC\"']" ex.pyhtml
>     Hello World!
>     <ul>
>      <li>&lt;AAA&gt;</li>
>      <li>B&amp;B</li>
>      <li>&quot;CCC&quot;</li>
>     </ul>

> Example of Python script

>     import tenjin
>     from tenjin.helpers import *   # or escape, to_str
>     engine = tenjin.Engine()
>     context = { 'name': 'World', 'items': ['<AAA>', 'B&B', '"CCC"'] }
>     output = engine.render('ex.pyhtml', context)
>     print output,

> ### Features

> pyTenjin has the following features:

> - pyTenjin runs very fast. It works about twice as fast as any other solution.
> - pyTenjin doesn't break HTML design because it uses XML Processing Instructions (PI) as embedded notation for Python statements.
> - pyTenjin is secure because it supports escaping expression value by default.
> - pyTenjin is small and lightweight. It is very easy to include pyTenjin into your application.
> - pyTenjin supports auto caching of converted Python code.
> - pyTenjin supports partial template and layout template. These are very useful especially for web application.
> - pyTenjin supports partial capturing of template.
> - pyTenjin can load YAML file as context data. Using pyTenjin, it is able to separate template file and data file.
> - pyTenjin now supports Goole AppEngine. See FAQ for details.

> ### Comparison with other solutions

> pyTenjin has advantages compared with other solutions (including other language solutions):

> - Easy to design -- JSP, ePerl, or eRuby breaks HTML design because they use '`<% ... %>`' as embedded notation which is not valid in HTML. pyTenjin doesn't break HTML desgin because it uses Processing Instructions (PI) as embedded notation which is valid in HTML.
> - Easy to write -- In PHP, it is a little bother to write embedded expression because the notation '`<?php echo $value; ?>`' is a little long, and very bother to embed escaped expression because '`<?php echo htmlspecialchars($value); ?>`' is very long. In pyTenjin, these are very easy to write because embedded expression notations ('`#{value}`' and '`${value}`') are very short.
> - Easy to learn -- Zope DTML, PageTemplate, Django Template, and other template systems are hard to learn because they are large, highly functinal, and based on non-Python syntax. pyTenjin is very easy to learn if you already know Python language because it is very small and you can embed any Python code into HTML template.

> ### Benchmark

> Benchmark script is contained in pyTenjin archive. The following is an example of benchmark.

> MacOS X 10.4 Tiger, Intel CoreDuo 1.83GHz, Memory 2GB

>     $ cd pytenjin-X.X.X/benchmark
>     $ python -V
>     Python 2.5.1
>     $ python bench.py -q -n 10000
>     Compiling bench_cheetah.tmpl -> bench_cheetah.py (backup bench_cheetah.py.bak)
>     *** ntimes=10000
>                                utime      stime      total       real
>     tenjin                   6.47000    0.49000    6.96000    6.98909
>     tenjin-reuse             5.54000    0.06000    5.61000    5.63055
>     tenjin-nocache          20.14000    0.41000   20.55000   20.60475
>     django                  69.99000    1.34000   71.33000   71.57211
>     django-reuse            58.92000    0.88000   59.80000   59.94480
>     cheetah                 20.33000    0.03000   20.36000   20.41416
>     cheetah-reuse           19.80000    0.02000   19.82000   19.86858
>     myghty                 106.25000    1.63000  107.88000  108.16097
>     myghty-reuse            18.70000    0.60000   19.30000   19.35395
>     kid                    379.64000    0.60000  380.24000  381.11728
>     kid-reuse              378.52000    0.44000  378.96000  379.64911
>     genshi                 557.29000    3.00000  560.30000  561.71955
>     genshi-reuse           270.47000    1.22000  271.69000  272.26885
>     mako                    16.82000    0.96000   17.78000   18.36388
>     mako-reuse              13.47000    0.02000   13.49000   13.51232
>     mako-nocache           236.10000    1.67000  237.77000  238.38705
>     templetor              424.03000    4.15000  428.19000  429.59667
>     templetor-reuse         61.46000    0.07000   61.53000   61.68483

> Versions:

> - Python 2.5.1
> - Tenjin 0.6.1
> - Django 0.95
> - Cheetah 2.0
> - Myghty 1.1
> - Kid 0.9.6
> - Genshi 0.4.4
> - Mako 0.1.9
> - Templetor (web.py) 0.22

> This shows the followings.

> - pyTenjin is the fastest template engine.
> - Cheetah's performance is good.
> - Myghty's performance is good for mod_python, but is bad for CGI program.
> - Django's performance is not good.
> - Kid's performance is worse. It is too slow.
> - Genshi's performance is also worse. It is faster than Kid, but slower than others.
> - Mako's performance is very good when module caching is enabled.
> - Templetor's performance is not good for mod_python and worse for CGI program.

> ## Installation

> pyTenjin works on CPython (2.3 or higher, 3.0 supported).

> 1. Download pyTenjin-X.X.X.tar.gz and extract it.
> 2. Just type '`python setup.py install`' with administrator or root user, or copy 'lib/tenjin.py' and 'bin/pytenjin' into proper directory if you don't have administrator or root priviledge.
> 3. (Optional) Install PyYAML.

> ## Designer's Guide

> This section shows how to use pyTenjin for designer.

> If you want to know how to use pyTenjin in your program, see Developer's Guide section.

> ### Notation

> The following is the notation of pyTenjin.

> - '`<?py ... ?>`' : Python statement
> - '`#{...}`' : Python expression
> - '`${...}`' : Python expression with HTML escape
> File 'example1.pyhtml':

>     <table>
>       <tbody>
>     <?py i = 0 ?>
>     <?py for item in ['<foo>', 'bar&bar', '"baz"']: ?>
>     <?py     i += 1 ?>
>         <tr>
>           <td>#{item}</td>
>           <td>${item}</td>
>         </tr>
>     <?py #end ?>
>       <tbody>
>     </table>

> Notice that it is required to add '`<?py #end ?>`' line because Python doesn't have block-end mark. Block-end mark line tells pytenjin command the position of end of block. It is able to use '`#endfor`', '`#`', '`pass`', and so on as block-end mark.

> The following is the result of executing 'example1.pyhtml'.

> Result:

>     $ pytenjin example1.pyhtml
>     <table>
>       <tbody>
>         <tr>
>           <td><foo></td>
>           <td>&lt;foo&gt;</td>
>         </tr>
>         <tr>
>           <td>bar&bar</td>
>           <td>bar&amp;bar</td>
>         </tr>
>         <tr>
>           <td>"baz"</td>
>           <td>&quot;baz&quot;</td>
>         </tr>
>       <tbody>
>     </table>

> (Experimental) Since version 0.8.0, it is allowed to indent Python statements. pyTenjin inserts dummy if-statement automatically when the first Python statement is indented.

> For example:

>     <html>
>       <body>
>         <ul>
>           <?py for item in items: ?>
>           <li>${item}</li>
>           <?py #end ?>
>         </ul>
>       </body>
>     </html>

> The above is regarded as:

>     <html>
>       <body>
>         <ul>
>     <?py if True: ?>
>           <?py for item in items: ?>
>           <li>${item}</li>
>           <?py #end ?>
>         </ul>
>       </body>
>     </html>

> ### Embedded Statement Styles

> Two styles of embedded statement are available. The first style is shown in the previous section. In this style, it is able to put indent spaces before '`<?py`' like the following:

> File 'example1a.pyhtml':

>     <table>
>       <tbody>
>     <?py i = 0 ?>
>     <?py for item in ['<foo>', 'bar&bar', '"baz"']: ?>
>         <?py i += 1 ?>
>         <tr>
>           <td>#{item}</td>
>           <td>${item}</td>
>         </tr>
>     <?py #end ?>
>       </tbody>
>     </table>

> The second style is shown in the following. This style is convenient for a lot of statements.

> File 'example1b.pyhtml':

>     <table>
>       <tbody>
>     <?py
>     i = 0
>     for item in ['<foo>', 'bar&bar', '"baz"']:
>         i += 1
>     ?>
>         <tr>
>           <td>#{item}</td>
>           <td>${item}</td>
>         </tr>
>     <?py
>     #end
>     ?>
>       </tbody>
>     </table>

> It is able to mix two styles in a file, but the following styles are NOT available.

> Invalid example #1:

>     <ul>
>     <?py i = 0
>          for item in ['<foo>', 'bar&bar', '"baz"']:
>          i += 1 ?>
>      <li>#{item}
>          ${item}</li>
>     <?py #end ?>
>     </ul>

> Invalid example #2:

>     <ul>
>     <?py
>         i = 0
>         for item in ['<foo>', 'bar&bar', '"baz"']:
>             i += 1
>     ?>
>      <li>#{item}
>          ${item}</li>
>     <?py
>         #end
>     ?>
>     </ul>

> ### Convert into Python Code

> Command-line option '-s' converts embedded files into Python code.

> Result:

>     $ pytenjin -s example1.pyhtml
>     _buf = []; _buf.extend(('''<table>
>       <tbody>\n''', ));
>     i = 0
>     for item in ['<foo>', 'bar&bar', '"baz"']:
>         i += 1
>         _buf.extend(('''    <tr>
>           <td>''', to_str(item), '''</td>
>           <td>''', escape(to_str(item)), '''</td>
>         </tr>\n''', ));
>     #end
>     _buf.extend(('''  <tbody>
>     </table>\n''', ));
>     print ''.join(_buf)

> - Variable `_buf` is a list.
> - Function `to_str()` (= `tenjin.helpers.to_str()`) converts value into string except None is converted into `""` (empty string). Command-line option `--tostrfunc=func` makes pytenjin to use `func()` instead of `to_str()`.
> - Function `escape()` (= `tenjin.escape()`) escapes `'& < > "'` into `'&amp; &lt; &gt; &quot;'`(*1). Command-line option `--escapefunc=func` makes pytenjin to use `func()` instead of `escape()`.
> - End-of-line character ("\n" or "\r\n") is automatically detected by pyTenjin.

> '`_buf = [];`' is called as preamble and '`print ''.join(_buf)`' is called as postamble. Command-line option '`-b`' removes preamble and postamble.

> File 'example2.pyhtml'

>     <?py for i in [1, 2, 3]: ?>
>     <p>#{i}</p>
>     <?py #end ?>

> Result:

>     $ pytenjin -s example2.pyhtml
>     _buf = []
>     for i in [1, 2, 3]:
>         _buf.extend(('''<p>''', to_str(i), '''</p>\n''', ));
>     #end
>     print ''.join(_buf)
>     $ pytenjin -sb example2.pyhtml
>     for i in [1, 2, 3]:
>         _buf.extend(('''<p>''', to_str(i), '''</p>\n''', ));
>     #end

> Command-line option '-S' also show converted Python code but it doesn't print text part. This is useful to check Python code for debugging.

> Result:

>     $ pytenjin -S example1.pyhtml
>     _buf = [];

>     i = 0
>     for item in ['<foo>', 'bar&bar', '"baz"']:
>         i += 1

>               to_str(item);
>               escape(to_str(item));

>     #end

>     print ''.join(_buf)

> In addition, the following command-line options are available.

> -N
>   Add line number.
> -X
>   Delete expressions.
> -C
>   Remove empty lines (compact-mode).
> -U
>   Compress empty lines to a line (uniq-mode).
> Result:

>     $ pytenjin -SUNX example1.pyhtml
>         1:  _buf = [];

>         3:  i = 0
>         4:  for item in ['<foo>', 'bar&bar', '"baz"']:
>         5:      i += 1

>        10:  #end

>        13:  print ''.join(_buf)

> (*1)
>   Difference between `escape()` and `cgi.escape()` is that the former escapes double-quotation mark into '`&quot;`' and the latter doesn't.

> ### Syntax Checking

> Command-line option '`-z`' checks syntax error in embedded Python code.

> File example3.pyhtml:

>     <ul>
>     <?py for item in items: ?>
>      <li>${item}</li>
>     <?py   #end ?>
>     </ul>

> Result:

>     $ pytenjin -z example3.pyhtml
>     example3.pyhtml:4:3: unindent does not match any outer indentation level
>       4:   #end
>            ^

> Error message is the same format as gcc compiler or java compiler. Error jump in Emacs or other editor is available.

> Command-line option '-q' (quiet-mode) prints nothing if it has no errors.

> ### HTML Helper Functions

> pyTenjin provides small but useful HTML helper functions. See this section for details.

> example of HTML helper functions:

>     <table>
>     <?py cycle = new_cycle('odd', 'even') ?>
>     <?py curr_id = params['item_id'] ?>
>     <?py for item in items: ?>
>       <tr class="#{cycle()}" #{tagattrs(style=(item.readonly and 'color:gray'))}>
>         <td><input type="checkbox" name="items" value="#{item.id}" #{checked(curr_id==item.id)} /></td>
>         <td>${item.name}</td>
>       </tr>
>     <?py #endfor ?>
>     </table>

> output example:

>     <table>
>       <tr class="odd" >
>         <td><input type="checkbox" name="items" value="1"  /></td>
>         <td>Foo</td>
>       </tr>
>       <tr class="even"  style="color:gray">
>         <td><input type="checkbox" name="items" value="2"  checked="checked" /></td>
>         <td>Bar</td>
>       </tr>
>     </table>

> ### Context Data File

> pyTenjin allows you to specify context data by YAML file or Python script.

> File 'example4.pyhtml':

>     <p>
>       ${text}
>       #{num}
>       #{flag}
>     </p>

>     <?py for item in items: ?>
>     <p>${item}</p>
>     <?py #end ?>

>     <?py for key, value in hash.items(): ?>
>     <p>#{key} = ${value}</p>
>     <?py #end ?>

> File 'datafile.yaml':

>     text:   foo
>     num:    3.14
>     flag:   yes
>     items:
>       - foo
>       - bar
>       - baz
>     hash:
>       x: 1
>       y: 2

> Result:

>     $ pytenjin -f datafile.yaml example4.pyhtml
>     <p>
>       foo
>       3.14
>       True
>     </p>

>     <p>foo</p>
>     <p>bar</p>
>     <p>baz</p>

>     <p>y = 2</p>
>     <p>x = 1</p>

> File 'datafile.py':

>     text  = "foo"
>     num   = 3.14
>     flag  = True
>     items = ["foo", "bar", "baz"]
>     hash  = {"x":1, "y":2}

> Result:

>     $ pytenjin -f datafile.py example4.pyhtml
>     <p>
>       foo
>       3.14
>       True
>     </p>

>     <p>foo</p>
>     <p>bar</p>
>     <p>baz</p>

>     <p>y = 2</p>
>     <p>x = 1</p>

> You must install PyYAML if you want to use YAML-format context data file.

> ### Command-line Context Data

> Command-line option '`-c`' specifies context data in YAML format or Python code.

> File 'example5.pyhtml':

>     text:  #{text}
>     items:
>     <?py for item in items: ?>
>       - #{item}
>     <?py #end ?>
>     hash:
>     <?py for key, val in hash.items(): ?>
>       #{key}: #{val}
>     <?py #end ?>

> Result of context data in python code:

>     $ pytenjin -c 'text="foo"; items=["a","b","c"]; hash={"x":1,"y":2}' example5.pyhtml
>     text:  foo
>     items:
>       - a
>       - b
>       - c
>     hash:
>       y: 2
>       x: 1

> Result of context data in yaml format:

>     $ pytenjin -c '{text: foo, items: [a, b, c], hash: {x: 1, y: 2} }' example5.pyhtml
>     text:  foo
>     items:
>       - a
>       - b
>       - c
>     hash:
>       y: 2
>       x: 1

> You must install PyYAML at first if you want to specify context data in YAML format.

> ### Nested Template

> Template can include other templates. Included templates can also include other templates.

> The following function is available to include other templates.

> include(str template_name)
>   Include other template.
> File 'example6.pyhtml':

>     <html>
>       <body>

>         <div id="sidemenu">
>     <?py include('sidemenu.pyhtml') ?>
>         </div>

>         <div id="main-content">
>     <?py for item in items: ?>
>           <p>${item}</p>
>     <?py #end ?>
>         </div>

>         <div id="footer">
>     #{include('footer.pyhtml', False)}
>         </div>

>       </body>
>     </table>

> File 'sidemenu.pyhtml':

>     <ul>
>     <?py for item in menu: ?>
>       <li><a href="${item['url']}">${item['name']}</a></li>
>     <?py #end ?>
>     </ul>

> File 'footer.pyhtml':

>     <hr />
>     <address>
>       <a href="mailto:${webmaster_email}">${webmaster_email}</a>
>     </address>

> File 'contextdata.py':

>     items = [ '<FOO>', '&BAR', '"BAZ"' ]
>     webmaster_email = 'webmaster@example.com'
>     menu  = [
>         {'name': 'Top',      'url': '/' },
>         {'name': 'Products', 'url': '/prod' },
>         {'name': 'Support',  'url': '/support' },
>     ]

> Result:

>     $ pytenjin -f contextdata.py example6.pyhtml
>     <html>
>       <body>

>         <div id="sidemenu">
>     <ul>
>       <li><a href="/">Top</a></li>
>       <li><a href="/prod">Products</a></li>
>       <li><a href="/support">Support</a></li>
>     </ul>
>         </div>

>         <div id="main-content">
>           <p>&lt;FOO&gt;</p>
>           <p>&amp;BAR</p>
>           <p>&quot;BAZ&quot;</p>
>         </div>

>         <div id="footer">
>     <hr />
>     <address>
>       <a href="mailto:webmaster@example.com">webmaster@example.com</a>
>     </address>

>         </div>

>       </body>
>     </table>

> Function '`include()`' can take template filename (ex. 'user_main.pyhtml') or template short name (ex. ':main'). Template short name represents a template in short notation. It starts with colon (':').

> To make template short name available, command-line option '`--prefix`' and '`--postfix`' are required. For example, '`include("user_main.pyhtml")`' can be described as '`include(":main")`' when '`--prefix="user_"`' and '`--postfix=".pyhtml"`' are specified in command-line.

> ### Layout Template

> Command-line option '`--layout=templatename`' specifies layout template name.

> For example, 'exmample6.pyhtml' template in the previous section can be divided into layout file 'layout6.pyhtml' and content file 'content6.pyhtml'. Variable '`_content`' in layout template represents the result of content file.

> File 'layout6.pyhtml':

>     <html>
>       <body>

>         <div id="sidemenu">
>     <?py include('sidemenu.pyhtml') ?>
>         </div>

>         <div id="main-content">
>     #{_content}
>         </div>

>         <div id="footer">
>     #{include('footer.pyhtml', False)}
>         </div>

>       </body>
>     </table>

> File 'content6.pyhtml':

>     <?py for item in items: ?>
>       <p>${item}</p>
>     <?py #end ?>

> Result:

>     $ pytenjin -f contextdata.py --layout=layout6.pyhtml content6.pyhtml
>     <html>
>       <body>

>         <div id="sidemenu">
>     <ul>
>       <li><a href="/">Top</a></li>
>       <li><a href="/prod">Products</a></li>
>       <li><a href="/support">Support</a></li>
>     </ul>
>         </div>

>         <div id="main-content">
>       <p>&lt;FOO&gt;</p>
>       <p>&amp;BAR</p>
>       <p>&quot;BAZ&quot;</p>

>         </div>

>         <div id="footer">
>     <hr />
>     <address>
>       <a href="mailto:webmaster@example.com">webmaster@example.com</a>
>     </address>

>         </div>

>       </body>
>     </table>

> Target template and layout template don't share local variables. It means that local variables set in a template are not available in layout template.

> If you want variables set in a temlate to be available in layout template, you should use '_context' dict.

> File 'layout7.pyhtml':

>     ...
>     <h1>${title}</h1>

>     <div id="main-content">
>     #{_content}
>     <div>

>     <a href="${url}">Next page</a>
>     ...

> File 'content7.pyhtml':

>     <?py _context['title'] = 'Document Title' ?>
>     <?py _context['url'] = '/next/page' ?>
>     <table>
>       ...content...
>     </table>

> Result:

>     $ pytenjin --layout=layout7.pyhtml content7.pyhtml
>     ...
>     <h1>Document Title</h1>

>     <div id="main-content">
>     <table>
>       ...content...
>     </table>

>     <div>

>     <a href="/next/page">Next page</a>
>     ...

> Using '_context["_layout"]', it is able to specify layout template name in each template file. If you assigned False to '_context["_layout"]', no layout template is used.

> File 'content8.pyhtml':

>     <?py _context['_layout'] = ":layout8_xhtml" ?>
>     <h1>Hello World!</h1>

> File 'layout8_html.pyhtml':

>     <html>
>       <body>
>     #{_content}
>       </body>
>     </html>

> File 'layout8_xhtml.pyhtml':

>     <?xml version="1.0" encoding="UTF-8"?>
>     <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
>       <body>
>     #{_content}
>       </body>
>     </html>

> Result: ':layout8_html' is specified in command-line option but ':layout8_xhtml' is used

>     $ pytenjin --postfix='.pyhtml' --layout=':layout8_html' content8.pyhtml
>     <?xml version="1.0" encoding="UTF-8"?>
>     <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
>       <body>
>     <h1>Hello World!</h1>

>       </body>
>     </html>

> ### Capturing

> It is able to capture any part of template.

> File 'example9.pyhtml':

>     <?py _context['title'] = 'Capture Test' ?>
>     <html>
>       <body>

>     <?py start_capture('content_part') ?>
>         <ul>
>     <?py for i in [0, 1, 2]: ?>
>           <li>i = #{i}</li>
>     <?py #endfor ?>
>         </ul>
>     <?py stop_capture() ?>

>     <?py start_capture('footer_part') ?>
>         <div class="footer">copyright&copy; 2007 kuwata-lab.com</div>
>     <?py stop_capture() ?>

>       </body>
>     </html>

> Captured strings are accessable as local variables. For example, you can get captured string as a variable '`content_part`' in the above example.

> A template can contain several capturing. It is not able to nest capturing.

> In layout file, it is able to use strings captured in templates.

> File 'layout9.pyhtml':

>     <html lang="en">
>       <head>
>         <title>${title}</title>
>       </head>
>       <body>

>         <!-- HEADER -->
>     <?py if not captured_as('header_part'): ?>
>         <h1>${title}</h1>
>     <?py #endif ?>
>         <!-- /HEADER -->

>         <!-- CONTENT -->
>     #{content_part}
>         <!-- /CONTENT -->

>         <!-- FOOTER -->
>     <?py if not captured_as('footer_part'): ?>
>         <hr />
>         <address>webmaster@localhost</address>
>     <?py #endif ?>
>         <!-- /FOOTER -->

>       </body>
>     </html>

> '`if not captured_as("name"): ... #endif`' is equivarent to the following.

>     <?py if 'name' in _context: ?>
>     <?py     _buf.append(_context['name']) ?>
>     <?py else: ?>
>              ...
>     <?py #endif ?>

> The following result shows that content part and footer part are overrided by capturing in content template but header part is not.

> Result:

>     $ pytenjin --layout=layout9.pyhtml example9.pyhtml
>     <html lang="en">
>       <head>
>         <title>Capture Test</title>
>       </head>
>       <body>

>         <!-- HEADER -->
>         <h1>Capture Test</h1>
>         <!-- /HEADER -->

>         <!-- CONTENT -->
>         <ul>
>           <li>i = 0</li>
>           <li>i = 1</li>
>           <li>i = 2</li>
>         </ul>

>         <!-- /CONTENT -->

>         <!-- FOOTER -->
>         <div class="footer">copyright&copy; 2007 kuwata-lab.com</div>
>         <!-- /FOOTER -->

>       </body>
>     </html>

> ### Template Arguments

> It is able to specify template arguments in template files. Template arguments are variables which are passed by main program via context object. In the following example, '`title`' and '`name`' are template arguments.

> File 'example10.pyhtml':

>     <?xml version="1.0"?>
>     <?py #@ARGS title, name ?>
>     <h1>${title}</h1>
>     <p>Hello ${name}!</p>

> Template arguments line is converted into assignment statements of local variables.

>     $ pytenjin -s example10.pyhtml
>     _buf = []; _buf.extend(('''<?xml version="1.0"?>\n''', ));
>     title = _context.get('title'); name = _context.get('name');
>     _buf.extend(('''<h1>''', escape(to_str(title)), '''</h1>
>     <p>Hello ''', escape(to_str(name)), '''!</p>\n''', ));
>     print ''.join(_buf)

> If template arguments are specified, other variables passed by context object are not set.

> File 'example11.pyhtml':

>     <?py #@ARGS x ?>
>     <p>
>     x = #{x}
>     y = #{y}   # NameError
>     </p>

> Result:

>     $ pytenjin -c 'x=10;y=20' example11.pyhtml
>     NameError: name 'y' is not defined

> Special variable '`_context`' which represents context object is always available whether template arguments are specified or not.

> ### Preprocessing

> pyTenjin supports preprocessing of template. Preprocessing executes some logics when templates are loaded and that logics are not executed when rendering. Preprocessing makes your application much faster.

> Notation of preprocessing is the following.

> - `<?PY ... ?>` represents preprocessing statement.
> - `#{{...}}` represents preprocessing expression (without HTML escape).
> - `${{...}}` represents preprocessing expression (with HTML escape).

> For example, assume the following template.

> File 'example12.pyhtml':

>     <?PY states = { "CA": "California", ?>
>     <?PY            "NY": "New York", ?>
>     <?PY            "FL": "Florida",  ?>
>     <?PY            "TX": "Texas",  ?>
>     <?PY            "HI": "Hawaii", } ?>
>     <?PY # ?>
>     <?py chk = { params['state']: ' selected="selected"' } ?>
>     <?PY codes = states.keys() ?>
>     <?PY codes.sort() ?>
>     <select name="state">
>       <option value="">-</option>
>     <?PY for code in codes: ?>
>       <option value="#{{code}}"#{chk.get(#{{repr(code)}}, '')}>${{states[code]}}</option>
>     <?PY #endfor ?>
>     </select>

> If preprocessing is activated, the above will be converted into the following when template is loaded. (Command-line option `-P` shows the result of preprocessing.)

> Result of preprocessing:

>     $ pytenjin -P example12.pyhtml
>     <?py chk = { params['state']: ' selected="selected"' } ?>
>     <select name="state">
>       <option value="">-</option>
>       <option value="CA"#{chk.get('CA', '')}>California</option>
>       <option value="FL"#{chk.get('FL', '')}>Florida</option>
>       <option value="HI"#{chk.get('HI', '')}>Hawaii</option>
>       <option value="NY"#{chk.get('NY', '')}>New York</option>
>       <option value="TX"#{chk.get('TX', '')}>Texas</option>
>     </select>

> This means that for-loop is executed only once when template is loaded and is not executed when rendering. In the result, rendering speed becomes to be much faster.

> And the Python code is here. This shows that there is no for-loop.

> Translated script code:

>     $ pytenjin --preprocess -sb example12.pyhtml
>     chk = { params['state']: ' selected="selected"' }
>     _buf.extend(('''<select name="state">
>       <option value="">-</option>
>       <option value="CA"''', to_str(chk.get('CA', '')), '''>California</option>
>       <option value="FL"''', to_str(chk.get('FL', '')), '''>Florida</option>
>       <option value="HI"''', to_str(chk.get('HI', '')), '''>Hawaii</option>
>       <option value="NY"''', to_str(chk.get('NY', '')), '''>New York</option>
>       <option value="TX"''', to_str(chk.get('TX', '')), '''>Texas</option>
>     </select>\n''', ));

> If you have errors on preprocessing, you should check source script by `-Ps` option(*2).

> The following is an another example. Assume that `link_to()` is a helper method which takes label and url and generate `<a></a>` tag. In this case, label and url can be parameterized by `_p("...")` and `_P("...")`. The former is converted into `#{...}` and the latter converted into `${...}` by preprocessor.

> File 'example13.pyhtml':

>     <?PY ## ex. link_to('Show', '/show/1')  => <a href="/show/1">Show</a> ?>
>     <?PY def link_to(label, url): ?>
>     <?PY     import urllib ?>
>     <?PY     return '<a href="%s">%s</a>' % (urllib.quote(url), label) ?>
>     <?PY # ?>
>     #{{link_to('Show '+_P('params["name"]'), '/items/show/'+_p('params["id"]'))}}

> Preprocessed template:

>     $ pytenjin -P example13.pyhtml
>     <a href="/items/show/#{params["id"]}">Show ${params["name"]}</a>

> Translated script code:

>     $ pytenjin --preprocess -sb example13.pyhtml
>     _buf.extend(('''<a href="/items/show/''', to_str(params["id"]), '''">Show ''', escape(to_str(params["name"])), '''</a>\n''', ));

> There are many web-application framework and they provides helper functions. These helper functions are divided into two groups.

> - `link_to()` or `_()` (function for M17N) return the same result when the same arguments are passed. These functions can be expanded by preprocessor.
> - Some functions return the different result even if the same arguments are passed. These functions can't be expaned by preprocessor.

> Preprocessor has the power to make your application much faster, but it may make the debugging difficult. You should use it carefully.

> (*2)
>   Command-line option '-Ps' is available but '-PS' is not availabe. This is a current restriction of pytenjin.

> ### Template Encoding

> It is able to specify template encoding by command-line option '`-k encoding`' or '`--encoding=encoding`'.

> Notice that these options are not equal.(*3)

> File 'example14.pyhtml':

>     ## The following is OK for '-k encoding' option,
>     ## but is NG for '--encoding=encoding' option.
>     <?py val1 = '...non-ascii-characters...' ?>
>     val1 = #{val1}

>     ## The following is OK for '--encoding=encoding' option,
>     ## but is NG (UnicodeDecodeError) for '-k encoding' option.
>     <?py val2 = u'...non-ascii-characters...' ?>
>     val2 = #{val2}

> (*3)
>   (Internally) If command-line option '-k encoding' is specified, pytenjin command executes '`to_str = tenjin.generate_tostrfunc("encoding")`'. If command-line option '--encoding=encoding' is specified, pytenjin command pass 'encoding=encoding' option to tenjin.Engine(). See Developer's Guide for details.

> ### Other Options

> - Command-line option '`-i N`' or '`--indent=N`' changes indent depth to `N` (default 4).
> - Command-line option '`-m mod1,mod2,mod3`' loads modules mod1, mod2, and mod3. This option is equivarent to Python code '`import mod1, mod2, mod3`'.
> - Command-line option '`--escapefunc=func1`' changes `escape()` function name to `func1` and '`--tostrfunc=func2`' changes `to_str()` function name to `func2`.
> - Command-line option '`--path=dir1,dir2,...`' sepcifies template directory path.
> - Command-line option '`-k encoding`' or '`--encoding=encoding`' specifies encoding name (default None).

> ## Developer's Guide

> This section shows how to use pyTenjin in your Python script.

> If you want to know the notation or features of pyTenjin, see Designer's Guide section.

> If you want to handle non-ascii characters, see Template Encoding section.

> If you got UnicodeDecodeError, see FAQ.

> If you are interested in Google AppEngine, see FAQ.

> ### An Example

> The following is an example to use pyTenjin in Python.

> Example:

>     import tenjin
>     from tenjin.helpers import *   # or escape, to_str
>     engine = tenjin.Engine()
>     context = { 'title': 'pyTenjin Example', 'items': ['AAA', 'BBB', 'CCC'] }
>     filename = 'file.pyhtml'
>     output = engine.render(filename, context)
>     print output,

> If you want to define helper functions for template, see Add Your Helper Functions section.

> ### Class tenjin.Template

> tenjin.Template class represents a template file. An object of tenjin.Template corresponds to a template file. Main responsibilities of this class are:

> - Convert template file into python script
> - Render converted python script with context data

> This class has the following methods and attributes.

> tenjin.Template(filename=None, encoding=None, escapefunc='escape', tostrfunc='to_str', indent=4)
>   Create template object. If filename is given, read and convert it to Python code.
> tenjin.Template.convert(str input, str filename=None)
>   Convert input text into Python code and return it.
> tenjin.Template.convert_file(str filename)
>   Convert file into Python code and return it. This is equivarent to `tenjin.Template.convert(open(filename).read(), filename)`
> tenjin.Template.render(dict context=None)
>   Compile Python code, evaluate it with context data, and return the result of evaluation. If encoding name is specified when creating template object then render() method will return unicode object, else return str object.
> tenjin.Template.script
>   Converted Python code
> tenjin.Template.bytecode
>   Compiled Python code

> The followings are examples to use tenjin.Template in Python script.

> File 'example16.pyhtml':

>     <h1>#{title}</h1>
>     <ul>
>     <?py for item in items: ?>
>      <li>${item}</li>
>     <?py #end ?>
>     </ul>

> File 'example16.py':

>     ## template
>     filename = 'example16.pyhtml'

>     ## convert into python code
>     import tenjin
>     from tenjin.helpers import *   # or escape, to_str
>     template = tenjin.Template(filename)
>     script = template.script
>     ## or
>     # template = tenjin.Template()
>     # script = template.convert_file(filename)
>     ## or
>     # template = tenjin.Template()
>     # input = open(filename).read()
>     # script = template.convert(input, filename)  # filename is optional

>     ## show converted python code
>     print "---- python code ----"
>     print script,

>     ## evaluate python code
>     context = {'title': 'pyTenjin Example', 'items': ['<AAA>','B&B','"CCC"']}
>     output = template.render(context)
>     print "---- output ----"
>     print output,

> Result:

>     $ python example16.py
>     ---- python code ----
>     _buf.extend(('''<h1>''', to_str(title), '''</h1>
>     <ul>\n''', ));
>     for item in items:
>         _buf.extend((''' <li>''', escape(to_str(item)), '''</li>\n''', ));
>     #end
>     _buf.extend(('''</ul>\n''', ));
>     ---- output ----
>     <h1>pyTenjin Example</h1>
>     <ul>
>      <li>&lt;AAA&gt;</li>
>      <li>B&amp;B</li>
>      <li>&quot;CCC&quot;</li>
>     </ul>

> Constructor of tenjin.Template can take the follwoing options.

> - '`encoding`' (string) specifies encoding name of template file. If you specifies encoding, template file string is converted into unicode object. Template.render() will return unicode object if encoding is specified else return str object. Default is None.
> - '`escapefunc`' (string) specifies function name to escape string. Default is '`escape`' (= `tenjin.helpers.escape`).
> - '`tostrfunc`' (string) specifies function name to convert value into string. Default is '`to_str`' (= `tenjin.helpers.to_str`).
> - '`indent`' (integer) specifies width of indentation. Default is 4.

> Constructor of tenjin.Engine can also take the same options as above. These options given to constructor of tenjin.Engine are passed to constructor of tenjin.Template internally.

> File 'example17.py':

>     filename = 'example16.pyhtml'
>     import tenjin
>     from tenjin.helpers import escape, to_str
>     template = tenjin.Template(filename, escapefunc='cgi.escape', tostrfunc='str')
>     print template.script

>     import cgi
>     title = 'pyTenjin Example'
>     items = ['<foo>', '&bar', '"baz"', None, True, False]
>     output = template.render({'title':title, 'items':items})
>     print output,

> Result:

>     $ python example17.py
>     _buf.extend(('''<h1>''', str(title), '''</h1>
>     <ul>\n''', ));
>     for item in items:
>         _buf.extend((''' <li>''', cgi.escape(str(item)), '''</li>\n''', ));
>     #end
>     _buf.extend(('''</ul>\n''', ));

>     <h1>pyTenjin Example</h1>
>     <ul>
>      <li>&lt;foo&gt;</li>
>      <li>&amp;bar</li>
>      <li>"baz"</li>
>      <li>None</li>
>      <li>True</li>
>      <li>False</li>
>     </ul>

> ### Class tenjin.Engine

> tenjin.Engine class contains some template objects. It can handle nested template and layout template. This class provides many features such as:

> - Search template files
> - Nestable layout template
> - Partial template
> - File caching (caching feature is separated to tenjin.CacheStorage class.)
> - Preprocessing
> - and so on

> This class has the following methods.

> tenjin.Engine(str prefix='', str postfix='', str layout=None, list path=None, object cache=True, bool preprocess=False, type templateclass=Template, **kwargs)
>   Create Engine object. Argument 'cache' controls caching policy.
>   - If instance object of CacheStorage class, use it as cache storage.
>   - If True, new MarshalCacheStorage object is created and used. In the result, template objects are cached in both memory and marshal-base cache file.
>   - If None, new MemoryCacheStorage object is created and used. In the result, template objects are cached in memory but not saved in cache file.
>   - If False, nothing is done for caching. Template objects are not cached in both memory nor file.
>   Arguments `kwargs` are passed to tenjin.Template() internally.
> tenjin.Engine#render(str template_name, dict context=None, dict globals=None, str layout=None)
>   Convert template into Python code, evaluate it with context data, and return the result of it.
>   - If `layout` is True or None then layout template name specified by constructor option will be used as layout template.
>   - Else if `layout` is False then layout template will be not used,
>   - Else if `layout` is string then it is regarded as layout template name.
> tenjin.Engine#include(str template_name)
>   Include and evaluate other template.

> Argument `template_name` in render() methods is filename or short name of template. Template short name is a string starting with colon (':'). For example, '`render(":list", context)`' is equivarent to '`render("user_list.pyhtml", context)`' if prefix option is '`user_`' and postfix option is '`.pyhtml`'.

> ### Template Caching

> pyTenjin provides some classes which caches template objects. These classes and their objects are called as cache storage.

> MarshalCacheStorage
>   Caches template objects in both memory and cache file. Cache file is based on marshal format. This is the default cache storage in pyTenjin because marshal can save bytecode object into file.
> PickleCacheStorage
>   Caches template objects in both memory and cache file. Cache file is based on pickle format. Notice that pickle can't save bytecode object, so it is a little slower than MarshalCacheStorage. In this reason, it is recommended to use MarshalCacheStorage if it is available.
> TextCacheStorage
>   (experimental) Caches template objects in both memory and cache file. Cache file is based on normal text file. This may be useful when neither marshal nor pickle are available, such as Jython or so.
> MemoryCacheStorage
>   Caches template objects in memory, but not in file. This doesn't create cache file at all.
> GaeMemcacheCacheStorage
>   Caches template objects in both memory and memcache. This is available only in Google AppEngine environment. See FAQ for details.

> CacheStorage uses full-path of template files as key of cache. So you can share a cache storage between engine objects.

>     ## shared cache storage
>     shared = MarshalCacheStorage()

>     ## create an engine for BooksController
>     path1 = ["views/books", "views"]
>     engine1 = tenjin.Engine(path=path1, cache=shared)

>     ## create an engine for AuthorsController.
>     ## this engine has different parameter value,
>     ## but it is able to share cache storage.
>     path2 = ["views/authors", "views"]
>     engine2 = tenjin.Engine(path=path2, cache=shared)

> ### Variables and Functions

> In template file, the following variables and functions are available.

> _content
>   This variable represents the result of evaluation of other template. This is available only in layout template file.
> _context
>   This variable represents context data dictionary and same as the 2nd argument of tenjin.Engine.render(). You can tweak this dict. For example if you set `_context['title']="Example"` in your template file, `title` variable will be available in layout template file.
> include(str template_name)
>   Include and evaluate other template. This is an alias of tenjin.Engine.include().
> start_capture(name)
>   Start capturing. Result will be stored into `_context['name']`.
> stop_capture()
>   Stop capturing.
> captured_as(varname)
>   If captured string as varname is exist then append it into `_buf` and return True, else return False. This is a helper function for layout template.

> The followings are example of tenjin.Engine class.

> File 'user_form.pyhtml':

>     <?py #@ARGS params ?>
>     <p>
>       Name:  <input type="text" name="name"  value="${params['name']}" /><br />
>       Email: <input type="text" name="email" value="${params['email']}" /><br />
>       Gender:
>     <?py chk = { params['gender'] : ' checked="checked"' } ?>
>       <input type="radio" name="gender" value="m" #{chk.get('m')} />Male
>       <input type="radio" name="gender" value="f" #{chk.get('f')} />Female
>     </p>

> File 'user_create.pyhtml':

>     <?py #@ARGS ?>
>     <form action="user_app.cgi" method="post">
>       <input type="hidden" name="action" value="create" />
>     <?py include(':form') ?>
>       <input type="submit" value="Create" />
>     </form>

> File 'user_edit.pyhtml':

>     <?py #@ARGS params ?>
>     <form action="user_app.cgi" method="post">
>       <input type="hidden" name="action" value="edit" />
>       <input type="hidden" name="id" value="${params['id']}" />
>     <?py include(':form') ?>
>       <input type="submit" value="Edit" />
>     </form>

> File 'user_layout.pyhtml':

>     <?py #@ARGS _content, title ?>
>     <html>
>       <body>

>         <h1>${title}</h1>

>         <div id="main-content">
>     #{_content}
>         </div>

>         <div id="footer">
>     <?py include('footer.html') ?>
>         </div>

>       </body>
>     </html>

> File 'footer.html':

>     <?py #@ARGS ?>
>     <hr />
>     <address>
>       <a href="mailto:webmaster@example.com">webmaster@example.com</a>
>     </address>

> File 'user_app.cgi':

>     #!/usr/bin/env python

>     ## set action ('create' or 'edit')
>     import sys, os, cgi
>     action = None
>     form = None
>     if os.getenv('REQUEST_METHOD'):
>         form = cgi.FieldStorage()
>         action = form.getFirst('action')
>     elif len(sys.argv) >= 2:
>         action = sys.argv[1]
>     if action is None or action not in ['create', 'edit']:
>         action = 'create'

>     ## set context data
>     if action == 'create':
>         title = 'Create User'
>         params = {'name': None, 'email': None, 'gender': None}
>     else:
>         title = 'Edit User'
>         params = {'name': 'Margalette',
>                   'email': 'meg@example.com',
>                   'gender': 'f',
>                   'id': 123 }
>     context = {'title': title, 'params': params}

>     ## create engine object
>     import tenjin
>     from tenjin.helpers import *
>     from tenjin.helpers.html import *
>     layout = ':layout'   # or 'user_layout.pyhtml'
>     engine = tenjin.Engine(prefix='user_', postfix='.pyhtml', layout=layout)

>     ## evaluate template
>     template_name = ':' + action   # ':create' or ':edit'
>     output = engine.render(template_name, context)
>     if form:
>         print "Content-Type: text/html\r\n\r\n",
>     print output,

> Result:

>     $ python user_app.cgi create
>     <html>
>       <body>

>         <h1>Create User</h1>

>         <div id="main-content">
>     <form action="user_app.cgi" method="post">
>       <input type="hidden" name="action" value="create" />
>     <p>
>       Name:  <input type="text" name="name"  value="" /><br />
>       Email: <input type="text" name="email" value="" /><br />
>       Gender:
>       <input type="radio" name="gender" value="m"  />Male
>       <input type="radio" name="gender" value="f"  />Female
>     </p>
>       <input type="submit" value="Create" />
>     </form>

>         </div>

>         <div id="footer">
>     <hr />
>     <address>
>       <a href="mailto:webmaster@example.com">webmaster@example.com</a>
>     </address>
>         </div>

>       </body>
>     </html>

> ### HTML Helper Functions

> pyTenjin provides some useful helper functions for HTML. These are defined in tenjin.helpers.html module.

> checked(expr)
>   Return `' checked="checked"'` if expr is true.
> selected(expr)
>   Return `' selected="selected"'` if expr is true.
> disabled(expr)
>   Return `' disabled="disabled"'` if expr is true.
> nl2br(str)
>   Relplace "\n" in str into "<br />\n" and return it.
> tagattr(name, expr, value=None, escape=True)
>   (experimental) Return `' name="value"'` string if expr is true value, else return '' (empty string). If value is not specified then expr is used as value instead.

>       >>> from tenjin.helpers.html import *
>       >>> tagattr('size', 20)
>       ' size="20"'
>       >>> tagattr('size', 0)
>       ''
>       >>> tagattr('size', 0, 'large')
>       ' size="large"'

> tagattrs(**kwargs)
>   (experimental) Return `' name1="value2" name2=value2 ...'`.
>   If valueN is false value, both key and value are ignored.
>   If keyN is 'klass' then it will be converted into 'class'.
>   If keyN is 'checked', 'selected', or 'disabled', then keyN is used as value. For example, `tagattrs(checked=1)` returns ' `checked="checked"`'.

>       >>> from tenjin.helpers.html import *
>       >>> tagattrs(klass='error', size=20, checked=True)
>       ' class="error" size="20" checked="checked"'
>       >>> tagattrs(klass='', size=0, checked=False)
>       ''

>   Tips: 'and' operator may help you.

>       >>> from tenjin.helpers.html import *
>       >>> color = 'red'
>       >>> tagattrs(style=(color and 'color:%s' % color))
>       ' style="color:red"'
>       >>> color = None
>       >>> tagattrs(style=(color and 'color:%s' % color))
>       ''

> nv(name, value, sep=None, **kwargs)
>   Return `' name="name" value="value"'` string.
>   If sep is specified, `' id="name sep value'` will be added.
>   kwargs are converted into tag attributes by tagattrs() function.

>       >>> from tenjin.helpers.html import *
>       >>> nv('gender', 'F')
>       ' name="gender" value="F"'
>       >>> nv('gender', 'F', '.')
>       ' name="gender" value="F" id="gender.F"'
>       >>> nv('gender', 'F', '.', klass='error', checked=True, style="color:red")
>       ' name="gender" value="F" id="gender.F" class="error" checked="checked" style="color:red"'

> new_cycle(*values)
>   Cycle each value in values.

>       >>> from tenjin.helpers.html import *
>       >>> cycle = new_cycle('odd', 'even')
>       >>> cycle()
>       'odd'
>       >>> cycle()
>       'even'
>       >>> cycle()
>       'odd'
>       >>> cycle()
>       'even'

> ### Template Encoding

> If you got UnicodeDecodeError, see FAQ at first.

> #### Python 2.x

> pyTenjin provides two approaches for encoding in Python 2.x.

> (A) Binary-based approach

>   pyTenjin handles templates as binary file. For example, text "foo" will be converted into `_buf << 'foo'`. In this approach, unicode object should be encoded into binary(=str) by `to_str()`.

>       to_str = tenjin.generate_tostrfunc(encode='utf-8')
>          # ex. to_str(u'\u65e5\u672c\u8a9e') => '\xe6\x97\xa5\xe6\x9c\xac\xe8\xaa\x9e'
>       engine = tenjin.Engine()

>   If you have some troubles about encoding in this approach, try to add encoding declaration in your templates if your templates contain non-ascii characters.

>       <?py # -*- coding: utf-8 -*- ?>
>       <h1>Hello</h1>

> (B) Unicode-based approach

>   You can hanlde templates as unicode string with `encoding` option for tenjin.Engine(). For example, text "foo" will be converted into `_buf << u'foo'`. In this approach, binary(=str) should be decoded into unicode in `to_str()`.

>       to_str = tenjin.generate_tostrfunc(decode='utf-8')
>          # ex. to_str('\xe6\x97\xa5\xe6\x9c\xac\xe8\xaa\x9e')  #=> u'\u65e5\u672c\u8a9e'
>       engine = tenjin.Engine(encoding='utf-8')
>       html = engine.render('index.pyhtml')
>       if isinstance(html, unicode): html = html.encode('utf-8')
>       print(html)

>   You should not add encoding declaration in your templates, or you will get the following error.

>       SyntaxError: encoding declaration in Unicode string

> In Python 2.x, binary-based approach is faster than unicode-based approach. If you don't have any special reason, it is recommend to be with binary-based approach.

> #### Python 3.0

> In Python 3.0, string is treated as unicode object. So pyTenjin handles all templates in string base (= unicode base), and bytes data is converted into str by to_str() function. If you don't specify any encoding, pyTenjin uses 'utf-8' encoding as default.

>     to_str = tenjin.generate_tostrfunc('utf-8')  ## decode bytes into unicode
>     engine = tenjin.Engine(encoding='utf-8')     ## handle template as unicode

> ### Add Your Helper Functions

> There are several ways to use helper functions.

> Assume the following template.

> File 'example18.pyhtml':

>     <ul>
>       <li>#{link_to(label, url)}</li>
>     </ul>

> (A) Define helper functions as global.

> File 'example18a.py':

>     import tenjin
>     from tenjin.helpers import *

>     def link_to(label, url):
>         return '<a href="%s">%s</a>' % (escape(url), escape(label))

>     engine = tenjin.Engine()
>     context = { 'label': 'Top', 'url': '/' }
>     output = engine.render('example18.pyhtml', context)
>     print output,

> Result:

>     $ python example18a.py
>     <ul>
>       <li><a href="/">Top</a></li>
>     </ul>

> (B) Add helper functions into context.

> File 'example18b.py':

>     import tenjin
>     from tenjin.helpers import *

>     def f1(label, url):
>         return '<a href="%s">%s</a>' % (escape(url), escape(label))

>     engine = tenjin.Engine()
>     context = { 'label': 'Top', 'url': '/', 'link_to': f1 }
>     output = engine.render('example18.pyhtml', context)
>     print output,

> Result:

>     $ python example18b.py
>     <ul>
>       <li><a href="/">Top</a></li>
>     </ul>

> (C) Add functions to a dict object and pass it as global variables.

> File 'example18c.py':

>     import tenjin
>     from tenjin.helpers import *

>     def f1(label, url):
>         return '<a href="%s">%s</a>' % (escape(url), escape(label))

>     engine = tenjin.Engine()
>     context = { 'label': 'Top', 'url': '/' }
>     globals = {'escape':escape, 'to_str':to_str, 'link_to':f1}
>     output = engine.render('example18.pyhtml', context, globals=globals)
>     print output,

> Result:

>     $ python example18c.py
>     <ul>
>       <li><a href="/">Top</a></li>
>     </ul>

> ### Other Topics

> - tenjin.Template detects newline character ("\n" or "\r\n") automatically. If input file contains "\r\n", pyTenjin generates output which contains "\r\n".
> - tenjin.Template.render() can be called many times. If you create a tenjin.Template object, you can call render() method many times.
> - tenjin.Template.convert() also can be called many times. If you create a tenjin.Template object, you can call convert() (and also render()) method many times.