What is it?
License
Download
Installation
Requirements
Classes
Initialization
Objects
Access
Substitution
Attributes
Batch Substitution
State
Concatenation
Repetition
Appending
Iteration
Output
Development
webstring
A Python Template Engine
WHAT IS IT?
webstring is a template engine for programmers whose favorite template language is Python. webstring can be used to generate any text format from a template with the additional advantage of advanced XML and HTML templating using the lxml and cElementTree libraries.

webstring
is designed to:
  1. Separate model and view logic from controller logic. The advantages of an MVC architecture are:

    • Clarity: it is easy to tell which parts of an application do what. Code is cleaner, more compact, and readable, which can make errors easier to spot, a particularly important feature when more than one programmer maintains the code.
    • Modularity: model, view, or controller logic can be upgraded, reused, or replaced with little or no disruption to the other parts of an application if they are not mixed.
    • Division of Labor: designers can design the view, programmers can program the controller, and data architects can design the data model.

    webstring enforces MVC by following a push template style: the Python code using a webstring template has to explicitly push data into it because the template contains no callback logic and therefore cannot request data from the controller by itself.

  2. Separate template logic from template data. Template logic controls a template's output when when template data (the text stored in the template itself) and external data are mixed. Template data tells what a template is and template logic tells how it works. The advantages of keeping them separate are similar to the advantages of MVC:

    • Clarity: clean, maintainable code is more easily achieved when template data and logic are encapsulated in separate, clearly distinct packages. webstring keeps template data in one place and Python code, its template logic, in another.
    • Modularity: many template engines use some kind of template logic for placeholders. This tightly locks not only template logic and data together but model, view, and controller logic as well. webstring keeps template data and template logic separate. Python code does not have to know anything about a webstring template other than the name of its placeholders. Template data does not requires any modifications except for some unobstrusive placeholder identifiers.
    • Division of labor: separating template data from template logic allows for a clean separation of roles: Data architects can design text formats, designers can work on making text look good, and Python programmers can write controller code.

  3. Require only Python for templating. When templating with Python, the minimum requirement is, of course, Python. webstring goes further: its design is based on the proposition that the only tool you need to template text with Python is Python.

    By design, Python places programmer productivity ahead of computer productivity, an approach that Bruce Eckel nicely summarized as “Python fits your brain”. The average Python programmer can keep the core Python language in their head without constantly checking Python's documentation for information on basic language features. Layering another template language on top of Python increases the mental overhead of programming with few compensating advantages. Why use a dedicated template language when Python already does the job?

  4. Use Python syntax, idiom, and patterns. Making templating as "Pythonic" as possible is a major goal of webstring's design. Using webstring should be a direct and natural outgrowth of previous Python experience. webstring uses Python syntax, idiom, and patterns exclusively.

  5. Play well with other Python software. webstring is designed to be used with other Python software without adding additional complexity. Its API is Pythonic and many of its operations use standard Python syntax. All data inputs into webstring use standard Python data structures like strings, lists, and dictionaries.

    webstring also provides classes that provide a TurboGears/Buffet template plugin (TurboWebstring) and Web Server Gateway Interface (WSGI) middleware (WSGITemplate, WSGIHTMLTemplate, and WSGITextTemplate) that allows WSGI applications to dynamically output XML, HTML, and text.
webstring's XML and HTML templating support is designed to:
  1. Use XML as a data format, not a programming language. HTML and XML were never intended to be programming languages. Tim Berners-Lee, creator of the World Wide Web, HTML, and a driving force behind the standardization of XML, commented on HTML and, by implication, on XML that, "I chose HTML not to be a programming language because I wanted different programs to do different things with it: present it differently, extract tables of contents, index it, and so on." As a result of their design, HTML and XML are structured in ways that, while useful for a data format, are unnecessarily verbose, restrictive, and repetitive for a programming language, especially if the programming language is Python.

    XML is also frequently written by non-programmers. Mixing logic and XML may force non-programmers to acquire a programming awareness that may be counterproductive to their jobs. They might also be forced to wait for a programmer to change the data format before they can work on it (and vice versa).

    XML is for data. Python is for logic.

  2. Use existing XML formats. Existing XML formats often have to be modified to include template engine specific XML to work with a template engine. This requirement can violate MVC's advantages of clarity, modularity, and division of labor. It can make using software that provides special processing for a specific XML format difficult. It can lead to template engine lock-in since massive changes may be required to port a template from one template engine to another.

    webstring is designed to work with existing XML formats as they are. The only modifications to an XML document that webstring requires is assigning a unique identifier to each element used as a placeholder.

  3. Hide the parts of an XML document that are not being used. webstring uses a component API style: it only exposes elements that are marked with a placeholder and usually only "the elements in the template that need to be manipulated are marked". Each element marked with a placeholder can be accessed, evaluated, and manipulated separately. Elements and attributes that do not have a placeholder remain hidden and do not exist from the programmer or application's perspective.

  4. Use the best Python XML libraries. cElementTree provides a high performance library with a highly Pythonic API for processing XML with Python. lxml implements the same API as cElementTree but adds additional features. While cElementTree supports most of XML 1.1, XML namespaces, and parts of XPath 1.0, lxml, since it's a binding for the libxml2 and libxslt libraries, supports those standards plus XSLT, XInclude, XML Schema, RelaxNG, Xpointer, and xml:id. webstring does not force Python programmers to pick between Python's best XML processing libraries. They can use the library that best fits their needs while using the same simple yet powerful API.

  5. Bridge HTML and XML. XML purists would like to leave HTML behind. But they can't. To quote the Kid FAQ:

    Q: But isn't tag-soup like HTML 4 evil?

    A: No. HTML 4.01 isn't XML but it's the best option for serving pages at present because all browsers have relatively strong support for it. Serving XHTML as text/html has serious issues and not all browsers (IE <= 7.0) support XHTML as application/xhtml+xml.

    However, maintaining content in an XML based format is advantageous because you can perform validation, transformations, and other stuff using readily available tools. It's possible to have the best of both worlds: HTML 4.01 for the browser and XHTML (or some custom XML format) for the server.

    Some of us still believe that XHTML will eventually be supported well, and that when it does, it will be the way to go. You should be able to "flip a switch, make it work" when that day comes instead of doing a massive migration.

    webstring's HTMLTemplate class handles this transition from the past to the future seamlessly. It can take even horribly broken HTML and output it as HTML 4.01, XHTML 1.0, or XHTML 1.1 depending upon a developer's needs.

