Templates Reference

Albatross provides a templating system that applications use to generate HTML. Albatross templates are loosely based on XML, but are a little more forgiving. The primary features are:

  • All standard Albatross tags use an al- prefix, with the alx- prefix available applications and extensions to implement their own tags.
  • For Albatross tags that enclose content you can use the XML empty tag syntax to indicate that there is no content.
  • All Albatross tag and attribute names are transformed to lowercase.
  • Attributes may appear over multiple lines, and attribute values can be broken over multiple lines.
  • Attribute values can be enclosed with either single or double quotes.
  • The enclosing quote character can be used within the attribute string if it is escaped by a backslash (\).
  • The template parser uses regular expressions to locate all of the template tags. All text that is not recognised as an Albatross tag or associated trailing whitespace is passed unchanged through the parser. In practice this means that you can use the templating system for non-HTML files.

For example the following two constructs are identical. :

<al-for iter="i" expr="seq" pagesize="10" prepare>
</al-for>
<AL-FOR iter="i"
        expr="seq"
        pagesize="10"
        prepare/>

The Albatross templating system includes enhanced versions of many HTML tags, as well as tags that control the flow of execution of the template. These are described in detail in subsequent sections. The templating system also allows any other HTML tag to be prefixed with al- to gain access to the attribute evaluation system.

Attributes of Albatross tags can be processed in a number of ways:

  • Some attributes control the behaviour of the enhanced and flow control tags.

  • Appending expr to any attribute name causes the value of the attribute to be evaluated and the results of the evaluation substituted for the attribute value. For example:

    <al-td colspanexpr="i.span()">
    

    would produce

    <td colspan="3">
    
  • Appending bool to any attribute name causes the attribute to be being evaluated in a boolean context. If the result is True, a boolean HTML attribute is emitted, and if the result is False, no attribute is emitted. For example:

    <al-input name="abc.value" disabledbool="abc.isdisabled()">
    

    If abc.isdisabled() evaluates as True, then the disabled attribute is emitted:

    <input name="abc.value" disabled>
    

    But if abc.isdisabled() evaluates as False, then the disabled attribute is suppressed entirely:

    <input name="abc.value">
    
  • Any other attributes are passed through unchanged.

The following example demonstrates all of these to change the styling of alternate rows in a table:

>>> import albatross
>>> ctx = albatross.SimpleContext('.')
>>> ctx.locals.names = ['Alex', 'Fred', 'Guido', 'Martin', 'Raymond', 'Tim']
>>> albatross.Template(ctx, '<magic>', '''
... <table>
...  <al-for iter="i" expr="names">
...   <al-tr classexpr="['light', 'dark'][i.index() % 2]" whitespace>
...    <td><al-value expr="i.value()" /></td>
...   </al-tr>
...  </al-for>
... </table>
... ''').to_html(ctx)
>>> ctx.flush_content()
<table>
 <tr class="light">
   <td>Alex</td>
  </tr><tr class="dark">
   <td>Fred</td>
  </tr><tr class="light">
   <td>Guido</td>
  </tr><tr class="dark">
   <td>Martin</td>
  </tr><tr class="light">
   <td>Raymond</td>
  </tr><tr class="dark">
   <td>Tim</td>
  </tr></table>

Escaping generated content

Whenever template execution results in evaluated data being written, any characters that have special meaning in HTML are escaped to protect against cross-site scripting attacks [1]:

From To
& &amp;
< &lt;
> &gt;
&quot;
&#39;

For example:

>>> import albatross
>>> ctx = albatross.SimpleContext('.')
>>> ctx.locals.signature = '<script>alert("Naughty, naughty!")</script>'
>>> albatross.Template(ctx, '<magic>', '''
... <al-value expr="signature" whitespace>
... ''').to_html(ctx)
>>> ctx.flush_content()
&lt;script&gt;alert(&quot;Naughty, naughty!&quot;)&lt;/script&gt;

You may occasionally have valid reasons to want to write content without escaping. In these cases you can use htmlsafe() to signal that the content has already been rendered safe for HTML use:

>>> import albatross
>>> ctx = albatross.SimpleContext('.')
>>> ctx.locals.signature = albatross.htmlsafe('Be <b>careful</b>!')
>>> albatross.Template(ctx, '<magic>', '''
... <al-value expr="signature" whitespace>
... ''').to_html(ctx)
>>> ctx.flush_content()
Be <b>careful</b>!

htmlsafe() is a simple sub-class of the str() type, used to signal that escaping is not necessary; it has no methods of it’s own, and performing operations such as string concatenation on it will result in a regular str().

An alternative mechanism is to use the noescape attribute:

>>> import albatross
>>> ctx = albatross.SimpleContext('.')
>>> ctx.locals.signature = 'Be <b>careful</b>!'
>>> albatross.Template(ctx, '<magic>', '''
... <al-value expr="signature" noescape whitespace>
... ''').to_html(ctx)
>>> ctx.flush_content()
Be <b>careful</b>!

One potential pitfall to noescape is that the flag is separated from the code that generated the data and the security implications are not immediately apparent to anyone editing the code.

[1]XSS or Cross-site scripting on Wikipedia

Fake Application Harness

Some of the explanatory examples in this chapter require application functionality. The following fake application is used as an execution harness to drive the interactive examples.

import sys
import albatross


class Request:

    def get_uri(self):
        return 'http://www.com/fakeapp.py'

    def write_header(self, name, value):
        pass

    def end_headers(self):
        pass

    def write_content(self, data):
        sys.stdout.write(data)


app = albatross.SimpleApp(base_url='fakeapp.py',
                          template_path='.',
                          start_page='start',
                          secret='secret')

ctx = albatross.SessionAppContext(app)
ctx.set_request(Request())

Enhanced HTML Tags

Tags in this section are used in place of standard HTML tags to access values from the execution context.

All attributes that are not recognised by Albatross are passed without modification to the generated HTML.

<al-form>

Albatross browser request merging depends upon the functionality provided by the <al-form> tag. If you do no use this tag in applications then the standard request merging will not work.

The tag will automatically generate the HTML <form> action (action=”...” attribute) and enctype (enctype=”...” attribute) attributes if they are not supplied in the template.

If you are using an execution context that inherits from the NameRecorderMixin (nearly all do — see chapter Prepackaged Application and Execution Context Classes) then the execution context will raise a ApplicationError exception if multiple instances of some types of input tag with the same name are added to a form. The list attribute of the <al-input> tag is used indicate that multiple instances are intentional.

action="..." attribute

If you do not supply an action attribute the tag will generate one with based on the value returned by the current_url() method of the execution context.

>>> import albatross
>>> from fakeapp import ctx
>>> albatross.Template(ctx, '<magic>', '''
... <al-form whitespace>
...  <al-input type="text" name="name" whitespace>
... </al-form whitespace>
... 
... <al-form action="http://there.com/" whitespace>
...  <al-input type="text" name="name" whitespace>
... </al-form whitespace>
... ''').to_html(ctx)
>>> ctx.flush_content()
<form action="/fakeapp.py">
 <input type="text" name="name" />
<div><input type="hidden" name="__albform__" value="eJzb9mXFmseCLDkMLy6Hzfx1N6dK5rJwA1NtIWMoS15ibmohkzdDsR4AXWEPlg==
" /></div>
</form>

<form action="http://there.com/">
 <input type="text" name="name" />
<div><input type="hidden" name="__albform__" value="eJzb9mXFmseCLDkMLy6Hzfx1N6dK5rJwA1NtIWMoS15ibmohkzdDsR4AXWEPlg==
" /></div>
</form>

Note that the generated action attribute is relative to the document root.

enctype="..." attribute

If you include any file input fields then the open tag will automatically supply an enctype="multipart/form-data" attribute.

>>> import albatross
>>> from fakeapp import ctx
>>> albatross.Template(ctx, '<magic>', '''
... <al-form whitespace>
...  <al-input type="text" name="name" whitespace>
...  <al-input type="file" name="data" whitespace>
... </al-form whitespace>
... ''').to_html(ctx)
>>> ctx.flush_content()
<form action="/fakeapp.py" enctype="multipart/form-data">
 <input type="text" name="name" />
 <input type="file" name="data" />
<div><input type="hidden" name="__albform__" value="eJzjECjXXD/Z7bjIvorcXnm3j10Tnf83MNUWMmqEsqQkliQWMnkzh7LkJeamFjJ7M5TqAQC+UhCo
" /></div>
</form>

<al-input>

Albatross browser request merging depends upon the functionality provided by the <al-input> tag. If you do no use this tag in applications then the standard request merging will not work.

In the most common usage a value (value=”...” attribute) attribute is generated by evaluating the name (name=”...” attribute) attribute in the execution context. This can be overridden by supplying a value (value=”...” attribute) or expr (expr=”...” attribute) attribute.

There are a number of attributes that automatically generate the name attribute.

When merging browser requests the application object places the browser supplied value back into the execution context value referenced by the name attribute. The application request merging will not merge variable names prefixed by underscore. Use this to protect application values from browser modification.

alias="..." attribute

This attribute is ignored is any of the following attributes are present; prevpage, nextpage (prevpage=”...” and nextpage=”...” attributes), treefold, treeselect, or treeellipsis (treeselect=”...”, treefold=”...” and treeellipsis=”...” attributes).

The value of the alias attribute is passed to the make_alias() method of the execution context. The return value is then used as the generated name (name=”...” attribute) attribute.

The execution context make_alias() method splits the alias attribute at the last ‘.’ and resolves the left hand side to an object reference. The albatross_alias() method is then called on that object and the result is combined with the ‘.’ and the right hand side of the of the alias attribute to produce the generated name attribute. The resolved object is entered in the the local namespace and the session using the name returned by the albatross_alias() method.

The template samples/tree/tree1.html contains an example of this method for generating a name attribute.

<al-tree iter="n" expr="tree">
 <al-for iter="c" expr="range(n.depth())">
  <al-value expr="n.line(c.value())" lookup="indent">
 </al-for>
 -<al-input type="checkbox" alias="n.value().selected">
  <al-value expr="n.value().name" whitespace="newline">
</al-tree>

Note that each node in the tree has a checkbox that controls whether or not the node is selected. When processing the alias attribute the toolkit isolates the left hand side (n.value()) which happens to be the current tree node of TreeIterator n. To generate the alias the albatross_alias() method of the current node is called. In samples/tree/tree1.py the implementation of that method looks like:

class Node:
    def albatross_alias(self):
        return 'node%d' % self.node_num

When the template is executed a unique name is generated for each checkbox. The exact HTML produced by the above fragment from the sample looks like this:

-<input type="checkbox" name="node12.selected" value="on">a
 |-<input type="checkbox" name="node2.selected" value="on">a
 | |-<input type="checkbox" name="node0.selected" value="on">a
 | \-<input type="checkbox" name="node1.selected" value="on">b
 \-<input type="checkbox" name="node11.selected" value="on">b
   |-<input type="checkbox" name="node6.selected" value="on">a
   | \-<input type="checkbox" name="node5.selected" value="on">a
   |   |-<input type="checkbox" name="node3.selected" value="on">a
   |   \-<input type="checkbox" name="node4.selected" value="on">b
   |-<input type="checkbox" name="node7.selected" value="on">b
   \-<input type="checkbox" name="node10.selected" value="on">c
     |-<input type="checkbox" name="node8.selected" value="on">a
     \-<input type="checkbox" name="node9.selected" value="on">b

The alias handling uses the fact that all Python objects are stored by reference. It obtains a reference to an existing object by resolving an expression and stores that reference under a new name. Since both the original expression and the new name are the same reference, the toolkit can modify the object referenced by the original expression by using the new name.

Looking further into the samples/tree/tree1.py code you will note that the tree being iterated is generated once and placed into the session. This ensures that the alias names generated always contain references to the nodes in the tree. If the tree was not entered into the session but was generated from scratch every request, the nodes referenced in the alias names would not be the same nodes as those in the tree so all input would be lost.

checked attribute

This attribute is generated in radio and checkbox input field types if the generated value (value=”...” attribute) attribute matches the comparison value from valueexpr (valueexpr=”...” attribute) (or the literal 'on' for the checkbox input field type).

Refer to the documentation for individual input types for more details.

expr="..." attribute

For text, password, submit, reset, hidden, and button input field types the expression in the expr attribute is evaluated and the result is used to generate the value (value=”...” attribute) attribute. If the result is None then no value attribute will be written.

