summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authortavis_rudd <tavis_rudd>2001-06-13 03:50:36 +0000
committertavis_rudd <tavis_rudd>2001-06-13 03:50:36 +0000
commit32567d843c4f6be31453904b60e7863b9e889d25 (patch)
tree21d5d8f60f7b3226d517c6721f355620edb19a12
downloadpython-cheetah-32567d843c4f6be31453904b60e7863b9e889d25.tar.gz
Initial revision
-rw-r--r--CHANGES82
-rw-r--r--MANIFEST.in6
-rw-r--r--TODO54
-rw-r--r--bin/cheetah-compile5
-rw-r--r--docs/src/Makefile35
-rw-r--r--docs/src/TDL.aux42
-rw-r--r--docs/src/TDL.tex348
-rw-r--r--docs/src/TemplateObjects.aux27
-rw-r--r--docs/src/TemplateObjects.tex22
-rw-r--r--docs/src/customizing.aux30
-rw-r--r--docs/src/customizing.tex22
-rw-r--r--docs/src/errorMessages.tex5
-rw-r--r--docs/src/examples.aux25
-rw-r--r--docs/src/examples.tex26
-rw-r--r--docs/src/gettingStarted.aux27
-rw-r--r--docs/src/gettingStarted.tex94
-rw-r--r--docs/src/gfdl.tex367
-rw-r--r--docs/src/introduction.aux46
-rw-r--r--docs/src/introduction.tex213
-rw-r--r--docs/src/macros.aux31
-rw-r--r--docs/src/macros.tex18
-rw-r--r--docs/src/modusers_guide.ind4
-rwxr-xr-xdocs/src/moreverb.sty196
-rw-r--r--docs/src/users_guide.aux9
-rw-r--r--docs/src/users_guide.how110
-rw-r--r--docs/src/users_guide.ind4
-rw-r--r--docs/src/users_guide.l2h606
-rw-r--r--docs/src/users_guide.log93
-rw-r--r--docs/src/users_guide.pdfbin0 -> 44632 bytes
-rw-r--r--docs/src/users_guide.tex38
-rw-r--r--docs/src/users_guide.toc74
-rw-r--r--docs/src/webware.aux34
-rw-r--r--docs/src/webware.tex144
-rw-r--r--examples/webware_examples/cheetahSite/SiteTemplate.tmpl427
-rw-r--r--examples/webware_examples/cheetahSite/banner copy.jpgbin0 -> 23108 bytes
-rw-r--r--examples/webware_examples/cheetahSite/banner2.jpgbin0 -> 18926 bytes
-rw-r--r--examples/webware_examples/cheetahSite/banner3.jpgbin0 -> 16867 bytes
-rw-r--r--examples/webware_examples/cheetahSite/banner_cheetah.jpgbin0 -> 13047 bytes
-rw-r--r--examples/webware_examples/cheetahSite/banner_text.gifbin0 -> 1978 bytes
-rw-r--r--examples/webware_examples/cheetahSite/cheetah-face-black-medium.jpgbin0 -> 8734 bytes
-rw-r--r--examples/webware_examples/cheetahSite/cheetah-face-black.jpgbin0 -> 57091 bytes
-rw-r--r--examples/webware_examples/cheetahSite/cheetah-face-black.psdbin0 -> 232109 bytes
-rw-r--r--examples/webware_examples/cheetahSite/cheetah-face-original.jpgbin0 -> 52832 bytes
-rw-r--r--examples/webware_examples/cheetahSite/cheetah-face.jpgbin0 -> 56940 bytes
-rw-r--r--examples/webware_examples/cheetahSite/cheetah-face_medium.jpgbin0 -> 8832 bytes
-rw-r--r--examples/webware_examples/cheetahSite/contribute.tmpl93
-rw-r--r--examples/webware_examples/cheetahSite/download.tmpl33
-rw-r--r--examples/webware_examples/cheetahSite/index.html248
-rw-r--r--examples/webware_examples/cheetahSite/index.tmpl82
-rw-r--r--examples/webware_examples/cheetahSite/learn.html1668
-rw-r--r--examples/webware_examples/cheetahSite/learn.tmpl43
-rw-r--r--examples/webware_examples/cheetahSite/sourceforge_logo.gifbin0 -> 6429 bytes
-rw-r--r--examples/webware_examples/cheetahSite/spacer.gifbin0 -> 43 bytes
-rw-r--r--examples/webware_examples/hello_world.py8
-rw-r--r--examples/webware_examples/webwareSite/WebwareSiteTemplate.tmpl459
-rw-r--r--examples/webware_examples/webwareSite/communicate.tmpl29
-rw-r--r--examples/webware_examples/webwareSite/contribute.tmpl29
-rw-r--r--examples/webware_examples/webwareSite/download.tmpl29
-rw-r--r--examples/webware_examples/webwareSite/index.tmpl138
-rw-r--r--examples/webware_examples/webwareSite/learn.tmpl33
-rw-r--r--examples/webware_examples/webwareSite/onion_blue_on_white.gifbin0 -> 2318 bytes
-rw-r--r--examples/webware_examples/webwareSite/onion_blue_on_white_large.gifbin0 -> 4301 bytes
-rw-r--r--examples/webware_examples/webwareSite/sourceforge_logo.gifbin0 -> 6429 bytes
-rw-r--r--examples/webware_examples/webwareSite/spacer.gifbin0 -> 43 bytes
-rw-r--r--examples/webware_examples/webwareSite/webWare.gifbin0 -> 1616 bytes
-rw-r--r--examples/webware_examples/webwareSite/webWare_blueBG.gifbin0 -> 2673 bytes
-rw-r--r--examples/webware_examples/webwareSite/webWare_subtitle.gifbin0 -> 1166 bytes
-rw-r--r--examples/webware_examples/webwareSite/webonions.gifbin0 -> 1856 bytes
-rw-r--r--examples/webware_examples/webwareSite/webware_logo.gifbin0 -> 951 bytes
-rw-r--r--examples/webware_examples/webwareSite/webware_slogan_bw.gifbin0 -> 1335 bytes
-rw-r--r--examples/webware_examples/webwareSite/webware_subtitle_black.gifbin0 -> 1369 bytes
-rw-r--r--examples/webware_examples/webwareSite/webware_subtitle_inner_frame.gifbin0 -> 1364 bytes
-rw-r--r--examples/webware_examples/webwareSite/webware_title_bw.gifbin0 -> 1425 bytes
-rw-r--r--examples/webware_examples/webwareSite/webware_title_red-white.gifbin0 -> 1481 bytes
-rw-r--r--examples/webware_examples/webwareSite/webware_title_red-white_fat.gifbin0 -> 1626 bytes
-rwxr-xr-xsetup.py84
-rwxr-xr-xsetup_all.py17
-rw-r--r--src/CodeGenerator.py851
-rw-r--r--src/Compiler.py201
-rw-r--r--src/Components.py39
-rw-r--r--src/Delimeters.py248
-rw-r--r--src/ErrorHandlers.py265
-rw-r--r--src/Macros/HTML.py58
-rw-r--r--src/Macros/__init__.py31
-rw-r--r--src/NameMapper.py295
-rw-r--r--src/Plugins/Debugger.py226
-rw-r--r--src/Plugins/PSP.py113
-rw-r--r--src/Plugins/__init__.py5
-rw-r--r--src/Servlet.py62
-rw-r--r--src/SettingsManager.py76
-rw-r--r--src/Template.py681
-rw-r--r--src/Templates/SkeletonPage.py204
-rw-r--r--src/Templates/__init__.py1
-rw-r--r--src/Tests.py553
-rw-r--r--src/Utilities.py89
-rw-r--r--src/Validators.py59
-rw-r--r--src/Version.py1
-rw-r--r--src/__init__.py56
-rw-r--r--src/unittest_local_copy.py711
99 files changed, 11082 insertions, 0 deletions
diff --git a/CHANGES b/CHANGES
new file mode 100644
index 0000000..1f501bf
--- /dev/null
+++ b/CHANGES
@@ -0,0 +1,82 @@
+Please initial your changes
+================================================================================
+0.9.7 (not released yet)
+ - moved some stuff from __init__.py into CHANGES and TODO
+ - added a new command 'sdist_docs' to setup.py which rebuilds the docs
+ when making a source distribution
+ - changed the name of the ./Cheetah dir to ./src
+
+0.9.6 (June 12, 2001)
+ - fixed a bug in NameMapper that was preventing 'obj.__class__.__name__' from mapping (TR)
+
+0.9.5 (June 10, 2001)
+ - implemented the #cache directive - see the mailing list (TR)
+ - reworked the handling of cached $placeholders and set $var to mean NO_CACHE,
+ $*var to mean STATIC_CACHE, and $*15*var to mean TIMED_REFRESH_CACHE (TR)
+ - renamed Template._getValueForName as Template.mapName (TR)
+
+0.9.4 (June 9, 2001)
+ - created a SettingsManager base class to handle settings for the Template class (TR)
+ - moved the HTML docs icons into the same dir as the HTML (TR)
+
+0.9.3
+ - updated the User's Guide Makefile. Builds HTML, PDF, and PS in the ./docs dir now. (TR)
+ - changed the refs to 'Tavis Rudd' in the docs to 'The Cheetah Development Team' (TR)
+ - added a few bits to the docs (TR)
+ - did some internal renaming so 'nameMapperTags' are referred as 'placeholderTags' (TR)
+ - added the #slurp directive (TR)
+
+0.9.2
+ - got the PSP plugin working again. It still need test cases. (TR)
+
+0.9.1
+ - Changed the name of the package to 'Cheetah' from TemplateServer (TR)
+ - Changed the name of the Server module and its TemplateServer class to 'Template' (TR)
+ - Changed the name of the 'TScompile' script to 'cheetah-compile' (TR)
+ - updated the docs (TR)
+
+0.9.0
+ - changed the names and behaviour of the #parse and #include directives (TR)
+ see the docs for more. (TR)
+ - changed #verbatim to #raw (TR)
+ - fixed a bug in Tests.py that caused an error on the first run. (TR)
+ - more docs (TR + MO)
+ ! all tests pass with Python 2.1 and 2.0 (TR)
+
+0.8.4
+ - changed the #directive end tags to #end if instead of #/if and #end (TR)
+ macro instead of #/macro (TR)
+ - more work on the User's Guide (TR)
+ - fixed a bug in TScompile (TR)
+
+0.8.3
+ - fixed a problem with the regexs that caused $vars and #directives at the (TR)
+ very beginning of the template string not to match in Python 2.0 (TR)
+ - removed some Test cases that made invalid assumptions about the order (TR)
+ of items in dictionaries. (TR)
+
+0.8.2
+ - finished half of the User's Guide (TR)
+ - fixed several small bugs (TR)
+ - added the #comment directive and removed the old <# multiline comment tag #> (TR)
+ - changed the explicit directive closure to /# from ;# (TR)
+
+
+0.7.6
+ - several small bug fixes (TR)
+ - reimplemented the #block directive to avoid maximum recursion depth errors (TR)
+ with large blocks. (TR)
+ - created many new test cases in the regression testing suite (TR)
+ - added an example site to the examples/ directory (TR)
+ - started the User's Guide (TR)
+
+0.7.5
+ - implemented the command-line compiler (TR)
+
+0.7.3-4
+ - implemented the regression testing suite (TR)
+ - fixed a number of small bugs (TR)
+
+0.7.2
+ - implemented the #longMacro directive (TR)
+ \ No newline at end of file
diff --git a/MANIFEST.in b/MANIFEST.in
new file mode 100644
index 0000000..620b975
--- /dev/null
+++ b/MANIFEST.in
@@ -0,0 +1,6 @@
+include *.py TODO CHANGES README examples third_party_packages docs
+recursive-include bin *
+recursive-include docs *
+recursive-include PlateKit *.py
+recursive-include examples *
+recursive-include third_party_packages * \ No newline at end of file
diff --git a/TODO b/TODO
new file mode 100644
index 0000000..11f5173
--- /dev/null
+++ b/TODO
@@ -0,0 +1,54 @@
+Cheetah TODO list
+
+When a task is completed please change its - to an ! and move it to the bottom
+of the list.
+
+Specification
+================================================================================
+- firm up the list of features/syntax that are not subject to change
+- firm up the NamedValueAccess syntax (along with new test cases)
+
+Implementation
+================================================================================
+- design and implement the new parser we've been discussing
+- reimplement the #include directive so it monitors for changes to
+ the included files
+- redesign and implement the Servlet Factory for Webware so the #extend directive
+ will work with .tmpl files as well as .py files
+- make sure that all the #directives that have start and end tags are
+ implemented in such a way that they won't cause 'maximum recursion' limit
+ errors if their content block is long. Simple regexes won't cut it in these
+ cases.
+
+Test Suite
+================================================================================
+- write script that will run the test cases with both Python 2.0 and 2.1
+- independent suite of test cases for NameMapper
+- test cases for the PSP plugin
+- test cases for the SkeletonPage framework
+
+Documentation
+================================================================================
+- add the list of what's stable and what's not to the 'How Mature is Cheetah'
+ section
+- finish all the empty sections
+- flesh out the 'Quick Tutorial' in the 'Getting Started' section
+- add the glossary
+- add an 'Information for Developers' Section
+- add examples to the Examples section
+
+Website
+================================================================================
+- automate the documentation update (including regenerating the learn.html page)
+- add links to the CVS + some instructions
+- See if we can get WebKit working on Sourceforge...
+
+Examples
+================================================================================
+- create some non-html code generation examples
+
+Packaging
+================================================================================
+- Consider moving SettingsManager.py and NameMapper.py into the
+ third_party_packages dir and making setup_all.py the main install script
+! make 'setup.py sdist_docs' generate the update docs by executing the Makefile
diff --git a/bin/cheetah-compile b/bin/cheetah-compile
new file mode 100644
index 0000000..164ce49
--- /dev/null
+++ b/bin/cheetah-compile
@@ -0,0 +1,5 @@
+#! /usr/bin/env python
+
+from Cheetah.Compiler import MainProgram
+
+MainProgram().run() \ No newline at end of file
diff --git a/docs/src/Makefile b/docs/src/Makefile
new file mode 100644
index 0000000..173aef0
--- /dev/null
+++ b/docs/src/Makefile
@@ -0,0 +1,35 @@
+DOCNAME=users_guide
+PYTHONSRC=/home/tavis/tmp/Python-2.1
+MKHOWTO=$(PYTHONSRC)/Doc/tools/mkhowto
+MAIN_TEX_FILE= users_guide.tex
+
+all: pdf ps html text htmlMultiPage
+
+pdf:
+ $(MKHOWTO) --pdf $(MAIN_TEX_FILE)
+ mv $(DOCNAME).pdf ../
+
+ps:
+ $(MKHOWTO) --ps $(MAIN_TEX_FILE)
+ mv $(DOCNAME).ps ../
+html:
+ -rm -rf $(DOCNAME)
+ $(MKHOWTO) --html --split 1 --iconserver . $(MAIN_TEX_FILE)
+ -rm -R ../$(DOCNAME)_html
+ mv $(DOCNAME) ../$(DOCNAME)_html
+
+htmlMultiPage:
+ -rm -rf $(DOCNAME)
+ $(MKHOWTO) --html --iconserver . $(MAIN_TEX_FILE)
+ -rm -R ../$(DOCNAME)_html_multipage
+ mv $(DOCNAME) ../$(DOCNAME)_html_multipage
+
+text:
+ $(MKHOWTO) --text $(MAIN_TEX_FILE)
+ mv $(DOCNAME).txt ../
+
+clean:
+ -rm -rf $(DOCNAME)
+ -rm *.aux *.l2h *~
+ -rm -rf ../html
+
diff --git a/docs/src/TDL.aux b/docs/src/TDL.aux
new file mode 100644
index 0000000..6f921d2
--- /dev/null
+++ b/docs/src/TDL.aux
@@ -0,0 +1,42 @@
+\relax
+\@writefile{toc}{\contentsline {section}{\numberline {4}The Template Definition Language}{8}}
+\@writefile{toc}{\contentsline {subsection}{\numberline {4.1}Placeholder tags}{8}}
+\@writefile{toc}{\contentsline {subsubsection}{Rules for placeholder names}{8}}
+\@writefile{toc}{\contentsline {subsubsection}{The searchList}{9}}
+\@writefile{toc}{\contentsline {subsubsection}{Placeholder values}{9}}
+\@writefile{toc}{\contentsline {subsubsection}{Placeholders with no value defined}{10}}
+\@writefile{toc}{\contentsline {subsubsection}{Dotted notation}{10}}
+\@writefile{toc}{\contentsline {subsubsection}{Caching}{10}}
+\@writefile{toc}{\contentsline {subsection}{\numberline {4.2}Directive tags}{11}}
+\@writefile{toc}{\contentsline {subsubsection}{Escaping directives}{11}}
+\@writefile{toc}{\contentsline {subsubsection}{Tag closures: explicit and implicit}{11}}
+\@writefile{toc}{\contentsline {subsubsection}{Whitespace handling}{11}}
+\@writefile{toc}{\contentsline {subsection}{\numberline {4.3}Comment directives}{11}}
+\@writefile{toc}{\contentsline {subsection}{\numberline {4.4}\#raw directives}{12}}
+\@writefile{toc}{\contentsline {subsection}{\numberline {4.5}\#include directives}{12}}
+\@writefile{toc}{\contentsline {subsection}{\numberline {4.6}\#cache directives}{13}}
+\@writefile{toc}{\contentsline {subsection}{\numberline {4.7}Display logic directives}{13}}
+\@writefile{toc}{\contentsline {subsubsection}{Conditional blocks}{13}}
+\@writefile{toc}{\contentsline {subsubsection}{For loops}{13}}
+\@writefile{toc}{\contentsline {subsection}{\numberline {4.8}\#block directives}{13}}
+\@writefile{toc}{\contentsline {subsubsection}{\#redefine directives}{13}}
+\@setckpt{TDL}{
+\setcounter{page}{14}
+\setcounter{equation}{0}
+\setcounter{enumi}{2}
+\setcounter{enumii}{6}
+\setcounter{enumiii}{0}
+\setcounter{enumiv}{0}
+\setcounter{footnote}{1}
+\setcounter{mpfootnote}{0}
+\setcounter{part}{0}
+\setcounter{section}{4}
+\setcounter{subsection}{8}
+\setcounter{subsubsection}{0}
+\setcounter{paragraph}{0}
+\setcounter{subparagraph}{0}
+\setcounter{figure}{0}
+\setcounter{table}{0}
+\setcounter{LT@tables}{0}
+\setcounter{LT@chunks}{0}
+}
diff --git a/docs/src/TDL.tex b/docs/src/TDL.tex
new file mode 100644
index 0000000..2401d3d
--- /dev/null
+++ b/docs/src/TDL.tex
@@ -0,0 +1,348 @@
+\section{The Template Definition Language}
+
+{\bf Template definitions} are text strings, or files, that have been marked up
+with Cheetah tags for special processing. Cheetah has 2 types of tags:
+
+\begin{enumerate}
+\item {\bf placeholders}: for marking areas of the template that should be
+ replaced with something.
+
+ Placeholders begin with a dollar sign (\code{\$varName}).
+
+\item {\bf directives}: for everything else:
+ \begin{enumerate} % level 2
+ \item {\bf raw text} for marking verbatim blocks should not be parsed for
+ Cheetah tags.
+ \item {\bf comments} that should not appear in the output
+ \item {\bf includes} to include external text. The text can be included
+ verbatim or with parsing for Cheetah tags.
+ \item {\bf display logic} such as {\bf conditional blocks} (if-blocks) and
+ {\bf for loops}
+ \item {\bf blocks}, which are named sections of a template that can be
+ redefined (overridden) in a subclass or by template users
+ \item etc.
+ \end{enumerate} % level 2
+
+ Directives begin with a hash character (\#).
+
+\end{enumerate}
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsection{Placeholder tags}
+Cheetah uses placeholder tags in the form \code{\$varName} to mark out
+areas of the template definition that should be replaced with something. Placeholders are
+equivalent to {\bf fields} on a form. Placeholders can be replaced with plain
+content or variables, objects and function output from Python.
+
+The example below demonstrates the use of placeholders in an HTML document.
+
+\begin{verbatim}
+<HTML>
+<HEAD><TITLE>$title</TITLE></HEAD>
+<BODY>
+$contents
+</BODY>
+</HTML>
+\end{verbatim}
+
+When this template is filled in, the placeholders \code{\$title} and
+\code{\$content} will be replaced with the values of the variables \code{title}
+and \code{content}.
+
+\subsubsection{Rules for placeholder names}
+\begin{itemize}
+
+\item Cheetah ignores all dollar signs (\code{\$}) that are not followed
+ by a letter or an underscore. As a corollary, dollar amounts
+ (\code{\$2.50})are not placeholders but are instead output literally as
+ they should be. Cheetah also ignores any placeholder escaped by a
+ backslash (\code{$\backslash$\$placeholderName}).
+
+\item The first character of a placeholder name must be either an underscore or
+ a letter. Valid characters for the rest of the name are underscores,
+ letters, numbers and periods. These names are valid: \code{\$a},
+ \code{\$_}, \code{\$var}, \code{\$_var}, \code{\$var1}, \code{\$_1var},
+ \code{\$var2_}, \code{\$dict.key}, \code{\$list.item},
+ \code{\$object.method}. These names are not: \code{\$1}, \code{\$var@2},
+ \code{\$\^var}.
+
+\item names are case-sensitive. \code{\$var} does not equal \code{\$Var}
+ or \code{\$vAr} or \code{\$VAR}.
+
+\item Trailing periods are ignored. Cheetah will recognize that
+ placeholder name in \code{\$varName.} is \code{varName} and the period will
+ be left alone in the filled template output.
+
+\item Placeholders can also be written in the form \code{\$\{placeholderName\}}.
+ This is useful for cases where there is no whitespace between the
+ placeholder and surrounding text (\code{surrounding\$\{embeddedVar\}text}).
+\end{itemize}
+
+\subsubsection{The searchList}
+A {\bf namespace} is a Python dictionary that links names to values. Each
+template definition that has been loaded into a \code{Template} objet is
+associated with an ordered list of namespaces in which values for placeholder
+variable names can be stored. This list is called the {\bf searchList}.
+
+The searchList can contain one or more namespaces. In most cases only one
+namespace will be in the searchList unless you explicitly load extra ones. When
+Cheetah fills in \code{\$content} in previous example it searchs
+sequentially through the searchList until it finds a value for \code{\$content}.
+Thus, if three namespaces are loaded and two of them contain a value for
+\code{\$content}, the value for \code{content} from the namespace that is
+closest to the start of the searchList will be returned.
+
+If you add a Python object to the searchList, its attributes and methods will be
+accessible as placeholder names. For example, \code{myObject} contains
+\code{myAttrib} and \code{myMethod}. If \code{myObject} is added to the
+searchList, \code{\$myAttrib} and \code{\$myMethod} can be used as placeholder
+names.
+
+The default namespace in every searchList is the \code{Template} object itself.
+This means that any attributes or methods that are added to classes that inherit
+from \code{Template} can be accessed in templates via \code{\$placeholders}. New
+namespaces can be added to the searchList at any time using the
+\code{Template.addToSearchList()} method. See section \ref{webware} for more
+information on how to use namespaces and the searchList.
+
+\subsubsection{Placeholder values}
+
+Placeholder names can map to Python text strings, numbers, dictionaries, lists
+(arrays), functions, objects, or even nested Cheetah templates. If the value is
+not a String, Cheetah will call \code{str()} on it to obtain a string
+representation that can be inserted into the template. But if the value is a
+callable object (e.g., a function or a method), Cheetah will first call it and
+then call \code{str()} on the result. You may include or omit the () after
+callable placeholders: Cheetah will call it (without arguments) in either case.
+
+
+\begin{verbatim}
+<HTML>
+<HEAD><TITLE>$title</TITLE></HEAD>
+<BODY>
+
+$aString ## these names are chosen to indicate what
+$aNumber ## type of value they map to. They could be
+$aList ## any valid placeholder name.
+$aDictionary
+$aFunction
+$anObject
+
+</BODY>
+</HTML>
+\end{verbatim}
+
+Note that you don't need to include () after \code{\$aFunction}. Cheetah
+will recognize callable variables like functions and methods, call them and
+interpolate their return value in the filled template output.
+
+You cannot pass arguments to a function or method called this way. (Actually,
+you can, but this feature may be removed soon, because it introduces too much
+complexity that is better done in Python outside the template.) The function or
+method must either accept no arguments, or all its arguments must have default
+values. Macro calls (see the Macros section) can accept arguments, but we
+aren't talking about macros here.
+
+Templates can be nested. It is valid to embed a placeholder name that maps to
+another \code{Template} object. The filled output of the nested template will be
+interpolated into the top-level template's output. There is no limit on the
+depth of template nesting.
+
+\subsubsection{Placeholders with no value defined}
+If there is no value defined in the searchList for a placeholder name,
+Cheetah will search for an leading underscore version of the name. For
+example, if it can't find \code{\$varName} it will attempt to find
+\code{\$_varName}. If that fails, Cheetah will include the placeholder
+tag verbatim in the filled template output.
+
+This behaviour can be customized and a default value can be set for names
+that are not found. See section \ref{customizing} for more details.
+
+\subsubsection{Dotted notation}
+Placeholder names can also use {\bf dotted notation} to access entries in
+dictionaries, items in lists, and the attributes and methods of objects.
+Cheetah uses a consistent dotted notation syntax to access the contents
+of all types of containers. This is unlike Python, C++, Java, and other
+languages where dotted notation can only be used to access the attributes and
+methods of objects. \footnote{Cheetah uses a Python module called
+ NameMapper to handle this style of dotted notation. NameMapper is distributed
+ as part of the Cheetah package and can be used as a stand-alone tool.
+ NameMapper was inspired by Chuck Esterbrook's NamedValueAccess module.}
+
+\begin{verbatim}
+$aDictionary.keyName ## must be a valid key of the dictionary
+$aList.3 ## must be a valid index (0-based like in Python)
+$anObject.attributeName ## must be a valid attribute name
+$anObject.method ## must be a valid method name,
+ ## leave off the parentheses on method()
+\end{verbatim}
+
+
+Dotted notation can be used on nested containers of any depth.
+\begin{verbatim}
+$dict1.dict2.dict3.keyName ## nested dictionaries
+$anObject.nestedDict.keyName ## if the object contains a dictionary
+\end{verbatim}
+
+
+\subsubsection{Caching}
+
+By default the value of each \code{\$placeholder} is updated for each request.
+If you want to statically cache the value of the \code{\$placeholder} upon
+startup, add an asterisk after the \$ sign. \code{\$var} becomes \code{\$*var}.
+See the section on the \code{\#cache} directive below for more information.
+
+If you only need to update the value of the \code{\$placeholders} at specific time intervals use
+this form: \code{\$variable} becomes \code{\$*15*variable} , where 15 is the time interval in
+minutes. The time interval can also be specified in fractions of a minute such
+as \code{\$*0.5*variable}.
+
+\begin{verbatim}
+<HTML>
+<HEAD><TITLE>$title</TITLE></HEAD>
+<BODY>
+
+$var ## dynamic - will be reinterpolated for each request
+$*var2 ## static - will be interpolated only once at start-up
+$*5*var3 ## timed refresh - will be updated every 5 minutes.
+
+</BODY>
+</HTML>
+\end{verbatim}
+
+%Note that Cheetah will automatically detect nested \code{Template} objects
+%and these
+%% @@finish this off
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsection{Directive tags}
+Directives tags are used for all functionality that cannot be handled with
+simple placeholders and are enclosed in \code{\#} and \code{/\#}.
+Cheetah does not use HTML/XML style tags because they would be hard to
+distinguish from real HTML tags and would not be visible in rendered HTML when
+something goes wrong.
+
+Some directives consist of a single tag while others have {\bf start} and
+{\bf end} tags that surround a chunk of text. End tags are written in the form
+\code{\#end nameOfTheDirective/\#}.
+
+\subsubsection{Escaping directives}
+Directives can be escaped by placing a backslash ($\backslash$) before them.
+Escaped directives will be printed verbatim.
+
+\subsubsection{Tag closures: explicit and implicit}
+Directive tags can closed explicitly with \code{/\#} or implicitly with the end
+of the line if you're feeling lazy.
+\begin{verbatim}
+#block /#
+Text in the contents area of the
+block directive
+#end block /#
+\end{verbatim}
+or
+\begin{verbatim}
+#block
+Text in the contents area of the
+block directive
+#end block
+\end{verbatim}
+
+\subsubsection{Whitespace handling}
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsection{Comment directives}
+
+Comment directives are used to mark notes, explanations, and decorative text
+that should not appear in the output. There are two forms of the comment
+directive: single-line and multi-line.
+
+All text in a template definition that lies between 2 hash characters
+(\code{\#\#}) and the end of the line is treated as a single-line comment and
+will not show up in the output, unless the 2 hash characters are escaped with a
+backslash.
+\begin{verbatim}
+<HTML>
+<HEAD><TITLE>$title</TITLE></HEAD>
+<BODY>
+##==================================== a decorative comment
+$contents ## an end-of-line comment
+##====================================
+</BODY>
+</HTML>
+\end{verbatim}
+
+Any text between \code{\#*} and \code{*\#} will be treated as a multi-line
+comment.
+\begin{verbatim}
+<HTML>
+<HEAD><TITLE>$title</TITLE></HEAD>
+<BODY>
+#*
+ Here is some multiline
+ comment text
+*#
+##==================================== a decorative comment
+$contents ## an end-of-line comment
+##====================================
+</BODY>
+</HTML>
+\end{verbatim}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsection{\#raw directives}
+Any section of a template definition that is delimeted by \code{\#raw} and
+\code{\#end raw} will be printed verbatim without any parsing of
+\$placeholders or other directives. This can be very useful for debugging or
+writing Cheetah examples and tutorials.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsection{\#include directives}
+
+\code{\#include} directives are used to include text from outside the template
+definition. The text can come from \code{\$placeholder} variables or from
+external files. The example below demonstrates use with \code{\$placeholder}
+variables.
+
+\begin{verbatim}
+#include $myParseText
+\end{verbatim}
+
+This example demonstrates its use with external files.
+\begin{verbatim}
+#include "includeFileName.txt"
+\end{verbatim}
+
+By default, included text will be parsed for Cheetah tags. The keyword
+{\bf raw} can be used to mark the text for verbatim inclusion without any tag
+parsing.
+
+\begin{verbatim}
+#include raw $myParseText
+#include raw "includeFileName.txt"
+\end{verbatim}
+
+\code{Template} uses its .getFileContents(fileName) method to locate the file to
+be included. This method can be overriden in subclasses if you want to modify
+or extend its behaviour. It is possible to implement the logic for getting
+remote files such as \code{http://myserver.com/file.txt}.
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsection{\#cache directives}
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsection{Display logic directives}
+
+\subsubsection{Conditional blocks}
+
+\subsubsection{For loops}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsection{\#block directives}
+
+\subsubsection{\#redefine directives}
+
+
diff --git a/docs/src/TemplateObjects.aux b/docs/src/TemplateObjects.aux
new file mode 100644
index 0000000..d625967
--- /dev/null
+++ b/docs/src/TemplateObjects.aux
@@ -0,0 +1,27 @@
+\relax
+\@writefile{toc}{\contentsline {section}{\numberline {3}Template Objects}{7}}
+\newlabel{TSobjects}{{3}{7}}
+\@writefile{toc}{\contentsline {subsection}{\numberline {3.1}The Template class}{7}}
+\@writefile{toc}{\contentsline {subsection}{\numberline {3.2}Constructing Template objects}{7}}
+\@writefile{toc}{\contentsline {subsection}{\numberline {3.3}Using Template objects}{7}}
+\@writefile{toc}{\contentsline {subsection}{\numberline {3.4}Modifying Template objects}{7}}
+\@setckpt{TemplateObjects}{
+\setcounter{page}{8}
+\setcounter{equation}{0}
+\setcounter{enumi}{4}
+\setcounter{enumii}{0}
+\setcounter{enumiii}{0}
+\setcounter{enumiv}{0}
+\setcounter{footnote}{0}
+\setcounter{mpfootnote}{0}
+\setcounter{part}{0}
+\setcounter{section}{3}
+\setcounter{subsection}{4}
+\setcounter{subsubsection}{0}
+\setcounter{paragraph}{0}
+\setcounter{subparagraph}{0}
+\setcounter{figure}{0}
+\setcounter{table}{0}
+\setcounter{LT@tables}{0}
+\setcounter{LT@chunks}{0}
+}
diff --git a/docs/src/TemplateObjects.tex b/docs/src/TemplateObjects.tex
new file mode 100644
index 0000000..125e04b
--- /dev/null
+++ b/docs/src/TemplateObjects.tex
@@ -0,0 +1,22 @@
+\section{Template Objects}
+\label{TSobjects}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsection{The Template class}
+The Template class is the heart of Cheetah. It parses and compiles
+Template Definitions into python code and serves the filled template output to
+any client that requests it.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsection{Constructing Template objects}
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsection{Using Template objects}
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsection{Modifying Template objects}
+
+
+
diff --git a/docs/src/customizing.aux b/docs/src/customizing.aux
new file mode 100644
index 0000000..7eadb4d
--- /dev/null
+++ b/docs/src/customizing.aux
@@ -0,0 +1,30 @@
+\relax
+\@writefile{toc}{\contentsline {section}{\numberline {7}Customizing and extending Cheetah}{18}}
+\newlabel{customizing}{{7}{18}}
+\@writefile{toc}{\contentsline {subsection}{\numberline {7.1}Custom handling of unknown placeholder names}{18}}
+\@writefile{toc}{\contentsline {subsection}{\numberline {7.2}Plugins}{18}}
+\@writefile{toc}{\contentsline {subsubsection}{The PSP plugin}{18}}
+\@writefile{toc}{\contentsline {subsection}{\numberline {7.3}Custom variable-tags}{18}}
+\@writefile{toc}{\contentsline {subsection}{\numberline {7.4}Custom directives}{18}}
+\@writefile{toc}{\contentsline {subsection}{\numberline {7.5}Custom error handlers}{18}}
+\@writefile{toc}{\contentsline {subsection}{\numberline {7.6}Safe delegation}{18}}
+\@setckpt{customizing}{
+\setcounter{page}{19}
+\setcounter{equation}{0}
+\setcounter{enumi}{2}
+\setcounter{enumii}{6}
+\setcounter{enumiii}{0}
+\setcounter{enumiv}{0}
+\setcounter{footnote}{1}
+\setcounter{mpfootnote}{0}
+\setcounter{part}{0}
+\setcounter{section}{7}
+\setcounter{subsection}{6}
+\setcounter{subsubsection}{0}
+\setcounter{paragraph}{0}
+\setcounter{subparagraph}{0}
+\setcounter{figure}{0}
+\setcounter{table}{0}
+\setcounter{LT@tables}{0}
+\setcounter{LT@chunks}{0}
+}
diff --git a/docs/src/customizing.tex b/docs/src/customizing.tex
new file mode 100644
index 0000000..d1a0298
--- /dev/null
+++ b/docs/src/customizing.tex
@@ -0,0 +1,22 @@
+\section{Customizing and extending Cheetah}
+\label{customizing}
+
+\subsection{Custom handling of unknown placeholder names}
+%% handlers
+%% default vars
+
+\subsection{Plugins}
+\subsubsection{The PSP plugin}
+
+\subsection{Custom variable-tags}
+
+\subsection{Custom directives}
+
+\subsection{Custom error handlers}
+
+\subsection{Safe delegation}
+Safe Delegation, as provided by Zope and Allaire's Spectra, is not a core aim of
+Cheetah. However, several hooks were built into Cheetah so that Safe
+Delegation can be implemented at a later date.
+
+
diff --git a/docs/src/errorMessages.tex b/docs/src/errorMessages.tex
new file mode 100644
index 0000000..b628677
--- /dev/null
+++ b/docs/src/errorMessages.tex
@@ -0,0 +1,5 @@
+\section{Error Messages}
+
+\subsection{Errors during compilation}
+
+\subsection{Run-time errors} \ No newline at end of file
diff --git a/docs/src/examples.aux b/docs/src/examples.aux
new file mode 100644
index 0000000..889674a
--- /dev/null
+++ b/docs/src/examples.aux
@@ -0,0 +1,25 @@
+\relax
+\@writefile{toc}{\contentsline {section}{\numberline {8}Examples}{19}}
+\newlabel{examples}{{8}{19}}
+\@writefile{toc}{\contentsline {subsection}{\numberline {8.1}Syntax examples}{19}}
+\@writefile{toc}{\contentsline {subsection}{\numberline {8.2}Webware Examples}{19}}
+\@setckpt{examples}{
+\setcounter{page}{20}
+\setcounter{equation}{0}
+\setcounter{enumi}{2}
+\setcounter{enumii}{6}
+\setcounter{enumiii}{0}
+\setcounter{enumiv}{0}
+\setcounter{footnote}{1}
+\setcounter{mpfootnote}{0}
+\setcounter{part}{0}
+\setcounter{section}{8}
+\setcounter{subsection}{2}
+\setcounter{subsubsection}{0}
+\setcounter{paragraph}{0}
+\setcounter{subparagraph}{0}
+\setcounter{figure}{0}
+\setcounter{table}{0}
+\setcounter{LT@tables}{0}
+\setcounter{LT@chunks}{0}
+}
diff --git a/docs/src/examples.tex b/docs/src/examples.tex
new file mode 100644
index 0000000..5d313b8
--- /dev/null
+++ b/docs/src/examples.tex
@@ -0,0 +1,26 @@
+\section{Examples}
+\label{examples}
+
+The Cheetah distribution comes with an 'examples' directory. Browse the
+files in this directory and its subdirectories for examples of how
+Cheetah can be used.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsection{Syntax examples}
+Cheetah's \code{Tests} module contains a large number of test cases
+that can double as examples of how the Template Definition Language works.
+To view these cases go to the base directory of your Cheetah distribution
+and open the file Cheetah/Tests.py in a text editor.
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsection{Webware Examples}
+The 'examples' directory has a subdirectory called 'webware_examples'. It
+contains example servlets that use Webware.
+
+A subdirectory titled 'webwareSite' contains a complete website example. This
+site is my proposal for the new Webware website. The site demonstrates the
+advanced Cheetah features such as the \code{\#data} and \code{\#redefine}
+directives. It also demonstrates how the TScompile program can be used to
+generate Webware .py servlet files from .tmpl Template Definition files.
+
diff --git a/docs/src/gettingStarted.aux b/docs/src/gettingStarted.aux
new file mode 100644
index 0000000..cf15261
--- /dev/null
+++ b/docs/src/gettingStarted.aux
@@ -0,0 +1,27 @@
+\relax
+\@writefile{toc}{\contentsline {section}{\numberline {2}Getting Started}{5}}
+\newlabel{gettingStarted}{{2}{5}}
+\@writefile{toc}{\contentsline {subsection}{\numberline {2.1}Requirements}{5}}
+\@writefile{toc}{\contentsline {subsection}{\numberline {2.2}Installation}{5}}
+\@writefile{toc}{\contentsline {subsection}{\numberline {2.3}Testing your installation}{5}}
+\@writefile{toc}{\contentsline {subsection}{\numberline {2.4}Quickstart tutorial}{5}}
+\@setckpt{gettingStarted}{
+\setcounter{page}{7}
+\setcounter{equation}{0}
+\setcounter{enumi}{4}
+\setcounter{enumii}{0}
+\setcounter{enumiii}{0}
+\setcounter{enumiv}{0}
+\setcounter{footnote}{0}
+\setcounter{mpfootnote}{0}
+\setcounter{part}{0}
+\setcounter{section}{2}
+\setcounter{subsection}{4}
+\setcounter{subsubsection}{0}
+\setcounter{paragraph}{0}
+\setcounter{subparagraph}{0}
+\setcounter{figure}{0}
+\setcounter{table}{0}
+\setcounter{LT@tables}{0}
+\setcounter{LT@chunks}{0}
+}
diff --git a/docs/src/gettingStarted.tex b/docs/src/gettingStarted.tex
new file mode 100644
index 0000000..c940a0d
--- /dev/null
+++ b/docs/src/gettingStarted.tex
@@ -0,0 +1,94 @@
+\section{Getting Started}
+\label{gettingStarted}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsection{Requirements}
+Cheetah requires Python release 2.0 or greater and should run on any
+operating system that Python 2.0 runs on.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsection{Installation}
+
+To install Cheetah for a single user:
+\begin{enumerate}
+\item copy the 'src' sub-directory to a directory called 'Cheetah' that is in the
+ user's PYTHON_PATH
+
+\end{enumerate}
+
+To install Cheetah for system-wide use:
+\begin{enumerate}
+\item on POSIX systems (AIX, Solaris, Linux, IRIX, etc.) become the 'root' user
+ and run: python ./setup.py install
+
+\item On non-POSIX systems, such as Windows NT, login as an administrator and
+ type this at the command-line: python setup.py install
+\end{enumerate}
+
+
+On POSIX systems, the system-wide installation will also install the Cheetah's
+command-line compiler program, TScompile, to a system-wide executable path such as
+/usr/local/bin.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsection{Testing your installation}
+You can run the test suite to insure that your installation is correct by
+following these steps:
+\begin{enumerate}
+\item CD into the directory ./Cheetah
+\item type: \code{python Tests.py}
+\end{enumerate}
+
+If any of the tests fail please send a message to the email list with a copy of
+the test output and the following details about your installation:
+
+\begin{enumerate}
+\item your version of Cheetah
+\item your version of Python
+\item your operating system
+\item whether you have changed anything in the Cheetah installation
+\end{enumerate}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsection{Quickstart tutorial}
+This tutorial briefly introduces the basic usage of Cheetah. See the
+following chapters for more detailed explanations.
+
+{\bf This tutorial will be fleshed out further at later date.}
+
+The core of Cheetah is the \code{Template} class in the \code{Cheetah.Template}
+module. The following example shows how to use the \code{Template} class from an
+interactive Python session. Lines prefixed with \code{>>>} and \code{...} are
+user input. The remaining lines are Python output.
+
+\begin{verbatim}
+>>> from Cheetah.Template import Template
+>>> templateDef = """
+... <HTML>
+... <HEAD><TITLE>$title</TITLE></HEAD>
+... <BODY>
+... $contents
+... </BODY>
+... </HTML>"""
+>>> nameSpace = {'title': 'Hello World Example', 'contents': 'Hello World!'}
+>>> templateObj = Template(templateDef, nameSpace)
+>>> print templateObj
+
+<HTML>
+<HEAD><TITLE>Hello World Example</TITLE></HEAD>
+<BODY>
+Hello World!
+</BODY>
+</HTML>
+>>> print templateObj # templateObj can be printed as many times as you need
+
+<HTML>
+<HEAD><TITLE>Hello World Example</TITLE></HEAD>
+<BODY>
+Hello World!
+</BODY>
+</HTML>
+
+\end{verbatim}
+
+
diff --git a/docs/src/gfdl.tex b/docs/src/gfdl.tex
new file mode 100644
index 0000000..a6774c2
--- /dev/null
+++ b/docs/src/gfdl.tex
@@ -0,0 +1,367 @@
+% gfdl.tex
+% This file is a chapter. It must be included in a larger document to work
+% properly.
+
+\section{GNU Free Documentation License}
+
+Version 1.1, March 2000\\
+
+ Copyright $\copyright$ 2000 Free Software Foundation, Inc.\\
+ 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA\\
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+\subsection*{Preamble}
+
+The purpose of this License is to make a manual, textbook, or other
+written document ``free'' in the sense of freedom: to assure everyone
+the effective freedom to copy and redistribute it, with or without
+modifying it, either commercially or noncommercially. Secondarily,
+this License preserves for the author and publisher a way to get
+credit for their work, while not being considered responsible for
+modifications made by others.
+
+This License is a kind of ``copyleft'', which means that derivative
+works of the document must themselves be free in the same sense. It
+complements the GNU General Public License, which is a copyleft
+license designed for free software.
+
+We have designed this License in order to use it for manuals for free
+software, because free software needs free documentation: a free
+program should come with manuals providing the same freedoms that the
+software does. But this License is not limited to software manuals;
+it can be used for any textual work, regardless of subject matter or
+whether it is published as a printed book. We recommend this License
+principally for works whose purpose is instruction or reference.
+
+\subsection{Applicability and Definitions}
+
+This License applies to any manual or other work that contains a
+notice placed by the copyright holder saying it can be distributed
+under the terms of this License. The ``Document'', below, refers to any
+such manual or work. Any member of the public is a licensee, and is
+addressed as ``you''.
+
+A ``Modified Version'' of the Document means any work containing the
+Document or a portion of it, either copied verbatim, or with
+modifications and/or translated into another language.
+
+A ``Secondary Section'' is a named appendix or a front-matter section of
+the Document that deals exclusively with the relationship of the
+publishers or authors of the Document to the Document's overall subject
+(or to related matters) and contains nothing that could fall directly
+within that overall subject. (For example, if the Document is in part a
+textbook of mathematics, a Secondary Section may not explain any
+mathematics.) The relationship could be a matter of historical
+connection with the subject or with related matters, or of legal,
+commercial, philosophical, ethical or political position regarding
+them.
+
+The ``Invariant Sections'' are certain Secondary Sections whose titles
+are designated, as being those of Invariant Sections, in the notice
+that says that the Document is released under this License.
+
+The ``Cover Texts'' are certain short passages of text that are listed,
+as Front-Cover Texts or Back-Cover Texts, in the notice that says that
+the Document is released under this License.
+
+A ``Transparent'' copy of the Document means a machine-readable copy,
+represented in a format whose specification is available to the
+general public, whose contents can be viewed and edited directly and
+straightforwardly with generic text editors or (for images composed of
+pixels) generic paint programs or (for drawings) some widely available
+drawing editor, and that is suitable for input to text formatters or
+for automatic translation to a variety of formats suitable for input
+to text formatters. A copy made in an otherwise Transparent file
+format whose markup has been designed to thwart or discourage
+subsequent modification by readers is not Transparent. A copy that is
+not ``Transparent'' is called ``Opaque''.
+
+Examples of suitable formats for Transparent copies include plain
+ASCII without markup, Texinfo input format, \LaTeX~input format, SGML
+or XML using a publicly available DTD, and standard-conforming simple
+HTML designed for human modification. Opaque formats include
+PostScript, PDF, proprietary formats that can be read and edited only
+by proprietary word processors, SGML or XML for which the DTD and/or
+processing tools are not generally available, and the
+machine-generated HTML produced by some word processors for output
+purposes only.
+
+The ``Title Page'' means, for a printed book, the title page itself,
+plus such following pages as are needed to hold, legibly, the material
+this License requires to appear in the title page. For works in
+formats which do not have any title page as such, ``Title Page'' means
+the text near the most prominent appearance of the work's title,
+preceding the beginning of the body of the text.
+
+
+\subsection{Verbatim Copying}
+
+You may copy and distribute the Document in any medium, either
+commercially or noncommercially, provided that this License, the
+copyright notices, and the license notice saying this License applies
+to the Document are reproduced in all copies, and that you add no other
+conditions whatsoever to those of this License. You may not use
+technical measures to obstruct or control the reading or further
+copying of the copies you make or distribute. However, you may accept
+compensation in exchange for copies. If you distribute a large enough
+number of copies you must also follow the conditions in section 3.
+
+You may also lend copies, under the same conditions stated above, and
+you may publicly display copies.
+
+
+\subsection{Copying in Quantity}
+
+If you publish printed copies of the Document numbering more than 100,
+and the Document's license notice requires Cover Texts, you must enclose
+the copies in covers that carry, clearly and legibly, all these Cover
+Texts: Front-Cover Texts on the front cover, and Back-Cover Texts on
+the back cover. Both covers must also clearly and legibly identify
+you as the publisher of these copies. The front cover must present
+the full title with all words of the title equally prominent and
+visible. You may add other material on the covers in addition.
+Copying with changes limited to the covers, as long as they preserve
+the title of the Document and satisfy these conditions, can be treated
+as verbatim copying in other respects.
+
+If the required texts for either cover are too voluminous to fit
+legibly, you should put the first ones listed (as many as fit
+reasonably) on the actual cover, and continue the rest onto adjacent
+pages.
+
+If you publish or distribute Opaque copies of the Document numbering
+more than 100, you must either include a machine-readable Transparent
+copy along with each Opaque copy, or state in or with each Opaque copy
+a publicly-accessible computer-network location containing a complete
+Transparent copy of the Document, free of added material, which the
+general network-using public has access to download anonymously at no
+charge using public-standard network protocols. If you use the latter
+option, you must take reasonably prudent steps, when you begin
+distribution of Opaque copies in quantity, to ensure that this
+Transparent copy will remain thus accessible at the stated location
+until at least one year after the last time you distribute an Opaque
+copy (directly or through your agents or retailers) of that edition to
+the public.
+
+It is requested, but not required, that you contact the authors of the
+Document well before redistributing any large number of copies, to give
+them a chance to provide you with an updated version of the Document.
+
+
+\subsection{Modifications}
+
+You may copy and distribute a Modified Version of the Document under
+the conditions of sections 2 and 3 above, provided that you release
+the Modified Version under precisely this License, with the Modified
+Version filling the role of the Document, thus licensing distribution
+and modification of the Modified Version to whoever possesses a copy
+of it. In addition, you must do these things in the Modified Version:
+
+\begin{itemize}
+
+\item Use in the Title Page (and on the covers, if any) a title distinct
+ from that of the Document, and from those of previous versions
+ (which should, if there were any, be listed in the History section
+ of the Document). You may use the same title as a previous version
+ if the original publisher of that version gives permission.
+\item List on the Title Page, as authors, one or more persons or entities
+ responsible for authorship of the modifications in the Modified
+ Version, together with at least five of the principal authors of the
+ Document (all of its principal authors, if it has less than five).
+\item State on the Title page the name of the publisher of the
+ Modified Version, as the publisher.
+\item Preserve all the copyright notices of the Document.
+\item Add an appropriate copyright notice for your modifications
+ adjacent to the other copyright notices.
+\item Include, immediately after the copyright notices, a license notice
+ giving the public permission to use the Modified Version under the
+ terms of this License, in the form shown in the Addendum below.
+\item Preserve in that license notice the full lists of Invariant Sections
+ and required Cover Texts given in the Document's license notice.
+\item Include an unaltered copy of this License.
+\item Preserve the section entitled ``History'', and its title, and add to
+ it an item stating at least the title, year, new authors, and
+ publisher of the Modified Version as given on the Title Page. If
+ there is no section entitled ``History'' in the Document, create one
+ stating the title, year, authors, and publisher of the Document as
+ given on its Title Page, then add an item describing the Modified
+ Version as stated in the previous sentence.
+\item Preserve the network location, if any, given in the Document for
+ public access to a Transparent copy of the Document, and likewise
+ the network locations given in the Document for previous versions
+ it was based on. These may be placed in the ``History'' section.
+ You may omit a network location for a work that was published at
+ least four years before the Document itself, or if the original
+ publisher of the version it refers to gives permission.
+\item In any section entitled ``Acknowledgements'' or ``Dedications'',
+ preserve the section's title, and preserve in the section all the
+ substance and tone of each of the contributor acknowledgements
+ and/or dedications given therein.
+\item Preserve all the Invariant Sections of the Document,
+ unaltered in their text and in their titles. Section numbers
+ or the equivalent are not considered part of the section titles.
+\item Delete any section entitled ``Endorsements''. Such a section
+ may not be included in the Modified Version.
+\item Do not retitle any existing section as ``Endorsements''
+ or to conflict in title with any Invariant Section.
+
+\end{itemize}
+
+If the Modified Version includes new front-matter sections or
+appendices that qualify as Secondary Sections and contain no material
+copied from the Document, you may at your option designate some or all
+of these sections as invariant. To do this, add their titles to the
+list of Invariant Sections in the Modified Version's license notice.
+These titles must be distinct from any other section titles.
+
+You may add a section entitled ``Endorsements'', provided it contains
+nothing but endorsements of your Modified Version by various
+parties -- for example, statements of peer review or that the text has
+been approved by an organization as the authoritative definition of a
+standard.
+
+You may add a passage of up to five words as a Front-Cover Text, and a
+passage of up to 25 words as a Back-Cover Text, to the end of the list
+of Cover Texts in the Modified Version. Only one passage of
+Front-Cover Text and one of Back-Cover Text may be added by (or
+through arrangements made by) any one entity. If the Document already
+includes a cover text for the same cover, previously added by you or
+by arrangement made by the same entity you are acting on behalf of,
+you may not add another; but you may replace the old one, on explicit
+permission from the previous publisher that added the old one.
+
+The author(s) and publisher(s) of the Document do not by this License
+give permission to use their names for publicity for or to assert or
+imply endorsement of any Modified Version.
+
+
+\subsection{Combining Documents}
+
+You may combine the Document with other documents released under this
+License, under the terms defined in section 4 above for modified
+versions, provided that you include in the combination all of the
+Invariant Sections of all of the original documents, unmodified, and
+list them all as Invariant Sections of your combined work in its
+license notice.
+
+The combined work need only contain one copy of this License, and
+multiple identical Invariant Sections may be replaced with a single
+copy. If there are multiple Invariant Sections with the same name but
+different contents, make the title of each such section unique by
+adding at the end of it, in parentheses, the name of the original
+author or publisher of that section if known, or else a unique number.
+Make the same adjustment to the section titles in the list of
+Invariant Sections in the license notice of the combined work.
+
+In the combination, you must combine any sections entitled ``History''
+in the various original documents, forming one section entitled
+``History''; likewise combine any sections entitled ``Acknowledgements'',
+and any sections entitled ``Dedications''. You must delete all sections
+entitled ``Endorsements.''
+
+
+\subsection{Collections of Documents}
+
+You may make a collection consisting of the Document and other documents
+released under this License, and replace the individual copies of this
+License in the various documents with a single copy that is included in
+the collection, provided that you follow the rules of this License for
+verbatim copying of each of the documents in all other respects.
+
+You may extract a single document from such a collection, and distribute
+it individually under this License, provided you insert a copy of this
+License into the extracted document, and follow this License in all
+other respects regarding verbatim copying of that document.
+
+
+
+\subsection{Aggregation With Independent Works}
+
+A compilation of the Document or its derivatives with other separate
+and independent documents or works, in or on a volume of a storage or
+distribution medium, does not as a whole count as a Modified Version
+of the Document, provided no compilation copyright is claimed for the
+compilation. Such a compilation is called an ``aggregate'', and this
+License does not apply to the other self-contained works thus compiled
+with the Document, on account of their being thus compiled, if they
+are not themselves derivative works of the Document.
+
+If the Cover Text requirement of section 3 is applicable to these
+copies of the Document, then if the Document is less than one quarter
+of the entire aggregate, the Document's Cover Texts may be placed on
+covers that surround only the Document within the aggregate.
+Otherwise they must appear on covers around the whole aggregate.
+
+
+\subsection{Translation}
+
+Translation is considered a kind of modification, so you may
+distribute translations of the Document under the terms of section 4.
+Replacing Invariant Sections with translations requires special
+permission from their copyright holders, but you may include
+translations of some or all Invariant Sections in addition to the
+original versions of these Invariant Sections. You may include a
+translation of this License provided that you also include the
+original English version of this License. In case of a disagreement
+between the translation and the original English version of this
+License, the original English version will prevail.
+
+
+\subsection{Termination}
+
+You may not copy, modify, sublicense, or distribute the Document except
+as expressly provided for under this License. Any other attempt to
+copy, modify, sublicense or distribute the Document is void, and will
+automatically terminate your rights under this License. However,
+parties who have received copies, or rights, from you under this
+License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+
+\subsection{Future Revisions of This Licence}
+
+The Free Software Foundation may publish new, revised versions
+of the GNU Free Documentation License from time to time. Such new
+versions will be similar in spirit to the present version, but may
+differ in detail to address new problems or concerns. See
+\url{http://www.gnu.org/copyleft/}.
+
+Each version of the License is given a distinguishing version number.
+If the Document specifies that a particular numbered version of this
+License "or any later version" applies to it, you have the option of
+following the terms and conditions either of that specified version or
+of any later version that has been published (not as a draft) by the
+Free Software Foundation. If the Document does not specify a version
+number of this License, you may choose any version ever published (not
+as a draft) by the Free Software Foundation.
+
+\subsection*{ADDENDUM: How to use this License for your documents}
+
+To use this License in a document you have written, include a copy of
+the License in the document and put the following copyright and
+license notices just after the title page:
+
+\begin{quote}
+
+ Copyright $\copyright$ YEAR YOUR NAME.
+ Permission is granted to copy, distribute and/or modify this document
+ under the terms of the GNU Free Documentation License, Version 1.1
+ or any later version published by the Free Software Foundation;
+ with the Invariant Sections being LIST THEIR TITLES, with the
+ Front-Cover Texts being LIST, and with the Back-Cover Texts being LIST.
+ A copy of the license is included in the section entitled ``GNU
+ Free Documentation License''.
+
+\end{quote}
+
+If you have no Invariant Sections, write ``with no Invariant Sections''
+instead of saying which ones are invariant. If you have no
+Front-Cover Texts, write ``no Front-Cover Texts'' instead of
+``Front-Cover Texts being LIST''; likewise for Back-Cover Texts.
+
+If your document contains nontrivial examples of program code, we
+recommend releasing these examples in parallel under your choice of
+free software license, such as the GNU General Public License,
+to permit their use in free software.
+
diff --git a/docs/src/introduction.aux b/docs/src/introduction.aux
new file mode 100644
index 0000000..33f4f81
--- /dev/null
+++ b/docs/src/introduction.aux
@@ -0,0 +1,46 @@
+\relax
+\@writefile{toc}{\contentsline {section}{\numberline {1}Introduction}{2}}
+\newlabel{intro}{{1}{2}}
+\@writefile{toc}{\contentsline {subsection}{\numberline {1.1}What is Cheetah?}{2}}
+\newlabel{intro.whatIs}{{1.1}{2}}
+\@writefile{toc}{\contentsline {subsection}{\numberline {1.2}Why is it called Cheetah?}{3}}
+\newlabel{intro.name}{{1.2}{3}}
+\@writefile{toc}{\contentsline {subsection}{\numberline {1.3}Who developed Cheetah?}{3}}
+\newlabel{intro.developers}{{1.3}{3}}
+\@writefile{toc}{\contentsline {subsection}{\numberline {1.4}How mature is Cheetah?}{3}}
+\newlabel{intro.mature}{{1.4}{3}}
+\@writefile{toc}{\contentsline {subsection}{\numberline {1.5}Where can I get releases?}{3}}
+\newlabel{intro.releases}{{1.5}{3}}
+\@writefile{toc}{\contentsline {subsection}{\numberline {1.6}Where can I get news?}{3}}
+\newlabel{intro.news}{{1.6}{3}}
+\@writefile{toc}{\contentsline {subsection}{\numberline {1.7}How can I contribute?}{3}}
+\newlabel{intro.contribute}{{1.7}{3}}
+\@writefile{toc}{\contentsline {subsubsection}{Bug reports and patches}{4}}
+\@writefile{toc}{\contentsline {subsubsection}{Example sites and tutorials}{4}}
+\@writefile{toc}{\contentsline {subsubsection}{Macro libraries}{4}}
+\@writefile{toc}{\contentsline {subsubsection}{Test cases}{4}}
+\@writefile{toc}{\contentsline {subsubsection}{Publicity}{4}}
+\@writefile{toc}{\contentsline {subsection}{\numberline {1.8}Acknowledgements}{4}}
+\newlabel{intro.acknowledgments}{{1.8}{4}}
+\@writefile{toc}{\contentsline {subsection}{\numberline {1.9}License}{4}}
+\newlabel{intro.license}{{1.9}{4}}
+\@setckpt{introduction}{
+\setcounter{page}{5}
+\setcounter{equation}{0}
+\setcounter{enumi}{6}
+\setcounter{enumii}{0}
+\setcounter{enumiii}{0}
+\setcounter{enumiv}{0}
+\setcounter{footnote}{0}
+\setcounter{mpfootnote}{0}
+\setcounter{part}{0}
+\setcounter{section}{1}
+\setcounter{subsection}{9}
+\setcounter{subsubsection}{0}
+\setcounter{paragraph}{0}
+\setcounter{subparagraph}{0}
+\setcounter{figure}{0}
+\setcounter{table}{0}
+\setcounter{LT@tables}{0}
+\setcounter{LT@chunks}{0}
+}
diff --git a/docs/src/introduction.tex b/docs/src/introduction.tex
new file mode 100644
index 0000000..8652112
--- /dev/null
+++ b/docs/src/introduction.tex
@@ -0,0 +1,213 @@
+\section{Introduction}
+\label{intro}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsection{What is Cheetah?}
+\label{intro.whatIs}
+
+Cheetah is a Python-based template engine and code-generator. It aims:
+
+\begin{itemize}
+\item {\bf to make it easy to separate content, graphic design, and program code.}
+
+ Program code should not pollute HTML and HTML should not pollute program
+ code. Nor should content pollute the structure of complex HTML designs and
+ vice versa.
+
+ There should be no need for a designer to work through a programmer to
+ change a website's design or to use dynamic components that have already
+ been coded. Likewise, content-providers should not have to work through a
+ 'webmaster' to add new content to a site. All members of the team should
+ be able to work independently and in parallel.
+
+ A clean separation makes it easier for a team of content-providers,
+ designers, and programmers to work together without stepping on each
+ other's toes and polluting each other's work. Other advantages include
+ faster development time; HTML and program code that are easier to
+ understand and maintain; content that can be displayed in a variety of
+ non-HTML formats such as PDF; and highly modular, flexible, and reusable
+ site architectures.
+
+\item {\bf to make it easy to integrate content, graphic design, and program code.}
+
+ While it should be easy to develop content, graphic design, and program
+ code separately, it should NOT be difficult to integrate them as part of a
+ website. There should be no difficult hoops to jump through.
+
+ It should be easy:
+ \begin{itemize}
+ \item for programmers to create reusable components and functions that are
+ accessible and understandable to designers.
+ \item for designers to mark out placeholders for content and dynamic components
+ in their templates.
+ \item for designers to soft-code aspects of their design that are either
+ repeated in several places or are subject to change.
+ \item for designers to extend and customize existing templates and thus minimize
+ duplication of effort and code.
+ \item and, of course, for content-providers to use the templates that
+ designers have created.
+ \end{itemize}
+
+
+\item {\bf to provide template designers with a small set of 'Display Logic'
+ programming structures such as conditional blocks and
+ for loops}
+
+ Graphic designers often do tasks that would be easier, faster, and less
+ error prone if they had access to {\bf conditional blocks} and {\bf for
+ loops}. However, a full programming language would be overkill for these
+ simple tasks and most designers don't have the time or desire to learn one.
+
+\item {\bf to be equally well-suited for HTML, SGML, XML, SQL, Postscript, form
+ email, LaTeX, or any other text-based format.}
+
+ Although it was designed with dynamic websites and web applications in mind,
+ Cheetah is not HTML-specific.
+
+\item {\bf to achieve all these aims in a manner that is efficient, flexible, and
+ extendable.}
+
+\end{itemize}
+
+Cheetah achieves these aims by:
+
+\begin{itemize}
+
+\item blending the power and flexibility of Python with the simplicity of a
+ small Template Definition language that non-programmers can understand.
+
+\item giving template designers a simple way of accessing Python variables,
+ objects, and functions in their templates.
+
+\item providing a modular, object-orientated framework that makes it easy to
+ create and maintain large websites.
+
+\item compiling 'Template Definitions' into native Python code at startup.
+ Thereafter this code is executed for each request. This approach is
+ dramatically faster than the string substitution approach used by many
+ templating engines.
+
+\item providing a very simple, yet powerful, caching mechanism that can
+ significantly increase the responsiveness of a dynamic website.
+
+\end{itemize}
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsection{Why is it called Cheetah?}
+\label{intro.name}
+
+Cheetah is fast, flexible, agile and graceful - like its namesake.
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsection{Who developed Cheetah?}
+\label{intro.developers}
+
+Cheetah is one of several templating frameworks that grew out of a 'templates'
+thread on the 'Webware For Python' email list. Tavis Rudd, Mike Orr, Chuck
+Esterbrook, Ian Bicking and Tom Schwaller are the core developers.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsection{How mature is Cheetah?}
+\label{intro.mature}
+
+Cheetah is alpha/beta software as this User's Guide is incomplete and several aspects
+of the design are still subject to change. However, it has been tested
+extensively and has few known issues. We are hoping to release production
+version 1.0 in the summer of 2001.
+
+Here's a summary of known issues and aspects of the design that are in flux.
+\begin{itemize}
+\item The \#include directive is not working with relative path file includes
+ when used with Webware. This should be resolved soon.
+\item The \#include directive might be reworked to monitor for changes in the
+ included file at run-time. It currently does the include once-off at
+ compile-time.
+\item The implementation of \$placeholders(WithArgstrings) needs to be fleshed
+ out to handle nesting.
+\end{itemize}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsection{Where can I get releases?}
+\label{intro.releases}
+
+Cheetah releases can be downloaded from
+\url{http://CheetahTemplate.sourceforge.net}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsection{Where can I get news?}
+\label{intro.news}
+
+News and updates can be obtained from the the Cheetah website:
+\url{http://CheetahTemplate.sourceforge.net}
+
+Cheetah discussions take place on the list
+\email{cheetahtemplate-discuss@lists.sourceforge.net}.
+
+If you encounter difficulties, or are unsure about how to do something,
+please post a detailed message to the list.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsection{How can I contribute?}
+\label{intro.contribute}
+
+Cheetah is the work of many volunteers. If you use Cheetah please share your
+experiences, tricks, customizations, and frustrations.
+
+\subsubsection{Bug reports and patches}
+
+If you think there is a bug in Cheetah, send a message to the email list
+with the following information:
+
+\begin{enumerate}
+\item a description of what you were trying to do and what happened
+\item all tracebacks and error output
+\item your version of Cheetah
+\item your version of Python
+\item your operating system
+\item whether you have changed anything in the Cheetah installation
+\end{enumerate}
+
+\subsubsection{Example sites and tutorials}
+If you're developing a website with Cheetah, please send a link to the
+email list so we can keep track of Cheetah sites. Also, if you discover
+new and interesting ways to use Cheetah please share your experience and
+write a quick tutorial about your technique.
+
+\subsubsection{Macro libraries}
+We hope to build up a framework of macros libraries (see section
+\ref{macros.libraries}) to distribute with Cheetah and would appreciate
+any contributions.
+
+\subsubsection{Test cases}
+Cheetah is packaged with a regression testing suite that is run with each
+new release to ensure that everything is working as expected and that recent
+changes haven't broken anything. The test cases are in the Cheetah.Tests
+module. If you find a reproduceable bug please consider writing a test case
+that will pass only when the bug is fixed. Send any new test cases to the email
+list with the subject-line ``new test case for Cheetah.''
+
+\subsubsection{Publicity}
+Help spread the word ... recommend it to others, write articles about it, etc.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsection{Acknowledgements}
+\label{intro.acknowledgments}
+
+We'd like to thank the following people for contributing valuable advice, code
+and encouragement: Geoff Talvola, Jay Love, Terrel Shumway, Sasa Zivkov, Arkaitz
+Bitorika, Jeremiah Bellomy, Baruch Even, Paul Boddie, Stephan Diehl, and Geir
+Magnusson.
+
+The Velocity, WebMacro and Smarty projects provided inspiration and design
+ideas. Cheetah has benefited from the creativity and energy of their
+developers. Thank you.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsection{License}
+\label{intro.license}
+
+Cheetah is released for unlimited distribution under the terms of the
+Python license.
+
diff --git a/docs/src/macros.aux b/docs/src/macros.aux
new file mode 100644
index 0000000..bd9f6d6
--- /dev/null
+++ b/docs/src/macros.aux
@@ -0,0 +1,31 @@
+\relax
+\@writefile{toc}{\contentsline {section}{\numberline {5}Macros}{14}}
+\newlabel{macros}{{5}{14}}
+\@writefile{toc}{\contentsline {subsection}{\numberline {5.1}What are macros?}{14}}
+\newlabel{macros.whatAre}{{5.1}{14}}
+\@writefile{toc}{\contentsline {subsection}{\numberline {5.2}Using macros}{14}}
+\newlabel{macros.using}{{5.2}{14}}
+\@writefile{toc}{\contentsline {subsection}{\numberline {5.3}Defining macros}{14}}
+\newlabel{macros.defining}{{5.3}{14}}
+\@writefile{toc}{\contentsline {subsection}{\numberline {5.4}Macro libraries}{14}}
+\newlabel{macros.libraries}{{5.4}{14}}
+\@setckpt{macros}{
+\setcounter{page}{15}
+\setcounter{equation}{0}
+\setcounter{enumi}{2}
+\setcounter{enumii}{6}
+\setcounter{enumiii}{0}
+\setcounter{enumiv}{0}
+\setcounter{footnote}{1}
+\setcounter{mpfootnote}{0}
+\setcounter{part}{0}
+\setcounter{section}{5}
+\setcounter{subsection}{4}
+\setcounter{subsubsection}{0}
+\setcounter{paragraph}{0}
+\setcounter{subparagraph}{0}
+\setcounter{figure}{0}
+\setcounter{table}{0}
+\setcounter{LT@tables}{0}
+\setcounter{LT@chunks}{0}
+}
diff --git a/docs/src/macros.tex b/docs/src/macros.tex
new file mode 100644
index 0000000..1c3fa95
--- /dev/null
+++ b/docs/src/macros.tex
@@ -0,0 +1,18 @@
+\section{Macros}
+\label{macros}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsection{What are macros?}
+\label{macros.whatAre}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsection{Using macros}
+\label{macros.using}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsection{Defining macros}
+\label{macros.defining}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsection{Macro libraries}
+\label{macros.libraries} \ No newline at end of file
diff --git a/docs/src/modusers_guide.ind b/docs/src/modusers_guide.ind
new file mode 100644
index 0000000..8af6c8a
--- /dev/null
+++ b/docs/src/modusers_guide.ind
@@ -0,0 +1,4 @@
+\
+\begin{theindex}
+\label{modindex}
+\end{theindex}
diff --git a/docs/src/moreverb.sty b/docs/src/moreverb.sty
new file mode 100755
index 0000000..0f9470a
--- /dev/null
+++ b/docs/src/moreverb.sty
@@ -0,0 +1,196 @@
+%%% moreverb.sty
+%%% AJCD 20 Sep 91
+%%% adds various verbatim environments using Rainer Sch\"opf's new verbatim
+%%% environment.
+
+%%% Marginal hacks (RF) to work `properly' with 2e
+\def\filedate{1994/12/13}
+\def\fileversion{v2.0}
+%
+\NeedsTeXFormat{LaTeX2e}
+\ProvidesPackage{moreverb}
+ [\filedate\space \fileversion\space
+ LaTeX2e package for `more' verbatim enhancements]
+\typeout{Package: `moreverb'
+ \fileversion \space <\filedate> (RF, after AJCD and RmS)}
+%\typeout{English Documentation \@spaces <\docdate>} % oh no there isn't
+
+%%% load verbatim style if not already loaded.
+\@ifundefined{verbatim@processline}{\RequirePackage{verbatim}}{}
+
+%%% verbatimwrite writes all text in its body to a file, the name of which it
+%%% is given as an argument. Written by RmS.
+\newwrite \verbatim@out
+\def\verbatimwrite#1{%
+ \@bsphack
+ \immediate\openout \verbatim@out #1
+ \let\do\@makeother\dospecials
+ \catcode`\^^M\active \catcode`\^^I=12
+ \def\verbatim@processline{%
+ \immediate\write\verbatim@out
+ {\the\verbatim@line}}%
+ \verbatim@start}
+
+\def\endverbatimwrite{%
+ \immediate\closeout\verbatim@out
+ \@esphack}
+
+%%% Auxiliary macros and counters for expanding tabs. Use by listing and
+%%% verbatimtab environments.
+\newcount\tab@position \newcount\tab@size
+\newcount\verbatimtabsize \verbatimtabsize=8
+\def\@xobeytab{\leavevmode\penalty\@M
+ {\loop\ \global\advance\tab@position-1 \ifnum\tab@position>0 \repeat}}
+\begingroup
+ \catcode`\^^I=\active
+ \gdef\@vobeytabs{\catcode`\^^I\active\let^^I\@xobeytab}%
+\endgroup
+\def\verbatim@tabexpand#1{%
+ \ifx#1\@nil \let\next\par \else
+ \ifx#1\@xobeysp \@xobeysp\advance\tab@position-1 \else
+ \ifx#1\@xobeytab \@xobeytab\else
+ #1\advance\tab@position-1
+ \fi\fi
+ \ifnum\tab@position=0 \tab@position\tab@size \fi
+ \let\next\verbatim@tabexpand
+ \fi\next
+}
+
+%%% listing defines a verbatim environment with numbered lines; it takes an
+%%% optional argument specifying the number of lines between numbered
+%%% lines, and a mandatory argument specifying the starting line. listingcont
+%%% continues from the place where listing left off.
+%%% The style in which the label is set can be altered by re-defining
+%%% \listinglabel. * versions are provided.
+\newcount\listing@line \listing@line=1 \newcount\listing@step \listing@step=1
+% Adding an \hbox in front of the line causes a line break, so I go
+% through this rigmarole to get the lines aligned nicely. I probably
+% missed some obvious reason why \hboxes don't work.
+\def\listinglabel#1{\rlap{\small\rm\the#1}\hskip2.5em}
+\def\thelisting@line{%
+ \setbox0\hbox{\listinglabel\listing@line}%
+ \@tempcnta=\listing@line
+ \divide\@tempcnta\listing@step \multiply\@tempcnta\listing@step
+ \ifnum\listing@line=1 \unhbox0
+ \else \ifnum\@tempcnta=\listing@line \unhbox0
+ \else \hskip\wd0
+ \fi\fi}
+\def\listing{\@ifnextchar[{\@listing}{\@listing[1]}}
+\def\@listing[#1]#2{%
+ \global\listing@line=#2\global\listing@step=#1\listingcont}
+\def\listingcont{%
+ \tab@size=\verbatimtabsize
+ \def\verbatim@processline{\tab@position\tab@size
+ \thelisting@line \global\advance\listing@line1
+ \expandafter\verbatim@tabexpand\the\verbatim@line\@nil}%
+ \@verbatim\frenchspacing\@vobeyspaces\@vobeytabs\verbatim@start}
+\let\endlisting=\endtrivlist
+\let\endlistingcont=\endtrivlist
+\@namedef{listing*}{\@ifnextchar[{\@listingstar}{\@listingstar[1]}}
+\def\@listingstar[#1]#2{%
+ \global\listing@line=#2\global\listing@step=#1\relax
+ \csname listingcont*\endcsname}
+\@namedef{listingcont*}{%
+ \def\verbatim@processline{%
+ \thelisting@line \global\advance\listing@line1
+ \the\verbatim@line\par}%
+ \@verbatim\verbatim@start}
+\expandafter\let\csname endlisting*\endcsname =\endtrivlist
+\expandafter\let\csname endlistingcont*\endcsname =\endtrivlist
+
+%%% file input version of listing
+\def\listinginput{%
+ \@ifnextchar[{\@listinginput}{\@listinginput[1]}}
+{\catcode`\~=\active \lccode`\~=`\^^M \lccode`\N=`\N
+ \lowercase{%
+ \gdef\@listinginput[#1]#2#3{\begingroup
+ \global\listing@line=#2\global\listing@step=#1
+ \tab@size=\verbatimtabsize
+ \def\verbatim@processline{\tab@position\tab@size
+ \thelisting@line \global\advance\listing@line1
+ \expandafter\verbatim@tabexpand\the\verbatim@line\@nil}%
+ \@verbatim\frenchspacing\@vobeyspaces\@vobeytabs
+ \def\verbatim@addtoline##1~{%
+ \verbatim@line\expandafter{\the\verbatim@line##1}}%
+ \openin\verbtab@in=#3
+ \ifeof\verbtab@in\typeout{No file #3.}\else
+ \verbtab@oktrue
+ \loop
+ \read\verbtab@in to \verbtab@line
+ \ifeof\verbtab@in\verbtab@okfalse\else
+ \expandafter\verbatim@addtoline\verbtab@line
+ \verbatim@processline
+ \verbatim@startline
+ \fi
+ \ifverbtab@ok\repeat
+ \closein\verbtab@in\fi
+ \endtrivlist\endgroup\@doendpe}}}
+
+%%% verbatimcmd is a verbatim environment with the exception of the escape and
+%%% grouping characters \, {, }.
+\def\verbatimcmd{%
+ \@verbatim \catcode`\\=0 \catcode`\{=1 \catcode`\}=2
+ \frenchspacing\@vobeyspaces\verbatim@start
+}
+\def\endverbatimcmd{%
+ \let\par\relax
+ \def\verbatim@{\endtrivlist\endgroup}%
+ \begingroup}
+
+%%% boxedverbatim produces a verbatim environment in a framed box.
+%%% written by Victor Eijkhout
+\def\boxedverbatim{%
+ % redefine `processline' to produce only a line as wide
+ % as the natural width of the line
+ \def\verbatim@processline{%
+ {\setbox0=\hbox{\the\verbatim@line}%
+ \hsize=\wd0 \the\verbatim@line\par}}%
+ % save the verbatim code in a box
+ \setbox0=\vbox\bgroup \verbatim
+}
+\def\endboxedverbatim{%
+ \endverbatim
+ \egroup % close the box and `fbox' it
+ \fbox{\box0}% <<<=== change here for centering,...
+}
+
+%%% verbatimtab is a verbatim environment which expands tab characters; it
+%%% takes an optional argument specifying the width of tab stops
+\def\verbatimtab{\futurelet\next\@verbatimtab}
+\def\@verbatimtab{\if\next[ \let\next\@@verbatimtab\else
+ \def\next{\@@verbatimtab[\the\verbatimtabsize]}\fi\next}
+\def\@@verbatimtab[#1]{%
+ \do@verbatimtab{#1}{%
+ \@verbatim\frenchspacing\@vobeyspaces\@vobeytabs\verbatim@start}%
+}
+\def\do@verbatimtab#1#2{%
+ \tab@size=#1
+ \def\verbatim@processline{\tab@position\tab@size
+ \expandafter\verbatim@tabexpand\the\verbatim@line\@nil}#2
+}
+\let\endverbatimtab=\endtrivlist
+
+%%% file input version of verbatimtab
+\newread\verbtab@in \newif\ifverbtab@ok
+\def\verbatimtabinput{%
+ \@ifnextchar[{\@verbatimtabinput}{\@verbatimtabinput[\the\verbatimtabsize]}}
+{\catcode`\~=\active \lccode`\~=`\^^M \lccode`\N=`\N
+ \lowercase{%
+ \gdef\@verbatimtabinput[#1]#2{\begingroup
+ \do@verbatimtab{#1}{%
+ \@verbatim\frenchspacing\@vobeyspaces\@vobeytabs}%
+ \def\verbatim@addtoline##1~{%
+ \verbatim@line\expandafter{\the\verbatim@line##1}}%
+ \openin\verbtab@in=#2
+ \ifeof\verbtab@in\typeout{No file #2.}\else
+ \verbtab@oktrue
+ \loop
+ \read\verbtab@in to \verbtab@line
+ \ifeof\verbtab@in\verbtab@okfalse\else
+ \expandafter\verbatim@addtoline\verbtab@line
+ \verbatim@processline
+ \verbatim@startline
+ \fi
+ \ifverbtab@ok\repeat
+ \closein\verbtab@in\fi
+ \endtrivlist\endgroup\@doendpe}}}
diff --git a/docs/src/users_guide.aux b/docs/src/users_guide.aux
new file mode 100644
index 0000000..f3ba329
--- /dev/null
+++ b/docs/src/users_guide.aux
@@ -0,0 +1,9 @@
+\relax
+\@input{introduction.aux}
+\@input{gettingStarted.aux}
+\@input{TemplateObjects.aux}
+\@input{TDL.aux}
+\@input{macros.aux}
+\@input{webware.aux}
+\@input{customizing.aux}
+\@input{examples.aux}
diff --git a/docs/src/users_guide.how b/docs/src/users_guide.how
new file mode 100644
index 0000000..42f5ab2
--- /dev/null
+++ b/docs/src/users_guide.how
@@ -0,0 +1,110 @@
++++ TEXINPUTS=/home/tavis/Cheetah/docs/src:/home/tavis/tmp/Python-2.1/Doc/paper-letter:/home/tavis/tmp/Python-2.1/Doc/texinputs::::~/.TeX:/usr/doc/.TeX:/usr/doc/.TeX::~/.TeX:/usr/doc/.TeX:/usr/doc/.TeX::~/.TeX:/usr/doc/.TeX:/usr/doc/.TeX:
++++ pdflatex users_guide
+This is pdfTeX, Version 3.14159-13d (Web2C 7.3.1)
+(/home/tavis/Cheetah/docs/src/users_guide.tex[/var/lib/texmf/pdftex/config/pdft
+ex.cfg]
+LaTeX2e <1999/12/01> patch level 1
+Babel <v3.6Z> and hyphenation patterns for american, french, german, ngerman, n
+ohyphenation, loaded.
+(/home/tavis/tmp/Python-2.1/Doc/texinputs/howto.cls
+Document Class: howto 1998/02/25 Document class (Python HOWTO)
+(/home/tavis/tmp/Python-2.1/Doc/texinputs/pypaper.sty
+(/usr/share/texmf/tex/latex/psnfss/times.sty)
+Using Times instead of Computer Modern.
+) (/usr/share/texmf/tex/latex/base/article.cls
+Document Class: article 1999/09/10 v1.4a Standard LaTeX document class
+(/usr/share/texmf/tex/latex/base/size10.clo))
+(/usr/share/texmf/tex/latex/fancyhdr/fancyhdr.sty)
+Using fancier footers than usual.
+(/home/tavis/tmp/Python-2.1/Doc/texinputs/python.sty
+(/usr/share/texmf/tex/latex/tools/longtable.sty)
+(/usr/share/texmf/pdftex/plain/misc/pdfcolor.tex)
+(/usr/share/texmf/tex/latex/tools/verbatim.sty)
+(/usr/share/texmf/tex/latex/base/alltt.sty)))
+(/home/tavis/Cheetah/docs/src/moreverb.sty
+Package: `moreverb' v2.0 <1994/12/13> (RF, after AJCD and RmS)
+)
+No file users_guide.aux.
+(/usr/share/texmf/tex/latex/psnfss/ot1ptm.fd)
+Underfull \hbox (badness 10000) in paragraph at lines 11--11
+
+(/usr/share/texmf/tex/latex/psnfss/ot1phv.fd)
+No file users_guide.toc.
+(/usr/share/texmf/tex/latex/psnfss/omsptm.fd)
+Underfull \hbox (badness 10000) has occurred while \output is active
+[]
+[1[/var/lib/texmf/dvips/config/pdftex.map]]
+(/home/tavis/Cheetah/docs/src/introduction.tex [2] [3]
+
+LaTeX Warning: Reference `macros.libraries' on page 4 undefined on input line 1
+80.
+
+) [4] (/home/tavis/Cheetah/docs/src/gettingStarted.tex
+(/usr/share/texmf/tex/latex/psnfss/ot1pcr.fd)) [5] [6]
+(/home/tavis/Cheetah/docs/src/TemplateObjects.tex) [7]
+(/home/tavis/Cheetah/docs/src/TDL.tex [8]
+
+LaTeX Warning: Reference `webware' on page 9 undefined on input line 106.
+
+[9]
+
+LaTeX Warning: Reference `customizing' on page 10 undefined on input line 160.
+
+[10] [11] [12]) [13] (/home/tavis/Cheetah/docs/src/macros.tex) [14]
+(/home/tavis/Cheetah/docs/src/webware.tex [15] [16]) [17]
+(/home/tavis/Cheetah/docs/src/customizing.tex) [18]
+(/home/tavis/Cheetah/docs/src/examples.tex) [19]
+(/home/tavis/Cheetah/docs/src/users_guide.aux
+(/home/tavis/Cheetah/docs/src/introduction.aux)
+(/home/tavis/Cheetah/docs/src/gettingStarted.aux)
+(/home/tavis/Cheetah/docs/src/TemplateObjects.aux)
+(/home/tavis/Cheetah/docs/src/TDL.aux) (/home/tavis/Cheetah/docs/src/macros.aux
+) (/home/tavis/Cheetah/docs/src/webware.aux)
+(/home/tavis/Cheetah/docs/src/customizing.aux)
+(/home/tavis/Cheetah/docs/src/examples.aux))
+
+LaTeX Warning: There were undefined references.
+
+
+LaTeX Warning: Label(s) may have changed. Rerun to get cross-references right.
+
+ )
+(see the transcript file for additional information)<8r.enc><cmsy10.pfb>
+Output written on users_guide.pdf (19 pages, 44632 bytes).
+Transcript written on users_guide.log.
++++ pdflatex users_guide
+This is pdfTeX, Version 3.14159-13d (Web2C 7.3.1)
+(/home/tavis/Cheetah/docs/src/users_guide.tex[/var/lib/texmf/pdftex/config/pdft
+ex.cfg]
+LaTeX2e <1999/12/01> patch level 1
+Babel <v3.6Z> and hyphenation patterns for american, french, german, ngerman, n
+ohyphenation, loaded.
+(/home/tavis/tmp/Python-2.1/Doc/texinputs/howto.cls
+Document Class: howto 1998/02/25 Document class (Python HOWTO)
+(/home/tavis/tmp/Python-2.1/Doc/texinputs/pypaper.sty
+(/usr/share/texmf/tex/latex/psnfss/times.sty)
+Using Times instead of Computer Modern.
+) (/usr/share/texmf/tex/latex/base/article.cls
+Document Class: article 1999/09/10 v1.4a Standard LaTeX document class
+(/usr/share/texmf/tex/latex/base/size10.clo))
+(/usr/share/texmf/tex/latex/fancyhdr/fancyhdr.sty)
+Using fancier footers than usual.
+(/home/tavis/tmp/Python-2.1/Doc/texinputs/python.sty
+(/usr/share/texmf/tex/latex/tools/longtable.sty)
+(/usr/share/texmf/pdftex/plain/misc/pdfcolor.tex)
+(/usr/share/texmf/tex/latex/tools/verbatim.sty)
+(/usr/share/texmf/tex/latex/base/alltt.sty)
+! Interruption.
+\@argdef ...fdefinable #1{\@yargdef #1\@ne {#2}{#3
+ }}
+l.634 }{\end{fulllineitems}}
+
+?
+! Emergency stop.
+\@argdef ...fdefinable #1{\@yargdef #1\@ne {#2}{#3
+ }}
+l.634 }{\end{fulllineitems}}
+
+! ==> Fatal error occurred, the output PDF file not finished!
+Transcript written on users_guide.log.
+*** Session transcript and error messages are in users_guide.how.
diff --git a/docs/src/users_guide.ind b/docs/src/users_guide.ind
new file mode 100644
index 0000000..f68479c
--- /dev/null
+++ b/docs/src/users_guide.ind
@@ -0,0 +1,4 @@
+\
+\begin{theindex}
+\label{genindex}
+\end{theindex}
diff --git a/docs/src/users_guide.l2h b/docs/src/users_guide.l2h
new file mode 100644
index 0000000..b474c36
--- /dev/null
+++ b/docs/src/users_guide.l2h
@@ -0,0 +1,606 @@
+package main;
+push (@INC, '/home/tavis/tmp/Python-2.1/Doc/perl');
+$mydir = '/home/tavis/tmp/Python-2.1/Doc/perl';
+# LaTeX2HTML support base for use with Python documentation.
+
+package main;
+
+use L2hos;
+
+$HTML_VERSION = 4.0;
+
+$MAX_LINK_DEPTH = 2;
+$ADDRESS = '';
+
+$NO_FOOTNODE = 1;
+$NUMBERED_FOOTNOTES = 1;
+
+# Python documentation uses section numbers to support references to match
+# in the printed and online versions.
+#
+$SHOW_SECTION_NUMBERS = 1;
+
+$ICONSERVER = '../icons';
+$IMAGE_TYPE = 'gif';
+
+# Control where the navigation bars should show up:
+$TOP_NAVIGATION = 1;
+$BOTTOM_NAVIGATION = 1;
+$AUTO_NAVIGATION = 0;
+
+$BODYTEXT = '';
+$CHILDLINE = "\n<p><hr>\n";
+$VERBOSITY = 0;
+
+# default # of columns for the indexes
+$INDEX_COLUMNS = 2;
+$MODULE_INDEX_COLUMNS = 4;
+
+
+# A little painful, but lets us clean up the top level directory a little,
+# and not be tied to the current directory (as far as I can tell). Testing
+# an existing definition of $mydir is needed since it cannot be computed when
+# run under mkhowto with recent versions of LaTeX2HTML, since this file is
+# not read directly by LaTeX2HTML any more. mkhowto is required to prepend
+# the required definition at the top of the actual input file.
+#
+if (!defined $mydir) {
+ use Cwd;
+ use File::Basename;
+ ($myname, $mydir, $myext) = fileparse(__FILE__, '\..*');
+ chop $mydir; # remove trailing '/'
+ $mydir = getcwd() . "$dd$mydir"
+ unless $mydir =~ s|^/|/|;
+}
+$LATEX2HTMLSTYLES = "$mydir$envkey$LATEX2HTMLSTYLES";
+push (@INC, $mydir);
+
+($myrootname, $myrootdir, $myext) = fileparse($mydir, '\..*');
+chop $myrootdir;
+
+
+# Hackish way to get the appropriate paper-*/ directory into $TEXINPUTS;
+# pass in the paper size (a4 or letter) as the environment variable PAPER
+# to add the right directory. If not given, the current directory is
+# added instead for use with HOWTO processing.
+#
+if (defined $ENV{'PAPER'}) {
+ $mytexinputs = "$myrootdir${dd}paper-$ENV{'PAPER'}$envkey";
+}
+else {
+ $mytexinputs = getcwd() . $envkey;
+}
+$mytexinputs .= "$myrootdir${dd}texinputs";
+
+
+# Change this variable to change the text added in "About this document...";
+# this should be an absolute pathname to get it right.
+#
+$ABOUT_FILE = "$myrootdir${dd}html${dd}stdabout.dat";
+
+
+sub custom_driver_hook {
+ #
+ # This adds the directory of the main input file to $TEXINPUTS; it
+ # seems to be sufficiently general that it should be fine for HOWTO
+ # processing.
+ #
+ my $file = @_[0];
+ my($jobname, $dir, $ext) = fileparse($file, '\..*');
+ $dir = L2hos->Make_directory_absolute($dir);
+ $dir =~ s/$dd$//;
+ $TEXINPUTS = "$dir$envkey$mytexinputs";
+ print "\nAdding $dir to \$TEXINPUTS\n";
+}
+
+
+$CUSTOM_BUTTONS = '';
+
+sub make_nav_sectref {
+ my($label,$title) = @_;
+ if ($title) {
+ if ($title =~ /\<[aA] /) {
+ $title =~ s/\<[aA] /<a class="sectref" /;
+ }
+ else {
+ $title = "<span class=\"sectref\">$title</span>";
+ }
+ return "<b class=\"navlabel\">$label:</b> $title\n";
+ }
+ return '';
+}
+
+@my_icon_tags = ();
+$my_icon_tags{'next'} = 'Next Page';
+$my_icon_tags{'next_page'} = 'Next Page';
+$my_icon_tags{'previous'} = 'Previous Page';
+$my_icon_tags{'previous_page'} = 'Previous Page';
+$my_icon_tags{'up'} = 'Up One Level';
+$my_icon_tags{'contents'} = 'Contents';
+$my_icon_tags{'index'} = 'Index';
+$my_icon_tags{'modules'} = 'Module Index';
+
+@my_icon_names = ();
+$my_icon_names{'previous_page'} = 'previous';
+$my_icon_names{'next_page'} = 'next';
+
+sub get_my_icon {
+ my $name = @_[0];
+ my $text = $my_icon_tags{$name};
+ if ($my_icon_names{$name}) {
+ $name = $my_icon_names{$name};
+ }
+ if ($text eq '') {
+ $name = 'blank';
+ }
+ my $iconserver = ($ICONSERVER eq '.') ? '' : "$ICONSERVER/";
+ return "<img src=\"$iconserver$name.$IMAGE_TYPE\"\n border=\"0\""
+ . " height=\"32\"\n alt=\"$text\" width=\"32\">";
+}
+
+sub use_my_icon {
+ my $s = @_[0];
+ if ($s =~ /\<tex2html_([a-z_]+)_visible_mark\>/) {
+ my $r = get_my_icon($1);
+ $s =~ s/\<tex2html_[a-z_]+_visible_mark\>/$r/;
+ }
+ return $s;
+}
+
+sub make_nav_panel {
+ my $s;
+ my $BLANK_ICON = get_my_icon('blank');
+ $NEXT = $NEXT_TITLE ? use_my_icon("$NEXT") : $BLANK_ICON;
+ $UP = $UP_TITLE ? use_my_icon("$UP") : $BLANK_ICON;
+ $PREVIOUS = $PREVIOUS_TITLE ? use_my_icon("$PREVIOUS") : $BLANK_ICON;
+ $CONTENTS = use_my_icon("$CONTENTS");
+ $INDEX = $INDEX ? use_my_icon("$INDEX") : $BLANK_ICON;
+ if (!$CUSTOM_BUTTONS) {
+ $CUSTOM_BUTTONS = $BLANK_ICON;
+ }
+ $s = ('<table align="center" width="100%" cellpadding="0" cellspacing="2">'
+ . "\n<tr>"
+ # left-hand side
+ . "\n<td>$PREVIOUS</td>"
+ . "\n<td>$UP</td>"
+ . "\n<td>$NEXT</td>"
+ # title box
+ . "\n<td align=\"center\" width=\"100%\">$t_title</td>"
+ # right-hand side
+ . "\n<td>$CONTENTS</td>"
+ . "\n<td>$CUSTOM_BUTTONS</td>" # module index
+ . "\n<td>$INDEX</td>"
+ . "\n</tr></table>\n"
+ # textual navigation
+ . make_nav_sectref("Previous", $PREVIOUS_TITLE)
+ . make_nav_sectref("Up", $UP_TITLE)
+ . make_nav_sectref("Next", $NEXT_TITLE)
+ );
+ # remove these; they are unnecessary and cause errors from validation
+ $s =~ s/ NAME="tex2html\d+"\n */ /g;
+ return $s;
+}
+
+sub get_version_text {
+ if ($PACKAGE_VERSION ne '' && $t_date) {
+ return ("<span class=\"release-info\">"
+ . "Release $PACKAGE_VERSION,"
+ . " documentation updated on $t_date.</span>");
+ }
+ if ($PACKAGE_VERSION ne '') {
+ return ("<span class=\"release-info\">"
+ . "Release $PACKAGE_VERSION.</span>");
+ }
+ if ($t_date) {
+ return ("<span class=\"release-info\">Documentation released on "
+ . "$t_date.</span>");
+ }
+ return '';
+}
+
+
+sub top_navigation_panel {
+ return "\n"
+ . make_nav_panel()
+ . "<br><hr>\n";
+}
+
+sub bot_navigation_panel {
+ return "\n<p><hr>\n"
+ . make_nav_panel()
+ . "<hr>\n"
+ . get_version_text()
+ . "\n";
+}
+
+sub add_link {
+ # Returns a pair (iconic link, textual link)
+ my($icon, $current_file, @link) = @_;
+ my($dummy, $file, $title) = split($delim,
+ $section_info{join(' ',@link)});
+ if ($icon =~ /\<tex2html_([_a-z]+)_visible_mark\>/) {
+ my $r = get_my_icon($1);
+ $icon =~ s/\<tex2html_[_a-z]+_visible_mark\>/$r/;
+ }
+ if ($title && ($file ne $current_file)) {
+ $title = purify($title);
+ $title = get_first_words($title, $WORDS_IN_NAVIGATION_PANEL_TITLES);
+ return (make_href($file, $icon), make_href($file, "$title"))
+ }
+ elsif ($icon eq get_my_icon('up') && $EXTERNAL_UP_LINK) {
+ return (make_href($EXTERNAL_UP_LINK, $icon),
+ make_href($EXTERNAL_UP_LINK, "$EXTERNAL_UP_TITLE"))
+ }
+ elsif ($icon eq get_my_icon('previous')
+ && $EXTERNAL_PREV_LINK && $EXTERNAL_PREV_TITLE) {
+ return (make_href($EXTERNAL_PREV_LINK, $icon),
+ make_href($EXTERNAL_PREV_LINK, "$EXTERNAL_PREV_TITLE"))
+ }
+ elsif ($icon eq get_my_icon('next')
+ && $EXTERNAL_DOWN_LINK && $EXTERNAL_DOWN_TITLE) {
+ return (make_href($EXTERNAL_DOWN_LINK, $icon),
+ make_href($EXTERNAL_DOWN_LINK, "$EXTERNAL_DOWN_TITLE"))
+ }
+ return (&inactive_img($icon), "");
+}
+
+sub add_special_link {
+ my($icon, $file, $current_file) = @_;
+ if ($icon =~ /\<tex2html_([_a-z]+)_visible_mark\>/) {
+ my $r = get_my_icon($1);
+ $icon =~ s/\<tex2html_[_a-z]+_visible_mark\>/$r/;
+ }
+ return (($file && ($file ne $current_file))
+ ? make_href($file, $icon)
+ : undef)
+}
+
+# The img_tag() function seems only to be called with the parameter
+# 'anchor_invisible_mark', which we want to turn into ''. Since
+# replace_icon_marks() is the only interesting caller, and all it really
+# does is call img_tag(), we can just define the hook alternative to be
+# a no-op instead.
+#
+sub replace_icons_hook {}
+
+sub do_cmd_arabic {
+ # get rid of that nasty <SPAN CLASS="arabic">...</SPAN>
+ my($ctr, $val, $id, $text) = &read_counter_value(@_[0]);
+ return ($val ? farabic($val) : "0") . $text;
+}
+
+
+sub gen_index_id {
+ # this is used to ensure common index key generation and a stable sort
+ my($str,$extra) = @_;
+ sprintf('%s###%s%010d', $str, $extra, ++$global{'max_id'});
+}
+
+sub insert_index {
+ my($mark,$datafile,$columns,$letters,$prefix) = @_;
+ my $prog = "$myrootdir/tools/buildindex.py";
+ my $index;
+ if ($letters) {
+ $index = `$prog --columns $columns --letters $datafile`;
+ }
+ else {
+ $index = `$prog --columns $columns $datafile`;
+ }
+ if (!s/$mark/$prefix$index/) {
+ print "\nCould not locate index mark: $mark";
+ }
+}
+
+sub add_idx {
+ print "\nBuilding HTML for the index ...";
+ close(IDXFILE);
+ insert_index($idx_mark, 'index.dat', $INDEX_COLUMNS, 1, '');
+}
+
+
+$idx_module_mark = '<tex2html_idx_module_mark>';
+$idx_module_title = 'Module Index';
+
+sub add_module_idx {
+ print "\nBuilding HTML for the module index ...";
+ my $key;
+ my $first = 1;
+ my $prevplat = '';
+ my $allthesame = 1;
+ my $prefix = '';
+ foreach $key (keys %Modules) {
+ $key =~ s/<tt>([a-zA-Z0-9._]*)<\/tt>/\1/;
+ my $plat = "$ModulePlatforms{$key}";
+ $plat = ''
+ if ($plat eq $IGNORE_PLATFORM_ANNOTATION);
+ if (!$first) {
+ $allthesame = 0
+ if ($prevplat ne $plat);
+ }
+ else { $first = 0; }
+ $prevplat = $plat;
+ }
+ open(MODIDXFILE, '>modindex.dat') || die "\n$!\n";
+ foreach $key (keys %Modules) {
+ # dump the line in the data file; just use a dummy seqno field
+ my $nkey = $1;
+ my $moditem = "$Modules{$key}";
+ my $plat = '';
+ $key =~ s/<tt>([a-zA-Z0-9._]*)<\/tt>/\1/;
+ if ($ModulePlatforms{$key} && !$allthesame) {
+ $plat = (" <em>(<span class='platform'>$ModulePlatforms{$key}"
+ . '</span>)</em>');
+ }
+ print MODIDXFILE $moditem . $IDXFILE_FIELD_SEP
+ . "<tt class='module'>$key</tt>$plat###\n";
+ }
+ close(MODIDXFILE);
+
+ if ($GLOBAL_MODULE_INDEX) {
+ $prefix = <<MODULE_INDEX_PREFIX;
+
+<p> This index only lists modules documented in this manual.
+ The <em class="citetitle"><a href="$GLOBAL_MODULE_INDEX">Global Module
+ Index</a></em> lists all modules that are documented in this set
+ of manuals.</p>
+MODULE_INDEX_PREFIX
+ }
+ if (!$allthesame) {
+ $prefix .= <<PLAT_DISCUSS;
+
+<p> Some module names are followed by an annotation indicating what
+platform they are available on.</p>
+
+PLAT_DISCUSS
+ }
+ insert_index($idx_module_mark, 'modindex.dat', $MODULE_INDEX_COLUMNS, 0,
+ $prefix);
+}
+
+# replace both indexes as needed:
+sub add_idx_hook {
+ add_idx() if (/$idx_mark/);
+ process_python_state();
+ if ($MODULE_INDEX_FILE) {
+ local ($_);
+ open(MYFILE, "<$MODULE_INDEX_FILE");
+ sysread(MYFILE, $_, 1024*1024);
+ close(MYFILE);
+ add_module_idx();
+ open(MYFILE,">$MODULE_INDEX_FILE");
+ print MYFILE $_;
+ close(MYFILE);
+ }
+}
+
+
+# In addition to the standard stuff, add label to allow named node files and
+# support suppression of the page complete (for HTML Help use).
+sub do_cmd_tableofcontents {
+ local($_) = @_;
+ $TITLE = $toc_title;
+ $tocfile = $CURRENT_FILE;
+ my($closures,$reopens) = preserve_open_tags();
+ anchor_label('contents', $CURRENT_FILE, $_); # this is added
+ join('', "<BR>\n\\tableofchildlinks[off]", $closures
+ , make_section_heading($toc_title, 'H2'), $toc_mark
+ , $reopens, $_);
+}
+# In addition to the standard stuff, add label to allow named node files.
+sub do_cmd_listoffigures {
+ local($_) = @_;
+ $TITLE = $lof_title;
+ $loffile = $CURRENT_FILE;
+ my($closures,$reopens) = preserve_open_tags();
+ anchor_label('lof', $CURRENT_FILE, $_); # this is added
+ join('', "<BR>\n", $closures
+ , make_section_heading($lof_title, 'H2'), $lof_mark
+ , $reopens, $_);
+}
+# In addition to the standard stuff, add label to allow named node files.
+sub do_cmd_listoftables {
+ local($_) = @_;
+ $TITLE = $lot_title;
+ $lotfile = $CURRENT_FILE;
+ my($closures,$reopens) = preserve_open_tags();
+ anchor_label('lot', $CURRENT_FILE, $_); # this is added
+ join('', "<BR>\n", $closures
+ , make_section_heading($lot_title, 'H2'), $lot_mark
+ , $reopens, $_);
+}
+# In addition to the standard stuff, add label to allow named node files.
+sub do_cmd_textohtmlinfopage {
+ local($_) = @_;
+ if ($INFO) { #
+ anchor_label("about",$CURRENT_FILE,$_); # this is added
+ } #
+ my $the_version = ''; # and the rest is
+ if ($t_date) { # mostly ours
+ $the_version = ",\n$t_date";
+ if ($PACKAGE_VERSION) {
+ $the_version .= ", Release $PACKAGE_VERSION";
+ }
+ }
+ $_ = (($INFO == 1)
+ ? join('',
+ $close_all,
+ "<strong>$t_title</strong>$the_version\n",
+ `cat $ABOUT_FILE`,
+ $open_all, $_)
+ : join('', $close_all, $INFO,"\n", $open_all, $_));
+ $_;
+}
+
+# $idx_mark will be replaced with the real index at the end
+sub do_cmd_textohtmlindex {
+ local($_) = @_;
+ $TITLE = $idx_title;
+ $idxfile = $CURRENT_FILE;
+ if (%index_labels) { make_index_labels(); }
+ if (($SHORT_INDEX) && (%index_segment)) { make_preindex(); }
+ else { $preindex = ''; }
+ my $heading = make_section_heading($idx_title, 'h2') . $idx_mark;
+ my($pre,$post) = minimize_open_tags($heading);
+ anchor_label('genindex',$CURRENT_FILE,$_); # this is added
+ return "<br>\n" . $pre . $_;
+}
+
+$MODULE_INDEX_FILE = '';
+
+# $idx_module_mark will be replaced with the real index at the end
+sub do_cmd_textohtmlmoduleindex {
+ local($_) = @_;
+ $TITLE = $idx_module_title;
+ anchor_label('modindex', $CURRENT_FILE, $_);
+ $MODULE_INDEX_FILE = "$CURRENT_FILE";
+ $_ = ('<p>' . make_section_heading($idx_module_title, 'h2')
+ . $idx_module_mark . $_);
+ return $_;
+}
+
+# The bibliography and the index should be treated as separate
+# sections in their own HTML files. The \bibliography{} command acts
+# as a sectioning command that has the desired effect. But when the
+# bibliography is constructed manually using the thebibliography
+# environment, or when using the theindex environment it is not
+# possible to use the normal sectioning mechanism. This subroutine
+# inserts a \bibliography{} or a dummy \textohtmlindex command just
+# before the appropriate environments to force sectioning.
+
+# XXX This *assumes* that if there are two {theindex} environments,
+# the first is the module index and the second is the standard
+# index. This is sufficient for the current Python documentation,
+# but that's about it.
+
+sub add_bbl_and_idx_dummy_commands {
+ my $id = $global{'max_id'};
+
+ s/([\\]begin\s*$O\d+$C\s*thebibliography)/$bbl_cnt++; $1/eg;
+ s/([\\]begin\s*$O\d+$C\s*thebibliography)/$id++; "\\bibliography$O$id$C$O$id$C $1"/geo;
+ my(@parts) = split(/\\begin\s*$O\d+$C\s*theindex/);
+ if (scalar(@parts) == 3) {
+ # Be careful to re-write the string in place, since $_ is *not*
+ # returned explicity; *** nasty side-effect dependency! ***
+ print "\nadd_bbl_and_idx_dummy_commands ==> adding module index";
+ my $rx = "([\\\\]begin\\s*$O\\d+$C\\s*theindex[\\s\\S]*)"
+ . "([\\\\]begin\\s*$O\\d+$C\\s*theindex)";
+ s/$rx/\\textohtmlmoduleindex \1 \\textohtmlindex \2/o;
+ # Add a button to the navigation areas:
+ $CUSTOM_BUTTONS .= ('<a href="modindex.html" title="Module Index">'
+ . get_my_icon('modules')
+ . '</a>');
+ }
+ else {
+ $CUSTOM_BUTTONS .= get_my_icon('blank');
+ $global{'max_id'} = $id; # not sure why....
+ s/([\\]begin\s*$O\d+$C\s*theindex)/\\textohtmlindex $1/o;
+ s/[\\]printindex/\\textohtmlindex /o;
+ }
+ #----------------------------------------------------------------------
+ lib_add_bbl_and_idx_dummy_commands()
+ if defined(&lib_add_bbl_and_idx_dummy_commands);
+}
+
+# The bibliographic references, the appendices, the lists of figures
+# and tables etc. must appear in the contents table at the same level
+# as the outermost sectioning command. This subroutine finds what is
+# the outermost level and sets the above to the same level;
+
+sub set_depth_levels {
+ # Sets $outermost_level
+ my $level;
+ #RRM: do not alter user-set value for $MAX_SPLIT_DEPTH
+ foreach $level ("part", "chapter", "section", "subsection",
+ "subsubsection", "paragraph") {
+ last if (($outermost_level) = /\\($level)$delimiter_rx/);
+ }
+ $level = ($outermost_level ? $section_commands{$outermost_level} :
+ do {$outermost_level = 'section'; 3;});
+
+ #RRM: but calculate value for $MAX_SPLIT_DEPTH when a $REL_DEPTH was given
+ if ($REL_DEPTH && $MAX_SPLIT_DEPTH) {
+ $MAX_SPLIT_DEPTH = $level + $MAX_SPLIT_DEPTH;
+ } elsif (!($MAX_SPLIT_DEPTH)) { $MAX_SPLIT_DEPTH = 1 };
+
+ %unnumbered_section_commands = ('tableofcontents' => $level,
+ 'listoffigures' => $level,
+ 'listoftables' => $level,
+ 'bibliography' => $level,
+ 'textohtmlindex' => $level,
+ 'textohtmlmoduleindex' => $level);
+ $section_headings{'textohtmlmoduleindex'} = 'h1';
+
+ %section_commands = (%unnumbered_section_commands,
+ %section_commands);
+
+ make_sections_rx();
+}
+
+
+# This changes the markup used for {verbatim} environments, and is the
+# best way I've found that ensures the <dl> goes on the outside of the
+# <pre>...</pre>.
+#
+# Note that this *must* be done in the init file, not the python.perl
+# style support file. The %declarations must be set before
+# initialize() is called in the main LaTeX2HTML script (which happens
+# before style files are loaded).
+#
+%declarations = ('preform' => '<dl><dd><pre class="verbatim"></pre></dl>',
+ %declarations);
+
+
+# This is added to get rid of the long comment that follows the
+# doctype declaration; MSIE5 on NT4 SP4 barfs on it and drops the
+# content of the page.
+sub make_head_and_body {
+ my($title, $body) = @_;
+ $body = " $body" unless ($body eq '');
+ my $DTDcomment = '';
+ my($version, $isolanguage) = ($HTML_VERSION, 'EN');
+ my %isolanguages = ( 'english', 'EN' , 'USenglish', 'EN.US'
+ , 'original', 'EN' , 'german' , 'DE'
+ , 'austrian', 'DE.AT', 'french' , 'FR'
+ , 'spanish', 'ES');
+ $isolanguage = $isolanguages{$default_language};
+ $isolanguage = 'EN' unless $isolanguage;
+ $title = &purify($title,1);
+ eval("\$title = ". $default_title ) unless ($title);
+
+ # allow user-modification of the <TITLE> tag; thanks Dan Young
+ if (defined &custom_TITLE_hook) {
+ $title = &custom_TITLE_hook($title, $toc_sec_title);
+ }
+
+ if ($DOCTYPE =~ /\/\/[\w\.]+\s*$/) { # language spec included
+ $DTDcomment = "<!DOCTYPE html PUBLIC \"$DOCTYPE\">\n";
+ } else {
+ $DTDcomment = "<!DOCTYPE html PUBLIC \"$DOCTYPE//"
+ . ($ISO_LANGUAGE ? $ISO_LANGUAGE : $isolanguage) . "\">\n";
+ }
+
+ $STYLESHEET = $FILE.".css" unless $STYLESHEET;
+ if (!$charset && $CHARSET) { $charset = $CHARSET; $charset =~ s/_/\-/go; }
+
+ join('', ($DOCTYPE ? $DTDcomment : '' )
+ ,"<html>\n<head>\n<title>", $title, "</title>\n"
+ , &meta_information($title)
+ , ($CHARSET && $HTML_VERSION ge "2.1" ?
+ "<meta http-equiv=\"Content-Type\" content=\"text/html; charset=$charset\">\n"
+ : "" )
+ , ($BASE ? "<base href=\"$BASE\">\n" : "" )
+ , "<link rel=\"STYLESHEET\" href=\"$STYLESHEET\">"
+ , $more_links_mark
+ , "\n</head>\n<body$body>");
+}
+
+1; # This must be the last line
+
+# auxillary init file for latex2html
+# generated by mkhowto
+$NO_AUTO_LINK = 1;
+$ABOUT_FILE = "/home/tavis/tmp/Python-2.1/Doc/html/about.dat";
+$IMAGE_TYPE = "gif";
+$MAX_LINK_DEPTH = "3";
+$MAX_SPLIT_DEPTH = "6";
+1;
diff --git a/docs/src/users_guide.log b/docs/src/users_guide.log
new file mode 100644
index 0000000..efef21c
--- /dev/null
+++ b/docs/src/users_guide.log
@@ -0,0 +1,93 @@
+This is pdfTeX, Version 3.14159-13d (Web2C 7.3.1) (format=pdflatex 2000.3.11) 12 JUN 2001 20:48
+**users_guide
+(/home/tavis/Cheetah/docs/src/users_guide.tex[/var/lib/texmf/pdftex/config/pdft
+ex.cfg]
+LaTeX2e <1999/12/01> patch level 1
+Babel <v3.6Z> and hyphenation patterns for american, french, german, ngerman, n
+ohyphenation, loaded.
+(/home/tavis/tmp/Python-2.1/Doc/texinputs/howto.cls
+Document Class: howto 1998/02/25 Document class (Python HOWTO)
+(/home/tavis/tmp/Python-2.1/Doc/texinputs/pypaper.sty
+(/usr/share/texmf/tex/latex/psnfss/times.sty
+Package: times 1999/03/29 PSNFSS v.7.2 Times font as default roman : S Rahtz
+)
+Using Times instead of Computer Modern.
+) (/usr/share/texmf/tex/latex/base/article.cls
+Document Class: article 1999/09/10 v1.4a Standard LaTeX document class
+(/usr/share/texmf/tex/latex/base/size10.clo
+File: size10.clo 1999/09/10 v1.4a Standard LaTeX file (size option)
+)
+\c@part=\count79
+\c@section=\count80
+\c@subsection=\count81
+\c@subsubsection=\count82
+\c@paragraph=\count83
+\c@subparagraph=\count84
+\c@figure=\count85
+\c@table=\count86
+\abovecaptionskip=\skip41
+\belowcaptionskip=\skip42
+\bibindent=\dimen102
+)
+(/usr/share/texmf/tex/latex/fancyhdr/fancyhdr.sty
+\headwidth=\dimen103
+)
+Using fancier footers than usual.
+(/home/tavis/tmp/Python-2.1/Doc/texinputs/python.sty
+Package: python 1998/01/11 LaTeX package (Python markup)
+
+(/usr/share/texmf/tex/latex/tools/longtable.sty
+Package: longtable 1998/05/13 v4.09 Multi-page Table package (DPC)
+\LTleft=\skip43
+\LTright=\skip44
+\LTpre=\skip45
+\LTpost=\skip46
+\LTchunksize=\count87
+\LTcapwidth=\dimen104
+\LT@head=\box26
+\LT@firsthead=\box27
+\LT@foot=\box28
+\LT@lastfoot=\box29
+\LT@cols=\count88
+\LT@rows=\count89
+\c@LT@tables=\count90
+\c@LT@chunks=\count91
+\LT@p@ftn=\toks14
+)
+(/usr/share/texmf/pdftex/plain/misc/pdfcolor.tex)
+(/usr/share/texmf/tex/latex/tools/verbatim.sty
+Package: verbatim 2000/01/07 v1.5m LaTeX2e package for verbatim enhancements
+\every@verbatim=\toks15
+\verbatim@line=\toks16
+\verbatim@in@stream=\read1
+)
+\py@codewidth=\skip47
+
+(/usr/share/texmf/tex/latex/base/alltt.sty
+Package: alltt 1997/06/16 v2.0g defines alltt environment
+)
+\py@ModSynopsisFile=\write3
+
+! Interruption.
+\@argdef ...fdefinable #1{\@yargdef #1\@ne {#2}{#3
+ }}
+l.634 }{\end{fulllineitems}}
+
+?
+! Emergency stop.
+\@argdef ...fdefinable #1{\@yargdef #1\@ne {#2}{#3
+ }}
+l.634 }{\end{fulllineitems}}
+
+End of file on the terminal!
+
+
+Here is how much of TeX's memory you used:
+ 732 strings out of 25631
+ 8397 string characters out of 193579
+ 54392 words of memory out of 384000
+ 3753 multiletter control sequences out of 10000+15000
+ 3640 words of font info for 14 fonts, out of 400000 for 1000
+ 14 hyphenation exceptions out of 1000
+ 30i,0n,42p,231b,40s stack positions out of 300i,100n,500p,50000b,4000s
+! ==> Fatal error occurred, the output PDF file not finished!
diff --git a/docs/src/users_guide.pdf b/docs/src/users_guide.pdf
new file mode 100644
index 0000000..a71ab87
--- /dev/null
+++ b/docs/src/users_guide.pdf
Binary files differ
diff --git a/docs/src/users_guide.tex b/docs/src/users_guide.tex
new file mode 100644
index 0000000..8e410c1
--- /dev/null
+++ b/docs/src/users_guide.tex
@@ -0,0 +1,38 @@
+\documentclass{howto}
+\usepackage{moreverb} %% Verbatim Code Listings
+
+\title{Cheetah Users Guide}
+\release{0.9.6}
+
+\author{The Cheetah Development Team}
+\authoraddress{\email{cheetahtemplate-devel@lists.sourceforge.net}}
+
+\begin{document}
+\maketitle
+
+
+\tableofcontents
+
+
+\copyright{Copyright 2001, The Cheetah Development Team.
+ Permission is granted to copy, distribute and/or modify this document
+ under the terms of the GNU Free Documentation License, Version 1.1
+ or any later version published by the Free Software Foundation;
+ with no Invariant Sections, no Front-Cover Texts, and no Back-Cover Texts.}
+
+ %% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+ \include{introduction}
+ \include{gettingStarted}
+ \include{TemplateObjects}
+ \include{TDL}
+ \include{macros}
+ \include{webware}
+ \include{customizing}
+ \include{examples}
+ %\include{errorMessages}
+ %% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+ %\appendix
+ %\input{gfdl.tex}
+
+\end{document}
diff --git a/docs/src/users_guide.toc b/docs/src/users_guide.toc
new file mode 100644
index 0000000..0da3cdb
--- /dev/null
+++ b/docs/src/users_guide.toc
@@ -0,0 +1,74 @@
+\contentsline {section}{\numberline {1}Introduction}{2}
+\contentsline {subsection}{\numberline {1.1}What is Cheetah?}{2}
+\contentsline {subsection}{\numberline {1.2}Why is it called Cheetah?}{3}
+\contentsline {subsection}{\numberline {1.3}Who developed Cheetah?}{3}
+\contentsline {subsection}{\numberline {1.4}How mature is Cheetah?}{3}
+\contentsline {subsection}{\numberline {1.5}Where can I get releases?}{3}
+\contentsline {subsection}{\numberline {1.6}Where can I get news?}{3}
+\contentsline {subsection}{\numberline {1.7}How can I contribute?}{3}
+\contentsline {subsubsection}{Bug reports and patches}{4}
+\contentsline {subsubsection}{Example sites and tutorials}{4}
+\contentsline {subsubsection}{Macro libraries}{4}
+\contentsline {subsubsection}{Test cases}{4}
+\contentsline {subsubsection}{Publicity}{4}
+\contentsline {subsection}{\numberline {1.8}Acknowledgements}{4}
+\contentsline {subsection}{\numberline {1.9}License}{4}
+\contentsline {section}{\numberline {2}Getting Started}{5}
+\contentsline {subsection}{\numberline {2.1}Requirements}{5}
+\contentsline {subsection}{\numberline {2.2}Installation}{5}
+\contentsline {subsection}{\numberline {2.3}Testing your installation}{5}
+\contentsline {subsection}{\numberline {2.4}Quickstart tutorial}{5}
+\contentsline {section}{\numberline {3}Template Objects}{7}
+\contentsline {subsection}{\numberline {3.1}The Template class}{7}
+\contentsline {subsection}{\numberline {3.2}Constructing Template objects}{7}
+\contentsline {subsection}{\numberline {3.3}Using Template objects}{7}
+\contentsline {subsection}{\numberline {3.4}Modifying Template objects}{7}
+\contentsline {section}{\numberline {4}The Template Definition Language}{8}
+\contentsline {subsection}{\numberline {4.1}Placeholder tags}{8}
+\contentsline {subsubsection}{Rules for placeholder names}{8}
+\contentsline {subsubsection}{The searchList}{9}
+\contentsline {subsubsection}{Placeholder values}{9}
+\contentsline {subsubsection}{Placeholders with no value defined}{10}
+\contentsline {subsubsection}{Dotted notation}{10}
+\contentsline {subsubsection}{Caching}{10}
+\contentsline {subsection}{\numberline {4.2}Directive tags}{11}
+\contentsline {subsubsection}{Escaping directives}{11}
+\contentsline {subsubsection}{Tag closures: explicit and implicit}{11}
+\contentsline {subsubsection}{Whitespace handling}{11}
+\contentsline {subsection}{\numberline {4.3}Comment directives}{11}
+\contentsline {subsection}{\numberline {4.4}\#raw directives}{12}
+\contentsline {subsection}{\numberline {4.5}\#include directives}{12}
+\contentsline {subsection}{\numberline {4.6}\#cache directives}{13}
+\contentsline {subsection}{\numberline {4.7}Display logic directives}{13}
+\contentsline {subsubsection}{Conditional blocks}{13}
+\contentsline {subsubsection}{For loops}{13}
+\contentsline {subsection}{\numberline {4.8}\#block directives}{13}
+\contentsline {subsubsection}{\#redefine directives}{13}
+\contentsline {section}{\numberline {5}Macros}{14}
+\contentsline {subsection}{\numberline {5.1}What are macros?}{14}
+\contentsline {subsection}{\numberline {5.2}Using macros}{14}
+\contentsline {subsection}{\numberline {5.3}Defining macros}{14}
+\contentsline {subsection}{\numberline {5.4}Macro libraries}{14}
+\contentsline {section}{\numberline {6}Using Cheetah with Webware}{15}
+\contentsline {subsection}{\numberline {6.1}Background}{15}
+\contentsline {subsection}{\numberline {6.2}Using the Inheritance approach}{15}
+\contentsline {subsubsection}{The \#data directive}{17}
+\contentsline {subsubsection}{The SkeletonPage framework}{17}
+\contentsline {subsubsection}{PlateKit}{17}
+\contentsline {subsection}{\numberline {6.3}Using the Containment approach}{17}
+\contentsline {subsection}{\numberline {6.4}User interaction in either approach}{17}
+\contentsline {subsection}{\numberline {6.5}Components}{17}
+\contentsline {subsubsection}{What are components?}{17}
+\contentsline {subsubsection}{Using components}{17}
+\contentsline {subsubsection}{Building components}{17}
+\contentsline {section}{\numberline {7}Customizing and extending Cheetah}{18}
+\contentsline {subsection}{\numberline {7.1}Custom handling of unknown placeholder names}{18}
+\contentsline {subsection}{\numberline {7.2}Plugins}{18}
+\contentsline {subsubsection}{The PSP plugin}{18}
+\contentsline {subsection}{\numberline {7.3}Custom variable-tags}{18}
+\contentsline {subsection}{\numberline {7.4}Custom directives}{18}
+\contentsline {subsection}{\numberline {7.5}Custom error handlers}{18}
+\contentsline {subsection}{\numberline {7.6}Safe delegation}{18}
+\contentsline {section}{\numberline {8}Examples}{19}
+\contentsline {subsection}{\numberline {8.1}Syntax examples}{19}
+\contentsline {subsection}{\numberline {8.2}Webware Examples}{19}
diff --git a/docs/src/webware.aux b/docs/src/webware.aux
new file mode 100644
index 0000000..c6f2d6e
--- /dev/null
+++ b/docs/src/webware.aux
@@ -0,0 +1,34 @@
+\relax
+\@writefile{toc}{\contentsline {section}{\numberline {6}Using Cheetah with Webware}{15}}
+\newlabel{webware}{{6}{15}}
+\@writefile{toc}{\contentsline {subsection}{\numberline {6.1}Background}{15}}
+\@writefile{toc}{\contentsline {subsection}{\numberline {6.2}Using the Inheritance approach}{15}}
+\@writefile{toc}{\contentsline {subsubsection}{The \#data directive}{17}}
+\@writefile{toc}{\contentsline {subsubsection}{The SkeletonPage framework}{17}}
+\@writefile{toc}{\contentsline {subsubsection}{PlateKit}{17}}
+\@writefile{toc}{\contentsline {subsection}{\numberline {6.3}Using the Containment approach}{17}}
+\@writefile{toc}{\contentsline {subsection}{\numberline {6.4}User interaction in either approach}{17}}
+\@writefile{toc}{\contentsline {subsection}{\numberline {6.5}Components}{17}}
+\@writefile{toc}{\contentsline {subsubsection}{What are components?}{17}}
+\@writefile{toc}{\contentsline {subsubsection}{Using components}{17}}
+\@writefile{toc}{\contentsline {subsubsection}{Building components}{17}}
+\@setckpt{webware}{
+\setcounter{page}{18}
+\setcounter{equation}{0}
+\setcounter{enumi}{2}
+\setcounter{enumii}{6}
+\setcounter{enumiii}{0}
+\setcounter{enumiv}{0}
+\setcounter{footnote}{1}
+\setcounter{mpfootnote}{0}
+\setcounter{part}{0}
+\setcounter{section}{6}
+\setcounter{subsection}{5}
+\setcounter{subsubsection}{0}
+\setcounter{paragraph}{0}
+\setcounter{subparagraph}{0}
+\setcounter{figure}{0}
+\setcounter{table}{0}
+\setcounter{LT@tables}{0}
+\setcounter{LT@chunks}{0}
+}
diff --git a/docs/src/webware.tex b/docs/src/webware.tex
new file mode 100644
index 0000000..074bd0c
--- /dev/null
+++ b/docs/src/webware.tex
@@ -0,0 +1,144 @@
+\section{Using Cheetah with Webware}
+\label{webware}
+
+\subsection{Background}
+Webware is a 'Python-Powered Internet Platform' that uses servlets in a manner
+similar to Java servlets. 'WebKit' is the name of Webware's application server.
+For more details please visit http://webware.sourceforge.net.
+
+As Cheetah's core is flexible there are many ways to use it with Webware
+servlets. There are two broad categories: the Inheritance approach and
+the Containment approach. In the Inheritance approach a servlet is created that
+subclasses both the Template class and Webware's HTTPServlet class. The
+Template instance IS the servlet and its .respond() method is
+automatically called by WebKit for each request. All pre-request processing is
+handled via Cheetah.
+
+In the Containment approach an instance of the Template class is wrapped
+up inside of a Webware servlet class. Instances of the servlet class must
+explicitly call the Template instance's .respond(), or .__str__(), method
+for each request. In this case the servlet class can handle whatever
+per-request processing needs to be done before it calls Cheetah.respond().
+
+The Inheritance approach is the simplest and is best suited for building sites
+from scratch.
+
+The Containment approach is slightly more complex and is best suited for use
+with existing Webware servlets. It is also ideal for cases where you wish to
+use Cheetah for only a portion of the servlet's output, such as a
+discussion-forum table at the bottom of a webpage.
+
+\subsection{Using the Inheritance approach}
+Cheetah.Servlet provides a servlet class that can be subclassed to create
+Webware servlets, as in the trivial example below.
+
+\begin{verbatim}
+## FILE: hello_world.py ##
+template = """
+<HTML>
+<HEAD><TITLE>'Hello World - Test Servlet</TITLE></HEAD>
+<BODY>
+Hello World!
+</BODY>
+</HTML>
+"""
+from Cheetah.Servlet import TemplateServlet
+class hello_world(TemplateServlet):
+ def __init__(self):
+ TemplateServlet.__init__(self, template)
+\end{verbatim}
+
+TemplateServlet's constructor method (\code{TemplateServlet.__init__()}) adds
+the attribute dictionary of the servlet to the searchList that
+\code{\$placeholder} variables can extracted from. Thus, attributes and methods
+of the servlet object can be interpolated into the template like this:
+
+\begin{verbatim}
+## FILE: hello_world.py ##
+template = """
+<HTML>
+<HEAD><TITLE>$title</TITLE></HEAD>
+<BODY>
+$contents
+</BODY>
+</HTML>
+"""
+from Cheetah.Servlet import TemplateServlet
+class hello_world(TemplateServlet):
+ title = 'Hello World - Test Servlet'
+ def __init__(self):
+ TemplateServlet.__init__(self, template)
+
+ def contents(self):
+ return 'Hello World!'
+\end{verbatim}
+
+This process can be simplified for non-programmers. All the Python wrapper code
+in these examples can be generated automatically by \code{cheetah-compile}, a small program
+that is installed with Cheetah. \code{cheetah-compile} parses Template Definitions
+written in files with the .tmpl extension and generates a Webware servlet file
+with the .py extension for each .tmpl file. Type \code{cheetah-compile} after
+installing Cheetah to get information on how to use it.
+
+Here's the first example as a .tmpl file:
+
+\begin{verbatim}
+## FILE: hello_world.tmpl ##
+<HTML>
+<HEAD><TITLE>Hello World - Test Servlet</TITLE></HEAD>
+<BODY>
+Hello World!
+</BODY>
+</HTML>
+\end{verbatim}
+
+Here's the second example as a .tmpl file. Note that all lines that begin with
+\code{\#\#} are comment lines.
+
+\begin{verbatim}
+## FILE: hello_world.tmpl ##
+##===================================
+#data
+title = 'Hello World - Test Servlet'
+def contents():
+ return 'Hello World!'
+#/data
+##===================================
+<HTML>
+<HEAD><TITLE>$title</TITLE></HEAD>
+<BODY>
+$contents
+</BODY>
+</HTML>
+\end{verbatim}
+
+\subsubsection{The \#data directive}
+The previous example introduced the \code{\#data} directive, a shortcut means
+of adding data into the servlet's attribute dictionary and thus into
+the Template's {\bf searchList}. The \code{\#data} directive can be used in
+any Template Definition, regardless of whether the definition is in a .py or
+.tmpl file.
+
+\code{\#data} directives can contain any valid Python code
+
+\subsubsection{The SkeletonPage framework}
+
+\subsubsection{PlateKit}
+Forget what you knew about PlateKit. It's undergoing some changes and is out of
+action for the time-being.
+
+\subsection{Using the Containment approach}
+
+
+\subsection{User interaction in either approach}
+
+
+\subsection{Components}
+
+\subsubsection{What are components?}
+
+\subsubsection{Using components}
+
+\subsubsection{Building components}
+
+
diff --git a/examples/webware_examples/cheetahSite/SiteTemplate.tmpl b/examples/webware_examples/cheetahSite/SiteTemplate.tmpl
new file mode 100644
index 0000000..69fd500
--- /dev/null
+++ b/examples/webware_examples/cheetahSite/SiteTemplate.tmpl
@@ -0,0 +1,427 @@
+#extend Cheetah.Templates.SkeletonPage
+
+##============================================================================##
+## macros for the site
+
+#macro Python()
+<A HREF="http://www.python.org">Python</A>
+#end macro
+
+#macro insetBox(width=170, boxTitle='', boxContents='')
+<TABLE BORDER=0 CELLPADDING=0 CELLSPACING=0 WIDTH=$width>
+ <TR>
+ <TD COLSPAN=3 HEIGHT=1 BGCOLOR="$*insetBoxes.titleFrameColor">#spacer()</TD>
+ </TR>
+ <TR>
+ <TD WIDTH=1 BGCOLOR="$*insetBoxes.titleFrameColor">#spacer()</TD>
+ <TD WIDTH=900 HEIGHT=1 BGCOLOR="$mainTbl.outerFrameColor">#spacer()</TD> ## force full width
+ <TD WIDTH=1 BGCOLOR="$*insetBoxes.titleFrameColor">#spacer()</TD>
+ </TR>
+ <TR>
+ <TD WIDTH=1 BGCOLOR="$*insetBoxes.titleFrameColor">#spacer()</TD>
+ <TD WIDTH=900 HEIGHT=1 BGCOLOR="$mainTbl.innerFrameColor"> ## force full width
+ <DIV CLASS="insetBoxTitleContainer"><DIV CLASS="insetBoxTitle">$boxTitle</DIV></DIV>
+ </TD>
+ <TD WIDTH=1 BGCOLOR="$*insetBoxes.titleFrameColor">#spacer()</TD>
+ </TR>
+ <TR>
+ <TD WIDTH=1 BGCOLOR="$*insetBoxes.titleFrameColor">#spacer()</TD>
+ <TD WIDTH=900 HEIGHT=1 BGCOLOR="$mainTbl.outerFrameColor">#spacer()</TD> ## force full width
+ <TD WIDTH=1 BGCOLOR="$*insetBoxes.titleFrameColor">#spacer()</TD>
+ </TR>
+ <TR>
+ <TD COLSPAN=3 HEIGHT=1 BGCOLOR="$*insetBoxes.titleFrameColor">#spacer()</TD>
+ </TR>
+</TABLE>
+
+<TABLE VALIGN="top" WIDTH=$*width BORDER=0 CELLPADDING=0 CELLSPACING=0>
+ <TR>
+ <TD WIDTH=1 bgcolor="$*insetBoxes.frameColor">#spacer()</TD>
+ <TD WIDTH=900 BGCOLOR="$*insetBoxes.bgcolor"> ## force to full width
+ ## now begin the contents cell of the insetBox
+ $boxContents
+ ## end insetBox
+ </TD>
+ <TD WIDTH=1 BGCOLOR="$*insetBoxes.frameColor">#spacer()</TD>
+ </TR>
+</TABLE>
+
+<TABLE BORDER=0 CELLPADDING=0 CELLSPACING=0 WIDTH=$width>
+ <TR>
+ <TD HEIGHT=1 BGCOLOR="$*insetBoxes.frameColor">#spacer()</TD>
+ </TR>
+</TABLE>
+#end macro
+##============================================================================##
+
+
+
+##============================================================================##
+#data
+
+title = "Cheetah - The Python-Powered Template Engine"
+siteDomainName = "cheetahtemplate.sourceforge.net"
+siteCopyrightName= "The Cheetah Development Team"
+
+
+bodyTagAttribs = {"bgcolor": "black",
+ "text":"#000033", #333333
+ }
+
+navBar = {"bgcolor":"#3366CC", #EBEBEB #E0E0E0
+ }
+
+locationBar = {"bgcolor":"#3366CC", #EBEBEB #E0E0E0
+ "frameColor":"#CCCCCC",
+ }
+
+mainTbl = {"width":"90%",
+ "align":"center",
+ "contentsBgColor":"white",
+ "outerFrameColor":"#6699FF", #FFC850 #BDD4F1
+ "innerFrameColor":"#3366CC", #0059B2
+ "innerFrameWidth":8,
+ }
+
+insetBoxes = {"width":170,
+ "frameColor":"#DDDDDD", #000033
+ "titleFrameColor":"black", #000033
+ "bgcolor":"#FAFAFA",
+ }
+
+
+_sanSerifFonts = """helvetica, arial, "lucida sans", sans-serif"""
+
+_stylesheets = {
+ "H1.mainText": {"margin":"7px 2px",
+ "font-size":"small",
+ "font-weight":"bold",
+ "font-family": _sanSerifFonts,
+ },
+ "P, .mainText": {"margin-left":"10px"},
+
+ ".navBarContainer": {"padding": "4px 0px", "color":"white"},
+ ".navBar, A.navBar:link, A.navBar:visited": {"font-size":"small",
+ "font-weight":"bold",
+ "color":"white",
+ "text-decoration":"none",
+ "font-family": _sanSerifFonts,
+ },
+ "A.navBar:hover, A.navBar:active": {"text-decoration":"underline"},
+
+ ".locationBarContainer": {"padding": "2px 10px"},
+ ".locationBar, A.locationBar:link, A.locationBar:visited": {"font-size":"x-small",
+ "color":"#666666",
+ "text-decoration":"none",
+ "font-family": _sanSerifFonts,
+ },
+ "A.locationBar:hover, A.locationBar:active": {"text-decoration":"underline"},
+
+ ".copyrightFooter": {"color":"white", "font-size":"9pt"},
+
+ ".insetBoxTitleContainer":{"padding": "2px 5px",
+ "color":"white",
+ "font-weight":"bold",
+ },
+ ".insetBoxTitle":{"color":"white",
+ "font-size":"x-small",
+ "font-weight":"bold",
+ "text-decoration":"none",
+ "font-family": _sanSerifFonts,
+ },
+ ".insetBoxContainer":{"padding": "2px 2px 2px 5px"},
+ ".insetBox":{"font-size":"x-small",
+ "font-family": _sanSerifFonts,
+ },
+ "H1.insetBox":{"margin": "5px 5px",
+ "padding": "0px",
+ "font-size":"x-small",
+ "font-weight":"bold",
+ "font-family": _sanSerifFonts
+ },
+
+ "P.insetBox":{"margin": "2px 5px 7px 10px",
+ "padding": "0px",
+ "font-family": _sanSerifFonts
+ },
+ "A.insetBox:link, A.insetBox:visited":{"font-size":"x-small",
+ "text-decoration":"none",
+ },
+ "A.insetBox:hover, A.insetBox:active": {"text-decoration":"underline"},
+
+ ".listItems": {"margin-bottom":"200px"},
+ }
+
+_stylesheetsOrder = ["H1.mainText",
+ "P, .mainText",
+ ".navBarContainer",
+ ".navBar, A.navBar:link, A.navBar:visited",
+ "A.navBar:hover, A.navBar:active",
+ ".locationBarContainer",
+ ".locationBar, A.locationBar:link, A.locationBar:visited",
+ "A.locationBar:hover, A.locationBar:active",
+ ".copyrightFooter",
+ ".insetBoxTitleContainer",
+ ".insetBoxTitle",
+ ".insetBoxContainer",
+ ".insetBox",
+ "H1.insetBox",
+ "P.insetBox",
+ "A.insetBox:link, A.insetBox:visited",
+ "A.insetBox:hover, A.insetBox:active",
+ ".listItems",
+ ]
+
+#end data
+##============================================================================##
+
+
+##============================================================================##
+## from SkeletonPage
+#redefine bodyContents
+
+#####################
+#block header
+
+#end block header
+#####################
+
+<TABLE ALIGN=$*mainTbl.align BORDER=0 CELLPADDING=0 CELLSPACING=0 WIDTH=$*mainTbl.width>
+ <TR>
+ <TD>
+ #####################
+ #block mainTblOuterHeader
+ <DIV ALIGN="center"><IMG SRC="cheetah-face-black-medium.jpg"
+ ALT="">#spacer(35)<IMG SRC="banner_text.gif"
+ ALT="Cheetah: The Python-Powered Template Engine"></DIV>
+ ##spacer(1,10)
+
+ #end block mainTblOuterHeader
+ #####################
+ </TD>
+ </TR>
+ <TR>
+ <TD WIDTH=1 HEIGHT=1 bgcolor="$*mainTbl.outerFrameColor">#spacer()</TD>
+ ## top line of the mainTbl frame
+ </TR>
+</TABLE>
+
+
+<TABLE ALIGN=$mainTbl.align BORDER=0 CELLPADDING=0 CELLSPACING=0 WIDTH=$mainTbl.width>
+ <TR>
+ <TD WIDTH=1 bgcolor="$*mainTbl.outerFrameColor">#spacer()</TD>
+ <TD WIDTH=900> ## force to full width in Netscape
+ #####################
+ #block mainTblInnerHeader
+
+ ##############
+ #block navBar
+ <TABLE WIDTH=100% BGCOLOR="$*navBar.bgcolor" BORDER=0 CELLPADDING=0 CELLSPACING=0>
+ <TR>
+ <TD>
+#macro navDivider()
+#spacer(35)|#spacer(35)
+#end macro
+
+ <DIV ALIGN="center" CLASS="navBarContainer">
+ <A CLASS="navBar" HREF="index.html">Home</A>#navDivider()
+ <A CLASS="navBar" HREF="learn.html">Learn</A>#navDivider()
+ <A CLASS="navBar" HREF="download.html">Download</A>#navDivider()
+ <A CLASS="navBar" HREF="contribute.html">Contribute</A>
+ </DIV>
+ </TD>
+ </TR>
+ </TABLE>
+ #end block navBar
+ ##############
+
+ #end block mainTblInnerHeader
+ #####################
+ </TD>
+ <TD WIDTH="1" bgcolor="$*mainTbl.outerFrameColor">#spacer()</TD>
+ </TR>
+</TABLE>
+
+<TABLE ALIGN=$*mainTbl.align BORDER=0 CELLPADDING=0 CELLSPACING=0 WIDTH=$*mainTbl.width>
+ <TR>
+ <TD WIDTH=1 BGCOLOR="$*mainTbl.outerFrameColor">#spacer()</TD>
+ <TD WIDTH=$*mainTbl.innerFrameWidth bgcolor="$*mainTbl.innerFrameColor">#spacer()</TD>
+ <TD WIDTH=900 HEIGHT=1 BGCOLOR="black">#spacer(100)</TD> ## force to full width in Netscape
+ <TD WIDTH=$*mainTbl.innerFrameWidth bgcolor="$*mainTbl.innerFrameColor">#spacer()</TD>
+ <TD WIDTH="1" BGCOLOR="$*mainTbl.outerFrameColor">#spacer()</TD>
+ </TR>
+</TABLE>
+
+
+## MainTbl - contents ##
+
+<TABLE ALIGN=$*mainTbl.align VALIGN="top" BORDER=0 CELLPADDING=0 CELLSPACING=0 WIDTH=$*mainTbl.width>
+ <TR>
+ <TD WIDTH=1 BGCOLOR="$*mainTbl.outerFrameColor">#spacer()</TD>
+ <TD WIDTH=$*mainTbl.innerFrameWidth bgcolor="$*mainTbl.innerFrameColor">#spacer()</TD>
+ <TD WIDTH=1 BGCOLOR="black">#spacer()</TD>
+
+ <TD BGCOLOR="$*mainTbl.contentsBgColor">
+
+ ###################
+ #block locationBar
+ <TABLE WIDTH=100% BGCOLOR="#EBEBEB" BORDER=0 CELLPADDING=0 CELLSPACING=0>
+ <TR>
+ <TD>
+ <DIV CLASS="locationBarContainer">
+ ###################
+ #block locationBarContents
+ <A CLASS="locationBar" HREF="index.html">Home</A>&nbsp;&gt;&nbsp;
+ <A CLASS="locationBar" HREF="download.html">Download</A>
+ #end block locationBarContents
+ ###################
+ </DIV>
+ </TD>
+ </TR>
+ <TR>
+ <TD HEIGHT=1 BGCOLOR="$*locationBar.frameColor">#spacer()</TD>
+ </TR>
+ </TABLE>
+
+ #end block locationBar
+ ###################
+
+
+
+ ######################
+ #block mainTblContents
+
+ #spacer(1,25)
+
+ <TABLE VALIGN="top" WIDTH=100% BORDER=0 CELLPADDING=10 CELLSPACING=0>
+ <TR>
+ <TD VALIGN="top">
+ ################
+ #block leftSideBar
+
+ ##<DIV ALIGN="center"><IMG SRC="cheetah-face_medium.jpg" ALT="" BORDER=3></DIV>
+ ##<BR>
+
+ #*
+ ##======================================
+ #callMacro insetBox(boxTitle="Sections")
+ #arg boxContents
+ <DIV CLASS="insetBox">
+
+ <H1 CLASS="insetBox"><A CLASS="insetBox" HREF="learn">Learn</A></H1>
+ <P CLASS="insetBox">
+ Users Guide<BR>
+ Tutorials<BR>
+ Tips and Tricks<BR>
+ Articles<BR>
+ Language Comparisons<BR>
+ Developer Information<BR>
+ </P>
+
+ <H1 CLASS="insetBox"><A CLASS="insetBox" HREF="download">Download</A></H1>
+ <P CLASS="insetBox">
+ Stable Releases<BR>
+ CVS Snapshots<BR>
+ Third-Party Packages<BR>
+ Browse the CVS<BR>
+ </P>
+
+ <H1 CLASS="insetBox"><A CLASS="insetBox" HREF="contribute">Contribute</A></H1>
+ <P CLASS="insetBox">
+ Join the Mailing Lists<BR>
+ Links to Webware Sites<BR>
+ Bug Reports<BR>
+ Patches and New Code<BR>
+ Tutorials<BR>
+ Tips and Tricks<BR>
+ Articles<BR>
+ Language Comparisons<BR>
+ </P>
+ </DIV> ## end insetBox
+ #end arg
+ #end callMacro
+ ##======================================
+ *#
+
+ #end block leftSideBar
+ ################
+
+ </TD>
+ <TD VALIGN="top">
+ ########################
+ #block mainContentsCell
+ #end block mainContentsCell
+ ########################
+ </TD>
+ </TR>
+ </TABLE>
+
+ #spacer(1,200)
+ #end block mainTblContents
+ ######################
+ </TD>
+
+ <TD WIDTH=1 BGCOLOR="black">#spacer()</TD>
+ <TD WIDTH=$*mainTbl.innerFrameWidth BGCOLOR="$*mainTbl.innerFrameColor">#spacer()</TD>
+ <TD WIDTH="1" BGCOLOR="$*mainTbl.outerFrameColor">#spacer()</TD>
+ </TR>
+</TABLE>
+
+
+## MainTbl - closure ##
+<TABLE ALIGN=$*mainTbl.align BORDER=0 CELLPADDING=0 CELLSPACING=0 WIDTH=$*mainTbl.width>
+ <TR>
+ <TD WIDTH=1 BGCOLOR="$*mainTbl.outerFrameColor">#spacer()</TD>
+ <TD WIDTH=$*mainTbl.innerFrameWidth bgcolor="$*mainTbl.innerFrameColor">#spacer()</TD>
+ <TD WIDTH=900 BGCOLOR="black">#spacer(100)</TD> ## force to full width in Netscape
+ <TD WIDTH=$*mainTbl.innerFrameWidth bgcolor="$*mainTbl.innerFrameColor">#spacer()</TD>
+ <TD WIDTH="1" BGCOLOR="$*mainTbl.outerFrameColor">#spacer()</TD>
+ </TR>
+</TABLE>
+
+<TABLE ALIGN=$*mainTbl.align BORDER=0 CELLPADDING=0 CELLSPACING=0 WIDTH=$*mainTbl.width>
+ <TR>
+ <TD WIDTH=1 BGCOLOR="$*mainTbl.outerFrameColor">#spacer()</TD>
+ <TD WIDTH=900 BGCOLOR="$*mainTbl.innerFrameColor">#spacer(1,25)</TD> ## force to full width in Netscape
+ <TD WIDTH="1" BGCOLOR="$*mainTbl.outerFrameColor">#spacer()</TD>
+ </TR>
+</TABLE>
+
+<TABLE ALIGN=$*mainTbl.align BORDER=0 CELLPADDING=0 CELLSPACING=0 WIDTH=$*mainTbl.width>
+ <TR>
+ <TD WIDTH=1 HEIGHT=1 BGCOLOR="$*mainTbl.outerFrameColor">#spacer()</TD>
+ </TR>
+ <TR>
+ <TD>
+ #####################
+ #block mainTblOuterFooter
+ <DIV CLASS="copyrightFooter">
+ Copyright &copy; #currentYr() $*siteCopyrightName. All rights reserved.
+ </DIV>
+ <BR>
+
+ <DIV CLASS="copyrightFooter" ALIGN="center">Hosted on<BR>
+ <a href=http://sourceforge.net><img
+ src=http://sourceforge.net/sflogo.php?group_id=28961&type=1"
+ alt="SourceForge Home" height=31 width=88 border=0></a>
+ </DIV>
+
+ #end block mainTblOuterFooter
+ #####################
+ </TD>
+ </TR>
+</TABLE>
+
+
+#####################
+#block footer
+<BR>
+<BR>
+<BR>
+#end block footer
+#####################
+
+
+#end redefine bodyContents
+## from SkeletonPage
+##============================================================================##
diff --git a/examples/webware_examples/cheetahSite/banner copy.jpg b/examples/webware_examples/cheetahSite/banner copy.jpg
new file mode 100644
index 0000000..173f8c6
--- /dev/null
+++ b/examples/webware_examples/cheetahSite/banner copy.jpg
Binary files differ
diff --git a/examples/webware_examples/cheetahSite/banner2.jpg b/examples/webware_examples/cheetahSite/banner2.jpg
new file mode 100644
index 0000000..44face0
--- /dev/null
+++ b/examples/webware_examples/cheetahSite/banner2.jpg
Binary files differ
diff --git a/examples/webware_examples/cheetahSite/banner3.jpg b/examples/webware_examples/cheetahSite/banner3.jpg
new file mode 100644
index 0000000..8b48927
--- /dev/null
+++ b/examples/webware_examples/cheetahSite/banner3.jpg
Binary files differ
diff --git a/examples/webware_examples/cheetahSite/banner_cheetah.jpg b/examples/webware_examples/cheetahSite/banner_cheetah.jpg
new file mode 100644
index 0000000..acb4439
--- /dev/null
+++ b/examples/webware_examples/cheetahSite/banner_cheetah.jpg
Binary files differ
diff --git a/examples/webware_examples/cheetahSite/banner_text.gif b/examples/webware_examples/cheetahSite/banner_text.gif
new file mode 100644
index 0000000..98f33db
--- /dev/null
+++ b/examples/webware_examples/cheetahSite/banner_text.gif
Binary files differ
diff --git a/examples/webware_examples/cheetahSite/cheetah-face-black-medium.jpg b/examples/webware_examples/cheetahSite/cheetah-face-black-medium.jpg
new file mode 100644
index 0000000..fb4ad32
--- /dev/null
+++ b/examples/webware_examples/cheetahSite/cheetah-face-black-medium.jpg
Binary files differ
diff --git a/examples/webware_examples/cheetahSite/cheetah-face-black.jpg b/examples/webware_examples/cheetahSite/cheetah-face-black.jpg
new file mode 100644
index 0000000..ace02c8
--- /dev/null
+++ b/examples/webware_examples/cheetahSite/cheetah-face-black.jpg
Binary files differ
diff --git a/examples/webware_examples/cheetahSite/cheetah-face-black.psd b/examples/webware_examples/cheetahSite/cheetah-face-black.psd
new file mode 100644
index 0000000..7c765c6
--- /dev/null
+++ b/examples/webware_examples/cheetahSite/cheetah-face-black.psd
Binary files differ
diff --git a/examples/webware_examples/cheetahSite/cheetah-face-original.jpg b/examples/webware_examples/cheetahSite/cheetah-face-original.jpg
new file mode 100644
index 0000000..a63cd21
--- /dev/null
+++ b/examples/webware_examples/cheetahSite/cheetah-face-original.jpg
Binary files differ
diff --git a/examples/webware_examples/cheetahSite/cheetah-face.jpg b/examples/webware_examples/cheetahSite/cheetah-face.jpg
new file mode 100644
index 0000000..dace106
--- /dev/null
+++ b/examples/webware_examples/cheetahSite/cheetah-face.jpg
Binary files differ
diff --git a/examples/webware_examples/cheetahSite/cheetah-face_medium.jpg b/examples/webware_examples/cheetahSite/cheetah-face_medium.jpg
new file mode 100644
index 0000000..261888b
--- /dev/null
+++ b/examples/webware_examples/cheetahSite/cheetah-face_medium.jpg
Binary files differ
diff --git a/examples/webware_examples/cheetahSite/contribute.tmpl b/examples/webware_examples/cheetahSite/contribute.tmpl
new file mode 100644
index 0000000..6256758
--- /dev/null
+++ b/examples/webware_examples/cheetahSite/contribute.tmpl
@@ -0,0 +1,93 @@
+#extend SiteTemplate
+
+##============================================================================##
+#data
+
+#end data
+##============================================================================##
+
+
+##============================================================================##
+## from SiteTemplate
+#redefine locationBarContents
+ <A CLASS="locationBar" HREF="index.html">Home</A>&nbsp;&gt;&nbsp;
+ <A CLASS="locationBar" HREF="contribute.html">Contribute</A>
+#end redefine locationBarContents
+##============================================================================##
+
+
+##============================================================================##
+## from SiteTemplate
+#redefine mainContentsCell
+
+<H2>How can I contribute?
+</H2>
+
+<P> Cheetah is the work of many volunteers. If you use Cheetah, share
+your experiences, tricks, customizations, and frustrations. Please join the <a
+href=http://lists.sourceforge.net/mailman/listinfo/cheetahtemplate-discuss>mailing
+list</a>.
+
+<P>
+
+<H3>Bug reports and patches</A>
+</H3>
+
+<P>
+If you think there is a bug in Cheetah, send a message to the email list
+with the following information:
+
+<P>
+
+<OL>
+<LI>a description of what you were trying to do and what happened
+</LI>
+<LI>all tracebacks and error output
+</LI>
+<LI>your version of Cheetah
+</LI>
+<LI>your version of Python
+</LI>
+<LI>your operating system
+</LI>
+<LI>whether you have changed anything in the Cheetah installation
+</LI>
+</OL>
+
+<P>
+
+<H3>Example sites and tutorials</A>
+</H3>
+If you're developing a website with Cheetah, please send a link to the
+email list so we can keep track of Cheetah sites. Also, if you discover
+new and interesting ways to use Cheetah please share your experience and
+write a quick tutorial about your technique.
+
+<P>
+
+<H3>Macro libraries</A></H3>
+We hope to build up a framework of macros
+libraries (see the Users Guide) to distribute with Cheetah and would
+appreciate any contributions.
+
+<P>
+
+<H3>Test cases</A>
+</H3>
+Cheetah is packaged with a regression testing suite that is run with each
+new release to ensure that everything is working as expected and that recent
+changes haven't broken anything. The test cases are in the Cheetah.Tests
+module. If you find a reproduceable bug please consider writing a test case
+that will pass only when the bug is fixed. Send any new test cases to the email
+list with the subject-line ``new test case for Cheetah.''
+
+<P>
+
+<H3>Publicity</A>
+</H3>
+Help spread the word ... recommend it to others, write articles about it, etc.
+
+<P>
+
+#end redefine mainContentsCell
+##============================================================================##
diff --git a/examples/webware_examples/cheetahSite/download.tmpl b/examples/webware_examples/cheetahSite/download.tmpl
new file mode 100644
index 0000000..688f18e
--- /dev/null
+++ b/examples/webware_examples/cheetahSite/download.tmpl
@@ -0,0 +1,33 @@
+#extend SiteTemplate
+
+##============================================================================##
+#data
+
+#end data
+##============================================================================##
+
+
+##============================================================================##
+## from WebwareSiteTemplate
+#redefine locationBarContents
+ <A CLASS="locationBar" HREF="index.html">Home</A>&nbsp;&gt;&nbsp;
+ <A CLASS="locationBar" HREF="download.html">Download Cheetah</A>
+#end redefine locationBarContents
+## from WebwareSiteTemplate
+##============================================================================##
+
+
+##============================================================================##
+## from WebwareSiteTemplate
+#redefine mainContentsCell
+
+<P>
+<A HREF="Downloads/Cheetah-LATEST.tar.gz">Here</A> is the current Cheetah
+release (0.9.6).
+
+<P>
+And <A HREF="Downloads/">here</A> are all the recent Cheetah releases.
+
+#end redefine mainContentsCell
+## from WebwareSiteTemplate
+##============================================================================##
diff --git a/examples/webware_examples/cheetahSite/index.html b/examples/webware_examples/cheetahSite/index.html
new file mode 100644
index 0000000..3af64b7
--- /dev/null
+++ b/examples/webware_examples/cheetahSite/index.html
@@ -0,0 +1,248 @@
+
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
+<HTML>
+<!-- This document was autogenerated by Cheetah. Don't edit it directly!
+
+Copyright 2001 - The Cheetah Development Team - All Rights Reserved.
+Feel free to copy any javascript or html you like on this site,
+provided you remove all links and/or references to cheetahtemplate.sourceforge.net
+However, please do not copy any content or images without permission.
+
+Designed & Implemented by Tavis Rudd
+
+-->
+
+
+
+<HEAD>
+<TITLE>Cheetah - The Python-Powered Template Engine</TITLE>
+
+<STYLE TYPE="text/css"><!--
+
+H1.mainText {font-family: helvetica, arial, "lucida sans", sans-serif; margin: 7px 2px; font-weight: bold; font-size: small}
+P, .mainText {margin-left: 10px}
+.navBarContainer {color: white; padding: 4px 0px}
+.navBar, A.navBar:link, A.navBar:visited {text-decoration: none; font-family: helvetica, arial, "lucida sans", sans-serif; font-weight: bold; font-size: small; color: white}
+A.navBar:hover, A.navBar:active {text-decoration: underline}
+.locationBarContainer {padding: 2px 10px}
+.locationBar, A.locationBar:link, A.locationBar:visited {text-decoration: none; font-family: helvetica, arial, "lucida sans", sans-serif; font-size: x-small; color: #666666}
+A.locationBar:hover, A.locationBar:active {text-decoration: underline}
+.copyrightFooter {font-size: 9pt; color: white}
+.insetBoxTitleContainer {padding: 2px 5px; font-weight: bold; color: white}
+.insetBoxTitle {text-decoration: none; font-family: helvetica, arial, "lucida sans", sans-serif; font-weight: bold; font-size: x-small; color: white}
+.insetBoxContainer {padding: 2px 2px 2px 5px}
+.insetBox {font-family: helvetica, arial, "lucida sans", sans-serif; font-size: x-small}
+H1.insetBox {font-family: helvetica, arial, "lucida sans", sans-serif; margin: 5px 5px; font-weight: bold; font-size: x-small; padding: 0px}
+P.insetBox {margin: 2px 5px 7px 10px; font-family: helvetica, arial, "lucida sans", sans-serif; padding: 0px}
+A.insetBox:link, A.insetBox:visited {text-decoration: none; font-size: x-small}
+A.insetBox:hover, A.insetBox:active {text-decoration: underline}
+.listItems {margin-bottom: 200px}
+//--></STYLE>
+
+</HEAD>
+
+
+
+
+<BODY BGCOLOR="black" TEXT="#000033">
+
+
+
+
+
+<TABLE ALIGN=center BORDER=0 CELLPADDING=0 CELLSPACING=0 WIDTH=90%>
+ <TR>
+ <TD>
+ <DIV ALIGN="center"><IMG SRC="cheetah-face-black-medium.jpg"
+ ALT=""><IMG SRC="spacer.gif" WIDTH=35 HEIGHT=1 ALT=""><IMG SRC="banner_text.gif"
+ ALT="Cheetah: The Python-Powered Template Engine"></DIV>
+
+ </TD>
+ </TR>
+ <TR>
+ <TD WIDTH=1 HEIGHT=1 bgcolor="#6699FF"><IMG SRC="spacer.gif" WIDTH=1 HEIGHT=1 ALT=""></TD>
+ </TR>
+</TABLE>
+
+
+<TABLE ALIGN=center BORDER=0 CELLPADDING=0 CELLSPACING=0 WIDTH=90%>
+ <TR>
+ <TD WIDTH=1 bgcolor="#6699FF"><IMG SRC="spacer.gif" WIDTH=1 HEIGHT=1 ALT=""></TD>
+ <TD WIDTH=900>
+
+ <TABLE WIDTH=100% BGCOLOR="#3366CC" BORDER=0 CELLPADDING=0 CELLSPACING=0>
+ <TR>
+ <TD>
+
+ <DIV ALIGN="center" CLASS="navBarContainer">
+ <A CLASS="navBar" HREF="index.html">Home</A><IMG SRC="spacer.gif" WIDTH=35 HEIGHT=1 ALT="">|<IMG SRC="spacer.gif" WIDTH=35 HEIGHT=1 ALT="">
+ <A CLASS="navBar" HREF="learn.html">Learn</A><IMG SRC="spacer.gif" WIDTH=35 HEIGHT=1 ALT="">|<IMG SRC="spacer.gif" WIDTH=35 HEIGHT=1 ALT="">
+ <A CLASS="navBar" HREF="download.html">Download</A><IMG SRC="spacer.gif" WIDTH=35 HEIGHT=1 ALT="">|<IMG SRC="spacer.gif" WIDTH=35 HEIGHT=1 ALT="">
+ <A CLASS="navBar" HREF="contribute.html">Contribute</A>
+ </DIV>
+ </TD>
+ </TR>
+ </TABLE>
+
+ </TD>
+ <TD WIDTH="1" bgcolor="#6699FF"><IMG SRC="spacer.gif" WIDTH=1 HEIGHT=1 ALT=""></TD>
+ </TR>
+</TABLE>
+
+<TABLE ALIGN=center BORDER=0 CELLPADDING=0 CELLSPACING=0 WIDTH=90%>
+ <TR>
+ <TD WIDTH=1 BGCOLOR="#6699FF"><IMG SRC="spacer.gif" WIDTH=1 HEIGHT=1 ALT=""></TD>
+ <TD WIDTH=8 bgcolor="#3366CC"><IMG SRC="spacer.gif" WIDTH=1 HEIGHT=1 ALT=""></TD>
+ <TD WIDTH=900 HEIGHT=1 BGCOLOR="black"><IMG SRC="spacer.gif" WIDTH=100 HEIGHT=1 ALT=""></TD>
+ <TD WIDTH=8 bgcolor="#3366CC"><IMG SRC="spacer.gif" WIDTH=1 HEIGHT=1 ALT=""></TD>
+ <TD WIDTH="1" BGCOLOR="#6699FF"><IMG SRC="spacer.gif" WIDTH=1 HEIGHT=1 ALT=""></TD>
+ </TR>
+</TABLE>
+
+
+
+<TABLE ALIGN=center VALIGN="top" BORDER=0 CELLPADDING=0 CELLSPACING=0 WIDTH=90%>
+ <TR>
+ <TD WIDTH=1 BGCOLOR="#6699FF"><IMG SRC="spacer.gif" WIDTH=1 HEIGHT=1 ALT=""></TD>
+ <TD WIDTH=8 bgcolor="#3366CC"><IMG SRC="spacer.gif" WIDTH=1 HEIGHT=1 ALT=""></TD>
+ <TD WIDTH=1 BGCOLOR="black"><IMG SRC="spacer.gif" WIDTH=1 HEIGHT=1 ALT=""></TD>
+
+ <TD BGCOLOR="white">
+
+
+
+
+
+
+ <IMG SRC="spacer.gif" WIDTH=1 HEIGHT=25 ALT="">
+
+ <TABLE VALIGN="top" WIDTH=100% BORDER=0 CELLPADDING=10 CELLSPACING=0>
+ <TR>
+ <TD VALIGN="top">
+ <BR>
+ <a href=http://lists.sourceforge.net/mailman/listinfo/cheetahtemplate-discuss><B>Mailing
+ List</B></a><BR>
+ <A HREF="http://sourceforge.net/cvs/?group_id=28961"><B>CVS</B></A><BR>
+
+ </TD>
+ <TD VALIGN="top">
+
+<P>
+<B>Cheetah</B> is a <A HREF="http://www.python.org">Python</A>-powered template engine and code-generator. It aims to:
+<ul>
+ <li> make it easy to separate content, graphic design, and program code.
+ <li> make it easy to integrate content, graphic design, and program code.
+ <li> provide template designers with a small set of <i>display
+ logic</i> programming structures such as conditional blocks and for loops
+ <li> be equally well-suited for any text format including HTML,
+ SGML, XML, SQL, Postscript, e-mail and LaTeX
+ <li> achieve all these aims in a manner that is efficient, flexible, and extendable.
+</ul>
+</P>
+
+
+<P>
+<B>Cheetah</B> achieves these aims by:
+
+<UL>
+<LI>blending the power and flexibility of Python with the simplicity of a
+ small Template Definition language that non-programmers can understand.
+<LI>giving template designers a simple way of accessing Python variables,
+ objects, and functions in their templates.
+
+<LI>providing a modular, object-orientated framework that makes it easy to
+ create and maintain large websites.
+
+<LI>compiling 'Template Definitions' into native Python code at startup.
+ Thereafter this code is executed for each request. This approach is
+ dramatically faster than the string substitution approach used by many
+ templating engines.
+
+</LI>
+<LI>providing a very simple, yet powerful, caching mechanism that can
+ significantly increase the responsiveness of a dynamic website.
+</LI>
+</UL>
+
+<P><B>Cheetah</B> requires <A HREF="http://www.python.org">Python</A> 2.0 or greater. It can be used from ordinary Python
+programs, but also provides integration with <a
+href=http://webware.sourceforge.net/>Webware for Python</a>.</P>
+
+<P><B>Cheetah</B> is Open Source Software, released under the terms of the Python
+License.
+
+<P><B>Cheetah</B> is alpha software as some aspects of its design are still subject to
+change. We plan to release a stable version later this summer.
+
+<P> The discussion list is
+<a href=mailto:cheetahtemplate-discuss@lists.sourceforge.net
+>cheetahtemplate-discuss@lists.sourceforge.net</a>.
+You can join <a href=http://lists.sourceforge.net/mailman/listinfo/cheetahtemplate-discuss>here</a>
+and view the archives <a
+href=http://www.geocrawler.com/redir-sf.php3?list=cheetahtemplate-discuss>here</a>.
+</P>
+
+
+ </TD>
+ </TR>
+ </TABLE>
+
+ <IMG SRC="spacer.gif" WIDTH=1 HEIGHT=200 ALT="">
+ </TD>
+
+ <TD WIDTH=1 BGCOLOR="black"><IMG SRC="spacer.gif" WIDTH=1 HEIGHT=1 ALT=""></TD>
+ <TD WIDTH=8 BGCOLOR="#3366CC"><IMG SRC="spacer.gif" WIDTH=1 HEIGHT=1 ALT=""></TD>
+ <TD WIDTH="1" BGCOLOR="#6699FF"><IMG SRC="spacer.gif" WIDTH=1 HEIGHT=1 ALT=""></TD>
+ </TR>
+</TABLE>
+
+
+<TABLE ALIGN=center BORDER=0 CELLPADDING=0 CELLSPACING=0 WIDTH=90%>
+ <TR>
+ <TD WIDTH=1 BGCOLOR="#6699FF"><IMG SRC="spacer.gif" WIDTH=1 HEIGHT=1 ALT=""></TD>
+ <TD WIDTH=8 bgcolor="#3366CC"><IMG SRC="spacer.gif" WIDTH=1 HEIGHT=1 ALT=""></TD>
+ <TD WIDTH=900 BGCOLOR="black"><IMG SRC="spacer.gif" WIDTH=100 HEIGHT=1 ALT=""></TD>
+ <TD WIDTH=8 bgcolor="#3366CC"><IMG SRC="spacer.gif" WIDTH=1 HEIGHT=1 ALT=""></TD>
+ <TD WIDTH="1" BGCOLOR="#6699FF"><IMG SRC="spacer.gif" WIDTH=1 HEIGHT=1 ALT=""></TD>
+ </TR>
+</TABLE>
+
+<TABLE ALIGN=center BORDER=0 CELLPADDING=0 CELLSPACING=0 WIDTH=90%>
+ <TR>
+ <TD WIDTH=1 BGCOLOR="#6699FF"><IMG SRC="spacer.gif" WIDTH=1 HEIGHT=1 ALT=""></TD>
+ <TD WIDTH=900 BGCOLOR="#3366CC"><IMG SRC="spacer.gif" WIDTH=1 HEIGHT=25 ALT=""></TD>
+ <TD WIDTH="1" BGCOLOR="#6699FF"><IMG SRC="spacer.gif" WIDTH=1 HEIGHT=1 ALT=""></TD>
+ </TR>
+</TABLE>
+
+<TABLE ALIGN=center BORDER=0 CELLPADDING=0 CELLSPACING=0 WIDTH=90%>
+ <TR>
+ <TD WIDTH=1 HEIGHT=1 BGCOLOR="#6699FF"><IMG SRC="spacer.gif" WIDTH=1 HEIGHT=1 ALT=""></TD>
+ </TR>
+ <TR>
+ <TD>
+ <DIV CLASS="copyrightFooter">
+ Copyright &copy; 2001 The Cheetah Development Team. All rights reserved.
+ </DIV>
+ <BR>
+
+ <DIV CLASS="copyrightFooter" ALIGN="center">Hosted on<BR>
+ <a href=http://sourceforge.net><img
+ src=http://sourceforge.net/sflogo.php?group_id=28961&type=1"
+ alt="SourceForge Home" height=31 width=88 border=0></a>
+ </DIV>
+
+ </TD>
+ </TR>
+</TABLE>
+
+
+<BR>
+<BR>
+<BR>
+
+
+
+
+</BODY>
+</HTML>
diff --git a/examples/webware_examples/cheetahSite/index.tmpl b/examples/webware_examples/cheetahSite/index.tmpl
new file mode 100644
index 0000000..d573a07
--- /dev/null
+++ b/examples/webware_examples/cheetahSite/index.tmpl
@@ -0,0 +1,82 @@
+#extend SiteTemplate
+
+###################
+#redefine locationBar
+
+#end redefine locationBar
+###################
+
+
+##============================================================================##
+## from SiteTemplate
+#redefine leftSideBar
+ <BR>
+ <a href=http://lists.sourceforge.net/mailman/listinfo/cheetahtemplate-discuss><B>Mailing
+ List</B></a><BR>
+ <A HREF="http://sourceforge.net/cvs/?group_id=28961"><B>CVS</B></A><BR>
+#end redefine leftSideBar
+##============================================================================##
+
+
+########################
+#redefine mainContentsCell
+
+##<H1 CLASS="mainText">What is Cheetah?</H1>
+<P>
+<B>Cheetah</B> is a #Python()-powered template engine and code-generator. It aims to:
+<ul>
+ <li> make it easy to separate content, graphic design, and program code.
+ <li> make it easy to integrate content, graphic design, and program code.
+ <li> provide template designers with a small set of <i>display
+ logic</i> programming structures such as conditional blocks and for loops
+ <li> be equally well-suited for any text format including HTML,
+ SGML, XML, SQL, Postscript, e-mail and LaTeX
+ <li> achieve all these aims in a manner that is efficient, flexible, and extendable.
+</ul>
+</P>
+
+
+<P>
+<B>Cheetah</B> achieves these aims by:
+
+<UL>
+<LI>blending the power and flexibility of Python with the simplicity of a
+ small Template Definition language that non-programmers can understand.
+<LI>giving template designers a simple way of accessing Python variables,
+ objects, and functions in their templates.
+
+<LI>providing a modular, object-orientated framework that makes it easy to
+ create and maintain large websites.
+
+<LI>compiling 'Template Definitions' into native Python code at startup.
+ Thereafter this code is executed for each request. This approach is
+ dramatically faster than the string substitution approach used by many
+ templating engines.
+
+</LI>
+<LI>providing a very simple, yet powerful, caching mechanism that can
+ significantly increase the responsiveness of a dynamic website.
+</LI>
+</UL>
+
+<P><B>Cheetah</B> requires #Python() 2.0 or greater. It can be used from ordinary Python
+programs, but also provides integration with <a
+href=http://webware.sourceforge.net/>Webware for Python</a>.</P>
+
+<P><B>Cheetah</B> is Open Source Software, released under the terms of the Python
+License.
+
+<P><B>Cheetah</B> is alpha software as some aspects of its design are still subject to
+change. We plan to release a stable version later this summer.
+
+<P> The discussion list is
+<a href=mailto:cheetahtemplate-discuss@lists.sourceforge.net
+>cheetahtemplate-discuss@lists.sourceforge.net</a>.
+You can join <a href=http://lists.sourceforge.net/mailman/listinfo/cheetahtemplate-discuss>here</a>
+and view the archives <a
+href=http://www.geocrawler.com/redir-sf.php3?list=cheetahtemplate-discuss>here</a>.
+</P>
+
+
+#end redefine mainContentsCell
+########################
diff --git a/examples/webware_examples/cheetahSite/learn.html b/examples/webware_examples/cheetahSite/learn.html
new file mode 100644
index 0000000..f6c907e
--- /dev/null
+++ b/examples/webware_examples/cheetahSite/learn.html
@@ -0,0 +1,1668 @@
+
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
+<HTML>
+<!-- This document was autogenerated by Cheetah. Don't edit it directly!
+
+Copyright 2001 - The Cheetah Development Team - All Rights Reserved.
+Feel free to copy any javascript or html you like on this site,
+provided you remove all links and/or references to cheetahtemplate.sourceforge.net
+However, please do not copy any content or images without permission.
+
+Designed & Implemented by Tavis Rudd
+
+-->
+
+
+
+<HEAD>
+<TITLE>Cheetah - The Python-Powered Template Engine</TITLE>
+
+<STYLE TYPE="text/css"><!--
+
+H1.mainText {font-family: helvetica, arial, "lucida sans", sans-serif; margin: 7px 2px; font-weight: bold; font-size: small}
+P, .mainText {margin-left: 10px}
+.navBarContainer {color: white; padding: 4px 0px}
+.navBar, A.navBar:link, A.navBar:visited {text-decoration: none; font-family: helvetica, arial, "lucida sans", sans-serif; font-weight: bold; font-size: small; color: white}
+A.navBar:hover, A.navBar:active {text-decoration: underline}
+.locationBarContainer {padding: 2px 10px}
+.locationBar, A.locationBar:link, A.locationBar:visited {text-decoration: none; font-family: helvetica, arial, "lucida sans", sans-serif; font-size: x-small; color: #666666}
+A.locationBar:hover, A.locationBar:active {text-decoration: underline}
+.copyrightFooter {font-size: 9pt; color: white}
+.insetBoxTitleContainer {padding: 2px 5px; font-weight: bold; color: white}
+.insetBoxTitle {text-decoration: none; font-family: helvetica, arial, "lucida sans", sans-serif; font-weight: bold; font-size: x-small; color: white}
+.insetBoxContainer {padding: 2px 2px 2px 5px}
+.insetBox {font-family: helvetica, arial, "lucida sans", sans-serif; font-size: x-small}
+H1.insetBox {font-family: helvetica, arial, "lucida sans", sans-serif; margin: 5px 5px; font-weight: bold; font-size: x-small; padding: 0px}
+P.insetBox {margin: 2px 5px 7px 10px; font-family: helvetica, arial, "lucida sans", sans-serif; padding: 0px}
+A.insetBox:link, A.insetBox:visited {text-decoration: none; font-size: x-small}
+A.insetBox:hover, A.insetBox:active {text-decoration: underline}
+.listItems {margin-bottom: 200px}
+//--></STYLE>
+
+</HEAD>
+
+
+
+
+<BODY BGCOLOR="black" TEXT="#000033">
+
+
+
+
+
+<TABLE ALIGN=center BORDER=0 CELLPADDING=0 CELLSPACING=0 WIDTH=90%>
+ <TR>
+ <TD>
+ <DIV ALIGN="center"><IMG SRC="cheetah-face-black-medium.jpg"
+ ALT=""><IMG SRC="spacer.gif" WIDTH=35 HEIGHT=1 ALT=""><IMG SRC="banner_text.gif"
+ ALT="Cheetah: The Python-Powered Template Engine"></DIV>
+
+ </TD>
+ </TR>
+ <TR>
+ <TD WIDTH=1 HEIGHT=1 bgcolor="#6699FF"><IMG SRC="spacer.gif" WIDTH=1 HEIGHT=1 ALT=""></TD>
+ </TR>
+</TABLE>
+
+
+<TABLE ALIGN=center BORDER=0 CELLPADDING=0 CELLSPACING=0 WIDTH=90%>
+ <TR>
+ <TD WIDTH=1 bgcolor="#6699FF"><IMG SRC="spacer.gif" WIDTH=1 HEIGHT=1 ALT=""></TD>
+ <TD WIDTH=900>
+
+ <TABLE WIDTH=100% BGCOLOR="#3366CC" BORDER=0 CELLPADDING=0 CELLSPACING=0>
+ <TR>
+ <TD>
+
+ <DIV ALIGN="center" CLASS="navBarContainer">
+ <A CLASS="navBar" HREF="index.html">Home</A><IMG SRC="spacer.gif" WIDTH=35 HEIGHT=1 ALT="">|<IMG SRC="spacer.gif" WIDTH=35 HEIGHT=1 ALT="">
+ <A CLASS="navBar" HREF="learn.html">Learn</A><IMG SRC="spacer.gif" WIDTH=35 HEIGHT=1 ALT="">|<IMG SRC="spacer.gif" WIDTH=35 HEIGHT=1 ALT="">
+ <A CLASS="navBar" HREF="download.html">Download</A><IMG SRC="spacer.gif" WIDTH=35 HEIGHT=1 ALT="">|<IMG SRC="spacer.gif" WIDTH=35 HEIGHT=1 ALT="">
+ <A CLASS="navBar" HREF="contribute.html">Contribute</A>
+ </DIV>
+ </TD>
+ </TR>
+ </TABLE>
+
+ </TD>
+ <TD WIDTH="1" bgcolor="#6699FF"><IMG SRC="spacer.gif" WIDTH=1 HEIGHT=1 ALT=""></TD>
+ </TR>
+</TABLE>
+
+<TABLE ALIGN=center BORDER=0 CELLPADDING=0 CELLSPACING=0 WIDTH=90%>
+ <TR>
+ <TD WIDTH=1 BGCOLOR="#6699FF"><IMG SRC="spacer.gif" WIDTH=1 HEIGHT=1 ALT=""></TD>
+ <TD WIDTH=8 bgcolor="#3366CC"><IMG SRC="spacer.gif" WIDTH=1 HEIGHT=1 ALT=""></TD>
+ <TD WIDTH=900 HEIGHT=1 BGCOLOR="black"><IMG SRC="spacer.gif" WIDTH=100 HEIGHT=1 ALT=""></TD>
+ <TD WIDTH=8 bgcolor="#3366CC"><IMG SRC="spacer.gif" WIDTH=1 HEIGHT=1 ALT=""></TD>
+ <TD WIDTH="1" BGCOLOR="#6699FF"><IMG SRC="spacer.gif" WIDTH=1 HEIGHT=1 ALT=""></TD>
+ </TR>
+</TABLE>
+
+
+
+<TABLE ALIGN=center VALIGN="top" BORDER=0 CELLPADDING=0 CELLSPACING=0 WIDTH=90%>
+ <TR>
+ <TD WIDTH=1 BGCOLOR="#6699FF"><IMG SRC="spacer.gif" WIDTH=1 HEIGHT=1 ALT=""></TD>
+ <TD WIDTH=8 bgcolor="#3366CC"><IMG SRC="spacer.gif" WIDTH=1 HEIGHT=1 ALT=""></TD>
+ <TD WIDTH=1 BGCOLOR="black"><IMG SRC="spacer.gif" WIDTH=1 HEIGHT=1 ALT=""></TD>
+
+ <TD BGCOLOR="white">
+
+ <TABLE WIDTH=100% BGCOLOR="#EBEBEB" BORDER=0 CELLPADDING=0 CELLSPACING=0>
+ <TR>
+ <TD>
+ <DIV CLASS="locationBarContainer">
+ <A CLASS="locationBar" HREF="index.html">Home</A>&nbsp;&gt;&nbsp;
+ <A CLASS="locationBar" HREF="learn.html">Learn</A>
+ </DIV>
+ </TD>
+ </TR>
+ <TR>
+ <TD HEIGHT=1 BGCOLOR="#CCCCCC"><IMG SRC="spacer.gif" WIDTH=1 HEIGHT=1 ALT=""></TD>
+ </TR>
+ </TABLE>
+
+
+
+
+
+ <IMG SRC="spacer.gif" WIDTH=1 HEIGHT=25 ALT="">
+
+ <TABLE VALIGN="top" WIDTH=100% BORDER=0 CELLPADDING=10 CELLSPACING=0>
+ <TR>
+ <TD VALIGN="top">
+ <BR>
+ <A HREF="Downloads/users_guide.pdf"><B>PDF</B></A><BR>
+ <A HREF="Downloads/users_guide.ps"><B>PostScript</B></A><BR>
+ <A HREF="Downloads/users_guide.txt"><B>Plain Text</B></A><BR>
+
+ </TD>
+ <TD VALIGN="top">
+
+<div class="titlepage">
+<center>
+<h1>Cheetah Users Guide</h1>
+<p><b><font size='+2'>The Cheetah Development Team</font></b></p>
+<p><span class="email">cheetahtemplate-devel@lists.sourceforge.net</span></p>
+<p>
+</center>
+</div>
+
+<P>
+<BR>
+<BR><H2><A NAME="SECTION000100000000000000000">
+Contents</A>
+</H2>
+<!--Table of Contents-->
+
+<UL CLASS="TofC">
+<LI><A NAME="tex2html2"
+ HREF="users_guide.html">1 Introduction</A>
+<UL>
+<LI><A NAME="tex2html3"
+ HREF="#SECTION000210000000000000000">1.1 What is Cheetah?</A>
+<LI><A NAME="tex2html4"
+ HREF="#SECTION000220000000000000000">1.2 Why is it called Cheetah?</A>
+<LI><A NAME="tex2html5"
+ HREF="#SECTION000230000000000000000">1.3 Who developed Cheetah?</A>
+<LI><A NAME="tex2html6"
+ HREF="#SECTION000240000000000000000">1.4 How mature is Cheetah?</A>
+<LI><A NAME="tex2html7"
+ HREF="#SECTION000250000000000000000">1.5 Where can I get releases?</A>
+<LI><A NAME="tex2html8"
+ HREF="#SECTION000260000000000000000">1.6 Where can I get news?</A>
+<LI><A NAME="tex2html9"
+ HREF="#SECTION000270000000000000000">1.7 How can I contribute?</A>
+<LI><A NAME="tex2html10"
+ HREF="#SECTION000280000000000000000">1.8 Acknowledgements</A>
+<LI><A NAME="tex2html11"
+ HREF="#SECTION000290000000000000000">1.9 License</A>
+</UL>
+<BR>
+<LI><A NAME="tex2html12"
+ HREF="#SECTION000300000000000000000">2 Getting Started</A>
+<UL>
+<LI><A NAME="tex2html13"
+ HREF="#SECTION000310000000000000000">2.1 Requirements</A>
+<LI><A NAME="tex2html14"
+ HREF="#SECTION000320000000000000000">2.2 Installation</A>
+<LI><A NAME="tex2html15"
+ HREF="#SECTION000330000000000000000">2.3 Testing your installation</A>
+<LI><A NAME="tex2html16"
+ HREF="#SECTION000340000000000000000">2.4 Quickstart tutorial</A>
+</UL>
+<BR>
+<LI><A NAME="tex2html17"
+ HREF="#SECTION000400000000000000000">3 Template Objects</A>
+<UL>
+<LI><A NAME="tex2html18"
+ HREF="#SECTION000410000000000000000">3.1 The Template class</A>
+<LI><A NAME="tex2html19"
+ HREF="#SECTION000420000000000000000">3.2 Constructing Template objects</A>
+<LI><A NAME="tex2html20"
+ HREF="#SECTION000430000000000000000">3.3 Using Template objects</A>
+<LI><A NAME="tex2html21"
+ HREF="#SECTION000440000000000000000">3.4 Modifying Template objects</A>
+</UL>
+<BR>
+<LI><A NAME="tex2html22"
+ HREF="#SECTION000500000000000000000">4 The Template Definition Language</A>
+<UL>
+<LI><A NAME="tex2html23"
+ HREF="#SECTION000510000000000000000">4.1 Placeholder tags</A>
+<LI><A NAME="tex2html24"
+ HREF="#SECTION000520000000000000000">4.2 Directive tags</A>
+<LI><A NAME="tex2html25"
+ HREF="#SECTION000530000000000000000">4.3 Comment directives</A>
+<LI><A NAME="tex2html26"
+ HREF="#SECTION000540000000000000000">4.4 #raw directives</A>
+<LI><A NAME="tex2html27"
+ HREF="#SECTION000550000000000000000">4.5 #include directives</A>
+<LI><A NAME="tex2html28"
+ HREF="#SECTION000560000000000000000">4.6 #cache directives</A>
+<LI><A NAME="tex2html29"
+ HREF="#SECTION000570000000000000000">4.7 Display logic directives</A>
+<LI><A NAME="tex2html30"
+ HREF="#SECTION000580000000000000000">4.8 #block directives</A>
+</UL>
+<BR>
+<LI><A NAME="tex2html31"
+ HREF="#SECTION000600000000000000000">5 Macros</A>
+<UL>
+<LI><A NAME="tex2html32"
+ HREF="#SECTION000610000000000000000">5.1 What are macros?</A>
+<LI><A NAME="tex2html33"
+ HREF="#SECTION000620000000000000000">5.2 Using macros</A>
+<LI><A NAME="tex2html34"
+ HREF="#SECTION000630000000000000000">5.3 Defining macros</A>
+<LI><A NAME="tex2html35"
+ HREF="#SECTION000640000000000000000">5.4 Macro libraries</A>
+</UL>
+<BR>
+<LI><A NAME="tex2html36"
+ HREF="#SECTION000700000000000000000">6 Using Cheetah with Webware</A>
+<UL>
+<LI><A NAME="tex2html37"
+ HREF="#SECTION000710000000000000000">6.1 Background</A>
+<LI><A NAME="tex2html38"
+ HREF="#SECTION000720000000000000000">6.2 Using the Inheritance approach</A>
+<LI><A NAME="tex2html39"
+ HREF="#SECTION000730000000000000000">6.3 Using the Containment approach</A>
+<LI><A NAME="tex2html40"
+ HREF="#SECTION000740000000000000000">6.4 User interaction in either approach</A>
+<LI><A NAME="tex2html41"
+ HREF="#SECTION000750000000000000000">6.5 Components</A>
+</UL>
+<BR>
+<LI><A NAME="tex2html42"
+ HREF="#SECTION000800000000000000000">7 Customizing and extending Cheetah</A>
+<UL>
+<LI><A NAME="tex2html43"
+ HREF="#SECTION000810000000000000000">7.1 Custom handling of unknown placeholder names</A>
+<LI><A NAME="tex2html44"
+ HREF="#SECTION000820000000000000000">7.2 Plugins</A>
+<LI><A NAME="tex2html45"
+ HREF="#SECTION000830000000000000000">7.3 Custom variable-tags</A>
+<LI><A NAME="tex2html46"
+ HREF="#SECTION000840000000000000000">7.4 Custom directives</A>
+<LI><A NAME="tex2html47"
+ HREF="#SECTION000850000000000000000">7.5 Custom error handlers</A>
+<LI><A NAME="tex2html48"
+ HREF="#SECTION000860000000000000000">7.6 Safe delegation</A>
+</UL>
+<BR>
+<LI><A NAME="tex2html49"
+ HREF="#SECTION000900000000000000000">8 Examples</A>
+<UL>
+<LI><A NAME="tex2html50"
+ HREF="#SECTION000910000000000000000">8.1 Syntax examples</A>
+<LI><A NAME="tex2html51"
+ HREF="#SECTION000920000000000000000">8.2 Webware Examples</A>
+</UL>
+<BR>
+<LI><A NAME="tex2html52"
+ HREF="#SECTION0001000000000000000000">About this document ...</A>
+</UL>
+<!--End of Table of Contents-->
+<P>
+&#169;Copyright 2001, The Cheetah Development Team.
+ Permission is granted to copy, distribute and/or modify this document
+ under the terms of the GNU Free Documentation License, Version 1.1
+ or any later version published by the Free Software Foundation;
+ with no Invariant Sections, no Front-Cover Texts, and no Back-Cover Texts.
+
+<P>
+
+<H1><A NAME="SECTION000200000000000000000">&nbsp;</A>
+<BR>
+1 Introduction
+</H1>
+
+<P>
+
+<H2><A NAME="SECTION000210000000000000000">&nbsp;</A>
+<BR>
+1.1 What is Cheetah?
+</H2>
+
+<P>
+Cheetah is a Python-based template engine and code-generator. It aims:
+
+<P>
+
+<UL>
+<LI><B>to make it easy to separate content, graphic design, and program code.</B>
+
+<P>
+Program code should not pollute HTML and HTML should not pollute program
+ code. Nor should content pollute the structure of complex HTML designs and
+ vice versa.
+
+<P>
+There should be no need for a designer to work through a programmer to
+ change a website's design or to use dynamic components that have already
+ been coded. Likewise, content-providers should not have to work through a
+ 'webmaster' to add new content to a site. All members of the team should
+ be able to work independently and in parallel.
+
+<P>
+A clean separation makes it easier for a team of content-providers,
+ designers, and programmers to work together without stepping on each
+ other's toes and polluting each other's work. Other advantages include
+ faster development time; HTML and program code that are easier to
+ understand and maintain; content that can be displayed in a variety of
+ non-HTML formats such as PDF; and highly modular, flexible, and reusable
+ site architectures.
+
+<P>
+</LI>
+<LI><B>to make it easy to integrate content, graphic design, and program code.</B>
+
+<P>
+While it should be easy to develop content, graphic design, and program
+ code separately, it should NOT be difficult to integrate them as part of a
+ website. There should be no difficult hoops to jump through.
+
+<P>
+It should be easy:
+
+<UL>
+<LI>for programmers to create reusable components and functions that are
+ accessible and understandable to designers.
+</LI>
+<LI>for designers to mark out placeholders for content and dynamic components
+ in their templates.
+</LI>
+<LI>for designers to soft-code aspects of their design that are either
+ repeated in several places or are subject to change.
+</LI>
+<LI>for designers to extend and customize existing templates and thus minimize
+ duplication of effort and code.
+</LI>
+<LI>and, of course, for content-providers to use the templates that
+ designers have created.
+
+</LI>
+</UL>
+
+<P>
+</LI>
+<LI><B>to provide template designers with a small set of 'Display Logic'
+ programming structures such as conditional blocks and
+ for loops</B>
+
+<P>
+Graphic designers often do tasks that would be easier, faster, and less
+ error prone if they had access to <B>conditional blocks</B> and <B>for
+ loops</B>. However, a full programming language would be overkill for these
+ simple tasks and most designers don't have the time or desire to learn one.
+
+<P>
+</LI>
+<LI><B>to be equally well-suited for HTML, SGML, XML, SQL, Postscript, form
+ email, LaTeX, or any other text-based format.</B>
+
+<P>
+Although it was designed with dynamic websites and web applications in mind,
+ Cheetah is not HTML-specific.
+
+<P>
+</LI>
+<LI><B>to achieve all these aims in a manner that is efficient, flexible, and
+ extendable.</B>
+
+<P>
+</LI>
+</UL>
+
+<P>
+Cheetah achieves these aims by:
+
+<P>
+
+<UL>
+<LI>blending the power and flexibility of Python with the simplicity of a
+ small Template Definition language that non-programmers can understand.
+
+<P>
+</LI>
+<LI>giving template designers a simple way of accessing Python variables,
+ objects, and functions in their templates.
+
+<P>
+</LI>
+<LI>providing a modular, object-orientated framework that makes it easy to
+ create and maintain large websites.
+
+<P>
+</LI>
+<LI>compiling 'Template Definitions' into native Python code at startup.
+ Thereafter this code is executed for each request. This approach is
+ dramatically faster than the string substitution approach used by many
+ templating engines.
+
+<P>
+</LI>
+<LI>providing a very simple, yet powerful, caching mechanism that can
+ significantly increase the responsiveness of a dynamic website.
+
+<P>
+</LI>
+</UL>
+
+<P>
+
+<H2><A NAME="SECTION000220000000000000000">&nbsp;</A>
+<BR>
+1.2 Why is it called Cheetah?
+</H2>
+
+<P>
+Cheetah is fast, flexible, agile and graceful - like its namesake.
+
+<P>
+
+<H2><A NAME="SECTION000230000000000000000">&nbsp;</A>
+<BR>
+1.3 Who developed Cheetah?
+</H2>
+
+<P>
+Cheetah is one of several templating frameworks that grew out of a 'templates'
+thread on the 'Webware For Python' email list. Tavis Rudd, Mike Orr, Chuck
+Esterbrook, Ian Bicking and Tom Schwaller are the core developers.
+
+<P>
+
+<H2><A NAME="SECTION000240000000000000000">&nbsp;</A>
+<BR>
+1.4 How mature is Cheetah?
+</H2>
+
+<P>
+Cheetah is alpha/beta software as this User's Guide is incomplete and several aspects
+of the design are still subject to change. However, it has been tested
+extensively and has few known issues. We are hoping to release production
+version 1.0 in the summer of 2001.
+
+<P>
+Here's a summary of known issues and aspects of the design that are in flux.
+
+<UL>
+<LI>The #include directive is not working with relative path file includes
+ when used with Webware. This should be resolved soon.
+</LI>
+<LI>The #include directive might be reworked to monitor for changes in the
+ included file at run-time. It currently does the include once-off at
+ compile-time.
+</LI>
+<LI>The implementation of $placeholders(WithArgstrings) needs to be fleshed
+ out to handle nesting.
+</LI>
+</UL>
+
+<P>
+
+<H2><A NAME="SECTION000250000000000000000">&nbsp;</A>
+<BR>
+1.5 Where can I get releases?
+</H2>
+
+<P>
+Cheetah releases can be downloaded from
+<a class="url" href="http://CheetahTemplate.sourceforge.net">http://CheetahTemplate.sourceforge.net</a>
+<P>
+
+<H2><A NAME="SECTION000260000000000000000">&nbsp;</A>
+<BR>
+1.6 Where can I get news?
+</H2>
+
+<P>
+News and updates can be obtained from the the Cheetah website:
+<a class="url" href="http://CheetahTemplate.sourceforge.net">http://CheetahTemplate.sourceforge.net</a>
+<P>
+Cheetah discussions take place on the list
+<span class="email">cheetahtemplate-discuss@lists.sourceforge.net</span>.
+
+<P>
+If you encounter difficulties, or are unsure about how to do something,
+please post a detailed message to the list.
+
+<P>
+
+<H2><A NAME="SECTION000270000000000000000">&nbsp;</A>
+<BR>
+1.7 How can I contribute?
+</H2>
+
+<P>
+Cheetah is the work of many volunteers. If you use Cheetah please share your
+experiences, tricks, customizations, and frustrations.
+
+<P>
+
+<H3><A NAME="SECTION000271000000000000000">
+1.7.1 Bug reports and patches</A>
+</H3>
+
+<P>
+If you think there is a bug in Cheetah, send a message to the email list
+with the following information:
+
+<P>
+
+<OL>
+<LI>a description of what you were trying to do and what happened
+</LI>
+<LI>all tracebacks and error output
+</LI>
+<LI>your version of Cheetah
+</LI>
+<LI>your version of Python
+</LI>
+<LI>your operating system
+</LI>
+<LI>whether you have changed anything in the Cheetah installation
+</LI>
+</OL>
+
+<P>
+
+<H3><A NAME="SECTION000272000000000000000">
+1.7.2 Example sites and tutorials</A>
+</H3>
+If you're developing a website with Cheetah, please send a link to the
+email list so we can keep track of Cheetah sites. Also, if you discover
+new and interesting ways to use Cheetah please share your experience and
+write a quick tutorial about your technique.
+
+<P>
+
+<H3><A NAME="SECTION000273000000000000000">
+1.7.3 Macro libraries</A>
+</H3>
+We hope to build up a framework of macros libraries (see section
+<A HREF="users_guide.html#macros.libraries">5.4</A>) to distribute with Cheetah and would appreciate
+any contributions.
+
+<P>
+
+<H3><A NAME="SECTION000274000000000000000">
+1.7.4 Test cases</A>
+</H3>
+Cheetah is packaged with a regression testing suite that is run with each
+new release to ensure that everything is working as expected and that recent
+changes haven't broken anything. The test cases are in the Cheetah.Tests
+module. If you find a reproduceable bug please consider writing a test case
+that will pass only when the bug is fixed. Send any new test cases to the email
+list with the subject-line ``new test case for Cheetah.''
+
+<P>
+
+<H3><A NAME="SECTION000275000000000000000">
+1.7.5 Publicity</A>
+</H3>
+Help spread the word ... recommend it to others, write articles about it, etc.
+
+<P>
+
+<H2><A NAME="SECTION000280000000000000000">&nbsp;</A>
+<BR>
+1.8 Acknowledgements
+</H2>
+
+<P>
+We'd like to thank the following people for contributing valuable advice, code
+and encouragement: Geoff Talvola, Jay Love, Terrel Shumway, Sasa Zivkov, Arkaitz
+Bitorika, Jeremiah Bellomy, Baruch Even, Paul Boddie, Stephan Diehl, and Geir
+Magnusson.
+
+<P>
+The Velocity, WebMacro and Smarty projects provided inspiration and design
+ideas. Cheetah has benefited from the creativity and energy of their
+developers. Thank you.
+
+<P>
+
+<H2><A NAME="SECTION000290000000000000000">&nbsp;</A>
+<BR>
+1.9 License
+</H2>
+
+<P>
+Cheetah is released for unlimited distribution under the terms of the
+Python license.
+
+<H1><A NAME="SECTION000300000000000000000">&nbsp;</A>
+<BR>
+2 Getting Started
+</H1>
+
+<P>
+
+<H2><A NAME="SECTION000310000000000000000">
+2.1 Requirements</A>
+</H2>
+Cheetah requires Python release 2.0 or greater and should run on any
+operating system that Python 2.0 runs on.
+
+<P>
+
+<H2><A NAME="SECTION000320000000000000000">
+2.2 Installation</A>
+</H2>
+
+<P>
+To install Cheetah for a single user:
+
+<OL>
+<LI>copy the 'Cheetah' sub-directory to a location that is in the user's
+ PYTHON_PATH
+</LI>
+</OL>
+
+<P>
+To install Cheetah for system-wide use:
+
+<OL>
+<LI>on POSIX systems (AIX, Solaris, Linux, IRIX, etc.) become the 'root' user
+ and run: python ./setup.py install
+
+<P>
+</LI>
+<LI>On non-POSIX systems, such as Windows NT, login as an administrator and
+ type this at the command-line: python setup.py install
+</LI>
+</OL>
+
+<P>
+On POSIX systems, the system-wide installation will also install the Cheetah's
+command-line compiler program, TScompile, to a system-wide executable path such as
+/usr/local/bin.
+
+<P>
+
+<H2><A NAME="SECTION000330000000000000000">
+2.3 Testing your installation</A>
+</H2>
+You can run the test suite to insure that your installation is correct by
+following these steps:
+
+<OL>
+<LI>CD into the directory ./Cheetah
+</LI>
+<LI>type: <code>python Tests.py</code>
+</LI>
+</OL>
+
+<P>
+If any of the tests fail please send a message to the email list with a copy of
+the test output and the following details about your installation:
+
+<P>
+
+<OL>
+<LI>your version of Cheetah
+</LI>
+<LI>your version of Python
+</LI>
+<LI>your operating system
+</LI>
+<LI>whether you have changed anything in the Cheetah installation
+</LI>
+</OL>
+
+<P>
+
+<H2><A NAME="SECTION000340000000000000000">
+2.4 Quickstart tutorial</A>
+</H2>
+This tutorial briefly introduces the basic usage of Cheetah. See the
+following chapters for more detailed explanations.
+
+<P>
+<B>This tutorial will be fleshed out further at later date.</B>
+
+<P>
+The core of Cheetah is the <code>Template</code> class in the <code>Cheetah.Template</code>
+module. The following example shows how to use the <code>Template</code> class from an
+interactive Python session. Lines prefixed with <code>&#187;&gt;</code> and <code>...</code> are
+user input. The remaining lines are Python output.
+
+<P>
+<dl><dd><pre class="verbatim">
+&gt;&gt;&gt; from Cheetah.Template import Template
+&gt;&gt;&gt; templateDef = """
+... &lt;HTML&gt;
+... &lt;HEAD&gt;&lt;TITLE&gt;$title&lt;/TITLE&gt;&lt;/HEAD&gt;
+... &lt;BODY&gt;
+... $contents
+... &lt;/BODY&gt;
+... &lt;/HTML&gt;"""
+&gt;&gt;&gt; nameSpace = {'title': 'Hello World Example', 'contents': 'Hello World!'}
+&gt;&gt;&gt; templateObj = Template(templateDef, nameSpace)
+&gt;&gt;&gt; print templateObj
+
+&lt;HTML&gt;
+&lt;HEAD&gt;&lt;TITLE&gt;Hello World Example&lt;/TITLE&gt;&lt;/HEAD&gt;
+&lt;BODY&gt;
+Hello World!
+&lt;/BODY&gt;
+&lt;/HTML&gt;
+&gt;&gt;&gt; print templateObj # templateObj can be printed as many times as you need
+
+&lt;HTML&gt;
+&lt;HEAD&gt;&lt;TITLE&gt;Hello World Example&lt;/TITLE&gt;&lt;/HEAD&gt;
+&lt;BODY&gt;
+Hello World!
+&lt;/BODY&gt;
+&lt;/HTML&gt;
+</pre></dl>
+
+<P>
+
+<H1><A NAME="SECTION000400000000000000000">&nbsp;</A>
+<BR>
+3 Template Objects
+</H1>
+
+<P>
+
+<H2><A NAME="SECTION000410000000000000000">
+3.1 The Template class</A>
+</H2>
+The Template class is the heart of Cheetah. It parses and compiles
+Template Definitions into python code and serves the filled template output to
+any client that requests it.
+
+<P>
+
+<H2><A NAME="SECTION000420000000000000000">
+3.2 Constructing Template objects</A>
+</H2>
+
+<P>
+
+<H2><A NAME="SECTION000430000000000000000">
+3.3 Using Template objects</A>
+</H2>
+
+<P>
+
+<H2><A NAME="SECTION000440000000000000000">
+3.4 Modifying Template objects</A>
+</H2>
+
+<P>
+
+<H1><A NAME="SECTION000500000000000000000">
+4 The Template Definition Language</A>
+</H1>
+
+<P>
+<B>Template definitions</B> are text strings, or files, that have been marked up
+with Cheetah tags for special processing. Cheetah has 2 types of tags:
+
+<P>
+
+<OL>
+<LI><B>placeholders</B>: for marking areas of the template that should be
+ replaced with something.
+
+<P>
+Placeholders begin with a dollar sign (<code>$varName</code>).
+
+<P>
+</LI>
+<LI><B>directives</B>: for everything else:
+
+ <OL>
+<LI><B>raw text</B> for marking verbatim blocks should not be parsed for
+ Cheetah tags.
+</LI>
+<LI><B>comments</B> that should not appear in the output
+</LI>
+<LI><B>includes</B> to include external text. The text can be included
+ verbatim or with parsing for Cheetah tags.
+</LI>
+<LI><B>display logic</B> such as <B>conditional blocks</B> (if-blocks) and
+ <B>for loops</B>
+</LI>
+<LI><B>blocks</B>, which are named sections of a template that can be
+ redefined (overridden) in a subclass or by template users
+</LI>
+<LI>etc.
+
+</LI>
+</OL>
+<P>
+Directives begin with a hash character (#).
+
+<P>
+</LI>
+</OL>
+
+<P>
+
+<H2><A NAME="SECTION000510000000000000000">
+4.1 Placeholder tags</A>
+</H2>
+Cheetah uses placeholder tags in the form <code>$varName</code> to mark out
+areas of the template definition that should be replaced with something. Placeholders are
+equivalent to <B>fields</B> on a form. Placeholders can be replaced with plain
+content or variables, objects and function output from Python.
+
+<P>
+The example below demonstrates the use of placeholders in an HTML document.
+
+<P>
+<dl><dd><pre class="verbatim">
+&lt;HTML&gt;
+&lt;HEAD&gt;&lt;TITLE&gt;$title&lt;/TITLE&gt;&lt;/HEAD&gt;
+&lt;BODY&gt;
+$contents
+&lt;/BODY&gt;
+&lt;/HTML&gt;
+</pre></dl>
+
+<P>
+When this template is filled in, the placeholders <code>$title</code> and
+<code>$content</code> will be replaced with the values of the variables <code>title</code>
+and <code>content</code>.
+
+<P>
+
+<H3><A NAME="SECTION000511000000000000000">
+4.1.1 Rules for placeholder names</A>
+</H3>
+
+<UL>
+<LI>Cheetah ignores all dollar signs (<code>$</code>) that are not followed
+ by a letter or an underscore. As a corollary, dollar amounts
+ (<code>$2.50</code>)are not placeholders but are instead output literally as
+ they should be. Cheetah also ignores any placeholder escaped by a
+ backslash (<code><SPAN CLASS="MATH"><IMG
+ WIDTH="12" HEIGHT="31" ALIGN="MIDDLE" BORDER="0"
+ SRC="img1.gif"
+ ALT="$ackslash$"></SPAN>$placeholderName</code>).
+
+<P>
+</LI>
+<LI>The first character of a placeholder name must be either an underscore or
+ a letter. Valid characters for the rest of the name are underscores,
+ letters, numbers and periods. These names are valid: <code>$a</code>,
+ <code>$_</code>, <code>$var</code>, <code>$_var</code>, <code>$var1</code>, <code>$_1var</code>,
+ <code>$var2_</code>, <code>$dict.key</code>, <code>$list.item</code>,
+ <code>$object.method</code>. These names are not: <code>$1</code>, <code>$var@2</code>,
+ <code>$var</code>.
+
+<P>
+</LI>
+<LI>names are case-sensitive. <code>$var</code> does not equal <code>$Var</code>
+ or <code>$vAr</code> or <code>$VAR</code>.
+
+<P>
+</LI>
+<LI>Trailing periods are ignored. Cheetah will recognize that
+ placeholder name in <code>$varName.</code> is <code>varName</code> and the period will
+ be left alone in the filled template output.
+
+<P>
+</LI>
+<LI>Placeholders can also be written in the form <code>${placeholderName}</code>.
+ This is useful for cases where there is no whitespace between the
+ placeholder and surrounding text (<code>surrounding${embeddedVar}text</code>).
+</LI>
+</UL>
+
+<P>
+
+<H3><A NAME="SECTION000512000000000000000">
+4.1.2 The searchList</A>
+</H3>
+A <B>namespace</B> is a Python dictionary that links names to values. Each
+template definition that has been loaded into a <code>Template</code> objet is
+associated with an ordered list of namespaces in which values for placeholder
+variable names can be stored. This list is called the <B>searchList</B>.
+
+<P>
+The searchList can contain one or more namespaces. In most cases only one
+namespace will be in the searchList unless you explicitly load extra ones. When
+Cheetah fills in <code>$content</code> in previous example it searchs
+sequentially through the searchList until it finds a value for <code>$content</code>.
+Thus, if three namespaces are loaded and two of them contain a value for
+<code>$content</code>, the value for <code>content</code> from the namespace that is
+closest to the start of the searchList will be returned.
+
+<P>
+If you add a Python object to the searchList, its attributes and methods will be
+accessible as placeholder names. For example, <code>myObject</code> contains
+<code>myAttrib</code> and <code>myMethod</code>. If <code>myObject</code> is added to the
+searchList, <code>$myAttrib</code> and <code>$myMethod</code> can be used as placeholder
+names.
+
+<P>
+The default namespace in every searchList is the <code>Template</code> object itself.
+This means that any attributes or methods that are added to classes that inherit
+from <code>Template</code> can be accessed in templates via <code>$placeholders</code>. New
+namespaces can be added to the searchList at any time using the
+<code>Template.addToSearchList()</code> method. See section <A HREF="users_guide.html#webware">6</A> for more
+information on how to use namespaces and the searchList.
+
+<P>
+
+<H3><A NAME="SECTION000513000000000000000">
+4.1.3 Placeholder values</A>
+</H3>
+
+<P>
+Placeholder names can map to Python text strings, numbers, dictionaries, lists
+(arrays), functions, objects, or even nested Cheetah templates. If the value is
+not a String, Cheetah will call <code>str()</code> on it to obtain a string
+representation that can be inserted into the template. But if the value is a
+callable object (e.g., a function or a method), Cheetah will first call it and
+then call <code>str()</code> on the result. You may include or omit the () after
+callable placeholders: Cheetah will call it (without arguments) in either case.
+
+<P>
+<dl><dd><pre class="verbatim">
+&lt;HTML&gt;
+&lt;HEAD&gt;&lt;TITLE&gt;$title&lt;/TITLE&gt;&lt;/HEAD&gt;
+&lt;BODY&gt;
+
+$aString ## these names are chosen to indicate what
+$aNumber ## type of value they map to. They could be
+$aList ## any valid placeholder name.
+$aDictionary
+$aFunction
+$anObject
+
+&lt;/BODY&gt;
+&lt;/HTML&gt;
+</pre></dl>
+
+<P>
+Note that you don't need to include () after <code>$aFunction</code>. Cheetah
+will recognize callable variables like functions and methods, call them and
+interpolate their return value in the filled template output.
+
+<P>
+You cannot pass arguments to a function or method called this way. (Actually,
+you can, but this feature may be removed soon, because it introduces too much
+complexity that is better done in Python outside the template.) The function or
+method must either accept no arguments, or all its arguments must have default
+values. Macro calls (see the Macros section) can accept arguments, but we
+aren't talking about macros here.
+
+<P>
+Templates can be nested. It is valid to embed a placeholder name that maps to
+another <code>Template</code> object. The filled output of the nested template will be
+interpolated into the top-level template's output. There is no limit on the
+depth of template nesting.
+
+<P>
+
+<H3><A NAME="SECTION000514000000000000000">
+4.1.4 Placeholders with no value defined</A>
+</H3>
+If there is no value defined in the searchList for a placeholder name,
+Cheetah will search for an leading underscore version of the name. For
+example, if it can't find <code>$varName</code> it will attempt to find
+<code>$_varName</code>. If that fails, Cheetah will include the placeholder
+tag verbatim in the filled template output.
+
+<P>
+This behaviour can be customized and a default value can be set for names
+that are not found. See section <A HREF="users_guide.html#customizing">7</A> for more details.
+
+<P>
+
+<H3><A NAME="SECTION000515000000000000000">
+4.1.5 Dotted notation</A>
+</H3>
+Placeholder names can also use <B>dotted notation</B> to access entries in
+dictionaries, items in lists, and the attributes and methods of objects.
+Cheetah uses a consistent dotted notation syntax to access the contents
+of all types of containers. This is unlike Python, C++, Java, and other
+languages where dotted notation can only be used to access the attributes and
+methods of objects. <A NAME="tex2html1"
+ HREF="#foot343"><SUP>1</SUP></A>
+<P>
+<dl><dd><pre class="verbatim">
+$aDictionary.keyName ## must be a valid key of the dictionary
+$aList.3 ## must be a valid index (0-based like in Python)
+$anObject.attributeName ## must be a valid attribute name
+$anObject.method ## must be a valid method name,
+ ## leave off the parentheses on method()
+</pre></dl>
+
+<P>
+Dotted notation can be used on nested containers of any depth.
+<dl><dd><pre class="verbatim">
+$dict1.dict2.dict3.keyName ## nested dictionaries
+$anObject.nestedDict.keyName ## if the object contains a dictionary
+</pre></dl>
+
+<P>
+
+<H3><A NAME="SECTION000516000000000000000">
+4.1.6 Caching</A>
+</H3>
+
+<P>
+By default the value of each <code>$placeholder</code> is updated for each request.
+If you want to statically cache the value of the <code>$placeholder</code> upon
+startup, add an asterisk after the $ sign. <code>$var</code> becomes <code>$*var</code>.
+See the section on the <code>#cache</code> directive below for more information.
+
+<P>
+If you only need to update the value of the <code>$placeholders</code> at specific time intervals use
+this form: <code>$variable</code> becomes <code>$*15*variable</code> , where 15 is the time interval in
+minutes. The time interval can also be specified in fractions of a minute such
+as <code>$*0.5*variable</code>.
+
+<P>
+<dl><dd><pre class="verbatim">
+&lt;HTML&gt;
+&lt;HEAD&gt;&lt;TITLE&gt;$title&lt;/TITLE&gt;&lt;/HEAD&gt;
+&lt;BODY&gt;
+
+$var ## dynamic - will be reinterpolated for each request
+$*var2 ## static - will be interpolated only once at start-up
+$*5*var3 ## timed refresh - will be updated every 5 minutes.
+
+&lt;/BODY&gt;
+&lt;/HTML&gt;
+</pre></dl>
+
+<P>
+
+<H2><A NAME="SECTION000520000000000000000">
+4.2 Directive tags</A>
+</H2>
+Directives tags are used for all functionality that cannot be handled with
+simple placeholders and are enclosed in <code>#</code> and <code>/#</code>.
+Cheetah does not use HTML/XML style tags because they would be hard to
+distinguish from real HTML tags and would not be visible in rendered HTML when
+something goes wrong.
+
+<P>
+Some directives consist of a single tag while others have <B>start</B> and
+<B>end</B> tags that surround a chunk of text. End tags are written in the form
+<code>#end nameOfTheDirective/#</code>.
+
+<P>
+
+<H3><A NAME="SECTION000521000000000000000">
+4.2.1 Escaping directives</A>
+</H3>
+Directives can be escaped by placing a backslash (<SPAN CLASS="MATH"><IMG
+ WIDTH="12" HEIGHT="31" ALIGN="MIDDLE" BORDER="0"
+ SRC="img1.gif"
+ ALT="$ackslash$"></SPAN>) before them.
+Escaped directives will be printed verbatim.
+
+<P>
+
+<H3><A NAME="SECTION000522000000000000000">
+4.2.2 Tag closures: explicit and implicit</A>
+</H3>
+Directive tags can closed explicitly with <code>/#</code> or implicitly with the end
+of the line if you're feeling lazy.
+<dl><dd><pre class="verbatim">
+#block /#
+Text in the contents area of the
+block directive
+#end block /#
+</pre></dl>
+or
+<dl><dd><pre class="verbatim">
+#block
+Text in the contents area of the
+block directive
+#end block
+</pre></dl>
+
+<P>
+
+<H3><A NAME="SECTION000523000000000000000">
+4.2.3 Whitespace handling</A>
+</H3>
+
+<P>
+
+<H2><A NAME="SECTION000530000000000000000">
+4.3 Comment directives</A>
+</H2>
+
+<P>
+Comment directives are used to mark notes, explanations, and decorative text
+that should not appear in the output. There are two forms of the comment
+directive: single-line and multi-line.
+
+<P>
+All text in a template definition that lies between 2 hash characters
+(<code>##</code>) and the end of the line is treated as a single-line comment and
+will not show up in the output, unless the 2 hash characters are escaped with a
+backslash.
+<dl><dd><pre class="verbatim">
+&lt;HTML&gt;
+&lt;HEAD&gt;&lt;TITLE&gt;$title&lt;/TITLE&gt;&lt;/HEAD&gt;
+&lt;BODY&gt;
+##==================================== a decorative comment
+$contents ## an end-of-line comment
+##====================================
+&lt;/BODY&gt;
+&lt;/HTML&gt;
+</pre></dl>
+
+<P>
+Any text between <code>#*</code> and <code>*#</code> will be treated as a multi-line
+comment.
+<dl><dd><pre class="verbatim">
+&lt;HTML&gt;
+&lt;HEAD&gt;&lt;TITLE&gt;$title&lt;/TITLE&gt;&lt;/HEAD&gt;
+&lt;BODY&gt;
+#*
+ Here is some multiline
+ comment text
+*#
+##==================================== a decorative comment
+$contents ## an end-of-line comment
+##====================================
+&lt;/BODY&gt;
+&lt;/HTML&gt;
+</pre></dl>
+
+<P>
+
+<H2><A NAME="SECTION000540000000000000000">
+4.4 #raw directives</A>
+</H2>
+Any section of a template definition that is delimeted by <code>#raw</code> and
+<code>#end raw</code> will be printed verbatim without any parsing of
+$placeholders or other directives. This can be very useful for debugging or
+writing Cheetah examples and tutorials.
+
+<P>
+
+<H2><A NAME="SECTION000550000000000000000">
+4.5 #include directives</A>
+</H2>
+
+<P>
+<code>#include</code> directives are used to include text from outside the template
+definition. The text can come from <code>$placeholder</code> variables or from
+external files. The example below demonstrates use with <code>$placeholder</code>
+variables.
+
+<P>
+<dl><dd><pre class="verbatim">
+#include $myParseText
+</pre></dl>
+
+<P>
+This example demonstrates its use with external files.
+<dl><dd><pre class="verbatim">
+#include "includeFileName.txt"
+</pre></dl>
+
+<P>
+By default, included text will be parsed for Cheetah tags. The keyword
+<B>raw</B> can be used to mark the text for verbatim inclusion without any tag
+parsing.
+
+<P>
+<dl><dd><pre class="verbatim">
+#include raw $myParseText
+#include raw "includeFileName.txt"
+</pre></dl>
+
+<P>
+<code>Template</code> uses its .getFileContents(fileName) method to locate the file to
+be included. This method can be overriden in subclasses if you want to modify
+or extend its behaviour. It is possible to implement the logic for getting
+remote files such as <code>http://myserver.com/file.txt</code>.
+
+<P>
+
+<H2><A NAME="SECTION000560000000000000000">
+4.6 #cache directives</A>
+</H2>
+
+<P>
+
+<H2><A NAME="SECTION000570000000000000000">
+4.7 Display logic directives</A>
+</H2>
+
+<P>
+
+<H3><A NAME="SECTION000571000000000000000">
+4.7.1 Conditional blocks</A>
+</H3>
+
+<P>
+
+<H3><A NAME="SECTION000572000000000000000">
+4.7.2 For loops</A>
+</H3>
+
+<P>
+
+<H2><A NAME="SECTION000580000000000000000">
+4.8 #block directives</A>
+</H2>
+
+<P>
+
+<H3><A NAME="SECTION000581000000000000000">
+4.8.1 #redefine directives</A>
+</H3>
+
+<P>
+
+<H1><A NAME="SECTION000600000000000000000">&nbsp;</A>
+<BR>
+5 Macros
+</H1>
+
+<P>
+
+<H2><A NAME="SECTION000610000000000000000">&nbsp;</A>
+<BR>
+5.1 What are macros?
+</H2>
+
+<P>
+
+<H2><A NAME="SECTION000620000000000000000">&nbsp;</A>
+<BR>
+5.2 Using macros
+</H2>
+
+<P>
+
+<H2><A NAME="SECTION000630000000000000000">&nbsp;</A>
+<BR>
+5.3 Defining macros
+</H2>
+
+<P>
+
+<H2><A NAME="SECTION000640000000000000000">&nbsp;</A>
+<BR>
+5.4 Macro libraries
+</H2>
+
+<H1><A NAME="SECTION000700000000000000000">&nbsp;</A>
+<BR>
+6 Using Cheetah with Webware
+</H1>
+
+<P>
+
+<H2><A NAME="SECTION000710000000000000000">
+6.1 Background</A>
+</H2>
+Webware is a 'Python-Powered Internet Platform' that uses servlets in a manner
+similar to Java servlets. 'WebKit' is the name of Webware's application server.
+For more details please visit http://webware.sourceforge.net.
+
+<P>
+As Cheetah's core is flexible there are many ways to use it with Webware
+servlets. There are two broad categories: the Inheritance approach and
+the Containment approach. In the Inheritance approach a servlet is created that
+subclasses both the Template class and Webware's HTTPServlet class. The
+Template instance IS the servlet and its .respond() method is
+automatically called by WebKit for each request. All pre-request processing is
+handled via Cheetah.
+
+<P>
+In the Containment approach an instance of the Template class is wrapped
+up inside of a Webware servlet class. Instances of the servlet class must
+explicitly call the Template instance's .respond(), or .__str__(), method
+for each request. In this case the servlet class can handle whatever
+per-request processing needs to be done before it calls Cheetah.respond().
+
+<P>
+The Inheritance approach is the simplest and is best suited for building sites
+from scratch.
+
+<P>
+The Containment approach is slightly more complex and is best suited for use
+with existing Webware servlets. It is also ideal for cases where you wish to
+use Cheetah for only a portion of the servlet's output, such as a
+discussion-forum table at the bottom of a webpage.
+
+<P>
+
+<H2><A NAME="SECTION000720000000000000000">
+6.2 Using the Inheritance approach</A>
+</H2>
+Cheetah.Servlet provides a servlet class that can be subclassed to create
+Webware servlets, as in the trivial example below.
+
+<P>
+<dl><dd><pre class="verbatim">
+## FILE: hello_world.py ##
+template = """
+&lt;HTML&gt;
+&lt;HEAD&gt;&lt;TITLE&gt;'Hello World - Test Servlet&lt;/TITLE&gt;&lt;/HEAD&gt;
+&lt;BODY&gt;
+Hello World!
+&lt;/BODY&gt;
+&lt;/HTML&gt;
+"""
+from Cheetah.Servlet import TemplateServlet
+class hello_world(TemplateServlet):
+ def __init__(self):
+ TemplateServlet.__init__(self, template)
+</pre></dl>
+
+<P>
+TemplateServlet's constructor method (<code>TemplateServlet.__init__()</code>) adds
+the attribute dictionary of the servlet to the searchList that
+<code>$placeholder</code> variables can extracted from. Thus, attributes and methods
+of the servlet object can be interpolated into the template like this:
+
+<P>
+<dl><dd><pre class="verbatim">
+## FILE: hello_world.py ##
+template = """
+&lt;HTML&gt;
+&lt;HEAD&gt;&lt;TITLE&gt;$title&lt;/TITLE&gt;&lt;/HEAD&gt;
+&lt;BODY&gt;
+$contents
+&lt;/BODY&gt;
+&lt;/HTML&gt;
+"""
+from Cheetah.Servlet import TemplateServlet
+class hello_world(TemplateServlet):
+ title = 'Hello World - Test Servlet'
+ def __init__(self):
+ TemplateServlet.__init__(self, template)
+
+ def contents(self):
+ return 'Hello World!'
+</pre></dl>
+
+<P>
+This process can be simplified for non-programmers. All the Python wrapper code
+in these examples can be generated automatically by <code>cheetah-compile</code>, a small program
+that is installed with Cheetah. <code>cheetah-compile</code> parses Template Definitions
+written in files with the .tmpl extension and generates a Webware servlet file
+with the .py extension for each .tmpl file. Type <code>cheetah-compile</code> after
+installing Cheetah to get information on how to use it.
+
+<P>
+Here's the first example as a .tmpl file:
+
+<P>
+<dl><dd><pre class="verbatim">
+## FILE: hello_world.tmpl ##
+&lt;HTML&gt;
+&lt;HEAD&gt;&lt;TITLE&gt;Hello World - Test Servlet&lt;/TITLE&gt;&lt;/HEAD&gt;
+&lt;BODY&gt;
+Hello World!
+&lt;/BODY&gt;
+&lt;/HTML&gt;
+</pre></dl>
+
+<P>
+Here's the second example as a .tmpl file. Note that all lines that begin with
+<code>##</code> are comment lines.
+
+<P>
+<dl><dd><pre class="verbatim">
+## FILE: hello_world.tmpl ##
+##===================================
+#data
+title = 'Hello World - Test Servlet'
+def contents():
+ return 'Hello World!'
+#/data
+##===================================
+&lt;HTML&gt;
+&lt;HEAD&gt;&lt;TITLE&gt;$title&lt;/TITLE&gt;&lt;/HEAD&gt;
+&lt;BODY&gt;
+$contents
+&lt;/BODY&gt;
+&lt;/HTML&gt;
+</pre></dl>
+
+<P>
+
+<H3><A NAME="SECTION000721000000000000000">
+6.2.1 The #data directive</A>
+</H3>
+The previous example introduced the <code>#data</code> directive, a shortcut means
+of adding data into the servlet's attribute dictionary and thus into
+the Template's <B>searchList</B>. The <code>#data</code> directive can be used in
+any Template Definition, regardless of whether the definition is in a .py or
+.tmpl file.
+
+<P>
+<code>#data</code> directives can contain any valid Python code
+
+<P>
+
+<H3><A NAME="SECTION000722000000000000000">
+6.2.2 The SkeletonPage framework</A>
+</H3>
+
+<P>
+
+<H3><A NAME="SECTION000723000000000000000">
+6.2.3 PlateKit</A>
+</H3>
+Forget what you knew about PlateKit. It's undergoing some changes and is out of
+action for the time-being.
+
+<P>
+
+<H2><A NAME="SECTION000730000000000000000">
+6.3 Using the Containment approach</A>
+</H2>
+
+<P>
+
+<H2><A NAME="SECTION000740000000000000000">
+6.4 User interaction in either approach</A>
+</H2>
+
+<P>
+
+<H2><A NAME="SECTION000750000000000000000">
+6.5 Components</A>
+</H2>
+
+<P>
+
+<H3><A NAME="SECTION000751000000000000000">
+6.5.1 What are components?</A>
+</H3>
+
+<P>
+
+<H3><A NAME="SECTION000752000000000000000">
+6.5.2 Using components</A>
+</H3>
+
+<P>
+
+<H3><A NAME="SECTION000753000000000000000">
+6.5.3 Building components</A>
+</H3>
+
+<P>
+
+<H1><A NAME="SECTION000800000000000000000">&nbsp;</A>
+<BR>
+7 Customizing and extending Cheetah
+</H1>
+
+<P>
+
+<H2><A NAME="SECTION000810000000000000000">
+7.1 Custom handling of unknown placeholder names</A>
+</H2>
+
+<P>
+
+<H2><A NAME="SECTION000820000000000000000">
+7.2 Plugins</A>
+</H2>
+
+<H3><A NAME="SECTION000821000000000000000">
+7.2.1 The PSP plugin</A>
+</H3>
+
+<P>
+
+<H2><A NAME="SECTION000830000000000000000">
+7.3 Custom variable-tags</A>
+</H2>
+
+<P>
+
+<H2><A NAME="SECTION000840000000000000000">
+7.4 Custom directives</A>
+</H2>
+
+<P>
+
+<H2><A NAME="SECTION000850000000000000000">
+7.5 Custom error handlers</A>
+</H2>
+
+<P>
+
+<H2><A NAME="SECTION000860000000000000000">
+7.6 Safe delegation</A>
+</H2>
+Safe Delegation, as provided by Zope and Allaire's Spectra, is not a core aim of
+Cheetah. However, several hooks were built into Cheetah so that Safe
+Delegation can be implemented at a later date.
+
+<P>
+
+<H1><A NAME="SECTION000900000000000000000">&nbsp;</A>
+<BR>
+8 Examples
+</H1>
+
+<P>
+The Cheetah distribution comes with an 'examples' directory. Browse the
+files in this directory and its subdirectories for examples of how
+Cheetah can be used.
+
+<P>
+
+<H2><A NAME="SECTION000910000000000000000">
+8.1 Syntax examples</A>
+</H2>
+Cheetah's <code>Tests</code> module contains a large number of test cases
+that can double as examples of how the Template Definition Language works.
+To view these cases go to the base directory of your Cheetah distribution
+and open the file Cheetah/Tests.py in a text editor.
+
+<P>
+
+<H2><A NAME="SECTION000920000000000000000">
+8.2 Webware Examples</A>
+</H2>
+The 'examples' directory has a subdirectory called 'webware_examples'. It
+contains example servlets that use Webware.
+
+<P>
+A subdirectory titled 'webwareSite' contains a complete website example. This
+site is my proposal for the new Webware website. The site demonstrates the
+advanced Cheetah features such as the <code>#data</code> and <code>#redefine</code>
+directives. It also demonstrates how the TScompile program can be used to
+generate Webware .py servlet files from .tmpl Template Definition files.
+
+<P>
+
+<H1><A NAME="SECTION0001000000000000000000">
+About this document ...</A>
+</H1>
+ <strong>Cheetah Users Guide</strong>
+<p> This document was generated using the <a
+ href="http://saftsack.fs.uni-bayreuth.de/~latex2ht/">
+ <strong>LaTeX</strong>2<tt>HTML</tt></a> translator.
+</p>
+
+<p> <a
+ href="http://saftsack.fs.uni-bayreuth.de/~latex2ht/">
+ <strong>LaTeX</strong>2<tt>HTML</tt></a> is Copyright &copy;
+ 1993, 1994, 1995, 1996, 1997, <a
+ href="http://cbl.leeds.ac.uk/nikos/personal.html">Nikos
+ Drakos</a>, Computer Based Learning Unit, University of
+ Leeds, and Copyright &copy; 1997, 1998, <a
+ href="http://www.maths.mq.edu.au/~ross/">Ross
+ Moore</a>, Mathematics Department, Macquarie University,
+ Sydney.
+</p>
+
+<p> The application of <a
+ href="http://saftsack.fs.uni-bayreuth.de/~latex2ht/">
+ <strong>LaTeX</strong>2<tt>HTML</tt></a> to the Python
+ documentation has been heavily tailored by Fred L. Drake,
+ Jr. Original navigation icons were contributed by Christopher
+ Petrilli.
+</p>
+<BR><HR><H4>Footnotes</H4>
+<DL>
+<DT><A NAME="foot343">... objects.</A><A NAME="foot343"
+ HREF="users_guide.html#tex2html1"><SUP>1</SUP></A>
+<DD>Cheetah uses a Python module called
+ NameMapper to handle this style of dotted notation. NameMapper is distributed
+ as part of the Cheetah package and can be used as a stand-alone tool.
+ NameMapper was inspired by Chuck Esterbrook's NamedValueAccess module
+
+ </TD>
+ </TR>
+ </TABLE>
+
+ <IMG SRC="spacer.gif" WIDTH=1 HEIGHT=200 ALT="">
+ </TD>
+
+ <TD WIDTH=1 BGCOLOR="black"><IMG SRC="spacer.gif" WIDTH=1 HEIGHT=1 ALT=""></TD>
+ <TD WIDTH=8 BGCOLOR="#3366CC"><IMG SRC="spacer.gif" WIDTH=1 HEIGHT=1 ALT=""></TD>
+ <TD WIDTH="1" BGCOLOR="#6699FF"><IMG SRC="spacer.gif" WIDTH=1 HEIGHT=1 ALT=""></TD>
+ </TR>
+</TABLE>
+
+
+<TABLE ALIGN=center BORDER=0 CELLPADDING=0 CELLSPACING=0 WIDTH=90%>
+ <TR>
+ <TD WIDTH=1 BGCOLOR="#6699FF"><IMG SRC="spacer.gif" WIDTH=1 HEIGHT=1 ALT=""></TD>
+ <TD WIDTH=8 bgcolor="#3366CC"><IMG SRC="spacer.gif" WIDTH=1 HEIGHT=1 ALT=""></TD>
+ <TD WIDTH=900 BGCOLOR="black"><IMG SRC="spacer.gif" WIDTH=100 HEIGHT=1 ALT=""></TD>
+ <TD WIDTH=8 bgcolor="#3366CC"><IMG SRC="spacer.gif" WIDTH=1 HEIGHT=1 ALT=""></TD>
+ <TD WIDTH="1" BGCOLOR="#6699FF"><IMG SRC="spacer.gif" WIDTH=1 HEIGHT=1 ALT=""></TD>
+ </TR>
+</TABLE>
+
+<TABLE ALIGN=center BORDER=0 CELLPADDING=0 CELLSPACING=0 WIDTH=90%>
+ <TR>
+ <TD WIDTH=1 BGCOLOR="#6699FF"><IMG SRC="spacer.gif" WIDTH=1 HEIGHT=1 ALT=""></TD>
+ <TD WIDTH=900 BGCOLOR="#3366CC"><IMG SRC="spacer.gif" WIDTH=1 HEIGHT=25 ALT=""></TD>
+ <TD WIDTH="1" BGCOLOR="#6699FF"><IMG SRC="spacer.gif" WIDTH=1 HEIGHT=1 ALT=""></TD>
+ </TR>
+</TABLE>
+
+<TABLE ALIGN=center BORDER=0 CELLPADDING=0 CELLSPACING=0 WIDTH=90%>
+ <TR>
+ <TD WIDTH=1 HEIGHT=1 BGCOLOR="#6699FF"><IMG SRC="spacer.gif" WIDTH=1 HEIGHT=1 ALT=""></TD>
+ </TR>
+ <TR>
+ <TD>
+ <DIV CLASS="copyrightFooter">
+ Copyright &copy; 2001 The Cheetah Development Team. All rights reserved.
+ </DIV>
+ <BR>
+
+ <DIV CLASS="copyrightFooter" ALIGN="center">Hosted on<BR>
+ <a href=http://sourceforge.net><img
+ src=http://sourceforge.net/sflogo.php?group_id=28961&type=1"
+ alt="SourceForge Home" height=31 width=88 border=0></a>
+ </DIV>
+
+ </TD>
+ </TR>
+</TABLE>
+
+
+<BR>
+<BR>
+<BR>
+
+
+
+
+</BODY>
+</HTML>
diff --git a/examples/webware_examples/cheetahSite/learn.tmpl b/examples/webware_examples/cheetahSite/learn.tmpl
new file mode 100644
index 0000000..7ef7ff1
--- /dev/null
+++ b/examples/webware_examples/cheetahSite/learn.tmpl
@@ -0,0 +1,43 @@
+#extend SiteTemplate
+
+##============================================================================##
+#data
+
+def usersGuideTxt():
+ USERS_GUIDE_PATH = '/home/tavis/Cheetah/docs/users_guide_html/index.html'
+ txt = open(USERS_GUIDE_PATH).read()
+ start = txt.find('<div class="titlepage">')
+ end = txt.find('</DL>\\n<DIV CLASS="navigation">') - 3
+ return txt[start:end]
+
+#end data
+##============================================================================##
+
+
+##============================================================================##
+## from SiteTemplate
+#redefine locationBarContents
+ <A CLASS="locationBar" HREF="index.html">Home</A>&nbsp;&gt;&nbsp;
+ <A CLASS="locationBar" HREF="learn.html">Learn</A>
+#end redefine locationBarContents
+##============================================================================##
+
+
+##============================================================================##
+## from SiteTemplate
+#redefine leftSideBar
+ <BR>
+ <A HREF="Downloads/users_guide.pdf"><B>PDF</B></A><BR>
+ <A HREF="Downloads/users_guide.ps"><B>PostScript</B></A><BR>
+ <A HREF="Downloads/users_guide.txt"><B>Plain Text</B></A><BR>
+#end redefine leftSideBar
+##============================================================================##
+
+##============================================================================##
+## from SiteTemplate
+#redefine mainContentsCell
+
+$*usersGuideTxt
+
+#end redefine mainContentsCell
+##============================================================================##
diff --git a/examples/webware_examples/cheetahSite/sourceforge_logo.gif b/examples/webware_examples/cheetahSite/sourceforge_logo.gif
new file mode 100644
index 0000000..08589d2
--- /dev/null
+++ b/examples/webware_examples/cheetahSite/sourceforge_logo.gif
Binary files differ
diff --git a/examples/webware_examples/cheetahSite/spacer.gif b/examples/webware_examples/cheetahSite/spacer.gif
new file mode 100644
index 0000000..5bfd67a
--- /dev/null
+++ b/examples/webware_examples/cheetahSite/spacer.gif
Binary files differ
diff --git a/examples/webware_examples/hello_world.py b/examples/webware_examples/hello_world.py
new file mode 100644
index 0000000..a3f6aa5
--- /dev/null
+++ b/examples/webware_examples/hello_world.py
@@ -0,0 +1,8 @@
+templateDef = '''
+Hello World!
+'''
+
+from Cheetah.Templates.SkeletonPage import SkeletonPage
+class hello_world(SkeletonPage):
+ def initializeTemplate(self):
+ self.extendTemplate(templateDef)
diff --git a/examples/webware_examples/webwareSite/WebwareSiteTemplate.tmpl b/examples/webware_examples/webwareSite/WebwareSiteTemplate.tmpl
new file mode 100644
index 0000000..7f39859
--- /dev/null
+++ b/examples/webware_examples/webwareSite/WebwareSiteTemplate.tmpl
@@ -0,0 +1,459 @@
+#extend Cheetah.Templates.SkeletonPage
+
+##============================================================================##
+## macros for the Webware site
+
+#macro Python()
+<A HREF="http://www.python.org">Python</A>
+#end macro
+
+#macro PHP()
+<A HREF="http://www.php.net">PHP</A>
+#end macro
+
+#macro Zope()
+<A HREF="http://www.zope.org">Zope</A>
+#end macro
+
+#macro ASP()
+<A HREF="http://www.asp.net">ASP</A>
+#end macro
+
+#macro JSP()
+<A HREF="http://java.sun.com/products/jsp/">JSP</A>
+#end macro
+
+#macro ColdFusion()
+<A HREF="http://www.allaire.com">ColdFusion</A>
+#end macro
+
+#macro ASP()
+<A HREF="http://www.asp.net">ASP</A>
+#end macro
+
+#macro insetBox(width=170, boxTitle='', boxContents='')
+#cache
+<TABLE BORDER=0 CELLPADDING=0 CELLSPACING=0 WIDTH=$width>
+ <TR>
+ <TD COLSPAN=3 HEIGHT=1 BGCOLOR="$insetBoxes.titleFrameColor">#spacer()</TD>
+ </TR>
+ <TR>
+ <TD WIDTH=1 BGCOLOR="$insetBoxes.titleFrameColor">#spacer()</TD>
+ <TD WIDTH=900 HEIGHT=1 BGCOLOR="$mainTbl.outerFrameColor">#spacer()</TD> ## force full width
+ <TD WIDTH=1 BGCOLOR="$insetBoxes.titleFrameColor">#spacer()</TD>
+ </TR>
+ <TR>
+ <TD WIDTH=1 BGCOLOR="$insetBoxes.titleFrameColor">#spacer()</TD>
+ <TD WIDTH=900 HEIGHT=1 BGCOLOR="$mainTbl.innerFrameColor"> ## force full width
+ <DIV CLASS="insetBoxTitleContainer"><DIV CLASS="insetBoxTitle">$boxTitle</DIV></DIV>
+ </TD>
+ <TD WIDTH=1 BGCOLOR="$insetBoxes.titleFrameColor">#spacer()</TD>
+ </TR>
+ <TR>
+ <TD WIDTH=1 BGCOLOR="$insetBoxes.titleFrameColor">#spacer()</TD>
+ <TD WIDTH=900 HEIGHT=1 BGCOLOR="$mainTbl.outerFrameColor">#spacer()</TD> ## force full width
+ <TD WIDTH=1 BGCOLOR="$insetBoxes.titleFrameColor">#spacer()</TD>
+ </TR>
+ <TR>
+ <TD COLSPAN=3 HEIGHT=1 BGCOLOR="$insetBoxes.titleFrameColor">#spacer()</TD>
+ </TR>
+</TABLE>
+
+<TABLE VALIGN="top" WIDTH=$width BORDER=0 CELLPADDING=0 CELLSPACING=0>
+ <TR>
+ <TD WIDTH=1 bgcolor="$insetBoxes.frameColor">#spacer()</TD>
+ <TD WIDTH=900 BGCOLOR="$insetBoxes.bgcolor"> ## force to full width
+ ## now begin the contents cell of the insetBox
+#end cache
+ $boxContents
+#cache
+ ## end insetBox
+ </TD>
+ <TD WIDTH=1 BGCOLOR="$insetBoxes.frameColor">#spacer()</TD>
+ </TR>
+</TABLE>
+
+<TABLE BORDER=0 CELLPADDING=0 CELLSPACING=0 WIDTH=$width>
+ <TR>
+ <TD HEIGHT=1 BGCOLOR="$insetBoxes.frameColor">#spacer()</TD>
+ </TR>
+</TABLE>
+#end cache
+#end macro
+##============================================================================##
+
+
+
+##============================================================================##
+#data
+
+title = "Webware - The Python-Powered Internet Platform"
+siteDomainName = "webware.sourceforge.net"
+siteCopyrightName= "The Webware Development Team"
+
+
+bodyTagAttribs = {"bgcolor": "black",
+ "text":"#000033", #333333
+ }
+
+navBar = {"bgcolor":"#3366CC", #EBEBEB #E0E0E0
+ }
+
+locationBar = {"bgcolor":"#3366CC", #EBEBEB #E0E0E0
+ "frameColor":"#CCCCCC",
+ }
+
+mainTbl = {"width":"90%",
+ "align":"center",
+ "contentsBgColor":"white",
+ "outerFrameColor":"#6699FF", #FFC850 #BDD4F1
+ "innerFrameColor":"#3366CC", #0059B2
+ "innerFrameWidth":8,
+ }
+
+insetBoxes = {"width":170,
+ "frameColor":"#DDDDDD", #000033
+ "titleFrameColor":"black", #000033
+ "bgcolor":"#FAFAFA",
+ }
+
+
+_sanSerifFonts = """helvetica, arial, "lucida sans", sans-serif"""
+
+_stylesheets = {
+ "H1.mainText": {"margin":"7px 2px",
+ "font-size":"small",
+ "font-weight":"bold",
+ "font-family": _sanSerifFonts,
+ },
+ ".mainText": {"margin-left":"10px"},
+
+ ".navBarContainer": {"padding": "4px 0px", "color":"white"},
+ ".navBar, A.navBar:link, A.navBar:visited": {"font-size":"small",
+ "font-weight":"bold",
+ "color":"white",
+ "text-decoration":"none",
+ "font-family": _sanSerifFonts,
+ },
+ "A.navBar:hover, A.navBar:active": {"text-decoration":"underline"},
+
+ ".locationBarContainer": {"padding": "2px 10px"},
+ ".locationBar, A.locationBar:link, A.locationBar:visited": {"font-size":"x-small",
+ "color":"#666666",
+ "text-decoration":"none",
+ "font-family": _sanSerifFonts,
+ },
+ "A.locationBar:hover, A.locationBar:active": {"text-decoration":"underline"},
+
+ ".copyrightFooter": {"color":"white", "font-size":"9pt"},
+
+ ".insetBoxTitleContainer":{"padding": "2px 5px",
+ "color":"white",
+ "font-weight":"bold",
+ },
+ ".insetBoxTitle":{"color":"white",
+ "font-size":"x-small",
+ "font-weight":"bold",
+ "text-decoration":"none",
+ "font-family": _sanSerifFonts,
+ },
+ ".insetBoxContainer":{"padding": "2px 2px 2px 5px"},
+ ".insetBox":{"font-size":"x-small",
+ "font-family": _sanSerifFonts,
+ },
+ "H1.insetBox":{"margin": "5px 5px",
+ "padding": "0px",
+ "font-size":"x-small",
+ "font-weight":"bold",
+ "font-family": _sanSerifFonts
+ },
+
+ "P.insetBox":{"margin": "2px 5px 7px 10px",
+ "padding": "0px",
+ "font-family": _sanSerifFonts
+ },
+ "A.insetBox:link, A.insetBox:visited":{"font-size":"x-small",
+ "text-decoration":"none",
+ },
+ "A.insetBox:hover, A.insetBox:active": {"text-decoration":"underline"},
+
+ ".listItems": {"margin-bottom":"200px"},
+ }
+
+_stylesheetsOrder = ["H1.mainText",
+ ".mainText",
+ ".navBarContainer",
+ ".navBar, A.navBar:link, A.navBar:visited",
+ "A.navBar:hover, A.navBar:active",
+ ".locationBarContainer",
+ ".locationBar, A.locationBar:link, A.locationBar:visited",
+ "A.locationBar:hover, A.locationBar:active",
+ ".copyrightFooter",
+ ".insetBoxTitleContainer",
+ ".insetBoxTitle",
+ ".insetBoxContainer",
+ ".insetBox",
+ "H1.insetBox",
+ "P.insetBox",
+ "A.insetBox:link, A.insetBox:visited",
+ "A.insetBox:hover, A.insetBox:active",
+ ".listItems",
+ ]
+
+#end data
+##============================================================================##
+
+
+##============================================================================##
+## from SkeletonPage
+#redefine bodyContents
+
+#####################
+#block header
+
+#end block header
+#####################
+
+<TABLE ALIGN=$mainTbl.align BORDER=0 CELLPADDING=0 CELLSPACING=0 WIDTH=$mainTbl.width>
+ <TR>
+ <TD>
+ #####################
+ #block mainTblOuterHeader
+ <DIV ALIGN="center"><IMG SRC="webware_title_red-white_fat.gif" ALT="Webware"></DIV>
+ <DIV ALIGN="center"><IMG SRC="webware_subtitle_black.gif" ALT="The
+ Python-Powered Internet Platform"></DIV>
+ #spacer(1,5)
+
+ #end block mainTblOuterHeader
+ #####################
+ </TD>
+ </TR>
+ <TR>
+ <TD WIDTH=1 HEIGHT=1 bgcolor="$mainTbl.outerFrameColor">#spacer()</TD>
+ ## top line of the mainTbl frame
+ </TR>
+</TABLE>
+
+
+<TABLE ALIGN=$mainTbl.align BORDER=0 CELLPADDING=0 CELLSPACING=0 WIDTH=$mainTbl.width>
+ <TR>
+ <TD WIDTH=1 bgcolor="$mainTbl.outerFrameColor">#spacer()</TD>
+ <TD WIDTH=900> ## force to full width in Netscape
+ #####################
+ #block mainTblInnerHeader
+
+ ##############
+ #block navBar
+ <TABLE WIDTH=100% BGCOLOR="$navBar.bgcolor" BORDER=0 CELLPADDING=0 CELLSPACING=0>
+ <TR>
+ <TD>
+ <DIV ALIGN="center" CLASS="navBarContainer">
+ <A CLASS="navBar" HREF="index.py">Home</A>&nbsp;&nbsp;&nbsp;|&nbsp;&nbsp;&nbsp;
+ <A CLASS="navBar" HREF="learn.py">Learn</A>&nbsp;&nbsp;&nbsp;|&nbsp;&nbsp;&nbsp;
+ <A CLASS="navBar" HREF="download.py">Download</A>&nbsp;&nbsp;&nbsp;|&nbsp;&nbsp;&nbsp;
+ <A CLASS="navBar" HREF="contribute.py">Contribute</A>&nbsp;&nbsp;&nbsp;|&nbsp;&nbsp;&nbsp;
+ <A CLASS="navBar" HREF="communicate.py">Communicate</A>
+ </DIV>
+ </TD>
+ </TR>
+ </TABLE>
+ #end block navBar
+ ##############
+
+ #end block mainTblInnerHeader
+ #####################
+ </TD>
+ <TD WIDTH="1" bgcolor="$mainTbl.outerFrameColor">#spacer()</TD>
+ </TR>
+</TABLE>
+
+<TABLE ALIGN=$mainTbl.align BORDER=0 CELLPADDING=0 CELLSPACING=0 WIDTH=$mainTbl.width>
+ <TR>
+ <TD WIDTH=1 BGCOLOR="$mainTbl.outerFrameColor">#spacer()</TD>
+ <TD WIDTH=$mainTbl.innerFrameWidth bgcolor="$mainTbl.innerFrameColor">#spacer()</TD>
+ <TD WIDTH=900 HEIGHT=1 BGCOLOR="black">#spacer(100)</TD> ## force to full width in Netscape
+ <TD WIDTH=$mainTbl.innerFrameWidth bgcolor="$mainTbl.innerFrameColor">#spacer()</TD>
+ <TD WIDTH="1" BGCOLOR="$mainTbl.outerFrameColor">#spacer()</TD>
+ </TR>
+</TABLE>
+
+
+## MainTbl - contents ##
+
+<TABLE ALIGN=$mainTbl.align VALIGN="top" BORDER=0 CELLPADDING=0 CELLSPACING=0 WIDTH=$mainTbl.width>
+ <TR>
+ <TD WIDTH=1 BGCOLOR="$mainTbl.outerFrameColor">#spacer()</TD>
+ <TD WIDTH=$mainTbl.innerFrameWidth bgcolor="$mainTbl.innerFrameColor">#spacer()</TD>
+ <TD WIDTH=1 BGCOLOR="black">#spacer()</TD>
+
+ <TD BGCOLOR="$mainTbl.contentsBgColor">
+
+ ###################
+ #block locationBar
+ <TABLE WIDTH=100% BGCOLOR="#EBEBEB" BORDER=0 CELLPADDING=0 CELLSPACING=0>
+ <TR>
+ <TD>
+ <DIV CLASS="locationBarContainer">
+ ###################
+ #block locationBarContents
+ <A CLASS="locationBar" HREF="index.py">Home</A>&nbsp;&gt;&nbsp;
+ <A CLASS="locationBar" HREF="download.py">Download</A>
+ #end block locationBarContents
+ ###################
+ </DIV>
+ </TD>
+ </TR>
+ <TR>
+ <TD HEIGHT=1 BGCOLOR="$locationBar.frameColor">#spacer()</TD>
+ </TR>
+ </TABLE>
+
+ #end block locationBar
+ ###################
+
+
+
+ ######################
+ #block mainTblContents
+
+ #spacer(1,5)
+
+ <TABLE VALIGN="top" WIDTH=100% BORDER=0 CELLPADDING=10 CELLSPACING=0>
+ <TR>
+ <TD VALIGN="top">
+ ################
+ #block leftSideBar
+ <DIV ALIGN="center"><IMG SRC="onion_blue_on_white.gif" ALT=""></DIV>
+
+ <BR>
+
+ ##======================================
+ #callMacro insetBox(boxTitle="Sections")
+ #arg boxContents
+ <DIV CLASS="insetBox">
+
+ <H1 CLASS="insetBox"><A CLASS="insetBox" HREF="learn.py">Learn</A></H1>
+ <P CLASS="insetBox">
+ Manual<BR>
+ Tutorials<BR>
+ Tips and Tricks<BR>
+ Articles<BR>
+ Language Comparisons<BR>
+ Developer Information<BR>
+ </P>
+
+ <H1 CLASS="insetBox"><A CLASS="insetBox" HREF="download.py">Download</A></H1>
+ <P CLASS="insetBox">
+ Stable Releases<BR>
+ CVS Snapshots<BR>
+ Third-Party Packages<BR>
+ Browse the CVS<BR>
+ </P>
+
+ <H1 CLASS="insetBox"><A CLASS="insetBox" HREF="contribute.py">Contribute</A></H1>
+ <P CLASS="insetBox">
+ Links to Webware Sites<BR>
+ Bug Reports<BR>
+ Patches and New Code<BR>
+ Tutorials<BR>
+ Tips and Tricks<BR>
+ Articles<BR>
+ Language Comparisons<BR>
+ </P>
+
+ <H1 CLASS="insetBox"><A CLASS="insetBox" HREF="communicate.py">Communicate</A></H1>
+ <P CLASS="insetBox">
+ News<BR>
+ Mailing Lists<BR>
+ The Developers<BR>
+ Introduce Yourself<BR>
+ </P>
+
+ </DIV> ## end insetBox
+ #end arg
+ #end callMacro
+ ##======================================
+
+ #end block leftSideBar
+ ################
+
+ </TD>
+ <TD VALIGN="top" WIDTH=80%>
+ ########################
+ #block mainContentsCell
+ #end block mainContentsCell
+ ########################
+ </TD>
+ </TR>
+ </TABLE>
+
+ #spacer(1,200)
+ #end block mainTblContents
+ ######################
+ </TD>
+
+ <TD WIDTH=1 BGCOLOR="black">#spacer()</TD>
+ <TD WIDTH=$mainTbl.innerFrameWidth BGCOLOR="$mainTbl.innerFrameColor">#spacer()</TD>
+ <TD WIDTH="1" BGCOLOR="$mainTbl.outerFrameColor">#spacer()</TD>
+ </TR>
+</TABLE>
+
+
+## MainTbl - closure ##
+<TABLE ALIGN=$mainTbl.align BORDER=0 CELLPADDING=0 CELLSPACING=0 WIDTH=$mainTbl.width>
+ <TR>
+ <TD WIDTH=1 BGCOLOR="$mainTbl.outerFrameColor">#spacer()</TD>
+ <TD WIDTH=$mainTbl.innerFrameWidth bgcolor="$mainTbl.innerFrameColor">#spacer()</TD>
+ <TD WIDTH=900 BGCOLOR="black">#spacer(100)</TD> ## force to full width in Netscape
+ <TD WIDTH=$mainTbl.innerFrameWidth bgcolor="$mainTbl.innerFrameColor">#spacer()</TD>
+ <TD WIDTH="1" BGCOLOR="$mainTbl.outerFrameColor">#spacer()</TD>
+ </TR>
+</TABLE>
+
+<TABLE ALIGN=$mainTbl.align BORDER=0 CELLPADDING=0 CELLSPACING=0 WIDTH=$mainTbl.width>
+ <TR>
+ <TD WIDTH=1 BGCOLOR="$mainTbl.outerFrameColor">#spacer()</TD>
+ <TD WIDTH=900 BGCOLOR="$mainTbl.innerFrameColor">#spacer(1,25)</TD> ## force to full width in Netscape
+ <TD WIDTH="1" BGCOLOR="$mainTbl.outerFrameColor">#spacer()</TD>
+ </TR>
+</TABLE>
+
+<TABLE ALIGN=$mainTbl.align BORDER=0 CELLPADDING=0 CELLSPACING=0 WIDTH=$mainTbl.width>
+ <TR>
+ <TD WIDTH=1 HEIGHT=1 BGCOLOR="$mainTbl.outerFrameColor">#spacer()</TD>
+ </TR>
+ <TR>
+ <TD>
+ #####################
+ #block mainTblOuterFooter
+ <DIV CLASS="copyrightFooter">
+ Copyright &copy; 1999 - #currentYr() The Webware Development Team. All rights reserved.
+ </DIV>
+ <BR>
+
+ <DIV CLASS="copyrightFooter" ALIGN="center">Hosted on<BR>
+ <A HREF="http://sourceforge.net"><IMG BORDER=0 SRC="sourceforge_logo.gif" ALT="sourceforge.net"></A>
+ </DIV>
+
+ ##<DIV ALIGN="center"><IMG SRC="webware_logo.gif"></DIV>
+ ##<DIV ALIGN="center"><IMG SRC="webware_slogan_bw.gif"></DIV>
+
+ #end block mainTblOuterFooter
+ #####################
+ </TD>
+ </TR>
+</TABLE>
+
+
+#####################
+#block footer
+<BR>
+<BR>
+<BR>
+#end block footer
+#####################
+
+
+#end redefine bodyContents
+## from SkeletonPage
+##============================================================================##
diff --git a/examples/webware_examples/webwareSite/communicate.tmpl b/examples/webware_examples/webwareSite/communicate.tmpl
new file mode 100644
index 0000000..0d56a5b
--- /dev/null
+++ b/examples/webware_examples/webwareSite/communicate.tmpl
@@ -0,0 +1,29 @@
+#extend WebwareSiteTemplate
+
+##============================================================================##
+#data
+
+title = "Webware - The Python-Powered Internet Platform - Communicate"
+
+#end data
+##============================================================================##
+
+
+##============================================================================##
+## from WebwareSiteTemplate
+#redefine locationBarContents
+ <A CLASS="locationBar" HREF="index.py">Home</A>&nbsp;&gt;&nbsp;
+ <A CLASS="locationBar" HREF="communicate.py">Communicate</A>
+#end redefine locationBarContents
+## from WebwareSiteTemplate
+##============================================================================##
+
+
+##============================================================================##
+## from WebwareSiteTemplate
+#redefine mainContentsCell
+This is the "communicate" page, and it\'s blank for now!
+
+#end redefine mainContentsCell
+## from WebwareSiteTemplate
+##============================================================================##
diff --git a/examples/webware_examples/webwareSite/contribute.tmpl b/examples/webware_examples/webwareSite/contribute.tmpl
new file mode 100644
index 0000000..593e61d
--- /dev/null
+++ b/examples/webware_examples/webwareSite/contribute.tmpl
@@ -0,0 +1,29 @@
+#extend WebwareSiteTemplate
+
+##============================================================================##
+#data
+
+title = "Webware - The Python-Powered Internet Platform - Contribute"
+
+#end data
+##============================================================================##
+
+
+##============================================================================##
+## from WebwareSiteTemplate
+#redefine locationBarContents
+ <A CLASS="locationBar" HREF="index.py">Home</A>&nbsp;&gt;&nbsp;
+ <A CLASS="locationBar" HREF="contribute.py">Contribute</A>
+#end redefine locationBarContents
+## from WebwareSiteTemplate
+##============================================================================##
+
+
+##============================================================================##
+## from WebwareSiteTemplate
+#redefine mainContentsCell
+This is the "contribute" page, and it\'s blank for now!
+
+#end redefine mainContentsCell
+## from WebwareSiteTemplate
+##============================================================================##
diff --git a/examples/webware_examples/webwareSite/download.tmpl b/examples/webware_examples/webwareSite/download.tmpl
new file mode 100644
index 0000000..71485aa
--- /dev/null
+++ b/examples/webware_examples/webwareSite/download.tmpl
@@ -0,0 +1,29 @@
+#extend WebwareSiteTemplate
+
+##============================================================================##
+#data
+
+title = "Webware - The Python-Powered Internet Platform - Download"
+
+#end data
+##============================================================================##
+
+
+##============================================================================##
+## from WebwareSiteTemplate
+#redefine locationBarContents
+ <A CLASS="locationBar" HREF="index.py">Home</A>&nbsp;&gt;&nbsp;
+ <A CLASS="locationBar" HREF="download.py">Download</A>
+#end redefine locationBarContents
+## from WebwareSiteTemplate
+##============================================================================##
+
+
+##============================================================================##
+## from WebwareSiteTemplate
+#redefine mainContentsCell
+This is the download page, and it\'s blank for now!
+
+#end redefine mainContentsCell
+## from WebwareSiteTemplate
+##============================================================================##
diff --git a/examples/webware_examples/webwareSite/index.tmpl b/examples/webware_examples/webwareSite/index.tmpl
new file mode 100644
index 0000000..5c7b82b
--- /dev/null
+++ b/examples/webware_examples/webwareSite/index.tmpl
@@ -0,0 +1,138 @@
+#extend WebwareSiteTemplate
+
+###################
+#redefine locationBar
+
+#end redefine locationBar
+###################
+
+
+########################
+#redefine mainContentsCell
+
+<H1 CLASS="mainText">What is Webware?</H1>
+
+<P CLASS="mainText">
+Webware is an internet development platform powered by
+#Python(), the
+Open Source programming language developed by Guido van Rossum.</P>
+
+<H1 CLASS="mainText">What does Webware provide?</H1>
+<UL>
+ <LI>The strength of <A HREF="http://jakarta.apache.org">server-side Java</A>,
+ without its complexity.</LI>
+
+ <LI>The power of server-side <A HREF="http://perl.apache.org/">Perl</A>,
+ without its syntax.</LI>
+
+ <LI>The Python of #Zope(), without its headaches.</LI>
+ <LI>The flexibility of <A HREF="http://hoohoo.ncsa.uiuc.edu/cgi/intro.html">CGI</A>,
+ without its overhead.</LI>
+
+ <LI>The simplicity of #PHP(), without its limitations.</LI>
+ <LI>The capabilities of #ASP() and #ColdFusion(), without their
+ price tag.</LI>
+</UL>
+<P CLASS="mainText">
+...The power of the web, with the ease of #Python()!
+</P>
+
+
+<H1 CLASS="mainText">Cut the marketing-speak! I want details.</H1>
+
+<P CLASS="mainText">
+ok ... Webware provides:
+<UL>
+ <LI CLASS=".listItems">a fast, reliable, extensible, and scalable application server.</LI>
+
+ <LI CLASS=".listItems">coding in #Python(): a powerful object-orientated language.
+ Python dramatically improves developer productivity and code readible over Java, C++, and
+ other compiled languages. Furthermore, it's easier
+ to learn than other interpreted languages.</LI>
+
+ <LI CLASS=".listItems">support for several development approaches:
+ using pure Python Servlets as
+ in Java Servlets; using embedded PSP script as in ASP, PHP, JSP, and embed-Perl;
+ using templating languages as in Velocity and WebMacro;
+ and any combination of the above.</LI>
+
+ <LI CLASS=".listItems">full access to all Python modules.
+ Webware doesn't require you jump through hoops to import relevant Python modules.</LI>
+
+ <LI CLASS=".listItems">an extensive suite of add-ons and tools.</LI>
+
+ <LI CLASS=".listItems">automatic management of sessions, cookies, HTTP headers, etc.</LI>
+
+ <LI CLASS=".listItems">extensive support for XML, SOAP, XML-RPC,
+ COM, and CORBA through Python.</LI>
+
+ <LI CLASS=".listItems">structured exception handling through Python.
+ This makes Webware much suitable for large complex projects than environments
+ without structured exception handling, like PHP.</LI>
+
+ <LI CLASS=".listItems">full object persistance and caching.
+ This feature of the appserver a major advantage over solutions
+ that are embedded in the webserver process, such as PHP where
+ it costs over $2000US to purchase an add-on that only caches the
+ byte-compiled code.</LI>
+
+ <LI CLASS=".listItems">database connection persistance and pooling.</LI>
+
+ <LI CLASS=".listItems">portability.
+ It works anywhere Python works: Linux, Unixes, Windows, OS2, etc.</LI>
+
+ <LI CLASS=".listItems">web server portability.
+ It works with Apache, IIS, and most others.</LI>
+
+ <LI CLASS=".listItems">web server independence.
+ The application server runs in a separate process and can
+ even run on separate machines. One webserver can communicate with multiple application
+ servers, and vice-versa. It work crash your web-server and your webserver
+ wont crash it. Load-balancing is easy.</LI>
+
+ <LI CLASS=".listItems">protocol independence.
+ Although the application server was developed with HTTP in mind, it can be
+ extended to work with any TCP/IP based network protocol,
+ even <A HREF="http://www.blug.linux.no/rfc1149/">CPIP</A> ;-).</LI>
+
+ <LI CLASS=".listItems">a powerful template framework that separates the page logic
+ from its design/display and supports a reusable component-based design.</LI>
+
+ <LI CLASS=".listItems">interopability with other programming languages,
+ such as C, C++ and Java, through Python's extension framework.</LI>
+
+ <LI CLASS=".listItems">full source code.
+ All code is licensed under the 'Open Source' Python license, which
+ allows for unrestricted use, modification, and redistribution. Unlike the
+ GNU GPL, it allows the code to be used in commercial products.
+ </LI>
+
+ <LI CLASS=".listItems">and most importantly, an active and supportive developer community!</LI>
+
+</UL>
+</P>
+
+
+<H1 CLASS="mainText">What's with the onion?!</H1>
+<P CLASS="mainText"> Onions are
+ubiquitous. They grow almost anywhere and can be used in almost any dish. You'll
+find them from bachelor pads to haut cuisine restaurants, because they work well with
+others and are hard to botch, yet offer a surprising array of taste in the right
+hands.</P>
+
+<P CLASS="mainText">Onions are layered. Thus, they can be transformed into an
+endless range of shapes. You can cook with an entire onion, or even several, but you
+can also just slice off a few rings. </P>
+
+<P CLASS="mainText">Onions are easy to understand. You don't need expensive training
+to cook with them.</P>
+
+<P CLASS="mainText"> So it should be with internet development platforms. They
+should be hardy and work well with others; be easy to use for simple and complex
+tasks alike; should allow you to use a little or alot, depending on your tastes, and
+they shouldn't require a computer science degree to be understood. But, unlike
+onions, they shouldn't they make you cry ;-)</P>
+
+
+#end redefine mainContentsCell
+########################
diff --git a/examples/webware_examples/webwareSite/learn.tmpl b/examples/webware_examples/webwareSite/learn.tmpl
new file mode 100644
index 0000000..18bf8f4
--- /dev/null
+++ b/examples/webware_examples/webwareSite/learn.tmpl
@@ -0,0 +1,33 @@
+#extend WebwareSiteTemplate
+
+##============================================================================##
+#data
+
+title = "Webware - The Python-Powered Internet Platform - Learn"
+#end data
+#data
+def testFunc():
+ return 1234
+
+#end data
+##============================================================================##
+
+
+##============================================================================##
+## from WebwareSiteTemplate
+#redefine locationBarContents
+ <A CLASS="locationBar" HREF="index.py">Home</A>&nbsp;&gt;&nbsp;
+ <A CLASS="locationBar" HREF="learn.py">Learn Webware</A>
+#end redefine locationBarContents
+## from WebwareSiteTemplate
+##============================================================================##
+
+
+##============================================================================##
+## from WebwareSiteTemplate
+#redefine mainContentsCell
+This is the "Learn Webware" page, and it\'s blank for now!
+<BR>Testing $testFunc
+#end redefine mainContentsCell
+## from WebwareSiteTemplate
+##============================================================================##
diff --git a/examples/webware_examples/webwareSite/onion_blue_on_white.gif b/examples/webware_examples/webwareSite/onion_blue_on_white.gif
new file mode 100644
index 0000000..2e9c585
--- /dev/null
+++ b/examples/webware_examples/webwareSite/onion_blue_on_white.gif
Binary files differ
diff --git a/examples/webware_examples/webwareSite/onion_blue_on_white_large.gif b/examples/webware_examples/webwareSite/onion_blue_on_white_large.gif
new file mode 100644
index 0000000..1690cc0
--- /dev/null
+++ b/examples/webware_examples/webwareSite/onion_blue_on_white_large.gif
Binary files differ
diff --git a/examples/webware_examples/webwareSite/sourceforge_logo.gif b/examples/webware_examples/webwareSite/sourceforge_logo.gif
new file mode 100644
index 0000000..08589d2
--- /dev/null
+++ b/examples/webware_examples/webwareSite/sourceforge_logo.gif
Binary files differ
diff --git a/examples/webware_examples/webwareSite/spacer.gif b/examples/webware_examples/webwareSite/spacer.gif
new file mode 100644
index 0000000..5bfd67a
--- /dev/null
+++ b/examples/webware_examples/webwareSite/spacer.gif
Binary files differ
diff --git a/examples/webware_examples/webwareSite/webWare.gif b/examples/webware_examples/webwareSite/webWare.gif
new file mode 100644
index 0000000..cf48c95
--- /dev/null
+++ b/examples/webware_examples/webwareSite/webWare.gif
Binary files differ
diff --git a/examples/webware_examples/webwareSite/webWare_blueBG.gif b/examples/webware_examples/webwareSite/webWare_blueBG.gif
new file mode 100644
index 0000000..606e8b7
--- /dev/null
+++ b/examples/webware_examples/webwareSite/webWare_blueBG.gif
Binary files differ
diff --git a/examples/webware_examples/webwareSite/webWare_subtitle.gif b/examples/webware_examples/webwareSite/webWare_subtitle.gif
new file mode 100644
index 0000000..de314c4
--- /dev/null
+++ b/examples/webware_examples/webwareSite/webWare_subtitle.gif
Binary files differ
diff --git a/examples/webware_examples/webwareSite/webonions.gif b/examples/webware_examples/webwareSite/webonions.gif
new file mode 100644
index 0000000..40e8510
--- /dev/null
+++ b/examples/webware_examples/webwareSite/webonions.gif
Binary files differ
diff --git a/examples/webware_examples/webwareSite/webware_logo.gif b/examples/webware_examples/webwareSite/webware_logo.gif
new file mode 100644
index 0000000..58a4eb3
--- /dev/null
+++ b/examples/webware_examples/webwareSite/webware_logo.gif
Binary files differ
diff --git a/examples/webware_examples/webwareSite/webware_slogan_bw.gif b/examples/webware_examples/webwareSite/webware_slogan_bw.gif
new file mode 100644
index 0000000..8d7a41f
--- /dev/null
+++ b/examples/webware_examples/webwareSite/webware_slogan_bw.gif
Binary files differ
diff --git a/examples/webware_examples/webwareSite/webware_subtitle_black.gif b/examples/webware_examples/webwareSite/webware_subtitle_black.gif
new file mode 100644
index 0000000..491284a
--- /dev/null
+++ b/examples/webware_examples/webwareSite/webware_subtitle_black.gif
Binary files differ
diff --git a/examples/webware_examples/webwareSite/webware_subtitle_inner_frame.gif b/examples/webware_examples/webwareSite/webware_subtitle_inner_frame.gif
new file mode 100644
index 0000000..28e32dc
--- /dev/null
+++ b/examples/webware_examples/webwareSite/webware_subtitle_inner_frame.gif
Binary files differ
diff --git a/examples/webware_examples/webwareSite/webware_title_bw.gif b/examples/webware_examples/webwareSite/webware_title_bw.gif
new file mode 100644
index 0000000..338cda4
--- /dev/null
+++ b/examples/webware_examples/webwareSite/webware_title_bw.gif
Binary files differ
diff --git a/examples/webware_examples/webwareSite/webware_title_red-white.gif b/examples/webware_examples/webwareSite/webware_title_red-white.gif
new file mode 100644
index 0000000..366ddab
--- /dev/null
+++ b/examples/webware_examples/webwareSite/webware_title_red-white.gif
Binary files differ
diff --git a/examples/webware_examples/webwareSite/webware_title_red-white_fat.gif b/examples/webware_examples/webwareSite/webware_title_red-white_fat.gif
new file mode 100644
index 0000000..8f4b79a
--- /dev/null
+++ b/examples/webware_examples/webwareSite/webware_title_red-white_fat.gif
Binary files differ
diff --git a/setup.py b/setup.py
new file mode 100755
index 0000000..b3c784c
--- /dev/null
+++ b/setup.py
@@ -0,0 +1,84 @@
+#!/usr/bin/env python
+# $Id: setup.py,v 1.1 2001/06/13 03:50:36 tavis_rudd Exp $
+"""A setup module for the Cheetah package, based on the disutils module
+
+Meta-Data
+==========
+Author: Tavis Rudd <tavis@calrudd.com>
+License: This software is released for unlimited distribution under the
+ terms of the Python license.
+Version: $Revision: 1.1 $
+Start Date: 2001/03/30
+Last Revision Date: $Date: 2001/06/13 03:50:36 $
+"""
+__author__ = "Tavis Rudd <tavis@calrudd.com>"
+__version__ = "$Revision: 1.1 $"[11:-2]
+
+##################################################
+## DEPENDENCIES ##
+import os
+import os.path
+from distutils.core import setup
+from distutils.command.sdist import sdist
+
+##################################################
+## CONSTANTS & GLOBALS ##
+
+True = (1==1)
+False = (0==1)
+
+
+##################################################
+## CLASSES ##
+
+class sdist_docs(sdist):
+ """a setup command that will rebuild Users Guide"""
+ def run(self):
+ try:
+ currentDir = os.getcwd()
+ os.chdir(os.path.join(currentDir,'docs','src'))
+ os.system('make -f Makefile')
+ os.chdir(currentDir)
+ except:
+ print "The sdist_docs command couldn't rebuild the Users Guide"
+
+ sdist.run(self)
+
+
+##################################################
+## if run from the command line ##
+
+if __name__ == '__main__':
+ from Cheetah.Version import version
+
+ from Cheetah import __doc__
+ README = open('README','w')
+ README.write(__doc__)
+ README.close()
+ synopsis = __doc__.split('\n')[0]
+
+ packages = ['Cheetah',
+ 'Cheetah.Templates',
+ 'Cheetah.Plugins',
+ 'Cheetah.Macros',
+ ]
+
+ setup (name = "Cheetah",
+ author = "The Cheetah Development Team",
+ author_email = "cheetahtemplate-devel@sourceforge.net",
+ version = version,
+ license = "The Python License",
+ description = synopsis,
+ long_description = __doc__,
+ maintainer = "Tavis Rudd",
+ url = "http://www.calrudd.com/tavis",
+
+ packages = packages,
+ package_dir = {'Cheetah':'src'},
+
+ scripts = ['bin/cheetah-compile',],
+
+ cmdclass = { 'sdist_docs' : sdist_docs },
+ )
+
+
diff --git a/setup_all.py b/setup_all.py
new file mode 100755
index 0000000..c9e5edf
--- /dev/null
+++ b/setup_all.py
@@ -0,0 +1,17 @@
+#!/usr/bin/env python
+"""This version of the setup.py script is used to install Cheetah + all third_party_packages
+
+"""
+import string, os, sys
+cwd = os.getcwd()
+
+### install the main package
+execfile('setup.py')
+
+### then install all the third_party_packages
+packages=[]
+for p in packages:
+ d = os.path.join(cwd, 'third_party_packages', p)
+ os.chdir(d)
+ print "Working on", d, "(", os.getcwd(), ")"
+ os.system(sys.executable+' setup.py '+string.join(sys.argv[1:],' '))
diff --git a/src/CodeGenerator.py b/src/CodeGenerator.py
new file mode 100644
index 0000000..55b26da
--- /dev/null
+++ b/src/CodeGenerator.py
@@ -0,0 +1,851 @@
+#!/usr/bin/env python
+# $Id: CodeGenerator.py,v 1.1 2001/06/13 03:50:39 tavis_rudd Exp $
+"""Utilities, processors and filters for Cheetah's codeGenerator
+
+Cheetah's codeGenerator is designed to be extensible with plugin
+functions. This module contains the default plugins.
+
+Meta-Data
+================================================================================
+Author: Tavis Rudd <tavis@calrudd.com>
+License: This software is released for unlimited distribution under the
+ terms of the Python license.
+Version: $Revision: 1.1 $
+Start Date: 2001/03/30
+Last Revision Date: $Date: 2001/06/13 03:50:39 $
+"""
+__author__ = "Tavis Rudd <tavis@calrudd.com>"
+__version__ = "$Revision: 1.1 $"[11:-2]
+
+##################################################
+## DEPENDENCIES ##
+
+import re
+import types
+from time import time as currentTime # used in the cache refresh code
+
+# intra-package imports ...
+import NameMapper
+from Validators import \
+ validateDisplayLogicCode, \
+ validateArgStringInPlaceholderTag, \
+ validateIncludeDirective, \
+ validateMacroDirective, \
+ validateSetDirective
+
+from Delimeters import delimeters
+from Components import Component
+import Template
+from Utilities import lineNumFromPos
+##################################################
+## CONSTANTS & GLOBALS ##
+
+True = (1==1)
+False = (0==1)
+
+
+# cacheType's for $placeholders
+NO_CACHE = 0
+STATIC_CACHE = 1
+TIMED_REFRESH_CACHE = 2
+
+##################################################
+## CLASSES ##
+
+class Error(Exception):
+ pass
+
+class NoDefault:
+ pass
+
+##################################################
+## FUNCTIONS ##
+
+def swapDelims(string, delimStruct, newStartDelim, newEndDelim,
+ unescapeEscaped=True):
+ """return a copy of 'string' with the delimeters specified in delimStruct
+ replaced with the newStartDelim and newEndDelim"""
+
+ def replaceDelims(match, newStartDelim=newStartDelim,
+ newEndDelim=newEndDelim):
+
+ return newStartDelim + match.group(1) + newEndDelim
+
+ startDelim = delimStruct['start']
+ endDelim = delimStruct['end']
+ startDelimEscaped = delimStruct['startEscaped']
+ endDelimEscaped = delimStruct['endEscaped']
+ placeholderRE = delimStruct['placeholderRE']
+
+ if startDelimEscaped:
+ string = string.replace(startDelimEscaped,'<startDelimEscaped>')
+
+ if endDelimEscaped:
+ string = string.replace(endDelimEscaped,'<endDelimEscaped>')
+
+ ## do the relacement ##
+ string = placeholderRE.sub(replaceDelims, string)
+
+ if startDelimEscaped:
+ if unescapeEscaped:
+ string = string.replace('<startDelimEscaped>', startDelim)
+ else:
+ string = string.replace('<startDelimEscaped>', startDelimEscaped)
+
+ if endDelimEscaped:
+ if unescapeEscaped:
+ string = string.replace('<endDelimEscaped>', endDelim)
+ else:
+ string = string.replace('<endDelimEscaped>', endDelimEscaped)
+
+ ##
+ return string
+
+def separateTagsFromText(initialText, placeholderRE):
+ """breaks a string up into a textVsTagsList where the odd items are plain
+ text and the even items are the contents of the tags matched by
+ placeholderRE"""
+
+ textVsTagsList = []
+ position = [0,]
+
+ def subber(match, textVsTagsList=textVsTagsList,
+ position=position, initialText=initialText):
+
+ textVsTagsList.append( initialText[position[0]:match.start()] )
+ position[0] = match.end()
+ textVsTagsList.append(match.group(1))
+ return '' # dummy output that is ignored
+
+ placeholderRE.sub(subber, initialText) # ignoring the return value
+ textVsTagsList.append(initialText[position[0]:])
+ return textVsTagsList
+
+
+def processTextVsTagsList(textVsTagsList, tagProcessorFunction):
+ """loops through textVsTagsList - the output from separateTagsFromText() -
+ and filters all the tag items with the tagProcessorFunction"""
+
+ ## odd items are plain text, even ones are tags
+ processedList = textVsTagsList[:]
+ for i in range(1, len(processedList), 2):
+ processedList[i] = tagProcessorFunction(processedList[i])
+ return processedList
+
+
+
+## codeGenerator plugins ##
+
+def preProcessComments(templateObj, templateDef):
+ """cut comments out of the templateDef"""
+ def subber(match):
+ #commentString = match.group(1)
+ return ''
+
+ for regex in templateObj._settings['extDelimeters']['comments']:
+ templateDef = regex.sub(subber, templateDef)
+
+ return templateDef
+
+def preProcessSlurpDirective(templateObj, templateDef):
+ """cut #slurp's out of the templateDef"""
+ def subber(match):
+ return ''
+
+ for regex in templateObj._settings['extDelimeters']['slurp']:
+ templateDef = regex.sub(subber, templateDef)
+ return templateDef
+
+def preProcessDataDirectives(templateObj, templateDef):
+
+ def dataDirectiveProcessor(match, templateObj=templateObj):
+ """process any #data directives that are found in the template
+ extension"""
+
+ args = match.group('args').split(',')
+ contents = match.group('contents')
+
+ newDataDict = {'self':templateObj}
+ exec contents in {}, newDataDict
+
+ del newDataDict['self']
+ if not 'overwrite' in args:
+ templateObj.mergeNewTemplateData(newDataDict)
+ else:
+ for key, val in newDataDict.items():
+ setattr(templateObj,key,val)
+
+ return '' # strip the directive from the extension
+
+ for RE in templateObj._settings['extDelimeters']['dataDirective']:
+ templateDef = RE.sub(dataDirectiveProcessor, templateDef)
+ return templateDef
+
+def preProcessMacroDirectives(templateObj, templateDef):
+ """handle any inline #macro definitions """
+
+ if not hasattr(templateObj, '_macros'):
+ templateObj._macros = {}
+
+ def handleMacroDefs(match, templateObj=templateObj):
+ """process each match of the macro definition regex"""
+ macroSignature = match.group(1)
+ validateMacroDirective(templateObj, macroSignature)
+
+ firstParenthesis = macroSignature.find('(')
+ macroArgstring = macroSignature[firstParenthesis+1:-1]
+ macroName = macroSignature[0:firstParenthesis]
+
+ argStringChunks = [chunk.strip() for chunk in macroArgstring.split(',')]
+ argNamesList = [(chunk.split('='))[0] for chunk in argStringChunks]
+ #@@tr: not safe if the default args have commas or = in them!!!
+
+ macroBody = match.group(2)
+
+ def handleArgsUsedInBody(match, argNamesList=argNamesList):
+ """check each $var in the macroBody to see if it is in this macro's
+ argNamesList and needs substituting"""
+
+ if match.group(1) in argNamesList:
+ return "''' + str(" + match.group(1) + ") + '''"
+ else:
+ return match.group()
+
+ for delimStruct in \
+ templateObj._settings['codeGenerator']['coreTags']['placeholders']['delims']:
+
+ regex = delimStruct['placeholderRE']
+ macroBody = regex.sub(handleArgsUsedInBody,
+ macroBody.replace("'''","\'\'\'"))
+
+ if macroName not in vars().keys():
+ macroFuncName = macroName
+ else:
+ macroFuncName = 'macroFunction'
+
+ macroCode = "def " + macroFuncName + "(" + macroArgstring + "):\n" + \
+ " return '''" + macroBody + "'''\n"
+
+ exec macroCode in None, None
+ exec "templateObj._macros[macroName] = " + macroFuncName in vars()
+
+ return ''
+
+ for RE in templateObj._settings['extDelimeters']['macroDirective']:
+ templateDef = RE.sub(handleMacroDefs, templateDef)
+ return templateDef
+
+def preProcessLazyMacroCalls(templateObj, templateDef):
+ """Handle any calls to macros that are already defined."""
+
+ def handleMacroCalls(match, templateObj=templateObj):
+ """for each macro call that is found in the template, substitute it with
+ the macro's output"""
+
+ macroSignature = match.group(1)[1:]
+ firstParenthesis = macroSignature.find('(')
+ macroArgstring = macroSignature[firstParenthesis+1:-1]
+ macroName = macroSignature[0:firstParenthesis]
+
+ try:
+ macroArgstring = processPlaceholdersInString(templateObj, macroArgstring,
+ replaceWithValue=True,
+ wrapInQuotes=True)
+ except NameMapper.NotFound, name:
+ line = lineNumFromPos(match.string, match.start())
+ raise Error('Undeclared variable $' + str(name) + \
+ ' used in macro call #'+ macroSignature + ' on line ' +
+ str(line))
+
+
+ validateMacroDirective(templateObj, macroArgstring)
+ if macroName in templateObj._macros.keys():
+ return eval('templateObj._macros[macroName](' + macroArgstring + ')',
+ vars())
+ else:
+ raise Error('The macro ' + macroName + \
+ ' was called, but it does not exist')
+
+ for RE in templateObj._settings['extDelimeters']['lazyMacroCalls']:
+ templateDef = RE.sub(handleMacroCalls, templateDef)
+ return templateDef
+
+
+def preProcessExplicitMacroCalls(templateObj, templateDef):
+ """process the explicit callMacro directives"""
+
+ def subber(match, templateObj=templateObj):
+ macroName = match.group('macroName').strip()
+ argString = match.group('argString')
+ extendedArgString = match.group('extendedArgString')
+
+ try:
+ argString = processPlaceholdersInString(templateObj, argString,
+ replaceWithValue=True,
+ wrapInQuotes=True)
+ except NameMapper.NotFound, name:
+ line = lineNumFromPos(match.string, match.start())
+ raise Error('Undeclared variable $' + str(name) +
+ ' used in macro call #'+ macroSignature +
+ ' on line ' + str(line))
+
+ extendedArgsDict = {}
+
+ def processExtendedArgs(match, extendedArgsDict=extendedArgsDict):
+ """check each $var in the macroBody to see if it is in this macro's
+ argNamesList and needs substituting"""
+ extendedArgsDict[ match.group('argName') ] = match.group('argValue')
+ return ''
+
+ regex = templateObj._settings['extDelimeters']['callMacroArgs']
+ regex.sub(processExtendedArgs, extendedArgString)
+
+
+ fullArgString = argString
+ if fullArgString:
+ fullArgString += ', '
+ for argName in extendedArgsDict.keys():
+ fullArgString += argName + '=extendedArgsDict["' + argName + \
+ '"]' + ', '
+
+ validateMacroDirective(templateObj, fullArgString)
+ if macroName in templateObj._macros.keys():
+ return eval('templateObj._macros[macroName](' + fullArgString + ')', vars())
+ else:
+ raise Error('The macro ' + macroName + \
+ ' was called, but it does not exist')
+
+ for RE in templateObj._settings['extDelimeters']['callMacro']:
+ templateDef = RE.sub(subber, templateDef)
+
+ return templateDef
+
+
+def preProcessRawDirectives(templateObj, templateDef):
+ """extract all chunks of the template that have been escaped with the #raw
+ directive"""
+ def subber(match, templateObj=templateObj):
+ unparsedBlock = match.group(1)
+ blockID = str(id(unparsedBlock))
+ templateObj._unparsedBlocks[blockID]= unparsedBlock
+ return '<CheetahUnparsedBlock>' + blockID + \
+ '</CheetahUnparsedBlock>'
+
+ if not hasattr(templateObj, '_unparsedBlocks'):
+ templateObj._unparsedBlocks = {}
+
+ for RE in templateObj._settings['extDelimeters']['rawDirective']:
+ templateDef = RE.sub(subber, templateDef)
+ return templateDef
+
+def postProcessRawDirectives(templateObj, templateDef):
+ """reinsert all chunks that were removed in the preProcessing stage by the
+ #raw directive"""
+
+ def subber(match, templateObj=templateObj):
+ blockID = match.group(1)
+ return templateObj._unparsedBlocks[blockID]
+
+ templateDef = re.sub(r'<CheetahUnparsedBlock>(.+)' +
+ r'</CheetahUnparsedBlock>',
+ subber, templateDef)
+ return templateDef
+
+
+def preProcessIncludeDirectives(templateObj, templateDef, RE):
+ """replace any #include statements with their substitution value. This method
+ can handle includes from file (absolute paths only at the moment) and from
+ placeholders such as $getBodyTemplate"""
+
+ def subber(match, templateObj=templateObj):
+ args = match.group(1).strip()
+ # do a safety/security check on this tag
+ validateIncludeDirective(templateObj, args)
+ includeString = match.group(1).strip()
+
+ if args.split()[0] == 'raw':
+ return '<CheetahRawInclude>' + \
+ re.sub(r'(?:(?<=\A)|(?<!\\))\$',r'\$',
+ ' '.join(args.split()[1:] ) ) + \
+ '</CheetahRawInclude>'
+ else:
+
+ if args[0] == '$':
+ # it's a placeholder, substitute its value
+ if args.find('${') == -1: # it's a single $placeholder tag
+ includeString = templateObj.mapName(args[1:],
+ executeCallables=True)
+ else: # it's a ${...} placeholder tag
+ includeString = templateObj.mapName(parseArgs[2:-1],
+ executeCallables=True)
+
+ elif args.startswith('"') or args.startswith("'"):
+ fileName = args[1:-1]
+ includeString = templateObj.getFileContents( fileName )
+
+ return includeString
+
+ templateDef = RE.sub(subber, templateDef)
+ return templateDef
+
+def preProcessBlockDirectives(templateObj, templateDef):
+ """process the block directives"""
+ if not hasattr(templateObj, '_blocks'):
+ templateObj._blocks = {}
+
+ def handleBlock(blockName, startTagMatch, endTagRE,
+ templateDef=templateDef, templateObj=templateObj):
+
+ endTagMatch = endTagRE.search(templateDef)
+ blockContents = templateDef[startTagMatch.end() : endTagMatch.start()]
+
+ if not hasattr(templateObj, '_blocks'):
+ templateObj._blocks = {}
+
+ if not templateObj._blocks.has_key(blockName):
+ templateObj._blocks[blockName] = blockContents
+
+ if templateObj._settings['includeBlockMarkers']:
+ markerStart = templateObj._settings['blockMarkerStart']
+ markerEnd = templateObj._settings['blockMarkerEnd']
+
+ replaceString = markerStart[0] + blockName + markerStart[1] + \
+ '#parse $blocks.' + blockName + '/#' + \
+ markerEnd[0] + blockName + markerEnd[1]
+ else:
+ replaceString = '#include $blocks.' + blockName + '/#'
+
+ return templateDef[0:startTagMatch.start()] + replaceString + \
+ templateDef[endTagMatch.end():]
+
+ ## handle the whitespace-gobbling blocks
+
+ for startTagRE in templateObj._settings['extDelimeters']['blockDirectiveStart']:
+
+ while startTagRE.search(templateDef):
+ startTagMatch = startTagRE.search(templateDef)
+ blockName = startTagMatch.group('blockName')
+ endTagRE = re.compile(r'^[\t ]*#end block[\t ]+' + blockName +
+ r'[\t ]*(?:\r\n|\n|\Z)|'+
+ r'#end block[\t ]+' + blockName +
+ r'[\t ]*(?:/#|\r\n|\n|\Z)',
+ re.DOTALL | re.MULTILINE)
+ templateDef = handleBlock(blockName, startTagMatch, endTagRE,
+ templateDef=templateDef)
+
+ return templateDef
+
+
+def preProcessSetDirectives(templateObj, templateDef):
+ """escape $vars in the directives, so the placeholderTagProcessor doesn't
+ picked them up"""
+
+ def subber(match):
+ directive = match.group()
+ return re.sub(r'(?:(?<=\A)|(?<!\\))\$',r'\$',match.group())
+
+ for delimStruct in \
+ templateObj._settings['codeGenerator']['coreTags']['setDirective']['delims']:
+
+ regex = delimStruct['placeholderRE']
+ templateDef = regex.sub(subber, templateDef)
+
+ return templateDef
+
+def postProcessRawIncludeDirectives(templateObj, templateDef):
+ """replace any include statements with their substitution value. This
+ method can handle includes from file (absolute paths only at the moment) and
+ from $placeholders such as $getBodyTemplate"""
+
+ def subber(match, templateObj=templateObj):
+ args = match.group(1).strip()
+ # do a safety/security check on this tag
+ validateIncludeDirective(templateObj, args)
+
+ includeString = match.group(1).strip()
+
+ if args[0] == '$':
+ # it's a $placeholder tag, substitute its value
+ if args.find('${') == -1:
+ # it's a single $placeholder tag
+ includeString = templateObj.mapName(args[1:],
+ executeCallables=True)
+ else: # it's a ${...} placeholder tag
+ includeString = templateObj.mapName(args[2:-1],
+ executeCallables=True)
+ elif args.startswith('"') or args.startswith("'"):
+ fileName = args[1:-1]
+ includeString = templateObj.getFileContents( fileName )
+
+ return includeString
+
+ templateDef = re.sub(r'<CheetahRawInclude>(.+)' +
+ r'</CheetahRawInclude>',
+ subber, templateDef)
+ return templateDef
+
+
+def preProcessDisplayLogic(templateObj, templateDef):
+ """swap $ for \$ in the displayLogic, so the placeholderTagProcessor doesn't
+ picked them up"""
+
+ def subber(match):
+ return re.sub(r'(?:(?<=\A)|(?<!\\))\$',r'\$',match.group())
+
+ for delim in \
+ templateObj._settings['codeGenerator']['coreTags']['displayLogic']['delims']:
+
+ templateDef = delim['placeholderRE'].sub(subber, templateDef)
+
+ return templateDef
+
+
+## codeGenerator plugins for processing each of the token-prefixed tags ##
+
+def cacheDirectiveStartTagProcessor(templateObj, directive):
+ if not templateObj._codeGeneratorState.has_key('defaultCacheType'):
+ templateObj._codeGeneratorState['defaultCacheType'] = NoDefault
+
+ directive = directive.strip()
+
+ if not directive:
+ templateObj._codeGeneratorState['defaultCacheType'] = STATIC_CACHE
+ else:
+ templateObj._codeGeneratorState['defaultCacheType'] = TIMED_REFRESH_CACHE
+ templateObj._codeGeneratorState['cacheRefreshInterval'] = float(directive)
+ return "''"
+
+def cacheDirectiveEndTagProcessor(templateObj, directive):
+ templateObj._codeGeneratorState['defaultCacheType'] = NoDefault
+ return "''"
+
+def setDirectiveTagProcessor(templateObj, directive):
+ """generate python code from setDirective tags, and register the vars with
+ placeholderTagProcessor as local vars."""
+ validateSetDirective(templateObj, directive)
+
+ firstEqualSign = directive.find('=')
+ varName = directive[0: firstEqualSign].replace('$','').strip()
+ valueString = directive[firstEqualSign+1:]
+ valueString = processPlaceholdersInString(templateObj, valueString)
+ templateObj._localVarsList.append(varName)
+
+ indent = templateObj._settings['codeGenerator']['indentationStep']
+ if not templateObj._codeGeneratorState.has_key('indentLevel'):
+ templateObj._codeGeneratorState['indentLevel'] = \
+ templateObj._settings['codeGenerator']['initialIndentLevel']
+
+ return indent*(templateObj._codeGeneratorState['indentLevel']) + varName + \
+ "=" + valueString + "\n" + \
+ indent * templateObj._codeGeneratorState['indentLevel']
+
+def placeholderTagProcessor(templateObj, tag, convertToString=True,
+ cacheType=NoDefault, cacheRefreshInterval=15):
+ """generate the python code that will be evaluated for an embedded name
+ during each request.
+
+ By default all $vars are cached except 'components', nested templates, and
+ vars that have been set locally in for loops or with the #set directive. To
+ make the $var completely dynamic so it is reinterpolated during each request
+ use the form $*var. For timed-refresh interpolation used
+ $*[timeInterval]*var -> $*15*var. """
+
+ ## setup a reference to templateObj so $placeholders in argstrings can be eval'd here
+ self = templateObj
+
+ ## do the rest of the setup
+ if not hasattr(templateObj,'_perResponseSetupCodeChunks'):
+ templateObj._perResponseSetupCodeChunks = {}
+ if not templateObj._perResponseSetupCodeChunks.has_key('placeholders'):
+ ## setup the code to be included at the beginning of each response ##
+ indent = templateObj._settings['codeGenerator']['indentationStep'] * \
+ templateObj._settings['codeGenerator']['initialIndentLevel']
+
+ templateObj._perResponseSetupCodeChunks['placeholders'] = \
+ indent + "if self._checkForCacheRefreshes:\n"\
+ + indent * 2 + "currTime = currentTime()\n"\
+ + indent * 2 + "self._timedRefreshList.sort()\n"\
+ + indent * 2 + "if currTime >= self._timedRefreshList[0][1]:\n"\
+ + indent * 3 + " self._timedRefresh(currTime)\n"\
+ + indent + " \n" \
+ + indent + "timedRefreshCache = self._timedRefreshCache\n" \
+ + indent + "callableNames = self._callableNamesCache\n" \
+ + indent + "nestedTemplates = self._nestedTemplatesCache\n" \
+ + indent + "components = self._componentsDict\n"
+
+ ## initialize the caches, the localVarsList, and the timedRefreshList
+ templateObj._timedRefreshCache = {} # caching timedRefresh vars
+ templateObj._callableNamesCache = {} # caching name mappings that are callable
+ templateObj._nestedTemplatesCache = {} # caching references to nested templates
+ templateObj._componentsDict = {} # you get the idea...
+ templateObj._timedRefreshList = []
+ templateObj._checkForCacheRefreshes = False
+ if not hasattr(templateObj, '_localVarsList'):
+ # may have already been set by #set or #for
+ templateObj._localVarsList = []
+
+ if not templateObj._codeGeneratorState.has_key('defaultCacheType'):
+ templateObj._codeGeneratorState['defaultCacheType'] = NoDefault
+
+ ## Check for an argString in the tag ##
+ firstParenthesis = tag.find('(')
+ if firstParenthesis != -1:
+ argString = tag[firstParenthesis+1:-1]
+
+ #@@ disabled for the time-being
+ #argString = processPlaceholdersInString(templateObj, argString)
+ validateArgStringInPlaceholderTag(templateObj, argString)
+ varName = tag[0:firstParenthesis]
+ else:
+ varName = tag
+ argString = ''
+
+ ## check for caching of the $placeholder ##
+ if cacheType == NoDefault:
+ if not templateObj._codeGeneratorState['defaultCacheType'] == NoDefault:
+ cacheType = templateObj._codeGeneratorState['defaultCacheType']
+ if cacheType == TIMED_REFRESH_CACHE:
+ cacheRefreshInterval = \
+ templateObj._codeGeneratorState['cacheRefreshInterval']
+ else:
+ cacheType = NO_CACHE
+
+ if varName.find('*') != -1:
+ if re.match(r'\*[A-Za-z_]',varName.strip()):
+ # it's a static cached $placeholder
+ cacheType = STATIC_CACHE
+ varName = (varName.split('*'))[1]
+ else:
+ # timedRefresh dynamic var: $*interval*varName -> $*15*var
+ cacheType = TIMED_REFRESH_CACHE
+ cacheRefreshInterval, varName = varName[1:].split('*')
+ cacheRefreshInterval = float(cacheRefreshInterval)
+
+ if cacheType == TIMED_REFRESH_CACHE:
+ templateObj._setTimedRefresh(varName, cacheRefreshInterval)
+
+ ##deal with local vars from #set and #for directives
+ splitVarName = varName.split('.')
+ if varName in templateObj._localVarsList:
+ if argString:
+ processedTag = varName + '(' + argString + ')'
+ else:
+ processedTag = varName
+ if convertToString:
+ processedTag = 'str(' + processedTag + ')'
+ return processedTag
+
+ elif splitVarName[0] in templateObj._localVarsList:
+ if argString:
+ processedTag = 'valueForName(' + splitVarName[0] + ',"""' + \
+ '.'.join(splitVarName[1:]) + '""")(' + argString + ')'
+ else:
+ processedTag = 'valueForName(' + splitVarName[0] + ',"""' + \
+ '.'.join(splitVarName[1:]) + '""", exectuteCallables=True)'
+ if convertToString:
+ processedTag = 'str(' + processedTag + ')'
+ return processedTag
+
+
+ ## find a value for the $placeholder
+ try:
+ binding = templateObj.mapName(varName)
+ except NameMapper.NotFound:
+ return templateObj._settings['varNotFound_handler'](templateObj, tag)
+
+
+ ## generate the Python code that will evaluate to the value of the placeholder
+
+ if isinstance(binding, Component):
+ templateObj._componentsDict[varName] = binding
+ processedTag = 'components["' + varName + '"](trans, templateObj=self)'
+
+ elif (type(binding) == types.MethodType and isinstance(binding.im_self,
+ Component)):
+ # it's a method of a component
+ templateObj._componentsDict[varName] = binding
+ processedTag = 'components["' + varName + '"](trans, templateObj=self)'
+
+ elif isinstance(binding, Template.Template):
+ templateObj._nestedTemplatesCache[varName] = binding.__str__
+ processedTag = 'nestedTemplates["' + varName + '"](trans, iAmNested=True)'
+
+ elif cacheType == STATIC_CACHE:
+ if callable(binding):
+ if argString:
+ value = "'''" + str(eval('binding(' + argString + \
+ ')')).replace("'''",r"\'\'\'") + "'''"
+ else:
+ value = "'''" + str(binding()).replace("'''",r"\'\'\'") + "'''"
+ else:
+ value = "'''" + str(binding).replace("'''",r"\'\'\'") + "'''"
+ processedTag = value
+
+ elif cacheType == TIMED_REFRESH_CACHE:
+ if callable(binding):
+ if argString:
+ varName = varName + '(' + argString + ')'
+ binding = templateObj._timedRefreshCache[varName] = \
+ str(eval('binding(' + argString + ')'))
+ else:
+ binding = templateObj._timedRefreshCache[varName] = str(binding())
+ else:
+ templateObj._timedRefreshCache[varName] = str(binding)
+
+ processedTag = 'timedRefreshCache["""' + varName + '"""]'
+
+ elif callable(binding):
+ templateObj._callableNamesCache[varName] = binding
+ if argString:
+ processedTag = 'str(callableNames["' + varName + '"](' + \
+ argString + '))'
+ else:
+ if (not type(binding()) == types.StringType) and convertToString:
+ processedTag = 'str(callableNames["' + varName + '"]())'
+ else:
+ processedTag = 'callableNames["' + varName + '"]()'
+
+ else:
+ if (not type(binding) == types.StringType) and convertToString:
+ processedTag ='str( self.mapName("' + varName + '") )'
+ else:
+ processedTag ='self.mapName("' + varName + '")'
+ ##
+ return processedTag
+
+
+def displayLogicTagProcessor(templateObj, displayLogic):
+ """process display logic embedded in the template"""
+
+ settings = templateObj._settings
+ indent = settings['codeGenerator']['indentationStep']
+
+ displayLogic = displayLogic.strip()
+ validateDisplayLogicCode(templateObj, displayLogic)
+
+ if not hasattr(templateObj, '_localVarsList'):
+ # may have already been set by #set or #for
+ templateObj._localVarsList = []
+
+ if not templateObj._codeGeneratorState.has_key('indentLevel'):
+ templateObj._codeGeneratorState['indentLevel'] = \
+ settings['codeGenerator']['initialIndentLevel']
+
+ if displayLogic.lower() in \
+ settings['codeGenerator']['displayLogicblockEndings']:
+
+ templateObj._codeGeneratorState['indentLevel'] -= 1
+ outputCode = indent*templateObj._codeGeneratorState['indentLevel']
+
+ elif displayLogic.lower()[0:4] in ('else','elif'):
+ displayLogic = displayLogic.replace('else if','elif')
+
+ if displayLogic.lower()[0:4] == 'elif':
+ displayLogic = processPlaceholdersInString(templateObj, displayLogic)
+
+ outputCode = indent*(templateObj._codeGeneratorState['indentLevel']-1) + \
+ displayLogic +":\n" + \
+ indent*templateObj._codeGeneratorState['indentLevel']
+
+ elif re.match(r'if +|for +', displayLogic): # it's the start of a new block
+ templateObj._codeGeneratorState['indentLevel'] += 1
+
+ if displayLogic[0:3] == 'for':
+ ##translate this #for $i in $list/# to this #for i in $list/#
+ INkeywordPos = displayLogic.find(' in ')
+ displayLogic = displayLogic[0:INkeywordPos].replace('$','') + \
+ displayLogic[INkeywordPos:]
+
+ ## register the local vars in the loop with the templateObj ##
+ # so placeholderTagProcessor will recognize them
+ # and handle their use appropriately
+ localVars, restOfForStatement = displayLogic[3:].split(' in ')
+ localVarsList = [localVar.strip() for localVar in
+ localVars.split(',')]
+ templateObj._localVarsList += localVarsList
+
+ displayLogic = processPlaceholdersInString(templateObj, displayLogic)
+ outputCode = indent*(templateObj._codeGeneratorState['indentLevel']-1) + \
+ displayLogic + ":\n" + \
+ indent*templateObj._codeGeneratorState['indentLevel']
+
+ else: # it's a chunk of plain python code
+ outputCode = indent*(templateObj._codeGeneratorState['indentLevel']) + \
+ displayLogic + \
+ "\n" + indent*templateObj._codeGeneratorState['indentLevel']
+
+ return outputCode
+
+def processPlaceholdersInString(templateObj, string, replaceWithValue=False,
+ wrapInQuotes=False):
+ """search string for placeholders and return the python code needed to
+ access those placeholders, or the value of the placeholders if
+ replaceWithValue==True"""
+
+ def subber(match, templateObj=templateObj, replaceWithValue=replaceWithValue,
+ wrapInQuotes=wrapInQuotes):
+
+ if replaceWithValue:
+ if wrapInQuotes:
+ return '"""' + str(
+ templateObj.mapName(match.group(1),
+ executeCallables=True)
+ ) + '"""'
+ else:
+ return templateObj.mapName(match.group(1),
+ executeCallables=True)
+ else:
+ return placeholderTagProcessor(templateObj, match.group(1),
+ convertToString=False,
+ cacheType=NO_CACHE)
+
+ for delimStruct in \
+ templateObj._settings['codeGenerator']['coreTags']['placeholders']['delims']:
+
+ string = delimStruct['placeholderRE'].sub(subber, string)
+
+ return string
+
+
+## codeGenerator plugins for final filtering of the generated code ##
+
+def addPerResponseCode(templateObj, generatedCode):
+ """insert the setup code that must be executed at the beginning of each
+ request.
+
+ This code has been contributed by the tagProcessors and is stored as chunks
+ in the dictionary templateObj._perResponseSetupCodeChunks"""
+
+ if not hasattr(templateObj,'_perResponseSetupCodeChunks'):
+ return generatedCode
+
+ indent = templateObj._settings['codeGenerator']['indentationStep'] * \
+ templateObj._settings['codeGenerator']['initialIndentLevel']
+ perResponseSetupCode = ''
+ for tagProcessor, codeChunk in templateObj._perResponseSetupCodeChunks.items():
+ perResponseSetupCode += codeChunk
+
+ def insertCode(match, perResponseSetupCode=perResponseSetupCode):
+ return match.group() + perResponseSetupCode
+
+ return re.sub(r'#setupCodeInsertMarker\n', insertCode , generatedCode)
+
+
+def removeEmptyStrings(templateObj, generatedCode):
+ """filter out the empty-string entries that creep in between adjacent
+ tags"""
+
+ generatedCode = generatedCode.replace(", '''''', ",', ')
+ generatedCode = generatedCode.replace("''', '''",'')
+ generatedCode = re.sub(r"\s*outputList \+= \['''''',\]\n", '\n',
+ generatedCode)
+ return generatedCode
+
+
+## varNotFound handlers ##
+def varNotFound_echo(templateObj, tag):
+ return "'''$" + tag + "'''"
+
+def varNotFound_bigWarning(templateObj, tag):
+ return "'''" + "="*15 + "&lt;$" + tag + " could not be found&gt;" + \
+ "="*15 + "'''"
diff --git a/src/Compiler.py b/src/Compiler.py
new file mode 100644
index 0000000..1d11754
--- /dev/null
+++ b/src/Compiler.py
@@ -0,0 +1,201 @@
+#!/usr/bin/env python
+# $Id: Compiler.py,v 1.1 2001/06/13 03:50:39 tavis_rudd Exp $
+"""A command line compiler for turning Cheetah files (.tmpl) into Webware
+servlet files (.py).
+
+Meta-Data
+================================================================================
+Author: Tavis Rudd <tavis@calrudd.com>
+License: This software is released for unlimited distribution under the
+ terms of the Python license.
+Version: $Revision: 1.1 $
+Start Date: 2001/03/30
+Last Revision Date: $Date: 2001/06/13 03:50:39 $
+"""
+__author__ = "Tavis Rudd <tavis@calrudd.com>"
+__version__ = "$Revision: 1.1 $"[11:-2]
+
+##################################################
+## DEPENDENCIES ##
+
+import sys
+import re
+import os
+import getopt
+from os.path import \
+ walk as pathWalk, \
+ split as pathSplit, \
+ splitext as pathSplitext, \
+ exists
+
+from glob import glob
+
+#intra-package imports ...
+from Delimeters import delimeters
+from Version import version
+
+##################################################
+## GLOBALS & CONTANTS ##
+
+True = (1==1)
+False = (1==0)
+
+CHEETAH_EXTENSION = '.tmpl'
+SERVLET_EXTENSION = '.py'
+SERVLET_BACKUP_EXT = '.py_bak'
+
+extendDirectiveRE = delimeters['extendDirective']
+
+##################################################
+## FUNCTIONS ##
+
+def wrapTemplateCode(templateExt, name):
+ """Wrap a template definition string in the boiler-plate code needed to create a
+ Webware servlet."""
+
+ parentTemplate = [False,]
+ def extendDirectiveProcessor(match, parentTemplate=parentTemplate):
+ """process any #redefine directives that are found in the template extension"""
+ parentTemplate[0] = match.group('parent').strip()
+ return '' # strip the directive from the extension
+
+ templateExt = extendDirectiveRE.sub(extendDirectiveProcessor, templateExt)
+ parentTemplate = parentTemplate[0]
+
+ if parentTemplate:
+ parentServlet = parentTemplate.split('.')[-1]
+ servletCode = "templateExt = '''" + templateExt
+ servletCode += """'''
+from %(parentModule)s import %(parentServlet)s
+
+class %(name)s(%(parentServlet)s):
+ def initializeTemplate(self):
+ %(parentServlet)s.initializeTemplate(self)
+ self.extendTemplate(templateExt)
+
+""" % {'parentModule': parentTemplate,
+ 'parentServlet': parentServlet,
+ 'name': name
+ }
+ else:
+ servletCode = "templateExt = '''" + templateExt
+ servletCode += """'''
+from Cheetah.Servlet import TemplateServlet
+
+class %(name)s(TemplateServlet):
+ def __init__(self):
+ TemplateServlet.__init__(self, template=templateExt)
+
+""" % {'name':name}
+
+ return servletCode
+
+
+def compileDir(dirName='.', backupServletFiles=True):
+ """Compile all the Cheetah files in a directory."""
+
+ cheetahFiles = glob(dirName + '/*' + CHEETAH_EXTENSION)
+ namesMinusExt = [pathSplitext(fileName)[0] for fileName in cheetahFiles]
+
+ if backupServletFiles:
+ for i in range(len(namesMinusExt)):
+ if exists( namesMinusExt[i] + SERVLET_EXTENSION):
+ print 'backing up', namesMinusExt[i] + SERVLET_EXTENSION
+ os.rename(namesMinusExt[i] + SERVLET_EXTENSION,
+ namesMinusExt[i] + SERVLET_BACKUP_EXT)
+
+ for name in namesMinusExt:
+ compileFile(name)
+
+def compileFile(fileNameMinusExt):
+ """Compile an single Cheetah file. """
+
+ fp = open(fileNameMinusExt + CHEETAH_EXTENSION)
+ templateExt = fp.read()
+ fp.close()
+ servletName = pathSplit(fileNameMinusExt)[1]
+ servletCode = wrapTemplateCode(templateExt, servletName)
+ print 'compiled ', fileNameMinusExt + CHEETAH_EXTENSION, 'to', fileNameMinusExt + SERVLET_EXTENSION
+
+ fp = open(fileNameMinusExt + SERVLET_EXTENSION,'w')
+ fp.write(servletCode)
+ fp.close
+
+def recursiveCompile(dir='.', backupServletFiles=True):
+ """Recursively walk through a directory tree and compile Cheetah files."""
+ pending = [dir]
+ while pending:
+ dir = pending.pop()
+ ## add sub-dirs
+ for shortname in os.listdir(dir):
+ path = os.path.join(dir, shortname)
+ if os.path.isdir(path):
+ pending.append(path)
+
+ ## do it!
+ compileDir(dir, backupServletFiles=backupServletFiles)
+
+class MainProgram:
+ """A command line interface class."""
+
+ def run(self):
+ """The main program controller."""
+ try:
+ opts, args = getopt.getopt( sys.argv[1:], 'd:R:', [])
+
+ except getopt.GetoptError:
+ # print help information and exit:
+ self.usage()
+ sys.exit(2)
+
+ if not opts:
+ try:
+ fileName = args[0]
+ except:
+ self.usage()
+ sys.exit(2)
+
+ servletName = pathSplitext(pathSplit(fileName)[1])[0]
+ fp = open(fileName)
+ templateExt = fp.read()
+ fp.close()
+ print wrapTemplateCode(templateExt, servletName)
+
+ for o, a in opts:
+ if o in ('-h',):
+ self.usage()
+ sys.exit()
+ if o in ('-R',):
+ recursiveCompile(a)
+ sys.exit()
+ if o in ('-d',):
+ compileDir(a)
+ sys.exit()
+
+
+ def usage(self):
+ print \
+"""Cheetah %(version)s Command-Line Compiler by %(author)s
+
+Compiles Cheetah files (.tmpl) into Webware servlet files (.py)
+
+Usage:
+ %(scriptName)s filename ---> compile 'filename', output to stdout
+ %(scriptName)s -h ---> print this help and exit
+ %(scriptName)s -d dir ---> compile all files in dir, output to new files
+ %(scriptName)s -R dir ---> same as -d, but operates recursively on subdirs
+
+""" % {'scriptName':sys.argv[0],
+ 'version':version,
+ 'author':'Tavis Rudd',
+ }
+
+
+
+##################################################
+## if run from the command line ##
+if __name__ == '__main__':
+
+ MainProgram().run()
+
+
diff --git a/src/Components.py b/src/Components.py
new file mode 100644
index 0000000..629ccc4
--- /dev/null
+++ b/src/Components.py
@@ -0,0 +1,39 @@
+#!/usr/bin/env python
+# $Id: Components.py,v 1.1 2001/06/13 03:50:39 tavis_rudd Exp $
+"""Components that can be used with the Cheetah component framework
+
+See the Cheetah User's Guide for more information on components.
+
+Meta-Data
+================================================================================
+Author: Tavis Rudd <tavis@calrudd.com>
+License: This software is released for unlimited distribution under the
+ terms of the Python license.
+Version: $Revision: 1.1 $
+Start Date: 2001/03/30
+Last Revision Date: $Date: 2001/06/13 03:50:39 $
+"""
+__author__ = "Tavis Rudd <tavis@calrudd.com>"
+__version__ = "$Revision: 1.1 $"[11:-2]
+
+
+##################################################
+## CLASSES ##
+
+class Component:
+ """an abstract base-class for Cheetah components"""
+
+ def __call__(self, transaction=None, templateObj=None):
+ """this method is called by the Cheetah when a component is embedded
+ in a template. Each component must reimplement this method.
+
+ In order to allow command-line debugging, the component should be equiped with
+ default settings to handle cases where the transaction==None. The code below
+ is a silly example"""
+
+ if transaction:
+ return 'component called: ' + str( transaction.request().fields() )
+ else:
+ return 'component called'
+
+
diff --git a/src/Delimeters.py b/src/Delimeters.py
new file mode 100644
index 0000000..e674fcc
--- /dev/null
+++ b/src/Delimeters.py
@@ -0,0 +1,248 @@
+#!/usr/bin/env python
+# $Id: Delimeters.py,v 1.1 2001/06/13 03:50:39 tavis_rudd Exp $
+"""A dictionary of delimeter regular expressions that are used in Cheetah
+
+Meta-Data
+================================================================================
+Author: Tavis Rudd <tavis@calrudd.com>
+License: This software is released for unlimited distribution under the
+ terms of the Python license.
+Version: $Revision: 1.1 $
+Start Date: 2001/03/30
+Last Revision Date: $Date: 2001/06/13 03:50:39 $
+"""
+__author__ = "Tavis Rudd <tavis@calrudd.com>"
+__version__ = "$Revision: 1.1 $"[11:-2]
+
+
+##################################################
+## DEPENDENCIES ##
+
+import re
+
+##################################################
+### CONSTANTS & GLOBALS ###
+
+delimeters = {
+ # A library of delimStructs that can be used for tagsets
+ # Each delimStruct contains the following items:
+ # start, startEscaped, end, endEscaped, and placeholderRE
+
+ '$':{'start': r'$',
+ 'startEscaped': r'\$',
+ 'end': None,
+ 'endEscaped': None,
+ 'placeholderRE': re.compile(r"(?:(?<=\A)|(?<!\\))\$((?:\*|(?:\*[0-9\.]*\*)){0,1}" +
+ r"[A-Za-z_](?:[A-Za-z0-9_\.]*[A-Za-z0-9_]+)*" +
+ r"(?:\(.*?\))*)",
+ re.DOTALL)
+ },
+
+ '${,}':{'start': r'${',
+ 'startEscaped': r'\${',
+ 'end': r'}',
+ 'endEscaped': None,
+ 'placeholderRE': re.compile(r"(?:(?<=\A)|(?<!\\))\${(.+?)}",re.DOTALL)
+ },
+
+ '$[,]':{'start': r'$[',
+ 'startEscaped': r'\$[',
+ 'end': r']',
+ 'endEscaped': None,
+ 'placeholderRE': re.compile(r"\$\[(.+?)\]",re.DOTALL)
+ },
+
+ '$(,)':{'start': r'$(',
+ 'startEscaped': r'\$(',
+ 'end': r')',
+ 'endEscaped': None,
+ 'placeholderRE': re.compile(r"\$\((.+?)\)",re.DOTALL)
+ },
+
+ '[%,%]':{'start': r'[%',
+ 'startEscaped': r'\[%',
+ 'end': r'%]',
+ 'endEscaped': r'\%]',
+ 'placeholderRE': re.compile(r"\[%(.+?)%\]",re.DOTALL)
+ },
+
+ '{,}':{'start': r'{',
+ 'startEscaped': r'\{',
+ 'end': r'}',
+ 'endEscaped': r'\}',
+ 'placeholderRE': re.compile(r"{(.+?)}",re.DOTALL)
+ },
+
+ 'xml':{'start': r'<Cheetah>',
+ 'startEscaped': '<\Cheetah>',
+ 'end': r'</Cheetah>',
+ 'endEscaped': r'<\/Cheetah>',
+ 'placeholderRE': re.compile(r"<Cheetah>(.+?)</Cheetah>",
+ re.DOTALL)
+ }, # the tags must be exactly as shown here, no spaces
+
+ '<%,%>':{'start': r'<%',
+ 'startEscaped': r'<\%',
+ 'end': r'%>',
+ 'endEscaped': r'\%>',
+ 'placeholderRE': re.compile(r"<%(.+?)%>",re.DOTALL)
+ },
+
+ '<#,#>':{'start': r'<#',
+ 'startEscaped': r'\<#',
+ 'end': r'#>',
+ 'endEscaped': r'\#>',
+ 'placeholderRE': re.compile(r"<#(.*?)#>",re.DOTALL)
+ },
+
+
+ # the suffix _gobbleWS stands for gobble whitespace - any directive on a
+ # line by itself will have all preceeding and trailing WS on that line
+ # gobbled up with the directive
+
+ 'slurpDirective_gobbleWS': re.compile(r'(?:\A|^)[\t ]*#slurp[\t ]*' +
+ r'(?:\r\n|\n|\Z)',
+ re.MULTILINE),
+ 'slurpDirective': re.compile(r'(?:(?<=\A)|(?<!\\))#slurp[\t ]*(?:\r\n|\n|\Z)'),
+
+ 'singleLineComment':re.compile(r'(?:\A|^)[\t ]*##(.*?)\n|' +
+ r'(?:(?<=\A)|(?<!\\))##(.*?)$', #this one doesn't gobble the \n !!!
+ re.MULTILINE),
+
+ 'multiLineComment': re.compile(r'(?:(?<=\A)|(?<!\\))#\*' +
+ r'(.*?)' +
+ r'\*#',
+ re.DOTALL | re.MULTILINE),
+
+ 'displayLogic_gobbleWS':{'start': r'^#',
+ 'startEscaped': None,
+ 'end': r'\n',
+ 'endEscaped': None,
+ 'placeholderRE': re.compile(r'(?:\A|^)[\t ]*#(' +
+ r'if[\t ]+[^(?:/#)]+?|' +
+ r'else[\t ]*?|' +
+ r'else[\t ]if[\t ]+[^(?:/#)]+?|' +
+ r'elif[\t ]+[^(?:/#)]+?|' +
+ r'for[\t ][^(?:/#)]+?|' +
+ r'end if|' +
+ r'end for|' +
+ r')[\t ]*(?:\n|\r\n|\Z)',
+ re.MULTILINE
+ )
+ },
+
+ 'displayLogic':{'start': r'#',
+ 'startEscaped': None,
+ 'end': r'/#',
+ 'endEscaped': r'\/#',
+ 'placeholderRE': re.compile(r'(?:(?<=\A)|(?<!\\))#(' +
+ r'if[\t ]+.+?|' +
+ r'else[\t ]*?|' +
+ r'else[\t ]if[\t ]+.+?|' +
+ r'elif[\t ]+.+?|' +
+ r'for[\t ].+?|' +
+ r'end if|' +
+ r'end for|' +
+ r')[\t ]*(?:/#|\n|\r\n|\Z)',
+ re.MULTILINE
+ )
+ },
+
+ 'setDirective':{'start': r'#set',
+ 'startEscaped': r'\#set',
+ 'end': r'\n',
+ 'endEscaped': None,
+ 'placeholderRE': re.compile(r'(?:(?<=\A)|(?<!\\))#set[\t ]+(.+?)(?:/#|\r\n|\n|\Z)')
+ },
+
+ 'cacheDirectiveStartTag':{'start': r'#cache',
+ 'startEscaped': r'\#cache',
+ 'end': r'\n',
+ 'endEscaped': None,
+ 'placeholderRE': re.compile(r'(?:(?<=\A)|(?<!\\))' +
+ r'#cache(.*?)(?:/#|\r\n|\n|\Z)')
+ },
+
+ 'cacheDirectiveEndTag':{'start': r'#cache',
+ 'startEscaped': r'\#cache',
+ 'end': r'\n',
+ 'endEscaped': None,
+ 'placeholderRE': re.compile(r'(?:(?<=\A)|(?<!\\))' +
+ r'#end cache(.*?)(?:/#|\r\n|\n|\Z)')
+ },
+
+ ## The following directive delimeters are not intended to be used in the same manner
+ # as the rest of the delim structs above. The placeholderRE is the only item of
+ # interest here as they are only used in the pre/post-processing stages.
+
+ 'includeDirective_gobbleWS': re.compile(r'(?:\A|^)[\t ]*#include[\t ]+' +
+ r'([^(?:/#)]+?)(?:\r\n|\n|\Z)',
+ re.MULTILINE),
+ 'includeDirective': re.compile(r'(?:(?<=\A)|(?<!\\))#include[\t ]+(.+?)(?:/#|\r\n|\n|\Z)'),
+
+
+ ## no gobbleWS for stop and restart directives!!!
+ # manage this explicitly if you need
+ 'rawDirective': re.compile(r'(?:(?<=\A)|(?<!\\))#raw[\t ]*(?:/#|\r\n|\n|\Z)(.*?)' +
+ r'(?:(?:#end raw[\t ]*(?:/#|\r\n|\n)|$)|\Z)',
+ re.DOTALL),
+
+ ## there is also no gobbleWS for macro defs.
+ # They must be on lines by themselves!!!
+ # @@doc this
+ 'macroDirective': re.compile(r'(?:\A|^)[\t ]*#macro[\t ]+' +
+ r'(.+?)(?:/#|\r\n|\n)(.*?)' +
+ r'(?:\r\n|\n)[\t ]*#end macro[\t ]*(?:\r\n|\n|\Z)',
+ re.DOTALL | re.MULTILINE),
+
+ # macroCalls should NOT gobbleWS - so that's why there aren't gobbleWS version
+
+ 'callMacro': re.compile(r'(?:(?<=\A)|(?<!\\))#callMacro[\t ]+' +
+ r'(?P<macroName>[A-Za-z_][A-Za-z_0-9]*?)' +
+ r'\((?P<argString>.*?)\)[\t ]*(?:/#|\r\n|\n)' +
+ r'(?P<extendedArgString>.*?)' +
+ r'#end callMacro[\t ]*(?:/#|\r\n|\n|\Z)',
+ re.DOTALL | re.MULTILINE),
+
+ 'callMacroArgs': re.compile(r'#arg[\t ]+' +
+ r'(?P<argName>[A-Za-z_][A-Za-z_0-9]*?)' +
+ r'[\t ]*(?:/#|\r\n|\n)' +
+ r'(?P<argValue>.*?)' +
+ r'(?:\r\n|\n)[\t ]*#end arg[\t ]*(?:/#|\r\n|\n)',
+ re.DOTALL | re.MULTILINE),
+
+ 'lazyMacroCalls':re.compile(r'((?:(?<=\A)|(?<!\\))#[a-zA-Z_][a-zA-Z_0-9\.]*\(.*?\))'),
+ #'macroCalls':re.compile(r'((?<!#)#[a-zA-Z_][a-zA-Z_0-9\.]*\(.*?\))[\t ]*(?:\n|/#)'),
+
+
+ # the block directives are handled differently from the macro directives, etc.
+ # to avoid maximum recursion limit errors when the content of the block is
+ # large. The end tag is dynamically generated by the blockDirective processor
+ 'blockDirectiveStart_gobbleWS': re.compile(r'(?:\A|^)[\t ]*#block[\t ]+' +
+ r'(?P<blockName>[A-Za-z_][A-Za-z_0-9]*?)' +
+ r'[\t ]*(?:\r\n|\n)' ,
+ re.DOTALL | re.MULTILINE),
+
+ 'blockDirectiveStart': re.compile(r'(?:(?<=\A)|(?<!\\))#block[\t ]+' +
+ r'(?P<blockName>[A-Za-z_][A-Za-z_0-9]*?)' +
+ r'[\t ]*(?:/#|\r\n|\n)' ,
+ re.DOTALL | re.MULTILINE),
+
+ 'dataDirective_gobbleWS': re.compile(r'(?:\A|^)[\t ]*#data[\t ]*(?P<args>.*?)' +
+ r'(?:/#|\r\n|\n)' +
+ r'(?P<contents>.*?)' +
+ r'#end data[\t ]*(?:/#|\r\n|\n|\Z)',
+ re.DOTALL | re.MULTILINE),
+
+ 'dataDirective': re.compile(r'(?:(?<=\A)|(?<!\\))#data[\t ]*(?P<args>.*?)' +
+ r'(?:/#|\r\n|\n)' +
+ r'(?P<contents>.*?)' +
+ r'#end data[\t ]*(?:/#|\r\n|\n|\Z)',
+ re.DOTALL | re.MULTILINE),
+
+ 'extendDirective':re.compile(r'(?:(?<=\A)|(?<!\\))#extend[\t ]+(?P<parent>.*?)' +
+ r'[\t ]*(?:/#|\r\n|\n|\Z)', re.DOTALL),
+ }
+
+
+
diff --git a/src/ErrorHandlers.py b/src/ErrorHandlers.py
new file mode 100644
index 0000000..e63a226
--- /dev/null
+++ b/src/ErrorHandlers.py
@@ -0,0 +1,265 @@
+"""Error Handlers for Cheetah
+
+Meta-Data
+================================================================================
+Author: Tavis Rudd <tavis@calrudd.com>
+License: This software is released for unlimited distribution under the
+ terms of the Python license.
+Version: $Revision: 1.1 $
+Start Date: 2001/03/30
+Last Revision Date: $Date: 2001/06/13 03:50:39 $
+"""
+__author__ = "Tavis Rudd <tavis@calrudd.com>"
+__version__ = "$Revision: 1.1 $"[11:-2]
+
+##################################################
+## DEPENDENCIES ##
+
+import re
+import sys
+import types
+from traceback import format_tb, format_exception
+
+
+#intra-package imports ...
+from Utilities import insertLineNums, getLines, lineNumFromPos
+from Version import version
+
+##################################################
+## CONSTANTS & GLOBALS ##
+
+True = (1==1)
+False = (0==1)
+
+##################################################
+## CLASSES ##
+
+class ErrorHandler:
+ """An abstract base class for Cheetah ErrorHandlers"""
+
+ def __init__(self):
+ """collect some information about the exception and the server"""
+ self._localvars = localvars = sys.exc_traceback.tb_frame.f_locals
+ self._server = server = localvars['self']
+ self._debug = server._settings['debug']
+
+ def format_exc(self):
+ return ''.join( format_exception(sys.exc_type, sys.exc_value,
+ sys.exc_traceback) )
+
+ def __str__(self):
+ """print the error message"""
+ fillValues ={'ver':version,
+ 'introText': self.introText(),
+ 'debugSettingWarning':self._debugSettingWarning(),
+ 'excType':sys.exc_type,
+ 'excValue':sys.exc_value,
+ 'errorDetails': self.errorDetails(),
+ }
+
+ return \
+"""================================================================================
+ Cheetah Version %(ver)s
+
+%(introText)s
+%(debugSettingWarning)s
+
+Exception Type: %(excType)s
+Exception Value: %(excValue)s
+
+More information about the error is printed below. The most general information
+from the high-level error handlers is printed first, followed by more specific
+information from the lower-level error handlers. A Python traceback is printed
+at the very end. There may be some repetition of information.
+
+================================================================================
+%(errorDetails)s
+""" % fillValues
+
+ def introText(self):
+ return ''
+
+ def errorDetails(self):
+ return ''
+
+ def _debugSettingWarning(self):
+ if not self._debug:
+
+ return "\nNOTE: you have Cheetah's debug setting set to " + \
+ "False. TemplateServer will\nusually be more helpful " + \
+ "with problems if you set debug to True."
+
+ else:
+ return ''
+
+class ResponseErrorHandler(ErrorHandler):
+ """An error handler for exceptions raised during the Template serving
+ cycle"""
+
+ def __init__(self):
+ ErrorHandler.__init__(self)
+
+ def introText(self):
+ return """Cheetah compiled this template successfully, but an error
+(aka Exception) occurred when Cheetah was executing the generated code."""
+
+ def errorDetails(self):
+ msg = self.format_exc()
+ msg += "\nHere's a copy of the code that Cheetah Template generated:\n\n"
+ msg += insertLineNums( self._server._generatedCode )
+
+ self._server._errorMsgStack.append(msg)
+ self._server._errorMsgStack.reverse()
+ return '\n'.join( self._server._errorMsgStack )
+
+
+
+
+class CodeGeneratorErrorHandler(ErrorHandler):
+ """The master ErrorHandler for Cheetah's codeGenerator."""
+ def __init__(self):
+ ErrorHandler.__init__(self)
+ server = self._server
+ self._stage = stage = self._localvars['stage']
+ self._stageSettings = server._settings['codeGenerator']['stages'][stage]
+
+ def introText(self):
+ fillValues = {'stage': self._stage,
+ 'stageDescription':self._stageSettings['description'],
+ 'stageTitle':self._stageSettings['title'],
+ }
+ return \
+"""An error (aka Exception) occurred during code generation stage %(stage)d.
+Stage %(stage)s, '%(stageTitle)s', is when %(stageDescription)s""" % fillValues
+
+ def errorDetails(self):
+ self._stageSettings['errorHandler']()
+ self._server._errorMsgStack.reverse()
+ return '\n'.join( self._server._errorMsgStack )
+
+
+
+
+
+
+class StageErrorHandler(ErrorHandler):
+ """ErrorHandler base class for stages of Cheetah's codeGenerator."""
+ def __init__(self):
+ ErrorHandler.__init__(self)
+ self._server._errorMsgStack.append( self.generateErrMsg() )
+
+ def generateErrMsg(self):
+ pass
+
+class Stage1ErrorHandler(StageErrorHandler):
+ """ErrorHandler for stage 1 of Cheetah's codeGenerator."""
+
+ def generateErrMsg(self):
+ msg = '\n'
+ msg += "TemplateServer was executing the " + \
+ self._localvars['preProcessor'][0] + \
+ " preProcessor when the error occurred.\n"
+
+ if not self._localvars['preProcessor'][0] == 'parseDirectiveLoop':
+ msg += "This was the state of the template when the error " + \
+ "occurred:\n\n"
+ msg += insertLineNums( self._localvars['template'] )
+
+ msg += '-'*80
+ return msg
+
+
+
+class Stage2ErrorHandler(StageErrorHandler):
+ """ErrorHandler for stage 2 of Cheetah's codeGenerator."""
+
+ def generateErrMsg(self):
+ msg = '\n'
+ msg += "Cheetah was processing " + self._localvars['token'] + \
+ " tags when the error occurred.\n"
+ msg += "This was the state of the template when the error occurred:\n\n"
+ msg += insertLineNums( self._localvars['template'] )
+ msg += '-'*80
+ return msg
+
+
+class Stage3ErrorHandler(StageErrorHandler):
+ """ErrorHandler for stage 3 of Cheetah's codeGenerator."""
+
+ def generateErrMsg(self):
+ msg = '\n'
+ msg += "Cheetah was executing the " + \
+ self._localvars['postProcessor'][0] + \
+ " postProcessor when the error occurred.\n"
+ msg += "This was the state of the template when the error occurred:\n\n"
+ msg += insertLineNums( self._localvars['template'] )
+ msg += '-'*80
+ return msg
+
+
+class Stage4ErrorHandler(StageErrorHandler):
+ """ErrorHandler for stage 4 of Cheetah's codeGenerator."""
+
+ def generateErrMsg(self):
+ msg = '\n'
+ if self._localvars['subStage'] == 'a':
+ msg += "Cheetah was separating tags from the normal text " + \
+ "when the error occurred.\n"
+ msg += "This was the state of the template when the error occurred:\n\n"
+ msg += insertLineNums( self._localvars['template'] )
+
+ elif self._localvars['subStage'] == 'b':
+ msg += "Cheetah was generating code for each of the tags " + \
+ "when the error occurred.\n"
+ msg += "This was the textVsTagList being processed:\n\n"
+ msg += str( self._localvars['textVsTagsList'] )
+ msg += '-'*80
+ msg += "\nThis was the state of the template when the error occurred:\n\n"
+ msg += insertLineNums( self._localvars['template'] )
+
+ else:
+ msg += "Cheetah was joining the code from each of the tags into " + \
+ "a string when the error occurred.\n"
+ msg += "This was the list of code chunks (even) and normal text (odd) " + \
+ "being joined:\n\n"
+ msg += str( self._localvars['codePiecesFromTextVsTagsList'] )
+ msg += '-'*80
+
+ return msg
+
+
+class Stage5ErrorHandler(StageErrorHandler):
+ """ErrorHandler for stage 5 of Cheetah's codeGenerator."""
+
+ def generateErrMsg(self):
+ msg = '\n'
+ msg += "Cheetah was wrapping the code from the tags up in a " + \
+ "function definition when the error occurred.\n"
+ msg += "This was the code from that came from the tag processors:\n\n"
+ msg += self._localvars['codeFromTextVsTagsList']
+ msg += '-'*80
+ return msg
+
+
+class Stage6ErrorHandler(StageErrorHandler):
+ """ErrorHandler for stage 6 of Cheetah's codeGenerator."""
+
+ def generateErrMsg(self):
+ msg = '\n'
+ msg += "Cheetah was executing the " + self._localvars['filter'][0] + \
+ " filter when the error occurred.\n"
+ msg += "This was the state of the generated when the error occurred:\n\n"
+ msg += insertLineNums( self._localvars['generatedCode'] )
+ msg += '-'*80
+ return msg
+
+
+class Stage7ErrorHandler(StageErrorHandler):
+ """ErrorHandler for stage 7 of Cheetah's codeGenerator."""
+ def generateErrMsg(self):
+ generatedCode = self._localvars['generatedCode']
+ msg = '\n'
+ msg += "Here's a copy of the code that Cheetah generated:\n\n"
+ msg += insertLineNums(generatedCode)
+ msg += '-'*80
+ return msg
diff --git a/src/Macros/HTML.py b/src/Macros/HTML.py
new file mode 100644
index 0000000..1491f82
--- /dev/null
+++ b/src/Macros/HTML.py
@@ -0,0 +1,58 @@
+#!/usr/bin/env python
+# $Id: HTML.py,v 1.1 2001/06/13 03:50:40 tavis_rudd Exp $
+"""HTML macros for use with Cheetah templates
+
+Meta-Data
+==========
+Author: Tavis Rudd <tavis@calrudd.com>,
+Version: $Revision: 1.1 $
+Start Date: 2001/04/05
+Last Revision Date: $Date: 2001/06/13 03:50:40 $
+"""
+__author__ = "Tavis Rudd <tavis@calrudd.com>"
+__version__ = "$Revision: 1.1 $"[11:-2]
+
+##################################################
+## DEPENDENCIES ##
+
+import time
+import types
+import os
+
+##################################################
+## GLOBALS AND CONSTANTS ##
+
+True = (1==1)
+False = (0==1)
+
+##################################################
+## FUNCTIONS ##
+
+def currentYr():
+ return time.strftime("%Y",time.localtime(time.time()))
+
+def spacer(width=1,height=1):
+ return '<IMG SRC="spacer.gif" WIDTH=%s HEIGHT=%s ALT="">'% (str(width), str(height))
+
+def formHTMLTag(tagName, attributes={}):
+ """returns a string containing an HTML <tag> """
+ tagTxt = ['<', tagName.upper()]
+ for name, val in attributes.items():
+ if type(val)==types.StringType:
+ val = '"' + val + '"'
+ tagTxt += [' ', name.upper(), '=', str(val)]
+ tagTxt.append('>')
+ return ''.join(tagTxt)
+
+def formatMetaTags(metaTags):
+ """format a dict of metaTag definitions into an HTML version"""
+ metaTagsTxt = []
+ for http_equiv, contents in metaTags['HTTP_EQUIV'].items():
+ metaTagsTxt += ['<META HTTP_EQUIV="', str(http_equiv), '" CONTENTS="',
+ str(contents), '">\n']
+
+ for name, contents in metaTags['NAME'].items():
+ metaTagsTxt += ['<META NAME="', str(name), '" CONTENTS="', str(contents),
+ '">\n']
+
+ return ''.join(metaTagsTxt)
diff --git a/src/Macros/__init__.py b/src/Macros/__init__.py
new file mode 100644
index 0000000..f745909
--- /dev/null
+++ b/src/Macros/__init__.py
@@ -0,0 +1,31 @@
+#!/usr/bin/env python
+# $Id: __init__.py,v 1.1 2001/06/13 03:50:40 tavis_rudd Exp $
+"""Macros for use with Cheetah templates
+
+Meta-Data
+==========
+Author: Tavis Rudd <tavis@calrudd.com>,
+Version: $Revision: 1.1 $
+Start Date: 2001/04/05
+Last Revision Date: $Date: 2001/06/13 03:50:40 $
+"""
+__author__ = "Tavis Rudd <tavis@calrudd.com>"
+__version__ = "$Revision: 1.1 $"[11:-2]
+
+##################################################
+## DEPENDENCIES ##
+
+
+##################################################
+## GLOBALS AND CONSTANTS ##
+
+True = (1==1)
+False = (0==1)
+
+
+##################################################
+## FUNCTIONS ##
+
+
+##################################################
+## CLASSES ##
diff --git a/src/NameMapper.py b/src/NameMapper.py
new file mode 100644
index 0000000..34b5cfe
--- /dev/null
+++ b/src/NameMapper.py
@@ -0,0 +1,295 @@
+#!/usr/bin/env python
+# $Id: NameMapper.py,v 1.1 2001/06/13 03:50:39 tavis_rudd Exp $
+
+"""Utilities for accessing the members of an object via string representations
+of those members. Template processing is its primary intended use.
+
+Description
+================================================================================
+@@ add basic desc.
+
+This module is similar to Webware's NamedValueAccess, but with the following
+basic differences:
+
+- you don't need to inherit the NamedValueAccess mixin to use it
+
+- the functionality of NamedValueAccess was abstracted into two standalone
+ functions: valueForName() and valueForKey(). valueForName() is recursive and
+ relies on valueForKey.
+
+- the mappings aren't cached in this version. We are investigating ways of
+ caching mappings for mutatible objects only.
+
+- any mapping object or object that works with python's getattr() builtin
+ function can be searched (classes, instances, dictionaries, btrees?, etc.),
+ therefore locals(), globals(), and __builtins__ all work
+
+- NamedValueAccess only works with methods. NameMapper works with any callable
+ object, such as plain functions or classes.
+
+- if a name maps to a callable object (function, method, class, etc.)
+ valueForName returns a reference to the object is returned instead of the
+ object's return value. The caller is responsible for dealing with it. This
+ allows the caller to cache the reference and execute it when ever needed.
+
+and some extra features:
+
+- nested objects can be descended into, for any depth, so long as nested objects
+ are dicts or work with getattr(). NamedValueAccess was limited to ojects that
+ inherited the NamedValueAccess mixin.
+
+Like NamedValueAccess:
+
+- you can provide a default value to substitute for names that can't be found.
+ If you don't provide a default and the name can't be found the exception
+ NameMapper.NotFound will be raised. This is similar to the .get() method of
+ dictionaries.
+
+
+Usage
+================================================================================
+This module is not safe for 'from NameMapper import *'!
+
+See the example at the bottom of this file.
+The TemplateServer module implements a less trivial example.
+
+Changes
+================================================================================
+1.10- got rid of the NameMapper class. There are now two normal functions,
+ valueForName and valueForKey, which do all the work. This brings the core
+ down to less than 60 lines of code, and greatly simplifies the interface.
+
+TODO
+================================================================================
+- write the test cases
+
+Meta-Data
+================================================================================
+Authors: Tavis Rudd <tavis@calrudd.com>,
+ Chuck Esterbrook <echuck@mindspring.com>
+License: This software is released for unlimited distribution
+ under the terms of the Python license.
+Version: $Revision: 1.1 $
+Start Date: 2001/04/03
+Last Revision Date: $Date: 2001/06/13 03:50:39 $
+"""
+__author__ = "Tavis Rudd <tavis@calrudd.com>," +\
+ "\nChuck Esterbrook <echuck@mindspring.com>"
+__version__ = "$Revision: 1.1 $"[11:-2]
+
+##################################################
+## DEPENDENCIES ##
+
+import types
+from types import StringType, InstanceType, ClassType
+import re
+# it uses the string methods and list comprehensions added in recent versions of python
+
+##################################################
+## GLOBALS AND CONSTANTS ##
+
+True = (1==1)
+False = (0==1)
+digitsRE = re.compile(r'[0-9]+')
+isDigit = digitsRE.match
+digits = ['0','1','2','3','4','5','6','7','8','9']
+
+
+class NotFound(Exception):
+ pass
+
+class NoDefault:
+ pass
+
+##################################################
+## FUNCTIONS ##
+
+def valueForName(obj, name, default=NoDefault, exectuteCallables=False):
+ """Get the value for the specified name. This function can be called
+ recursively. """
+
+ if type(name)==StringType:
+ # then this is the first call to this function.
+ nameChunks=name.split('.')
+ else:
+ #if this function calls itself then name already be a list of nameChunks
+ nameChunks = name
+
+ ## go get a binding for the key ##
+ firstKey = nameChunks[0]
+ if len(nameChunks) > 1:
+ # its a composite name like: nestedObject.item
+ binding = valueForKey(obj, firstKey, default)
+ if callable(binding) and type(binding) not in (InstanceType, ClassType):
+ # the type check allows access to the methods of instances
+ # of classes with __call__() defined
+ # and also allows obj.__class__.__name__
+ binding = binding()
+
+ return valueForName(binding, nameChunks[1:], default,
+ exectuteCallables=exectuteCallables)
+ else:
+ # its a single key like: nestedObject
+ binding = valueForKey(obj, firstKey, default)
+ if exectuteCallables and callable(binding):
+ binding = binding()
+ return binding
+
+
+
+def valueForKey(obj, key, default=NoDefault):
+ """Get the value of the specified key. The key can be a mapping item, an
+ attribute or an underscored attribute."""
+
+ if hasattr(obj, key):
+ binding = getattr(obj, key)
+ elif hasattr(obj, '_' + key):
+ binding = getattr(obj, '_' + key)
+ else:
+ try:
+ if key in digits:
+ binding = obj[int(key)]
+ else:
+ binding = obj.get(key, default)
+ except:
+ binding = default
+
+ if binding==NoDefault:
+ raise NotFound
+ else:
+ # this is a value or a reference to a callable object
+ return binding
+
+
+def determineNameType(obj, name):
+ """Return the type of a name or raise an exception if it can't be found.
+
+ This function is useful for caching in situations where a caller needs to
+ access the a NameMapper-style name repeatedly. Using this information, the
+ name can be translated into a standard python representation by the caller
+ on the first request. Every subsequent request will eval() the python
+ representation of the name, with is more efficient than using
+ valueForName().
+
+ Names can be of the following types:
+ - a 'callable' object.
+ In which case we just retrieve a reference of it and call it when needed
+ - a 'plain_attribute'
+ - an 'underscored_attribute'
+ - a 'mapping_key'
+ - raise NotFound
+ """
+
+ mapping = valueForName(obj,name)
+ if callable(mapping):
+ return 'callable'
+ else:
+ nameChunks = name.split('.')
+ if len(nameChunks) == 1:
+ return determineKeyType(obj, name)
+ else:
+ numChunks = len(nameChunks)
+ return determineKeyType(
+ valueForName(obj, "".join(nameChunks[0:numChunks-1])),
+ nameChunks[numChunks-1])
+
+
+def determineKeyType(obj, key):
+ if hasattr(obj, key):
+ if callable(getattr(obj, key)):
+ return 'callable'
+ else:
+ return 'plain_attribute'
+ elif hasattr(obj, '_' + key):
+ if callable(getattr(obj, '_' + key)):
+ return 'callable'
+ else:
+ return 'underscored_attribute'
+ elif hasattr(obj,'has_key'):
+ try:
+ if callable(obj.get(key)):
+ return 'callable'
+ else:
+ return 'mapping_key'
+ except:
+ raise NotFound
+
+
+
+##################################################
+## CLASSES ##
+
+class Mixin:
+ """@@ document me"""
+ def valueForName(self, name, handleArgStrings=False, localsDict=None):
+ return valueForName(self, name, handleArgStrings=handleArgStrings,
+ localsDict=localsDict)
+
+ def valueForKey(self, key, handleArgStrings=False, localsDict=None):
+ return valueForKey(self, key, handleArgStrings=handleArgStrings,
+ localsDict=localsDict)
+
+##################################################
+## TEST ROUTINES ##
+def test():
+ import NameMapper.Tests
+ NameMapper.Tests.run_tests()
+
+##################################################
+## if run from the command line ##
+
+def example():
+ class A(Mixin):
+ classVar = 'classVar val'
+ def method(self,arg='method 1 default arg'):
+ return arg
+
+ def method2(self, arg='meth 2 default arg'):
+ return {'item1':arg}
+
+ def method3(self, arg='meth 3 default'):
+ return arg
+
+ class B(A):
+ classBvar = 'classBvar val'
+ _underScoreVar = '_underScoreVar val'
+
+ def _underScoreMethod(self):
+ return '_underScoreMethod output'
+
+ a = A()
+ a.one = 'valueForOne'
+ def function(whichOne='default'):
+ values = {
+ 'default': 'default output',
+ 'one': 'output option one',
+ 'two': 'output option two'
+ }
+ return values[whichOne]
+
+ a.dic = {
+ 'func': function,
+ 'method': a.method3,
+ 'item': 'itemval',
+ 'subDict': {'nestedMethod':a.method3}
+ }
+ b = 'this is local b'
+
+ alist = ['item0','item1','item2']
+
+ print determineNameType(vars(),'B.underScoreVar')
+ print valueForKey(a.dic,'subDict','NotFound')
+ print valueForName(a, 'dic.item','NotFound')
+ print valueForName(vars(), 'b','NotFound')
+ print valueForName(__builtins__, 'dir','NotFound')()
+ print valueForName(vars(), 'a.classVar','NotFound')
+ print valueForName(vars(), 'B.underScoreVar','NotFound')
+ print valueForName(vars(), 'a.dic.func','NotFound')
+ print valueForName(vars(), 'a.method2.item1','NotFound')
+ print valueForName(vars(), 'alist.0','NotFound')
+
+if __name__ == '__main__':
+ example()
+
+
+
diff --git a/src/Plugins/Debugger.py b/src/Plugins/Debugger.py
new file mode 100644
index 0000000..fff4d54
--- /dev/null
+++ b/src/Plugins/Debugger.py
@@ -0,0 +1,226 @@
+#!/usr/bin/env python
+# $Id: Debugger.py,v 1.1 2001/06/13 03:50:40 tavis_rudd Exp $
+"""Debugging tools for the TemplateServer package
+
+THIS FILE IS NOT READY FOR USE!!
+It was created for an earlier version of TemplateServer and has not been updated yet.
+
+Meta-Data
+==========
+Author: Tavis Rudd <tavis@calrudd.com>
+License: This software is released for unlimited distribution under the
+ terms of the Python license.
+Version: $Revision: 1.1 $
+Start Date: 2001/03/30
+Last Revision Date: $Date: 2001/06/13 03:50:40 $
+"""
+__author__ = "Tavis Rudd <tavis@calrudd.com>"
+__version__ = "$Revision: 1.1 $"[11:-2]
+
+
+
+##################################################
+## DEPENDENCIES ##
+import time
+from TemplateServer.Version import version
+
+##################################################
+## CONSTANTS & GLOBALS ##
+
+True = (1==1)
+False = (0==1)
+currentTime = time.time # used in the cache refresh code
+
+
+##################################################
+## FUNCTIONS ##
+
+
+## methods for obtaining meta-information about the template being servered ##
+
+def _tags(server, removeDuplicates=True):
+ """return a list of all TemplateServer tags in the template."""
+ tags = server._settings['internalDelims']['placeholderRE'].findall(server._internalTemplate)
+
+ if removeDuplicates:
+ tags = removeDuplicateValues(tags)
+
+ tokens = []
+ tagsList = {}
+
+ for tag in tags:
+ token, tag = tag.split(server._settings['codeGenerator']['tagTokenSeparator'])
+ if tagsList.has_key(token):
+ tagsList[token].append(tag)
+ else:
+ tagsList[token] = [tag,]
+
+ return tagsList
+
+def _names(server, removeDuplicates=True):
+ """return a list of all the NameMapper vars embedded in the template."""
+ names = server._tags()['nameMapper']
+
+ if removeDuplicates:
+ names = removeDuplicateValues(names)
+ return names
+
+def _cachedNames(server):
+ """return a list of all NameMapper vars in the template that are cached"""
+ names = [name for name in filter(lambda name: name.find('*')!=-1 , server._names()) ]
+ return names
+
+def _uncachedNames(server):
+ """return a list of all NameMapper vars in the template that aren't cached"""
+ names = filter(lambda name: name.find('*')==-1 , server._names())
+ return names
+
+
+## methods to handle command-line usage ##
+
+def runAsMainProgram(server):
+ """Enables the template to function as a standalone command-line program for
+ static page generation and testing/debugging"""
+
+ import getopt, sys
+ from pprint import pprint
+
+ try:
+ opts, args = getopt.getopt(
+ sys.argv[1:], 'horiptnvdV', ['output','raw','afterParsedIncludes','afterIncludes',
+ 'internalTemplate',
+ 'pythonCode','tags','names','cached','uncached',
+ 'allnames','values','doc','help','version'])
+ except getopt.GetoptError:
+ # print help information and exit:
+ server._TemplateServerUsage()
+ sys.exit(2)
+ if not opts:
+ print server
+
+ for o, a in opts:
+ if o in ('-h', '--help'):
+ server._TemplateServerUsage()
+
+ if o in ('-o', '--output'):
+ print server
+ if o in ('-r','--raw'):
+ print server._rawTemplate
+ if o in ('--afterParsedIncludes',):
+ print server._rawTemplateAfterParsedIncludes
+ if o in ('-i','--afterIncludes'):
+ print server._rawTemplateAfterPlainIncludes
+
+ if o in ('--internalTemplate',):
+ print server._internalTemplate
+
+ if o in ('-p','--pythonCode'):
+ print server._templateFunctionDef
+
+ if o in ('-t','--tags'):
+ #pprint( server._names() )
+ pprint( server._tags() )
+
+ if o in ('-n','--names'):
+ #pprint( server._names() )
+ pprint( server._tags()['nameMapper'] )
+ if o in ('--cached',):
+ pprint( server._cachedNames() )
+ if o in ('--uncached',):
+ pprint( server._uncachedNames() )
+ if o in ('--allnames',):
+ pprint( server._names(removeDuplicates=False) )
+
+ if o in ('-v','--values',):
+
+ print "="*80
+ print "staticValuesCache (all names prefixed with *)"
+ print "="*80
+ print
+ pprint( server._staticValuesCache )
+ print
+
+ print "="*80
+ print "callableNamesCache (all names that map to methods, functions, etc.)"
+ print "="*80
+ print
+ pprint( server._callableNamesCache )
+ print
+
+ print "="*80
+ print "pluginsCache (all names that map to TemplatServer plugins)"
+ print "="*80
+ print
+ pprint( server._plugins )
+ print
+
+ print "="*80
+ print "nestedTemplatesCache (all names that map to other templates)"
+ print "="*80
+ print
+ pprint( server._nestedTemplatesCache )
+ print
+
+ print "="*80
+ print "timedRefreshList (all cached vars that are scheduled for an update)"
+ print "="*80
+ print "current time in epoch seconds:", currentTime()
+ print
+ print "name, nextUpdateTime(secs-since-epoch), updateInterval(minutes)"
+ print "-"*80
+ pprint( server._timedRefreshList )
+ print
+
+ if o in ('-d','--doc'):
+ import os
+ os.system('pydoc ./' + server.__filename__)
+ if o in ('-V', '--version'):
+ print version
+
+
+def _TemplateServerUsage():
+ """Print the TemplateServer command line usage information."""
+ print \
+"""Usage: python %(scriptName)s [OPTIONS]
+
+TemplateServer %(version)s by %(author)s
+
+ -h, --help print this help and exit
+
+ -o, --output print the template's default output
+ -r, --raw print the raw template after processing the includes
+ -b, --beforeIncludes
+ print the raw template before processing the includes
+ -i, --internal print template's tranlation to the internal delimeters and exit
+ -p, --pythonCode print template's tranlation to python code and exit
+
+ The following options show the variable names embedded in the
+ template with their original order maintained.
+ ---------------------------------------------------------------
+ -t, --tags print a list of all the tags in the template, grouped by type
+
+ -n, --names print a list of the variable names in the template
+ Names prefixed with * are statically cached.
+ --cached print a list of the cached variable names
+ --uncached print a list of the uncached variable names
+ --allnames same as names, but shows duplicates
+
+ -v --values print data stored internally in the cache dictionaries/lists:
+ *staticValuesCache
+ *callableNamesCache
+ *nestedTemplatesCache
+ *timedRefreshList
+
+ -d, --doc display the templates documentation using pydoc
+ The author must have set the Template's
+ __filename__ attribute for this to work.
+ If this is not set, the docs for the last base class that
+ did set this attribute will be shown instead.
+ > pydoc ./THISFILENAME does the same thing.
+
+ -V, --version print version information
+
+""" % {'scriptName':sys.argv[0],
+ 'version':version,
+ 'author':'Tavis Rudd',
+ }
diff --git a/src/Plugins/PSP.py b/src/Plugins/PSP.py
new file mode 100644
index 0000000..78988e6
--- /dev/null
+++ b/src/Plugins/PSP.py
@@ -0,0 +1,113 @@
+#!/usr/bin/env python
+# $Id: PSP.py,v 1.1 2001/06/13 03:50:40 tavis_rudd Exp $
+"""A plugin that allows Cheetah to handle PythonServerPages style coding
+
+Meta-Data
+==========
+Author: Tavis Rudd <tavis@calrudd.com>
+License: This software is released for unlimited distribution under the
+ terms of the Python license.
+Version: $Revision: 1.1 $
+Start Date: 2001/03/30
+Last Revision Date: $Date: 2001/06/13 03:50:40 $
+"""
+
+__author__ = "Tavis Rudd <tavis@calrudd.com>"
+__version__ = "$Revision: 1.1 $"[11:-2]
+
+##################################################
+## DEPENDENCIES ##
+
+from Cheetah.Delimeters import delimeters
+from Cheetah.Template import EXEC_TAG_TYPE
+
+##################################################
+## CONSTANTS & GLOBALS ##
+
+True = (1==1)
+False = (0==1)
+
+##################################################
+## FUNCTIONS ##
+
+
+def pythonScriptTagProcessor(templateObj, pythonScript):
+ """Process PSP style code that is embedded in the template definition."""
+
+ pythonScript = pythonScript.strip()
+ settings = templateObj._settings
+ indent = settings['codeGenerator']['indentationStep']
+ if not templateObj._codeGeneratorState.has_key('indentLevel'):
+ templateObj._codeGeneratorState['indentLevel'] = \
+ settings['codeGenerator']['initialIndentLevel']
+
+ if pythonScript.lower() == 'end': # move out one indent level
+ templateObj._codeGeneratorState['indentLevel'] -= 1
+ outputCode = indent*templateObj._codeGeneratorState['indentLevel']
+
+ elif pythonScript.lower()[0:4] in ('else','elif') or \
+ pythonScript.lower().startswith('except'):
+ # continuation of previous block
+
+ outputCode = indent*(templateObj._codeGeneratorState['indentLevel']-1) + \
+ pythonScript +"\n" + \
+ indent*templateObj._codeGeneratorState['indentLevel']
+
+ elif pythonScript[len(pythonScript.strip()) -1] == ":":
+ # it's the start of a new block
+ templateObj._codeGeneratorState['indentLevel'] += 1
+ outputCode = indent*(templateObj._codeGeneratorState['indentLevel']-1) + \
+ pythonScript + "\n" + \
+ indent*templateObj._codeGeneratorState['indentLevel']
+
+ elif pythonScript.lower().startswith('='): # it's a python value eval()
+ outputCode = indent*(templateObj._codeGeneratorState['indentLevel']) + \
+ "outputList += [str(" + pythonScript[1:] + "),]\n" + \
+ indent*templateObj._codeGeneratorState['indentLevel']
+ else: # it's a chunk of plain python code
+ outputCode = indent*(templateObj._codeGeneratorState['indentLevel']) + \
+ pythonScript + "\n" + \
+ indent*templateObj._codeGeneratorState['indentLevel']
+
+ return outputCode
+
+
+##################################################
+## CLASSES ##
+
+class PSPplugin:
+ """A plugin for Cheetah that allows PythonServerPages code (<%...%>,
+ <%=...%>) in templates"""
+
+ def bindToTemplateServer(self, templateObj):
+ """insert the settings neccessary for PSP into the templateObj"""
+ pythonScriptSettingsDict = {'type':EXEC_TAG_TYPE,
+ 'processor':pythonScriptTagProcessor,
+ 'delims':[delimeters['<%,%>'], ],
+ }
+ templateObj._settings['codeGenerator']['coreTags']['pythonScript'] = \
+ pythonScriptSettingsDict
+
+
+##################################################
+## test from command-line ##
+if __name__ == '__main__':
+ from Cheetah.Template import Template
+
+ templateDef = """
+ Testing Cheetah's PSP plugin:
+
+ $testVar
+ <% pspVar = 'X' %>
+ #set $list = [1,2,3]
+
+ #for $i in map(lambda x: x*x*x, $list)
+ $i
+ <%=self.mapName('testVar')*15%>
+ <%for i in range(15):%> <%=i*15%><%=pspVar%><%end%>
+ #end for
+ """
+
+ print Template(templateDef, {'testVar':1234}, plugins=[PSPplugin()])
+
+
diff --git a/src/Plugins/__init__.py b/src/Plugins/__init__.py
new file mode 100644
index 0000000..2014d08
--- /dev/null
+++ b/src/Plugins/__init__.py
@@ -0,0 +1,5 @@
+#!/usr/bin/env python
+# $Id: __init__.py,v 1.1 2001/06/13 03:50:40 tavis_rudd Exp $
+
+__author__ = "Tavis Rudd <tavis@calrudd.com>"
+__version__ = "$Revision: 1.1 $"[11:-2]
diff --git a/src/Servlet.py b/src/Servlet.py
new file mode 100644
index 0000000..6e24a8f
--- /dev/null
+++ b/src/Servlet.py
@@ -0,0 +1,62 @@
+#!/usr/bin/env python
+# $Id: Servlet.py,v 1.1 2001/06/13 03:50:39 tavis_rudd Exp $
+"""An abstract base class for Cheetah Servlets that can be used with Webware
+
+Meta-Data
+================================================================================
+Author: Tavis Rudd <tavis@calrudd.com>,
+Version: $Revision: 1.1 $
+Start Date: 2001/04/05
+Last Revision Date: $Date: 2001/06/13 03:50:39 $
+"""
+__author__ = "Tavis Rudd <tavis@calrudd.com>"
+__version__ = "$Revision: 1.1 $"[11:-2]
+
+##################################################
+## DEPENDENCIES ##
+
+import types
+
+# intra-package imports ...
+from Template import Template
+from Utilities import mergeNestedDictionaries
+import CodeGenerator as CodeGen
+
+try:
+ from WebKit.HTTPServlet import HTTPServlet
+except:
+ class HTTPServlet:
+ pass
+
+##################################################
+## GLOBALS AND CONSTANTS ##
+
+True = (1==1)
+False = (0==1)
+
+##################################################
+## CLASSES ##
+
+class TemplateServlet(Template, HTTPServlet):
+ """An abstract base class for Cheetah servlets that can be used with
+ Webware"""
+
+ def __init__(self, template='', *searchList, **kw):
+ """ """
+ if not kw.has_key('settings'):
+ kw['settings']={}
+ kw['settings']['delayedStart'] = True
+ Template.__init__(self, template, *searchList, **kw)
+ self.initializeTemplate()
+ self.startServer()
+
+ def initializeTemplate(self):
+ """a hook that can be used by subclasses to do things after the
+ Template has been initialized, but before it has been started
+ (i.e. before the codeGeneration process starts) """
+ pass
+
+
+
+
+
diff --git a/src/SettingsManager.py b/src/SettingsManager.py
new file mode 100644
index 0000000..811fc21
--- /dev/null
+++ b/src/SettingsManager.py
@@ -0,0 +1,76 @@
+#!/usr/bin/env python
+# $Id: SettingsManager.py,v 1.1 2001/06/13 03:50:40 tavis_rudd Exp $
+"""Provides a mixin class for managing settings dictionaries
+
+Meta-Data
+==========
+Author: Tavis Rudd <tavis@calrudd.com>
+Version: $Revision: 1.1 $
+Start Date: 2001/05/30
+Last Revision Date: $Date: 2001/06/13 03:50:40 $
+"""
+__author__ = "Tavis Rudd <tavis@calrudd.com>"
+__version__ = "$Revision: 1.1 $"[11:-2]
+
+
+##################################################
+## DEPENDENCIES ##
+
+from copy import deepcopy, copy
+
+#intra-package imports ...
+from Utilities import mergeNestedDictionaries
+
+##################################################
+## CONSTANTS & GLOBALS ##
+
+True = (1==1)
+False = (1==0)
+
+
+##################################################
+## CLASSES ##
+
+class Error(Exception):
+ pass
+
+class NoDefault:
+ pass
+
+class SettingsManager:
+ """A mixin class that provides facilities for managing settings dictionaries."""
+
+ def initializeSettings(self):
+ """This dummy method should be reimplemented by subclasses."""
+ self._settings = {}
+
+ def updateSettings(self, newSettings, merge=True):
+ """Update the settings with a selective merge or a complete overwrite."""
+ if merge:
+ self._settings = mergeNestedDictionaries(self._settings, newSettings)
+ else:
+ self._settings = newSettings
+
+ def setting(self, name, default=NoDefault):
+ """Get a setting from self._settings, with or without a default value."""
+ if default is NoDefault:
+ return self._settings[name]
+ else:
+ return self._settings.get(name, default)
+
+ def setSetting(self, name, value):
+ """Set a setting in self._settings."""
+ self._settings[name] = value
+
+ def settings(self):
+ """Return a reference to the settings dictionary"""
+ return self._settings
+
+ def copySettings(self):
+ """Returns a shallow copy of the settings dictionary"""
+ return copy(self._settings)
+
+ def deepcopySettings(self):
+ """Returns a deep copy of the settings dictionary"""
+ return deepcopy(self._settings)
+
diff --git a/src/Template.py b/src/Template.py
new file mode 100644
index 0000000..683400e
--- /dev/null
+++ b/src/Template.py
@@ -0,0 +1,681 @@
+#!/usr/bin/env python
+# $Id: Template.py,v 1.1 2001/06/13 03:50:39 tavis_rudd Exp $
+"""Provides the core Template class for Cheetah
+See the docstring in __init__.py and the User's Guide for more information
+
+Meta-Data
+================================================================================
+Author: Tavis Rudd <tavis@calrudd.com>,
+ with code/advice from Ian Bicking, Mike Orr, Chuck Esterbrook and others
+ It was inspired by Chuck's RNV module <echuck@mindspring.com>
+License: This software is released for unlimited distribution under the
+ terms of the Python license.
+Version: $Revision: 1.1 $
+Start Date: 2001/03/30
+Last Revision Date: $Date: 2001/06/13 03:50:39 $
+"""
+__author__ = "Tavis Rudd <tavis@calrudd.com>"
+__version__ = "$Revision: 1.1 $"[11:-2]
+
+
+##################################################
+## DEPENDENCIES ##
+
+import os # used to get environ vars, etc.
+import sys # used in the error handling code
+import new # used to bind the compiled template code
+import types # used in the mergeNewTemplateData method
+import time # used in the cache refresh code
+from time import time as currentTime # used in the cache refresh code
+
+# intra-package imports ...
+from SettingsManager import SettingsManager
+from NameMapper import valueForName, determineNameType
+import NameMapper
+import CodeGenerator as CodeGen
+import ErrorHandlers
+from Delimeters import delimeters as delims
+from Utilities import \
+ removeDuplicateValues, \
+ mergeNestedDictionaries, \
+ insertLineNums
+
+##################################################
+## CONSTANTS & GLOBALS ##
+
+True = (1==1)
+False = (0==1)
+
+EVAL_TAG_TYPE = 0
+EXEC_TAG_TYPE = 1
+
+
+# text descriptions of what each stage in TemplateServer._codeGenerator() does
+stageDescriptions = {
+1:"""the raw template is filtered using
+the pre-processors specified in the TemplateServer settings.""",
+2:"""the core tags ($, #set, #if, #for,
+<%...%>, and <%=...%>, etc.) are converted to the internal tag format.""",
+3:"""the template is filtered for the 2nd
+time using the post-processors specified in the TemplateServer settings.""",
+4:"""the tags that have been translated to
+the internal format are converted into chunks of python code.""",
+5:"""the chunks of python code from stage 4
+are wrapped up in a code string of a function definition.""",
+6:"""the generated code string is filtered
+using the filters defined in the TemplateServer settings.""",
+7:"""the generated code string is executed
+to produce a function that will be bound as a method of the TemplateServer.""",
+}
+
+
+##################################################
+## CLASSES ##
+
+class Error(Exception):
+ pass
+
+class NoDefault:
+ pass
+
+class Template(SettingsManager):
+ """The core template engine: parses, compiles, and serves templates."""
+
+ def __init__(self, templateDef, *searchList, **kw):
+ """setup the namespace search list, process settings, then call
+ self._start() to parse/compile the template and prepare the
+ self.__str__() and self.respond() methods for serving the template.
+
+ If the environment var CHEETAH_DEBUG is set to True the internal
+ debug setting will also be set to True."""
+
+ self._searchList = list(searchList) + [self,]
+ if kw.has_key('searchList'):
+ self._searchList += kw['searchList']
+
+ self.initializeSettings()
+ if kw.has_key('settings'):
+ self.updateSettings(kw['settings'])
+
+ if os.environ.get('CHEETAH_DEBUG'):
+ self._settings['debug'] = True
+
+ if kw.has_key('plugins'):
+ self._settings['plugins'] += kw['plugins']
+ for plugin in self._settings['plugins']:
+ self.registerServerPlugin(plugin)
+
+ self._rawTemplate = str( templateDef )
+
+ if not self._settings['delayedStart']:
+ self.startServer()
+
+
+ def initializeSettings(self):
+ """create the default settings """
+
+ def includeDirectiveLoop(server, template):
+ """process the include Directives recursively and plus all the other
+ directives/filters that affect them or are affected by them"""
+ settings = server._settings
+ extDelimeters = settings['extDelimeters']
+
+ regexs = extDelimeters['includeDirective']
+ regexs += extDelimeters['macroDirective']
+ regexs += extDelimeters['blockDirectiveStart']
+
+ try:
+ while filter(None, [regex.search(template) for regex in regexs]):
+ for includeDirectiveRE in extDelimeters['includeDirective']:
+
+ ## must alternate between the #parse regexes or
+ # whitespace gobbling in nested #parse blocks
+ # won't be handled properly
+
+ for processor in \
+ settings['codeGenerator']['includeDirectiveLoop']:
+
+ template = processor[1](server, template)
+
+ template = \
+ CodeGen.preProcessIncludeDirectives(server,
+ template,
+ includeDirectiveRE)
+ ## do one more final pass
+ for processor in settings['codeGenerator']['includeDirectiveLoop']:
+ template = processor[1](server, template)
+
+ except:
+ errMsg = '\n'
+ errMsg += "\nThe includeDirectiveLoop was processing " + \
+ processor[0] + \
+ " when the error occurred.\n"
+ errMsg += "This was the state of the template when the error " +\
+ "occurred:\n\n"
+ errMsg += insertLineNums( template ) + '\n'
+ server._errorMsgStack.append( errMsg )
+ raise
+
+ return template
+
+ self._settings = {
+ 'delayedStart': False,
+
+ 'plugins':[],
+ 'defaultVarValue':None,
+ # default val for names, if ==None then varNotFound_handler is called
+ 'varNotFound_handler': CodeGen.varNotFound_echo,
+ # only called if defaultVarValue==None and the $var can't be found
+
+ 'debug': False,
+ 'keepCodeGeneratorResults': False,
+
+ 'blockMarkerStart':['<!-- START BLOCK: ',' -->'],
+ 'blockMarkerEnd':['<!-- END BLOCK: ',' -->'],
+ 'includeBlockMarkers': False,
+
+
+ 'extDelimeters':{'includeDirective': [delims['includeDirective_gobbleWS'],
+ delims['includeDirective'],
+ ],
+ 'dataDirective': [delims['dataDirective_gobbleWS'],
+ delims['dataDirective'],
+ ],
+ 'macroDirective': [delims['macroDirective'],
+ ],
+ 'blockDirectiveStart': [delims['blockDirectiveStart_gobbleWS'],
+ delims['blockDirectiveStart'],
+ ],
+ 'lazyMacroCalls': [delims['lazyMacroCalls'],],
+ 'callMacro': [delims['callMacro'],],
+ 'callMacroArgs': delims['callMacroArgs'],
+ 'rawDirective': [delims['rawDirective'],],
+ 'comments': [delims['multiLineComment'],
+ delims['singleLineComment']],
+ 'slurp': [delims['slurpDirective_gobbleWS'],
+ delims['slurpDirective'],
+ ],
+ },
+
+
+ 'responseErrorHandler': ErrorHandlers.ResponseErrorHandler,
+
+ 'codeGenerator':{
+ 'displayLogicblockEndings':['end if','end for'],
+ 'internalDelims': delims['xml'],
+ 'tagTokenSeparator': '__@__',
+ 'indentationStep': ' '*4, # 4 spaces - used in the generated code
+ 'initialIndentLevel': 2,
+
+ ## must loop over these processores to handle
+ # nested #include, #macro and #block Directives
+ 'includeDirectiveLoop':[('rawDirectives',
+ CodeGen.preProcessRawDirectives),
+ ('comments',
+ CodeGen.preProcessComments),
+ ('setDirectives',
+ CodeGen.preProcessSetDirectives),
+ ('dataDirectives',
+ CodeGen.preProcessDataDirectives),
+ ('blockDirectives',
+ CodeGen.preProcessBlockDirectives),
+ ('macroDirectives',
+ CodeGen.preProcessMacroDirectives),
+ ('lazyMacroCalls',
+ CodeGen.preProcessLazyMacroCalls),
+ ('explicitMacroCalls',
+ CodeGen.preProcessExplicitMacroCalls),
+ ('comments',
+ CodeGen.preProcessComments),
+ ('rawDirectives',
+ CodeGen.preProcessRawDirectives),
+ ('setDirectives',
+ CodeGen.preProcessSetDirectives),
+ ],
+
+ 'preProcessors': [('slurpDirectives',
+ CodeGen.preProcessSlurpDirective),
+ ('includeDirectiveLoop',
+ includeDirectiveLoop), # see above
+ ('displayLogic$Escaping',
+ CodeGen.preProcessDisplayLogic),
+ ],
+
+ 'postProcessors': [('rawDirectives',
+ CodeGen.postProcessRawDirectives),
+ ('rawIncludeDirectives',
+ CodeGen.postProcessRawIncludeDirectives),
+ ],
+
+ 'coreTags':{'displayLogic':{'type': EXEC_TAG_TYPE,
+ 'processor': CodeGen.displayLogicTagProcessor,
+ 'delims': [delims['displayLogic_gobbleWS'],
+ delims['displayLogic'],
+ ],
+ },
+ 'placeholders':{'type': EVAL_TAG_TYPE,
+ 'processor': CodeGen.placeholderTagProcessor,
+ 'delims': [delims['${,}'],delims['$']],
+ ##the braced version must go first
+ },
+ 'setDirective':{'type': EXEC_TAG_TYPE,
+ 'processor': CodeGen.setDirectiveTagProcessor,
+ 'delims': [delims['setDirective'],],
+ },
+ 'cacheStartTag':{'type': EVAL_TAG_TYPE,
+ 'processor': CodeGen.cacheDirectiveStartTagProcessor,
+ 'delims': [delims['cacheDirectiveStartTag'],],
+ },
+ 'cacheEndTag':{'type': EVAL_TAG_TYPE,
+ 'processor': CodeGen.cacheDirectiveEndTagProcessor,
+ 'delims': [delims['cacheDirectiveEndTag'],],
+ },
+
+ },
+
+ 'generatedCodeFilters':[('removeEmptyStrings',
+ CodeGen.removeEmptyStrings),
+ ('addPerResponseCode',
+ CodeGen.addPerResponseCode),
+ ],
+
+ 'masterErrorHandler':ErrorHandlers.CodeGeneratorErrorHandler,
+
+ 'stages':{1:{'title':'pre-processing',
+ 'description':stageDescriptions[1],
+ 'errorHandler':ErrorHandlers.Stage1ErrorHandler,
+ },
+ 2:{'title':'translate-to-internal-tags',
+ 'description':stageDescriptions[2],
+ 'errorHandler':ErrorHandlers.Stage2ErrorHandler,
+ },
+ 3:{'title':'post-processing',
+ 'description':stageDescriptions[3],
+ 'errorHandler':ErrorHandlers.Stage3ErrorHandler,
+ },
+ 4:{'title':'convert-tags-to-code',
+ 'description':stageDescriptions[4],
+ 'errorHandler':ErrorHandlers.Stage4ErrorHandler,
+ },
+ 5:{'title':'wrap-code-in-function-definition',
+ 'description':stageDescriptions[5],
+ 'errorHandler':ErrorHandlers.Stage5ErrorHandler,
+ },
+ 6:{'title':'filter-generated-code',
+ 'description':stageDescriptions[6],
+ 'errorHandler':ErrorHandlers.Stage6ErrorHandler,
+ },
+ 7:{'title':'execute-generated-code',
+ 'description':stageDescriptions[7],
+ 'errorHandler':ErrorHandlers.Stage7ErrorHandler,
+ },
+ },
+ },
+ }
+
+ def addToSearchList(self, object, restart=True):
+ self._searchList.append(object)
+ if restart:
+ self.startServer()
+
+ def startServer(self):
+ """Process and parse the template, then compile it into a function definition
+ that is bound to self.__str__() and self.respond()"""
+
+ self._errorMsgStack = []
+ generatedFunction = self._codeGenerator( self._rawTemplate )
+ self.__str__ = self._bindFunctionAsMethod( generatedFunction )
+ self.respond = self._bindFunctionAsMethod( generatedFunction )
+
+ if not self._settings['keepCodeGeneratorResults']:
+ self._codeGeneratorResults = {}
+
+
+ def _codeGenerator(self, template):
+
+ """parse the template definition, generate a python code string from it,
+ then execute the code string to create a python function which can be
+ bound as a method of the Template. Returns a reference to the function.
+
+ stage 1 - the raw template is filtered using the pre-processors
+ specified in the TemplateServer settings
+
+ stage 2 - convert the coreTags to internal tags. Core tags are: $var,
+ ${var}, #if .../#, #for .../#, #set, and the PSP tags <%...%>,
+ <%=...%>. Each internal tag will contain a token prefix to identify what
+ type of tag it was originally. These tokens are used in stage 4 to
+ determine how the tag should be processed.
+
+ stage 3 - the template is filtered for the 2nd time using the
+ post-processors specified in the TemplateServer settings
+
+ stage 4 - the tags that have been translated to the internal format are
+ converted into chunks of python code
+
+ stage 5 - the chunks of python code from stage 4 are wrapped up in a
+ code string of a function definition
+
+ stage 6 - the generated code string is filtered using the filters
+ defined in the TemplateServer settings
+
+ stage 7 - the generated code string is executed to produce a python
+ function, that will become a method of the TemplateServer
+
+ These stages are contain in a try: ... except: ... block that will
+ provide helpful information for debugging if an error is caught."""
+
+ settings = self._settings
+ generatorSettings = settings['codeGenerator']
+ stageSettings = generatorSettings['stages']
+ debug = settings['debug']
+ results = self._codeGeneratorResults = {}
+ state = self._codeGeneratorState = {}
+ self._localVarsList = [] # used to track vars from #set and #for
+
+ try:
+ ## stage 1 - preProcessing of the template string ##
+ stage = 1
+ if debug: results['stage1'] = []
+ for preProcessor in generatorSettings['preProcessors']:
+ template = preProcessor[1](self, template)
+ if debug: results['stage1'].append((preProcessor[0], template))
+
+
+ ## stage 2 - translate the coreTag delimeters to internalDelims ##
+ # with tokens that will be recognized by the self._tagTokenProcessor
+ stage = 2
+ if debug: results['stage2'] = []
+ template = template.replace("'''",r"\'\'\'") # ''' must be escaped
+ for token, tagSettings in generatorSettings['coreTags'].items():
+ for delimStruct in tagSettings['delims']:
+ # this loop allows multiple delims to be used for each token
+ template = CodeGen.swapDelims(
+ template, delimStruct,
+ generatorSettings['internalDelims']['start'] + token \
+ + generatorSettings['tagTokenSeparator'],
+ generatorSettings['internalDelims']['end'],
+ )
+ if debug: results['stage2'].append( (token, template) )
+
+
+ ## stage 3 - postProcessing after the coreTag delim translation ##
+ stage = 3
+ if debug: results['stage3'] = []
+ for postProcessor in generatorSettings['postProcessors']:
+ template = postProcessor[1](self, template)
+ if debug: results['stage3'].append( (postProcessor[0], template) )
+
+
+ ## stage 4 - generate the python code for each of the tokenized tags ##
+ # a) separate internal tags from text in the template to create
+ # textVsTagsList
+ # b) send textVsTagsList through self._tagTokenProcessor to generate
+ # the code pieces
+ # c) merge the code pieces into a single string
+ stage = 4
+ if debug: results['stage4'] = []
+
+ # a)
+ subStage = 'a'
+ textVsTagsList = CodeGen.separateTagsFromText(
+ template, generatorSettings['internalDelims']['placeholderRE'])
+ if debug:
+ results['stage4'].append(('textVsTagsList', textVsTagsList))
+ # b)
+ subStage = 'b'
+ codePiecesFromTextVsTagsList = CodeGen.processTextVsTagsList(
+ textVsTagsList,
+ self._tagTokenProcessor)
+ # c)
+ subStage = 'c'
+ codeFromTextVsTagsList = "".join(codePiecesFromTextVsTagsList)
+ if debug:
+ results['stage4'].append(('codeFromTextVsTagsList',
+ codeFromTextVsTagsList))
+
+ ## stage 5 - wrap the code up in a function definition ##
+ stage = 5
+ if debug: results['stage5'] = []
+ indent = generatorSettings['indentationStep']
+ generatedCode = \
+ "def generatedFunction(self, trans=None, iAmNested=False):\n" \
+ + indent * 1 + "try:\n" \
+ + indent * 2 + "#setupCodeInsertMarker\n" \
+ + indent * 2 + "outputList = []\n" \
+ + indent * 2 + "outputList += ['''" + codeFromTextVsTagsList + \
+ "''',]\n" \
+ + indent * 2 + "output = ''.join(outputList)\n" \
+ + indent * 2 + "if trans and not iAmNested:\n" \
+ + indent * 3 + "trans.response().write(output)\n" \
+ + indent * 2 + "return output\n" \
+ + indent * 1 + "except:\n" \
+ + indent * 2 + "print self._settings['responseErrorHandler']()\n" \
+ + indent * 2 + "raise\n" \
+
+ if debug: results['stage5'].append( ('generatedCode', generatedCode) )
+
+
+ ## stage 6 - final filtering of the generatedCode ##
+ stage = 6
+ if debug: results['stage6'] = []
+ for filter in generatorSettings['generatedCodeFilters']:
+ generatedCode = filter[1](self, generatedCode)
+ if debug: results['stage6'].append( (filter[0], generatedCode) )
+
+ ## stage 7 - create "generatedFunction" in this namespace ##
+ stage = 7
+ if debug: results['stage7'] = []
+ exec generatedCode
+ if debug:
+ results['stage7'].append(('generatedFunction', generatedFunction))
+
+ ##
+ self._generatedCode = generatedCode
+
+ return generatedFunction
+
+ except:
+ ## call masterErrorHandler, which in turn calls the ErrorHandler ##
+ # for the stage in which the error occurred
+ print generatorSettings['masterErrorHandler']()
+ raise
+
+
+ def mergeNewTemplateData(self, newDataDict):
+ """merge the newDataDict into self.__dict__. This is a recursive merge
+ that handles nested dictionaries in the same way as
+ Template.updateServerSettings()"""
+
+ for key, val in newDataDict.items():
+ if type(val) == types.DictType and hasattr(self,key) \
+ and type(getattr(self,key)) == types.DictType:
+
+ setattr(self,key, mergeNestedDictionaries(getattr(self,key), val))
+ else:
+ setattr(self,key,val)
+
+ def registerServerPlugin(self, plugin):
+ """register a plugin that extends the functionality of the Template"""
+ plugin.bindToTemplateServer(self)
+
+ def _bindFunctionAsMethod(self, function):
+ """used to dynamically bind a plain function as a method of the
+ Template instance"""
+ return new.instancemethod(function, self, self.__class__)
+
+ def _tagTokenProcessor(self, tag, wrapOutput=True):
+ """an abstract tag processor that will identify the tag type from its
+ tagToken prefix and call the appropriate processor for that type of
+ tag"""
+ settings = self._settings
+
+ tagToken, tag = tag.split(settings['codeGenerator']['tagTokenSeparator'])
+
+ for token, tagSettings in settings['codeGenerator']['coreTags'].items():
+ if tagToken == token:
+ processedTag = tagSettings['processor'](self, tag)
+
+ if not wrapOutput:
+ return processedTag
+ elif tagSettings['type'] == EVAL_TAG_TYPE:
+ return "''', " + processedTag + ", '''"
+ elif tagSettings['type'] == EXEC_TAG_TYPE:
+ return "''',]\n" + processedTag + "outputList += ['''"
+
+
+ ## methods for dealing with the embedded NameMapper object references ##
+
+ def mapName(self, name, default=None, executeCallables=False):
+ """Returns a mapping for the placeholder name to its actual value
+
+ This function is similar, but not identical, to Webware's valueForName!
+ """
+
+ for namespace in self._searchList:
+ binding = valueForName(namespace, name, '<!NotFound!>')
+ if binding != '<!NotFound!>':
+ break
+
+ if binding == '<!NotFound!>':
+ if default!=None:
+ binding = defaultVarValue
+ elif self._settings['defaultVarValue']!=None:
+ binding = self._settings['defaultVarValue']
+ else:
+ raise NameMapper.NotFound(name)
+
+ if executeCallables and callable(binding):
+ binding = binding()
+ return binding
+
+
+ def _setTimedRefresh(self, placeholderName, cacheRefreshInterval):
+ nextUpdateTime = currentTime() + cacheRefreshInterval * 60
+ self._timedRefreshList.append(
+ [placeholderName, nextUpdateTime, cacheRefreshInterval])
+ self._checkForCacheRefreshes = True
+
+ def _timedRefresh(self, currTime):
+ """refresh all the cached NameMapper vars that are scheduled for a
+ refresh at this time, and reschedule them for their next update.
+
+ the entries in the recache list are in the format [name, interval,
+ nextRecacheTime] """
+
+ def updateList(item, currTime=currTime):
+ if item[1] < currTime:
+ item[1] = currTime + (item[2]*60) # reschedule for next update
+ return True
+ else:
+ return False
+
+ for name in filter(updateList, self._timedRefreshList):
+ if self._settings['debug']:
+ print repr(self), 'refreshing {' + name[0] + '} at time:', \
+ currentTime()
+ print
+
+ ## send it back through the tag processor to recache the value
+ CodeGen.placeholderTagProcessor(
+ self, tag=name[0], cacheType=CodeGen.TIMED_REFRESH_CACHE,
+ cacheRefreshInterval=float(name[1]))
+
+ ## the placeholderTagProcessor will have added a new entry to the
+ # list which we don't need so ...
+ self._timedRefreshList.pop()
+
+
+ def defineTemplateBlock(self, blockName, blockContents):
+ """ """
+ if not hasattr(self, '_blocks'):
+ self._blocks = {}
+
+ self._blocks[blockName]= blockContents
+
+ def killTemplateBlock(self, *blockNames):
+ """ """
+ if not hasattr(self, '_blocks'):
+ return False
+ for blockName in blockNames:
+ self._blocks[blockName]= ''
+
+ def loadMacro(self, macroName, macro):
+ """Load a macro into the macros dictionary, using the specified macroName"""
+ if not hasattr(self, '_macros'):
+ self._macros = {}
+
+ self._macros[macroName] = macro
+
+
+ def loadMacros(self, *macros):
+ """Create macros from any number of functions and/or bound methods. For
+ each macro, the function/method name is used as the macro name. """
+
+ for macro in macros:
+ self.loadMacro(macro.__name__, macro)
+
+ def loadMacrosFromModule(self, module):
+ """Load all the macros from a module into the macros dictionary"""
+
+ if hasattr(module,'_exclusionList'):
+ exclusionList = module._exclusionList
+ else:
+ exclusionList = ()
+
+ macrosList = []
+ for obj in module.__dict__.values():
+ if callable(obj) and obj not in exclusionList:
+ macrosList.append( (obj.__name__, obj) )
+
+ for macro in macrosList:
+ self.loadMacro(macro[0], macro[1])
+
+
+ def extendTemplate(self, extension):
+ """
+ @@needs documenting
+ #redefine and #data directives MUST NOT be nested!!
+ """
+ import re
+
+ if not hasattr(self, '_blocks'):
+ self._blocks = {}
+
+ redefineDirectiveRE = re.compile(
+ r'(?<!#)#redefine[\t ]+' +
+ r'(?P<blockName>[A-Za-z_][A-Za-z_0-9]*?)' +
+ r'(?:/#|\r\n|\n|\Z)',re.DOTALL)
+
+ while redefineDirectiveRE.search(extension):
+ startTagMatch = redefineDirectiveRE.search(extension)
+ blockName = startTagMatch.group('blockName')
+ endTagRE = re.compile(r'#end redefine[\t ]+' + blockName + r'[\t ]*(?:/#|\r\n|\n|\Z)',
+ re.DOTALL | re.MULTILINE)
+ endTagMatch = endTagRE.search(extension)
+ blockContents = extension[startTagMatch.end() : endTagMatch.start()]
+ self.defineTemplateBlock(blockName, blockContents)
+ extension = extension[0:startTagMatch.start()] + \
+ extension[endTagMatch.end():]
+
+ ## process the #data and #macro definition directives
+ # after removing comments
+ extension = CodeGen.preProcessComments(self, extension)
+ CodeGen.preProcessDataDirectives(self, extension)
+ CodeGen.preProcessMacroDirectives(self, extension)
+
+
+ ## utility functions ##
+ def getFileContents(self, fileName):
+ fp = open(fileName,'r')
+ output = fp.read()
+ fp.close()
+ return output
+
+ def runAsMainProgram(self):
+ """An abstract method that can be reimplemented to enable the Template
+ to function as a standalone command-line program for static page
+ generation and testing/debugging.
+
+ The debugging facilities are provided by a plugin to Template."""
+
+ print self
diff --git a/src/Templates/SkeletonPage.py b/src/Templates/SkeletonPage.py
new file mode 100644
index 0000000..9c06cdd
--- /dev/null
+++ b/src/Templates/SkeletonPage.py
@@ -0,0 +1,204 @@
+#!/usr/bin/env python
+# $Id: SkeletonPage.py,v 1.1 2001/06/13 03:50:40 tavis_rudd Exp $
+"""A skeleton page template for use with the Cheetah package
+
+Meta-Data
+==========
+Author: Tavis Rudd <tavis@calrudd.com>,
+Version: $Revision: 1.1 $
+Start Date: 2001/04/05
+Last Revision Date: $Date: 2001/06/13 03:50:40 $
+"""
+__author__ = "Tavis Rudd <tavis@calrudd.com>"
+__version__ = "$Revision: 1.1 $"[11:-2]
+
+##################################################
+## DEPENDENCIES ##
+
+import types, os, sys
+
+
+from Cheetah.Servlet import TemplateServlet
+import Cheetah.Macros.HTML as HTMLMacros
+from Cheetah.Macros.HTML import spacer, currentYr, formHTMLTag, formatMetaTags
+
+##################################################
+## GLOBALS AND CONSTANTS ##
+
+True = (1==1)
+False = (0==1)
+
+##################################################
+## TEMPLATE ELEMENTS ##
+
+template = """$docType
+<HTML>
+####################
+#block headerComment
+<!-- This document was autogenerated by Cheetah. Don't edit it directly!
+
+Copyright #currentYr() - $siteCopyrightName - All Rights Reserved.
+Feel free to copy any javascript or html you like on this site,
+provided you remove all links and/or references to $siteDomainName
+However, please do not copy any content or images without permission.
+
+$siteCredits
+
+-->
+
+#end block headerComment
+#####################
+
+#################
+#block headTag /#
+<HEAD>
+<TITLE>$title</TITLE>
+$metaTags
+$stylesheetTags
+$javascriptTags
+</HEAD>
+#end block headTag /#
+#################
+
+
+#################
+#block bodyTag /#
+$bodyTag
+#end block bodyTag /#
+#################
+
+#block bodyContents /#
+This skeleton page has no flesh. Its body needs to be implemented.
+#end block bodyContents /#
+
+</BODY>
+</HTML>
+"""
+
+##################################################
+## TEMPLATE SERVLET ##
+
+class SkeletonPage(TemplateServlet):
+ """A Skeleton HTML page template"""
+
+ def __init__(self, template=template, *searchList, **kw):
+ """ """
+ self.loadMacrosFromModule(HTMLMacros)
+ self.loadMacro('imgTag', self.imgTag )
+ TemplateServlet.__init__(self, template, *searchList, **kw)
+
+
+
+ ## Default values for the names embedded in the template ##
+
+ docType = '<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" ' + \
+ '"http://www.w3.org/TR/html4/loose.dtd">'
+
+ title = ''
+ #_metaTags = {'HTTP_EQUIV':{'test':1234}, 'NAME':{'test':1234,'test2':1234} }
+ #_stylesheets = {'.cssClassName':'stylesheetCode'}
+ #_stylesheetLibs = {'libName':'libSrcPath'}
+ #_javascriptLibs = {'libName':'libSrcPath'}
+ bodyTagAttribs = {}
+
+ siteDomainName = 'www.example.com'
+ siteCredits = 'Designed & Implemented by Tavis Rudd'
+ siteCopyrightName = "Tavis Rudd"
+
+
+ def metaTags(self):
+ """ """
+ if not hasattr(self,'_metaTags'):
+ return ''
+ else:
+ formatMetaTags(self._metaTags)
+
+ def stylesheetTags(self):
+ stylesheetTagsTxt = ''
+ if hasattr(self,'_stylesheetLibs'):
+ for title, src in self._stylesheetLibs.items():
+ stylesheetTagsTxt += '<LINK REL="stylesheet" TYPE="text/css" HREF="' + str(src) + '">\n'
+
+ if hasattr(self,'_stylesheets') and hasattr(self,'_stylesheetsOrder'):
+ stylesheetTagsTxt += '<STYLE TYPE="text/css"><!--\n'
+ for identifier in self._stylesheetsOrder:
+ if not self._stylesheets.has_key(identifier):
+ warning = '# the identifier ' + identifier + \
+ 'was in self._stylesheetsOrder, but not in self._stylesheets'
+ print warning
+ stylesheetTagsTxt += warning
+ continue
+
+ attribsDict = self._stylesheets[identifier]
+ cssCode = ''
+ attribCode = ''
+ for k, v in attribsDict.items():
+ attribCode += str(k) + ': ' + str(v) + '; '
+ attribCode = attribCode[:-2] # get rid of the last semicolon
+
+ cssCode = '\n' + identifier + ' {' + attribCode + '}'
+ stylesheetTagsTxt += cssCode
+ stylesheetTagsTxt += '\n//--></STYLE>\n'
+
+ return stylesheetTagsTxt.strip()
+
+ def javascriptTags(self):
+ if not hasattr(self,'_javascriptTags'):
+ return ''
+ #else...
+ javascriptTagsTxt = []
+ if self._javascriptTags.has_key('libs'):
+ for title, details in self._javascriptTags['libs'].items():
+ if type(details) not in (types.ListType, types.TupleType):
+ details = ['',details]
+
+ javascriptTagsTxt += ['<SCRIPT LANGUAGE="JavaScript', str(details[0]),
+ '" SRC="', str(details[1]), '">\n']
+ return ''.join(javascriptTagsTxt)
+
+ def bodyTag(self):
+ if not hasattr(self,'bodyTagAttribs'):
+ self.bodyTagAttribs = {}
+
+ bodyTagTxt = formHTMLTag('BODY', self.bodyTagAttribs)
+ return bodyTagTxt
+
+
+ def imgTag(self, src, alt='', width=None, height=None, border=0):
+ """dynamically generate an image tag
+
+ The dimensions are calculated using PIL or ImageMagick if they are available."""
+ try:
+ src = self.serverSidePath(src)
+ print src
+ except:
+ pass
+
+ try:
+ if not width or not height:
+ try: # see if the dimensions can be calc'd with PIL
+ import Image
+ im = Image.open(src)
+ calcWidth, calcHeight = im.size
+ del im
+ except:
+ try: # try imageMagick instead
+ calcWidth, calcHeight = os.popen('identify -format "%w,%h" ' + src).read().split(',')
+ except:
+ pass
+
+ if not width: width = calcWidth
+ if not height: height = calcHeight
+
+ return ''.join(['<IMG SRC="', src, '" WIDTH=', str(width), ' HEIGHT=', str(height),
+ ' ALT="', alt, '" BORDER=', str(border), '>'])
+ except:
+ return ''.join(['<IMG SRC="', src, '" ALT="', alt, '" BORDER=', str(border),'>'])
+
+
+##################################################
+## if run from the command line ##
+
+if __name__ == '__main__':
+ SkeletonPage().runAsMainProgram()
+
diff --git a/src/Templates/__init__.py b/src/Templates/__init__.py
new file mode 100644
index 0000000..4265cc3
--- /dev/null
+++ b/src/Templates/__init__.py
@@ -0,0 +1 @@
+#!/usr/bin/env python
diff --git a/src/Tests.py b/src/Tests.py
new file mode 100644
index 0000000..286912f
--- /dev/null
+++ b/src/Tests.py
@@ -0,0 +1,553 @@
+#!/usr/bin/env python
+# $Id: Tests.py,v 1.1 2001/06/13 03:50:39 tavis_rudd Exp $
+"""Unit-testing framework for the Cheetah package
+
+TODO
+================================================================================
+- Check NameMapper independently
+
+
+Meta-Data
+================================================================================
+Author: Tavis Rudd <tavis@calrudd.com>,
+License: This software is released for unlimited distribution under the
+ terms of the Python license.
+Version: $Revision: 1.1 $
+Start Date: 2001/03/30
+Last Revision Date: $Date: 2001/06/13 03:50:39 $
+"""
+__author__ = "Tavis Rudd <tavis@calrudd.com>"
+__version__ = "$Revision: 1.1 $"[11:-2]
+
+
+##################################################
+## DEPENDENCIES ##
+
+import sys
+import types
+import re
+from copy import deepcopy
+import os.path
+
+
+try:
+ import unittest
+except:
+ import unittest_local_copy as unittest
+
+# intra-package imports ...
+import NameMapper as NameMapper
+from Template import Template
+from Delimeters import delimeters
+
+##################################################
+## CONSTANTS & GLOBALS ##
+
+True = (1==1)
+False = (0==1)
+
+
+##################################################
+## TEST CLASSES ##
+
+class AbstractTestCase(unittest.TestCase):
+ def nameSpace(self):
+ return self._nameSpace
+
+ def __init__(self, title, template, expectedOutput, nameSpace):
+ self.testTitle = title
+ self.template = template
+ self.expectedOutput = expectedOutput
+ self._nameSpace = nameSpace
+ unittest.TestCase.__init__(self, "runTest")
+
+class VerifyTemplateOutput(AbstractTestCase):
+ def runTest(self):
+ servlet = Template(self.template, self.nameSpace())
+ output = servlet.respond()
+
+ assert output == self.expectedOutput, \
+ ('Template output mismatch\n\tTest Title: ' + self.testTitle +
+ '\n\tInput Template = %(template)s\n\tExpected Output = ' +
+ '%(expected)s---\n\tActual Output = %(actual)s---\n' ) \
+ % {'template':self.template, 'expected':self.expectedOutput,
+ 'actual':output}
+
+
+##################################################
+## TEST DATA FOR USE IN THE TEMPLATES ##
+
+class DummyClass:
+ def __str__(self):
+ return 'object'
+
+ def meth(self, arg="arff"):
+ return str(arg)
+
+ def meth1(self, arg="doo"):
+ return arg
+
+ def meth2(self, arg1="a1", arg2="a2"):
+ return str(arg1) + str(arg2)
+
+def dummyFunc(arg="Scooby"):
+ return arg
+
+defaultTestNameSpace = {
+ 'numOne': 1,
+ 'numTwo': 2,
+ 'c':"blarg",
+ 'numFive':5,
+ 'emptyString':'',
+ 'numZero':0,
+ 'func':dummyFunc,
+ 'meth':DummyClass().meth1,
+ 'obj':DummyClass(),
+ 'dict':{'one':'item1',
+ 'two':'item2',
+ 'nestedDict':{1:'nestedItem1',
+ 'two':'nestedItem2'
+ },
+ 'nestedFunc':dummyFunc,
+ },
+ 'dict2': {'one':'item1', 'two':'item2'},
+ 'blockToBeParsed':"""$numOne $numTwo""",
+ }
+
+
+##################################################
+## TEST CASES ##
+
+
+# combo tests
+# negative test cases for expected exceptions
+# black-box vs clear-box testing
+# do some tests that run the Template for long enough to check that the refresh code works
+
+## [testTitle, template, expected output]
+
+posixCases = [
+ ['single $var','$c','blarg'],
+ ['simple $var permutations',
+ """
+$ $500 $*. $. \$var
+'''
+$emptyString $numZero
+$numOne$numTwo
+$numOne and $numTwo $c. $dict.one $dict.nestedDict.1 $dict.nestedFunc
+$func $func. $func(). $func(4). $func('x'). $func("x"). $func("x"*2). $func(arg="x"). $func(arg='x').
+$meth $meth. $meth(). $meth(5). $meth('y'). $meth("y"). $meth("y"*2). $meth(arg="y"). $meth(arg='y').
+$obj $obj.
+$obj.meth $obj.meth. $obj.meth(). $obj.meth(6).
+$obj.meth('z'). $obj.meth("z"). $obj.meth("z"*2). $obj.meth(arg="z"). $obj.meth(arg='z').""",
+
+ """
+$ $500 $*. $. $var
+'''
+ 0
+12
+1 and 2 blarg. item1 nestedItem1 Scooby
+Scooby Scooby. Scooby. 4. x. x. xx. x. x.
+doo doo. doo. 5. y. y. yy. y. y.
+object object.
+arff arff. arff. 6.
+z. z. zz. z. z."""
+ ],
+
+ ]
+
+
+standardVarRegex = delimeters['$']['placeholderRE']
+def convertVars(titlePrefix, replacementString, caseData):
+ caseData = deepcopy(caseData)
+ caseData[0] = titlePrefix + caseData[0]
+ caseData[1] = standardVarRegex.sub(replacementString, caseData[1])
+ return caseData
+
+for i in range(len(posixCases)):
+ posixCases.append(convertVars("Braced cached ${vars} : ", r"${\1}", posixCases[i] ))
+ posixCases.append(convertVars("Dynamic $*vars : ", r"$*\1", posixCases[i] ))
+ posixCases.append(convertVars("Braced Dynamic ${*vars} : ", r"${*\1}", posixCases[i] ))
+ posixCases.append(convertVars("Dynamic Refresh $*15*vars : ", r"$*15*\1", posixCases[i] ))
+ posixCases.append(convertVars("Braced Dynamic Refresh ${*15*vars} : ", r"${*15*\1}", posixCases[i] ))
+
+
+
+## I have disabled nested calls like $var($anotherVar) for the time-being
+#nestedVarTests = [
+# ['$var($var)',
+# """$func($numOne) $meth($numOne) $obj.meth($numOne)""",
+# '1 1 1'],
+# ['$var($var)',
+# """${func($numOne)} ${meth($numOne)} ${obj.meth($numOne)}""",
+# '1 1 1'],
+# ]
+#posixCases += nestedVarTests
+
+commentTests = [
+ ['simple ## comment - with whitespace - should gobble',
+ " ## \n",
+ "",],
+
+ ['simple ## comment - no whitespace',
+ "##",
+ "",],
+
+ ['simple ## comment - after other text',
+ "\nblarg ## foo",
+ "\nblarg ",],
+
+ ['simple ## comment - with #if directive',
+ "##if 0\nblarg\n##end if\n",
+ "blarg\n",],
+
+ ['simple #* comment *# - no whitespace',
+ "#* \naoeuaoeu\naoeuaoeu\n *#",
+ "",],
+
+ ['simple #* comment *# - with whitespace',
+ "\n#* \naoeuaoeu\naoeuaoeu\n *# ",
+ "\n ",],
+ ]
+posixCases += commentTests
+
+forLoopTests = [
+ ['simple #for loop',
+ """#for $i in range(5)\n$i\n#end for\n""",
+ """0\n1\n2\n3\n4\n""",],
+ ['simple #for loop with no whitespace at the end',
+ """#for $i in range(5)\n$i\n#end for""",
+ """0\n1\n2\n3\n4\n""",],
+ ['simple #for loop with no whitespace',
+ """#for $i in range(5)\n$i#end for""",
+ """01234""",],
+ ['simple #for loop with explicit closures',
+ """#for $i in range(5)/#$i#end for/#""",
+ """01234""",],
+
+ ['simple #for loop using another $var',
+ """#for $i in range($numFive)\n$i\n#end for\n""",
+ """0\n1\n2\n3\n4\n""",],
+
+ ['simple #for loop using $dict2',
+ """#for $key, $val in $dict2.items\n$key - $val\n#end for\n""",
+ """one - item1\ntwo - item2\n""",],
+ ['simple #for loop using $dict2, with $ on $key,$val',
+ """#for key, val in $dict2.items\n$key - $val\n#end for\n""",
+ """one - item1\ntwo - item2\n""",],
+
+ ['simple #for loop using $dict2 and another $var ($c)',
+ "#for $key, $val in $dict2.items\n$key - $val - $c\n#end for\n",
+ "one - item1 - blarg\ntwo - item2 - blarg\n",],
+ ['simple #for loop using $dict2 and another $*var ($*c)',
+ "#for $key, $val in $dict2.items\n$key - $val - $*c\n#end for\n",
+ "one - item1 - blarg\ntwo - item2 - blarg\n",],
+ ['simple #for loop using $dict2 and another $*15*var ($*15*c)',
+ "#for $key, $val in $dict2.items\n$key - $val - $*15*c\n#end for\n",
+ "one - item1 - blarg\ntwo - item2 - blarg\n",],
+
+ ['simple #for loop using $dict2 and a method of the local var',
+ "#for $key, $val in $dict2.items\n$key - $val.upper\n#end for\n",
+ "one - ITEM1\ntwo - ITEM2\n",],
+ ]
+posixCases += forLoopTests
+
+
+ifBlockTests = [
+ ['simple #if block',
+ "#if 1\n$c\n#end if\n",
+ "blarg\n",],
+
+ ['simple #if block with no trailing whitespace',
+ "#if 1\n$c\n#end if",
+ "blarg\n",],
+
+ ['simple #if block with explicit closures',
+ "#if 1/#$c#end if/#",
+ "blarg",],
+
+ ['simple #if block using $numOne',
+ "#if $numOne\n$c\n#end if\n",
+ "blarg\n",],
+
+ ['simple #if block using a $numZero',
+ "#if $numZero\n$c\n#end if\n",
+ "",],
+
+ ['simple #if block using a $emptyString',
+ "#if $emptyString\n$c\n#end if\n",
+ "",],
+
+ ['simple #if... #else ... block using a $emptyString',
+ "#if $emptyString\n$c\n#else\n$c - $c#end if\n",
+ "blarg - blarg",],
+
+ ['simple #if... #elif ... #else ... block using a $emptyString',
+ "#if $emptyString\n$c\n#elif $numOne\n$numOne\n#else\n$c - $c#end if\n",
+ "1\n",],
+
+ ['simple "#if not" test',
+ "#if not $emptyString\n$c\n#elif $numOne\n$numOne\n#else\n$c - $c#end if\n",
+ "blarg\n",],
+
+ ## These currently fail @@work on this
+ #['simple #if block using a $*emptyString',
+ # "#if $*emptyString\n$c\n#end if\n",
+ # "",],
+
+ #['simple #if block using a ${emptyString}',
+ # "#if ${emptyString}\n$c\n#end if\n",
+ # "",],
+
+ ]
+posixCases += ifBlockTests
+
+blockTests = [
+ ['simple #block - with no whitespace',
+ "#block testBlock\nthis is a\ntest block\n#end block testBlock\n",
+ "this is a\ntest block\n",],
+
+ ['simple #block - with whitespace - should gobble',
+ " #block testBlock\nthis is a\ntest block\n #end block testBlock\n",
+ "this is a\ntest block\n",],
+
+ ['simple #block - with explicit closures',
+ "#block testBlock/#this is a\ntest block#end block testBlock/#\n",
+ "this is a\ntest block\n",],
+
+ ['#block - with explicit closures and surrounding whitespace',
+ " #block testBlock/#this is a\ntest block#end block testBlock/# \n",
+ " this is a\ntest block \n",],
+
+ ['#block - long block test',
+ '#block longBlock\n' + ' aoeu aoeu aoeu aoeuoae ao uaoeu aoeu aoeu\n'*2000 +
+ '#end block longBlock',
+ ' aoeu aoeu aoeu aoeuoae ao uaoeu aoeu aoeu\n'*2000,],
+
+
+ ]
+posixCases += blockTests
+
+
+macroTests = [
+ ['simple #macro - with no whitespace',
+ "#macro testMacro()\nthis is a\ntest block\n#end macro",
+ "",],
+ ['simple #macro + call - with no whitespace',
+ "#macro testMacro()\nthis is a\ntest block\n#end macro\n#testMacro()",
+ "this is a\ntest block",],
+
+ ['simple #macro - with whitespace - should gobble',
+ " #macro testMacro()\nthis is a\ntest block\n#end macro ",
+ "",],
+
+ ['simple #macro + call - with whitespace - should gobble',
+ " #macro testMacro()\nthis is a\ntest block\n#end macro \n#testMacro()",
+ "this is a\ntest block",],
+
+ ['simple #macro + call - with an arg',
+ "#macro testMacro(a=1234)\nthis is a\ntest block $a\n#end macro\n#testMacro()",
+ "this is a\ntest block 1234",],
+
+ ['simple #macro + call - with an arg, using arg in call',
+ "#macro testMacro(a=1234)\nthis is a\ntest block $a\n#end macro\n#testMacro(9876)",
+ "this is a\ntest block 9876",],
+
+ ['simple #macro + call - with two args, using one',
+ """#macro testMacro(a=1234, b='blarg')\nthis is a\ntest block $a $b\n#end macro\n#testMacro(9876)""",
+ "this is a\ntest block 9876 blarg",],
+
+ ['simple #macro + call - with two args, using both',
+ """#macro testMacro(a=1234, b='blarg')\nthis is a\ntest block $a $b\n#end macro\n#testMacro(5,$numOne)""",
+ "this is a\ntest block 5 1",],
+
+ ]
+posixCases += macroTests
+
+
+setTests = [
+ ['simple #set',
+ "#set $testVar = 'blarg'",
+ "",],
+
+ ['simple #set - with no whitespace',
+ "#set $testVar='blarg'",
+ "",],
+
+ ['simple #set + use of var',
+ "#set $testVar = 'blarg'\n$testVar",
+ "blarg",],
+
+ ['#set with a dictionary',
+ """#set $testDict = {"one":"one1","two":"two2","three":"three3"}
+$testDict.one
+$testDict.two""",
+ """one1
+two2""",],
+
+
+ ['#set with string, then used in #if block',
+ """#set $test='a string'
+#if $test/#blarg#end if""",
+ "blarg",],
+
+ ]
+
+posixCases += setTests
+
+
+rawTests = [
+ ['simple #raw - with no whitespace',
+ "#raw",
+ "",],
+
+ ['#raw followed by $vars',
+ "#raw\n$varName1 $varName2",
+ "$varName1 $varName2",],
+
+ ['#raw followed by $vars, and preceeded by real $vars',
+ "$numOne\n#raw\n$varName1 $varName2",
+ "1\n$varName1 $varName2",],
+
+ ['#raw and #end raw surrounded by real $vars',
+ "$numOne\n#raw\n$varName1 $varName2\n#end raw\n$numTwo",
+ "1\n$varName1 $varName2\n2",],
+
+ ]
+posixCases += rawTests
+
+includeTests = [
+ ['simple #include of $emptyString - with no whitespace',
+ "#include $emptyString",
+ "",],
+
+ ['simple #include of $blockToBeParsed - with no whitespace',
+ "#include $blockToBeParsed",
+ "1 2",],
+
+ ['simple #include of $blockToBeParsed - with whitespace',
+ "\n#include $blockToBeParsed\n",
+ "\n1 2",],
+
+ ['simple #include of file - with no whitespace',
+ "#include 'parseTest.txt'",
+ "1 2",],
+
+ ['simple #include of file - with whitespace',
+ "#include 'parseTest.txt'",
+ "1 2",],
+
+ ]
+posixCases += includeTests
+
+
+includeRawTests = [
+ ['simple #include raw of $emptyString - with no whitespace',
+ "#include raw $emptyString",
+ "",],
+
+ ['simple #include raw of $blockToBeParsed - with no whitespace',
+ "#include raw $blockToBeParsed",
+ "$numOne $numTwo",],
+
+ ['simple #include raw of file - with no whitespace',
+ "#include raw 'parseTest.txt'",
+ "$numOne $numTwo",],
+
+ ['simple #include raw of file - with whitespace',
+ "#include raw 'parseTest.txt'",
+ "$numOne $numTwo",],
+
+ ]
+posixCases += includeRawTests
+
+callMacroTests = [
+
+ ['simple #macro + explicit #callMacro call',
+ """#macro testMacro(a=1234, b='blarg',c='argC')
+this is a
+test block $a $b $c
+#end macro
+#callMacro testMacro(a=9876)
+#arg b
+joe
+#end arg
+
+#arg c
+bloggs
+#end arg
+
+#end callMacro
+""",
+ "this is a\ntest block 9876 joe bloggs",],
+
+ ]
+posixCases += callMacroTests
+
+
+#extendTests = [
+# ['simple #extend - with no whitespace',
+# "#extend Cheetah.Templates.SkeletonPage",
+# "",],
+# ]
+#posixCases += extendTests
+# @@ at the moment the #entend directive is only caught by Servlet.extendTemplate()
+
+
+
+windowsCases = deepcopy(posixCases)
+for case in windowsCases:
+ case[1] = case[1].replace("\n","\r\n")
+ case[2] = case[2].replace("\n","\r\n")
+
+# the dataDirectiveTests must be added after the windows line ending conversion
+# as \r\n appears to be an invalid line ending for python code chunks that are
+# exec'd on Posix systems
+
+dataDirectiveTests = [
+ ['simple #data block + use of the vars that are assigned',
+ """#data
+testVar = 1234
+testVar2 = 'aoeu'
+#end data
+$testVar
+$testVar2
+""",
+ "1234\naoeu\n",],
+ ]
+posixCases += dataDirectiveTests
+
+suiteData = [
+ ['posixCases', posixCases],
+ ['windowsCases', windowsCases],
+ ]
+
+
+def buildTestSuite(suiteTitle, testCasesData):
+ suite = unittest.TestSuite()
+ for testData in testCasesData:
+ suite.addTest( VerifyTemplateOutput(
+ suiteTitle + ": " + testData[0], testData[1], testData[2], defaultTestNameSpace) )
+ return suite
+
+def allSuites():
+ suitesList = []
+ for testSuite in suiteData:
+ suitesList.append( buildTestSuite(testSuite[0], testSuite[1]) )
+
+ return unittest.TestSuite(suitesList)
+
+def runTests():
+ runner = unittest.TextTestRunner(stream=sys.stdout)
+ unittest.main(defaultTest='allSuites', testRunner=runner)
+
+##################################################
+## if run from the command line ##
+if __name__ == '__main__':
+ if not os.path.exists('parseTest.txt'):
+ fp = open('parseTest.txt','w')
+ fp.write("$numOne $numTwo")
+ fp.flush()
+ fp.close
+ runTests()
+
diff --git a/src/Utilities.py b/src/Utilities.py
new file mode 100644
index 0000000..f739abf
--- /dev/null
+++ b/src/Utilities.py
@@ -0,0 +1,89 @@
+#!/usr/bin/env python
+# $Id: Utilities.py,v 1.1 2001/06/13 03:50:39 tavis_rudd Exp $
+"""Utility classes and functions used in the Cheetah package
+
+
+Meta-Data
+================================================================================
+Author: Tavis Rudd <tavis@calrudd.com>
+License: This software is released for unlimited distribution under the
+ terms of the Python license.
+Version: $Revision: 1.1 $
+Start Date: 2001/03/30
+Last Revision Date: $Date: 2001/06/13 03:50:39 $
+"""
+__author__ = "Tavis Rudd <tavis@calrudd.com>"
+__version__ = "$Revision: 1.1 $"[11:-2]
+
+
+##################################################
+## DEPENDENCIES ##
+
+import re
+import types
+
+##################################################
+## CONSTANTS & GLOBALS ##
+
+True = (1==1)
+False = (0==1)
+
+##################################################
+## FUNCTIONS ##
+
+def insertLineNums(string):
+ lineNum = [0,]
+ def lineNums(match, lineNum=lineNum):
+ lineNum[0] +=1
+ return "%(match)s%(line)-3d|"% {'match':match.group(), 'line':lineNum[0]}
+
+ if string.find('\n') != -1:
+ return re.sub(r'^|\n', lineNums, string)
+ else:
+ return "1 |" + string
+
+def getLines(string, lineNums):
+ lines = string.split('\n')
+ return lines[lineNums]
+
+def lineNumFromPos(string, pos):
+ return len(string[0:pos].split('\n'))
+
+
+def removeDuplicateValues(list):
+ """remove all duplicate values in a list"""
+ listCopy = []
+ while len(list) > 0:
+ if not list[0] in listCopy:
+ listCopy.append(list[0])
+ del list[0]
+
+ return listCopy
+
+def mergeNestedDictionaries(dict1, dict2):
+ """Recursively merge the values of dict2 into dict1.
+
+ This little function is very handy for selectively overriding settings in a
+ settings dictionary that has a nested structure. """
+
+ newDict = dict1.copy()
+ for key,val in dict2.items():
+ if newDict.has_key(key) and type(val) == types.DictType and type(newDict[key]) == types.DictType:
+ newDict[key] = mergeNestedDictionaries(newDict[key], val)
+ else:
+ newDict[key] = val
+ return newDict
+
+
+
+##################################################
+## CLASSES ##
+
+#none yet
+
+##################################################
+## if run from the command line ##
+
+if __name__ == '__main__':
+ pass
+
diff --git a/src/Validators.py b/src/Validators.py
new file mode 100644
index 0000000..51fc625
--- /dev/null
+++ b/src/Validators.py
@@ -0,0 +1,59 @@
+#!/usr/bin/env python
+# $Id: Validators.py,v 1.1 2001/06/13 03:50:39 tavis_rudd Exp $
+"""Code validation tools for the Cheetah package
+
+
+Meta-Data
+================================================================================
+Author: Tavis Rudd <tavis@calrudd.com>
+License: This software is released for unlimited distribution under the
+ terms of the Python license.
+Version: $Revision: 1.1 $
+Start Date: 2001/03/30
+Last Revision Date: $Date: 2001/06/13 03:50:39 $
+"""
+__author__ = "Tavis Rudd <tavis@calrudd.com>"
+__version__ = "$Revision: 1.1 $"[11:-2]
+
+##################################################
+## DEPENDENCIES ##
+
+import re, types
+
+
+##################################################
+## CONSTANTS & GLOBALS ##
+
+True = (1==1)
+False = (0==1)
+
+##################################################
+## FUNCTIONS ##
+
+def validateDisplayLogicCode(templateObj, displayLogic):
+ """check for any unsafe code in displayLogic - NOT IMPLEMENTED YET - this will
+ implement a form of 'Safe Delegation' as this term is used in Zope and Spectra"""
+ pass
+
+def validateArgStringInPlaceholderTag(templateObj, argString):
+ """check for any unsafe code in argStrings - NOT IMPLEMENTED YET - this will
+ implement a form of 'Safe Delegation' as this term is used in Zope and Spectra"""
+ pass
+
+def validateIncludeDirective(templateObj, includeDirective):
+ """check for any unsafe code in includeDirective - NOT IMPLEMENTED YET - this
+ will implement a form of 'Safe Delegation' as this term is used in Zope and
+ Spectra"""
+ pass
+
+def validateMacroDirective(templateObj, macroDirective):
+ """check for any unsafe code in macroDirective - NOT IMPLEMENTED YET - this will
+ implement a form of 'Safe Delegation' as this term is used in Zope and Spectra"""
+
+ pass
+
+def validateSetDirective(templateObj, setDirective):
+ """check for any unsafe code in setDirective - NOT IMPLEMENTED YET - this will
+ implement a form of 'Safe Delegation' as this term is used in Zope and Spectra"""
+ pass
+
diff --git a/src/Version.py b/src/Version.py
new file mode 100644
index 0000000..54e34e5
--- /dev/null
+++ b/src/Version.py
@@ -0,0 +1 @@
+version = '0.9.6'
diff --git a/src/__init__.py b/src/__init__.py
new file mode 100644
index 0000000..2d19864
--- /dev/null
+++ b/src/__init__.py
@@ -0,0 +1,56 @@
+#!/usr/bin/env python
+# $Id: __init__.py,v 1.1 2001/06/13 03:50:39 tavis_rudd Exp $
+
+"""Cheetah is a Python-powered template engine and code-generator.
+It similar to the Jakarta project's Velocity.
+
+Homepage
+================================================================================
+http://www.CheetahTemplate.org
+
+Mailing list
+================================================================================
+cheetahtemplate-discuss@lists.sourceforge.net
+Subscribe at
+http://lists.sourceforge.net/lists/listinfo/cheetahtemplate-discuss
+
+Documentation
+================================================================================
+
+For a high-level introduction to Cheetah please refer to the User's Guide
+in the docs/ directory of the Cheetah distribution.
+
+Installation
+================================================================================
+Cheetah can be run a directory in the from the system-wide Python path or
+from a directory in a user's Python path.
+
+To install Cheetah for a single user:
+ - copy the 'src' sub-directory to a directory called 'Cheetah' that is in the
+ user's PYTHON_PATH
+
+To install Cheetah for system-wide use:
+ - on Posix systems (AIX, Solaris, Linux, IRIX, etc.) become the 'root' user
+ and run: python ./setup.py install
+
+ - On non-posix systems, such as Windows NT, login as an administrator and
+ type this at the command-line: python setup.py install
+
+On Posix systems, the system-wide installation will also install the Cheetah's
+command-line compiler program, TScompile, to a system-wide executable path such as
+/usr/local/bin.
+
+
+Meta-Data
+================================================================================
+Authors: The Cheetah Development Team (Tavis Rudd, Mike Orr, Chuck Esterbrook
+ Ian Bicking, Tom Schwaller)
+License: This software is released for unlimited distribution under the
+ terms of the Python license.
+Version: $Revision: 1.1 $
+Start Date: 2001/03/30
+Last Revision Date: $Date: 2001/06/13 03:50:39 $
+"""
+__author__ = "Tavis Rudd <tavis@calrudd.com>"
+__version__ = "$Revision: 1.1 $"[11:-2]
+
diff --git a/src/unittest_local_copy.py b/src/unittest_local_copy.py
new file mode 100644
index 0000000..4cd84d4
--- /dev/null
+++ b/src/unittest_local_copy.py
@@ -0,0 +1,711 @@
+#!/usr/bin/env python
+'''
+Python unit testing framework, based on Erich Gamma's JUnit and Kent Beck's
+Smalltalk testing framework.
+
+This module contains the core framework classes that form the basis of
+specific test cases and suites (TestCase, TestSuite etc.), and also a
+text-based utility class for running the tests and reporting the results
+(TextTestRunner).
+
+Simple usage:
+
+ import unittest
+
+ class IntegerArithmenticTestCase(unittest.TestCase):
+ def testAdd(self): ## test method names begin 'test*'
+ self.assertEquals((1 + 2), 3)
+ self.assertEquals(0 + 1, 1)
+ def testMultiply(self);
+ self.assertEquals((0 * 10), 0)
+ self.assertEquals((5 * 8), 40)
+
+ if __name__ == '__main__':
+ unittest.main()
+
+Further information is available in the bundled documentation, and from
+
+ http://pyunit.sourceforge.net/
+
+Copyright (c) 1999, 2000, 2001 Steve Purcell
+This module is free software, and you may redistribute it and/or modify
+it under the same terms as Python itself, so long as this copyright message
+and disclaimer are retained in their original form.
+
+IN NO EVENT SHALL THE AUTHOR BE LIABLE TO ANY PARTY FOR DIRECT, INDIRECT,
+SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OF
+THIS CODE, EVEN IF THE AUTHOR HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+DAMAGE.
+
+THE AUTHOR SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+PARTICULAR PURPOSE. THE CODE PROVIDED HEREUNDER IS ON AN "AS IS" BASIS,
+AND THERE IS NO OBLIGATION WHATSOEVER TO PROVIDE MAINTENANCE,
+SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+'''
+
+__author__ = "Steve Purcell"
+__email__ = "stephen_purcell at yahoo dot com"
+__version__ = "$Revision: 1.1 $"[11:-2]
+
+import time
+import sys
+import traceback
+import string
+import os
+import types
+
+##############################################################################
+# Test framework core
+##############################################################################
+
+class TestResult:
+ """Holder for test result information.
+
+ Test results are automatically managed by the TestCase and TestSuite
+ classes, and do not need to be explicitly manipulated by writers of tests.
+
+ Each instance holds the total number of tests run, and collections of
+ failures and errors that occurred among those test runs. The collections
+ contain tuples of (testcase, exceptioninfo), where exceptioninfo is a
+ tuple of values as returned by sys.exc_info().
+ """
+ def __init__(self):
+ self.failures = []
+ self.errors = []
+ self.testsRun = 0
+ self.shouldStop = 0
+
+ def startTest(self, test):
+ "Called when the given test is about to be run"
+ self.testsRun = self.testsRun + 1
+
+ def stopTest(self, test):
+ "Called when the given test has been run"
+ pass
+
+ def addError(self, test, err):
+ "Called when an error has occurred"
+ self.errors.append((test, err))
+
+ def addFailure(self, test, err):
+ "Called when a failure has occurred"
+ self.failures.append((test, err))
+
+ def addSuccess(self, test):
+ "Called when a test has completed successfully"
+ pass
+
+ def wasSuccessful(self):
+ "Tells whether or not this result was a success"
+ return len(self.failures) == len(self.errors) == 0
+
+ def stop(self):
+ "Indicates that the tests should be aborted"
+ self.shouldStop = 1
+
+ def __repr__(self):
+ return "<%s run=%i errors=%i failures=%i>" % \
+ (self.__class__, self.testsRun, len(self.errors),
+ len(self.failures))
+
+
+class TestCase:
+ """A class whose instances are single test cases.
+
+ By default, the test code itself should be placed in a method named
+ 'runTest'.
+
+ If the fixture may be used for many test cases, create as
+ many test methods as are needed. When instantiating such a TestCase
+ subclass, specify in the constructor arguments the name of the test method
+ that the instance is to execute.
+
+ Test authors should subclass TestCase for their own tests. Construction
+ and deconstruction of the test's environment ('fixture') can be
+ implemented by overriding the 'setUp' and 'tearDown' methods respectively.
+
+ If it is necessary to override the __init__ method, the base class
+ __init__ method must always be called. It is important that subclasses
+ should not change the signature of their __init__ method, since instances
+ of the classes are instantiated automatically by parts of the framework
+ in order to be run.
+ """
+
+ # This attribute determines which exception will be raised when
+ # the instance's assertion methods fail; test methods raising this
+ # exception will be deemed to have 'failed' rather than 'errored'
+
+ failureException = AssertionError
+
+ def __init__(self, methodName='runTest'):
+ """Create an instance of the class that will use the named test
+ method when executed. Raises a ValueError if the instance does
+ not have a method with the specified name.
+ """
+ try:
+ self.__testMethodName = methodName
+ testMethod = getattr(self, methodName)
+ self.__testMethodDoc = testMethod.__doc__
+ except AttributeError:
+ raise ValueError, "no such test method in %s: %s" % \
+ (self.__class__, methodName)
+
+ def setUp(self):
+ "Hook method for setting up the test fixture before exercising it."
+ pass
+
+ def tearDown(self):
+ "Hook method for deconstructing the test fixture after testing it."
+ pass
+
+ def countTestCases(self):
+ return 1
+
+ def defaultTestResult(self):
+ return TestResult()
+
+ def shortDescription(self):
+ """Returns a one-line description of the test, or None if no
+ description has been provided.
+
+ The default implementation of this method returns the first line of
+ the specified test method's docstring.
+ """
+ doc = self.__testMethodDoc
+ return doc and string.strip(string.split(doc, "\n")[0]) or None
+
+ def id(self):
+ return "%s.%s" % (self.__class__, self.__testMethodName)
+
+ def __str__(self):
+ return "%s (%s)" % (self.__testMethodName, self.__class__)
+
+ def __repr__(self):
+ return "<%s testMethod=%s>" % \
+ (self.__class__, self.__testMethodName)
+
+ def run(self, result=None):
+ return self(result)
+
+ def __call__(self, result=None):
+ if result is None: result = self.defaultTestResult()
+ result.startTest(self)
+ testMethod = getattr(self, self.__testMethodName)
+ try:
+ try:
+ self.setUp()
+ except:
+ result.addError(self,self.__exc_info())
+ return
+
+ ok = 0
+ try:
+ testMethod()
+ ok = 1
+ except self.failureException, e:
+ result.addFailure(self,self.__exc_info())
+ except:
+ result.addError(self,self.__exc_info())
+
+ try:
+ self.tearDown()
+ except:
+ result.addError(self,self.__exc_info())
+ ok = 0
+ if ok: result.addSuccess(self)
+ finally:
+ result.stopTest(self)
+
+ def debug(self):
+ """Run the test without collecting errors in a TestResult"""
+ self.setUp()
+ getattr(self, self.__testMethodName)()
+ self.tearDown()
+
+ def __exc_info(self):
+ """Return a version of sys.exc_info() with the traceback frame
+ minimised; usually the top level of the traceback frame is not
+ needed.
+ """
+ exctype, excvalue, tb = sys.exc_info()
+ if sys.platform[:4] == 'java': ## tracebacks look different in Jython
+ return (exctype, excvalue, tb)
+ newtb = tb.tb_next
+ if newtb is None:
+ return (exctype, excvalue, tb)
+ return (exctype, excvalue, newtb)
+
+ def fail(self, msg=None):
+ """Fail immediately, with the given message."""
+ raise self.failureException, msg
+
+ def failIf(self, expr, msg=None):
+ "Fail the test if the expression is true."
+ if expr: raise self.failureException, msg
+
+ def failUnless(self, expr, msg=None):
+ """Fail the test unless the expression is true."""
+ if not expr: raise self.failureException, msg
+
+ def failUnlessRaises(self, excClass, callableObj, *args, **kwargs):
+ """Fail unless an exception of class excClass is thrown
+ by callableObj when invoked with arguments args and keyword
+ arguments kwargs. If a different type of exception is
+ thrown, it will not be caught, and the test case will be
+ deemed to have suffered an error, exactly as for an
+ unexpected exception.
+ """
+ try:
+ apply(callableObj, args, kwargs)
+ except excClass:
+ return
+ else:
+ if hasattr(excClass,'__name__'): excName = excClass.__name__
+ else: excName = str(excClass)
+ raise self.failureException, excName
+
+ def failUnlessEqual(self, first, second, msg=None):
+ """Fail if the two objects are unequal as determined by the '!='
+ operator.
+ """
+ if first != second:
+ raise self.failureException, (msg or '%s != %s' % (first, second))
+
+ def failIfEqual(self, first, second, msg=None):
+ """Fail if the two objects are equal as determined by the '=='
+ operator.
+ """
+ if first == second:
+ raise self.failureException, (msg or '%s == %s' % (first, second))
+
+ assertEqual = assertEquals = failUnlessEqual
+
+ assertNotEqual = assertNotEquals = failIfEqual
+
+ assertRaises = failUnlessRaises
+
+ assert_ = failUnless
+
+
+
+class TestSuite:
+ """A test suite is a composite test consisting of a number of TestCases.
+
+ For use, create an instance of TestSuite, then add test case instances.
+ When all tests have been added, the suite can be passed to a test
+ runner, such as TextTestRunner. It will run the individual test cases
+ in the order in which they were added, aggregating the results. When
+ subclassing, do not forget to call the base class constructor.
+ """
+ def __init__(self, tests=()):
+ self._tests = []
+ self.addTests(tests)
+
+ def __repr__(self):
+ return "<%s tests=%s>" % (self.__class__, self._tests)
+
+ __str__ = __repr__
+
+ def countTestCases(self):
+ cases = 0
+ for test in self._tests:
+ cases = cases + test.countTestCases()
+ return cases
+
+ def addTest(self, test):
+ self._tests.append(test)
+
+ def addTests(self, tests):
+ for test in tests:
+ self.addTest(test)
+
+ def run(self, result):
+ return self(result)
+
+ def __call__(self, result):
+ for test in self._tests:
+ if result.shouldStop:
+ break
+ test(result)
+ return result
+
+ def debug(self):
+ """Run the tests without collecting errors in a TestResult"""
+ for test in self._tests: test.debug()
+
+
+class FunctionTestCase(TestCase):
+ """A test case that wraps a test function.
+
+ This is useful for slipping pre-existing test functions into the
+ PyUnit framework. Optionally, set-up and tidy-up functions can be
+ supplied. As with TestCase, the tidy-up ('tearDown') function will
+ always be called if the set-up ('setUp') function ran successfully.
+ """
+
+ def __init__(self, testFunc, setUp=None, tearDown=None,
+ description=None):
+ TestCase.__init__(self)
+ self.__setUpFunc = setUp
+ self.__tearDownFunc = tearDown
+ self.__testFunc = testFunc
+ self.__description = description
+
+ def setUp(self):
+ if self.__setUpFunc is not None:
+ self.__setUpFunc()
+
+ def tearDown(self):
+ if self.__tearDownFunc is not None:
+ self.__tearDownFunc()
+
+ def runTest(self):
+ self.__testFunc()
+
+ def id(self):
+ return self.__testFunc.__name__
+
+ def __str__(self):
+ return "%s (%s)" % (self.__class__, self.__testFunc.__name__)
+
+ def __repr__(self):
+ return "<%s testFunc=%s>" % (self.__class__, self.__testFunc)
+
+ def shortDescription(self):
+ if self.__description is not None: return self.__description
+ doc = self.__testFunc.__doc__
+ return doc and string.strip(string.split(doc, "\n")[0]) or None
+
+
+
+##############################################################################
+# Locating and loading tests
+##############################################################################
+
+class TestLoader:
+ """This class is responsible for loading tests according to various
+ criteria and returning them wrapped in a Test
+ """
+ testMethodPrefix = 'test'
+ sortTestMethodsUsing = cmp
+ suiteClass = TestSuite
+
+ def loadTestsFromTestCase(self, testCaseClass):
+ """Return a suite of all tests cases contained in testCaseClass"""
+ return self.suiteClass(map(testCaseClass,
+ self.getTestCaseNames(testCaseClass)))
+
+ def loadTestsFromModule(self, module):
+ """Return a suite of all tests cases contained in the given module"""
+ tests = []
+ for name in dir(module):
+ obj = getattr(module, name)
+ if type(obj) == types.ClassType and issubclass(obj, TestCase):
+ tests.append(self.loadTestsFromTestCase(obj))
+ return self.suiteClass(tests)
+
+ def loadTestsFromName(self, name, module=None):
+ """Return a suite of all tests cases given a string specifier.
+
+ The name may resolve either to a module, a test case class, a
+ test method within a test case class, or a callable object which
+ returns a TestCase or TestSuite instance.
+
+ The method optionally resolves the names relative to a given module.
+ """
+ parts = string.split(name, '.')
+ if module is None:
+ if not parts:
+ raise ValueError, "incomplete test name: %s" % name
+ else:
+ parts_copy = parts[:]
+ while parts_copy:
+ try:
+ module = __import__(string.join(parts_copy,'.'))
+ break
+ except ImportError:
+ del parts_copy[-1]
+ if not parts_copy: raise
+ parts = parts[1:]
+ obj = module
+ for part in parts:
+ obj = getattr(obj, part)
+
+ if type(obj) == types.ModuleType:
+ return self.loadTestsFromModule(obj)
+ elif type(obj) == types.ClassType and issubclass(obj, TestCase):
+ return self.loadTestsFromTestCase(obj)
+ elif type(obj) == types.UnboundMethodType:
+ return obj.im_class(obj.__name__)
+ elif callable(obj):
+ test = obj()
+ if not isinstance(test, TestCase) and \
+ not isinstance(test, TestSuite):
+ raise ValueError, \
+ "calling %s returned %s, not a test" % obj,test
+ return test
+ else:
+ raise ValueError, "don't know how to make test from: %s" % obj
+
+ def loadTestsFromNames(self, names, module=None):
+ """Return a suite of all tests cases found using the given sequence
+ of string specifiers. See 'loadTestsFromName()'.
+ """
+ suites = []
+ for name in names:
+ suites.append(self.loadTestsFromName(name, module))
+ return self.suiteClass(suites)
+
+ def getTestCaseNames(self, testCaseClass):
+ """Return a sorted sequence of method names found within testCaseClass
+ """
+ testFnNames = filter(lambda n,p=self.testMethodPrefix: n[:len(p)] == p,
+ dir(testCaseClass))
+ for baseclass in testCaseClass.__bases__:
+ for testFnName in self.getTestCaseNames(baseclass):
+ if testFnName not in testFnNames: # handle overridden methods
+ testFnNames.append(testFnName)
+ if self.sortTestMethodsUsing:
+ testFnNames.sort(self.sortTestMethodsUsing)
+ return testFnNames
+
+
+
+defaultTestLoader = TestLoader()
+
+
+##############################################################################
+# Patches for old functions: these functions should be considered obsolete
+##############################################################################
+
+def _makeLoader(prefix, sortUsing, suiteClass=None):
+ loader = TestLoader()
+ loader.sortTestMethodsUsing = sortUsing
+ loader.testMethodPrefix = prefix
+ if suiteClass: loader.suiteClass = suiteClass
+ return loader
+
+def getTestCaseNames(testCaseClass, prefix, sortUsing=cmp):
+ return _makeLoader(prefix, sortUsing).getTestCaseNames(testCaseClass)
+
+def makeSuite(testCaseClass, prefix='test', sortUsing=cmp, suiteClass=TestSuite):
+ return _makeLoader(prefix, sortUsing, suiteClass).loadTestsFromTestCase(testCaseClass)
+
+def findTestCases(module, prefix='test', sortUsing=cmp, suiteClass=TestSuite):
+ return _makeLoader(prefix, sortUsing, suiteClass).loadTestsFromModule(module)
+
+
+##############################################################################
+# Text UI
+##############################################################################
+
+class _WritelnDecorator:
+ """Used to decorate file-like objects with a handy 'writeln' method"""
+ def __init__(self,stream):
+ self.stream = stream
+
+ def __getattr__(self, attr):
+ return getattr(self.stream,attr)
+
+ def writeln(self, *args):
+ if args: apply(self.write, args)
+ self.write('\n') # text-mode streams translate to \r\n if needed
+
+
+class _TextTestResult(TestResult):
+ """A test result class that can print formatted text results to a stream.
+
+ Used by TextTestRunner.
+ """
+ separator1 = '=' * 70
+ separator2 = '-' * 70
+
+ def __init__(self, stream, descriptions, verbosity):
+ TestResult.__init__(self)
+ self.stream = stream
+ self.showAll = verbosity > 1
+ self.dots = verbosity == 1
+ self.descriptions = descriptions
+
+ def getDescription(self, test):
+ if self.descriptions:
+ return test.shortDescription() or str(test)
+ else:
+ return str(test)
+
+ def startTest(self, test):
+ TestResult.startTest(self, test)
+ if self.showAll:
+ self.stream.write(self.getDescription(test))
+ self.stream.write(" ... ")
+
+ def addSuccess(self, test):
+ TestResult.addSuccess(self, test)
+ if self.showAll:
+ self.stream.writeln("ok")
+ elif self.dots:
+ self.stream.write('.')
+
+ def addError(self, test, err):
+ TestResult.addError(self, test, err)
+ if self.showAll:
+ self.stream.writeln("ERROR")
+ elif self.dots:
+ self.stream.write('E')
+ if err[0] is KeyboardInterrupt:
+ self.shouldStop = 1
+
+ def addFailure(self, test, err):
+ TestResult.addFailure(self, test, err)
+ if self.showAll:
+ self.stream.writeln("FAIL")
+ elif self.dots:
+ self.stream.write('F')
+
+ def printErrors(self):
+ if self.dots or self.showAll:
+ self.stream.writeln()
+ self.printErrorList('ERROR', self.errors)
+ self.printErrorList('FAIL', self.failures)
+
+ def printErrorList(self, flavour, errors):
+ for test, err in errors:
+ self.stream.writeln(self.separator1)
+ self.stream.writeln("%s: %s" % (flavour,self.getDescription(test)))
+ self.stream.writeln(self.separator2)
+ for line in apply(traceback.format_exception, err):
+ for l in string.split(line,"\n")[:-1]:
+ self.stream.writeln("%s" % l)
+
+
+class TextTestRunner:
+ """A test runner class that displays results in textual form.
+
+ It prints out the names of tests as they are run, errors as they
+ occur, and a summary of the results at the end of the test run.
+ """
+ def __init__(self, stream=sys.stderr, descriptions=1, verbosity=1):
+ self.stream = _WritelnDecorator(stream)
+ self.descriptions = descriptions
+ self.verbosity = verbosity
+
+ def _makeResult(self):
+ return _TextTestResult(self.stream, self.descriptions, self.verbosity)
+
+ def run(self, test):
+ "Run the given test case or test suite."
+ result = self._makeResult()
+ startTime = time.time()
+ test(result)
+ stopTime = time.time()
+ timeTaken = float(stopTime - startTime)
+ result.printErrors()
+ self.stream.writeln(result.separator2)
+ run = result.testsRun
+ self.stream.writeln("Ran %d test%s in %.3fs" %
+ (run, run == 1 and "" or "s", timeTaken))
+ self.stream.writeln()
+ if not result.wasSuccessful():
+ self.stream.write("FAILED (")
+ failed, errored = map(len, (result.failures, result.errors))
+ if failed:
+ self.stream.write("failures=%d" % failed)
+ if errored:
+ if failed: self.stream.write(", ")
+ self.stream.write("errors=%d" % errored)
+ self.stream.writeln(")")
+ else:
+ self.stream.writeln("OK")
+ return result
+
+
+
+##############################################################################
+# Facilities for running tests from the command line
+##############################################################################
+
+class TestProgram:
+ """A command-line program that runs a set of tests; this is primarily
+ for making test modules conveniently executable.
+ """
+ USAGE = """\
+Usage: %(progName)s [options] [test] [...]
+
+Options:
+ -h, --help Show this message
+ -v, --verbose Verbose output
+ -q, --quiet Minimal output
+
+Examples:
+ %(progName)s - run default set of tests
+ %(progName)s MyTestSuite - run suite 'MyTestSuite'
+ %(progName)s MyTestCase.testSomething - run MyTestCase.testSomething
+ %(progName)s MyTestCase - run all 'test*' test methods
+ in MyTestCase
+"""
+ def __init__(self, module='__main__', defaultTest=None,
+ argv=None, testRunner=None, testLoader=defaultTestLoader):
+ if type(module) == type(''):
+ self.module = __import__(module)
+ for part in string.split(module,'.')[1:]:
+ self.module = getattr(self.module, part)
+ else:
+ self.module = module
+ if argv is None:
+ argv = sys.argv
+ self.verbosity = 1
+ self.defaultTest = defaultTest
+ self.testRunner = testRunner
+ self.testLoader = testLoader
+ self.progName = os.path.basename(argv[0])
+ self.parseArgs(argv)
+ self.runTests()
+
+ def usageExit(self, msg=None):
+ if msg: print msg
+ print self.USAGE % self.__dict__
+ sys.exit(2)
+
+ def parseArgs(self, argv):
+ import getopt
+ try:
+ options, args = getopt.getopt(argv[1:], 'hHvq',
+ ['help','verbose','quiet'])
+ for opt, value in options:
+ if opt in ('-h','-H','--help'):
+ self.usageExit()
+ if opt in ('-q','--quiet'):
+ self.verbosity = 0
+ if opt in ('-v','--verbose'):
+ self.verbosity = 2
+ if len(args) == 0 and self.defaultTest is None:
+ self.test = self.testLoader.loadTestsFromModule(self.module)
+ return
+ if len(args) > 0:
+ self.testNames = args
+ else:
+ self.testNames = (self.defaultTest,)
+ self.createTests()
+ except getopt.error, msg:
+ self.usageExit(msg)
+
+ def createTests(self):
+ self.test = self.testLoader.loadTestsFromNames(self.testNames,
+ self.module)
+
+ def runTests(self):
+ if self.testRunner is None:
+ self.testRunner = TextTestRunner(verbosity=self.verbosity)
+ result = self.testRunner.run(self.test)
+ sys.exit(not result.wasSuccessful())
+
+main = TestProgram
+
+
+##############################################################################
+# Executing this module from the command line
+##############################################################################
+
+if __name__ == "__main__":
+ main(module=None)