LICENSE
webstring is distributed under a BSD license.
DOWNLOAD
webstring is available from the CheeseShop as a: webstring's current release is 0.5 (1.4.2007). The change log for 0.5 is available here: CHANGELOG-0.5.

lwebstring was a standalone lxml-based implementation of webstring available from the CheeseShop as a: The last lwebstring release was 0.5 (10.5.2006). After this release, lwebstring was incorporated into webstring proper. The change log for lwebstring 0.5 is available here: CHANGELOG-lwebstring-0.5.
INSTALLATION
To install webstring using easy_install, run the following command with administrative privileges:
easy_install webstring
REQUIREMENTS
webstring requires cElementTree, elementtree, and lxml. webstring can use either cElementTree or lxml as its XML processing library. Installing webstring with easy_install automatically downloads and installs cElementTree, elementtree, and lxml if necessary along with webstring.

elementtidy is required for webstring's HTMLTemplate class if you need to handle broken HTML with cElementTree.

webstring has been tested and verified to work with Python 2.4 and Python 2.5.

CLASSES
webstring comes with the following specialized classes: These classes can be used individually or accessed through webstring's Template class, which acts as a general purpose frontend to all of webstring's specialized classes.
>>> from webstring import Template
The remaining examples in this document use webstring's XmlTemplate as the backend to Template. LxmlTemplate and EtreeTemplate have minor differences in output. In LxmlTemplate, there is no space between the tag name and the closing /> of a closed tag (i.e. <br/> instead of <br />) and in EtreeTemplate there is a space. Attribute order may also vary and namespaces may be handled differently.
INITIALIZATION
A Template object can be initialized empty:
>>> exampleA = Template()
From a file with the fromfile method:
>>> exampleB.fromfile('example.rss')
By passing the path to the file as the first argument to the class constructor:
>>> exampleC = Template('example.rss')
From a string with the fromstring method:
>>> exampleD = Template().fromstring("""<rss version="2.0">
... <channel>
... <title>Example</title>
... <link>http://www.example.org/</link>
... <description>RSS Example</description>
... <language>en-us</language>
... <pubDate id="cpubdate">$month $day, $year</pubDate>
... <lastBuildDate id="lastbuilddate">%(month)s %(day)s, %(year)s</lastBuildDate> ... <item class="item">
... <title id="title" />
... <link id="link" />
... <guid id="guid" isPermaLink="true" />
... <description id="description" />
... <pubDate id="ipubdate" />
... </item>
... </channel>
... </rss>""")
By passing a string as the first argument to the class constructor:
>>> example = Template("""<rss version="2.0">
... <channel>
... <title>Example</title>
... <link>http://www.example.org/</link>
... <description>RSS Example</description>
... <language>en-us</language>
... <pubDate id="cpubdate">$month $day, $year</pubDate>
... <lastBuildDate id="lastbuilddate">%(month)s %(day)s, %(year)s</lastBuildDate> ... <item class="item">
... <title id="title" />
... <link id="link" />
... <guid id="guid" isPermaLink="true" />
... <description id="description" />
... <pubDate id="ipubdate" />
... </item>
... </channel>
... </rss>""")
The choice of whether to use lxml and LxmlTemplate or cElementTree and EtreeTemplate is controlled by using the engine keyword passed to the object constructor:
>>> example = Template(engine="lxml")
This selects LxmlTemplate and uses lxml as the XML processing library. LxmlTemplate and lxml support additional advanced XML features like XSLT and XInclude that EtreeTemplate and cElementTree do not.

HTMLTemplate can be selected using the format keyword passed to the object constructor:
>>> example = Template(format="html", engine="lxml")
HTMLTemplate can also be told to use lxml as its HTML processing library by passing the engine keyword together with the format keyword to the object constructor:
>>> example = Template(format="html", engine="lxml")
OBJECTS
webstring automatically maps an XML document to a root object. Any XML element with an XML attribute (by default, id) that uniquely identifies it within an XML document is mapped to a field object and attached to the root object of its parent document.
<pubDate id="cpubdate">$month $day, $year</pubDate>
Groups of fields can be created by assigning a parent element an attribute (by default, class) whose value is a globally unique identifier within an XML document. webstring then maps the parent element to a group object which is attached to the root object of its parent document. Any child elements marked as fields within that parent element are mapped to field objects and attached to the group object.

Group objects cannot contain other group objects.
<item class="item">
<title id="title" />
<link id="link" />
<guid id="guid" isPermaLink="true" />
<description id="description" />
<pubDate id="ipubdate" />
</item>
XML attributes of an element marked as a field are mapped to attribute objects and attached to that element's field object.

At maximum, a root's object hierarchy will never be more than four levels deep.

Root level:
>>> example
<Template "root" at db7dd0>
Group level:
>>> example.item
<Template "item" at f5d870>
Field level:
>>> example.item.guid
<Template "guid" at f5da70>
Attribute level:
>>> example.item.guid.isPermaLink
'true'
At minimum, a root object's depth will only be two levels deep.

Root level:
>>> example
<Template "root" at db7dd0>
Field level:
>>> example.cpubdate
<Template "cpubdate" at e7a350>
By default, all elements tagged as fields or groups become part of a root's object hierarchy except for elements marked as groups that have no child elements marked as fields. However, existing fields or groups within an XML document can be excluded or included before or after a root is initialized by using its exclude or include methods.