For radio and checkbox input field types the expression in the expr attribute is evaluated and the result is compared with the generated value attribute to determine whether or not the checked (checked attribute) attribute should be written.

Refer to the documentation for individual input types for more details.

list attribute

If you are using an execution context that inherits from the NameRecorderMixin (nearly all do — see chapter Prepackaged Application and Execution Context Classes) then the execution context will raise a ApplicationError exception if multiple instances of some types of input tag with the same name are added to a form. The list attribute of the <al-input> tag is used indicate that multiple instances are intentional.

The presence of the list attribute on an <al-input> tag makes the request merging in the NameRecorderMixin class place any browser request values for the field into a list (field not present is represented by the empty list).

The attribute must not be used for input field types radio, submit, and image. The attribute is ignored for the file input field type.

name="..." attribute

When determining the generated name attribute the tag looks for a number of attributes. Any supplied name attribute will be ignored if any of the following attributes are present; prevpage, nextpage (prevpage=”...” and nextpage=”...” attributes), treefold, treeselect, treeellipsis (treeselect=”...”, treefold=”...” and treeellipsis=”...” attributes), alias (alias=”...” attribute), or nameexpr (nameexpr=”...” attribute).

All of the attributes that automatically generate names and are looked up in the above sequence. The first of those attributes found will be used to determine the name used in the tag.

nameexpr="..." attribute

This attribute is ignored if any of the following attributes are present; prevpage, nextpage (prevpage=”...” and nextpage=”...” attributes), treefold, treeselect, treeellipsis (treeselect=”...”, treefold=”...” and treeellipsis=”...” attributes), or alias (:ref :tag-input-alias).

The expression in the value of the nameexpr attribute is evaluated to determine the generated name (name=”...” attribute) attribute.

One shortcoming of the alias attribute is that you can only perform input on object attributes. The nameexpr enables you to perform input on list elements.

>>> import albatross
>>> ctx = albatross.SimpleContext('.')
>>> ctx.locals.names = ['John', 'Terry', 'Eric']
>>> albatross.Template(ctx, '<magic>', '''
... <al-for iter="i" expr="range(len(names))">
...  <al-input nameexpr="'names[%d]' % i.value()" whitespace>
... </al-for>
... ''').to_html(ctx)
>>> ctx.flush_content()
<input name="names[0]" value="John" />
<input name="names[1]" value="Terry" />
<input name="names[2]" value="Eric" />

When the browser request is merged into the execution context the names elements of the names list will be replaced.

node="..." attribute

The node attribute is used in conjunction with the treeselect, treefold and treeellipsis (treeselect=”...”, treefold=”...” and treeellipsis=”...” attributes) attributes. It is ignored otherwise.

When this attribute is present the node identified by evaluating the expression in the attribute value will be used when generating the name attribute.

The name (name=”...” attribute) attribute is generated as follows:

>>> import albatross
>>> ctx = albatross.SimpleContext('.')
>>> class Node:
...     def __init__(self, ino):
...         self.ino = ino
...     def albatross_alias(self):
...         return 'ino%d' % self.ino
...
>>> ctx.locals.node = Node(81489)
>>> albatross.Template(ctx, '<magic>', '''
... <al-input type="image" treeselect="n" node="node" src="/icons/face.gif" border="0" whitespace>
... ''').to_html(ctx)
>>> ctx.flush_content()
<input type="image" src="/icons/face.gif" border="0" name="treeselect,n,ino81489" />

Refer to the documentation for treeselect, treefold and treeellipsis (treeselect=”...”, treefold=”...” and treeellipsis=”...” attributes) attributes for more information.

prevpage="..." and nextpage="..." attributes

The prevpage and nextpage attributes respectively select the previous and next pages of an <al-for> ListIterator (ListIterator Objects).

An attribute value must be supplied that specifies the name of the iterator.

The name (name=”...” attribute) attribute is generated as follows:

>>> import albatross
>>> ctx = albatross.SimpleContext('.')
>>> albatross.Template(ctx, '<magic>', '''
... <al-input type="image" nextpage="i" src="/icons/right.gif" border="0" whitespace>
... ''').to_html(ctx)
>>> ctx.flush_content()
<input type="image" src="/icons/right.gif" border="0" name="nextpage,i" />

When merging the browser request the NamespaceMixin.set_value() (:ref :mixin-namespace) method looks for field names that contain commas. These names are split into operation, iterator, and optional value then the set_backdoor() method of the identified iterator is invoked.

During request merging the above example will execute code equivalent to the following.

ctx.locals.i.set_backdoor('nextpage', 'nextpage,i')

treeselect="...", treefold="..." and treeellipsis="..." attributes

The treeselect, treefold, and treeellipsis attributes respectively select, open/close, or expand the ellipsis of an <al-tree> node via a LazyTreeIterator (LazyTreeIterator Objects) or EllipsisTreeIterator (EllipsisTreeIterator Objects) iterator.

These attributes are ignored if any of the following attributes are present; prevpage, or nextpage (prevpage=”...” and nextpage=”...” attributes).

An attribute value must be supplied that specifies the name of the LazyTreeIterator iterator.

The name (name=”...” attribute) attribute is generated as follows:

>>> import albatross
>>> ctx = albatross.SimpleContext('.')
>>> class Node:
...     def __init__(self, ino):
...         self.ino = ino
...     def albatross_alias(self):
...         return 'ino%d' % self.ino
...
>>> ctx.locals.tree = Node(81489)
>>> albatross.Template(ctx, '<magic>', '''
... <al-tree expr="tree" iter="n">
... <al-input type="image" treeselect="n" src="/icons/face.gif" border="0" whitespace>
... </al-tree>
... ''').to_html(ctx)
>>> ctx.flush_content()
<input type="image" src="/icons/face.gif" border="0" name="treeselect,n,ino81489" />

When merging the browser request the NamespaceMixin.set_value() (:ref :mixin-namespace) method looks for field names that contain commas. These names are split into operation, iterator, and optional value then the set_backdoor() method of the identified iterator is invoked.

During request merging the above example will execute code equivalent to the following.

ctx.locals.n.set_backdoor('treeselect', 'ino81489', 'treeselect,n,ino81489')

The “ino81489” string is generated by calling the albatross_alias() method for the tree node.

If the node(node=”...” attribute) attribute is not specified, the node referenced by the current value of the iterator will be used to determine the alias.

value="..." attribute

When determining the generated value attribute the tag looks for a number of attributes. Any supplied value attribute will be ignored if either expr (expr=”...” attribute) or valueexpr (valueexpr=”...” attribute) attributes (depending upon input type) present.

If no expr, valueexpr, or value attribute is present then the value identified by the generated name (name=”...” attribute) attribute will be taken from the local namespace. If this value is None then no value attribute will be written. The name used to look into the local namespace is the result of evaluating all name related attributes.

For input field types radio and checkbox the valueexpr attribute if present takes priority over and specified value attribute.

Refer to the documentation for individual input types for more details.

valueexpr="..." attribute

This attribute is used to generate a value (value=”...” attribute) attribute for radio and checkbox input field types. It is ignored for in all other cases.

Refer to the documentation for individual input types for more details.

type="..." attribute (text, password, submit, reset, hidden, button)

The tag determines the generated name (name=”...” attribute) from the name related attributes.

To determine the generated value (value=”...” attribute) attribute the tag first looks for an expr (expr=”...” attribute) attribute, then a value attribute, and then if that fails it looks up the generated name in the execution context.

If the generated name contains a non-empty value it will be written as the name attribute. If the generated value is not None it will be escaped and written as the value attribute. Escaping values makes all &, <, >, and " characters safe.

For example:

>>> import albatross
>>> class Ctx(albatross.SimpleContext):
...     def input_add(self, *args):
...         print args
... 
>>> ctx = Ctx('.')
>>> ctx.locals.zero = 0
>>> ctx.locals.zerostr = '0'
>>> ctx.locals.width = 5
>>> ctx.locals.height = 7
>>> ctx.locals.secret = 42
>>> ctx.locals.other_secret = '<"&'
>>> albatross.Template(ctx, '<magic>', '''
... <al-input name="zero" whitespace>
... <al-input name="zerostr" whitespace>
... <al-input name="width" whitespace>
... <al-input name="area" expr="width * height" whitespace>
... <al-input type="password" name="passwd" whitespace>
... <al-input type="submit" name="login" value="Login" whitespace>
... <al-input type="hidden" name="secret" whitespace>
... <al-input type="hidden" name="other_secret" whitespace>
... ''').to_html(ctx)
('text', 'zero', 0, False)
('text', 'zerostr', '0', False)
('text', 'width', 5, False)
('text', 'area', 35, False)
('password', 'passwd', None, False)
('submit', 'login', 'Login', False)
('hidden', 'secret', 42, False)
('hidden', 'other_secret', '<"&', False)
>>> ctx.flush_content()
<input name="zero" value="0" />
<input name="zerostr" value="0" />
<input name="width" value="5" />
<input name="area" value="35" />
<input type="password" name="passwd" />
<input type="submit" name="login" value="Login" />
<input type="hidden" name="secret" value="42" />
<input type="hidden" name="other_secret" value="&lt;&quot;&amp;" />

After writing all tag attributes the execution context input_add() method is called with the following arguments; input field type ('text', 'password, 'submit, 'reset, 'hidden, or 'button'), the generated name, the generated value, and a flag indicating whether or not the list (list attribute) attribute was present.

Application code handling browser requests typically determines the submit input pressed by the user via the execution context req_equals() method. The req_equals() method simply tests that the named input is present in the browser request and contains a non-empty value.

For example:

def page_process(ctx):
    if ctx.req_equals('login'):
        user = process_login(ctx.locals.username, ctx.locals.passwd)
        if user:
            ctx.locals._user = user
            ctx.add_session_vars('_user')
            ctx.set_page('home')

type="..." attribute (radio)

The tag determines the generated name (name=”...” attribute) from the name related attributes. Then an internal comparison value is determined by evaluating the expr (expr=”...” attribute) attribute if it is present, or by looking up the generated name in the execution context.

If the comparison value equals the generated value (value=”...” attribute) attribute then the checked (checked attribute) attribute is written. Both values are converted to string before being compared.

To determine the generated value attribute the tag first looks for a valueexpr (valueexpr=”...” attribute) attribute, then a value attribute.

For example:

>>> import albatross
>>> class Ctx(albatross.SimpleContext):
...     def input_add(self, *args):
...         print args
... 
>>> ctx = Ctx('.')
>>> ctx.locals.swallow = 'African'
>>> albatross.Template(ctx, '<magic>', '''
... <al-input type="radio" name="swallow" value="African" whitespace>
... <al-input type="radio" name="swallow" value="European" whitespace>
... ''').to_html(ctx)
('radio', 'swallow', 'African', False)
('radio', 'swallow', 'European', False)
>>> ctx.flush_content()
<input type="radio" name="swallow" value="African" checked />
<input type="radio" name="swallow" value="European" />

The expr attribute can be used to generate the internal comparison value. This is then compared with the value attribute to control the state of the checked attribute.

For example:

>>> import albatross
>>> class Ctx(albatross.SimpleContext):
...     def input_add(self, *args):
...         print args
... 
>>> ctx = Ctx('.')
>>> ctx.locals.swallows = ['African', 'European']
>>> ctx.locals.num = 0
>>> albatross.Template(ctx, '<magic>', '''
... <al-input type="radio" name="swallow" expr="swallows[num]" value="African" whitespace>
... <al-input type="radio" name="swallow" expr="swallows[num]" value="European" whitespace>
... ''').to_html(ctx)
('radio', 'swallow', 'African', False)
('radio', 'swallow', 'European', False)
>>> ctx.flush_content()
<input type="radio" name="swallow" value="African" checked />
<input type="radio" name="swallow" value="European" />

The valueexpr attribute can be used to dynamically generate the value attribute.

For example:

>>> import albatross
>>> class Ctx(albatross.SimpleContext):
...     def input_add(self, *args):
...         print args
... 
>>> ctx = Ctx('.')
>>> ctx.locals.swallows = ['African', 'European']
>>> ctx.locals.num = 0
>>> albatross.Template(ctx, '<magic>', '''
... <al-for iter="s" expr="swallows">
... <al-input type="radio" name="swallow" expr="swallows[num]" valueexpr="s.value()" whitespace>
... </al-for>
... ''').to_html(ctx)
('radio', 'swallow', 'African', False)
('radio', 'swallow', 'European', False)
>>> ctx.flush_content()
<input type="radio" name="swallow" value="African" checked />
<input type="radio" name="swallow" value="European" />

