    Template classes
    ================

    The ``chameleon.zpt`` package provides the ``PageTemplate`` and
    ``PageTemplateFile`` classes which allow easy usage of templates in
    your application.

    Usage
    -----

      >>> from chameleon.zpt.template import PageTemplate

      >>> print PageTemplate("""\
      ...
... Hello World! ...
""", None)()
Hello World!
      >>> from chameleon.zpt.template import PageTemplateFile
      >>> from chameleon import tests

      >>> import os
      >>> path = os.path.join(tests.__path__[0], 'templates')
      >>> t = PageTemplateFile(os.path.join(path, 'helloworld.pt'), None)
      >>> print t()
Hello World!
      >>> t.filename.startswith(os.sep)
      True

      >>> from chameleon.zpt.template import PageTextTemplate
      >>> print PageTextTemplate("Hello World!")()
      Hello World!

    METAL template integration
    --------------------------

    Page templates expose macros in their ``macros`` attribute. A
    ``macros`` global variable exposes the original (ie, source) template
    macros, while ``template`` is always the template being executed.

      >>> main = PageTemplate("""\
      ...
      ...
      ...
... ... Who are you, ${foo}? ...
... I will be replaced ...
... ... ... ... ...
""") >>> print main()
Who are you, foo?
A template provides access to its macros through the ``macros`` dictionary. >>> main.macros['master']
    Only macros which exist in the template may be accessed.

      >>> main.macros['bad_macro']
      Traceback (most recent call last):
       ...
      KeyError: 'bad_macro'

      >>> content = PageTemplate("""\
      ...
      ...
      ...
...
... ... I replace you: ${dummy|foo} (${bar}). ... ...
... ...
""") >>> print content(main=main, bar='bar')
Who are you, foo?
I replace you: foo (boo).
It is possible to define a macro that re-uses a macro from a different template in it's definition, filling a slot of the re-used macro and re-defining this same slot: >>> form = PageTemplate("""\ ...
...
... ...
...
    """)

      >>> another = PageTemplate("""\
      ...
... ... ... Who are you? ... ... ... ...
    """)

      >>> content = PageTemplate("""\
      ...
... ... Baz, you? ... ...
""") >>> print content(another=another, form=form)
Who are you? Baz, you?
It is possible to extend a macro and re-define a slot within a fill-slot to add extra content, using the same name as the filled slot: >>> form = PageTemplate("""\ ...
...
...

Who are you?

... ...
...
    """)

      >>> another = PageTemplate("""\
      ...
... ... ...
... Who? Me? ... ... ...
... ... ...
    """)

      >>> content = PageTemplate("""\
      ...
...
...
...

Tell me, who are you?

... ... Baz, you? ... ...

... ...

...
...
...
""") >>> print content(another=another, form=form)

Tell me, who are you?

Who? Me? Baz, you?
It is NOT possible to define a macro and use a macro at the same time. This is what extend-macro is meant for: >>> form = PageTemplate("""\ ...
...
... Form macro ...
...
    """)

      >>> another = PageTemplate("""\
      ...
... ... Another macro ... ...
    """)

      >>> content = PageTemplate("""\
      ...
... Use macro ...
    """)

      >>> print content(another=another, form=form)
      Traceback (most recent call last):
       ...
      RuntimeError: ...
       ...
      SyntaxError: Cannot use `define-macro` with `use-macro`; ...

    Redefining a macro with ``use-macro`` does not carry forward the inner
    slots (instead ``extend-macro`` should be used; this is tested
    elsewhere).

      >>> form = PageTemplate("""\
      ...
...
... ... ...
...
    """)

      >>> another = PageTemplate("""\
      ...
... ... ...

Who are you?

... ... ...
    """)

      >>> content = PageTemplate("""\
      ...
...

Tell me, who are you?

... ... Baz, you? ... ...
    """)

      >>> print content(another=another, form=form)
      Traceback (most recent call last):
       ...
      RuntimeError: Caught exception rendering template.
       ...
      KeyError: 'extra_info'

      >>> body = """\
      ...
...
... ...
...
... ...
...
... ...
...
    """
      >>> template = PageTemplate(body)
      >>> print template()
Hey
Hello
Hey
>>> template1 = PageTemplate("""\ ...
...

... ... Another macro ... ...

... ...

... ... Another macro ... ...

...
    """)

      >>> template2 = PageTemplate("""\
      ...
...
...
""") >>> print template1(macro=template2.macros['macro'])

- 1

1

- 1

The empty string macro comprises the full template: >>> inner = PageTemplate("""\ ...
      ... """)

      >>> outer = PageTemplate("""\
      ...
...
...

#

...

...
...
""") >>> print inner(macro=outer.macros[""])

# Hello world!

Welcome.

>>> macro = PageTemplate(""" ...
...
...
... This is the macro's slot: ...
...
...
      ... """)

      >>> template = PageTemplate("""
      ...
...
...
... message ...
...
...
""") >>> print template(macro=macro)
This is the macro's slot: message
Doctype declarations -------------------- A macro can have an xml and/or doctype declaration. >>> main = PageTemplate(""" ... ...
...
...
... Content goes here ...
...
...
      ... """)

    A pagetemplate can also contain a doctype

      >>> template = PageTemplate("""
      ...
      ...
...
... Content ...
...
""") The final rendered result should have only one doctype, although the doctype is defined both on the macro and the template. (http://bugs.repoze.org/issue116) >>> print template(main=main)
Content
If a template is used directly in ``metal:use-macro``, it inherits even the doctype. >>> template = PageTemplate(""" ...
...
... Content ...
...
""") >>> print template(main=main)
Content
Error handling -------------- When an exception is raised which does not expose a bug in the TAL translation machinery, we expect the exception to contain the part of the template source that caused the exception. Exception while evaluating expression: >>> PageTemplate("""\ ...
... ...
""").render() Traceback (most recent call last): ... RuntimeError: Caught exception rendering template. ... TypeError: range expected at least 1 arguments, got 0 Exception while evaluating definition: >>> PageTemplate("""\ ...
... ...
""").render() Traceback (most recent call last): ... RuntimeError: Caught exception rendering template. ... TypeError: range expected at least 1 arguments, got 0 Exception while evaluating interpolation: >>> PageTemplate("""\ ...
... ${range()} ...
    """).render()
      Traceback (most recent call last):
        ...
      RuntimeError: Caught exception rendering template.
        ...
      TypeError: range expected at least 1 arguments, got 0

    Output Encoding Handling
    ------------------------

    All output is unicode. If a particular encoding is required, use
    Python's conversion utilities:

      >>> print PageTemplate(u"""\
      ...
...
... U Can\u2019t Touch This. ... ... ... ...
...
""".encode("utf-8"), encoding="latin-1")( ... test=u"U Can\u2019t Touch This.").encode('latin-1', 'xmlcharrefreplace')
U Can’t Touch This. U Can’t Touch This. U Can’t Touch This. U Can’t Touch This.
Otherwise, it is output as-is: >>> print PageTemplate(u"""\ ...
...
U Can\u2019t Touch This.
...
""".encode("utf-8"))()
U Can’t Touch This.
Syntax errors ------------- When syntactical errors appear in expressions, the traceback hints at the location of the error. Interpolation: >>> PageTemplate("""\ ...
... ${print 'Hello world'} ...
    """).render()
      Traceback (most recent call last):
       ...
          print 'Hello world'
                 ^
       ...
      SyntaxError: invalid syntax

    Variable definition:

      >>> PageTemplate("""\
      ...
...
""").render() Traceback (most recent call last): ... print 'Hello world' ^ ... SyntaxError: invalid syntax