Exclusion with the exclude method:
>>> example.exclude('cpubdate', 'guid') 
>>> print example
<rss version="2.0">
<channel>
<title>Example</title>
<link>http://www.example.org/</link>
<description>RSS Example</description>
<language>en-us</language>
<lastBuildDate id="lastbuilddate">%(month)s %(day)s, %(year)s</lastBuildDate>
<item class="item">
<title id="title" />
<link id="link" />
<description id="description" />
<pubDate id="ipubdate" />
</item>
</channel>
</rss>
Inclusion with the include method:
>>> example.include('cpubdate', 'guid')
>>> print example
<rss version="2.0">
<channel>
<title>Example</title>
<link>http://www.example.org/</link>
<description>RSS Example</description>
<language>en-us</language>
<pubDate id="cpubdate">$month $day, $year</pubDate>
<lastBuildDate id="lastbuilddate">%(month)s %(day)s, %(year)s</lastBuildDate> <item class="item">
<title id="title" />
<link id="link" />
<guid id="guid" isPermaLink="true" />
<description id="description" />
<pubDate id="ipubdate" />
</item>
</channel>
</rss>
Fields and groups can be removed by deleting their object attribute:
>>> del example.cpubdate
>>> print example
<rss version="2.0">
<channel>
<title>Example</title>
<link>http://www.example.org/</link>
<description>RSS Example</description>
<language>en-us</language>
<lastBuildDate id="lastbuilddate">%(month)s %(day)s, %(year)s</lastBuildDate>
<item class="item">
<title id="title" />
<link id="link" />
<guid id="guid" isPermaLink="true" />
<description id="description" />
<pubDate id="ipubdate" />
</item>
</channel>
</rss>
A root, field, or group can be set back to its default state by calling its reset method:
>>> example.reset()
>>> print example
<rss version="2.0">
<channel>
<title>Example</title>
<link>http://www.example.org/</link>
<description>RSS Example</description>
<language>en-us</language>
<pubDate id="cpubdate">$month $day, $year</pubDate>
<lastBuildDate id="lastbuilddate">%(month)s %(day)s, %(year)s</lastBuildDate>
<item class="item">
<title id="title" />
<link id="link" />
<guid id="guid" isPermaLink="true" />
<description id="description" />
<pubDate id="ipubdate" />
</item>
</channel>
</rss>
ACCESS
Fields can be accessed by object attribute:
>>> example.cpubdate
<Template "cpubdate" at e7a350>
By keyword:
>>> example['cpubdate']
<Template "cpubdate" at e7a350>
By position:
>>> example[0]
<Template "cpubdate" at e7a350>
Groups are accessed the same way:
>>> example.item 
<Template "item" at f5d870>
>>> example['item']
<Template "item" at f5d870>
>>> example[2]
<Template "item" at f5d870>
Fields within a group are accessed by combining its identifier and its group identifier:
>>> example.item.title 
<Template "title" at f5d9b0>
>>> example['item']['title']
<Template "title" at f5d9b0>
>>> example[2][0]
<Template "title" at f5d9b0>
Attributes can be accessed either by object attribute or by keyword:
>>> example.item.guid.isPermaLink
'true'
>>> example['item']['guid']['isPermaLink']
'true'
By default, webstring automatically maps fields, groups, and attributes to object attributes with the same name as the field, group, or attribute's identifier. This "auto-magical" behavior can be disabled by passing a boolean value of False to the class constructor as the second argument when a Template or HTMLTemplate object is initialized:
>>> exampleZ = Template('example.rss', False)
Fields, groups, and attributes can then be accessed by keyword or position but not by named object attribute.
SUBSTITUTION
Text can be substituted into a field by assigning a string to a field's text property:
>>> example.item.description.text = 'Example of assigning text to a field.'
>>> print example
<rss version="2.0">
<channel>
<title>Example</title>
<link>http://www.example.org/</link>
<description>RSS Example</description>
<language>en-us</language>
<pubDate id="cpubdate">June 06, 2006</pubDate>
<lastBuildDate id="lastbuilddate">June 06, 2006</lastBuildDate>
<item class="item">
<title id="title" />
<link id="link" />
<guid id="guid" isPermaLink="true" />
<description id="description">
Example of assigning text to a field.</description>
<pubDate id="ipubdate" />
</item>
</channel>
</rss>
Text can also be formatted by substituting text into a field's string template. There are two ways to assign a string template to a field. The first way is to embed a string template directly in the XML source:
<pubDate id="cpubdate">$month $day, $year</pubDate>
This string template follows the pattern used by Python's string.Template class where $ identifies a location (e.g. $month) where strings can be substituted into a template to make a new string. A field's template is used to format its text when a dictionary of location identifier/substitute string pairs is assigned to its text property:
>>> example.cpubdate.text = {'month':'June', 'day':'06', 'year':'2006'}
>>> print example
<rss version="2.0">
<channel>
<title>Example</title>
<link>http://www.example.org/</link>
<description>RSS Example</description>
<language>en-us</language>
<pubDate id="cpubdate">
June 06, 2006</pubDate>
<lastBuildDate id="lastbuilddate">%(month)s %(day)s, %(year)s</lastBuildDate>
<item class="item">
<title id="title" />
<link id="link" />
<guid id="guid" isPermaLink="true" />
<description id="description" />
<pubDate id="ipubdate" />
</item>
</channel>
</rss>
lastbuilddate's embedded string template follows a pattern used by Python's built-in string formatting where %(name)s identifies locations (e.g. %(month)s) where strings can be substituted into a template to make a new string:
<lastBuildDate id="lastbuilddate">%(month)s %(day)s, %(year)s</lastBuildDate>
String templates following this pattern are used exactly like string templates that follow the string.Template pattern:
>>> example.lastbuilddate.text = {'month':'June', 'day':'06', 'year':'2006'}
>>> print example
<rss version="2.0">
<channel>
<title>Example</title>
<link>http://www.example.org/</link>
<description>RSS Example</description>
<language>en-us</language>
<pubDate id="cpubdate">June 06, 2006</pubDate>
<lastBuildDate id="lastbuilddate">
June 06, 2006</lastBuildDate>
<item class="item">
<title id="title" />
<link id="link" />
<guid id="guid" isPermaLink="true" />
<description id="description" />
<pubDate id="ipubdate" />
</item>
</channel>
</rss>
String templates embedded in an XML document are automatically assigned to a field's template property:
>>> example.cpubdate.template
<string.Template object at 0x00F81F70>
>>> example.lastbuilddate.template
%(month)s %(day)s, %(year)s
The second way to assign a string template to a field is to manually assign it to a field's template property:
>>> example.item.link.template = 'http://www.example.org/rss/$id'
>>> example.item.link.template
<string.Template object at 0x00DB7E70>
>>> example.item.link.text = {'id':'5423093'}
>>> print example
<rss version="2.0">
<channel>
<title>Example</title>
<link>http://www.example.org/</link>
<description>RSS Example</description>
<language>en-us</language>
<pubDate id="cpubdate">June 06, 2006</pubDate>
<lastBuildDate id="lastbuilddate">June 06, 2006</lastBuildDate>
<item class="item">
<title id="title" />
<link id="link">
http://www.example.org/rss/5423093</link>
<guid id="guid" isPermaLink="true" />
<description id="description">Example of assigning text to a field.</description>
<pubDate id="ipubdate" />
</item>
</channel>
</rss>
String templates can be assigned to multiple fields in one operation by using a root or group's templates method:
>>> example.item.templates({'title':{'text':'Example Title: $content'},
... 'ipubdate':{'text':'$month $day, $year'}})
>>> example.item.title.template
<string.Template object at 0x00EA75B0>
>>> example.item.ipubdate.template
<string.Template object at 0x00EA7690>
>>> example.item.title.text = {'content':'First Example'}
>>> example.item.ipubdate.text = {'month':'June', 'day':'6', 'year':'2006'}
>>> print example
<rss version="2.0">
<channel>
<title>Example</title>
<link>http://www.example.org/</link>
<description>RSS Example</description>
<language>en-us</language>
<pubDate id="cpubdate">June 06, 2006</pubDate>
<lastBuildDate id="lastbuilddate">June 06, 2006</lastBuildDate>
<item class="item">
<title id="title">
Example Title: First Example</title>
<link id="link">http://www.example.org/rss/5423093</link>
<guid id="guid" isPermaLink="true" />
<description id="description">Example of assigning text to a field.</description>
<pubDate id="ipubdate">
June 6, 2006</pubDate>
</item>
</channel>
</rss>
ATTRIBUTES
The value of an XML attribute can be set by assigning a string to its object attribute:
>>> example.item.guid.isPermaLink = 'false'
>>> print example
<rss version="2.0">
<channel>
<title>Example</title>
<link>http://www.example.org/</link>
<description>RSS Example</description>
<language>en-us</language>
<pubDate id="cpubdate">June 06, 2006</pubDate>
<lastBuildDate id="lastbuilddate">June 06, 2006</lastBuildDate>
<item class="item">
<title id="title">Example Title: First Example</title>
<link id="link">http://www.example.org/rss/5423093</link>
<guid id="guid" isPermaLink=
"false" />
<description id="description">Example of assigning text to a field.</description>
<pubDate id="ipubdate">June 6, 2006</pubDate>
</item>
</channel>
</rss>
One or more existing attribute values can be changed or one or more new attributes added using a field's update method:
>>> example.item.guid.update({'isPermaLink':'true', 'id':'GUID',
... '{http://www.w3.org/1999/02/22-rdf-syntax-ns#}resource':'http://www.example.org/rss/5423093'})
>>> print example
<rss version="2.0">
<channel>
<title>Example</title>
<link>http://www.example.org/</link>
<description>RSS Example</description>
<language>en-us</language>
<pubDate id="cpubdate">June 06, 2006</pubDate>
<lastBuildDate id="lastbuilddate">June 06, 2006</lastBuildDate>
<item class="item">
<title id="title">Example Title: First Example</title>
<link id="link">http://www.example.org/rss/5423093</link>
<guid
id="GUID" isPermaLink="true" rdf:resource="http://www.example.org/rss/5423093" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" />
<description id="description">Example of assigning text to a field.</description>
<pubDate id="ipubdate">June 6, 2006</pubDate>
</item>
</channel>
</rss>
One or more string templates for formatting attribute values can be set by assigning a dictionary of attribute name/attribute template pairs to a field's atemplates property:
>>> example.item.guid.atemplates = {
... '{http://www.w3.org/1999/02/22-rdf-syntax-ns#}resource':'http://www.example.org/rss/$guid'}
The attribute value is then formatted by the attribute's template when a dictionary of attribute name/dictionary pairs is passed to the attribute either with the update method:
>>> example.item.guid.update({
... '{http://www.w3.org/1999/02/22-rdf-syntax-ns#}resource':{'guid':'5423094'}})
or by assigning a dictionary of attribute name/dictionary pairs directly to its object attribute:
>>> example.item.guid.resource = {'guid':'5423093'}
>>> print example
<rss version="2.0">
<channel>
<title>Example</title>
<link>http://www.example.org/</link>
<description>RSS Example</description>
<language>en-us</language>
<pubDate id="cpubdate">June 06, 2006</pubDate>
<lastBuildDate id="lastbuilddate">June 06, 2006</lastBuildDate>
<item class="item">
<title id="title">Example Title: First Example</title>
<link id="link">http://www.example.org/rss/5423093</link>
<guid id="GUID" isPermaLink="true"
rdf:resource="http://www.example.org/rss/5423093" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" />
<description id="description">Example of assigning text to a field.</description>
<pubDate id="ipubdate">June 6, 2006</pubDate>
</item>
</channel>
</rss>
One or more attributes can be removed by using a field's purge method:
>>> example.item.guid.purge('{http://www.w3.org/1999/02/22-rdf-syntax-ns#}resource')
>>> print example
<rss version="2.0">
<channel>
<title>Example</title>
<link>http://www.example.org/</link>
<description>RSS Example</description>
<language>en-us</language>
<pubDate id="cpubdate">June 06, 2006</pubDate>
<lastBuildDate id="lastbuilddate">June 06, 2006</lastBuildDate>
<item class="item">
<title id="title">Example Title: First Example</title>
<link id="link">http://www.example.org/rss/5423093</link>
<guid id="GUID" isPermaLink="true" />
<description id="description">Example of assigning text to a field.</description>
<pubDate id="ipubdate">June 6, 2006</pubDate>
</item>
</channel>
</rss>
An attribute can also be removed by deleting its object attribute:
>>> example.item.guid.update(
... {'{http://www.w3.org/1999/02/22-rdf-syntax-ns#}resource':'http://www.example.org/rss/5423093'})
>>> del example.item.guid.resource
>>> print example
<rss version="2.0">
<channel>
<title>Example</title>
<link>http://www.example.org/</link>
<description>RSS Example</description>
<language>en-us</language>
<pubDate id="cpubdate">June 06, 2006</pubDate>
<lastBuildDate id="lastbuilddate">June 06, 2006</lastBuildDate>
<item class="item">
<title id="title">Example Title: First Example</title>
<link id="link">http://www.example.org/rss/5423093</link>
<guid id="guid" isPermaLink="false" />
<description id="description">Example of assigning text to a field.</description>
<pubDate id="ipubdate">June 6, 2006</pubDate>
</item>
</channel>
</rss>
BATCH SUBSTITUTION
The modulo operator can be used to assign templates and substitute text into elements and attributes in one batch substitution. Using the modulo operator without the assignment operator (%) creates a new object. Using the modulo operator with the assignment operator (%=) modifies the left-hand object.