After writing all tag attributes the execution context input_add() method is called with the arguments; input field type ('radio'), the generated name, the generated value, and a flag indicating whether or not the list (list attribute) attribute was present.

type="..." attribute (checkbox)

The tag determines the generated name (name=”...” attribute) from the name related attributes. Then an internal comparison value is determined by evaluating the expr (expr=”...” attribute) attribute if it is present, or by looking up the generated name in the execution context.

If the comparison value equals the generated value (value=”...” attribute) attribute then the checked (checked attribute) attribute is written. Both values are converted to string before being compared.

If the internal comparison value is either a list or tuple the checked attribute is written if the generated value attribute is present in the list/tuple.

To determine the generated value attribute the tag first looks for a valueexpr (valueexpr=”...” attribute) attribute, then a value attribute, and then if that fails it defaults to the value 'on'.

For example:

>>> import albatross
>>> class Ctx(albatross.SimpleContext):
...     def input_add(self, *args):
...         print args
... 
>>> ctx = Ctx('.')
>>> ctx.locals.menu = ['spam', 'eggs']
>>> ctx.locals.eric = 'half'
>>> ctx.locals.parrot = 'on'
>>> ctx.locals.halibut = 'off'
>>> albatross.Template(ctx, '<magic>', '''
... <al-input type="checkbox" name="menu" value="spam" whitespace>
... <al-input type="checkbox" name="menu" value="eggs" whitespace>
... <al-input type="checkbox" name="menu" value="bacon" whitespace>
... <al-input type="checkbox" name="eric" value="half" whitespace>
... <al-input type="checkbox" name="parrot" whitespace>
... <al-input type="checkbox" name="halibut" whitespace>
... ''').to_html(ctx)
('checkbox', 'menu', 'spam', False)
('checkbox', 'menu', 'eggs', False)
('checkbox', 'menu', 'bacon', False)
('checkbox', 'eric', 'half', False)
('checkbox', 'parrot', 'on', False)
('checkbox', 'halibut', 'on', False)
>>> ctx.flush_content()
<input type="checkbox" name="menu" value="spam" checked />
<input type="checkbox" name="menu" value="eggs" checked />
<input type="checkbox" name="menu" value="bacon" />
<input type="checkbox" name="eric" value="half" checked />
<input type="checkbox" name="parrot" value="on" checked />
<input type="checkbox" name="halibut" value="on" />

After writing all tag attributes the execution context input_add() method is called with the arguments; input field type ('checkbox'), the generated name, the generated value, and a flag indicating whether or not the list (list attribute) attribute was present.

type="..." attribute (image)

The tag determines the generated name (name=”...” attribute) from the name related attributes.

For example:

>>> import albatross
>>> class Ctx(albatross.SimpleContext):
...     def input_add(self, *args):
...         print args
... 
>>> ctx = Ctx('.')
>>> albatross.Template(ctx, '<magic>', '''
... <al-input type="image" nextpage="m" src="/icons/right.gif" whitespace>
... ''').to_html(ctx)
('image', 'nextpage,m', None, False)
>>> ctx.flush_content()
<input type="image" src="/icons/right.gif" name="nextpage,m" />

After writing all tag attributes the execution context input_add() method is called with the arguments; input field type ('image'), the generated name, None, and a flag indicating whether or not the list (:ref :tag-input-list) attribute was present.

When a browser submits input to an image input it sends an x and y value for the field. These are saved as attributes of the field.

For example, if an image input named map was clicked by the user, then the code to detect and process the input would look something like this:

def page_process(ctx):
    if ctx.req_equals('map'):
        map_clicked_at(ctx.locals.map.x, ctx.locals.map.y)

type="..." attribute (file)

The tag determines the generated name (name=”...” attribute) from the name related attributes.

If you are using an execution context that inherits from the NameRecorderMixin (RecorderMixin Classes) then using this input field type will automatically cause the enclosing <al-form> (<al-form>) tag to include an enctype="multipart/form-data" (enctype=”...” attribute) attribute.

For example:

>>> import albatross
>>> class Ctx(albatross.SimpleContext):
...     def input_add(self, *args):
...         print args
... 
>>> ctx = Ctx('.')
>>> albatross.Template(ctx, '<magic>', '''
... <al-input type="file" name="resume" whitespace>
... ''').to_html(ctx)
('file', 'resume', None, False)
>>> ctx.flush_content()
<input type="file" name="resume" />

After writing all tag attributes the execution context input_add() method is called with the arguments; input field type ('file'), the generated name, None, and a flag indicating whether or not the list (:ref :tag-input-list) attribute was present.

The request merging allows the user to submit more than one file in a file input field. To simplify application code the Request always returns a list of FileField objects for file inputs.

Application code to process file inputs typically looks like the following:

def page_process(ctx):
    if ctx.req_equals('resume'):
        for r in ctx.locals.resume:
            if r.filename:
                save_uploaded_resume(r.filename, r.file.read())

<al-select>

Albatross browser request merging depends upon the functionality provided by the <al-select> tag. If you do no use this tag in applications then the standard request merging will not work.

To determine the name (name=”...” attribute) attribute in the generated tag a number of attributes are used. The generated name is evaluated in the execution context to determine an internal compare value.

The compare value is used to control which option tags are generated with the selected (selected attribute) attribute. <select> tags in multi- select mode are supported by list or tuple compare values.

The <al-select> tag can automatically generate the list of enclosed <option> tags using the optionexpr (optionexpr=”...” attribute) attribute, or can work with enclosed <al-option> (<al-option>) tags.

alias="..." attribute

The value of the alias attribute is passed to the make_alias() method of the execution context. The return value is then used as the generated name (name=”...” attribute) attribute.

The execution context make_alias() method splits the alias attribute at the last ‘.’ and resolves the left hand side to an object reference. The albatross_alias() method is then called on that object and the result is combined with the ‘.’ and the right hand side of the of the alias attribute to produce the generated name attribute. The resolved object is entered in the the local namespace and the session using the name returned by the albatross_alias() method.

Refer to the documentation of the alias attribute of the <al-input> tag (alias=”...” attribute) for an example of the mechanism described above.

list attribute

If you are using an execution context that inherits from the NameRecorderMixin (nearly all do — see chapter Prepackaged Application and Execution Context Classes) then the execution context will raise a ApplicationError exception if multiple instances of a non-multi-select <al-select> tag with the same name are added to a form. The list attribute is used indicate that multiple instances are intentional.

The presence of the list attribute on an <al-select> tag makes the request merging in the NameRecorderMixin class place any browser request values for the field into a list (field not present is represented by the empty list).

multiple attribute

For the purposes of the NameRecorderMixin class, this attribute performs the same role as the list (list attribute) attribute. It tells the browser request merging to place all input values into a list (field not present is represented by the empty list).

name="..." attribute

When determining the generated name attribute the tag looks for a number of attributes. Any supplied name attribute will be ignored if either the alias (alias=”...” attribute) or nameexpr (nameexpr=”...” attribute) attributes are present.

nameexpr="..." attribute

This attribute is ignored if the alias (alias=”...” attribute) attribute is present.

The expression in the value of the nameexpr attribute is evaluated to determine the generated name (name=”...” attribute) attribute.

One shortcoming of the alias attribute is that you can only perform input on object attributes. The nameexpr enables you to perform input on list elements.

Refer to the documentation of the nameexpr attribute of the <al-input> tag (nameexpr=”...” attribute) for an example.

noescape attribute

The noescape attribute is used with the optionexpr (optionexpr=”...” attribute) attribute to suppress escaping of each option value returned by the expression. The htmlsafe() mechanism provides an alternative way to achieve this (see Escaping generated content for more details).

optionexpr="..." attribute

If this attribute is present the expression in the attribute value is evaluated to determine a sequence of option values. One <option> tag is generated for each item in the sequence.

When this attribute is not present all of the directly enclosed <al-option> (<al-option>) tags are processed to generate the enclosed <option> tags.

If an item in the optionexpr sequence is not a tuple, it is converted to string and then compared with the comparison value derived from the name (name=”...” attribute) attribute.

To support multiple selected <option> tags the comparison value must be either a list or tuple.

For example:

>>> import albatross
>>> ctx = albatross.SimpleContext('.')
>>> ctx.locals.sel1 = 3
>>> ctx.locals.sel2 = (2,3)
>>> albatross.Template(ctx, '<magic>', '''
... <al-select name="sel1" optionexpr="range(5)" whitespace/>
... <al-select name="sel2" optionexpr="range(5)" multiple whitespace/>
... ''').to_html(ctx)
>>> ctx.flush_content()
<select name="sel1"><option>0</option>
<option>1</option>
<option>2</option>
<option selected>3</option>
<option>4</option>
</select>
<select multiple name="sel2"><option>0</option>
<option>1</option>
<option selected>2</option>
<option selected>3</option>
<option>4</option>
</select>

If an item in the optionexpr sequence is a tuple it must contain two values. The first value is used to specify the value attribute of the generated <option> tag and the second value provides the <option> tag content.

For example:

>>> import albatross
>>> ctx = albatross.SimpleContext('.')
>>> ctx.locals.menu = [(1, 'Spam'), (2, 'Eggs'), (3, 'Bacon')]
>>> ctx.locals.sel = 1
>>> albatross.Template(ctx, '<magic>', '''
... <al-select name="sel" optionexpr="menu" whitespace/>
... ''').to_html(ctx)
>>> ctx.flush_content()
<select name="sel"><option selected value="1">Spam</option>
<option value="2">Eggs</option>
<option value="3">Bacon</option>
</select>

All values generated by the optionexpr method are escaped to make all &, <, >, and " characters safe.

<al-option>

Unless explicitly overridden, the selected attribute is controlled by the comparison of the value of the enclosing <al-select> (<al-select>) tag with the evaluated value of the <al-option> tag.

The value of the <al-option> tag is specified either by evaluating the valueexpr (valueexpr=”...” attribute) attribute, or the value (:ref :tag-option-value) attribute, or if neither attribute is present, by the content enclosed by the <al-option> tag. The enclosed content of the tag is evaluated before it is compared. This allows the content to be generated using other Albatross tags.

Albatross browser request merging depends upon the functionality provided by the <al-option> tag. If you do no use this tag in applications then the standard request merging will not work.

For example — this shows how the <al-option> content is evaluated before it is compared with the <al-select> value:

>>> import albatross
>>> ctx = albatross.SimpleContext('.')
>>> ctx.locals.opt = 'spam'
>>> ctx.locals.sel = 'spam'
>>> albatross.Template(ctx, '<magic>', '''
... <al-select name="sel">
...  <al-option><al-value expr="opt"></al-option>
...  <al-option>eggs</al-option>
... </al-select whitespace>
... ''').to_html(ctx)
>>> ctx.flush_content()
<select name="sel"><option selected>spam</option><option>eggs</option></select>

selected attribute

The selected attribute overrides the value comparison logic. When the selectedbool form is used, this allows the selected flag to be controlled via arbitrary logic.

value="..." attribute

Use the value attribute to specify a value to be compared with the comparison value of the enclosing <al-select> (<al-select>) tag.

>>> import albatross
>>> ctx = albatross.SimpleContext('.')
>>> ctx.locals.opt = 3
>>> ctx.locals.sel = ['spam', 'eggs']
>>> albatross.Template(ctx, '<magic>', '''
... <al-select name="sel" multiple>
...  <al-option value="spam">Spam <al-value expr="opt"> times
...  </al-option>
...  <al-option>eggs</al-option>
...  <al-option>bacon</al-option>
... </al-select whitespace>
... ''').to_html(ctx)
>>> ctx.flush_content()
<select multiple name="sel"><option value="spam" selected>Spam 3 times
 </option><option selected>eggs</option><option>bacon</option></select>

valueexpr="..." attribute

Use the valueexpr attribute to specify an expression to be evaluated to derive the value to be compared with the comparison value of the enclosing <al-select> (<al-select>) tag.

If the valueexpr attribute evaluates to a 2-tuple, the first item becomes the value and the second becomes the label.

