home *** CD-ROM | disk | FTP | other *** search
Text File | 2004-05-18 | 182.3 KB | 3,994 lines |
- [[.compact]]
- [[.import names="include,stdout,transform,toc"]]
- [[ include.spyce('inc/head.spi', {'pagename': 'Documentation', 'page': 'doc.html'}) ]]
- [[.include file="inc/static.spi"]]
-
- [[\
- import verchk, re, os, string, sys
- import spyceModule, spyce
-
- def escapeHTMLandCode(html):
- import re
- html = re.sub(r'\<', '<', html)
- html = re.sub(r'\>', '>', html)
- html = re.sub(r'\[\+', '['*2, html)
- html = re.sub(r'\+\]', ']'*2, html)
- return html
-
- # single and multipage
- sectionbegin = 1
- try:
- singlepage # does it exist?
- multipage = 0
- except: multipage = 1
-
- # table of contents functions
- def genTOClink(tag, multipage=multipage):
- import os.path
- if multipage:
- filename = os.path.splitext(os.path.basename(request.filename()))[0]
- if tag=='root': return "%s.html" % filename
- else: return "%s-%s.html" % (filename, tag)
- else: return '#%s' % tag
-
- def getNumData(tag):
- import string
- data = None
- num = ''
- if tag:
- data = str(toc.getData(tag))
- num = toc.getNumbering(tag)
- if num: num = string.join(map(str, num),'.')
- return num, data
-
- def getLink(tag, genTOClink=genTOClink, getNumData=getNumData):
- num, data = getNumData(tag)
- if tag:
- if num: return '<a href="%s">%s - %s</a>' % (genTOClink(tag), num, data)
- else: return '<a href="%s">%s</a>' % (genTOClink(tag), data)
- else: return 'None'
-
- def emitLocation(tag, getLink=getLink):
- next = getLink(toc.getNextTag(tag))
- prev = getLink(toc.getPrevTag(tag))
- up = getLink(toc.getParentTag(tag))
- print '''
- <table width="100%s" align=center><tr>
- <td align=left width="33%s" nowrap><font face="arial, helvetica" size="-1"><b>Prev:</b> %s</font></td>
- <td align=center width="33%s" nowrap><font face="arial, helvetica" size="-1"><b>Up:</b> %s</font></td>
- <td align=right width="33%s" nowrap><font face="arial, helvetica" size="-1"><b>Next:</b> %s</font></td>
- </tr></table>''' % ('%', '%', prev, '%', up, '%', next)
-
- def emitChildren(tag, getLink=getLink):
- nodes = toc.getChildrenTags(tag)
- if not nodes: return
- links = map(getLink, nodes)
- print '<p>'
- print '<b>Sub-sections:</b>'
- print '<ul>'
- for l in links:
- print '<li>%s</li>' % l
- print '</ul>'
-
- def emitHeader(tag, emitLocation=emitLocation, genTOClink=genTOClink):
- tag2 = toc.getTag()
- pagename, page = 'Documentation', 'doc.html'
- while tag2:
- titles = {
- 'intro': ('Introduction', 'intro'),
- 'lang': ('Language', 'lang'),
- 'runtime': ('Runtime', 'runtime'),
- 'mod': ('Modules', 'mod'),
- 'tag': ('Tags', 'tag'),
- 'conf': ('Install', 'conf'),
- 'add_perf': ('Performance', None),
- 'add_history': ('History', None),
- 'add_related': ('Related', None),
- }
- if titles.has_key(tag2):
- pagename = pagename+' - '+titles[tag2][0]
- if titles[tag2][1]:
- page = 'doc-'+titles[tag2][1]+'.html'
- break
- tag2 = toc.getParentTag(tag2)
- context = {
- 'pagename': pagename,
- 'page': page,
- 'nexturl': genTOClink(toc.getNextTag(tag)),
- 'prevurl': genTOClink(toc.getPrevTag(tag)),
- 'contentsurl': 'doc.html',
- }
- include.spyce('inc/head.spi', context)
- emitLocation(tag)
- print '<hr>'
-
- def emitFooter(tag, emitLocation=emitLocation, emitChildren=emitChildren):
- print '<hr>'
- emitLocation(tag)
- emitChildren(tag)
- include.spyce('inc/tail.spi')
-
-
- def toc_tocPush(depth, tag, numbering, data):
- print '<ul>\n'
-
- def toc_tocPop(depth, tag, numbering, data):
- print '</ul>\n'
-
- def toc_tocStart(depth, tag, numbering, data, genTOClink=genTOClink):
- if depth==1:
- print '<li>%s <a href="%s">%s</a></li>\n' % (
- reduce(lambda s, i: '%s%d.' % (s, i), numbering, ''),
- genTOClink(tag),
- data)
- if depth==2:
- print '<li>%s <a href="%s">%s</a></li>\n' % (
- reduce(lambda s, i: '%s%d.' % (s, i), numbering, ''),
- genTOClink(tag),
- data)
- if depth==3:
- print '<li>%s <a href="%s">%s</a></li>\n' % (
- reduce(lambda s, i: '%s%d.' % (s, i), numbering, ''),
- genTOClink(tag),
- data)
-
- def toc_docStart(depth, tag, numbering, data, multipage=multipage, genTOClink=genTOClink, emitHeader=emitHeader):
- import string
- if multipage:
- stdout.push(genTOClink(tag))
- emitHeader(tag)
- if depth==1:
- print '<p><big><a name="%s"></a><b>%s <font color=#ee0000>%s</font></b></big><p>\n' % (
- tag,
- reduce(lambda s, i: '%s%d.' % (s, i), numbering, ''),
- string.upper(data))
- if depth==2:
- print '<big><a name="%s"></a><b>%s <font color=#ee0000>%s</font></b></big><p>\n' % (
- tag,
- reduce(lambda s, i: '%s%d.' % (s, i), numbering, ''),
- data)
- if depth==3:
- print '<big><a name="%s"></a><b>%s <font color=#ee0000><i>%s</i></font></b></big><p>\n' % (
- tag,
- reduce(lambda s, i: '%s%d.' % (s, i), numbering, ''),
- data)
-
- def toc_docEnd(depth, tag, numbering, data, multipage=multipage, emitFooter=emitFooter):
- if multipage:
- emitFooter(tag)
- stdout.pop()
-
- toc.setTOC_PUSH(toc_tocPush)
- toc.setTOC_POP(toc_tocPop)
- toc.setTOC_ENTRY(toc_tocStart)
- toc.setDOC_START(toc_docStart)
- toc.setDOC_END(toc_docEnd)
- ]]
-
-
- [[toc.anchor("Table of Contents")]]
-
- <center>
- <big><b>Spyce - Python Server Pages (PSP) <br>
- <big>User Documentation</big></b></big><p>
- <b>Rimon Barr</b><p>
- Release [[=spyce.__version__]]<p>
- </center>
-
- <center><b>
- [[ if multipage: { ]]
- [ <a href="doc-single.html">Single-Page Format</a> ]
- [[ } else: { ]]
- [ <a href="doc.html">Multi-Page Format</a> ]
- [[ } ]]
- </b></center>
- <p>
-
- <font color="#ee0000"><b>TABLE OF CONTENTS</b><br></font>
- [[toc.showTOC()]]
- [[\
- if not multipage:
- print '<hr>'
- ]]
-
- [[toc.b("Introduction", "intro")]]
-
- This document aims to be the only necessary and authoritative source of
- information about spyce, usable as a comprehensive refence, a user guide and a
- tutorial all-in-one. It should be read at least once from end to end. <p>
-
- [[ import spyce ]]
- [[=spyce.__doc__]]<p>
-
- Like JSP, PHP, ASP and other similar HTML scripting languages it allows the
- generation of dynamic content via embedded programming logic, and does not
- attempt to provide an encompassing application framework. Just as JSP uses
- Java, PHP uses a Perl-like language and ASP most commonly uses Visual Basic,
- Spyce embeds Python. Its <a
- href="[[=genTOClink('add_perf')]]">performance</a> is comparable to the
- other solutions in its class. <p>
-
- [[toc.b("Rationale", "intro_rationale")]]
-
- A natural question to ask is why one would choose Spyce, over JSP, ASP, PHP
- and a handful of other popular HTML scripting languages or technologies that
- perform a similar function. We compare Spyce with an array of exising tools
- (chosen for their interesting points of comparison, not <a
- href="[[=genTOClink('add_related')]]">completeness</a>):
-
- <ul>
-
- <li><i><b>Java Server Pages, JSP,</b></i> is a widely popular, effective and
- well-supported solution based on Java Servlet technology. Spyce differs from
- JSP in that it embeds Python code among the HTML, thus providing a number of
- advantages over Java. First, Python is a high-level scripting language,
- where rapid prototyping is syntactically easier to perform. Second, Python
- is interpreted and latently typed, which can be advantageous for
- prototyping, especially in avoiding unnecessary binary incompatibility of
- classes for minor changes. Third, Spyce code is of first-order in the Spyce
- language, unlike JSP, which allows you to create useful Spyce lambda
- functions. And, lastly, creating new active tags and modules is simpler in
- Spyce than in JSP. Like Java, Python is portable.</li> <p>
-
- <li><i><b>PHP</b></i> is another popular webserver module for dynamic
- content generation. The PHP interpreter engine and the language itself were
- explicitly designed for the task of dynamic HTML generation, while Python is
- a general-purpose scripting language. Spyce borrows from the extensive
- development effort in Python: since any Python library can be imported and
- reused, Spyce does not need to rebuild many of the core function libraries
- that have been implemented by the PHP project. Moreover, the use of Python
- often simplifies integration of Spyce with existing system environments.
- Spyce code is also first-order in the Spyce language and Spyce supports
- active tags, unlike PHP. Lastly, Spyce is modular in its design, allowing
- users to easily extend its base functinality with add-on modules. Spyce,
- like PHP, can run entirely within the process space of a webserver or via
- CGI (as well as other web server adapters), and has been benchmarked to be
- competitive in <a href="[[=genTOClink('add_perf')]]">performance</a>. In
- addition, the Spyce engine can be run from the command-line, which allows
- Spyce to be used as an HTML preprocessor. </li> <p>
-
- <li><i><b>Active Server Pages, ASP,</b></i> is a Microsoft technology
- popular with Microsoft Internet Information Server (IIS) users. The default
- and most common language embedding is Visual Basic. Stated briefly, the
- author strongly prefers the language design and syntax of Python over VB.
- Secondly, ASP is not well-supported outside the IIS environment. Spyce can
- currently run under mod_python (Apache), as well as under CGI and <a
- href="http://www.fastcgi.com">FastCGI</a>, or as a proxy server, which is
- supported in the majority of web server environments. Adapters have also
- been written for Xitami, Coil, Cheetah -- other web servers and frameworks.
- Lastly, Spyce is open-source, and free. </li> <p>
-
- <li><i><b>WebWare with Python Server Pages, PSP,</b></i> is a recent
- Python-based open-source development. PSP is similar in design to the Spyce
- language, and shares many of the same benefits. However, Spyce supports both
- Python chunks (indented Python) as well as PSP-style statements (braced
- Python). Spyce code is also first-order in the Spyce language, as discussed
- above, and supports active tags, unlike PSP. PSP is also an integral part of
- WebWare, an application-server framework similar to Tomcat Java-based
- application server of the Apache Jakarta project. Spyce is to WebWare as JSP
- is to Tomcat. Spyce is far simpler to install and run than WebWare (in the
- author's humble opinion), and does not involve notions such as application
- contexts. It aims to do only one thing well: provide a preprocessor and
- runtime engine for the dynamic generation of HTML using embedded Python.
- </li> <p>
-
- <li><i><b>Zope</b></i> is an object-oriented open-source application server,
- specializing in "content management, portals, and custom applications." It
- is more mature than WebWare, but also attacks a much larger problem than
- Spyce. Zope provides a scripting language called DHTML and can call
- extensions written in Perl or Python. Spyce embeds Python directly in the
- HTML, and only Python. It is an HTML-embedded language, not an application
- server. </li> <p>
-
- </ul>
-
- In summary, the most popular of these solutions seem to be the solutions (JSP,
- PHP and ASP) that focus on the smaller problem of embedding a language within
- HTML. Spyce embeds Python in HTML, and no more. Many users have said that this
- is "exactly what they have been waiting for". Hopefully, this is the correct
- point in the design space for your project as well. You really just have to
- try it and see whether it suits your needs. <p>
-
- [[toc.n('Design Goals', 'intro_design')]]
-
- As a Spyce user, it helps to understand the broad design goals of this tool.
- Spyce is designed to be:
-
- <ul>
-
- <li><b>Feature poor:</b> The philosophy behind the design of Spyce is only
- to include features that particularly enhance its functionality over the
- wealth that is already available from within Python. One can readily import
- and use Python modules for many functions, and there is no need to recode
- large bodies of functionality. </li> <p>
-
- <li><b>Small:</b> There is an active push keep Spyce small, especially the
- core engine. The engine currently stands at 5000 lines of code -- tiny
- compared to PHP or Zope, etc. The standard modules comprise another
- 2000 lines. </li> <p>
-
- <li><b>Modular:</b> Spyce is built to be extended with Spyce modules and
- active tags that provide additional functionality over the core engine
- capabilities and standard Python modules. New features in the core engine
- and language are rationalised against the option of creating a new module or
- a new tag library. Standard Spyce modules and tag libraries are those that
- are considered useful in a general setting and are included in the default
- Spyce distribution. Users and third-parties are encouraged to develop their
- own Spyce modules. </li> <p>
-
- <li><b>Intuitive:</b> Obey user expectations. </li> <p>
-
- <li><b>Convenient:</b> Using Spyce should be made as efficient as possible.
- This, for example, is the reason behind the choice of <font
- face=courier>\[[</font> as delimeters over alternatives such as <font
- face=courier><?</font> (php) and <font face=courier><%</font> (jsp).
- Note that ASP/JSP-style delimeters are supported as well! Functions and
- modules are also designed with as many defaults as possible.
- </li> <p>
-
- <li><b>Single-purpose:</b> To be the best, most versatile, wildly-popular
- Python-based dynamic HTML engine. Nothing more; nothing less. </li> <p>
-
- <li><b>Fast:</b> <a href="[[=genTOClink('add_perf')]]">Performance</a> is
- important. It is expected that Spyce will perform comparably with any other
- dynamic, scripting solutions available. Of paramount importance, however, is
- clean design and syntax, and high degree of modularity and usability. The
- philosophy is to build and tweak only when necessary and only for
- significant performance gains, tested empirically.</li> <p>
-
- </ul> <p>
-
- Now, let's start using Spyce... <p>
-
- [[toc.n('Getting Started', 'intro_started')]]
-
- After <a href="[[=genTOClink('conf')]]">installing</a> Spyce, you are ready to
- write your first Spyce page. Start by editing a file called <b>hello.spy</b>
- in some web-published directory (a directory served by your webserver, and
- where <b>.spy</b> files will be handled correctly). Enter the following Spyce
- code:<p>
-
- [[includeCode('examples/hello.spy') ]]<p>
-
- <b>Note:</b> This manual assumes a knowledge of Python and focusses
- exclusively on Spyce. If you do not already know Python, it is easiest to
- learn via this short <a
- href="http://www.python.org/doc/current/tut/tut.html">tutorial</a>, and has
- extensive <a href="http://www.python.org/doc/current/">documentation</a>.<p>
-
- Save the file, and execute: <p>
-
- <table border=0 align=center><tr><td>
- <font face=courier>
- <pre>spyce hello.spy</pre>
- </font>
- </td></tr></table><p>
-
- The output should be:<p>
-
- [[.compact mode=off]]
- <table border=0 align=center><tr><td>
- <font face=courier>
- <b><pre>[[=escapeHTMLandCode('''
- <html><body>
- Hello world!
- 0
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- </body></html>''')]]</pre></b>
- </font>
- </td></tr></table><p>
- [[.compact]]
-
- You might want to see this inside a browser! For this, you need a Spyce
- enabled web server. Spyce can integrate into a number of web environments, but
- the easiest for the purposes of "getting started" is the built-in Spyce web
- server. You can run it by typing:<p>
-
- <table border=0 align=center><tr><td>
- <font face=courier>
- <pre>spyce -l -p port root</pre>
- </font>
- </td></tr></table><p>
-
- Replace <b>port</b> with some other port number, or omit the <b>-p</b> switch
- and assume the default, which is port 80. Replace <b>root</b> with the root
- document directory; the default is the current directory. All web requests are
- served relative to the server's root document directory. <p>
-
- Windows users should note that there is no command called <font
- face=courier>spyce</font> installed on their system. They should execute,
- instead: <p>
-
- <table border=0 align=center><tr><td>
- <font face=courier>
- <pre>C:\python_directory\python.exe C:\spyce_directory\spyce.py ...</pre>
- </font>
- </td></tr></table><p>
-
- Now, if you fire up your browser, and browse to:<p>
-
- <table border=0 align=center><tr><td>
- <font face=courier>
- <pre>http://your.domain:8080/some_path/hello.spy</pre>
- </font>
- </td></tr></table><p>
-
- then you will see the file: <font face=courier>root/some_path/hello.spy</font>
- and the output should be:<p>
-
- [[.compact mode=off]]
- <table border=0 align=center><tr><td>
- <font face=courier>
- <b><pre>
- Hello world! 0 1 2 3 4 5 6 7 8 9
- </pre></b>
- </font>
- </td></tr></table><p>
- [[.compact]]
-
- If you are curious, you can see the Python source code of the compiled Spyce
- script. It sometimes helps to understand the transformation that is taking
- place. Execute: <p>
-
- <table border=0 align=center><tr><td>
- <font face=courier>
- <pre>spyce -c hello.spy</pre>
- </font>
- </td></tr></table><p>
-
- That's all folks! You have just written your first Spyce script. Having
- performed the obligatory <font face=courier>Hello world!</font> ritual, we now
- describe the Spyce language more systematically. <p>
-
- [[toc.e()]]
-
- [[toc.n('Language', 'lang')]]
-
- The basic structure of a Spyce script is an HTML file with embeddings. There
- are six types of possible embeddings among the plain HTML text:
-
- <ul>
- <li><font face=courier><b><</b><a href="[[=genTOClink('lang_string')]]">taglib:name</a> attr1=val1 ...<b>></b></font></li>
- <li><font face=courier><b>\[[--</b> <a href="[[=genTOClink('lang_comment')]]">Spyce comment</a> <b>--\]]</b></font></li>
- <li><font face=courier><b>\[[.</b> <a href="[[=genTOClink('lang_directive')]]">Spyce directive</a> <b>\]]</b></font></li>
- <li><font face=courier><b>\[[</b> <a href="[[=genTOClink('lang_stmt')]]">Python statement(s)</a> <b>\]]</b></font></li>
- <li><font face=courier><b>\[[\</b> <a href="[[=genTOClink('lang_chunk')]]">Python chunk</a> <b>\]]</b></font></li>
- <li><font face=courier><b>\[[=</b> <a href="[[=genTOClink('lang_expr')]]">Python expression</a> <b>\]]</b></font></li>
- </ul>
-
- The majority of HTML strings are written out to the browser, verbatim.
- However, the Spyce language also supports "active" HTML <i>tags</i> via <a
- href="[[=genTOClink('tag')]]">tag libraries</a>, in the sense that they can
- execute custom code. Spyce <i>comments</i> are elided. The Spyce
- <i>directives</i> affect the interpreter behaviour. Python <i>statements</i>
- and <i>chunks</i> are executed. Finally, Python <i>expressions</i> are
- evaluated and the result is emitted to the browser. Each Spyce tag type has a
- unique beginning delimeter, namely <font face=courier><b>\[[</b></font>, <font
- face=courier><b>\[[\</b></font>, <font face=courier><b>\[[=</b></font>, <font
- face=courier><b>\[[.</b></font> or <font face=courier><b>\[[--</b></font>. All
- tags end with <font face=courier><b>\]]</b></font>, except comment tags, which
- end with <font face=courier><b>--\]]</b></font>.<p>
-
- Since
- <font face=courier><b>\[[</b></font> and
- <font face=courier><b>\]]</b></font>
- are special Spyce delimeters, one would escape them as
- <font face=courier><b>\\[[</b></font> and
- <font face=courier><b>\\]]</b></font>
- for use in HTML text. They can not be escaped within Python code, but the
- string expressions
- <font face=courier><b>("["*2)</b></font> and
- <font face=courier><b>("]"*2)</b></font>, or equivalent expressions,
- can be used instead, or the brackets can be conveniently separated with a
- space in the case of list or slicing expressions. <p>
-
- In addition, Spyce scripts are first-class members of the Spyce language: you
- can create a <a href="[[=genTOClink('lang_lambda')]]">Spyce <i>lambda</i></a>
- out of a Spyce string using the syntax below, in any of the Spyce Python
- elements (statements, chunks and expressions). A Spyce lambda can be invoked
- like a regular Python function, and its execution context (i.e. modules) is
- that of its caller. Spyce lambdas do not currently support nested variable
- scoping, nor default parameters, which Python users may be accustomed to.
-
- <ul>
-
- <li><font face=courier><b>\[[spy</b> [parameters] <b>:</b> <a href="[[=genTOClink('lang_lambda')]]">spyce lambda code</a> <b>\]]</b></font></li>
-
- </ul> <p>
-
- Note that braces in "<font face=courier>[parameters]</font>" above indicate
- that a Spyce lambda may have zero or more parameters. Consider the parameter
- list of a Spyce lambda to be the same as the parameter list of a Python lambda
- definition. <p>
-
- [[toc.b('Plain HTML and Active Tags', 'lang_string')]]
-
- Static plain HTML strings are printed as they are encountered. Depending on
- the <a href="[[=genTOClink('lang_directive')]]">compacting</a> mode of the
- Spyce interpreter, some whitespace may be eliminated. The Spyce <a
- href="[[=genTOClink('mod_transform')]]">transform</a> module, for example, may
- further pre-processes this string, by inserting transformations into the
- output pipe. This is useful, for example, for dynamic compression of the
- script result. <p>
-
- The Spyce language supports <a href="[[genTOClink('tag')]]">tag libraries</a>.
- Once a tag library is imported under some name, <i>mytags</i>, then all static
- HTML tags of the form <font
- face=courier><<i>mytags</i>:foo ... ></font> become "active".
- That is, code from the tag library is executed at that point in the document.
- Tags can control their output, conditionally skip or loop the execution of
- their bodies, and can interact with other active tags in the document. They
- are similar, in spirit and functionality, to JSP tags. Tag libraries and <a
- href="[[genTOClink('mod')]]">modules</a> (discussed later) can both
- considerably reduce the amount of code on a Spyce page, and increase code
- reuse and modularity. <p>
-
- [[toc.n('Spyce Comments', 'lang_comment')]]
-
- <b>Syntax: <font face=courier>\[[-- comment --\]]</font></b> <p>
-
- Spyce comments are ignored, and do not produce any output, meaning that they
- will not appear at the browser even in the HTML source. The first line of a
- Spyce file, if it begins with the characters <b>#!</b>, is also considered a
- comment, by Unix scripting convention. Spyce comments do <i>not</i> nest. <p>
-
- [[toc.n('Spyce Directives', 'lang_directive')]]
-
- <b>Syntax: <font face=courier>\[[. directive \]]</font></b>
- <p>
-
- Spyce directives directly affect the operation of the Spyce interpreter. There
- is a limited set of directives, listed and explained below:
-
- <ul>
-
- <li><b><font face=courier>
- \[[.<b>include</b> file=<i>file</i>\]] </font>:</b><br>
-
- Upon encountering this tag the Spyce compiler will insert the file
- referenced, treating its contents as if it had been typed inline. Statically
- included files are not checked for updates. Only an update to the primary
- file will invalidate the cache. For dynamic includes, please refer to the <a
- href="[[=genTOClink('mod_include')]]">include</a> module. Developers should
- also consider creating custom Python or <a
- href="[[=genTOClink('mod_new')]]">Spyce modules</a>, when the included file
- is primarily code, since there is no need to involve the Spyce compiler to
- convert primarily Python code to... Python code! </li> <p>
-
- <li><b><font face=courier>
- \[[.<b>compact</b> mode=<i>mode</i>\]] </font>:</b><br>
-
- Spyce can output the static HTML strings in various modes of compaction,
- which can both save bandwidth and improve download times without visibly
- affecting the output. Compaction of static HTML strings is performed once
- when the input Spyce file is compiled, and there is no additional run-time
- overhead beyond that. Dynamically generated content from Python code tags
- and expressions is not compacted nor altered in any way. Spyce can operate
- in one of the compaction modes listed below. One can use the <b>compact</b>
- tag to change the compaction mode from that point in the file forwards. <p>
-
- <ul>
-
- <li><b>off</b>: No compaction is performed. Every space and newline in the
- static HTML strings is preserved. </li> <p>
-
- <li><b>space</b>: Space compaction involves reducing any consecutive runs
- of spaces or tabs down to a single space. Any spaces or tabs at the
- beginning of a line are eliminated. These transformations will not affect
- HTML output, barring the <pre> tag, but can considerably reduce the
- size of the emitted text. </li> <p>
-
- <li><b>line</b>: Line compaction eliminates any (invisible) trailing
- whitespace at the end of lines. More significantly it improves the indented
- presentation of HTML, by ignoring any lines that do not contain any static
- text or expression tags. Namely, it removes all the whitespace, including
- the line break, surrounding the code or directives on that line. This
- compaction method usually "does the right thing", and produces nice HTML
- without requiring tricky indentation tricks by the developer. It is,
- therefore, the <b>initial</b> compaction mode. </li><p>
-
- <li><b>full</b>: Full compaction applies both space and line compaction. If
- the optional mode attribute is omitted, full compaction mode is the
- <b>default</b> value assumed. </li><p>
-
- </ul>
- </li><p>
-
- <li><b><font face=courier>
- \[[.<b>import</b> name=<i>name</i> from=<i>file</i> as=<i>name</i> args=<i>arguments</i>\]] </font>:</b><br>
-
- The import directive loads and defines a Spyce module into the global
- context. (The <font face=courier>\[[.module ... \]]</font>directive
- is synonymous.) A Spyce <a href="[[=genTOClink('mod')]]">module</a> is a
- Python file, written specifically to interact with Spyce. The <b>name</b>
- parameter is required, specifying the name of the Python class to load. The
- <b>file</b> parameter is optional, specifying the file where the named class
- is to be found. If omitted, file will equal <font
- face=courier><i>name</i>.py</font>. The file path can be absolute or
- relative. Relative paths are scanned in the Spyce home, user-configurable <a
- href="[[=genTOClink('runtime_common')]]">server path</a> directories and current
- script directory, in that order. Users are encouraged to name or prefix their
- modules uniquely so as not to be masked by system modules or tag libraries.
- The <b>as</b> parameter is optional, and specifies the name under which the
- module will be installed in the global context. If omitted, this parameter
- defaults to the name parameter. Lastly, the optional <b>args</b> parameter
- provides arguments to be passed to the module initialization function. All
- Spyce modules are <b>start()</b>ed before Spyce processing begins,
- <b>init()</b>ed at the point where the directive is placed in the code, and
- <b>finish()</b>ed after Spyce processing terminates. It is convention to
- place modules at, or near, the very top of the file unless the location of
- initialization is relevant for the functioning of the specific module. <p>
-
- <b><font face=courier>
- \[[.<b>import</b> names="<i>name1,name2,...</i>"\]] </font>:</b><br>
-
- An alternative syntax allows convenient loading of multiple Spyce modules.
- One can not specify non-standard module file locations, nor rename the
- modules using this syntax. </li><p>
-
- <li><b><font face=courier>
- \[[.<b>taglib</b> name=<i>name</i> from=<i>file</i> as=<i>name</i>\]] </font>:</b><br>
-
- The taglib directive loads a Spyce tag library. A Spyce <a
- href="[[=genTOClink('tag')]]">tag library</a> is a Python file, written
- specifically to interact with Spyce. The <b>name</b> parameter is required,
- specifying the name of the Python class to load. The <b>file</b> parameter is
- optional, specifying the file where the named class is to be found. If
- omitted, file will equal <font face=courier><i>name</i>.py</font>. The file
- path can be absolute or relative. Relative paths are scanned in the Spyce
- home, user-configurable <a href="[[=genTOClink('runtime_common')]]">server
- path</a> directories and current script directory, in that order. Users are
- encouraged to name or prefix their tag libraries uniquely so as not to be
- masked by system tag libraries and modules. The <b>as</b> parameter is
- optional, and specifies the unique tag prefix that will be used to identify
- the tags from this library. If omitted, this parameter defaults to the name
- parameter. It is convention to place tag library directives at, or near, the
- very top of the file. The tags only become active after the point of the tag
- library directive. <p>
-
- <b><font face=courier>
- \[[.<b>taglib</b> names="<i>name1,name2,...</i>"\]] </font>:</b><br>
-
- An alternative syntax allows convenient loading of multiple Spyce tag
- libraries. One can not specify non-standard tag library locations, nor
- specify a prefix of the tag libraries using this syntax. </li><p>
-
- </ul> <p>
-
- It is important to note that Spyce directives are processed at <i>compile</i>
- time, not during the execution of the script, much like directives in C, and
- other languages. In other words, they are processed as the Python code for the
- Spyce script is being produced, not as it is being executed. Consequently, it
- is not possible to include runtime values as parameters to the various
- directives.<p>
-
- [[toc.n('Python Statements', 'lang_stmt')]]
-
- <b>Syntax: <font face=courier>\[[ statement(s) \]]</font></b> <p>
-
- The contents of a code tag is one or more Python statements. The statements
- are executed when the page is emitted. There will be no output unless the
- statements themselves generate output. <p>
-
- The statements are separated with semi-colons or new lines, as in regular
- Python scripts. However, unlike regular Python code, Python statements <i>do
- <b>not</b> nest based on their level of indentation</i>. This is because
- indenting code properly in the middle of HTML is difficult on the developer.
- To alleviate this problem, Spyce supports a slightly modifed Python syntax:
- proper nesting of Spyce statements is achieved using begin- and end-braces:
- <font face=courier><b>{</b></font> and <font face=courier><b>}</b></font>,
- respectively. These <b>MUST</b> be used, because the compiler regenerates the
- correct indentation based on these markers alone. Even single-statement blocks
- of code must be wrapped with begin and end braces. (If you prefer to use
- Python-like indentation, read about <a
- href="[[=genTOClink('lang_chunk')]]">chunks</a>). <p>
-
- The following Spyce code, from the <font face=courier>Hello World!</font>
- example above: <p>
-
- [[.compact mode=off]]
- <table align=center border=0><tr><td>
- <font face=courier>
- <b><pre>
- \[[ for i in range(10): <font color=red>{</font> \]]
- \[[=i\]]
- \[[ <font color=red>}</font> \]]
- </pre></b>
- </font>
- </td></tr></table>
- [[.compact]]<p>
-
- produces the following indented Python code:<p>
-
- [[.compact mode=off]]
- <table align=center border=0><tr><td>
- <font face=courier>
- <b><pre>
- for i in range(10):
- response.writeStatic(' ')
- response.writeExpr(i)
- response.writeStatic('\n')
- </pre></b>
- </font>
- </td></tr></table>
- [[.compact]]<p>
-
- Without the braces, the code produced would be unindented and, in this case,
- also invalid: <p>
-
- [[.compact mode=off]]
- <table align=center border=0><tr><td>
- <font face=courier>
- <b><pre>
- for i in range(10):
- response.writeStatic(' ')
- response.writeExpr(i)
- response.writeStatic('\n')
- </pre></b>
- </font>
- </td></tr></table>
- [[.compact]]<p>
-
- Note how the indentation of the expression does not affect the indentation of
- the Python code that is produced; it merely changes the number of spaces in
- the <font face=courier>writeStatic</font> string. Also note that unbalanced
- open and close braces within a single tag are allowed, as in the example
- above, and they modify the indentation level outside the code tag. However,
- the braces must be balanced across an entire file. Remember: inside the <font
- face=courier>\[[ ... \]]</font> delimiters, <b>braces are always
- required</b> to change the indentation level. <p>
-
- [[toc.n('Python Chunks', 'lang_chunk')]]
-
- <b>Syntax: <font face=courier>\[[\ Python chunk \]]</font></b>
- <p>
-
- There are many Python users that experience anguish, disgust or dismay upon
- reading the previous section: "Braces!? Give me real, indented Python!". These
- intendation zealots will be more comfortable using Python chunks, which is why
- Spyce supports them. Feel free to use Spyce statements or chunks
- inter-changeably, as the need arises.<p>
-
- A Python chunk is straight Python code, and the <i>internal</i> indentation is
- preserved. The entire block is merely outdented (or indented) as a whole, such
- that the first non-empty line of the block matches the indentation level of
- the context into which the chunk was placed. Thus, a Python chunk can not
- affect the indentation level outside its scope, but internal indentation is
- fully respected, relative to the first line of code, and braces (<font
- face=courier>{</font>, <font face=courier>}</font>) are not required, nor
- expected for anything but Python dictionaries. Since the first line of code is
- used as an indentation reference, it is recommended that the start delimeter
- of the tag (i.e. the <font face=courier>\[[\</font>) be placed on its own
- line, above the code chunk, as shown in the following example: <p>
-
- [[.compact mode=off]]
- <table align=center border=0><tr><td>
- <font face=courier>
- <b><pre>\[[\
- def printHello(num):
- for i in range(num):
- response.write('hello<br>')
-
- printHello(5)
- \]]</pre></b>
- </font>
- </td></tr></table>
- [[.compact]]
- <p>
-
- Naturally, one should <i>not</i> use braces here for purposes of indentation,
- only for Python dictionaries. Additional braces will merely generate Python
- syntax errors in the context of chunks. To recap: a Python statement tag
- should contain braced Python; A Python chunk tag should contain regular
- indented Python. <p>
-
- [[toc.n('Python Expressions', 'lang_expr')]]
-
- <b>Syntax: <font face=courier>\[[= expression \]]</font></b>
- <p>
-
- The contents of an expression tag is a Python expression. The result of that
- expression evaluation is printed using the its string representation. The
- Spyce <a href="[[=genTOClink('mod_transform')]]">transform</a> module, can
- pre-processes this result, to assist with mundane tasks such as ensuring that
- the string is properly HTML-encoded, or formatted. <p>
-
- [[toc.n('Spyce Lambdas', 'lang_lambda')]]
-
- <b>Syntax: <font face=courier>\[[spy <i>[params]</i> : <i>spyce lambda code</i> \]]</font></b>
- <br>
- <b>or: <font face=courier>\[[spy! <i>[params]</i> : <i>spyce lambda code</i> \]]</font></b>
- <p>
-
- A nice feature of Spyce is that Spyce scripts are first-class members of the
- language. In other words, you can create a Spyce lambda (or function) in any
- of the Spyce Python elements (statements, chunks and expressions). These can
- then be invoked like regular Python functions, stored in variables for later
- use, or be passed around as paramaters. This feature is often very useful for
- templating (example shown below), and can also be used to implement more
- esoteric processing functionality, such as internationalization, multi-modal
- component frameworks and other kinds of polymorphic renderers. <p>
-
- It is instructive to understand how these functions are generated. The <font
- face="courier">\[[spy ... : ... \]]</font> syntax is first
- translated during compilation into a call to the define() function of the <a
- href="[[=genTOClink('mod_lambda')]]">spylambda</a> module. At runtime, this
- call compiles the Spyce code at the point of its definition, and returns a
- function. While the invocation of a Spyce lambda is reasonably efficient, it
- is certainly not as fast as a regular Python function invocation. The
- spycelambda can be memoized (explained in the <a
- href="[[=genTOClink('mod_lambda')]]">spylambda</a> module section) by using
- the <font face=courier>\[[spy! ... : ... \]]</font>
- syntax. However, even with this optimization one should take care to use
- Python lambdas and functions when the overhead of Spyce parsing and invocation
- is not needed. <p>
-
- Note that Spyce lambdas do not currently support nested variable scoping, nor
- default parameters. The global execution context (specifically, Spyce modules)
- of the Spyce lambda is defined at the point of its execution. <p>
-
- [[includeCode('examples/spylambda.spy') ]] <p>
-
- [[toc.n('ASP/JSP syntax', 'lang_asp') ]]
-
- Finally, due to popular demand, because of current editor support and people
- who actually enjoy pains in their wrists, the Spyce engine will respect
- ASP/JSP-like delimeters. In other words, it will also recognize the following
- syntax:
-
- <ul>
- <li><font face=courier><b><%--</b> <a href="[[=genTOClink('lang_comment')]]">Spyce comment</a> <b>--%></b></font></li>
- <li><font face=courier><b><%@</b> <a href="[[=genTOClink('lang_directive')]]">Spyce directive</a> <b>%></b></font></li>
- <li><font face=courier><b><%</b> <a href="[[=genTOClink('lang_stmt')]]">Python statement(s)</a> <b>%></b></font></li>
- <li><font face=courier><b><%\</b> <a href="[[=genTOClink('lang_chunk')]]">Python chunk</a> <b>%></b></font></li>
- <li><font face=courier><b><%=</b> <a href="[[=genTOClink('lang_expr')]]">Python expression</a> <b>%></b></font></li>
- <li><font face=courier><b><%spy</b> [parameters] <b>:</b> <a href="[[=genTOClink('lang_lambda')]]">spyce lambda code</a> <b>%></b></font></li>
- </ul>
-
- The two sets of delimeters may be used interchangeably within the same file,
- though this is not recommended.
-
- [[toc.e()]]
-
- [[toc.n('Runtime', 'runtime')]]
-
- Having covered the Spyce language syntax, we now move to describing the
- runtime processing. Each time a request comes in, the cache of compiled Spyce
- files is checked for the compiled version of the requisite Spyce file. If one
- is not found, the Spyce file is quickly read, transformed, compiled and cached
- for future use. <p>
-
- The compiled Spyce is initialized, then processed, then finalized. The
- initialization consists of initializing all the Spyce modules. The Spyce file
- is executed top-down, until the end is reached or an exception is thrown,
- whichever comes first. The finalization step then finalizes each module in
- reverse order of initialization, and any buffered output is automatically
- flushed. <p>
-
- [[toc.b('Exceptions', 'runtime_except')]]
-
- The Spyce file is executed top-down, until the end of the file is reached, a
- valued is <font face="courier">return</font>ed, or an exception is thrown,
- whichever comes first. If the code terminates via an unhandled exception, then
- it is caught by the Spyce engine. Depending on the exception type, different
- actions are taken:
-
- <ul>
-
- <li><b>spyceDone</b> can be raised at any time to stop the Spyce processing
- (without error) at that point. It is often used to stop further output, as
- in the example below that emits a binary image file. The spyceDone
- exception, however, is more useful for modules writers. In regular Spyce
- code one could simply issue a <font face="courier">return</font> statement,
- with the same effect. </li> <p>
-
- <li><b>spyceRedirect</b> is used by the <a
- href="[[=genTOClink('mod_redirect')]]">redirect</a> module. It causes the
- Spyce engine to immediately redirect the request to another Spyce file
- <i>internally</i>. Internally means that we do not send back a redirect to
- the browser, but merely clear the output buffer and start processing a new
- script. </li> <p>
-
- <li><b>All other exceptions</b> that occur at runtime will be processed via
- the Spyce <a href="[[=genTOClink('mod_error')]]">error</a> module. This
- module will emit a default error message, unless the user has installed some
- other error handler. </li> <p>
-
- </ul><p>
-
- Note that non-runtime exceptions, such as exceptions caused by compile errors,
- missing files, access restrictions and the like, are handled by the server.
- The default <a href="[[=genTOClink('runtime_common')]]">server error handler</a>
- can be configured via the server configuration file. <p>
-
- [[includeCode('examples/gif.spy') ]]
- <p>
-
- [[toc.n('Code Transformation', 'runtime_transform')]]
-
- While the minutia of the code transformation that produces Python code from
- the Spyce sources is of no interest to the casual user, it has some slight,
- but important, ramifications on certain aspects of the Python language
- semantics when used inside a Spyce file. <p>
-
- The result of the Spyce compilation is some Python code, wherein the majority
- of the Spyce code actually resides in a single function called
- <b>spyceProcess</b>. If you are curious to see the result of a Spyce
- compilation, execute: <font face=courier>"spyce -c".</font> <p>
-
- It follows from the compilation transformation that:
-
- <ul>
-
- <li>Any functions defined within the Spyce file are actually nested
- functions within the spyceProcess function. </li> <p>
-
- <li>The use of <b>global</b> variables within Spyce code is not supported,
- but also not needed. If nested scoping is available (Python versions
- >2.1) then these variables will simply be available. If not, then you
- will need to pass variables into functions as default parameters, and will
- not be able to update them by value (standard Python limitations). It is
- good practice to store constants and other globals in a single class, or to
- to place them in a single, included file, or both. </li> <p>
-
- <li><p>The global Spyce namespace is reserved for special variables, such as
- Spyce and Python modules. While the use of the keyword <font
- face=courier>global</font> is not explicitly checked, it will pollute this
- space and may result in unexpected behaviour or runtime errors. </li> <p>
-
- <li>The lifetime of variables is the duration of a request. Variables with
- lifetimes longer than a single request can be stored using the <a
- href="[[=genTOClink('mod_pool')]]">pool</a> module. </li> <p>
-
- </ul> <p>
-
- [[toc.n('Dynamic Content', 'runtime_web')]]
-
- The most common use of Spyce is to serve dynamic HTML content, but it should
- be noted that Spyce can be used as a general purpose text engine. It can be
- used to generate XML, text and other output, as easily as HTML. In fact, the
- engine can also be used to generate dynamic binary data, such as images, PDF
- files, etc., if needed. <p>
-
- The Spyce engine can be <a href="[[=genTOClink('conf')]]">installed</a>
- in a number of different configurations that can produce dynamic output.
- The first three options exhibit high performance; the CGI approach is
- slowest, since a new engine must be created for each request:
-
- <ul>
-
- <li><b>mod_python</b>: Apache module that runs a Python interpreter
- in-process </li> <p>
-
- <li><b>FCGI</b>: A CGI-like method that does not incur the large
- process startup overhead on each request </li> <p>
-
- <li><b>webserver</b>: The engine can operate as a proxy webserver,
- serving requests redirected to it by a primary server. It can also
- operate as a primary server, although this webserver is extremely
- feature poor. </li> <p>
-
- <li><b>CGI</b>: Spyce can interoperate with any webserver that
- supports CGI. The CGI-mode, while the slowest alternative, is
- especially convenient during the development of Python modules,
- ensuring that modules always get reloaded. Another alternative is to
- use the <a href="[[=genTOClink('runtime_common_debug')]]">debug</a>
- configuration switch. </li><p>
-
- <li><b>Others</b>: Spyce abstracts its operating environment using a thin
- indirection layer. Spyce users have written small Spyce adapters for the
- Xitami webserver and also to integrate with the Coil framework. Writing your
- own adapter, should the need arise, is therefore a realistic possibility.
- </li><p>
-
- </ul>
-
- [[toc.n('Static Content', 'runtime_static')]]
-
- A nice feature of Spyce is that it can be invoked both from within a web
- server to process a web request dynamically and also from the command-line.
- The processing engine itself is the same in both cases. The command-line
- option is actually just a modified CGI client, and is often used to
- pre-process static content, such as this manual. <p>
-
- Some remarks regarding command-line execution specifics are in order. The
- request and response objects for a command-line request are connected to
- standard input and output, as expected. A minimal CGI-like environment is
- created among the other shell environment variables. Header and cookie lookups
- will return None and the engine will accept input on stdin for POST
- information, if requested. There is also no compiler cache, since the process
- memory is lost at the end of every execution. <p>
-
- Most commonly, Spyce is invoked from the command-line to generate static .html
- ouput. Spyce then becomes a rather handy and powerful .html preprocessing
- tool. It was used on this documentation to produce the consistent headers and
- footers, to include and highlight the example code snippets, etc...<p>
-
- The following makefile rule comes in handy:<p>
-
- [[.compact mode=off]]
- <table align=center border=0><tr><td><font face=courier>
- <b><pre>
- %.html: %.spy
- spyce -o $@ $<
- </pre></b>
- [[.compact]]
- </font></td></tr></table><p>
-
- [[toc.n('Command line', 'runtime_cmdline')]]
-
- The full command-line syntax is:<p>
-
- [[.compact mode=off]]
- <table align=center border=0><tr><td>
- <font face=courier size=-1>
- <b><pre>[[
- import spyceCmd, cStringIO
- buf = cStringIO.StringIO()
- spyceCmd.showUsage(buf)
- response.write(escapeHTMLandCode(buf.getvalue()))
- ]]</pre></b>
- </font>
- </td></tr></table>
- [[.compact]] <p>
-
- [[toc.n('Configuration', 'runtime_common')]]
-
-
- Since there are a variety of very different <a
- href="[[=genTOClink('conf')]]">installation</a> alternatives for the Spyce
- engine, effort has been invested in consolidating all the various runtime
- configuration options. By default, the Spyce engine will search for a file
- called <b>spyce.conf</b> in its installation directory. An alternative file
- location may be specified via the <b>--conf</b> command-line option. However,
- the engine will operate just fine without any configuration file, using its
- built-in default values. In general, command-line options supercede
- configuration file options which, in turn, supercede built-in configuration
- defaults. The options specified in the Spyce configuration file are
- server-level configuration options (as opposed to file-level options). An
- example configuration file is provided, called <b>spyce.conf.eg</b>. <p>
-
- The general format of a Spyce file is that of a Windows .ini file. Sections
- are denoted inside braces (i.e. [section]), and options with values are
- specified on independent lines separated by a colon (i.e. option: value).
- Comment lines begin with either a semicolon ';' or a hash '#'. An example of
- an extensive Spyce file (i.e. with all options specified) is shown, with the
- individual options described below.
-
- [[toc.b('Example configuration', 'runtime_common_sample')]]
-
- <table border=0 align=center><tr><td>
- <font face=courier size=-1>
- [[.compact mode=off]]
- <pre><b>[[=transform.html_encode(include.dump('../spyce.conf.eg'))]]</b></pre>
- [[.compact]]
- </font>
- </td></tr></table>
- <p>
-
- [[toc.n('path', 'runtime_common_path')]]
-
- The <b>path</b> option specifies additional module and import directories
- searched by the Spyce engine. Specifically, the Spyce path (the path searched
- to resolve <font face=courier>\[[.import\]]</font> directives) is the Spyce
- home modules directory, plus the directories specified here, plus the
- directory of the current Spyce file. The directories specified here are also
- added to the sys.path, so that import statements search these directories as
- well. The format for this option is a colon-separated (or semicolon-separated,
- depending on your operating system) list of directories. If path is omitted
- from the configuration file, the engine will check for a <font
- face=courier>SPYCE_PATH</font> variable in the process environment, and use
- that. Failing this, the default is an empty string. <p>
-
- [[toc.n('import', 'runtime_common_import')]]
-
- The <b>import</b> option specifies <i>Python</i> modules (not Spyce modules)
- that should be loaded (i.e. imported) at server startup. This can be useful to
- perform initialization at server startup. Using the <a
- href="[[=genTOClink('mod_pool')]]">pool</a> module is prefered for storing
- long-lived values. The string for this option is a comma-separated list of
- Python module names. In CGI-mode, the Spyce engine is restarted on each
- request, so these modules will be loaded on each request. For other
- configurations, the import is performed once. To use this module, you will
- still need to import the module in your Spyce modules or Spyce files, but it
- will be taken from the Python module cache. <p>
-
- [[toc.n('error', 'runtime_common_error')]]
-
- The <b>error</b> option specifies an error handling function to call for
- server-level errors. Server-level errors include: <b>spyce.spyceNotFound</b>,
- <b>spyce.spyceForbidden</b>, <b>spyce.spyceSyntaxError</b> and
- <b>spyce.pythonSyntaxError</b>. The string should be of the format
- "module:function", and the module should be on the server path. The function
- is invoked as <b>function(server, request, response, error)</b> to
- generate the error output. The default is "error:serverHandler". Please look
- at the serverHandler function in the error module, if you are considering
- writing your own server error handler. File-level (or request-level) errors
- are handled using the <a href="[[=genTOClink('mod_error')]]">error</a> module.
- <p>
-
- [[toc.n('pageerror', 'runtime_common_pageerror')]]
-
- The <b>pageerror</b> option specifies the default page-level error handler
- used by the error module. Page-level errors include all runtime errors that
- occur during the processing of a Spyce script (i.e. after the compilation
- phase has completed successfully). This option can take one of two forms: 1)
- "string:<b>module</b>:<b>variable</b>", where string is a literal,
- <b>module</b> specifies a module in the server path and <b>variable</b> is the
- name of a variable containing the default error template Spyce string; or 2)
- "file:<b>filename</b>", where file is a literal and <b>filename</b> is the
- name of the Spyce file to use as the default error template. The default value
- for this option is "string:error:defaultErrorTemplate". Please refer to this
- string in the error module to see how to define your own page-level error
- handlers.
-
- [[toc.n('concurrency', 'runtime_common_concurrency')]]
-
- The <b>concurrency</b> option specifies the concurrency mode that the engine
- will operate in. The Spyce engine can process requests serially (i.e.
- non-concurrently or one-at-a-time), or in parallel using either process-level
- (forking) or thread-level (threading) concurrency. Note that this option
- affects the request processing parallelism of a <i>single</i> engine. Request
- processing parallelism may also be acheived by running multiple instances of
- the engine, and this choice is independent of the follow discussion. <p>
-
- The "one shot" Spyce configurations, namely CGI-based or command-line
- execution, are unaffected by the concurrency option, since the server only
- processes one request per Spyce engine invocation. <p>
-
- The FastCGI configuration does not currently support parallel request
- processing due to a limitation of the FastCGI interface code (not anything
- inherent to the core of the Spyce engine). This will be remedied in the
- future. <p>
-
- The mod_python configuration does not support parallel request processing
- within a single engine, since it is not supported by the mod_python interface.
- In fact, many mod_python compilations do not even allow threading in the
- Python interpretter. However, mod_python configurations run one Spyce engine
- per Apache child, and achieve request processing parallelism in this way. <p>
-
- Lastly, the Spyce engine can be run in webserver mode. This mode is affected
- by the concurrency mode. The webserver runs in serial mode by default. It can
- fork a new process for each request. Alternatively, it can spawn a new thread
- for each request, if threading is supported by the Python interpretter. <p>
-
- The possible values for the <b>concurrency</b> option are 'thread' (or
- 'threading') and 'fork' (or 'forking'). Any other value results in a serial
- execution, which is also the default. <p>
-
- [[toc.n('cache', 'runtime_common_cache')]]
-
- The <b>cache</b> option allows the user to select the underlying Spyce caching
- implementation used. The format of the string for this option is
- "<b>type:info</b>", where the <b>type</b> specifies the cache type, and the
- optional <b>info</b> parameter is passed on to the cache implementation for
- initialization. Spyce currently supports the following cache implementations:
-
- <ul>
-
- <li><b>memory</b>: This is the default cache type, which uses an in-memory
- dictionary to store the compiled Spyces. It takes no parameters.</li> <p>
-
- <li><b>file</b>: The file-based cache type will serialize the compiled
- Spyces and store them in a given directory. The info parameter should be the
- <b>directory</b> location. </li> <p>
-
- </ul> <p>
-
- [[toc.n('debug', 'runtime_common_debug')]]
-
- The <b>debug</b> option specifies whether the engine will run in debug mode.
- Debug mode currently only affects the caching of Spyce files and Spyce
- modules. Namely, Spyce files are always recompiled and Spyce modules are not
- cached (are reloaded on each request) in debug mode, which is useful for
- module developers. <b>This switch should not be used in a production
- environment</b>, because compilation will slow things down considerably! The
- default value for this option is off. The values '0', 'false', and 'off'
- represent production mode (as does leaving out the debug option entirely),
- while any other value for this option turns debugging on. <p>
-
- Alternatively, one could decide to perform development under CGI mode.
- This ensures that a newly loaded Spyce engine is used on every request.
- <p>
-
- [[toc.n('globals', 'runtime_common_globals')]]
-
- The <b>globals</b> section is also entirely optional. Any "key: expr" pair
- will be stored in a hashtable, h, as h[key]=eval(expr). In other words, the
- expression is evaluated first, and the result is stored in the hashtable using
- the given key. This hashtable is accessible as <b>self.wrapper.server.globals</b>
- within any Spyce module, or as <b>pool.globals</b> within any Spyce file (with
- the pool module loaded). <p>
-
- [[toc.n('www', 'runtime_common_www')]]
-
- The <b>www</b> section defines values for the built-in Spyce webserver. <p>
-
- The <b>root</b> option defines the root directory from which all requests are
- served. By default, this is the current directory. It may be overridden on the
- command-line. <p>
-
- The <b>port</b> option defines the port at which the web server will listen.
- By default, this is port 80, the regular HTTP port. It may be overridden using
- the <b>-p</b> command-line switch. <p>
-
- The <b>mime</b> option is a comma-separated list of files that define
- mime-types for common file extensions. The files should be in the standard
- Apache format. By default, the Spyce engine will read a file called
- <b>spyce.mime</b> in from the installation directory. <p>
-
- The <b>ext_</b> and <b>ext_<i>foo</i></b> (where <b><i>foo</i></b> can be
- replaced by any extension) define the handler mapping. The legitimate values
- are: <b>spyce</b> and <b>dump</b>. The spyce handler processes the file at the
- requested path as a spyce script. The dump handler transfers the file at the
- requested path verbatim, using the appropriate "Content-type" header, if it is
- known. <b>ext_foo</b> option defines a mapping for a file that ends with
- <b>foo</b>. The <b>ext_</b> option defines the default handler. By default,
- all files ending with <b>.spy</b> are processed by the spyce handler, and all
- other files are processed using the dump handler. Other handlers may readily
- be added to the Spyce webserver, but it is never intended to be a fully
- featured web server. <p>
-
- [[toc.e()]]
-
- [[toc.n('Programmatic Interface', 'runtime_prog')]]
-
- It is also possible to embed Spyce into another program. All you need is to
- run or <a href="http://www.python.org/doc/current/ext/embedding.html">embed
- </a> a Python interpretter. Although other entry points into the engine code
- as possible, the most convenient entry points are in <b>spyce.py</b>:
-
- <ul>
-
- <li> <b>spyceFileHandler</b>( request, response, filename, [sig], [args], [kwargs], [config_file] ) <br>
-
- <i>explanation forthcoming; read the code for now, or send an email</i>
-
- </li> <p>
-
- <li> <b>spyceStringHandler</b>( request, response, code, [sig], [args], [kwargs], [config_file] ) <br>
-
- <i>explanation forthcoming; read the code for now, or send an email</i>
-
- </li> <p>
-
- </ul> <p>
-
- [[toc.e()]]
-
- [[toc.n('Modules', 'mod')]]
-
- The Spyce language, as described above, is simple and small. The Spyce
- compiler merely embeds the power of Python using special Spyce tags. Most
- functionality is provided at runtime through Spyce modules and Python modules.
- A suite of standard Spyce modules is included with the Spyce distribution. The
- standard Python modules are included in the Python distribution. You, of
- course, may also write <a href="[[=genTOClink('mod_new')]]">new Spyce
- modules</a> and Python modules, or use code <a
- href="http://spyce.sourceforge.net/contrib/index.html">contributed</a>
- (sourceforge link) by others to extend the base functionality available in
- Spyce. <p>
-
- It is important, from the outset, to define what a Spyce module is, and is
- not. Specifically, it is important to differentiate a Spyce module from a
- Python module. A Python module is a file with Python code, usually with a
- common theme, and not necessarily related to Spyce. In contrast, a Spyce
- module is a file with Python code that is written in a specific way to
- interact directly with the Spyce runtime engine. A Spyce module may access the
- internal request and response structures, require per-request startup and
- tear-down callbacks from the engine, build on the existing standard modules,
- or alter the behaviour of the runtime engine in some way, whereas a Python
- module does not. <p>
-
- Both can be imported and used with equal ease at runtime. Spyce modules are
- imported using the Spyce <a href="[[=genTOClink('lang_directive')]]"><font
- face=courier>\[[.import\]]</font></a> directive. Python modules are
- imported using Python <font face=courier>import</font> keyword. Remember that
- modules need to have the same read permissions as regular files that you
- expect the web server to read. <p>
-
- Once included, a module may be accessed anywhere in the Spyce code as a global
- variable, just like Python modules. Spyce modules are objects. They provide
- methods and fields. One uses them as regular Python objects. Modules are
- <b>start</b>()ed before pre-Spyce processing, and <b>finish</b>()ed post-Spyce
- processing. They are <b>init</b>()ialized with optional arguments during Spyce
- processing at the point of the Spyce directive. <p>
-
- Modules may be renamed from their defaults using the <b>as</b> attribute,
- though this is discouraged in most cases. Doing this may cause unexpected
- behaviour. The <b>session</b> module, for example, may expect to find or
- otherwise load a module named <b>cookie</b> in the Spyce environment, if
- cookies are chosen for session management; the <b>taglib</b> module expects to
- find the standard <b>stdout</b> module to capture the output of tag body
- processing; the <b>stdout</b> and <b>filter</b> modules interact very closely
- with the <b>response</b> module; etc. In other words, you should rename
- modules only if you have read their implementations and know what you are
- doing. <p>
-
- The following standard Spyce modules are loaded implictly into the spyce
- environment, because they are required for Spyce operation: <b>request</b>,
- <b>response</b>, <b>error</b>, <b>stdout</b>, <b>spylambda</b> and
- <b>taglib</b>. The latter two are loaded only if Spyce lambdas and active tags
- are used in the script, respectively. Of the standard Python modules, only the
- __builtins__ module is imported, along with a number of Spyce exceptions from
- the spyceException module. <p>
-
- Below, we document each individual standard Spyce module, and then describe
- how one would write <a href="[[=genTOClink('mod_new')]]">new</a> Spyce
- modules. <p>
-
- [[toc.b('Request', 'mod_request')]]
-
- The request module is loaded implicitly into every Spyce environment. It
- provides the following methods:
-
- <ul>
-
- <li><b>uri</b>( [component] ): <br> Returns the request URI, or some
- component thereof. If the optional <b>component</b> parameter is specified,
- it should be one of the following strings:
-
- <font face=courier>'scheme'</font>,
- <font face=courier>'location'</font>,
- <font face=courier>'path'</font>,
- <font face=courier>'parameters'</font>,
- <font face=courier>'query'</font> or
- <font face=courier>'fragment'</font>.
-
- </li><p>
-
- <li><b>method</b>(): <br> Returns request method type (GET, POST,
- ...)</li><p>
-
- <li><b>query</b>(): <br> Returns the request query string</li><p>
-
- <li><b>get</b>( [name], [default], [ignoreCase] ): <br> Returns request GET
- information. If <b>name</b> is specified then a single list of values is
- returned if the parameter exists, or <b>default</b>, which defaults to None,
- if the parameter does not exist. Parameters without values are skipped,
- though empty string values are allowed. If name is omitted, then a
- dictionary of lists is returned. If <b>ignoreCase</b> is true, then the
- above behaviour is performed in a case insensitive manner (all parameters
- are treated as lowercase). </li><p>
-
- <li><b>get1</b>( [name], [default], [ignoreCase] ): <br> Returns request GET
- information, similarly to (though slightly differently from) the function
- above. If <b>name</b> is specified then a single string is returned if the
- parameter exists, or <b>default</b>, which default to None, if the parameter
- does not exist. If there is more than one value for a parameter, then only
- one is returned. Parameters without values are skipped, though empty string
- values are allowed. If name is omitted, then a dictionary of strings is
- returned. If the optional <b>ignoreCase</b> flag is true, then the above
- behaviour is performed in a case insensitive manner (all parameters are
- treated as lowercase). </li><p>
-
- <li><b>post</b>( [name], [default], [ignoreCase] ): <br> Returns request
- POST information. If <b>name</b> is specified then a single list of values
- is returned if the parameter exists, or <b>default</b>, which defaults to
- None, if the parameter does not exist. Parameters without values are
- skipped, though empty string values are allowed. If name is omitted, then a
- dictionary of lists is returned. If <b>ignoreCase</b> is true, then the
- above behaviour is performed in a case insensitive manner (all parameters
- are treated as lowercase). This function understands form information
- encoded either as 'application/x-www-form-urlencoded' or
- 'multipart/form-data'. Uploaded file parameters are not included in this
- dictionary; they can be accessed via the file method. </li><p>
-
- <li><b>post1</b>( [name], [default], [ignoreCase] ): <br> Returns request
- POST information, similarly to (though slightly differently from) the
- function above. If <b>name</b> is specified then a single string is returned
- if the parameter exists, or <b>default</b>, which defaults to None, if the
- parameter does not exist. If there is more than one value for a parameter,
- then only one is returned. Parameters without values are skipped, though
- empty string values are allowed. If name is omitted, then a dictionary of
- strings is returned. If the optional <b>ignoreCase</b> flag is true, then
- the above behaviour is performed in a case insensitive manner (all
- parameters are treated as lowercase). This function understands form
- information encoded either as 'application/x-www-form-urlencoded' or
- 'multipart/form-data'. Uploaded file parameters are not included in this
- dictionary; they can be accessed via the file method. </li><p>
-
- <li><b>file</b>( [name], [ignoreCase] ): <br> Returns files POSTed in the
- request. If <b>name</b> is specified then a single cgi.FieldStorage class is
- returned if such a file parameter exists, otherwise None. If name is
- omitted, then a dictionary of file entries is returned. If the optional
- <b>ignoreCase</b> flag is true, then the above behaviour is performed in a
- case insensitive manner (all parameters are treated as lowercase). The
- interesting fields of the FieldStorage class are: <p>
-
- <ul>
-
- <li><b>name:</b> the field name, if specified; otherwise None</li>
-
- <li><b>filename:</b> the filename, if specified; otherwise None; this is
- the client-side filename, not the filename in which the content is stored
- - a temporary file you don't deal with
-
- <li><b>value:</b> the value as a string; for file uploads, this
- transparently reads the file every time you request the value
-
- <li><b>file:</b> the file(-like) object from which you can read the data;
- None if the data is stored a simple string
-
- <li><b>type:</b> the content-type, or None if not specified
-
- <li><b>type_options:</b> dictionary of options specified on the
- content-type line
-
- <li><b>disposition:</b> content-disposition, or None if not specified
-
- <li><b>disposition_options:</b> dictionary of corresponding options
-
- <li><b>headers:</b> a dictionary(-like) object (sometimes rfc822.Message
- or a subclass thereof) containing *all* headers
-
- </ul><p>
-
- <li><b>__getitem__</b>( key ): <br> The request module can be used as a
- dictionary: i.e. request['foo']. This method first calls the get1() method,
- then the post1() method and lastly the file() method trying to find the
- first non-None value to return. If no value is found, then this method
- returns None. Note: Throwing an exception seemed too strong a semantics, and
- so this is a break from Python. One can also iterate over the request
- object, as if over a dictionary of field names in the get1 and post1
- dictionaries. In the case of overlap, the get1() dictionary takes
- precedence. </li><p>
-
- <li><b>getpost</b>( [name], [default], [ignoreCase] ): <br> Using given
- parameters, return get() result if not None, otherwise return post() result
- if not None, otherwise <b>default</b>. </li><p>
-
- <li><b>getpost1</b>( [name], [default], [ignoreCase] ): <br> Using given
- parameters, return get1() result if not None, otherwise return post1()
- result if not None, otherwise <b>default</b>. </li><p>
-
- <li><b>postget</b>( [name], [default], [ignoreCase] ): <br> Using given
- parameters, return post() result if not None, otherwise return get() result
- if not None, otherwise <b>default</b>. </li><p>
-
- <li><b>postget1</b>( [name], [default], [ignoreCase] ): <br> Using given
- parameters, return post1() result if not None, otherwise return get1()
- result if not None, otherwise <b>default</b>. </li><p>
-
- <li><b>env</b>( [name], [default] ): <br> Returns a dictionary with CGI-like
- environment information of this request. If <b>name</b> is specified then a
- single entry is returned if the parameter exists, otherwise <b>default</b>,
- which defaults to None, if omitted. </li><p>
-
- <li><b>getHeader</b>( [type] ): <br> Return a specific header sent by the
- browser. If optional <b>type</b> is omitted, a dictionary of all headers is
- returned.</li><p>
-
- <li><b>filename</b>( [path] ): <br> Return the Spyce filename of the request
- currently being processed. If an optional <b>path</b> parameter is provided,
- then that path is made relative to the Spyce filename of the request
- currently being processed. </li><p>
-
- <li><b>default</b>( value, value2 ): <br> (convenience method) Return
- <b>value</b> if it is not None, otherwise return <b>value2</b>. </li><p>
-
- </ul>
-
- Dynamic web pages frequently need to access GET and POST information sent by
- the browser. Here is an example that shows this is done.<br>
-
- [[includeCode('examples/getpost.spy') ]]
- <p>
-
- The example below presents the results of all the method calls list above. Run
- it to understand the information available. <p>
-
- [[includeCode('examples/request.spy') ]]
- <p>
-
- A more complicated form... <p>
-
- [[includeCode('examples/form.spy') ]]
- <p>
-
- Lastly, the following example shows how to deal with uploaded files. <p>
-
- [[includeCode('examples/fileupload.spy') ]] <p>
-
- [[toc.n('Response', 'mod_response')]]
-
- Like the request module, the response module is also loaded implicitly by every
- Spyce. It provides the following methods:
-
- <ul>
-
- <li><b>write</b>( string ): <br> Sends a <b>string</b> to the client. All
- writes are buffered by default and sent at the end of Spyce processing to
- allow appending headers, setting cookies and exception handling. Note that
- using the <font face="courier">print</font> statement is often easier, and
- <a href="[[=genTOClink('mod_stdout')]]">stdout</a> is implicitly redirected
- to the browser. </li> <p>
-
- <li><b>writeln</b>( string ): <br> Sends a <b>string</b> to the client, and
- appends a newline. </li><p>
-
- <li><b>writeStatic</b>( string ): <br> All static HTML <b>strings</b> are
- emitted to the client via this method, which (by default) simply calls
- write(). This method is <i>not</i> commonly invoked by the user. </li><p>
-
- <li><b>writeExpr</b>( object ): <br> All expression results are emitted to
- the client via this method, which (by default) calls write() with the str()
- of the result <b>object</b>. This method is <i>not</i> commonly invoked by
- the user. </li><p>
-
- <li><b>clear</b>( ): Clears the output buffer. </li> <p>
-
- <li><b>flush</b>( ): Sends buffered output to the client immediately. This
- is a blocking call, and can incur a performance hit.</li> <p>
-
- <li><b>setContentType</b>( contentType ): <br> Sets the MIME <b>content
- type</b> of the response. </li> <p>
-
- <li><b>setReturnCode</b>( code ): <br> Set the HTTP return code for this
- response. This <b>return code</b> may be overriden if an error occurs or by
- functions in other modules (such as redirects). </li> <p>
-
- <li><b>addHeader</b>( type, data, [replace] ): <br> Adds the header line
- "<font face=courier>type: data</font>" to the outgoing response. The
- optional <b>replace</b> flag determines whether any previous headers of the
- same type are first removed. </li><p>
-
- <li><b>unbuffer</b>(): <br> Turns off buffering on the output stream. In
- other words, each write is followed by a flush(). An unbuffered output
- stream should be used only when sending large amounts of data (ie. file
- transfers) that would take up server memory unnecessarily, and involve
- consistently large writes. Note that using an unbuffered response stream
- will not allow the output to be cleared if an exception occurs. It will also
- immediately send any headers. </li><p>
-
- <li><b>isCancelled</b>(): <br> Returns true if it has been detected that the
- client is no longer connected. This flag will turn on, and remain on, after
- the first client output failure. However, the detection is best-effort, and
- may never turn on in certain configurations (such as CGI) due to buffering.
- </li><p>
-
- <li><b>timestamp</b>( [thetime] ):<br> Timestamps the response with an HTTP
- <font face=courier>Date:</font> header, using the optional <b>thetime</b>
- parameter, which may be either be the number of seconds since the epoch
- (see Python <a
- href="http://www.python.org/doc/current/lib/module-time.html">time</a>
- module), or a properly formatted HTTP date string. If thetime is omitted,
- the current time is used. </li> <p>
-
- <li><b>expires</b>( [thetime] ):<br> Sets the expiration time of the
- response with an HTTP <font face=courier>Expires:</font> header, using the
- optional <b>thetime</b> parameter, which may be either the number of seconds
- since the epoch (see Python <a
- href="http://www.python.org/doc/current/lib/module-time.html">time</a>
- module), or a properly formatted HTTP date string. If thetime is omitted,
- the current time is used. </li> <p>
-
- <li><b>expiresRel</b>( [secs] ):<br> Sets the expiration time of the
- response <i>relative to the current time</i> with an HTTP <font
- face=courier>Expires:</font> header. The optional <b>secs</b> (which may
- also be negative) indicates the number of seconds to add to the current time
- to compute the expiration time. If secs is omitted, it defaults to zero.
- </li> <p>
-
- <li><b>lastModified</b>( [thetime] ):<br> Sets the last modification time of
- the response with an HTTP <font face=courier>Last-Modified:</font> header,
- using the optional <b>thetime</b> parameter, which can be either the number
- of seconds since the epoch (see Python <a
- href="http://www.python.org/doc/current/lib/module-time.html">time</a>
- module), or a properly formatted HTTP date string, or None indicating the
- current time. If thetime is omitted, this function will default to the last
- modification time of the Spyce file for this request, and raise an exception
- if this time can not be determined. Note that, as per the HTTP
- specification, you should not set a last modification time that is beyond
- the response timestamp.</li> <p>
-
- <li><b>uncacheable</b>():<br> Sets the HTTP/1.1 <font
- face=courier>Cache-Control:</font> and HTTP/1.0 <font
- face=courier>Pragma:</font> headers to inform clients and proxies that this
- content should not be cached. </li> <p>
-
- </ul>
-
- The methods are self-explanatory. One of the more interesting things that one could do is
- to emit non-HTML content types. The example below emits the Spyce logo as a GIF.<p>
-
- [[includeCode('examples/gif.spy') ]] <p>
-
- [[toc.n('Error', 'mod_error')]]
-
- The error module is implicitly loaded and provides error-handling
- functionality. An error is any <a
- href="[[=genTOClink('runtime_except')]]">unhandled runtime exception</a> that
- occurs <b>during Spyce processing</b>. This mechanism does not include
- exceptions that are not related to Spyce processing (i.e. server-related
- exceptions), that can be caused before or after Spyce processing by invalid
- syntax, missing files and file access restrictions. To install a server-level
- error handler use a <a href="[[=genTOClink('runtime_common')]]">configuration
- file</a>. The default page-level error handler can also be modified in the <a
- href="[[=genTOClink('runtime_common')]]">configuration file</a>. This module
- allows the user to install page-level error handling code, overriding the
- default page-level handler, by using one of the following functions: <p>
-
- <ul>
-
- <li><b>setStringHandler</b>( string ): <br> Installs a function that will
- processes the given <b>string</b>, as Spyce code, for error handling.
- </li><p>
-
- <li><b>setFileHandler</b>( file ): <br> Installs a function that will
- processes the given <b>file</b> for error handling. </li><p>
-
- <li><b>setHandler</b>( fn ): <br> Installs the <b>fn</b> function for error
- handling. The function is passed one parameter, a reference to the error
- module. From this, all the error information as well as references to other
- modules and Spyce objects can be accessed.</li><p>
-
- </ul>
-
- The error module provides the following information about an error: <p>
-
- <ul>
-
- <li><b>isError</b>(): <br> Returns whether an error is being handled.
- </li><p>
-
- <li><b>getMessage</b>(): <br> Return the error message; the string of the
- object that was raised, or None if there is no current error. </li><p>
-
- <li><b>getType</b>(): <br> Return the error type; the type of the object
- that was raised, or None if there is no current error. </li><p>
-
- <li><b>getFile</b>(): <br> Return the file where the error was raised, or
- None if there is no current error. </li><p>
-
- <li><b>getTraceback</b>(): <br> Return the stack trace as an array of
- tuples, or None if there is no current error. Each tuple entry is of the
- form: (file, line numbers, function name, code context).</li><p>
-
- <li><b>getString</b>(): <br> Return the string of the entire error (the
- string representation of the message, type, location and stack trace), or
- None if there is no current error. </li><p>
-
- </ul>
-
- The default error handling function uses the following string handler:
-
- [[\
- template = response._api.spyceModule('defaultErrorTemplate', 'error.py')
- ]]
- <table border=0 align=center><tr><td>
- <pre>[[=include.spycecode(string=template)]]</pre>
- </td></tr></table>
- <p>
-
- The example below shows the error module in use. Error handling can often be
- used to send emails notifying webmasters of problems, as this example shows.
- <p>
-
- [[includeCode('examples/error.spy')]]
- <p>
-
- [[includeCode('examples/error.spi', run=0)]]
- <p>
-
- This mechanism is not a subsititute for proper exception handling within the
- code itself, and should not be abused. It does, however, serve as a useful
- catch-all for bugs that slip through the cracks. <p>
-
- [[toc.n('Stdout', 'mod_stdout')]]
-
- The stdout module is loaded implicitly and redirects Python's <font
- face=courier>sys.stdout</font> (in a thread-safe manner) to the appropriate
- response object for the duration of Spyce processing. This allows one to use
- <font face=courier>print</font>, without having to write <font
- face=courier>print >> response, ...</font>. The stdout
- module provides a variable <font face=courier>stdout.stdout</font>, which
- refers to the original stream, but is unlikely to be needed. It may also be
- useful to know that <font face=courier>sys.stderr</font> is, under many
- configurations, connected to the webserver error log. <p>
-
- In addition, the stdout module provides the following functions for capturing
- or redirecting output:
-
- <ul>
-
- <li><b>push</b>( [filename] ): <br> Begin capturing output. Namely, the current
- output stream is pushed onto the stack and replaced with a memory buffer. An
- optional <b>filename</b> may be associated with this operation (see pop()
- method below). </li> <p>
-
- <li><b>pop</b>(): <br> Close current output buffer, and return the captured
- output as a string. If a filename was associated with the push(), then the
- string will also be written to that file. </li> <p>
-
- <li><b>capture</b>(f, [*args], [**kwargs] ): <br> Push the current stream,
- call the given function <b>f</b> with any supplied arguments <b>*args</b>
- and keyword arguments <b>**kwargs</b>, and then pop it back. Capture returns
- a tuple (r,s), where r is the result returned by f and s is a string of its
- output. </li> <p>
-
- </ul> <p>
-
- The example below show how the module is used:
-
- [[includeCode('examples/stdout.spy')]]
- <p>
-
- [[toc.n('Spylambda', 'mod_lambda')]]
-
- The spylambda module is loaded implicitly and allows the definition of
- functions based on Spyce scripts. The spylambda module provides the following
- methods:
-
- <ul>
-
- <li><b>define</b>( args, code, [memoize] ): <br> Returns a function that
- accepts the given <b>args</b> and executes the Spyce script defined by the
- <b>code</b> parameter. Note that the code is compiled immediately and that
- <font face=courier>spyce.spyceSyntaxError</font> or <font
- face=courier>spyce.spycePythonError</font> exceptions can be thrown for
- invalid code arguments. The optional <b>memoize</b> parameter sets whether
- the spyce can or can not be memoized, with the default being false.
- Memoizing a function means capturing the result and output and caching them,
- keyed on the function parameters. Later, if a function is called again with
- the same parameters, the cached information is returned, if it exists, and
- the function may not actually be called. Thus, you should only memoize
- functions that are truly functional, i.e. they do not have side-effects:
- they only return a value and output data to the response object, and their
- behaviour depends exclusively on their parameters. If you memoize code that
- does have side-effects, those side-effects may not occur on every
- invocation. </li> <p>
-
- <li><b>__call__</b>( args, code, _spyceCache ): <br> This is an alias to the
- define function. Because of the special method name, the spylambda module
- object can be <a
- href="http://www.python.org/doc/current/ref/callable-types.html">called as
- if it were a function</a>. </li> <p>
-
- </ul>
-
- This function is not frequently called directly from Spyce code, because
- writing the Spyce code argument in a manner that does not conflict with the
- Spyce tag delimiters is cumbersome. Rather the <a
- href="[[=genTOClink('lang_lambda')]]">Spyce lambda</a> syntax is used and
- translated into this function call at compilation time, as in the example
- below. <p>
-
- [[includeCode('examples/spylambda.spy') ]] <p>
-
- It often useful to use the spylambda module directly from other Spyce modules
- that may need to perform significant amounts of output. Rather than calling
- <font face="courier">print</font> repeatedly, it is more convenient to invoke
- a Spyce, as in the example below. Though highly simplified, this example also
- shows how Spyce lambdas can be used to easily build a complex rendering
- environment. <p>
-
- [[includeCode('examples/myPortal.spy') ]] <p>
-
- [[includeCode('examples/myPortal.py', run=0) ]] <p>
-
- [[toc.n('Taglib', 'mod_taglib')]]
-
- The taglib module is loaded implicitly and supports the active tags
- functionality at runtime. It is expected that the casual user will not have
- much use for this module, and will only call its functions indirectly by
- importing tag libraries and using active tags. Primarily, this is because the
- methods interoperate very tightly and require a very strict calling sequence,
- which is generated by the Spyce compiler for each active tag it encounters.
- Nevertheless, for completeness, the taglib module provides the following
- methods:
-
- <ul>
-
- <li><b>context</b>: This field is a dictionary that serves as the context in
- which tags operate. Tags can store variables and evaluate expressions within
- this context. The tag context contains references to all the loaded modules.
- Thus, it is valid to refer to something like <font
- face=courier>request.query()</font> in a tag expression. However, it is not
- valid to change any module variable references. While this will not cause
- any harm, the user should expect that these new values can be reset by the
- runtime at any time. </li> <p>
-
- <li><b>load</b>( libname, [libfrom], [libas] ): <br> Loads a tag library
- class named <b>libname</b> from a file called <b>libfrom</b> in the search
- path, and installed it under the tag prefix <b>libas</b>. The default for
- libfrom is <font face=courier><i>libname</i>.py</font>. The default for
- libas is <font face=courier><i>libname</i></font>. Once installed, a library
- name is its unique tag prefix. </li> <p>
-
- <li><b>unload</b>( libname ): <br> Unload a tag library that is installed
- under the <b>libname</b> prefix. This is usually performed only at the end
- of a request. </li> <p>
-
- <li><b>tagPush</b>( libname, tagname, pair ): <br> Push a new tag object for
- a <b>libname</b>:<b>tagname</b> tag onto the tag stack. The <b>pair</b>
- parameter is a flag indicating whether this is a singleton or a paired tag.
- </li> <p>
-
- <li><b>tagPop</b>(): <br> Pop the current tag from the tag stack. </li> <p>
-
- <li><b>getTag</b>(): <br> Return the current tag object. </li> <p>
-
- <li><b>outPush</b>(): <br> Begin capturing the current output stream. This
- is usually called by the tagBegin method. </li> <p>
-
- <li><b>outPopCond</b>(): <br> End capturing the current output stream, and
- return the captured contents. It will only "pop" once, even if called
- multiple times for the same tag. This method is usually called by either the
- tagEnd(), tagCatch, or tagPop() methods. </li> <p>
-
- <li><b>tagBegin</b>( attrs ): <br> This method sets the tag output and
- variable environment, and then calls the tag's <b>begin()</b> method with
- the given <b>attrs</b> tag attribute dictionary. This method returns a flag,
- and the tag body must be processed if and only if this flag is true. </li>
- <p>
-
- <li><b>tagBody</b>(): <br> This method sets the tag output and variable
- environment, and then calls the tag's <b>body()</b> method with the captured
- output of the body processing. If this method returns true, then the
- processing of the body must be repeated. </li> <p>
-
- <li><b>tagEnd</b>(): <br> This method sets the tag output and variable
- environment, and then calls the tag's <b>end()</b> method. This method must
- be called if the tagBegin() method completes successfully in order to
- preserve tag semantics. </li> <p>
-
- <li><b>tagCatch</b>(): <br> This method should be called if any of the
- tagBegin, tagBody or tagEnd methods raise an exception. It calls the tag's
- <b>catch()</b> method with the current exception. </li> <p>
-
- </ul> <p>
-
- As stated previously, it is expected that the user will not call these methods
- directly, but rather simply use the active tag functionality that this module
- supports. Spyce comes with various standard <a
- href="[[=genTOClink('tag')]]">tag libraries</a>. The following example shows a
- few simple ones in use:
-
- [[includeCode('examples/tag.spy') ]] <p>
-
- [[toc.n('Include', 'mod_include')]]
-
- Many websites carry a theme across their various pages, which is often
- achieved by including a common header or footer. The include module provides
- exactly this functionality, and more. For example, it can also be used to
- define dynamic site-wide constants, and other similar globals that can not
- otherwise be initialized with static include directives (due to their dynamic
- nature). For example, language specific constants may be selected dynamically
- from the required language include file, based on a GET or POST language
- parameter or, better yet, from the Accept-Language request header. This module
- also provides other functions that are similar in nature. For example, it can
- currently pretty print Spyce code. In the future, it will be able to generate
- the Spyce and Spyce Powered logo, and possibly other similar trinkets. <p>
-
- <ul>
-
- <li><b>spyce</b>( file, [context] ): <br> Dynamically includes the specified
- <b>file</b>, and processes it as Spyce code. The return value is that of the
- included Spyce file. One can optionally provide a <b>context</b> value to
- the included file. If omitted, the value defaults to None. All currently
- imported modules are passed along into the included file without
- re-initialization. However, for each explicit <font
- face=courier>\[[.import \]]</font> tag in the included file, a new
- module is initialized and also finalized up at the end of processing. The
- include module provides three fields for use inside included files: <p>
-
- <ul>
-
- <li>include.<b>context</b>: This field stores the value passed in at the
- point of inclusion. Note that if the value is one that is passed by
- reference (as is the case with object, list, and dictionary types), then
- the context may be used to pass information back to the including file, in
- addition to the return value. </li> <p>
-
- <li>include.<b>vars</b>: If the include context is of type dictionary,
- then the vars field is initialized, otherwise it is None. The vars field
- provides attribute-based access to the context dictionary, merely for
- convenience. In other words, <font face=courier>self.vars.x</font> is
- equivalent to <font face=courier>self.context['x']</font>. </li> <p>
-
- <li>include.<b>fromFile</b>: stores the name of the file name from which
- this file was included. </li><p>
-
- </ul>
-
- Note that either the locals() or globals() dictionaries may be passed in as
- include contexts. However, be advised that due to Python optimizations of
- local variable access, any updates to the locals() dictionary may not be
- reflected in the local namespace under all circumstances and all versions of
- Python. In fact, this is the reason why the context has been made explicit,
- and does not simply grab the locals() dictionary. It may, however, safely be
- used for read access. With respect to the globals() dictionary, it is not
- advised to <a href="[[=genTOClink('runtime_transform')]]">pollute</a> this
- namespace. </li><p>
-
- <li><b>dump</b>( file, [binary] ): <br> Contents of the <b>file</b> are
- returned. If the <b>binary</b> parameter is true, the file is opened in
- binary mode. By default, text mode is used. </li><p>
-
- <li><b>spycecode</b>( file ): <br> Contents of the <b>file</b> are returned
- as HTML formatted Spyce code. </li><p>
-
- </ul>
-
- The example below (taken from this documentation file), uses a common header
- template only requiring two context variables to change the title and the
- highlighted link:<br>
-
- [[.compact mode=off]]
- <table align=center border=0><tr><td>
- <font face=courier>
- <b><pre> \[[.import name=include\]]
- \[[include.spyce('inc/head.spi',
- {'pagename': 'Documentation',
- 'page': 'manual.html'}) \]]</pre></b>
- </font>
- </td></tr></table><p>
- [[.compact]]
-
- In <b>head.spi</b>, we use this information to set the title:<p>
-
- [[.compact mode=off]]
- <table align=center border=0><tr><td>
- <font face=courier>
- <b><pre>
- \[[.import name=include\]]
- [[=escapeHTMLandCode("<title>[+=include.context['pagename'] +]</title>") ]]
- </pre></b>
- </font>
- </td></tr></table><p>
- [[.compact]]
-
- By convention, included files are given the extension <b>.spi</b>.<p>
-
- Below we contrast the difference between static and dynamic includes. A
- dynamic include is included on each request; a static include is inserted at
- compile time. A static include runs in the same context, while a dynamic
- include has a separate context.<p>
-
- [[includeCode('examples/include.spy') ]]<p>
- [[includeCode('examples/include.spi', run=0) ]]<p>
- [[includeCode('examples/includestatic.spy') ]]<p>
- [[includeCode('examples/includestatic.spi', run=0) ]]<p>
-
- [[toc.n('Transform', 'mod_transform')]]
-
- The transform module contains useful text transformation functions, commonly
- used during web-page generation. <p>
-
- <ul>
-
- <li><b>html_encode</b>( string, [also] ): <br> Returns a HTML-encoded
- <b>string</b>, with special characters replaced by entity references as
- defined in the HTML 3.2 and 4 specifications. The optional <b>also</b>
- parameter can be used to encode additional characters. </li><p>
-
- <li><b>url_encode</b>( string, ): <br> Returns an URL-encoded <b>string</b>,
- with special characters replaced with %XX equivalents as defined by the URI
- RFC document. </li><p>
-
- </ul>
-
- The transform module also be used to intercept and insert intermediate
- processing steps when <b>response.writeStatic()</b>,
- <b>response.writeExpr()</b> and <b>response.write()</b> are called to emit
- static html, expressions and dynamic content, respectively. It can be useful,
- for example, to automatically ensure that expressions never produce output
- that is HTML-unsafe, in other words strings that contain characters such as
- [[=transform.html_encode('''&, < and >''')]]. Many interesting processing
- functions can be defined. By default, the transform module leaves all output
- untouched. These processing functions, called filters, can be inserted via the
- following module functions: <p>
-
- <ul>
-
- <li><b>static</b>( [ fn ] ): <br> Defines the processing performed on all
- static HTML strings from this point forwards. The <b>fn</b> parameter is
- explained below.</li><p>
-
- <li><b>expr</b>( [ fn ] ): <br> Defines the processing performed on all the
- results of all expression tags from this point forwards. The <b>fn</b>
- parameter is explained below.</li><p>
-
- <li><b>dynamic</b>( [ fn ] ): <br> Defines the processing performed on all
- dynamic content generated, i.e. content generated using response.write in the
- code tags. The <b>fn</b> parameter is explained below. </li><p>
-
- </ul>
- <p>
-
- Each of the functions above take a single, optional parameter, which specifies
- the processing to be performed. The parameter can be one of the following
- types:
-
- <ul>
-
- <li><b>None</b>: <br> If the paramter is None, or omitted, then no processing
- is performed other converting the output to a string. </li><p>
-
- <li><b>Function</b>: <br> If a parameter of function type is specified, then
- that function is called to process the output. The output can be any Python
- type, and the output may be any Python type. The result is then converted
- into a string for output. The first parameter to a filter will always be the
- object to be processed for output. However, the function should be properly
- defined so as to possibly accept other parameters. The details of how to
- define filters are explained below. </li><p>
-
- <li><b>String</b>: <br> If a paramter of string type is specified, then the
- string should be of the following format: <font
- face=courier>"file:name"</font>, where <b>file</b> is the location where
- the function is defined and <b>name</b> is the name of the filter. The file
- component is optional, and is searched for using the standard module-finding
- rules. If only the function name is specified, then the default location
- (inside the transform module itself) is used, where the standard Spyce
- filters reside. The standard Spyce filters are described below. </li><p>
-
- <li><b>List</b> / <b>Tuple</b>: <br> If a parameter of list or tuple type is
- specified, its elements should be functions, strings, lists or
- tuples. The compound filter is recursively defined as
- <font face=courier>f=fn(...f2(f1())...)</font>, for the parameter
- <font face=courier>(f1,f2,...,fn)</font>.
- </li><p>
-
- </ul>
- <p>
-
- Having explained how to install filters, we now list the standard Spyce
- filters and show how they are used:
-
- <ul>
-
- <li><b>ignore_none</b>( o ): <br> Emits any input <b>o</b> except for None,
- which is converted into an empty string. </li><p>
-
- <li><b>truncate</b>( o, [maxlen] ): <br> If <b>maxlen</b> is specified,
- then only the first maxlen characters of input <b>o</b> are returned,
- otherwise the entire original. </li><p>
-
- <li><b>html_encode</b>( o, [also] ): <br> Converts any '&', '<' and
- '>' characters of input <b>o</b> into HTML entities for safe inclusion in
- among HTML. The optional <b>also</b> parameter can specify, additional
- characters that should be entity encoded.</li><p>
-
- <li><b>url_encode</b>( o ): <br> Converts input <b>o</b> into a URL-encoded
- string. </li><p>
-
- <li><b>nb_space</b>( o ): <br> Replaces all spaces in input <b>o</b> with
- " ". </li><p>
-
- <li><b>silence</b>( o ): <br> Outputs nothing. </li><p>
-
- </ul>
- <p>
-
- The optional parameters to some of these filters can be passed to the various
- write functions as <b>named parameters</b>. They can also be specified in an
- expression tag, as in the following example. (One should simply imagine that
- the entire expression tag is replaced with a call to response.writeExpr).
-
- <table align=center border=0><tr><td><font face=courier>
- [[.compact mode=off]]
- <b><pre>\[[.import name=transform\]]
- \[[ transform.expr(("truncate", "html_encode")) \]]
- \[[='This is an unsafe (< > &) string... '*100, <font color=#ff0000>maxlen=500</font>\]] </pre></b>
- [[.compact]]
- </font></td></tr></table>
- <p>
-
- In the example above, the unsafe string is repeated 100 times. It is then
- passed through a <font face=courier>truncate</font> filter that will accept
- only the first 500 characters. It is then passed through the <font
- face=courier>html_encode</font> filter that will convert the unsafe
- characters into their safe, equivalent HTML entities. The resulting string is
- emitted.<p>
-
- The parameters (specified by their names) are simply accepted by the
- appropriate write method (writeExpr() in the case above) and passed along to
- the installed filter. Note that in the case of compound filters, the
- parameters are passed to <b>ALL</b> the functions. The html_encode filter is
- written to ignore the maxlen parameter, and does not fail. <p>
-
- For those who would like to write their own filters, looking at the definition
- of the truncate filter will help. The other standard filters are in <font
- face="courier">modules/transform.py</font>.
-
- <table align=center border=0><tr><td><font face=courier>
- [[.compact mode=off]]
- <b><pre>def truncate(o, maxlen=None, **kwargs):</pre></b>
- [[.compact]]
- </font></td></tr></table>
- <p>
-
- When writing a filter, any function will do, but it is strongly advised to
- follow the model above. The important points are:
-
- <ul>
-
- <li>The input o can be of <b>any type</b>, not only a string.</li>
-
- <li>The function <b>result</b> does not have to be string either. It is
- automatically stringified at the end.</li>
-
- <li>The function can accept <b>parameters</b> that modify its behaviour, such
- as maxlen, above.</li>
-
- <li>It is recommended to provide convenient user <b>defaults</b> for all
- parameters.</li>
-
- <li>The last parameter should be <b>**kwargs</b> so that unneeded parameters
- are quietly passed along.</li>
-
- </ul>
- <p>
-
- Lastly, one can retrieve filters. This can be useful when creating new
- functions that depend on existing filters, but can not be compounded using the
- tuple syntax above. For example, one might use one filter or another
- conditionally. For whatever purpose, the following module function is provided
- to retreive standard Spyce filters, if needed: <p>
-
- <ul>
-
- <li><b>create</b>( [ fn ] ): <br> Returns a filter. The <b>fn</b> parameter
- can be of type None, function, string, list or tuple and is handled as in
- the installation functions discussed above. </li><p>
-
- </ul>
-
- The transform module is flexible, but not complicated to use. The example
- below is <i>not</i> examplary of typical use. Rather it highlights some of the
- flexibility, so that users can think about creative uses.<p>
-
- [[includeCode('examples/transform.spy') ]] <p>
-
- [[toc.n('Redirect', 'mod_redirect')]]
-
- The redirect module allows requests to be redirected to different pages, by
- providing the following methods:
-
- <ul>
-
- <li><b>internal</b>( file ): <br> Performs an internal redirect. All
- processing on the current page ends, the output buffer is cleared and
- processing continues at the named <b>file</b>. The browser URI remains
- unchanged, and does not realise that a redirect has even occurred during
- processing. </li><p>
-
- <li><b>external</b>( uri, [permanent] ): <br> Performs an external redirect
- using the HTTP Location header to a new <b>uri</b>. Processing of the
- current file continues, but the content is ignored (ie. the buffer is
- cleared at the end). The status of the document is set to 301 MOVED
- PERMANENTLY or 302 MOVED TEMPORARILY, depending on the <b>permanent</b>
- boolean parameter, which defaults to false or temporary. The redirect
- document is sent to the browser, which requests the new relative uri.
- </li><p>
-
- <li><b>externalRefresh</b>( uri, [seconds] ): <br> Performs an external
- redirect using the HTTP Refresh header a new <b>uri</b>. Processing of the
- current file continues, and will be displayed on the browser as a regular
- document. Unless interrupted by the user, the browser will request the new
- URL after the specified number of <b>seconds</b>, which defaults to zero if
- omitted. Many websites use this functionality to show some page, while a
- file is being downloaded. To do this, one would show the page using Spyce,
- and redirect with an externalRefresh to the download URI. Remember to set
- the <font face=courier>Content-Type</font> on the target download file page
- to be something that the browser can not display, only download. </li><p>
-
- </ul>
-
- The example below, shows the possible redirects in use:<p>
-
- [[includeCode('examples/redirect.spy') ]] <p>
-
- [[toc.n('Cookie', 'mod_cookie')]]
-
- This module provides cookie functionality. Its methods are:
-
- <ul>
-
- <li><b>get</b>( [key] ): <br> Return a specific cookie string sent by the
- browser. If the optional cookie <b>key</b> is omitted, a dictionary of all
- cookies is returned. The cookie module may also be accessed as an
- associative array to achieve the same result as calling: namely, <font
- face=courier>cookie['foo']</font> and <font
- face=courier>cookie.get('foo')</font> are equivalent. </li><p>
-
- <li><b>set</b>( key, value, [expire], [domain], [path], [secure] ): <br>
- Sends a cookie to the browser. The cookie will be sent back on
- <i>subsequent</i> requests and can be retreived using the get function. The
- <b>key</b> and <b>value</b> parameters are required; the rest are optional.
- The <b>expire</b> parameter determines how long this cookie information will
- remain valid. It is specified in seconds from the current time. If expire is
- omitted, no expiration value will be provided along with the cookie header,
- meaning that the cookie will expire when the browser is closed. The
- <b>domain</b> and <b>path</b> parameters specify when the cookie will get
- sent; it will be restricted to certain document paths at certain domains,
- based on the cookie standard. If these are omitted, then path and/or domain
- information will not be sent in the cookie header. Lastly, the <b>secure</b>
- parameter, which defaults to false if omitted, determines whether the cookie
- information can be sent over an HTTP connection, or only via HTTPS. </li><p>
-
- <li><b>delete</b>( key ): <br> Send a cookie delete header to the browser to
- delete the <b>key</b> cookie. The same may be achieved by: <font
- face=courier>del cookie[key]</font>. </li><p>
-
- </ul>
-
- The example below shows to manage browser cookies.<p>
-
- [[includeCode('examples/cookie.spy') ]] <p>
-
- [[toc.n('Session', 'mod_session')]]
-
- Sessions allow information to be efficiently passed from one request to the
- next via some browser mechanism: get, post or cookie. The potentially large or
- sensitive information is stored at the server, and only a short identifier is
- sent to the client. Sessions are often used to create sequences of pages that
- represent an application flow. This module manages session state. All session
- state has an expiration time and is automatically garbage collected.
-
- <ul>
-
- <li><b>setHandler</b>( type, [ params ] ): <br> Selects the session handler.
- This method must be called before invoking other session functions. The
- <b>type</b> specifies the handler, and the <b>param(s)</b> is (are) passed
- to the initialiser of the chosen handler. The type parameter is a string of
- the format: <font face=courier>"file:class"</font>, where <b>file</b> is the
- location where the session handler is defined and <b>class</b> is the name
- of the session handler. The file name component is optional, and is searched
- for using the standard module-finding rules. If only the class name is
- specified, then the default location is used: inside the session module
- itself, where the standard Spyce session handlers reside. <p>
-
- The standard Spyce session handlers are listed below, along with the
- parameters they take. If you would like to implement your own, custom
- session handler, there are two ways to do so. First, you can have a look at
- <font face=courier>modules/session.py</font> and define your subclass of the
- <font face=courier>sessionHandler</font> class. One would do this when
- defining a general-purpose session handler, and if you do go to this
- trouble, please email it in as a <a
- href="http://spyce.sourceforge.net/contrib/index.html">contribution</a>.
- Alternatively, you can simply use the <b>session_user</b> handler, also
- defined below, to your own install callback functions. The majority of users
- should be satisfied with the basic session handlers provided. <p>
-
- <ul>
-
- <li>setHandler( <b>'session_dir'</b>, directory ): <br> Uses inidividual
- files in the specified <b>directory</b> to store session information.
- </li><p>
-
- <li>setHandler( <b>'session_gdbm'</b>, file ): <br> Uses the gdbm library
- to create and manage the session information inside the specified
- <b>file</b>. </li><p>
-
- <li>setHandler( <b>'session_bsddb'</b>, file ): <br> Uses the BSD database
- library to create and manage the session information inside the specified
- <b>file</b>. </li><p>
-
- <li>setHandler( <b>'session_user'</b>, getf, setf, delf, idsf, info ):
- <br> Uses user-provided functions to create and manage session
- information. The parameters are as follows:
-
- <ul>
-
- <li><b>getf</b>: A function that will be called to get session state, as
- follows: <font face=courier>getf(info, id)</font>, where
- <b>info</b> is the parameter given to setHandler above, and <b>id</b> is
- the session identifier. This function should ensure that the session has
- not expired. If an expired session is found, it must be automatically
- deleted. Note that a delete may never be called on an object, so it is
- imperative for getf() to delete objects when expiration is detected. If
- the session has expired, or if the session does not exist, this function
- should return None, otherwise the session information. </li>
-
- <li><b>setf</b>: A function that will be called to set or create session
- state, as follows: <font
- face=courier>setf(info, state, expire, serverID, id)</font>,
- where <b>info</b> is the parameter given to setHandler above,
- <b>state</b> is the actual session information to be preserved,
- <b>expire</b> is the number of seconds after which this information will
- be invalidated, <b>serverID</b> is a unique identifier for this server
- that can be used to avoid race conditions between two Spyce engines
- generating new session identifiers, and <b>id</b> is the optional
- session identifier. If an identifier is provided, that session should be
- updated, otherwise (namely, in the case when id is set to None), a new
- session identifier should be generated. This function returns the (new
- or old) session identifier. </li>
-
- <li><b>delf</b>: A function that will be called to set delete a session,
- as follows: <font face=courier>delf(info, id)</font>, where
- <b>info</b> is the parameter given to the setHandler above and <b>id</b>
- is the session identifier of the session to be invalidated. </li>
-
- <li><b>idsf</b>: A function that will be called to get all the session
- identifiers, as follows: <font face=courier>idsf(info)</font>, where
- <b>info</b> is the parameter given to the setHandler above. This
- function should return ALL session identifiers, even those that have
- expired and are to be deleted. Among other purposes, this function is
- used to automatically clean up session state periodically, by performing
- a getf() on all sessions. (Remember that according to the semantics
- defined for getf(), it will delete any expired sessions.) </li>
-
- <li><b>info</b>: At the very least, this is a key that uniquely
- identifies this session handler. The info variable may also contain any
- other additional information. It is passed back as-is to each of the
- session callback functions, as described previously. </li>
-
- </ul> <p>
-
- </ul>
- <p>
-
-
- <li><b>get</b>( id ): <br> Returns the object stored under the given
- <b>id</b>. If the id does not exist, or was previously used but has expired,
- then None is returned. As with the cookie module, the session module may be
- treated as an associative array when retrieving session information.
- </li><p>
-
- <li><b>set</b>( data, expire, [id] ): <br> Stores the <b>data</b> object
- under the given <b>id</b>. If id is omitted, then a unique one is generated.
- On success, an id is returned, otherwise an exception raised. The
- <b>expire</b> field specifies the number of seconds that the session
- information is valid for. </li><p>
-
- <li><b>delete</b>( id ): <br> Deletes the session stored under the given
- <b>id</b>. Note that sessions are automatically deleted upon expiration, so
- this method need only be used when immediate invalidation is desired. As
- with the cookie module, the session module may be treated as an associative
- array when removing session information. </li><p>
-
- <li><b>autoSession</b>( expire, [method], [name] ): <br> This function can
- remove most of the code associated with session management, by doing it
- automatically. Namely, it automatically retrieves the session information
- and resaves it at the end of the request, using the <b>auto</b>,
- <b>autoID</b>, <b>autoName</b> and <b>autoMethod</b> fields (explained
- below). The <b>expire</b> parameters acts as before, to specify how long the
- session information remains valid. The <b>method</b> and <b>name</b>
- parameters instruct the session module how to find the session identifier.
- Method can be one of <font face=courier>'get'</font>, <font
- face=courier>'post'</font>, or <font face=courier>'cookie'</font>, which is
- the default. The name parameter, under which the session id is stored,
- defaults to 'spyceSession'. If the lookup is unable to find a session id for
- this request a new session is created. At the end of the request, the
- session information is automatically saved, and a cookie automatically
- generated if the 'cookie' method was chosen. For the 'get' and 'post'
- methods the user is required to encode the <b>autoID</b> (session id) inside
- all form targets and urls that are generated. </li><p>
-
- <li><b>auto</b>: <br> The field containing the actual session information,
- when automatic session management is used. Set it to whatever you like, as
- long as it can be serialized. Its initial value, for a new session, is None.
- </li><p>
-
- <li><b>autoID</b>: <br> The session identifier, when automatiic session
- management is used. </li><p>
-
- <li><b>autoName</b>: <br> The variable named used to identify the cookie or
- the parameter in the get or post requests containing the session identifier,
- when automatic session management is used.</li><p>
-
- <li><b>autoMethod</b>: <br> The method used ('cookie', 'post' or 'get') to
- load and save the session identifier, when automatic session management is
- used.</li><p>
-
- </ul>
-
- The example below shows how a session can be used to count the number of times
- the same open browser visited our page. The session ID is stored in a cookie
- that expires when the browser is closed. Note that the session module
- automatically loads the cookie module if not already loaded and is needed.<p>
-
- [[includeCode('examples/session.spy')]]
- <p>
-
- The next example highlights the convenience of using autoSession. By default,
- the session identifier is stored using a cookie named 'spyceSession'.<p>
-
-
- [[includeCode('examples/autosession.spy')]]
- <p>
-
- If cookies are not desired, the automatic session identifier
- <font face=courier>session.autoID</font> can also be transmitted via a browser
- post, as shown:<p>
-
- [[includeCode('examples/autosessionpost.spy')]] <p>
-
- Finally, one can easily define some new session handling mechanism using
- callback functions, as this last example shows:
-
- [[includeCode('examples/mysession.spy')]] <p>
-
-
- [[toc.n('Pool', 'mod_pool')]]
-
- The pool module provides support for server-pooled variables. That is support
- for variables whose lifetime begins when declared, and ends when explicitly
- deleted or when the server dies. These variables are often useful for storing
- persistent database connections and other information that may be expensive to
- compute at each request. Another interesting use of pool variables is to store
- file- or memory-based lock objects for concurrency control. A pooled variable
- can hold any Python value. <p>
-
- The pool module may be accessed as a regular dictionary, supporting the usual
- <font face=courier>get</font>, <font face=courier>set</font>, <font
- face=courier>delete</font>, <font face=courier>has_key</font>, <font
- face=courier>keys</font>, <font face=courier>values</font> and <font
- face=courier>clear</font> operations. Note that the pool is shared across all
- Spyce files. If file-specific variables are desired, simply include the
- filename in the pool variables name as a tuple [i.e. (filename, variable)], or
- in some other form. <p>
-
- The pool module also provides access to any server variables that are set in
- the Spyce engine <a href="[[=genTOClink('runtime_common')]]">configuration
- file</a>. A hashtable of these variables is available as <b>pool.server</b>.
-
- The example below shows how the module is used:<p>
-
- [[includeCode('examples/pool.spy')]] <p>
-
- [[toc.n('Template', 'mod_template')]]
-
- In general, a template is useful for separating form from function. Or, in
- other words, one would like web page designers to play with one file, and
- programmers to play with another, so that they don't step on each other's
- toes. A templating engine then puts the two pieces (template and data)
- together to create the final output. The Spyce language internally provides <a
- href="[[=genTOClink('lang_lambda')]]">Spyce lambdas</a>, which can be very
- useful for templating purposes. This module provides hooks to various external
- templating engines. <p>
-
- Spyce interacts with the rather powerful <a
- href="http://www.cheetahtemplate.org">Cheetah</a> Python-based templating
- engine. The Cheetah engine is not included with the Spyce distribution, some
- recommended installation instructions are provided below. The Cheetah engine
- is invoked as follows:<p>
-
- <ul>
-
- <li><b>cheetah</b>( file, [lookup] ): <br> Calling this function will invoke
- the Cheetah engine to compile (and cache) the template <b>file</b> provided.
- The engine then "runs" the template and fills in the appropriate data from
- the <b>lookup</b> dictionary, or list of dictionaries. If the lookup is
- omitted, the convenient default is to use the local and global variables
- from the current context. The template is filled and the resulting string is
- returned. </li><p>
-
- </ul><p>
-
- To install Cheetah (instructions correct as of version 0.9.15a1), follow the
- following steps:
-
- <ul>
-
- <li>Download the latest Cheetah engine from their <a
- href="http://www.cheetahtemplate.org">website</a>.</li>
-
- <li>Extract the files from the gzipped tarball into some directory</li>
-
- <li>Switch to root user</li>
-
- <li>In that directory type:
- <font face=courier>python setup.py install</font></li>
-
- <li>Now, change directory to:
- <font face=courier>/usr/lib/python2.2/site-packages/</font></li>
-
- <li>Type in:
- <font face=courier>chmod -R a+r Cheetah*</font></li>
-
- <li>Type in:
- <font face=courier>chmod a+x `find Cheetah -type d`</font></li>
-
- </ul>
- <p>
-
- In general, that the Python path must simply include the Cheetah installation
- directory and Spyce will find it. If not, you will see an import error. At
- this time, the Cheetah engine requires Python version 2.0 or higher.<p>
-
- Support for other templating engines will be added as needed. An example of
- how templates are used is shown below, with the template files appended
- thereafter.<p>
-
- [[includeCode('examples/template.spy')]] <p>
-
- [[includeCode('examples/template.tmpl')]] <p>
-
- [[toc.n('Compress', 'mod_compress')]]
-
- The compress module supports dynamic compression of Spyce output, and can save
- bandwidth in addition to static <a
- href="[[=genTOClink('lang_directive')]]">compaction</a>. The different forms
- of compression supported are described below.
-
- <ul>
-
- <li><b>spaces</b>( [ boolean ] ): <br> Controls dynamic space compression.
- Dynamic space compression will eliminate consecutive whitespaces (spaces,
- newlines and tabs) in the output stream, each time it is flushed. The optional
- <b>boolean</b> parameter defaults to true. <p>
-
- <li><b>gzip</b>( [ level ] ): <br> Applies gzip compression to the Spyce
- output stream, but only if the browser can support gzip content encoding. Note
- that this function will fail if the output stream has already been flushed,
- and should generally only be used with buffered output streams. The optional
- <b>level</b> parameter specifies the compression level, between 1 and 9
- inclusive. A value of zero disables compression. If level is omitted, the
- default gzip compression level is used. This function will automatically check
- the request's <i>Accept-Encoding</i> header, and set the response's
- <i>Content-Encoding</i> header. <p>
-
- </ul>
-
- The example below shows the compression module in use.<p>
-
- [[includeCode('examples/compress.spy')]]
- <p>
-
- Note that the compression functions need not be called at the beginning of the
- input, but before the output stream is flushed. Also, to really see what is
- going on, you should telnet to your web server, and provide something like the
- following request.
-
- [[.compact mode=off]]
- <table border=0 align=center cellspacing=0 cellpadding=0><tr><td>
- <font face=courier>
- <b><pre>GET /spyce/examples/compress.spy HTTP/1.1
- Accept-Encoding: gzip</pre></b>
- </font>
- </td></tr></table>
- [[.compact]] <p>
-
- [[toc.n('Automaton', 'mod_automaton')]]
-
- <i>The current release of the automaton module is preliminary and is still in
- flux.</i> The automaton module provides support for state machine-based
- application design, which is often useful when designing websites with
- application flows. The state machine is a directed, labelled graph. It has
- states (nodes with names), and transitions (directed edges with names). One of
- the states is defined to be a <b>begin</b> state for the machine. Every state
- has a <b>send</b> function, a <b>receive</b> function and a set of outgoing
- edges.<p>
-
- The basic idea behind the operation of the automaton module is as follows: The
- application is at some state when a request comes in. The receive function for
- that state is invoked to process the input from the browser. Based on this
- input the receive function returns some edge label, which takes the
- application from the current state to its new state. The send function of this
- new state is invoked to emit the appropriate application page. The data that
- returns from this page will be processed by the corresponding receive
- function, and so on. All you need to remember between requests is which state
- the application is in, which can be done via get or post, or via cookies using
- the cookie module. Better yet (to keep application states private and on the
- server for security reasons), one can store the state label in the session
- using the session module. <p>
-
- A state machine can be defined programmatically using the following functions:
-
- <ul>
-
- <li><b>state</b>( name, send, recv ): <br> Add a new state labelled
- <b>name</b> with associated <b>send</b> and <b>recv</b> functions. </li><p>
-
- <li><b>transition</b>( state1, name, state2 ): <br> Add a new edge labelled
- <b>name</b> from <b>state1</b> to <b>state2</b>. There is always a
- self-referencing edge with the label None, but this can be overidden.
- </li><p>
-
- <li><b>begin</b>( state ): <br> Define a given state to be the begin state.
- </li><p>
-
- <li><b>define</b>( sm, begin ): <br> Define an entire automaton <b>sm</b>
- all at once, where sm is a hashtable. The keys are the states and the values
- are triplets with a send function, a receive function and an edge hashtable.
- The edge hashtable has names of the edges as keys and the target states as
- values. The <b>begin</b> state is given. </li><p>
-
- </ul>
- <p>
-
- To step through the state machine transitions, you call:
-
- <ul>
-
- <li><b>step</b>( [state] ): <br> If <b>state</b> is specified, then call the
- receive function of that state. The receive function returns an edge label,
- which points to the new state. If no state is specified, just set the new
- state to the begin state of the automaton. Then, call the send function of
- the new state. Note that the send function is responsible for encoding its
- own state label, for use on the subsequent client request. </li><p>
-
- </ul>
- <p>
-
- Future releases of this module may add support for different types of send and
- receive handlers. For example, it is probably useful to be able to internally
- redirect to various Spyce pages for send processing, rather than inline
- functions. It may also be possible to pass information among the different
- functions, which could be useful, for example, in handling error messages
- during form processing. It may also be useful to define a sequence of states,
- where previous and next are implicit edges.<p>
-
- The following examples, shows the above in action:<p>
-
- [[includeCode('examples/automaton.spy')]]
- <p>
-
- [[toc.n('TOC', 'mod_toc')]]
-
- The TOC module provides support for constructing a table contents for a
- lengthy document, such as this user documentation. The primary task of the TOC
- module is to maintain a document tree, and initiate callbacks at the
- appropriate points in the document. Note that this module may automatically
- force a secondary processing of the Spyce file to resolve forward references.
- <p>
-
- The module provides the following methods to segment the document:
-
- <ul>
-
- <li><b>begin</b>( data, [tag] ): <br> Increase the nesting level and add a
- new section. The <b>data</b> is stored in the document tree, and used for
- callbacks (see later). An optional <b>tag</b> may be associated with the
- node, otherwise one will automatically be generated. The function <b>b</b>()
- is equivalent. </li><p>
-
- <li><b>next</b>( data, [tag] ): <br> Add a new section at the same nesting
- level. The <b>data</b> is stored in the document tree, and used for
- callbacks (see later). An optional <b>tag</b> may be associated with the
- node, or one will be automatically generated. The function <b>n</b>() is
- equivalent.</li><p>
-
- <li><b>end</b>(): <br> Decrease the nesting level. The function <b>e</b>()
- is equivalent.</li><p>
-
- <li><b>anchor</b>( data, [tag] ): <br> Set <b>data</b> and optionally the
- <b>tag</b> associated with the root of the document tree. If the tag is
- omitted, it defaults to the string <i>'root'</i>. </li><p>
-
- <li><b>level</b>( depth, data, [tag] ): <br> Start a new section at given
- <b>depth</b> with given <b>data</b> and optional <b>tag</b>. The necessary
- begin(), next() and end() calls are automatically made, based on the current
- document depth, so both types of calls can be inter-mixed. </li> <p>
-
- <li><b>l1</b>( data, [tag] ): <br> Start a level 1 section. This
- function merely calls <b>level</b>(1, <b>data</b>, <b>tag</b>).
- The functions, <b>l2</b>()...<b>l9</b>() are similarly defined. </li> <p>
-
- </ul> <p>
-
- The following methods provide access to document information:
-
- <ul>
-
- <li><b>getTag</b>(): <br> Return the tag of the current document section.
- </li><p>
-
- <li><b>getNumbering</b>( [tag] ) <br> Return the numbering of some section
- of the document identified by the given <b>tag</b>. If the tag is omitted,
- the current document section is assumed. The numbering is an array of
- numbers. This function may return 'None' on the first pass through a
- document. </li><p>
-
- <li><b>getData</b>( [tag] ) <br> Return the data associated with some
- section of the document identified by the given <b>tag</b>. If the tag is
- omitted, the current document section is assumed. This function may return
- 'None' on the first pass through a document. </li><p>
-
- <li><b>getDepth</b>( [tag] ) <br> Return the depth of some section of the
- document identified by the given <b>tag</b>. If the tag is omitted, the
- current document section is assumed. This function may return 'None' on the
- first pass through a document. </li><p>
-
- <li><b>getNextTag</b>( [tag] ) <br> Return the tag of the section following
- some section of the document identified by the given <b>tag</b>. If the tag
- is omitted, the current document section is assumed. If this is the last
- section of the document, then this function will return 'None'. This
- function may return 'None' on the first pass through a document. </li><p>
-
- <li><b>getPrevTag</b>( [tag] ) <br> Return the tag of the section before
- some section of the document identified by the given <b>tag</b>. If the tag
- is omitted, the current document section is assumed. If this is the first
- section of the document, then this function will return 'None'. This
- function may return 'None' on the first pass through a document. </li><p>
-
- <li><b>getParentTag</b>( [tag] ) <br> Return the tag of the section above
- (or containing) some section of the document identified by the given
- <b>tag</b>. If the tag is omitted, the current document section is assumed.
- If this is the top-most section of the document, then this function will
- return 'None'. This function may return 'None' on the first pass through a
- document. </li><p>
-
- <li><b>getChildrenTags</b>( [tag] ) <br> Return a list (possibly empty) of
- tags of the sections directly contained within some section of the document
- identified by the given <b>tag</b>. If the tag is omitted, the current
- document section is assumed. This function may return a shorter list than
- anticipated or 'None', on the first pass through a document.
-
- </ul> <p>
-
- The TOC modules can make callbacks to handlers that format the document
- correctly. The handlers should be defined and registered before the first
- section break in the document. The following functions register handlers:
-
- <ul>
-
- <li><b>setDOC_PUSH</b>( f ): <br> Register a function <b>f</b> to be called
- when the nesting depth of the document increases. </li><p>
-
- <li><b>setDOC_POP</b>( f ): <br> Register a function <b>f</b> to be called
- when the nesting depth of the document decreases. </li><p>
-
- <li><b>setDOC_START</b>( f ): <br> Register a funtion <b>f</b> to be called
- at the beginning of a section. </li><p>
-
- <li><b>setDOC_END</b>( f ): <br> Register a function <b>f</b> to be called
- at the end of a section. </li><p>
-
- <li><b>setTOC_PUSH</b>( f ): <br> Register a function <b>f</b> to be called
- when the nesting depth of the table of contents increases. </li><p>
-
- <li><b>setTOC_POP</b>( f ): <br> Register a function <b>f</b> to be called
- when the nesting depth of the table of contents decreases. </li><p>
-
- <li><b>setTOC_ENTRY</b>( f ): <br> Register a function <b>f</b> to be called
- for each table of contents entry. </li><p>
-
- </ul><p>
-
- Each callback function should be of the form: <center> <b>f</b>(depth,
- tag, numbering, data), </center> where: <b>depth</b> is the nesting depth,
- <b>tag</b> is the associated tag, <b>numbering</b> is the position array, and
- <b>data</b> is the associated data of the section for which the callback was
- made.<p>
-
- The <i>DOC</i> callbacks are made as the sections are encountered. The
- <i>TOC</i> callbacks are made while printing the table of contents. If the
- modules detects that forward references exist in the document, the document
- will be processed twice, and only the second output will be sent. Note that
- buffering MUST be turned on for this to function correctly. <p>
-
- To display a table of contents, define the appropriate TOC callback functions
- and call:
-
- <ul>
-
- <li><b>showTOC</b>(): Display the table of contents.
-
- </ul>
-
- For an example of how to use the TOC module, please refer to the source Spyce
- file of this documentation. <p>
-
- [[--includeCode('examples/automaton.spy')--]]
- <p>
-
- [[toc.n('Writing Modules', 'mod_new')]]
-
- Writing your own Spyce modules is simple. Let us begin with a basic example
- called myModule. It is a module that implements one function named foo().
-
- [[includeCode('examples/myModule.py', run=0)]]
- <p>
-
- Saving this code in <font face=courier>myModule.py</font> in the same
- directory as the Spyce script, or somewhere on the module path, we could use
- it as expected: <p>
-
- <table align=center border=0><tr><td><font face=courier>
- [[.compact mode=off]]
- <b><pre>\[[.import name=myModule\]]
- \[[ myModule.foo() \]]
- </pre></b>
- [[.compact]]
- </font></td></tr></table>
- <p>
-
- A Spyce module can be any Python class that derives from
- <b>spyceModule.spyceModule</b>. Do not override the <b> __init__(...)</b>
- method because it is inherited from spyceModule and has an fixed signature
- that is expected by the Spyce engine's module loader. The inherited method
- accepts a Spyce API object, a <a
- href="http://www.python.org/doc/current/lib/module-Bastion.html">Bastion</a>
- of <b>spyce.spyceWrapper</b>, an internal engine object, and stores it in
- <b>self._api</b>. This is the building block for all the functionality that
- any module provides. The available API methods of the wrapper are (listed in
- spyceModule.spyceModuleAPI):
-
- <ul>
-
- [[ for api in spyceModule.spyceModuleAPI: { ]]
-
- <li> <b>[[=api]]</b>: [[=eval('spyce.spyceWrapper.%s.__doc__'%api)]]</li>
-
- [[ } ]]
-
- </ul> <p>
-
- For convenience, one can sub-class the <b>spyceModulePlus</b> class instead of
- the regular <b>spyceModule</b>. The spyceModulePlus defines a
- <b>self.modules</b> field, which can be used to acquire references to other
- modules loaded into the Spyce environment. The <i>response</i> module, for
- instance, would be referenced as <i>self.modules.response</i>. Modules are
- loaded on demand, if necessary. The spyceModulePlus also contains a
- <b>self.globals</b> field, which is a reference to the Spyce global namespace
- dictionary, though this should rarely be needed. <p>
-
- <b>Note:</b> It is not expected that many module writers will need the entire
- API functionality. In fact, the vast majority of modules will use a small
- portion of the API, if at all. Many of these functions are included for just
- one of the standard Spyce modules that needs to perform some esoteric
- function. <p>
-
- Three Spyce module methods, <b>start()</b>, <b>init([args])</b> and
- <b>finish(error)</b> are special in that they are automatically called by the
- runtime during Spyce request startup, processing and cleanup, respectively.
- The modules are started in the order in which module directives appear in the
- file, before processing begins. The implicitly loaded modules are always
- loaded first. The init method is called during Spyce processing at the
- location of the module directive in the file, with the optional args attribute
- is passed as the arguments of this call. Finally, after Spyce processing is
- complete, the modules are finalized in reverse order. If there is an unhandled
- exception, it will be wrapped in a spyce.spyceException object and passed as
- the first parameter to finish(). During successful completion of Spyce
- processing (i.e. without exception), the error parameter is None. The default
- inherited start, init and finish methods from spyceModule are noops. <p>
-
- <b>Note 2:</b> When writing a Spyce module, consider carefully why you are
- selecting a Spyce module over a regular Python module. If it is just code,
- that does not interact with the Spyce engine, then a regular Python <font
- face=courier>import</font> instead of an Spyce <font
- face=courier>\[[.import\]]</font> can just as easily bring in the necessary
- code, and is preferred. In other words, choose a Spyce module only when there
- is a need for per-request initialization or for one of the engine APIs. <p>
-
- Module writers are encouraged to look at the existing standard modules as
- examples and the definitions of the core Spyce objects in <font
- face=courier>spyce.py</font> as well. If you write or use a novel Spyce
- module that you think is of general use, please email your <a
- href="http://spyce.sourceforge.net/contrib/index.html">contribution</a>, or
- a link to it. Also, please keep in mind that the standard modules are designed
- with the goal of being minimalist. Much functionality is readily available
- using the Python language libraries. If you think that they should be
- expanded, also please send a note. <p>
-
- [[toc.e()]]
-
- [[toc.n('Tags', 'tag')]]
-
- The previous chapter discussed the Spyce module facility, the standard Spyce
- modules and how users can create their own modules to extend Spyce. Spyce
- functionality can also be extended via active tags, which are defined in tag
- libraries. This chapter describes what Spyce active tags are, and how they are
- used. We then describe each of the standard active tag libraries and, finally,
- how to define <a href="[[=genTOClink('tag_new')]]">new tags libraries</a>. <p>
-
- It is important, from the outset, to define what an active tag actually does.
- A few illustrative examples may help. The examples below all use tags that are
- defined in the <a href="[[=genTOClink('tag_core')]]">core tag library</a>,
- that has been installed under the <b>spy</b> prefix, as follows: <p>
-
- [[.compact mode=off]]
- <table border=0 align=center><tr><td>
- <font size="-1"><font face=courier>
- <b><pre>[[=escapeHTMLandCode('''
- %s.taglib name=core as=spy %s
- ''' % ('['*2, ']'*2) )]]</pre></b>
- </font></font>
- </td></tr></table><p>
- [[.compact]]
-
- <ul>
-
- <li><font face=courier><spy:print val="=2+2"/></font> <br> Rather
- than emitting itself as plain text, this tag will output <font
- face=courier>4</font>. </li> <p>
-
- <li><font face=courier><spy:let var="foo" val="bar"/></font> <br> This
- tag will assign the constant string value <font face=courier>bar</font> to a
- variable named <b>foo</b> in the tag context, which will then be available
- to other tags that follow later in the document. </li> <p>
-
- <li><font face=courier> <spy:for items="=range(5)"> <br>
- <spy:print value="=foo"/> <br> </spy:for> </font>
- <br> As expected, these tags will print the value of <b>foo</b>, set to
- <font face=courier>bar</font> above, 5 times. </li> <p>
-
- </ul>
-
- Note that the same output could have been achieved in many different ways, and
- entirely without active tags. The manner in which you choose to organize your
- script or application, and when you choose active tags over other
- alternatives, is a matter of personal preference. Notice also that active tags
- entirely control their output and what they do with their attributes and the
- result of processing their bodies (in fact, whether the body of the tag is
- even processed). Tags can even supply additional syntax constraints on their
- attributes that will be enforced at compile-time. Most commonly a tag could
- require that certain attributes exist, and possibly that it be used only as a
- single or only as a paired (open and close) tag. Unlike early versions of
- HTML, active tags must be strictly balanced, and this will be enforced by the
- Spyce compiler. <p>
-
- Below, each individual standard Spyce tag library is documented, followed by a
- description of how one would write a <a href="[[=genTOClink('tag_new')]]">new
- active tag library</a>. The following general information will be useful for
- reading that material.
-
- <ul>
-
- <li>Active tags are installed using the <font
- face=courier>\[[.taglib\]]</font> <a
- href="[[=genTOClink('lang_directive')]]">directive</a>, under some prefix.
- Active tags are of the format <font
- face=courier><pre:name ... ></font>, where <b>pre</b> is the
- prefix under which the tag library was installed, and <b>name</b> is defined
- by the tag library. In the following tag library documentation, the prefix
- is omitted from the syntax. </li>
-
- <li>Tags store variables and evaluated expression within a separate tag
- context. This tag context dictionary is available via <b>taglib.context</b>
- (i.e. the context field of the <a
- href="[[=genTOClink('mod_taglib')]]">taglib module</a>). The tag context
- contains references to all the loaded modules. Thus, it is valid to refer to
- something like <font face=courier>request.query()</font> in a tag
- expression. However, it is not valid to change any module variable
- references. While this will not cause any harm, the user should expect that
- these new values can be reset at any time. </li>
-
- <li>The following notation is used in the documentation of the tag libraries
- below:
-
- <ul>
-
- <li> <name .../> : The tag should be used as a singleton. </li>
-
- <li> <name ... > ... </name> : The tag should be used as an
- open-close pair. </li>
-
- <li> [ x ] : The attribute is optional. Attributes not enclosed in
- brackets are required. </li>
-
- <li> foo|<u>bar</u> : indicates that an attribute may be one of two
- constant strings. The underlined value is the default. </li>
-
- <li> <i>string</i> : an arbitrary string constant </li>
-
- <li> <i>expression</i> : may be a string constant, and may be of the form
- <font face=courier>'=expr'</font>, where <font face=courier>expr</font> is
- a Python expression that will be evaluated in the tag context. </li>
-
- </ul>
-
- </ul> <p>
-
- [[toc.b('Core', 'tag_core')]]
-
- The core active tag library is modelled after some of the functionality that
- exists in Java's <a href=http://java.sun.com/products/jsp/jstl/>JSTL</a>. It
- is still in the preliminary design stages, and more tags are expected.
- Currently, it provides the following active tags: <p>
-
- <ul>
-
- <li><font face=courier><<b>print</b> val=<i>expression</i>
- [encode=false|<u>html</u>|url] [default=<i>expression</i>] /></font>
- <br> Outputs the value of the <b>val</b> expression. If there is an error
- and a <b>default</b> is provided, the default will be evaluated instead. The
- output may be encoded to be HTML- or URL-safe, depending on the
- <b>encode</b> attribute. </li> <p>
-
- <li><font face=courier><<b>let</b> var=<i>string</i>
- val=<i>expression</i> /></font> <br> Sets the variable <b>var</b> to
- the value <b>val</b> in the tag context. </li> <p>
-
- <li><font face=courier><<b>let</b> var=<i>string</i>
- val=<i>expression</i>> ... </<b>let</b>></font> <br> Same as above,
- except that the scope of the variable is that of the tag body, and the value
- of the variable, if it existed prior to the start tag, is restored after the
- end tag. </li> <p>
-
- <li><font face=courier><<b>unlet</b> var=<i>string</i> /></font>
- <br> Unset (i.e. delete) the variable <b>var</b> in the tag context. </li>
- <p>
-
- <li><font face=courier><<b>if</b> test=<i>expression</i>> ...
- </<b>if</b>></font> <br> Evaluate <b>test</b> and conditionally
- process body of tag. </li> <p>
-
- <li><font face=courier><<b>for</b> items=<i>expression</i>
- [var=<i>string</i>] [counter=<i>string</i>]> ...
- </<b>for</b>></font> <br> Iterate through a tuple or list of
- <b>items</b> and process the body each time. The current item can optionally
- be stored in variable named by <b>var</b>, and the current iteration number
- (starting at zero) can optionally be stored in a variable named by
- <b>counter</b>. </li> <p>
-
- </ul> <p>
-
- [[toc.n('Form', 'tag_form')]]
-
- The form active tag library is designed to simplify the generation of forms.
- The tags in this library closely follow the names of HTML form tags. The active
- tags automatically look up the appropriate data values or defaults. In time,
- more tags will be added for server-side verification.
-
- <ul>
-
- <li><font face=courier><<b>form</b>
- [method=<i>expr</i>] [action=<i>expr</i>] [value=<i>expr</i>] [default=<i>expr</i>] ...> </<b>form</b>></font>
- <br> Begin a new form. The <b>method</b> parameter is optional and defaults
- to 'GET'. The <b>action</b> is evaluated in the tag context. Both parameters
- are emitted. The <b>value</b> parameter is a dictionary of values, which
- overrides any submitted form values. The <b>default</b> parameter is a
- dictionary of values that is overridden by any submitted values. </li> <p>
-
- <li><font face=courier><<b>submit</b>
- [name=<i>expr</i>] [value=<i>expr</i>] ... /></font>
- <br> Create a submit button. The <b>name</b> and <b>value</b> parameters are
- evaluated within the tag context and emitted. </li> <p>
-
- <li><font face=courier><<b>hidden</b>
- name=<i>expr</i> [value=<i>expr</i>] [default=<i>expr</i>] .../></font>
- <br> Create a hidden form field. The <b>name</b> parameter is evaluated and
- emitted. Both the <b>value</b> and <b>default</b> optional parameters are
- expressions and are evaluated. The value emitted is, in order of decreasing
- priority: local tag value, form tag value, value in submitted request
- dictionary, local tag default, form tag default. We search this list for the
- first non-None value. </li> <p>
-
- <li><font face=courier><<b>text</b>
- name=<i>expr</i> [value=<i>expr</i>] [default=<i>expr</i>] [size=<i>expr</i>] [maxlength=<i>expr</i>] .../></font>
- <br> Create a form text field. The <b>name</b> parameter is evaluated and
- emitted. Both the <b>value</b> and <b>default</b> optional parameters are
- expressions and are evaluated. The value emitted is, in order of decreasing
- priority: local tag value, form tag value, value in submitted request
- dictionary, local tag default, form tag default. We search this list for the
- first non-None value. The <b>size</b> and <b>maxlength</b> optional
- parameters are evaluated and emitted. </li> <p>
-
- <li><font face=courier><<b>password</b>
- name=<i>expr</i> [value=<i>expr</i>] [default=<i>expr</i>] [size=<i>expr</i>] [maxlength=<i>expr</i>] .../></font>
- <br> Create a form password field. Parameters are the same as for <b>text</b>
- fields, explained above. </li> <p>
-
- <li><font face=courier><<b>textarea</b>
- name=<i>expr</i> [value=<i>expr</i>] [rows=<i>expr</i>] [cols=<i>expr</i>] ...>default</<b>textarea</b>></font>
- <br> Create a form textarea field. The <b>name</b> parameter is evaluated and
- emitted. The <b>value</b> optional parameter is evaluated. A <b>default</b>
- may be provided in the body of the tag. The value emitted is, in order of
- decreasing priority: local tag value, form tag value, value in submitted
- request dictionary, local tag default, form tag default. We search this list
- for the first non-None value. The <b>rows</b> and <b>cols</b> optional
- parameters are evaluated and emitted. </li> <p>
-
- <li><font face=courier><<b>radio</b>
- name=<i>expr</i> value=<i>expr</i> [checked] [default] .../></font>
- <br> Create a form radio-box. The <b>name</b> and <b>value</b> parameters are
- evaluated and emitted. A <b>checked</b> and <b>default</b> flags affect
- whether this box is checked. The box is checked based on the following
- values, in decreasing order of priority: local tag value, form tag value,
- value in submitted request dictionary, local tag default, form tag default.
- We search this list for the first non-None value. </li> <p>
-
- <li><font face=courier><<b>checkbox</b>
- name=<i>expr</i> value=<i>expr</i> [checked] [default] .../></font>
- <br> Create a form check-box. Parameters are the same as for <b>radio</b>
- fields, explained above. </li> <p>
-
- <li><font face=courier><<b>select</b>
- name=<i>expr</i> [value=<i>expr</i>] [default=<i>expr</i>] [multiple] [size=<i>expr</i>] ...>...</<b>select</b>></font>
- <br> Create a form select block. The <b>name</b> parameter and the optional
- <b>size</b> parameters are evaluated and emitted. The <b>value</b> and
- <b>default</b> optional parameters are evaluated and serve to select the
- nested option fields. The <b>multiple</b> flag sets whether multiple
- selections are allowed. </li><p>
-
- <li><font face=courier><<b>option</b>
- [text=<i>expr</i>] [value=<i>expr</i>] [selected] [default] .../></font>
- <br> <font face=courier><<b>option</b>
- [value=<i>expr</i>] [selected] [default] ...>text</<b>option</b>></font>
- <br> Create a form selection option. This tag must be nested within a
- <b>select</b> tag. The <b>text</b> optional parameter is evaluated and
- emitted in the body of the tag. It can also be provided in the body of the
- tag, as per the HTML standard. The optional <b>value</b> parameter is
- evaluated and emitted. The <b>selected</b> and <b>default</b> flags determine
- which options are selected. The options is selected based on the following
- values, in decreasing order of priority: local tag value, select tag value,
- form tag value, value in submitted request dictionary, local tag default,
- select tag default, form tag default. We search this list for the first
- non-None value. </li> <p>
-
- </ul> <p>
-
-
-
-
- [[toc.n('Writing Tag Libraries', 'tag_new')]]
-
- 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 <i>far</i>
- simpler than writing a JSP tag.<p>
-
- We begin with a basic example: <p>
-
- [[includeCode('examples/myTaglib.py', run=0)]] <p>
-
- Saving this code in <font face=courier>myTaglib.py</font>, in the same
- directory as your script or anywhere else in the search path, one could then
- use the <b>foo</b> active tag (defined above), as follows: <p>
-
- [[includeCode('examples/tag.spy')]] <p>
-
- An active tag library can be any Python class that derives from
- <b>spyceTag.spyceTagLibrary</b>. The interesting aspects of this class
- definition to implementors are: <p>
-
- <ul>
-
- <li><b>tags</b>: <br> This field is usually all that requires redefinition.
- It should be a list of the <i>classes</i> (as opposed to instances) of the
- active tags. </li> <p>
-
- <li><b>start</b>(): <br> This methd is invoked by the engine upon loading
- the library. The inherited method is a noop. </li> <p>
-
- <li><b>finish</b>(): <br> This method is invoked by the engine upon
- unloading the library after a request. The inherited method is a noop. </li>
- <p>
-
- </ul>
-
- Each active tag can be any Python class that derives from
- <b>spyceTag.spyceTag</b>. The interesting aspects of the class definition for
- tag implementors are: <p>
-
- <ul>
-
- <li><b>name</b>: <br> This field MUST be overidden to indicate the name of
- the tag that this class defines. </li> <p>
-
- <li><b>buffer</b>: <br> 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. </li> <p>
-
- <li><b>conditional</b>: <br> 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 <font face=courier>core:if</font> 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. </li>
- <p>
-
- <li><b>loops</b>: <br> 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
- <font face="courier">core:for</font> 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. </li> <p>
-
- <li><b>catches</b>: <br> 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 <font face="courier">core:catch</font>, 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. </li>
- <p>
-
- <li><b>mustend</b>: <br> This flag indicates whether this tag wants the
- end() method to get called, if the begin() completes successfully, <i>no
- matter what</i>. 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 <font face="courier">core:let</font> 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. </li> <p>
-
- <li><b>syntax</b>(): <br> 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
- <b>spyceTagSyntaxException</b>. </li> <p>
-
- <li><b>begin</b>( ... ): <br> 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
- <b>conditional</b> 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. </li> <p>
-
- <li><b>body</b>( contents ): <br> This method is invoked when the body of
- the tag has <i>completed</i> 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 <b>buffer</b> to true for capturing the body processing output
- (see above), then the string output of the body processing has been captured
- and stored in <b>contents</b>. It is the responsibility of this method to
- emit something, if necessary. If the tag does not buffer then
- <b>contents</b> will be None, and any output has already been written to the
- enclosing scope. If the <b>loops</b> 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. </li> <p>
-
- <li><b>end</b>(): <br> 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 <b>mustend</b> flag
- has been set to true, then the runtime engine semantics ensure that if the
- begin method terminates successfully, this method <i>will</i> get called,
- even in the case of an exception during body processing. The inherited
- method is a noop. </li> <p>
-
- <li><b>catch</b>( ex ): <br> If the <b>catches</b> 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 <b>ex</b> holds the value that was thrown. The inherited method
- simply re-raises the exception. </li> <p>
-
- <li><b>getPrefix</b>(): <br> Return the prefix under which this tag library
- was installed. </li> <p>
-
- <li><b>getAttributes</b>(): <br> Return a dictionary of tag attributes.
- </li> <p>
-
- <li><b>getPaired</b>(): <br> Return true if this is a paired (open and
- close) tag, or false if it is a singleton. </li> <p>
-
- <li><b>getParent</b>( [name] ): <br> 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
- <b>name</b> 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. </li> <p>
-
- <li><b>getOut</b>(): <br> Return the (possibly buffered) output stream that
- this tag should write to. </li> <p>
-
- <li><b>getContext</b>(): <br> 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.
- </li> <p>
-
- <li><b>getBuffered</b>(): <br> Returns true if the tag output stream is a
- local buffer, or false if the output is connected to the enlosing scope.
- </li> <p>
-
- </ul>
-
- For convenience, tag implementors may wish to derive their implementations
- from <b>spyceTagPlus</b>, which provides some useful additional methods:
-
- <ul>
-
- <li><b>contextSet</b>( name, (exists,value) ): <br> Accepts a variable
- <b>name</b> and a tuple containing an <b>exists</b> flag and a <b>value</b>.
- 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(). </li> <p>
-
- <li><b>contextGet</b>( name ): <br> Returns the current state of the
- variable <b>name</b> in the tag context. The state is a tuple containing a
- flag whether the variable is defined and its value. </li> <p>
-
- <li><b>contextEval</b>( expr ): <br> Evaluates a string <b>expr</b> 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. </li> <p>
-
- <li><b>contextGetModule</b>( name ): <br> Return a reference to a module
- from the tag context. The module is loaded, if necessary. </li> <p>
-
- <li><b>syntaxExist</b>( [must]* ): <br> Ensure that the list of attributes
- given in <b>must</b> are all defined in the attributes of this tag.
- Otherwise, a spyceTagSyntaxException is thrown. </li> <p>
-
- <li><b>syntaxExistOr</b>( [mustgroups]* ): <br> Ensure that at least one of
- the lists of attributes specified in <b>mustgroups</b> satisfies
- syntaxExist(). Otherwise, a spyceTagSyntaxException is thrown. </li> <p>
-
- <li><b>syntaxExistOrEx</b>( [mustgroups]* ): <br> Ensure that exactly one of
- the lists of attributes specified in <b>mustgroups</b> satisfies
- syntaxExist(). Otherwise, a spyceTagSyntaxException is thrown. </li> <p>
-
- <li><b>syntaxNonEmpty</b>( [names]* ): <br> Ensure that if the attributes
- listed in <b>names</b> 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. </li> <p>
-
- <li><b>syntaxValidSet</b>( name, validSet ): <br> Ensure that the value of
- the attribute <b>name</b>, if it exists, is one of the values in the set
- <b>validSet</b>. Otherwise, a spyceTagSyntaxException is raised. </li> <p>
-
- <li><b>syntaxPairOnly</b>(): <br> Ensure that this tag is a paired tag.
- Otherwise, a spyceTagSyntaxException is thrown. </li> <p>
-
- <li><b>syntaxSingleOnly</b>(): <br> Ensure that this tag is a singleton tag.
- Otherwise, a spyceTagSyntaxException is thrown. </li> <p>
-
- </ul>
-
- 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 <font
- face=courier>tags/core.py</font>. The more curious reader is welcome to look
- at the tag library internals in <font face=courier>spyceTag.py</font> and
- <font face=courier>modules/taglib.py</font>. The tag semantics are ensured by
- the Spyce compiler (see <font face=courier>spyceCompile.py</font>), though it
- is likely easier simply to look at the generated Python code using the <font
- face=courier>"spyce -c"</font> command-line facility. <p>
-
- [[toc.e()]]
-
- [[toc.n('Installation', 'conf')]]
-
- Spyce can be installed and used in many configurations. Hopefully, one of the
- ones below will suit your needs. If not, feel free to email the lists asking
- for assistance in using a different setup. If you have successfully set up
- Spyce by some other method, please email me the details so that I can post
- them for others. Lastly, if you had troubles following these instructions,
- please send an email with suggestions on how to improve them. <p>
-
- [[toc.b('Overview', 'conf_overview')]]
-
- Spyce supports a variety of installation methods (automated versus manual),
- webserver adapters (FastCGI, mod_python, proxy, CGI and command-line) and
- operating system environments (Linux and Windows), which require separate
- discussion and configuration-specific tweaks. These specifics are kept to an
- absolute minimum, however, and, wherever possible, the configuration of the
- Spyce engine is performed through a common configuration file. <p>
-
- The supported adapters are:
-
- <ul>
-
- <li> <b>Fast CGI:</b>The default Spyce integration with Apache is acheived
- via <a href="http://www.fastcgi.com">FastCGI</a>, a CGI-like interface that
- is relatively fast, because it does not incur the large process startup
- overhead on each request. </li> <p>
-
- <li> <b>mod_python:</b> If you really must have the fastest Spyce
- implementation (see the <a
- href="[[=genTOClink('add_perf')]]">performance</a> numbers), it is
- currently through an Apache module called <a
- href="http://www.modpython.org">mod_python</a>. Spyce has been tested with
- mod_python version 2.7.6 (and version 3.0.3 with apache 2.0.37). You can try
- to find some mod_python rpms <a
- href="http://www.rpmfind.net/linux/rpm2html/search.php?query=mod_python">here</a>,
- but in general one must compile mod_python from sources. The reason for this
- is because mod_python links with the Python library it finds on your system
- at compile time. Thus, even if you have the correct Python version installed
- on your system, mod_python will be using the Python library version on the
- system where it was compiled. Also, note that mod_python (or rather Apache)
- needs a Python that has been compiled without threading, so you may need to
- recompile Python as well for this reason. The process is not very difficult
- (just the usual: <i>./configure; make; make install</i> dance), but
- hopefully someone will suggest a better route in time. In any case, make
- sure you can first get mod_python running on your system, if that is that is
- your chosen Apache integration route. </li> <p>
-
- <li> <b>Web server:</b> Another fast alternative is to serve Spyce files via
- a proxy. This involves running Spyce in web-server mode, and configuring the
- main web server to forward the appropriate requests. The built-in Spyce web
- server can also be used to serve requests directly, but this is highly
- discouraged for production environments. </li> <p>
-
- <li> <b>CGI:</b> Failing these alternatives you can always process requests
- via regular CGI, but this alternative is the slowest option and is intended
- primarily for those who do not have much control over their web environments.
- </li> <p>
-
- <li> <b>Command line:</b> Lastly, one can use Spyce as a command-line tool
- for pre-processing Spyce pages and creating static HTML files. </li> <p>
-
- </ul>
-
- Users have written a number of additional Spyce adapters, including an adapter
- for the Xitami extension protocol and an adapter for the Coil framework. <p>
-
- [[toc.n('Requirements', 'conf_req')]]
-
- Spyce (the core engine and all the standard modules) currently requires
- <b>Python version [[=verchk.REQUIRED]] or greater</b>, and <b>Apache version
- 1.3.x or greater</b>. <p>
-
- However, Spyce is developed and tested mostly with <b>Python version
- 2.2.x</b>, and using <b>Apache version 2.0.x</b>. <p>
-
- Spyce currently does <i>not</i> require thread support. Spyce uses no
- version-specific Apache features. It is highly recommended that you use
- <b>Python version 2.1 or higher</b>, because certain common operations can be
- difficult without the new nested scoping functionality. <p>
-
- If you find a bug, please let me know! <p>
-
- [[toc.n('Installation Methods', 'conf_methods')]]
-
- Spyce can be installed in a variety of ways:
-
- <ul>
-
- <li> <a href="[[=genTOClink('conf_rpm')]]">Automated installation via RPM</a>
- </li> <p>
-
- <li> <a href="[[=genTOClink('conf_win')]]">Automated installation via Windows
- installer</a> </li> <p>
-
- <li> <a href="[[=genTOClink('conf_source')]]">Manual installation</a> </li>
- <p>
-
- </ul>
-
- <p>
-
- [[toc.b('Automated installation - RPM', 'conf_rpm')]]
-
- The easiest way to install Spyce on Linux is via RPM. Download the file from
- the link above and, as root user on your system, type in the following
- command: (If you upgrading Spyce, it is recommended to uninstall the previous
- version using <b>rpm -e spyce</b>, and then install a fresh copy, as opposed
- to using the <b>rpm -U</b> option.) <p>
-
- <font face=courier>
- [[.compact mode=off]]
- <pre>
- <b>rpm -i spyce-[[=spyce.__version__]]-[[=release]].rpm</b>
- </pre>
- [[.compact]]
- </font>
- <p>
-
- This will:
-
- <ul>
-
- <li>Install the Spyce engine</li>
- <li>Install the web-based and command-line Spyce processors</li>
- <li>Configure Apache to process .spy files via fast cgi, if installed or regular cgi otherwise
- <li>Restart the server</li>
- <li>Install the Spyce documentation under <a href="http://localhost/spyce">http://localhost/spyce</a></li>
-
- </ul>
-
- Note that the default Apache installation does not come with FastCGI
- configured. Thus, you will be running Spyce via CGI, which is slow. If you
- install FastCGI, you will get a considerable performance boost. Alternatively,
- you could configure the mod_python adapter, or use the proxy server, for a
- similar performance boost. <p>
-
- <b>Note to Redhat users:</b> Spyce should install without a hitch on RedHat
- 8.0 machines and up. Redhat (up until version 8.0) still used Python version
- 1.5, as many standard scripts depended on it. If you want to run Spyce with
- some of the newer Python2 rpms, you will need to change top line of the <font
- face=courier>run_spyceCmd.py</font>, <font
- face=courier>run_spyceCGI.py</font>, <font
- face=courier>run_spyceModpy.py</font> and <font face=courier>verchk.py</font>
- scripts, or reconfigure your path so that the default python is the version
- that you want. <p>
-
- <b>Note to Mandrake users:</b> Users have reported this distribution to have no
- problems with the RPM. <p>
-
- [[toc.n('Automated installation - Windows executable', 'conf_win')]]
-
- On Windows platforms, the easiest method to install Spyce is via the executable
- installer. Python is required on the system prior to installation. It is also
- recommended to install the ActivePython distribution, as Spyce will
- automatically detect and use various Windows-specific features. It is advised
- to install Apache before Spyce, so that the Spyce installer can automatically
- modify the configuration file. You will need to install FastCGI or mod_python
- separately. Otherwise, Spyce will function via CGI. <p>
-
- The Windows installer installs the Spyce engine in a user-specified directory,
- copies and compiles the Spyce documentation, configures Apache, registers an
- uninstaller and also registers the .spy file types with the shell for easy
- compilation. Don't forget to check the Apache configuration file, and
- <b>restart Apache</b>. If you have any problems with the installer please send
- an email. <p>
-
- [[toc.n('Manual installation', 'conf_source')]]
-
- One could also install Spyce manually from the source tarball (using
- FastCGI), as follows:
-
- <ul>
-
- <li>Ensure that you have Apache and FastCGI installed and functioning.</li>
-
- <li><b>Extract</b> the source tarball into some directory.</li>
-
- <li>Execute <font face=courier><b>make</b></font> in this directory to compile the
- python modules and build the documentation.</li>
-
- <li>(Optional) As <b>root user</b>, execute "<font face=courier><b>make
- install</b></font>", to install spyce into <font
- face=courier><b>/usr/share/spyce</b></font>.</li>
-
- <li>Create a link to the command-line executable: <br> "<font
- face=courier><b>ln -sf /usr/share/spyce/run_spyceCmd.py /usr/bin/spyce</b></font>"
- <br> or wherever you have chosen to install it. </li>
-
- <li>Create a link to the documentation in /usr/share/doc: <br> "<font
- face=courier><b>ln -sf /usr/share/spyce/docs/ /usr/share/doc/spyce</b></font>"
- <br> or wherever you have chosen to install it. </li>
-
- <li>Add the following lines to your <font face=courier><b>/etc/httpd/conf/httpd.conf</b></font>
- file, and replace the <font color=red><b>XXX</b></font> with the appropriate
- path.
- <br>
- <font face=courier size=-1>
- [[.compact mode=off]]
- <pre>[[=re.sub('XXX', '<font color=red><b>XXX</b></font>', transform.html_encode(include.dump('../spyceApache.conf')))]]</pre>
- [[.compact]]
- </font></li>
-
- <li>If you are installing on Windows, please also read follow the following
- <a href="[[=genTOClink('conf_windows')]]">instructions</a>. </li>
-
- <li><b>Restart Apache</b>, and you should be set.</li>
-
- </ul>
-
- Test the installation by browsing:
- <font face=courier>
- [[.compact mode=off]]
- <pre>
- <a href="http://localhost/spyce/examples/hello.spy">http://localhost/spyce/examples/hello.spy</a>
- </pre>
- [[.compact]]
- </font>
- <p>
-
- <b>Alternative CGI configuration:</b>
-
- The alternative CGI configuration directs the webserver to execute the Spyce
- file itself, not the Spyce engine. The Spyce file should have execute
- permissions for the web server, and the first line should be:<p>
-
- [[.compact mode=off]]
- <font face=courier>
- <pre>
- #! /usr/bin/python /home/username/spyce/run_spyceCGI.py
- </pre>
- </font>
- [[.compact]]
-
- Then add the following line to the <font face=courier>httpd.conf</font>, or to
- the <font face=courier>.htaccess</font> file in the same directory.
-
- [[.compact mode=off]]
- <font face=courier>
- <pre>
- AddHandler cgi-script spy
- </pre>
- </font>
- [[.compact]]
-
- And ensure that the directory itself has the <b>ExecCGI</b> option enabled.<p>
-
- Please refer to the Apache documentation, specifically
- <a href="http://httpd.apache.org/docs/mod/core.html#options">ExecCGI option</a>,
- <a href="http://httpd.apache.org/docs/mod/core.html#directory">Directory</a>,
- <a href="http://httpd.apache.org/docs/mod/core.html#location">Location</a>,
- <a href="http://httpd.apache.org/docs/mod/core.html#allowoverride">AllowOverride</a> and
- <a href="http://httpd.apache.org/docs/howto/cgi.html">Apache CGI documentation</a>,
- for more information on how to get a standard CGI setup working. <p>
-
- [[toc.e()]]
-
- [[toc.n('Configuration Specifics', 'conf_spec')]]
-
- While the default configuration above should suffice many users, it is often
- the case that you would like to use Spyce under a different adapter or in a
- different way. The following notes should be helpful, but should not be
- considered complete. Many other configurations are possible, and if you come
- across one that you find useful, please let me know. <p>
-
- [[toc.b('Mod_Python', 'conf_modpython')]]
-
- The mod_python webserver integration route is the fastest currently available.
- Try this option if you really need more speed, or if you simply can not get
- FastCGI to work. Before you try to install Spyce, first get mod_python to work!
- You may have to compile from sources, and possibly do the same for Python. (See
- the documentation at: <a href="http://www.modpython.org">mod_python</a>.) You
- might be able to find some useful python rpms <a
- href="http://www.rpmfind.net/linux/rpm2html/search.php?query=python">here</a>
- and mod_python rpms <a
- href="http://www.rpmfind.net/linux/rpm2html/search.php?query=mod_python">here</a>.
- Windows versions of mod_python are available as well. <p>
-
- To use Spyce via mod_python:
-
- <ul>
-
- <li><a href="[[=genTOClink('conf_methods')]]">Install Spyce</a>. </li> <p>
-
- <li>Then, edit the httpd.conf to comment the cgi parts of the inserted Spyce
- configuration lines and uncomment the mod_python parts. </li> <p>
-
- <li>Restart Apache</li> <p>
-
- </ul>
-
-
- [[toc.n('Web Server', 'conf_proxy')]]
-
- Yet another alternative for running Spyce, it to run it as a web-server either
- serving the web as a proxy server or as a primary. The Spyce web server is
- exceptionally feature poor, thus using it as a proxy behind a real web server
- is advised. Moreover, using it as the primary web server in a production is
- highly discouraged. The Spyce web server configuration is defined in a section
- of the <a href="[[=genTOClink('runtime_common')]]">runtime configuration</a>
- file. To run it as a proxy server behind Apache, following these instructions:
-
- <ul>
-
- <li><a href="[[=genTOClink('conf_methods')]]">Install Spyce</a>. </li> <p>
-
- <li>Then, uncomment the lines in the httpd.conf to comment the cgi parts and
- uncomment the proxy parts. </li> <p>
-
- <li>Start the Spyce web server. The command-line syntax for starting the
- server is: <br> <b><font face=courier>spyce -l [-p port] [<root>]</font><b> </li>
-
- <li>Restart Apache</li> <p>
-
- </ul>
-
- [[toc.n('Windows', 'conf_windows')]]
-
- If you are installing Spyce <a
- href="[[=genTOClink('conf_source')]]">manually</a> on Windows, remember to
- add the following line to Apache's httpd.conf: <p>
-
- [[.compact mode=off]]
- <font face=courier>
- <b><pre>
- ScriptInterpreterSource registry
- </pre></b>
- </font>
- [[.compact]]<p>
-
- This assumes that Python has registered itself with the Windows registry to
- run .py files. Otherwise, you can also omit this line, but make sure that the
- first line of the <font face=courier>run_spyceCGI.py</font> file points to a
- valid Python executable, as in:
-
- [[.compact mode=off]]
- <font face=courier>
- <b><pre>
- #! c:/progs/python/python.exe
- </pre></b>
- </font>
- [[.compact]]<p>
-
- If you are running using IIS on Windows, you can take a look at
- <a href="http://starship.python.net/crew/aaron_watters/pws.html">how to
- configure IIS or PWS for Python/CGI scripts</a>. <p>
-
- The basics for getting IIS to work with Spyce are:
-
- <ul>
-
- <li> Start the IIS administration console. You get to it from the <b>Control
- Panel</b>. Click on <b>Administrative Tools</b>, and then <b>Internet
- Services Manager</b>. </li>
-
- <li> Drill down to your <b>Default Web Site</b>. Right click and select
- <b>Properties</b>. </li>
-
- <li> Select the <b>Home Directory</b> tab, and click on the
- <b>Configuration...</b> button near the bottom right. </li>
-
- <li> Add an application mapping. On the <b>executable</b> line you should
- type the equivalent of: <br> <b><font
- face=courier>"c:\program files\python22\python.exe" "c:\program files\spyce\spyceCGI.py"</font></b>.
- <br> Set the <b>extension</b> to <b><font face=courier>.spy</font></b>, or
- whatever you like. <br> Limit the <b>Verbs</b> to <b><font
- face="courier">GET,POST</font></b>. <br> Select the <b>Script engine</b> and
- <b>Check that file exists</b> check-boxes. </li>
-
- <li>Click <b>OK</b> twice. Make sure to propagate these properties to all
- sub-nodes. That is, click <b>Select All</b> and then <b>OK</b> once more.
- </li>
-
- <li> You should now be able to browse .spy files within your website. Note,
- that this is a very slow mechanism, since it utilizes CGI and restarts the
- Spyce engine on each request. </li>
-
- <li> Using the Spyce proxy web server or installing FastCGI are much
- advised for the vast majority of environments. </li>
-
- </ul>
-
- [[toc.e()]]
-
- [[toc.e()]]
-
- [[toc.n('Addenda', 'add')]]
-
- List of appendices:
-
- <ul>
-
- <li><a href="[[=genTOClink('add_perf')]]">Performance</a> - Some throughput
- micro-benchmarks</li><p>
-
- <li><a href="[[=genTOClink('add_history')]]">History</a> - The brief history
- of Spyce</li><p>
-
- <li><a href="[[=genTOClink('add_related')]]">Related work</a> - Links to
- other related projects online</li><p>
-
- </ul>
-
- [[toc.b('Performance', 'add_perf')]]
-
- Although flexibility usually outweighs raw performance in the choice of
- technology, it is nice to know that the technology that you have chosen is not
- a resource hog, and can scale to large production sites. The current Spyce
- implementation is comparable to its cousin technologies: JSP, PHP and ASP. We
- ran a micro-benchmark using <b>hello.spy</b> and equivalents. All benchmark
- files are available in the <font face=courier>misc/benchmark</font>
- directory.<p>
-
- [[includeCode('examples/hello.spy')]]
- <p>
-
- Spyce was measured under CGI, FCGI, mod_python and proxy configurations. For
- calibration the static HTML, CGI-C, CGI-Python and FCGI-Python tests were
- performed. In the case of CGI-C, the request is handled by a compiled C
- program with the appropriate printf statements. In the case of CGI-Python, we
- have an executable Python script with the appropriate print statements.
- FCGI-Python is a similar script that is FCGI enabled. ASP was measured on a
- different machine, only to satisfy curiosity; those results are omitted. <p>
-
- <table border=1 align=center>
- <tr>
- <td bgcolor="#cccccc"><font face="arial, helvetica" size="-1">Configuration</font></td>
- <td bgcolor="#cccccc"><font face="arial, helvetica" size="-1">Hello world!</font></td>
- </tr>
- <tr>
- <td><font face="arial, helvetica" size="-1"><b>Spyce-modpython</b></font></td>
- <td align=right><font face="arial, helvetica" size="-1"><b>250</b></font></td>
- </tr>
- <tr>
- <td><font face="arial, helvetica" size="-1">modpython publisher</font></td>
- <td align=right><font face="arial, helvetica" size="-1">300</font></td>
- </tr>
- <tr>
- <td><font face="arial, helvetica" size="-1"><b>Spyce-proxy</b></font></td>
- <td align=right><font face="arial, helvetica" size="-1"><b>200</b></font></td>
- </tr>
- <tr>
- <td><font face="arial, helvetica" size="-1">JSP</font></td>
- <td align=right><font face="arial, helvetica" size="-1">100</font></td>
- </tr>
- <tr>
- <td><font face="arial, helvetica" size="-1">PHP</font></td>
- <td align=right><font face="arial, helvetica" size="-1">450</font></td>
- </tr>
- <tr>
- <td><font face="arial, helvetica" size="-1"><b>Spyce-FCGI</b></font></td>
- <td align=right><font face="arial, helvetica" size="-1"><b>100</b></font></td>
- </tr>
- <tr>
- <td><font face="arial, helvetica" size="-1">Python-FCGI</font></td>
- <td align=right><font face="arial, helvetica" size="-1">140</font></td>
- </tr>
- <tr>
- <td><font face="arial, helvetica" size="-1"><b>Spyce-CGI</b></font></td>
- <td align=right><font face="arial, helvetica" size="-1"><b>8</b></font></td>
- </tr>
- <tr>
- <td><font face="arial, helvetica" size="-1">Python-CGI</font></td>
- <td align=right><font face="arial, helvetica" size="-1">25</font></td>
- </tr>
- <tr>
- <td><font face="arial, helvetica" size="-1">C-CGI</font></td>
- <td align=right><font face="arial, helvetica" size="-1">180</font></td>
- </tr>
- <tr>
- <td><font face="arial, helvetica" size="-1">Static HTML</font></td>
- <td align=right><font face="arial, helvetica" size="-1">1500</font></td>
- </tr>
- </table>
- <p>
-
- The throughput results (shown above in requests per second) were measured on a
- Intel PIII 700MHz, with 128 MB of RAM and a 512 KB cache
- running RedHat Linux 7.2 (2.4.7-10 kernel), Apache 1.3.22 and
- Python 2.2 using loopback (http://localhost/...) requests. Since each of
- the script languages requires an initial compilation phase (of which JSP seems
- the longest), the server was warmed up with 100 requests before executing
- 1000 measured requests with a concurrency level of 3, using the
- ab (Apache benchmark) tool. Figures are rounded to the nearest
- 25 requests/second. <p>
-
- <b>Conclusion:</b> Both the mod_python and FCGI version can handle large
- websites, as the Spyce engine and cache persist between requests. The CGI
- version takes a hit in recompiling Spyce files on every request. This may be
- alleviated using a disk-based Spyce cache (as opposed to the current
- memory-based implementation). <p>
-
- [[toc.n('History', 'add_history')]]
-
- The initial idea for a Python-based HTML scripting language came in May 1999,
- a few months after I had first learned of Python, while working with JSP on
- some website. The idea was pretty basic and I felt that someone was bound to
- implement it sooner or later, so I waited. But, nobody stepped up to the task,
- and the idea remained little more than a design in my head for two and a half
- years. In early 2002, after having successfully used Python extensively for
- various other tasks and gaining experience with the language, I began to
- revisit my thoughts on a Python-based HTML scripting language, and by late
- May 2002 the beta of version 1.0 was released. <p>
-
- <b>Version 1.0</b> had support for standard features like get and post,
- cookies, session management, etc. Development was still on-going, but Spyce
- was mature and being used on live systems. Support of various features was
- enhanced for about a week or two, and then a new design idea popped into my
- head. <b>Version 1.1</b> was the first modular release of Spyce. Lots of
- prior functionality was shipped out of the core engine and into standard
- modules. Many, many new modules and features were added. Spyce popularity rose
- to the top percentile of SourceForge projects and the user base grew.
- <b>Version 1.2</b> represented a greatly matured release of Spyce. Spyce
- got a totally revamped website and documentation, and development continued.
- <b>Version 1.3</b> introduced active tags. More performance work, more
- modules, etc. Development continues... <p>
-
- For more detail, please refer to the <a
- href="http://cvs.sourceforge.net/cgi-bin/viewcvs.cgi/spyce/spyce/CHANGES?rev=HEAD">change
- log</a>. As always, user feedback is welcome and appreciated. <p>
-
- [[toc.n('Related Work', 'add_related')]]
-
- Links to websites and projects that are related in some way to Spyce (listed
- alphabetically).
-
- <ul>
-
- <li>Albatross -
- <a href="http://www.object-craft.com.au/projects/albatross/">http://www.object-craft.com.au/projects/albatross</a></li>
-
- <li>Apache -
- <a href="http://www.apache.org/">http://www.apache.org</a></li>
-
- <li>ASPy -
- <a href="http://archive.dstc.edu.au/aspy/">http://archive.dstc.edu.au/aspy</a></li>
-
- <li> Castalian -
- <a href="http://www.kryogenix.org/code/castalian/">http://www.kryogenix.org/code/castalian/</a></li>
-
- <li>Cheetah -
- <a href="http://www.cheetahtemplate.org">http://www.cheetahtemplate.org</a>
-
- <li>CherryPy -
- <a href="http://www.cherrypy.org">http://www.cherrypy.org</a>
-
- <li>Empy -
- <a href="http://www.alcyone.com/pyos/empy/">http://www.alcyone.com/pyos/empy/</a></li>
-
- <li>Fenster -
- <a href="http://fenster.sourceforge.net/">http://fenster.sourceforge.net</a></li>
-
- <li>Jaguar -
- <a href="http://jaguar.sourceforge.net/">http://jaguar.sourceforge.net</a></li>
-
- <li>Java Server Pages, JSP -
- <a href="http://java.sun.com/products/jsp/">http://java.sun.com/products/jsp</a></li>
-
- <li>JonPy -
- <a href="http://jonpy.sourceforge.net/">http://jonpy.sourceforge.net</a></li>
-
- <li>Open Directory Project, ODP -
- <a href="http://dmoz.org/Computers/Programming/Languages/Python/Modules/WWW/">http://dmoz.org/.../Python/Modules/WWW/
- </a></li>
-
- <li>PHP -
- <a href="http://www.php.net">http://www.php.net</a></li>
-
- <li>Poor Man's Zope, PMZ -
- <a href="http://pmz.sourceforge.net/">http://pmz.sourceforge.net</a></li>
-
- <li>PSP -
- <a href="http://www.ciobriefings.com/psp/">http://www.ciobriefings.com/psp</a></li>
-
- <li>PyHP -
- <a href="http://www.ccraig.org/software/pyhp">http://www.ccraig.org/software/pyhp</a></li>
-
- <li>PyML -
- <a href="http://www.pyml.org">http://www.pyml.org</a></li>
-
- <li>PyServ -
- <a href="http://pyserv.sourceforge.net/">http://pyserv.sourceforge.net</a></li>
-
- <li>Python -
- <a href="http://www.python.org/">http://www.python.org</a></li>
-
- <li>Python Pages -
- <a href="http://www.embl-heidelberg.de/~chenna/pythonpages/">http://www.embl-heidelberg.de/~chenna/pythonpages</a></li>
-
- <li>Python Web Frameworks -
- <a href="http://www.boddie.org.uk/python/web_frameworks.html">http://www.boddie.org.uk/python/web_frameworks.html</a></li>
-
- <li>PyWX -
- <a href="http://pywx.idyll.org">http://pywx.idyll.org</a></li>
-
- <li>mod_python -
- <a href="http://www.modpython.org/">http://www.modpython.org</a></li>
-
- <li>Quixote -
- <a href="http://www.mems-exchange.org/software/quixote/">http://www.mems-exchange.org/software/quixote/</a></li>
-
- <li>Roadkill -
- <a href="http://roadkill.sourceforge.net/">http://roadkill.sourceforge.net/</a></li>
-
- <li>Sixth Dev -
- <a href="http://www.sixthdev.com/">http://www.sixthdev.com</a></li>
-
- <li>Skunkweb -
- <a href="http://skunkweb.sourceforge.net">http://skunkweb.sourceforge.net</a></li>
-
- <li>Snakelets -
- <a href="http://snakelets.sourceforge.net/">http://snakelets.sourceforge.net</a></li>
-
- <li>Twisted Matrix -
- <a href="http://www.twistedmatrix.com/">http://www.twistedmatrix.com</a></li>
-
- <li>Velocity -
- <a href="http://jakarta.apache.org/velocity/">http://jakarta.apache.org/velocity</a></li>
-
- <li>Wasp -
- <a href="http://www.execulink.com/~robin1/wasp/readme.html">http://www.execulink.com/~robin1/wasp/readme.html</a></li>
-
- <li>Webmacro -
- <a href="http://www.webmacro.org/">http://www.webmacro.org</a></li>
-
- <li>Webware -
- <a href="http://webware.sourceforge.net/">http://webware.sourceforge.net</a></li>
-
- <li>Zope -
- <a href="http://www.zope.org/">http://www.zope.org</a></li>
-
- </ul> <p>
-
- [[toc.e()]]
-
- [[toc.e()]]
-
- [[include.spyce('inc/tail.spi') ]]
-