======================
Ophelia page templates
======================


API differences to Zope3 page templates
=======================================

Ophelia page templates are Zope3 page templates which provide some convenience
of usage.

    >>> from ophelia.pagetemplate import PageTemplate

For one thing, the template text is passed directly to the page template upon
instantiation:

    >>> pt = PageTemplate("""<h1 tal:content="title" />""")
    >>> pt._text
    '<h1 tal:content="title" />'

In order to render the page template, call it, passing the variables that
should be available in the TALES namespace. The call takes any number of
namespaces and name=value pairs as arguments, where names given earlier may be
overridden by names given later:

    >>> pt({"title": "A title"})
    u'<h1>A title</h1>\n'

    >>> pt(title= "The same title")
    u'<h1>The same title</h1>\n'

    >>> pt({"title": "A title"}, {"title": "A different title"})
    u'<h1>A different title</h1>\n'

    >>> pt({"title": "A title"}, title="Yet another title")
    u'<h1>Yet another title</h1>\n'

If the template can't be compiled, it raises an exception as soon as
compilation is attempted, which is on instantiation as well as on any update:

    >>> pt = PageTemplate("""<h1 tal:asdf="title" />""")
    Traceback (most recent call last):
    ...
    ValueError: There were errors in the page template text.

    >>> pt = PageTemplate("")
    >>> pt.write("""<h1 tal:asdf="title" />""")
    Traceback (most recent call last):
    ...
    ValueError: There were errors in the page template text.

When instantiating the template, a file path may be passed that is stored for
reference:

    >>> pt = PageTemplate("""<h1 tal:content="title" />""", "/tmp/asdf")
    >>> pt.pt_source_file()
    '/tmp/asdf'


Traceback supplements
=====================

If an Ophelia page template can't be compiled or evaluated, the traceback of
the exception raised includes supplementary information which can be made
visible using Zope3's exception formatter.

If evaluation fails, the Zope page templates' traceback supplement which
includes the source file path is rendered in the traceback's text
representation:

    >>> import sys
    >>> from zope.exceptions.exceptionformatter import print_exception

    >>> pt = PageTemplate("""<h1 tal:content="title" />""", "/tmp/asdf")
    >>> try:
    ...     pt()
    ... except Exception:
    ...     print_exception(file=sys.stdout, *sys.exc_info())
    Traceback (most recent call last):
    ...
       - /tmp/asdf
       - Line 1, Column 0
       - Expression: <PathExpr standard:'title'>
       - Names:
          ...
    KeyError: 'title'

If the template can't be compiled, the source file path is included along with
detailed information on what error caused the first failure and where in the
template text the problem occurred:

    >>> import sys
    >>> from zope.exceptions.exceptionformatter import print_exception

    >>> try:
    ...     pt = PageTemplate("""\
    ...     </body>
    ...     <h1 tal:asdf="title" />
    ...     """, "/tmp/asdf")
    ... except Exception:
    ...     print_exception(file=sys.stdout, *sys.exc_info())
    Traceback (most recent call last):
    ...
       - /tmp/asdf
       - Warning: Compilation failed
       - Warning: zope.tal.htmltalparser.NestingError:
                  No tags are open to match </body>, at line 1, column 5
       1:     </body>
       ->      ^
       2:     <h1 tal:asdf="title" />
    ValueError: There were errors in the page template text.


Evaluation context
==================

As seen above, a context namespace is passed to a page template when calling
it to render itself. In addition to those variables, the Python built-in None
is available by the name "None":

    >>> pt = PageTemplate("""<hr tal:attributes="class None" />""")
    >>> pt()
    u'<hr />\n'

That predefined variable "None" can be overridden (not that that would be a
great idea):

    >>> pt({"None": "asdf"})
    u'<hr class="asdf" />\n'