In a batch substitution, text substitutions are given the keyword 'text' and passed in a dictionary to a field:
>>> example.reset()
>>> example.cpubdate %= {'text':{'month':'June', 'day':'06', 'year':'2006'}}
>>> example.lastbuilddate %= {'text':{'month':'June', 'day':'06', 'year':'2006'}}
Template assignments are identified by the keyword 'templates' and text substitutions are identified by the keyword 'subs':
>>> example.item %= {
... 'templates':{
... 'title':{'text':'Example Title: $content'},
... 'ipubdate':{'text':'$month $day, $year'},
... 'link':{'text':'http://www.example.org/rss/$id'}},
... 'subs':((
... {'text':{'content':'First Example'}},
... {'text':{'id':'5423093'}}, {'attrib':{'id':'GUID'}},
... {'text':'http://www.example.org/rss/5423093'},
... 'Example of assigning text to a field.',
... {'text':{'month':'June', 'day':'6', 'year':'2006'}}),)}
>>> print example
<rss version="2.0">
<channel>
<title>Example</title>
<link>http://www.example.org/</link>
<description>RSS Example</description>
<language>en-us</language>
<pubDate id="cpubdate">June 06, 2006</pubDate>
<lastBuildDate id="lastbuilddate">June 06, 2006</lastBuildDate>
<item class="item">
<title id="title">
Example Title: First Example</title>
<link id="link">
http://www.example.org/rss/5423093</link>
<guid id="GUID" isPermaLink="true">
http://www.example.org/rss/5423093</guid>
<description id="description">
Example of assigning text to a field.</description>
<pubDate id="ipubdate">
June 6, 2006</pubDate>
</item>
</channel>
</rss>
Templates are stored in a dictionary under the name of the field they are assigned to (e.g. 'title'). This dictionary can contain any number of templates up to but not exceeding the number of fields in the root or group to which it is passed. Text templates are string templates that are stored under the keyword 'text'. Attribute templates, stored under the keyword 'attrib', are a dictionary of pairs consisting of an attribute name and a string template .

