spyce
         
home     documentation     download     Spyce logo


Documentation - Tags

Prev: 3.9.4 - Writing Tag Libraries Up: 3.9 - Tags Next: 3.10 - Installation

3.9.5. Writing Tag Libraries the hard way

You can still write tag libraries manually if the tag declaration language doesn't give you the tools you need. (About the only functionality it doesn't currently expose is the looping ability used in spy:for.) You may wish to approximate what you want with a new-style tag library and examine the compiler's output (spyceCmd.py -c).

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.syntaxNonEmpty('val')
  def begin(self, 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:

examples/tag.spy
[[.taglib name=myTaglib as=me]]
<html><body>
[[ for x in range(2, 6):{ ]]
  <me:foo val="=x">size [[= x ]]</me:foo>
[[ } ]]
</body></html>
Run this code

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 clause of the try-finally block which begins just after the begin(). This is useful for tag cleanup. However, many 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.
  • getBuffered():
    Returns true if the tag output stream is a local buffer, or false if the output is connected to the enlosing scope.
Note that Spyce goes to a lot of effort to avoid unnecessary indentation in the code it generates for Active Tags. A limitation of the Python runtime is that the level of indentation within any method is limited by a compile-time constant. You can change it, of course, but in most Python distributions as of this writing (Feb 2005), this is currently set to 100. See for instance this thread from python-bugs.

For convenience, tag implementors may wish to derive their implementations from spyceTagPlus, which provides some useful additional methods:

  • getModule( name ):
    Return a reference to a module from the page context. The module is loaded, if necessary.
  • syntaxExist( [must]* ):
    Removed in 2.0; Spyce now checks the signature of the begin method; arguments with no default are enforced as required automatically.

  • 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.


Prev: 3.9.4 - Writing Tag Libraries Up: 3.9 - Tags Next: 3.10 - Installation


Spyce logo
Python Server Pages
version 2.1.3
Spyce Powered SourceForge Logo