>>> import albatross
>>> ctx = albatross.SimpleContext('.')
>>> ctx.locals.spam = 'manufactured meat'
>>> ctx.locals.potato = ('mash', 'Mashed Potato')
>>> ctx.locals.sel = ['manufactured meat', 'eggs']
>>> albatross.Template(ctx, '<magic>', '''
... <al-select name="sel" multiple>
...  <al-option valueexpr="spam" />
...  <al-option valueexpr="potato" />
...  <al-option>eggs</al-option>
...  <al-option>bacon</al-option>
... </al-select whitespace>
... ''').to_html(ctx)
>>> ctx.flush_content()
<select multiple name="sel"><option selected>manufactured meat</option><option value="mash">Mashed Potato</option><option selected>eggs</option><option>bacon</option></select>

label="..." attribute

Use the label attribute to specify the control label. This overrides the body of the <al-option> tag.

labelexpr="..." attribute

Use the labelexpr attribute to specify an expression to be evaluated to derive the control label. This overrides the body of the <al-option> tag.

>>> import albatross
>>> ctx = albatross.SimpleContext('.')
>>> ctx.locals.spam = 'manufactured meat'
>>> ctx.locals.sel = ['spam', 'eggs']
>>> albatross.Template(ctx, '<magic>', '''
... <al-select name="sel" multiple>
...  <al-option labelexpr="spam">spam</al-option>
...  <al-option>eggs</al-option>
... </al-select whitespace>
... ''').to_html(ctx)
>>> ctx.flush_content()
<select multiple name="sel"><option value="spam" selected>manufactured meat</option><option selected>eggs</option></select>

<al-textarea>

Albatross browser request merging depends upon the functionality provided by the <al-textarea> tag. If you do no use this tag in applications then the standard request merging will not work.

alias="..." attribute

The value of the alias attribute is passed to the make_alias() method of the execution context. The return value is then used as the generated name (name=”...” attribute) attribute.

The execution context make_alias() method splits the alias attribute at the last ‘.’ and resolves the left hand side to an object reference. The albatross_alias() method is then called on that object and the result is combined with the ‘.’ and the right hand side of the of the alias attribute to produce the generated name attribute. The resolved object is entered in the the local namespace and the session using the name returned by the albatross_alias() method.

Refer to the documentation of the alias attribute of the <al-input> tag (alias=”...” attribute) for an example of the mechanism described above.

list attribute

If you are using an execution context that inherits from the NameRecorderMixin (nearly all do — see chapter Prepackaged Application and Execution Context Classes) then the execution context will raise a ApplicationError exception if multiple instances of an <al-textarea> tag with the same name are added to a form. The list attribute is used indicate that multiple instances are intentional.

The presence of the list attribute on an <al-textarea> tag makes the request merging in the NameRecorderMixin class place any browser request values for the field into a list (field not present is represented by the empty list).

name="..." attribute

When determining the generated name attribute the tag looks for a number of attributes. Any supplied name attribute will be ignored if either the alias (alias=”...” attribute) or nameexpr (nameexpr=”...” attribute) attributes are present.

If the value identified by the generated name attribute does not exist in the execution context then the enclosed content will be supplied as the initial tag value.

For example:

>>> import albatross
>>> ctx = albatross.SimpleContext('.')
>>> text = '''
... <al-textarea name="msg">
...  Type in some text
... </al-textarea whitespace>
... '''
>>> albatross.Template(ctx, '<magic>', text).to_html(ctx)
>>> ctx.flush_content()
<textarea name="msg">Type in some text
</textarea>
>>> ctx.locals.msg = 'This came from the program'
>>> albatross.Template(ctx, '<magic>', text).to_html(ctx)
>>> ctx.flush_content()
<textarea name="msg">This came from the program</textarea>

Before the tag value is written it is escaped to make all &, <, >, and " characters safe.

nameexpr="..." attribute

This attribute is ignored if the alias (alias=”...” attribute) attribute is present.

The expression in the value of the nameexpr attribute is evaluated to determine the generated name (name=”...” attribute) attribute.

One shortcoming of the alias attribute is that you can only perform input on object attributes. The nameexpr enables you to perform input on list elements.

Refer to the documentation of the nameexpr attribute of the <al-input> tag (nameexpr=”...” attribute) for an example.

noescape attribute

The noescape attribute is used to suppress escaping of the execution context value associated with the name (name=”...” attribute) attribute. The htmlsafe() mechanism provides an alternative way to achieve this (see Escaping generated content for more details).

>>> import albatross
>>> ctx = albatross.SimpleContext('.')
>>> ctx.locals.msg = 'Should escape < & >...'
>>> albatross.Template(ctx, '<magic>', '''
... <al-textarea name="msg" noescape whitespace/>
... ''').to_html(ctx)
>>> ctx.flush_content()
<textarea name="msg">Should escape < & >...</textarea>

<al-a>

This tag acts as an enhanced version of the standard HTML <a> tag.

expr="..." attributes

This attribute is ignored is any of the following attributes are present; prevpage, nextpage (prevpage=”...” and nextpage=”...” attributes), treefold, treeselect, or treeellipsis (treeselect=”...”, treefold=”...” and treeellipsis=”...” attributes).

The specified expression is evaluated to generate an href (href=”...” attributes) attribute. The generated attribute is then processed as per the href attribute.

href="..." attributes

This attribute is ignored is any of the following attributes are present; prevpage, nextpage (prevpage=”...” and nextpage=”...” attributes), treefold, treeselect, treeellipsis (treeselect=”...”, treefold=”...” and treeellipsis=”...” attributes), or expr (expr=”...” attributes).

When the expr attribute is used, then generated value is processed in the same as a value supplied in the href attribute.

If the href does not contain a ‘?’ (separates the path from the query), but does contain a ‘=’ then the href is rewritten as current_url?*href*.

>>> import albatross
>>> class Ctx(albatross.SimpleContext):
...     def current_url(self):
...         return 'magic'
...
>>> ctx = Ctx('.')
>>> albatross.Template(ctx, '<magic>', '''
... <al-a href="login=1">Login</al-a whitespace>
... ''').to_html(ctx)
>>> ctx.flush_content()
<a href="magic?login=1">Login</a>

If the href does not contain either a ‘?’ or a ‘=’ then the href is assumed to be a page identifier so it is transformed into a redirect url by the redirect_url() execution context method.

>>> import albatross
>>> class Ctx(albatross.SimpleContext):
...     def current_url(self):
...         return 'magic'
...     def redirect_url(self, loc):
...         return 'here/%s' % loc
...
>>> ctx = Ctx('.')
>>> ctx.locals.name = 'eric'
>>> albatross.Template(ctx, '<magic>', '''
... <al-a expr="'login=%s' % name">Login</al-a whitespace>
... <al-a expr="'remote?login=%s' % name">Login</al-a whitespace>
... <al-a href="page">Login</al-a whitespace>
... ''').to_html(ctx)
>>> ctx.flush_content()
<a href="magic?login=eric">Login</a>
<a href="remote?login=eric">Login</a>
<a href="here/page">Login</a>

node="..." attribute

The node attribute is used in conjunction with the treeselect, treefold and treeellipsis (treeselect=”...”, treefold=”...” and treeellipsis=”...” attributes) attributes. It is ignored otherwise.

When this attribute is present the node identified by evaluating the expression in the attribute value will be used when generating the href (href=”...” attributes) attribute.

prevpage="..." and nextpage="..." attributes

The prevpage and nextpage attributes generate an href (href=”...” attributes) attribute that respectively selects the previous and next pages of an <al-for> ListIterator (ListIterator Objects).

The attribute value specifies the name of the iterator.

The generated href attribute is of the form current_url?*name*,*iter*=1 where current_url is the path component returned from the Python urlparse.urlparse() function (via the execution context current_url() method), name is either prevpage or nextpage, and iter is the specified iterator.

For example:

>>> import albatross
>>> class Ctx(albatross.SimpleContext):
...     def current_url(self):
...         return 'magic'
...
>>> ctx = Ctx('.')
>>> albatross.Template(ctx, '<magic>', '''
... <al-a nextpage="m">Next Page</al-a whitespace>
... ''').to_html(ctx)
>>> ctx.flush_content()
<a href="magic?nextpage,m=1">Next Page</a>

treeselect="...", treefold="..." and treeellipsis="..." attributes

These attributes are ignored if any either the prevpage (prevpage=”...” and nextpage=”...” attributes), or nextpage attributes are present.

The treeselect, treefold, and treeellipsis attributes generate an href (href=”...” attributes) attribute that respectively select, open/close, or expand the ellipsis of an <al-tree> (<al-tree>) node via a LazyTreeIterator (LazyTreeIterator Objects) or EllipsisTreeIterator (EllipsisTreeIterator Objects) iterator.

Refer to the <al-input> tag for more information on how to use these attributes (treeselect=”...”, treefold=”...” and treeellipsis=”...” attributes).

If the node (node=”...” attribute) attribute if it is present it defines the node to operate upon. Otherwise the node operated upon is the current value of the LazyTreeIterator iterator.

The attribute value specifies the name of the LazyTreeIterator iterator.

The generated href attribute is of the form current_url?*name*,*iter*,*alias*=1 where current_url is the path component returned from the Python urlparse.urlparse() function (via the execution context current_url() method), name is either treeselect, treefold or treeellipsis, iter is the specified iterator, and alias is the values returned by the albatross_alias() method of the specified node.

<al-img>

Use this tag to dynamically generate the src (src=”...” attribute) attribute of an <img> tag.

expr="..." attribute

You must supply an expr attribute containing an expression that is evaluated to generate the output src (src=”...” attribute) attribute of the <img> tag.

For example:

>>> import albatross
>>> ctx = albatross.SimpleContext('.')
>>> ctx.locals.src = 'http://icons.r.us/spam.png'
>>> albatross.Template(ctx, '<magic>', '''
... <al-img expr="src" whitespace>
... ''').to_html(ctx)
>>> ctx.flush_content()
<img src="http://icons.r.us/spam.png" />

src="..." attribute

This attribute is ignored. Use the expr (expr=”...” attribute) attribute to generate the <src> attribute.

Execution and Control Flow

Tags in this section provide just enough programming capability to allow template files to react to and format values from the execution context.

<al-require>

This tag is used to specify the minimum version of the Albatross templating system that will correctly parse your template, or to specify templating features (that may be implemented by extension modules) that are required to parse your template.

If the templating system has a lower version number, or the extension feature is not available, an ApplicationError Exception is raised when the template is parsed.

version="..." attribute

This attribute specifies the minimum version of the Albatross templating system required to correctly parse your template. Specify the lowest version that will correctly parse your template.

Templating Version Albatross Version Template feature
1 up to 1.20  
2 1.30 and up prefixing any tag with al- now allows any attribute to be evaluated
3 1.42 and up push alternate expression evaluation namespaces

feature="..." attribute

If the feature attribute is present, it specifies a comma-separated list of templating features that will be required to correctly parse your template.

Name Template feature
namespace push alternate expression evaluation namespaces

<al-include>

Use this tag to load and execute another template file at the current location. You can specify the name of the included template file by name using the name (name=”...” attribute) attribute or by expression using the expr (expr=”...” attribute) attribute.

expr="..." attribute

If the expr attribute is present it is evaluated when the template is executed to generate the name of a template file. The specified template file is loaded and executed with the output replacing the <al-include> tag.

For example:

>>> open('other.html', 'w').write('name = "<al-value expr="name">"')
>>> import albatross
>>> ctx = albatross.SimpleContext('.')
>>> ctx.locals.name = 'other.html'
>>> albatross.Template(ctx, '<magic>', '''
... Inserting <al-value expr="name">: <al-include expr="name"> here.
... ''').to_html(ctx)
>>> ctx.flush_content()
Inserting other.html: name = "other.html" here.

name="..." attribute

This attribute is ignored if the expr attribute is present.

When the template is executed the specified template file is loaded and executed with the output replacing the <al-include> tag.

For example:

>>> open('other.html', 'w').write('name = "<al-value expr="name">"')
>>> import albatross
>>> ctx = albatross.SimpleContext('.')
>>> ctx.locals.name = 'other.html'
>>> albatross.Template(ctx, '<magic>', '''
... Inserting other.html: <al-include name="other.html"> here.
... ''').to_html(ctx)
>>> ctx.flush_content()
Inserting other.html: name = "other.html" here.

<al-comment>

This tag suppresses the execution and output of any contained content, although the contained content must be syntactically correct.