Substitutions are stored in a tuple. The number of items in a substitution's tuple must be equal to the number of fields in the root or group to which it is passed. Each item in a substitution's tuple can be a string or a dictionary of substitutions. Within a dictionary of substitutions, the keyword 'text' identifies text substitutions, the value of which can be either a string or a dictionary of text template substitutions, and the keyword 'attrib' identifies attribute substitutions, the value of which is a dictionary containing pairs of attribute names and strings or dictionaries of attribute template substitutions. All tuples containing substitutions are put in another tuple and stored under the 'subs' keyword.

The modulo operator can also substitute a dictionary containing pairs of field names and substitutions into every field or group in a root object in one operation:
>>> example %= {
... 'cpubdate':{'text':{'month':'June', 'day':'06', 'year':'2006'}},
... 'lastbuilddate':{'text':{'month':'June', 'day':'06', 'year':'2006'}},
... 'item':{'templates':{'title':{'text':'Example Title: $content'}, ... 'ipubdate':{'text':'$month $day, $year'},
... 'link':{'text':'http://www.example.org/rss/$id'}},
... 'title':{'text':{'content':'First Example'}},
... 'link':{'text':{'id':'5423093'}},
... 'guid':{'attrib':{'id':'GUID'}, 'text':'http://www.example.org/rss/5423093'},
... 'description':{'text':'Example of assigning text to a field.'},
... 'ipubdate':{'text':{'month':'June', 'day':'6', 'year':'2006'}}}}
>>> print example
<rss version="2.0">
<channel>
<title>Example</title>
<link>http://www.example.org/</link>
<description>RSS Example</description>
<language>en-us</language>
<pubDate id="cpubdate">
June 06, 2006</pubDate>
<lastBuildDate id="lastbuilddate">
June 06, 2006</lastBuildDate>
<item class="item">
<title id="title">
Example Title: First Example</title>
<link id="link">
http://www.example.org/rss/5423093</link>
<guid id=
"GUID" isPermaLink="true">http://www.example.org/rss/5423093</guid>
<description id="description">
Example of assigning text to a field.</description>
<pubDate id="ipubdate">
June 06, 2006</pubDate>
</item>
</channel>
</rss>
STATE
Each root, field, or group has a current and a default property. The current property returns the object's current state:
>>> example.current
<Template "root" at dcd290>
>>> print example.current
<rss version="2.0">
<channel>
<title>Example</title>
<link>http://www.example.org/</link>
<description>RSS Example</description>
<language>en-us</language>
<pubDate id="cpubdate">June 06, 2006</pubDate>
<lastBuildDate id="lastbuilddate">June 06, 2006</lastBuildDate>
<item class="item">
<title id="title">Example Title: First Example</title>
<link id="link">http://www.example.org/rss/5423093</link>
<guid id="GUID" isPermaLink="true">http://www.example.org/rss/5423093</guid>
<description id="description">Example of assigning text to a field.</description>
<pubDate id="ipubdate">June 6, 2006</pubDate>
</item>
</channel>
</rss>
The default property returns the object's default state:
>>> example.default
<Template "root" at f62d10>
>>> print example.default
<rss version="2.0">
<channel>
<title>Example</title>
<link>http://www.example.org/</link>
<description>RSS Example</description>
<language>en-us</language>
<pubDate id="cpubdate">$month $day, $year</pubDate>
<lastBuildDate id="lastbuilddate">%(month)s %(day)s, %(year)s</lastBuildDate>
<item class="item">
<title id="title" />
<link id="link" />
<guid id="guid" isPermaLink="true" />
<description id="description" />
<pubDate id="ipubdate" />
</item>
</channel>
</rss>
CONCATENATION
Roots, fields, and groups can be concatenated together using the addition operator.

