Creating your own active tags is quite easy and this section explains how. You
may want to create your own active tags for a number of reasons. More advanced
uses of tags include database querying, separation of business logic, or
component rendering. On the other hand, you might consider creating simpler
task-specific tag libraries. For example, if you do not wish to rely on
style-sheets you could easily define your own custom tags to perform the
formatting in a consistent manner at the server. Another convenient use for
tags is to automatically fill forms with session data. These are only a few of
the uses for tags. As you will see, writing a Spyce active tag is far
simpler than writing a JSP tag.
We begin with a basic example:
examples/myTaglib.py
from spyceTag import spyceTagLibrary, spyceTagPlus
class tag_foo(spyceTagPlus):
name = 'foo'
mustend = 1
def syntax(self):
self.syntaxPairOnly()
self.syntaxExist('val')
self.syntaxNonEmpty('val')
def begin(self, val):
val = self.contextEval(val)
self.getOut().write('<font size="%s"><b>' % str(val))
def end(self):
self.getOut().write('</b></font><br>')
class myTaglib(spyceTagLibrary):
tags = [
tag_foo,
]
Saving this code in myTaglib.py, in the same
directory as your script or anywhere else in the search path, one could then
use the foo active tag (defined above), as follows:
An active tag library can be any Python class that derives from
spyceTag.spyceTagLibrary. The interesting aspects of this class
definition to implementors are:
tags: This field is usually all that requires redefinition.
It should be a list of the classes (as opposed to instances) of the
active tags.
start(): This methd is invoked by the engine upon loading
the library. The inherited method is a noop.
finish(): This method is invoked by the engine upon
unloading the library after a request. The inherited method is a noop.
Each active tag can be any Python class that derives from
spyceTag.spyceTag. The interesting aspects of the class definition for
tag implementors are:
name: This field MUST be overidden to indicate the name of
the tag that this class defines.
buffer: This flag indicates whether the processing of the
body of the tag should be performed with the current output stream
(unbuffered) or with a new, buffered output stream. Buffering is useful for
tags that may want to transform, or otherwise use, the output of processing
their own bodies, before it is sent to the client. The inherited default is
false.
conditional: This flag indicates whether this tag may
conditionally control the execution of its body. If true, then the begin()
method of the tag must return true to process the tag body, or false to skip
it. If the flag is set to false, then return value of the begin() method is
ignored, and the body executed (unless an exception is triggered). Some
tags, such as the core:if tag, require this
functionality, and will set the flag true. Many other kinds of tags do not,
thus saving a level of indentation (which is unfortunately limited in Python
-- hence the need for this switch). The inherited default is false.
loops: This flag indicates whether this tag may want to loop
over the execution of its body. If true, then the body() method of the tag
must return true to repeat the body processing, or false to move on to the
end() of the tag. If the flag is set to false, then the return value of the
body() method is ignored, and the body is not looped. Some tags, such as the
core:for tag, require this functionality, and
will set the flag true. Many other kinds of tags do not, thus saving a level
of indentation. The inherited default is false.
catches: This flag indicates whether this tag may want to
catch any exceptions that occur during the execution of its body. If true,
then the catch() method of the tag will be invoked on exception. If the flag
is false, the exception will continue to propagate beyond this point. Some
tags, such as the core:catch, require this
functionality, and will set the flag true. Many other kinds of tags do not,
thus saving a level of indentation. The inherited default is false.
mustend: This flag indicates whether this tag wants the
end() method to get called, if the begin() completes successfully, no
matter what. In other words, the call to end() is placed in the finally
of try-finally block which begins just after the begin(). This is useful for
tag cleanup, such as in the core:let tag, which
sets this flag to true in order to ensure that variables are removed from
the tag context. However, many other tags do not perform anything in the
end() of their tag, or perhaps perform operations that are not important in
the case of an exception. Such tags do not require this functionaliy, thus
saving a level of indentation. The inherited default is false.
syntax(): This method is invoked at compile time to perform
any additional tag-specific syntax checks. The inherited method returns
None, which means that there are no syntax errors. If a syntax error is
detected, this function should return a string with a helpful message about
the problem. Alternatively, one could raise an
spyceTagSyntaxException.
begin( ... ): This method is invoked when the corresponding
start tag is encountered in the document. All the attributes of the tag are
passed in by name. This method may return a boolean flag. If
conditional is set to true (see above), then this flag indicates
whether the body of the tag should be processed (true), or skipped (false).
The inherited method performs no operation, except to return true.
body( contents ): This method is invoked when the body of
the tag has completed processing. It will be called also for a
singleton, which we assume simply has an empty body. However, it will not be
called if the begin() method has chosen to skip body processing entirely. If
the tag sets buffer to true for capturing the body processing output
(see above), then the string output of the body processing has been captured
and stored in contents. It is the responsibility of this method to
emit something, if necessary. If the tag does not buffer then
contents will be None, and any output has already been written to the
enclosing scope. If the loops flag is set to true, then this method
is expected to return a boolean flag. If the flag is true, then the body
will be processed again, followed by another invocation of this method. And
again, and again, until false is received. The inherited tag method performs
nothing and returns false.
end(): This method is invoked when the corresponding end tag
is encountered. For a singleton tag, we assume that the end tag immediately
follows the begin, and still invoke this method. If the mustend flag
has been set to true, then the runtime engine semantics ensure that if the
begin method terminates successfully, this method will get called,
even in the case of an exception during body processing. The inherited
method is a noop.
catch( ex ): If the catches flag has been set to
true, then if any exception occurs in the begin(), body() or end() methods
or from within the body processing, this method will be invoked. The
parameter ex holds the value that was thrown. The inherited method
simply re-raises the exception.
getPrefix(): Return the prefix under which this tag library
was installed.
getAttributes(): Return a dictionary of tag attributes.
getPaired(): Return true if this is a paired (open and
close) tag, or false if it is a singleton.
getParent( [name] ): Return the object of the direct parent
tag, or None if this is the root active tag. Plain (inactive) tags do not
have objects associated with them in this hierarchy. If the optional
name parameter is provided, then we search up the tree for an active
tag of the same library and with the given name. If such a tag can not be
found, then return None.
getOut(): Return the (possibly buffered) output stream that
this tag should write to.
getContext(): Return the tag context dictionary, where all
tags variables are kept and expression are evaluated. The context contains
references to each of the loaded Spyce modules. These variables may used to
access module functionality, but they should not be deleted or modified.
getBuffered(): Returns true if the tag output stream is a
local buffer, or false if the output is connected to the enlosing scope.
For convenience, tag implementors may wish to derive their implementations
from spyceTagPlus, which provides some useful additional methods:
contextSet( name, (exists,value) ): Accepts a variable
name and a tuple containing an exists flag and a value.
If the flag is true, then the variable is assigned the value within the tag
context. If the flag is false, the variable is deleted from the context
dictionary. This function returns the previous state of this variable, as
per contextGet().
contextGet( name ): Returns the current state of the
variable name in the tag context. The state is a tuple containing a
flag whether the variable is defined and its value.
contextEval( expr ): Evaluates a string expr as
follows. If the string begins with an '=', then the rest of the string is
treated as a Python expression. This is expression is evaluated within the
tag context dictionary, and the result is returned. Otherwise, the parameter
is treated as a string constant and returned as-is.
contextGetModule( name ): Return a reference to a module
from the tag context. The module is loaded, if necessary.
syntaxExist( [must]* ): Ensure that the list of attributes
given in must are all defined in the attributes of this tag.
Otherwise, a spyceTagSyntaxException is thrown.
syntaxExistOr( [mustgroups]* ): Ensure that at least one of
the lists of attributes specified in mustgroups satisfies
syntaxExist(). Otherwise, a spyceTagSyntaxException is thrown.
syntaxExistOrEx( [mustgroups]* ): Ensure that exactly one of
the lists of attributes specified in mustgroups satisfies
syntaxExist(). Otherwise, a spyceTagSyntaxException is thrown.
syntaxNonEmpty( [names]* ): Ensure that if the attributes
listed in names exist, then each of them does not contain an empty
string value. Otherwise, a spyceTagSyntaxException is thrown. Note that the
actual existence of a tag is checked by syntaxExists(), and that this method
only checks that a tag is non-empty. Specifically, there is no exception
raised from this method, if the attribute does not exist.
syntaxValidSet( name, validSet ): Ensure that the value of
the attribute name, if it exists, is one of the values in the set
validSet. Otherwise, a spyceTagSyntaxException is raised.
syntaxPairOnly(): Ensure that this tag is a paired tag.
Otherwise, a spyceTagSyntaxException is thrown.
syntaxSingleOnly(): Ensure that this tag is a singleton tag.
Otherwise, a spyceTagSyntaxException is thrown.
Despite the length of this description, most tags are trivial to write, as
shown in the initial example. The easiest way to start is by having at a look
at various implemented tag libraries, such as tags/core.py. The more curious reader is welcome to look
at the tag library internals in spyceTag.py and
modules/taglib.py. The tag semantics are ensured by
the Spyce compiler (see spyceCompile.py), though it
is likely easier simply to look at the generated Python code using the "spyce -c" command-line facility.