For example:

>>> import albatross
>>> ctx = albatross.SimpleContext('.')
>>> albatross.Template(ctx, '<magic>', '''
... Before,<al-comment>
...  This will not be executed
... </al-comment> after.
... ''').to_html(ctx)
>>> ctx.flush_content()
Before, after.

<al-flush>

When the template file interpreter encounters an <al-flush> during execution it flushes all accumulated HTML to output.

Usually HTML is accumulated in the execution context and is not sent to the output until the flush_content() is called. This gives programs the opportunity to handle exceptions encountered during template execution without partial output leaking to the browser.

When the program is performing an operation that runs for some time this behaviour may give user the impression that the application has entered an infinite loop. In these cases it is usually a good idea to provide incremental feedback to the user by placing <al-flush> tags in your template files.

>>> import albatross
>>> ctx = albatross.SimpleContext('.')
>>> albatross.Template(ctx, '<magic>', '''
... Processing results...
... <al-flush>
... Finished
... ''').to_html(ctx)
Processing results...
>>> ctx.flush_content()
Finished

<al-if>/<al-elif>/<al-else>

Use of these tags parallels the if/elif/else keywords in Python.

The <al-if> tag is a content enclosing tag while <al-elif> and <al-else> are empty tags that partition the content of the enclosing <al-if> tag.

expr="..." attribute

The expr attribute is used in the <al-if> and <al-elif> tags to specify a test expression. The expression is evaluated when the template is executed.

If the text expression in the expr attribute of the <al-if> tag evaluates to a TRUE value then the enclosed content up to either the next <al-elif> or <al-else> tag will be executed.

For example:

>>> import albatross
>>> ctx = albatross.SimpleContext('.')
>>> ctx.locals.a = 10
>>> albatross.Template(ctx, '<magic>', '''
... <al-if expr="a < 15">
...  a (<al-value expr="a">) is less than 15.
... <al-else>
...  a (<al-value expr="a">) is greater than or equal to 15.
... </al-if>
... ''').to_html(ctx)
>>> ctx.flush_content()
a (10) is less than 15.

If the expression in the expr attribute of the <al-if> tag evaluates FALSE then the enclosed content following the <al-else> tag is executed.

For example:

>>> import albatross
>>> ctx = albatross.SimpleContext('.')
>>> ctx.locals.a = 20
>>> albatross.Template(ctx, '<magic>', '''
... <al-if expr="a < 15">
...  a (<al-value expr="a">) is less than 15.
... <al-else>
...  a (<al-value expr="a">) is greater than or equal to 15.
... </al-if>
... ''').to_html(ctx)
>>> ctx.flush_content()
a (20) is greater than or equal to 15.

The <al-elif> tag is used to chain a number of expression that are tested in sequence. The first test that evaluates TRUE determines the content that is executed.

For example:

>>> import albatross
>>> ctx = albatross.SimpleContext('.')
>>> ctx.locals.a = 30
>>> albatross.Template(ctx, '<magic>', '''
... <al-if expr="a < 15">
...  a (<al-value expr="a">) is less than 15.
... <al-elif expr="a < 25">
...  a (<al-value expr="a">) is greater than or equal to 15 and less than 25.
... <al-elif expr="a < 35">
...  a (<al-value expr="a">) is greater than or equal to 25 and less than 35.
... <al-else>
...  a (<al-value expr="a">) is greater than or equal to 25.
... </al-if>
... ''').to_html(ctx)
>>> ctx.flush_content()
a (30) is greater than or equal to 25 and less than 35.

<al-value>

This tag allows you to evaluate simple expressions and write the result to output.

date="..." attribute

If a date attribute is specified then the enclosed format string is passed to the Python time.strftime() function along with the result of the expression in the expr (expr=”...” attribute) attribute. The result of time.strftime() is then written to the output.

For example:

>>> import albatross
>>> import time
>>> ctx = albatross.SimpleContext('.')
>>> albatross.Template(ctx, '<magic>', '''
... The time is <al-value expr="time.mktime((2001,12,25,1,23,45,0,0,-1))"
...                       date="%H:%M:%S" whitespace>
... ''').to_html(ctx)
>>> ctx.flush_content()
The time is 01:23:45

expr="..." attribute

This attribute must the specified. It contains an expression that is evaluated when the template is executed and the result is written as a string to the output.

For example:

>>> import albatross
>>> ctx = albatross.SimpleContext('.')
>>> ctx.locals.items = ['pencil', 'eraser', 'lunchbox']
>>> albatross.Template(ctx, '<magic>', '''
... There are <al-value expr="len(items)" whitespace> items
... ''').to_html(ctx)
>>> ctx.flush_content()
There are 3 items

lookup="..." attribute

When the lookup attribute is specified the result of the expression in the expr (expr=”...” attribute) attribute is used to retrieve content from the lookup table named in the lookup attribute. This is a very useful way to separate the internal representation of program value from the presentation of that value.

For example:

>>> import albatross
>>> ctx = albatross.SimpleContext('.')
>>> ctx.locals.simple = 0
>>> ctx.locals.works = 1
>>> albatross.Template(ctx, '<magic>', '''
... <al-lookup name="bool"><al-item expr="0">FALSE</al-item>TRUE</al-lookup>
... Simple: <al-value expr="simple" lookup="bool" whitespace>
...  Works: <al-value expr="works" lookup="bool" whitespace>
... ''').to_html(ctx)
>>> ctx.flush_content()
Simple: FALSE
 Works: TRUE

Please refer to the <al-lookup> tag reference for an explanation of that tag and more complex examples.

noescape attribute

If the noescape attribute is present then the value is not escaped. Only use this attribute when you are sure that the result of the expression is safe. Without this attribute all &, <, >, and " are escaped. The htmlsafe() mechanism provides an alternative way to achieve this (see Escaping generated content for more details).

For example:

>>> import albatross
>>> ctx = albatross.SimpleContext('.')
>>> ctx.locals.field = '<img src="http://rude.pictures.r.us/">'
>>> albatross.Template(ctx, '<magic>', '''
... Safe: <al-value expr="field" whitespace>
... Oops: <al-value expr="field" noescape whitespace>
... ''').to_html(ctx)
>>> ctx.flush_content()
Safe: &lt;img src=&quot;http://rude.pictures.r.us/&quot;&gt;
Oops: <img src="http://rude.pictures.r.us/">

<al-exec>

This tag allows you to place arbitrary Python code in a template file.

expr="..." attribute

The expression is specified in the expr attribute. It is compiled using kind = 'exec' and evaluated when the template is executed.

For example:

>>> import albatross
>>> ctx = albatross.SimpleContext('.')
>>> albatross.Template(ctx, '<magic>', '''
... <al-exec expr="
... results = []
... for n in range(2, 10):
...     for x in range(2, n):
...         if n % x == 0:
...            results.append('%d equals %d * %d' % (n, x, n/x))
...            break
...     else:
...          results.append('%d is a prime number' % n)
... ">
... <al-for iter="l" expr="results">
...  <al-value expr="l.value()" whitespace>
... </al-for>
... ''').to_html(ctx)
>>> ctx.flush_content()
2 is a prime number
3 is a prime number
4 equals 2 * 2
5 is a prime number
6 equals 2 * 3
7 is a prime number
8 equals 2 * 4
9 equals 3 * 3

If you need to include the same quote character used to enclose the attribute value in your expression you can escape it using a backslash (“$\”).

For example:

>>> import albatross
>>> ctx = albatross.SimpleContext('.')
>>> albatross.Template(ctx, '<magic>', r'''
... <al-exec expr="a = '\"'">
... a = <al-value expr="a" whitespace>
... ''').to_html(ctx)
>>> ctx.flush_content()
a = &quot;

<al-for>

This tag implements a loop in almost the same way as the for keyword in Python.

The tag uses an instance of the ListIterator (ListIterator Objects) identified in the local namespace by the iter (iter=”...” attribute) attribute to iterate over the sequence defined by the expression in the expr (expr=”...” attribute) attribute.

>>> import albatross
>>> ctx = albatross.SimpleContext('.')
>>> albatross.Template(ctx, '<magic>', '''
... <al-for iter="i" expr="range(15)" whitespace="indent">
...  <al-value expr="i.value()">
... </al-for whitespace>
... ''').to_html(ctx)
>>> ctx.flush_content()
 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14

Note that you must use the value() method of the iterator to retrieve the current sequence value, or set a template namespace name via the vars attribute into which it will be stored.

When using pagination mode via the pagesize (pagesize=”...” attribute) attribute the prevpage and nextpage attributes of the <al-input> (<al-input>) and <al-a> (<al-a>) tags can be used to automatically page forwards and backwards through a sequence.

The following simulates pagination via the set_backdoor() ListIterator method and shows other data that is maintained by the iterator.

>>> import albatross
>>> ctx = albatross.SimpleContext('.')
>>> ctx.locals.seq = range(9)
>>> t = albatross.Template(ctx, '<magic>', '''
... pagesize has_prevpage has_nextpage index start count value
... ----------------------------------------------------------
... <al-for iter="i" expr="seq" pagesize="3">
... <al-value expr="'%8s' % i.pagesize()">
... <al-value expr="'%13s' % i.has_prevpage()">
... <al-value expr="'%13s' % i.has_nextpage()">
... <al-value expr="'%6s' % i.index()">
... <al-value expr="'%6s' % i.start()">
... <al-value expr="'%6s' % i.count()">
... <al-value expr="'%6s' % i.value()" whitespace>
... </al-for>''')
>>> t.to_html(ctx)
>>> ctx.locals.i.set_backdoor('nextpage', 'nextpage,i')
>>> t.to_html(ctx)
>>> ctx.locals.i.set_backdoor('nextpage', 'nextpage,i')
>>> t.to_html(ctx)
>>> ctx.flush_content()
pagesize has_prevpage has_nextpage index start count value
----------------------------------------------------------
       3        False         True     0     0     0     0
       3        False         True     1     0     1     1
       3        False         True     2     0     2     2
pagesize has_prevpage has_nextpage index start count value
----------------------------------------------------------
       3         True         True     3     3     0     3
       3         True         True     4     3     1     4
       3         True         True     5     3     2     5
pagesize has_prevpage has_nextpage index start count value
----------------------------------------------------------
       3         True        False     6     6     0     6
       3         True        False     7     6     1     7
       3         True        False     8     6     2     8

cols="..." attribute

This attribute is used to format a sequence as multiple columns. The attribute value is an integer that specifies the number of columns.

Rather than evaluate the enclosed content once for each item in the sequence, the tag evaluates the content for each row of items in the sequence. The items in each row can be formatted by using an inner <al-for> tag.

By default the items flow down columns. To flow across columns you must use the flow (flow=”...” attribute)

For example:

>>> import albatross
>>> ctx = albatross.SimpleContext('.')
>>> albatross.Template(ctx, '<magic>', '''
... <al-for iter="i" expr="range(15)" cols="4">
...  <al-for iter="c" expr="i.value()">
...   <al-value expr="' %2d' % c.value()">
...  </al-for whitespace>
... </al-for>
... ''').to_html(ctx)
>>> ctx.flush_content()
  0  4  8 12
  1  5  9 13
  2  6 10 14
  3  7 11