Roots:
>>> example.reset()
>>> print example + example
<rss version="2.0">
<channel>
<title>Example</title>
<link>http://www.example.org/</link>
<description>RSS Example</description>
<language>en-us</language>
<pubDate id="cpubdate">$month $day, $year</pubDate>
<lastBuildDate id="lastbuilddate">%(month)s %(day)s, %(year)s</lastBuildDate>
<item class="item">
<title id="title" />
<link id="link" />
<guid id="guid" isPermaLink="true" />
<description id="description" />
<pubDate id="ipubdate" />
</item>
</channel>
<rss version="2.0"> <channel>
<title>Example</title>
<link>http://www.example.org/</link>
<description>RSS Example</description>
<language>en-us</language>
<pubDate id="cpubdate">$month $day, $year</pubDate>
<lastBuildDate id="lastbuilddate">%(month)s %(day)s, %(year)s</lastBuildDate>
<item class="item">
<title id="title" />
<link id="link" />
<guid id="guid" isPermaLink="true" />
<description id="description" />
<pubDate id="ipubdate" />
</item>
</channel> </rss>
</rss>
Groups:
>>> print example.item + example.item
<item class="item">
<title id="title" />
<link id="link" />
<guid id="guid" isPermaLink="true" />
<description id="description" />
<pubDate id="ipubdate" />
</item>
<item class="item">
<title id="title" />
<link id="link" />
<guid id="guid" isPermaLink="true" />
<description id="description" />
<pubDate id="ipubdate" />
</item>
Fields:
>>> print example.item.title + example.item.title
<title id="title" />
<title id="title" />
Using the addition operator (+) without the assignment operator (=) results in a new object. Using the addition and assignment operators together (+=) modifies the left-hand object:
>>> example.item += example.item
>>> print example
<rss version="2.0">
<channel>
<title>Example</title>
<link>http://www.example.org/</link>
<description>RSS Example</description>
<language>en-us</language>
<pubDate id="cpubdate">$month $day, $year</pubDate>
<lastBuildDate id="lastbuilddate">%(month)s %(day)s, %(year)s</lastBuildDate>
<item class="item">
<title id="title" />
<link id="link" />
<guid id="guid" isPermaLink="true" />
<description id="description" />
<pubDate id="ipubdate" />
</item>
<item class="item">
<title id="title" />
<link id="link" />
<guid id="guid" isPermaLink="true" />
<description id="description" />
<pubDate id="ipubdate" />
</item>

</channel>
</rss>
REPETITION
A root, group, or field can be repeated once by calling its repeat method:
>>> example.reset()
>>> example.item.repeat()
>>> print example
<rss version="2.0">
<channel>
<title>Example</title>
<link>http://www.example.org/</link>
<description>RSS Example</description>
<language>en-us</language>
<pubDate id="cpubdate">$month $day, $year</pubDate>
<lastBuildDate id="lastbuilddate">%(month)s %(day)s, %(year)s</lastBuildDate>
<item class="item">
<title id="title" />
<link id="link" />
<guid id="guid" isPermaLink="true" />
<description id="description" />
<pubDate id="ipubdate" />
</item>
<item class="item">
<title id="title" />
<link id="link" />
<guid id="guid" isPermaLink="true" />
<description id="description" />
<pubDate id="ipubdate" />
</item>

</channel>

</rss>
A root, group, or field can be repeated more than once by using the multiplication operator, either by itself (*), which returns a new object, or along with the assignment operator (*=), which modifies the left-hand object:
>>> example.reset()
>>> example.item *= 2
>>> print example
<rss version="2.0">
<channel>
<title>Example</title>
<link>http://www.example.org/</link>
<description>RSS Example</description>
<language>en-us</language>
<pubDate id="cpubdate">$month $day, $year</pubDate>
<lastBuildDate id="lastbuilddate">%(month)s %(day)s, %(year)s</lastBuildDate>
<item class="item">
<title id="title" />
<link id="link" />
<guid id="guid" isPermaLink="true" />
<description id="description" />
<pubDate id="ipubdate" />
</item>
<item class="item">
<title id="title" />
<link id="link" />
<guid id="guid" isPermaLink="true" />
<description id="description" />
<pubDate id="ipubdate" />
</item>

</channel>
</rss>
A root, field, or group can be repeated and have text substituted into it by passing a string, tuple, or dictionary to its repeat method:
>>> example.reset()
>>> example.item %= ( ... 'Example Title: First Example', ... 'http://www.example.org/rss/5423092', ... 'http://www.example.org/rss/5423092', ... 'Example of assigning text to a field.', ... 'June 06, 2006')
>>> example.item.repeat(( ... 'Example Title: Second Example', ... 'http://www.example.org/rss/5423093', ... 'http://www.example.org/rss/5423093', ... 'Example of group repetition.', ... 'June 06, 2006'))
>>> print example
<rss version="2.0">
<channel>
<title>Example</title>
<link>http://www.example.org/</link>
<description>RSS Example</description>
<language>en-us</language>
<pubDate id="cpubdate">$month $day, $year</pubDate>
<lastBuildDate id="lastbuilddate">%(month)s %(day)s, %(year)s</lastBuildDate>
<item class="item">
<title id="title">
Example Title: First Example</title>
<link id="link">
http://www.example.org/rss/5423092</link>
<guid id="guid" isPermaLink="true">
http://www.example.org/rss/5423092</guid>
<description id="description">
Example of assigning text to a field.</description>
<pubDate id="ipubdate">
June 06, 2006</pubDate>
</item>
<item class="item">
<title id="title">Example Title: Second Example</title>
<link id="link">http://www.example.org/rss/5423093</link>
<guid id="guid" isPermaLink="true">http://www.example.org/rss/5423093</guid>
<description id="description">Example of group repetition.</description>
<pubDate id="ipubdate">June 06, 2006</pubDate>
</item>

</channel>
</rss>
A root, field, or group can be expanded to fit a sequence of substitutions by using the power operator (**) either alone, which returns a new object, or joined with the assignment operator (**=), which modifies the left-hand object:
>>> example.reset()
>>> example.item **= (( ... 'Example Title: First Example', ... 'http://www.example.org/rss/5423092', ... 'http://www.example.org/rss/5423092', ... 'Example of assigning text to a field.', ... 'June 06, 2006'), ( ... 'Example Title: Second Example', ... 'http://www.example.org/rss/5423093', ... 'http://www.example.org/rss/5423093', ... 'Example of group repetition.', ... 'June 06, 2006'))
>>> print example
<rss version="2.0">
<channel>
<title>Example</title>
<link>http://www.example.org/</link>
<description>RSS Example</description>
<language>en-us</language>
<pubDate id="cpubdate">$month $day, $year</pubDate>
<lastBuildDate id="lastbuilddate">%(month)s %(day)s, %(year)s</lastBuildDate>
<item class="item">
<title id="title">
Example Title: First Example</title>
<link id="link">
http://www.example.org/rss/5423092</link>
<guid id="guid" isPermaLink="true">
http://www.example.org/rss/5423092</guid>
<description id="description">
Example of assigning text to a field.</description>
<pubDate id="ipubdate">
June 06, 2006</pubDate>
</item>
<item class="item">
<title id="title">Example Title: Second Example</title>
<link id="link">http://www.example.org/rss/5423093</link>
<guid id="guid" isPermaLink="true">http://www.example.org/rss/5423093</guid>
<description id="description">Example of group repetition.</description>
<pubDate id="ipubdate">June 06, 2006</pubDate>
</item>