Multi-column formatting does not support pagesize="...", namespace="..." or vars="..."` attributes.

continue attribute

When paginating items via the pagesize (pagesize=”...” attribute) attribute, the iterator index will reset to the first index displayed on the page if you use an iterator more than once on the page. The continue attribute suppresses the sequence index reset causing the elements to flow on from the previous page.

For example:

>>> import albatross
>>> class Ctx(albatross.SimpleContext, albatross.HiddenFieldSessionMixin):
...     def __init__(self):
...         albatross.SimpleContext.__init__(self, '.')
...         albatross.HiddenFieldSessionMixin.__init__(self)
... 
>>> ctx = Ctx()
>>> albatross.Template(ctx, '<magic>', '''
... A:<al-for iter="i" expr="range(500)" pagesize="20" whitespace="indent">
...  <al-value expr="i.value()">
... </al-for whitespace>
... B:<al-for iter="i" pagesize="10" whitespace="indent">
...  <al-value expr="i.value()">
... </al-for whitespace>
... C:<al-for iter="i" pagesize="15" continue whitespace="indent">
...  <al-value expr="i.value()">
... </al-for whitespace>
... ''').to_html(ctx)
>>> ctx.flush_content()
A: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
B: 0 1 2 3 4 5 6 7 8 9
C: 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24

expr="..." attribute

The expr attribute specifies an expression that yields a sequence that the iterator specified in the iter (iter=”...” attribute) attribute will iterate over. All of the enclosed content is then evaluated for each element in the sequence.

For example:

>>> import albatross
>>> ctx = albatross.SimpleContext('.')
>>> albatross.Template(ctx, '<magic>', '''
... <al-for iter="i" expr="range(15)" whitespace="indent">
...  <al-value expr="i.value()">
... </al-for whitespace>
... ''').to_html(ctx)
>>> ctx.flush_content()
 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14

flow="..." attribute

This attribute is used with the cols (cols=”...” attribute) attribute to control the flow of values across columns. The default value is "down". Use the value "across" to flow items across columns.

Rather than evaluate the enclosed content once for each item in the sequence, the tag evaluates the content for each row of items in the sequence. The items in each row can be formatted by using an inner <al-for> tag.

For example:

>>> import albatross
>>> ctx = albatross.SimpleContext('.')
>>> albatross.Template(ctx, '<magic>', '''
... <al-for iter="i" expr="range(15)" cols="4" flow="across">
...  <al-for iter="c" expr="i.value()">
...   <al-value expr="' %2d' % c.value()">
...  </al-for whitespace>
... </al-for>
... ''').to_html(ctx)
>>> ctx.flush_content()
  0  1  2  3
  4  5  6  7
  8  9 10 11
 12 13 14

Multi-column formatting does not support pagination.

iter="..." attribute

This attribute specifies the name of the ListIterator (ListIterator Objects) that will be used to iterate over the items in the sequence defined by the expression in the expr (expr=”...” attribute) attribute.

namespace="..." attribute

The namespace attribute specifies an expression that yields a sequence in the same manner as the expr attribute (expr=”...” attribute), however it also pushes an evaluation namespace for each item in the sequence. See the <al-namespace> (<al-namespace>) tag for more details on namespaces.

For example:

>>> import albatross
>>> class Field:
...     def __init__(self, label, value):
...         self.label = label
...         self.value = value
... 
>>> ctx = albatross.SimpleContext('.')
>>> ctx.locals.fields = [
...     Field('Name', 'Smith'), 
...     Field('Age', 23)
... ]
>>> albatross.Template(ctx, '<magic>', '''
... <al-for namespace="fields">
...  <al-value expr="label"> <al-input name="value" whitespace>
... </al-for>
... ''').to_html(ctx)
>>> ctx.flush_content()
Name <input name="fields[0].value" value="Smith" />
Age <input name="fields[1].value" value="23" />

namespace="..." cannot be used with the cols="..." (cols=”...” attribute), vars="..." or expr="..." attributes.

pagesize="..." attribute

This attribute is used to present a sequence of data one page at a time. The attribute value must be an integer that specifies the number of items to display in each page.

Use of the pagesize attribute places the sequence iterator into page mode and limits the number of elements that will be displayed.

For example:

>>> import albatross
>>> class Ctx(albatross.SimpleContext, albatross.HiddenFieldSessionMixin):
...     def __init__(self):
...         albatross.SimpleContext.__init__(self, '.')
... 
...
>>> ctx = Ctx()
>>> ctx.locals.__dict__.keys()
[]
>>> albatross.Template(ctx, '<magic>', '''
... <al-for iter="i" expr="range(500)" pagesize="20" whitespace="indent">
...  <al-value expr="i.value()">
... </al-for whitespace>
... ''').to_html(ctx)
>>> ctx.flush_content()
 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
>>> ctx.locals.__dict__.keys()
['i']

Pagination support requires that session support be present in the execution context. All of the Albatross application objects provide session capable execution contexts by default. The SimpleContext class does not support sessions so it is necessary to augment the class for the above example. Note also that when the <al-for> tag processes the pagesize attribute it places the sequence iterator into the session.

prepare attribute

This attribute allows you to place pagination controls before the formatted sequence content.

When the prepare attribute is present the <al-for> tag will perform all processing but will not write any output. This allows you to test pagination results before presenting output.

For example:

>>> import albatross
>>> class Ctx(albatross.SimpleContext, albatross.HiddenFieldSessionMixin):
...     def __init__(self):
...         albatross.SimpleContext.__init__(self, '.')
...         albatross.HiddenFieldSessionMixin.__init__(self)
... 
>>> ctx = Ctx()
>>> albatross.Template(ctx, '<magic>', '''
... <al-for iter="i" expr="range(500)" pagesize="20" prepare/>
... <al-if expr="i.has_prevpage()"> prev</al-if>
... <al-if expr="i.has_nextpage()"> next</al-if>
... <al-for iter="i" pagesize="20" whitespace="indent">
...  <al-value expr="i.value()">
... </al-for whitespace>
... ''').to_html(ctx)
>>> ctx.flush_content()
 next 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19

Note the XML empty tag syntax on the <al-for prepare> tag.

vars="..." attribute

If this attribute is set, the current value of the iterator (as returned by value() will be saved to a variable of this name in the local namespace.

For example:

>>> import albatross
>>> ctx = albatross.SimpleContext('.')
>>> albatross.Template(ctx, '<magic>', '''
... <al-for vars="v" expr="range(15)" whitespace="indent">
...  <al-value expr="v">
... </al-for whitespace>
... ''').to_html(ctx)
>>> ctx.flush_content()
 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14

If the attribute is set to a comma separated list of variables, the iterator value will be unpacked into these variables. The iterator values must iterable in this case (typically a tuple or list). If there are more variables listed than there are values to be unpacked, then the unused variables are left unchanged. Conversely, if there are more values than variables, only the values with corresponding names will be unpacked.

For example:

>>> import albatross
>>> ctx = albatross.SimpleContext('.')
>>> ctx.locals.items = [(1.23, 'Red'), (4.71, 'Green'), (0.33, 'Blue')]
>>> albatross.Template(ctx, '<magic>', '''
... <al-for vars="price, label" expr="items">
...  <al-value expr="'$%0.2f' % price"> <al-value expr="label" whitespace>
... </al-for>
... ''').to_html(ctx)
>>> ctx.flush_content()
$1.23 Red
$4.71 Green
$0.33 Blue

ListIterator Objects

The iterator named in the iter (iter=”...” attribute) attribute of the <al-for> tag is an instance of this class. By using an object to iterate over the sequence the toolkit is able to provide additional data that is useful in formatting HTML.

The iterator will retrieve each value from the sequence exactly once. This allows you to use objects that act as sequences by implementing the Python sequence protocol. Only __getitem__() is required unless you use pagination, then __len__() is also required.

ListIterator.pagesize()

Returns the pagesize that was set in the pagesize attribute.

ListIterator.has_prevpage()

Returns TRUE if the page start index is greater than zero indicating that there is a previous page.

ListIterator.has_nextpage()

Returns TRUE if the sequence length is greater than the page start index plus the _pagesize member indicating that there is a next page.

If the iterator has not been placed into “page mode” by the presence of a pagesize attribute a ListIteratorError exception will be raised.

ListIterator.index()

Returns the index of the current sequence element.

ListIterator.start()

Returns the index of the first sequence element on the page.

ListIterator.count()

Returns the index of the current sequence element within the current page. This is equivalent to index() - start().

ListIterator.value()

Returns the current sequence element.

Most of the methods and all of the members are not meant to be accessed from your code but are documented below to help clarify how the iterator behaves.

ListIterator._index

Current sequence index — returned by index().

ListIterator._start

Sequence index of first element on page — returned by start().

ListIterator._count

Sequence index of current element on page — returned by count().

ListIterator._seq

Sequence being iterated over — initialised to None and set by set_sequence().

ListIterator._have_value

Indicates the state of the current element. There are three possible values: None indicates that the state is unknown and will be established when the sequence is next accessed, zero indicates that the end of sequence has been reached and there is no valid element, and one indicates the current element is valid.

ListIterator._pagesize

Current page size — initialised to 0 and set by the presence of a pagesize attribute in the <al-for> tag.

ListIterator.__getstate__()

When “page mode” is enabled the iterator is saved into the session (via the execution context add_session_vars() method). This restricts the Python pickler to saving only the _start and _pagesize members.

ListIterator.__setstate__(tup)

Restores an iterator from the Python pickler.

ListIterator.__len__()

When in “page mode” it returns the _pagesize member else it returns the length of the sequence.

ListIterator.set_backdoor(op, value)

The <al-input> and <al-a> tags provide nextpage and prevpage attributes that generate names using a special backdoor format. When the browser request is merged the set_value() method of the NamespaceMixin directs list backdoor input fields to this method. Refer to the documentation in section NamespaceMixin Class.

The value argument is the browser submitted value for the backdoor field. If a value was submitted for the backdoor field then the op argument is processed. If op equals "prevpage" or "nextpage" then the iterator selects the previous or next page respectively.

ListIterator.get_backdoor(op)

When generating backdoor fields for the <al-input> and <al-a> tags the toolkit calls this method to determine the value that will assigned to that field. The method returns 1.

ListIterator.set_pagesize(size)

Sets the _pagesize member to size.

ListIterator.has_sequence()

Returns whether or not a sequence has been placed into the iterator (_seq is not None).

ListIterator.set_sequence(seq)

Sets the _seq to seq.

ListIterator.reset_index()

If the <al-for> tag does not contain a continue attribute then this is called just before executing the tag content for the first element in the sequence. It sets the _index member to _start.

ListIterator.reset_count()

This is called just before executing the tag content for the first element in the sequence. It sets the _count member to zero.

ListIterator.clear_value()

Sets the _have_value member to None which causes the next call of has_value() to retrieve the sequence element indexed by _index.

ListIterator.has_value()

When the _have_value member is None this method tries to retrieve the sequence element indexed by _index. If an element is returned by the sequence it is saved in the _value member and _have_value is set to one. If an IndexError exception is raised by the sequence then _have_value is set to zero.

The method returns TRUE if a sequence member is contained in _value.

By this mechanism the iterator retrieves each value from the sequence exactly once.

ListIterator.next()

Retrieves the next value (if available) from the sequence into the _value member.

ListIterator.set_value(value)

A back door hack for multi-column output that sets respective values of the iterator to sequences created by slicing the sequence in the expr attribute of the <al-for> tag.

<al-lookup>

The <al-lookup> tag uses a dictionary-style lookup to choose one of the contained <al-item> HTML fragments. If no <al-item> tag matches, then the tag returns any content that was not enclosed by an <al-item> tag. The <al-item> key values are derived by evaluating their expr attribute.

The <al-lookup> element will either be expanded in place if an expr attribute is given or, if named with an name="..." attribute, expanded later via an <al-value> lookup="..." tag.

expr="..." attribute

If the expr attribute is used, this is evaluated and the content of the matching <al-item> element is returned. If no match occurs, the unenclosed content is returned.

This form of the tag is akin to the switch or case statements that appear in some languages.

name="..." attribute

If the name="..." is used, the tag becomes a named lookup and expansion is deferred until the lookup is referenced via the <al-value> element. In this case, the lookup is performed on the evaluated value of the <al-value> expr attribute.

For example:

>>> import albatross
>>> ctx = albatross.SimpleContext('.')
>>> MILD, MEDIUM, HOT = 0, 1, 2
>>> albatross.Template(ctx, '<magic>',
... '''<al-lookup name="curry">
... <al-item expr="MILD">Mild <al-value expr="curry"></al-item>
... <al-item expr="MEDIUM">Medium <al-value expr="curry"></al-item>
... <al-item expr="HOT">Hot <al-value expr="curry"></al-item>
... </al-lookup>''').to_html(ctx)
>>> ctx.locals.spicy = 2
>>> ctx.locals.curry = 'Vindaloo'
>>> albatross.Template(ctx, '<magic>', '''
... <al-value expr="spicy" lookup="curry" whitespace>
... ''').to_html(ctx)
>>> ctx.flush_content()
Hot Vindaloo

By placing lookup tables in separate template files you can eliminate redundant processing via the run_template_once() execution context method. This method is defined in the AppContext class that is used as a base for all application execution contexts.

As the above example demonstrates, you are able to place arbitrary template HTML inside the lookup items. As the content of the item is only executed when referenced, all expressions are evaluated in the context of the template HTML that references the item.

<al-item>

The <al-item> tag must only be used as a child tag of an <al-lookup> (<al-lookup>) tag. to allow internal application values to be converted to display form.

expr="..." attribute

The expr attribute defines an expression that is evaluated to generate a lookup table key for the parent <al-lookup> (<al-lookup>) tag. When the parent <al-lookup> is executed all of the expr expressions are evaluated to build a dictionary of items.

For example:

>>> import albatross
>>> ctx = albatross.SimpleContext('.')
>>> ctx.locals.key = 2
>>> albatross.Template(ctx, '<magic>',
... '''<al-lookup name="look">
... <al-item expr="1">item expr="1" key is <al-value expr="key"></al-item>
... <al-item expr="key">item expr="key" key is <al-value expr="key"></al-item>
... </al-lookup>''').to_html(ctx)
>>> ctx.locals.key = 1
>>> t = albatross.Template(ctx, '<magic>', '''
... <al-value expr="key" lookup="look" whitespace>
... ''')
>>> t.to_html(ctx)
>>> ctx.flush_content()
item expr="1" key is 1
>>> ctx.locals.key = 2
>>> t.to_html(ctx)
>>> ctx.flush_content()
item expr="key" key is 2

<al-tree>

This tag is used to display tree structured data. A pre-order traversal (node visited before each child) of the tree is performed and the enclosed content is evaluated for each node in the tree.

The treefold, treeselect, and treeellipsis attributes on the <al-input> (<al-input>) tag allow the user to open, close, and select lazy tree nodes.

Section Displaying Tree Structured Data contains an example of a simple usage of the <al-tree> tag.

The samples/tree/tree1 sample program takes the simple example further and implements an application that places a checkbox next to the name of each node name. A unique input field name is generated for each checkbox by using the alias attribute described in section <al-input>.

The samples/tree/tree2 sample program demonstrates the use of the lazy <al-tree> attribute that enables lazy child loading mode.

The samples/tree/tree3 sample program demonstrates the use of the ellipsis <al-tree> attribute.

ellipsis attribute

The ellipsis attribute extends the lazy (lazy attribute) traversal. It collapses nodes close to the root of tree as deeper nodes are opened.

expr="..." attribute

The expr attribute defines the root of the tree traversal.

iter="..." attribute

This attribute specifies the name of the TreeIterator (TreeIterator Objects) that will be used to iterate over the nodes in the tree defined by the expression in the expr (expr=”...” attribute) attribute.

lazy attribute

The lazy attribute allows lazy traversal of the tree, with child nodes only being loaded when their parent is open.

single attribute

The single attribute places the tree in single select mode. Whenever a new node is selected by browser input the previous selected node(s) will be deselected.

TreeNode Objects

There is no actual TreeNode class. Trees can be constructed from objects of any class as long as they implement the interface described here.

TreeNode.children

This member must only be present in non leaf nodes. It contains a list of immediate child nodes of this node.

When using lazy loaded trees this member should be initialised to an empty list in the constructor. The presence of the children member makes the toolkit treat the node as a non-leaf node.

TreeNode.children_loaded

This member needs only be present in non leaf nodes that are referenced by an <al-tree> tag in lazy mode. It should be initialised to 0 in the node constructor. The toolkit will set the member to 1 once it has called the load_children() method of the node.

TreeNode.load_children(ctx)

This method must be defined for nodes that are referenced by an <al-tree> tag in lazy mode. It should must populate the children member with the immediate child nodes.

The toolkit will call this method when it needs to display the child nodes of a node and they have not yet been loaded (children_loaded== 0).

The toolkit “knows” when it needs to see the child nodes of a particular node so it asks that node to load the children. This allows potentially huge trees to be browsed by having the toolkit only load those nodes that are visible.

TreeNode.albatross_alias()

This method must be defined for nodes that are referenced by an <al-tree> tag in lazy mode. It must return a unique string identifier for this node that is suitable for use as part of an HTML input field name or URL component via the special treeselect, treefold and treeellipsis attributes. The identifier must be the same each time the program is run (so str(id(self)) will not work).

The TreeIterator uses the node identifier to record which nodes are open and which are selected. The same identifier is also used when the node is referenced via an alias (alias=”...” attribute) attribute of an <al-input> tag.

TreeIterator Objects

An instance of TreeIterator class (or the sub-classes LazyTreeIterator (LazyTreeIterator Objects) or EllipsisTreeIterator (EllipsisTreeIterator Objects)) will be placed into the execution context using the name specified in the iter (iter=”...” attribute) attribute. This iterator will contain traversal data for the current node each time the tag content is executed.

Note that it is also acceptable to create an instance of one of the TreeIterator classes prior to rendering the template. The set_selected_aliases() or set_open_aliases() methods can then be used to render the tree with nodes already selected or open.

By using an object to iterate over the tree the toolkit is able to provide additional data that is useful in formatting HTML. The toolkit also places the iterator into the session (you must be using an application class that supports sessions).

TreeIterator.value()

Returns the current node.

TreeIterator.tree_depth()

Returns the depth of the visible tree, from the root to deepest node. A single node tree has a depth of one.

TreeIterator.depth()

Returns the depth of the current node.

TreeIterator.span()

Is shorthand for n.tree_depth() - n.depth(). It is intended to be used for the colspan of the table cell containing the node name when laying the tree out in a table. See the samples/tree/tree2.html template for just such an example.

TreeIterator.line(depth)

Only useful when displaying a tree in tabular form where the root is in the first column of the first row. Returns the type of line that should be displayed in each column up to the depth of the current node.

A return value of 0 indicates no line, 1 indicates a line that joins a node later than this node, and 2 indicates a line that terminates at this node.

The example in section Displaying Tree Structured Data uses this method.

TreeIterator.is_open()

Returns TRUE if the current node is open. For non-lazy iterators, this is always TRUE except on leaf nodes.

TreeIterator.is_selected()

For non-lazy iterators, this always returns FALSE.

TreeIterator.has_children()

Returns TRUE if the current node has children (ie. it defines a children member).

Most of the methods and all of the members are not meant to be accessed from your code but are documented below to help clarify how the iterator behaves.

TreeIterator._value

Stores a reference to the current tree node — returned by value().

TreeIterator._stack

As the tree is being traversed this list attribute records all parent nodes between the current node and the root. This is used to determine which branch lines should be drawn for the current node.

TreeIterator._line

Stores the branch line drawing information for the current node. Elements of this list are returned by line(depth)().

TreeIterator._tree_depth

Stores the depth of the tree from the root to the deepest visible node. A single node tree has a depth of 1. This is calculated immediately before the tree is displayed. This is returned by tree_depth().

TreeIterator.set_line(line)

Saves the line argument in _line.

TreeIterator.set_value(node)

Sets the _value to the node argument.

TreeIterator.node_is_open(ctx, node)

Called internally whenever the toolkit needs to determine the open state of a tree node. For non-lazy iterators, it returns whether or not the node in the node argument has children (because non-lazy iterators are always open).

LazyTreeIterator Objects

<al-tree> tags that include the lazy attribute use an instance of the LazyTreeIterator class. This class supports all the methods of the TreeIterator class, as well as the following:

TreeIterator.is_open()

Returns TRUE is the current node is open. Calls the albatross_alias() method of the current node and returns TRUE if the returned alias exists in the _open_aliases dictionary member.

TreeIterator.is_selected()

Returns TRUE if the current node is selected. Calls the albatross_alias() method of the current node and returns TRUE if the returned alias exists in the _selected_aliases dictionary member.

Some methods are designed to be called from application code, not from templates.

TreeIterator.close_all()

Closes all tree nodes by reinitialising the _open_aliases to the empty dictionary.

TreeIterator.deselect_all()

Deselects all tree nodes by reinitialising the _selected_aliases to the empty dictionary.

TreeIterator.get_selected_aliases()

Returns a sorted list of aliases for all nodes that are selected (ie. in the _selected_aliases member).

TreeIterator.set_selected_aliases(aliases)

Builds a new _selected_aliases member from the sequence of aliases passed in the aliases argument.

TreeIterator.get_open_aliases()

Returns a sorted list of aliases for all nodes that are open (ie. in the _open_aliases member).

TreeIterator.set_open_aliases(aliases)

Builds a new _open_aliases member from the sequence of aliases passed in the aliases argument.

LazyTreeIterator instances add the follow private methods and members:

LazyTreeIterator._key

This member caches the value returned by the albatross_alias() method for the current node. This key is then used to look up the _open_aliases and _selected_aliases members.

LazyTreeIterator._open_aliases

A dictionary that contains the aliases for all tree nodes that are currently open. The contents of this dictionary is maintained via the set_backdoor() method.

LazyTreeIterator._selected_aliases

A dictionary that contains the aliases for all tree nodes that are currently selected. The contents of this dictionary is maintained via the set_backdoor() method.

LazyTreeIterator.__getstate__()

Used to save the iterator in the session. This restricts the Python pickler to saving only the _lazy, _open_aliases and _selected_aliases members.

LazyTreeIterator.__setstate__(tup)

Restores an iterator from the Python pickler.

LazyTreeIterator.set_value(node)

Sets the _value to the node argument. When operating in lazy mode the albatross_alias() method is called for node and the result is cached in _key.

LazyTreeIterator.node_is_open(ctx, node)

Called internally whenever the toolkit needs to determine the open state of a tree node. It returns whether or not the node in the node argument is open. This always returns 0 for leaf nodes as they do not have children.

When in lazy mode the open state of node is retrieved from _open_aliases. If the node state is open then the method checks the value of the node children_loaded member. If children_loaded is FALSE then the node load_children() is called to load the children of node.

LazyTreeIterator.set_backdoor(op, key, value)

The <al-input> and <al-a> tags provide treefold and treeselect attributes that generate names using a special backdoor format. When the browser request is processed, the set_value() method of the NamespaceMixin directs tree backdoor input fields to this method. Refer to the documentation in section NamespaceMixin Class.

When the op argument is "treeselect" the _selected_aliases is updated for the node identified by the key argument. If value is FALSE the key is removed else it is added.

When the op argument is "treefold" and value argument is TRUE then the open state of the node identified by the key argument is toggled.

LazyTreeIterator.get_backdoor(op, key)

When generating backdoor fields for the <al-input> and <al-a> tags the toolkit calls this method to determine the value that will assigned to that field.

When op is "treeselect" the method returns the current selected state of the node identified by key.

When op is "treefold" the method returns 1.

EllipsisTreeIterator Objects

EllipsisTreeIterator objects are created by using the ellipsis attribute on an <al-tree> tag. Ellipsis trees are a variant of lazy trees where nodes at shallower levels are progressively collapsed into ellipses as the user opens deeper nodes. The user can reopen the collapsed nodes by selecting an ellipsis.

They support all the methods of the LazyTreeIterator (LazyTreeIterator Objects), as well as the following methods:

EllipsisTreeIterator.node_type()

Returns 0 for a regular node, or 1 for nodes that have been collapsed into an ellipsis. This is actually implemented on the LazyTreeIterator, but will always return 0 there.

EllipsisTreeIterator objects also have the following private methods and members:

EllipsisTreeIterator._noellipsis_alias

Records the last ellipsis to be selected by the user, and is used to suppress the generate of an ellipsis at that location next time the tree is rendered.

EllipsisTreeIterator.node_use_ellipsis(ctx, node)

Returns TRUE if it is acceptable to render the specified node as an ellipsis. If the node’s alias matches _noellipsis_alias, FALSE is return, otherwise TRUE is returned if any of the node’s children are open.

The behaviour of the set_backdoor() and get_backdoor() methods has been extended to recognise a treeellipsis op. This is used to process browser requests to open an ellipsis (it sets the _noellipsis_alias member.

<al-namespace>

The <al-namespace> tag allows you to specify an object within ctx.locals to become the evaluation context for any contained elements. Within the scope, name attributes on contained <al-input>, <al-select>, and <al-textarea> will automatically be prefixed with the namespace name.

For example:

>>> import albatross
>>> ctx = albatross.SimpleContext('.')
>>> class Field:
...     prompt = 'Enter age'
...     data = 23
...
>>> ctx.locals.field = Field()
>>> albatross.Template(ctx, '<magic>', '''
... <al-namespace name="field">
...  <al-value expr="prompt">: <al-input name="data" whitespace>
... </al-namespace>
... ''').to_html(ctx)
>>> ctx.flush_content()
Enter age: <input name="field.data" value="23" />

Macro Processing

Tags in this section provide a simple macro processing environment for template files.

The main purpose of Albatross macros is to provide a mechanism to divide your HTML into presentation structure and presentation appearance. By defining appearance presentation tricks inside macros you can make global changes to your web application appearance by changing one macro.

The <al-macro> (<al-macro>) and <al-usearg> (<al-usearg>) tags are used to define macros, while <al-expand> (<al-expand>) and <al-setarg> (<al-setarg>) are used to invoke and expand previously defined macros.

The ResourceMixin (ResourceMixin Class) and ExecuteMixin (ExecuteMixin Class) classes provide the Albatross macro definition and execution facilities respectively.

<al-macro>

The <al-macro> tag is used to define a macro. All enclosed content becomes part of the macro definition.

Executing the macro registers the macro with the execution context via the register_macro() method using the name in the name (name=”...” attribute) attribute.

Note that the execution of the macro content is deferred until later when the macro is expanded via the <al-expand> (<al-expand>) tag. This means that executing a macro definition produces no output. Output is produced only when the macro is expanded.

>>> import albatross
>>> ctx = albatross.SimpleContext('.')
>>> albatross.Template(ctx, '<magic>', '''
... <al-macro name="noargs">
...  Will be executed when macro is expanded.
... </al-macro>
... ''').to_html(ctx)
>>> ctx.flush_content()
>>> albatross.Template(ctx, '<magic>', '''
... <al-expand name="noargs"/>
... ''').to_html(ctx)
>>> ctx.flush_content()
Will be executed when macro is expanded.

The deferred execution also means that you can include content that only works within the context of the <al-expand> tag.

>>> import albatross
>>> ctx = albatross.SimpleContext('.')
>>> albatross.Template(ctx, '<magic>', '''
... <al-macro name="oops">
...  <al-value expr="oops">
... </al-macro>
... ''').to_html(ctx)
>>> ctx.flush_content()
>>> templ = albatross.Template(ctx, '<magic>', '''
... <al-expand name="oops"/>
... ''')
>>> try:
...     templ.to_html(ctx)
...     ctx.flush_content()
... except NameError, e:
...     print e
... 
name 'oops' is not defined
>>> ctx.locals.oops = 'there is now'
>>> templ.to_html(ctx)
>>> ctx.flush_content()
there is now

In the above example the content of the macro makes reference to a oops that is not defined in the execution context when the macro was defined.

Inside a macro definition you can use as yet undefined macros.

>>> import albatross
>>> ctx = albatross.SimpleContext('.')
>>> albatross.Template(ctx, '<magic>', '''
... <al-macro name="bold-red">
... <font color="red">more <al-expand name="bold"><al-usearg></al-expand> please</font>
... <font color="red"><al-expand name="bold">more <al-usearg> please</al-expand></font>
... </al-macro>
... 
... <al-macro name="bold">
...  <b><al-usearg></b></al-macro>
... ''').to_html(ctx)
>>> ctx.flush_content()
>>> albatross.Template(ctx, '<magic>', '''
... <al-expand name="bold-red">spam</al-expand>
... ''').to_html(ctx)
>>> ctx.flush_content()
<font color="red">more <b>spam</b> please</font>
<font color="red"><b>more spam please</b></font>

Care must by taken to ensure that you do not make circular macro references else you will cause a stack overflow.

name="..." attribute

The name attribute is used to uniquely identify the macro in the application.

<al-usearg>

The <al-usearg> tag is used inside a macro definition to define the location where content enclosed by the <al-expand> (<al-expand>) tag should be placed when the macro is expanded.

All content enclosed by the <al-expand> tag is passed to the macro as the unnamed argument. The unnamed argument is retrieved in the macro definition by using an <al-usearg> tag without specifying a name (name=”...” attribute) attribute. When a macro expects only one argument it is best to use this mechanism.

>>> import albatross
>>> ctx = albatross.SimpleContext('.')
>>> albatross.Template(ctx, '<magic>', '''
... <al-macro name="double">
...  1. <al-usearg>
...  2. <al-usearg>
... </al-macro>
... ''').to_html(ctx)
>>> ctx.flush_content()
>>> albatross.Template(ctx, '<magic>', '''
... <al-expand name="double">
...  spam
... </al-expand>''').to_html(ctx)
>>> ctx.flush_content()
1. spam
2. spam

name="..." attribute

Macros can be defined to accept multiple arguments. The name attribute is used to retrieve named arguments. When invoking a macro that accepts named arguments the <al-setarg> (<al-setarg>) tag and name attribute are used to define the content for each named argument.

>>> import albatross
>>> ctx = albatross.SimpleContext('.')
>>> albatross.Template(ctx, '<magic>', '''
... <al-macro name="namedargs" whitespace="indent">
...  The unnamed arg (<al-usearg>) still works,
...  but we can also include named arguments (<al-usearg name="arg1">)
... </al-macro>
... ''').to_html(ctx)
>>> albatross.Template(ctx, '<magic>', '''
... <al-expand name="namedargs">
...  unnamed arg<al-setarg name="arg1">
...  named arg: arg1</al-setarg>
... </al-expand>
... ''').to_html(ctx)
>>> ctx.flush_content()
 The unnamed arg (unnamed arg) still works,
 but we can also include named arguments (named arg: arg1)

<al-setdefault>

The <al-setdefault> tag is used inside a macro definition to specify default content for a named macro argument. The content enclosed by this tag will be used if the caller does not override it with a <al-setarg> tag.

Note that only named arguments can have a default as the unnamed argument is always set, implicitly or explicitly, by the calling <al-expand> tag.

>>> import albatross
>>> ctx = albatross.SimpleContext('.')
>>> albatross.Template(ctx, '<magic>', '''
... <al-macro name="pagelayout">
...  <al-setdefault name="title">Parrot</al-setdefault>
...  <title><al-usearg name="title"></title>
... </al-macro>
... ''').to_html(ctx)
>>> albatross.Template(ctx, '<magic>', '''
... <al-expand name="pagelayout">
...  <al-setarg name="title">Lumberjack</al-setarg>
... </al-expand>''').to_html(ctx)
>>> ctx.flush_content()
<title>Lumberjack</title>
>>> albatross.Template(ctx, '<magic>', '''
... <al-expand name="pagelayout">
... </al-expand>''').to_html(ctx)
>>> ctx.flush_content()
<title>Parrot</title>

name="..." attribute

The name attribute is used to identify the named macro argument that will receive the enclosed content.

<al-expand>

The <al-expand> tag is used to expand a previously defined macro.

You can pass macro expansions as arguments to other macros.

>>> import albatross
>>> ctx = albatross.SimpleContext('.')
>>> albatross.Template(ctx, '<magic>', '''
... <al-macro name="red">
... <font color="red"><al-usearg></font>
... </al-macro>
... 
... <al-macro name="bold"><b><al-usearg></b></al-macro>
... ''').to_html(ctx)
>>> ctx.flush_content()
>>> albatross.Template(ctx, '<magic>', '''
... <al-expand name="red">more <al-expand name="bold">spam</al-expand> please</al-expand>
... <al-expand name="red"><al-expand name="bold">more spam please</al-expand></al-expand>
... ''').to_html(ctx)
>>> ctx.flush_content()
<font color="red">more <b>spam</b> please</font>
<font color="red"><b>more spam please</b></font>

All arguments to macros are executed each time they are used in the macro definition. This means that you need to be aware of side effects when using arguments more than once inside a macro.

>>> import albatross
>>> ctx = albatross.SimpleContext('.')
>>> albatross.Template(ctx, '<magic>', '''
... <al-macro name="yummy">
...  <al-for iter="i" expr="range(3)">
...   <al-usearg whitespace="indent">
...  </al-for>
... </al-macro>
... ''').to_html(ctx)
>>> ctx.locals.food = 'spam'
>>> albatross.Template(ctx, '<magic>', '''
... <al-expand name="yummy">
...  <al-exec expr="food = food + '!'">
...  <al-value expr="food">
... </al-expand whitespace>
... ''').to_html(ctx)
>>> ctx.flush_content()
spam! spam!! spam!!! 

name="..." attribute

The name attribute contains the name of the macro that will be expanded. Macros are defined and names using the <al-macro> (<al-macro>) tag.

namespace="..." attribute

This runs the macro in the specified namespace.

For example:

>>> import albatross
>>> class Field:
...     def __init__(self, label, value):
...         self.label = label
...         self.value = value
... 
>>> ctx = albatross.SimpleContext('.')
>>> ctx.locals.name = Field('Name', 'Smith')
>>> ctx.locals.age  = Field('Age', 23)
>>> albatross.Template(ctx, '<magic>', '''
... <al-macro name="fieldlayout">
...  <al-value expr="label"> <al-input name="value" whitespace>
... </al-macro>
... ''').to_html(ctx)
>>> ctx.flush_content()
>>> albatross.Template(ctx, '<magic>', '''
... <al-expand name="fieldlayout" namespace="name" />
... <al-expand name="fieldlayout" namespace="age" />
... ''').to_html(ctx)
>>> ctx.flush_content()
Name <input name="name.value" value="Smith" />
Age <input name="age.value" value="23" />

See the <al-namespace> (<al-namespace>) tag for more information on namespaces.

...arg="..." attributes

When macro arguments are simple strings, they can be specified as <al-expand> attributes by appending arg to the argument name. So, to set an argument called title, you could add an titlearg attribute to the <al-expand> tag.

>>> import albatross
>>> ctx = albatross.SimpleContext('.')
>>> albatross.Template(ctx, '<magic>', '''
... <al-macro name="pagelayout">
...  <title><al-usearg name="title"></title>
... </al-macro>
... ''').to_html(ctx)
>>> albatross.Template(ctx, '<magic>', '''
... <al-expand name="pagelayout" titlearg="Lumberjack" />''').to_html(ctx)
>>> ctx.flush_content()
<title>Lumberjack</title>

If the macro argument is longer or needs to contain markup, the <al-setarg> (<al-setarg>) tag should be used instead.

...argexpr="..." attributes

Macro arguments can also be derived by evaluating a python expression. Attributes of the <al-expand> tag that end in argexpr are evaluated, and the base name becomes the macro argument of that name.

For example:

<al-expand name="pagelayout" titleargexpr="foo" />

is functionally equivilent to:

<al-expand name="pagelayout">
    <al-setarg name="title"><al-value expr="foo"></al-setarg>
</al-expand>

For a more complete example:

>>> import albatross
>>> ctx = albatross.SimpleContext('.')
>>> ctx.locals.title = 'Lumberjack'
>>> albatross.Template(ctx, '<magic>', '''
... <al-macro name="pagelayout">
...  <title><al-usearg name="title"></title>
... </al-macro>
... ''').to_html(ctx)
>>> albatross.Template(ctx, '<magic>', '''
... <al-expand name="pagelayout" titleargexpr="title" />''').to_html(ctx)
>>> ctx.flush_content()
<title>Lumberjack</title>
>>> albatross.Template(ctx, '<magic>', '''
... <al-expand name="pagelayout">
...  <al-setarg name="title"><al-value expr="title"></al-setarg>
... </al-expand>''').to_html(ctx)
>>> ctx.flush_content()
<title>Lumberjack</title>

<al-setarg>

The <al-setarg> tag is used pass content to a macro. All content enclosed by the tag will be passed as an argument to the macro named by the parent <al-expand> (<al-expand>) tag.

The <al-setarg> tag is normally used to pass content to macros that define named arguments, but can also be used to enclose the unnamed argument.

>>> import albatross
>>> ctx = albatross.SimpleContext('.')
>>> albatross.Template(ctx, '<magic>', '''
... <al-macro name="title">
...  <title><al-usearg></title>
... </al-macro>
... ''').to_html(ctx)
>>> albatross.Template(ctx, '<magic>', '''
... <al-expand name="title">
...  <al-setarg>Lumberjack</al-setarg>
... </al-expand>''').to_html(ctx)
>>> ctx.flush_content()
<title>Lumberjack</title>
>>> albatross.Template(ctx, '<magic>', '''
... <al-expand name="title">
...  Lumberjack
... </al-expand>''').to_html(ctx)
>>> ctx.flush_content()
<title>Lumberjack
</title>

name="..." attribute

The name attribute is used to identify the named macro argument that will receive the enclosed content.

Table Of Contents

Previous topic

Albatross Forms Reference

Next topic

Developing Custom Tags

This Page