</channel>
</rss>
Expansion can be combined with template assignment into a single batch substitution using the modulo operator:
>>> example.reset()
>>> example.item %= { ... 'templates':{ ... 'title':{'text':'Example Title: $content'}, ... 'ipubdate':{'text':'$month $day, $year'}, ... 'link':{'text':'http://www.example.org/rss/$id'}},
... 'subs':( ... ({'text':{'content':'First Example'}}, ... {'text':{'id':'5423092'}}, ... 'http://www.example.org/rss/5423092',
... 'Example of assigning text to a field.', ... {'text':{'year':'June', 'day':'06', 'year':'2006'}}),
... ({'text':{'content':'Second Example'}}, ... {'text':{'id':'5423093'}}, ... 'http://www.example.org/rss/5423093',
... 'Example of group repetition.', ... {'text':{'month':'June', 'day':'06', 'year':'2006'}}))}
>>> print example
<rss version="2.0">
<channel>
<title>Example</title>
<link>http://www.example.org/</link>
<description>RSS Example</description>
<language>en-us</language>
<pubDate id="cpubdate">$month $day, $year</pubDate>
<lastBuildDate id="lastbuilddate">%(month)s %(day)s, %(year)s</lastBuildDate>
<item class="item">
<title id="title">
Example Title: First Example</title>
<link id="link">
http://www.example.org/rss/5423092</link>
<guid id="guid" isPermaLink="true">
http://www.example.org/rss/5423092</guid>
<description id="description">
Example of assigning text to a field.</description>
<pubDate id="ipubdate">
June 06, 2006</pubDate>
</item>
<item class="item">
<title id="title">Example Title: Second Example</title>
<link id="link">http://www.example.org/rss/5423093</link>
<guid id="guid" isPermaLink="true">http://www.example.org/rss/5423093</guid>
<description id="description">Example of group repetition.</description>
<pubDate id="ipubdate">June 06, 2006</pubDate>
</item>

</channel>
</rss>
The number of items in each tuple of substitutions must be equal to the number of fields in the root or group to which it is passed. These tuples are grouped together in another tuple and stored in the dictionary under the keyword 'subs'.

The maximum number of repetitions that can be performed on a root, field, or group is controlled by the max property. Assigning a new maximum number of repetitions to the max property assigns the same number to a root or group's child fields or groups. The default value of the max property is 25. Exceeding the maximum repetition value assigned to the max property triggers an exception:
>>> example.reset()
>>> example.max = 1
>>> example.item %= { ... 'templates':{ ... 'title':{'text':'Example Title: $content'}, ... 'ipubdate':{'text':'$month $day, $year'}, ... 'link':{'text':'http://www.example.org/rss/$id'}},
... 'subs':( ... ({'text':{'content':'First Example'}}, ... {'text':{'id':'5423092'}}, ... 'http://www.example.org/rss/5423092',
... 'Example of assigning text to a field.', ... {'text':{'year':'June', 'day':'06', 'year':'2006'}}),
... ({'text':{'content':'Second Example'}}, ... {'text':{'id':'5423093'}}, ... 'http://www.example.org/rss/5423093',
... 'Example of group repetition.', ... {'text':{'month':'June', 'day':'06', 'year':'2006'}}))}
Traceback (most recent call last):
File "<interactive input>", line 1, in ?
File "webstring.py", line 641, in __imod__
self.__ipow__(data.pop('subs'))
File "webstring.py", line 276, in __ipow__
raise TypeError(_exceptions[0])
TypeError: maximum allowed repetitions exceeded
Setting the max property back to a number greater that 1 allows the operation to be completed successfully:
>>> example.reset()
>>> example.max = 1
>>> example.item %= { ... 'templates':{ ... 'title':{'text':'Example Title: $content'}, ... 'ipubdate':{'text':'$month $day, $year'}, ... 'link':{'text':'http://www.example.org/rss/$id'}},
... 'subs':( ... ({'text':{'content':'First Example'}}, ... {'text':{'id':'5423092'}}, ... 'http://www.example.org/rss/5423092',
... 'Example of assigning text to a field.', ... {'text':{'year':'June', 'day':'06', 'year':'2006'}}),
... ({'text':{'content':'Second Example'}}, ... {'text':{'id':'5423093'}}, ... 'http://www.example.org/rss/5423093',
... 'Example of group repetition.', ... {'text':{'month':'June', 'day':'06', 'year':'2006'}}))}
>>>
APPENDING
Other Template objects can be appended to a root, field, or group using its append method:
>>> example.reset()
>>> example.cpubdate %= {'text':{'month':'June', 'day':'06', 'year':'2006'}}
>>> example.lastbuilddate %= {'text':{'month':'June', 'day':'06', 'year':'2006'}}
>>> example.item %= { ... 'templates':{ ... 'title':{'text':'Example Title: $content'}, ... 'ipubdate':{'text':'$month $day, $year'}, ... 'link':{'text':'http://www.example.org/rss/$id'}},
... 'subs':( ... ({'text':{'content':'First Example'}}, ... {'text':{'id':'5423092'}}, ... 'http://www.example.org/rss/5423092',
... 'Example of assigning text to a field.', ... {'text':{'year':'June', 'day':'06', 'year':'2006'}}),
... ({'text':{'content':'Second Example'}}, ... {'text':{'id':'5423093'}}, ... 'http://www.example.org/rss/5423093',
... 'Example of group repetition.', ... {'text':{'month':'June', 'day':'06', 'year':'2006'}}))}
>>> example.item.description.append(Template( ... '<html><head><title>Example</title></head><body><p>Paragraph</p></body></html>'))
>>> del example.item.description.text
>>> print example
<rss version="2.0">
<channel>
<title>Example</title>
<link>http://www.example.org/</link>
<description>RSS Example</description>
<language>en-us</language>
<pubDate id="cpubdate">June 06, 2006</pubDate>
<lastBuildDate id="lastbuilddate">June 06, 2006</lastBuildDate>
<item class="item">
<title id="title">Example Title: First Example</title>
<link id="link">http://www.example.org/rss/5423092</link>
<guid id="guid" isPermaLink="true">http://www.example.org/rss/5423092</guid>
<description id="description">
<html><head><title>Example</title></head><body><p>Paragraph</p></body></html></description>
<pubDate id="ipubdate">June 06, 2006</pubDate>
</item>
<item class="item">
<title id="title">Example Title: Second Example</title>
<link id="link">http://www.example.org/rss/5423093</link>
<guid id="guid" isPermaLink="true">http://www.example.org/rss/5423093</guid>
<description id="description">Example of group repetition.</description>
<pubDate id="ipubdate">June 06, 2006</pubDate>
</item>
</channel>
</rss>
ITERATION
Performing a loop operation on a root or group iterates over its fields or groups in sequence:
>>> for field in example: field.__repr__()
...
'<Template "cpubdate" at f62f50>'
'<Template "lastbuilddate" at e7a350>'
'<Template "item" at f627f0>'
Performing a loop operation on a field iterates over its attributes.
>>> for attr in example.item.guid: print attr.__repr__()
...
'isPermaLink'
'id'
OUTPUT
Identifier attributes for fields can be removed by deleting a root, field, or group's mark property. This operation also deletes field identifier attributes from any child fields or groups:
>>> del example.mark
>>> print example
<rss version="2.0">
<channel>
<title>Example</title>
<link>http://www.example.org/</link>
<description>RSS Example</description>
<language>en-us</language>
<pubDate>June 06, 2006</pubDate>
<lastBuildDate>June 06, 2006</lastBuildDate>
<item class="item">
<title>Example Title: First Example</title>
<link>http://www.example.org/rss/5423092</link>
<guid isPermaLink="true">http://www.example.org/rss/5423092</guid>
<description><html><head><title>Example</title></head><body><p>Paragraph</p></body></html></description>
<pubDate>June 06, 2006</pubDate>
</item>
<item class="item">
<title>Example Title: Second Example</title>
<link>http://www.example.org/rss/5423093</link>
<guid isPermaLink="true">http://www.example.org/rss/5423093</guid>
<description>Example of group repetition.</description>
<pubDate>June 06, 2006</pubDate>
</item>
</channel>
</rss>
Deleting the groupmark property deletes all group identifier attributes:
>>> del example.groupmark
>>> print example
<rss version="2.0">
<channel>
<title>Example</title>
<link>http://www.example.org/</link>
<description>RSS Example</description>
<language>en-us</language>
<pubDate>June 06, 2006</pubDate>
<lastBuildDate>June 06, 2006</lastBuildDate>
<item>
<title>Example Title: First Example</title>
<link>http://www.example.org/rss/5423092</link>
<guid isPermaLink="true">http://www.example.org/rss/5423092</guid>
<description><html><head><title>Example</title></head><body><p>Paragraph</p></body></html></description>
<pubDate>June 06, 2006</pubDate>
</item>
<item>
<title>Example Title: Second Example</title>
<link>http://www.example.org/rss/5423093</link>
<guid isPermaLink="true">http://www.example.org/rss/5423093</guid>
<description>Example of group repetition.</description>
<pubDate>June 06, 2006</pubDate>
</item>
</channel>
</rss>
The render method is called to output an XML document as a string:
>>> print example.render()
<rss version="2.0">
<channel>
<title>Example</title>
<link>http://www.example.org/</link>
<description>RSS Example</description>
<language>en-us</language>
<pubDate>June 06, 2006</pubDate>
<lastBuildDate>June 06, 2006</lastBuildDate>
<item>
<title>Example Title: First Example</title>
<link>http://www.example.org/rss/5423092</link>
<guid isPermaLink="true">http://www.example.org/rss/5423092</guid>
<description><html><head><title>Example</title></head><body><p>Paragraph</p></body></html></description>
<pubDate>June 06, 2006</pubDate>
</item>
<item>
<title>Example Title: Second Example</title>
<link>http://www.example.org/rss/5423093</link>
<guid isPermaLink="true">http://www.example.org/rss/5423093</guid>
<description>Example of group repetition.</description>
<pubDate>June 06, 2006</pubDate>
</item>
</channel>
</rss>
The write method is called to write an XML document to a file:
>>> example.write('finalexample.rss')
The pipe method outputs an XML document as a string and resets it back to its default state:
>>> print example.pipe()
<rss version="2.0">
<channel>
<title>Example</title>
<link>http://www.example.org/</link>
<description>RSS Example</description>
<language>en-us</language>
<pubDate>June 06, 2006</pubDate>
<lastBuildDate>June 06, 2006</lastBuildDate>
<item>
<title>Example Title: First Example</title>
<link>http://www.example.org/rss/5423092</link>
<guid isPermaLink="true">http://www.example.org/rss/5423092</guid>
<description><html><head><title>Example</title></head><body><p>Paragraph</p></body></html></description>
<pubDate>June 06, 2006</pubDate>
</item>
<item>
<title>Example Title: Second Example</title>
<link>http://www.example.org/rss/5423093</link>
<guid isPermaLink="true">http://www.example.org/rss/5423093</guid>
<description>Example of group repetition.</description>
<pubDate>June 06, 2006</pubDate>
</item>
</channel>
</rss>

>>> print example
<rss version="2.0">
<channel>
<title>Example</title>
<link>http://www.example.org/</link>
<description>RSS Example</description>
<language>en-us</language>
<pubDate id="cpubdate">$month $day, $year</pubDate>
<lastBuildDate id="lastbuilddate">%(month)s %(day)s, %(year)s</lastBuildDate>
<item class="item">
<title id="title" />
<link id="link" />
<guid id="guid" isPermaLink="true" />
<description id="description" />
<pubDate id="ipubdate" />
</item>
</channel>
</rss>
DEVELOPMENT
Send webstring feature requests, patches, and bug reports to (email address imaged and scrambled for spam-protection):
Contact

Valid XHTML 1.1