summaryrefslogtreecommitdiff
path: root/docs/devel_guide_src
diff options
context:
space:
mode:
authorR. Tyler Ballance <tyler@monkeypox.org>2009-11-16 21:09:13 -0800
committerR. Tyler Ballance <tyler@monkeypox.org>2009-11-16 21:09:13 -0800
commitd9ce7916e309e2393d824e249f512d2629e5e181 (patch)
tree6b7ad5cd6292f6e017e048fbeb4551facbabd174 /docs/devel_guide_src
parente43765a679b84c52df875e9629d303e304af50a1 (diff)
downloadpython-cheetah-docs.tar.gz
Revert "Delete the "old" docs directory to make way for fancy smancy sphinx"docs
This reverts commit 5dc95cfcd015628665d3672e56d0551943b5db6b.
Diffstat (limited to 'docs/devel_guide_src')
-rwxr-xr-xdocs/devel_guide_src/Makefile38
-rwxr-xr-xdocs/devel_guide_src/README9
-rwxr-xr-xdocs/devel_guide_src/bnf.tex7
-rwxr-xr-xdocs/devel_guide_src/cache.tex365
-rwxr-xr-xdocs/devel_guide_src/comments.tex100
-rwxr-xr-xdocs/devel_guide_src/compiler.tex8
-rwxr-xr-xdocs/devel_guide_src/design.tex102
-rwxr-xr-xdocs/devel_guide_src/devel_guide.tex55
-rwxr-xr-xdocs/devel_guide_src/documenting.tex8
-rwxr-xr-xdocs/devel_guide_src/errorHandling.tex309
-rwxr-xr-xdocs/devel_guide_src/files.tex11
-rwxr-xr-xdocs/devel_guide_src/flowControl.tex360
-rwxr-xr-xdocs/devel_guide_src/history.tex92
-rwxr-xr-xdocs/devel_guide_src/inheritanceEtc.tex232
-rwxr-xr-xdocs/devel_guide_src/introduction.tex29
-rwxr-xr-xdocs/devel_guide_src/output.tex282
-rwxr-xr-xdocs/devel_guide_src/parser.tex9
-rw-r--r--docs/devel_guide_src/parserInstructions.tex61
-rwxr-xr-xdocs/devel_guide_src/patching.tex134
-rwxr-xr-xdocs/devel_guide_src/placeholders.tex478
-rwxr-xr-xdocs/devel_guide_src/pyModules.tex246
-rwxr-xr-xdocs/devel_guide_src/safeDelegation.tex44
-rwxr-xr-xdocs/devel_guide_src/template.tex11
23 files changed, 2990 insertions, 0 deletions
diff --git a/docs/devel_guide_src/Makefile b/docs/devel_guide_src/Makefile
new file mode 100755
index 0000000..9e5da96
--- /dev/null
+++ b/docs/devel_guide_src/Makefile
@@ -0,0 +1,38 @@
+# You must change PYTHONSRC to the path of your Python source distributon.
+PYTHONSRC=/home/tavis/tmp/Python-2.2
+DOCNAME=devel_guide
+MKHOWTO=$(PYTHONSRC)/Doc/tools/mkhowto
+MAIN_TEX_FILE= devel_guide.tex
+
+all: ps pdf html htmlMultiPage text
+
+almost-all: ps html htmlMultiPage text
+
+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 -rf ../$(DOCNAME)_html
+ mv $(DOCNAME) ../$(DOCNAME)_html
+
+htmlMultiPage:
+ -rm -rf $(DOCNAME)
+ $(MKHOWTO) --html --iconserver . $(MAIN_TEX_FILE)
+ -rm -rf ../$(DOCNAME)_html_multipage
+ mv $(DOCNAME) ../$(DOCNAME)_html_multipage
+
+text:
+ $(MKHOWTO) --text $(MAIN_TEX_FILE)
+ mv $(DOCNAME).txt ../
+
+clean:
+ -rm -rf $(DOCNAME)
+ -rm -f *.aux *.l2h *~ *.log *.ind *.bkm *.how *.toc
+ -rm -rf ../html
+
diff --git a/docs/devel_guide_src/README b/docs/devel_guide_src/README
new file mode 100755
index 0000000..3b45564
--- /dev/null
+++ b/docs/devel_guide_src/README
@@ -0,0 +1,9 @@
+To build the Cheetah documentation, you need the 'mkhowto' program from
+the Python source distribution. So:
+
+1) Get the Python source distribution and unpack it in some directory.
+
+2) Edit the Cheetah documentation's Makefile and change PYTHONSRC to
+point to the top-level directory of your Python source distribution.
+
+3) Run 'make'.
diff --git a/docs/devel_guide_src/bnf.tex b/docs/devel_guide_src/bnf.tex
new file mode 100755
index 0000000..aa7149c
--- /dev/null
+++ b/docs/devel_guide_src/bnf.tex
@@ -0,0 +1,7 @@
+\section{A BNF Grammar of Cheetah}
+\label{bnf}
+
+
+% Local Variables:
+% TeX-master: "devel_guide"
+% End:
diff --git a/docs/devel_guide_src/cache.tex b/docs/devel_guide_src/cache.tex
new file mode 100755
index 0000000..043b8cf
--- /dev/null
+++ b/docs/devel_guide_src/cache.tex
@@ -0,0 +1,365 @@
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\section{Caching placeholders and \#cache}
+\label{cache}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsection{Dynamic placeholder -- no cache}
+\label{cache.dynamic}
+
+The template:
+\begin{verbatim}
+Dynamic variable: $voom
+\end{verbatim}
+
+The command line and the output:
+\begin{verbatim}
+% voom='Voom!' python x.py --env
+Dynamic variable: Voom!
+\end{verbatim}
+
+The generated code:
+\begin{verbatim}
+write('Dynamic variable: ')
+write(filter(VFS(SL,"voom",1))) # generated from '$voom' at line 1, col 20.
+write('\n')
+\end{verbatim}
+
+Just what we expected, like any other dynamic placeholder.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsection{Static placeholder}
+\label{cache.static}
+
+The template:
+\begin{verbatim}
+Cached variable: $*voom
+\end{verbatim}
+
+The command line and output:
+\begin{verbatim}
+% voom='Voom!' python x.py --env
+Cached variable: Voom!
+\end{verbatim}
+
+The generated code, with line numbers:
+\begin{verbatim}
+ 1 write('Cached variable: ')
+ 2 ## START CACHE REGION: at line, col (1, 19) in the source.
+ 3 RECACHE = True
+ 4 if not self._cacheData.has_key('19760169'):
+ 5 pass
+ 6 else:
+ 7 RECACHE = False
+ 8 if RECACHE:
+ 9 orig_trans = trans
+10 trans = cacheCollector = DummyTransaction()
+11 write = cacheCollector.response().write
+12 write(filter(VFS(SL,"voom",1))) # generated from '$*voom' at line 1,
+ # col 19.
+13 trans = orig_trans
+14 write = trans.response().write
+15 self._cacheData['19760169'] = cacheCollector.response().getvalue()
+16 del cacheCollector
+17 write(self._cacheData['19760169'])
+18 ## END CACHE REGION
+
+19 write('\n')
+\end{verbatim}
+
+That one little star generated a whole lotta code. First, instead of an
+ordinary \code{VFS} lookup (searchList) lookup, it converted the
+placeholder to a lookup in the \code{.\_cacheData} dictionary. Cheetah also
+generated a unique key (\code{'19760169'}) for our cached item -- this is its
+cache ID.
+
+Second, Cheetah put a pair of if-blocks before the \code{write}. The first
+(lines 3-7) determine whether the cache value is missing or out of date, and
+sets local variable \code{RECACHE} true or false.
+This stanza may look unnecessarily verbose -- lines 3-7 could be eliminated if
+line 8 was changed to
+\begin{verbatim}
+if not self._cacheData.has_key('19760169'):
+\end{verbatim}
+-- but this model is expandable for some of the cache features we'll see below.
+
+The second if-block, lines 8-16, do the cache updating if necessary.
+Clearly, the programmer is trying to stick as close to normal (dynamic)
+workflow as possible. Remember that \code{write}, even though it looks like a
+local function, is actually a method of a file-like object. So we create a
+temporary file-like object to divert the \code{write} object into, then read
+the result and stuff it into the cache.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsection{Timed-refresh placeholder}
+\label{cache.timed}
+
+The template:
+\begin{verbatim}
+Timed cache: $*.5m*voom
+\end{verbatim}
+
+The command line and the output:
+\begin{verbatim}
+% voom='Voom!' python x.py --env
+Timed cache: Voom!
+\end{verbatim}
+
+The generated method's docstring:
+\begin{verbatim}
+"""
+This is the main method generated by Cheetah
+This cache will be refreshed every 30.0 seconds.
+"""
+\end{verbatim}
+
+The generated code:
+\begin{verbatim}
+ 1 write('Timed cache: ')
+ 2 ## START CACHE REGION: at line, col (1, 15) in the source.
+ 3 RECACHE = True
+ 4 if not self._cacheData.has_key('55048032'):
+ 5 self.__cache55048032__refreshTime = currentTime() + 30.0
+ 6 elif currentTime() > self.__cache55048032__refreshTime:
+ 7 self.__cache55048032__refreshTime = currentTime() + 30.0
+ 8 else:
+ 9 RECACHE = False
+10 if RECACHE:
+11 orig_trans = trans
+12 trans = cacheCollector = DummyTransaction()
+13 write = cacheCollector.response().write
+14 write(filter(VFS(SL,"voom",1))) # generated from '$*.5m*voom' at
+ # line 1, col 15.
+15 trans = orig_trans
+16 write = trans.response().write
+17 self._cacheData['55048032'] = cacheCollector.response().getvalue()
+18 del cacheCollector
+19 write(self._cacheData['55048032'])
+20 ## END CACHE REGION
+
+21 write('\n')
+\end{verbatim}
+
+This code is identical to the static cache example except for the docstring
+and the first if-block. (OK, so the cache ID is different and the comment on
+line 14 is different too. Big deal.)
+
+Each timed-refresh cache item has a corrsponding private attribute
+\code{.\_\_cache\#\#\#\#\#\#\#\#\_\_refreshTime} giving the refresh time
+in ticks (=seconds since January 1, 1970). The first if-block (lines 3-9)
+checks whether the cache value is missing or its update time has passed, and if
+so, sets \code{RECACHE} to true and also schedules another refresh at the next
+interval.
+
+The method docstring reminds the user how often the cache will be refreshed.
+This information is unfortunately not as robust as it could be. Each
+timed-cache placeholder blindly generates a line in the docstring. If all
+refreshes are at the same interval, there will be multiple identical lines
+in the docstring. If the refreshes are at different intervals, you get a
+situation like this:
+\begin{verbatim}
+"""
+This is the main method generated by Cheetah
+This cache will be refreshed every 30.0 seconds.
+This cache will be refreshed every 60.0 seconds.
+This cache will be refreshed every 120.0 seconds.
+"""
+\end{verbatim}
+The docstring tells only that ``something'' will be refreshed every 60.0
+seconds, but doesn't reveal {\em which} placeholder that is. Only if you
+know the relative order of the placeholders in the template can you figure
+that out.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsection{Timed-refresh placeholder with braces}
+\label{cache.timed.braces}
+
+This example is the same but with the long placeholder syntax. It's here
+because it's a Cheetah FAQ whether to put the cache interval inside or outside
+the braces. (It's also here so I can look it up because I frequently forget.)
+The answer is: outside. The braces go around only the placeholder name (and
+perhaps some output-filter arguments.)
+
+The template:
+\begin{verbatim}
+Timed with {}: $*.5m*{voom}
+\end{verbatim}
+
+The output:
+\begin{verbatim}
+Timed with {}: Voom!
+\end{verbatim}
+
+The generated code differs only in the comment. Inside the cache-refresh
+if-block:
+\begin{verbatim}
+write(filter(VFS(SL,"voom",1))) # generated from '$*.5m*{voom}' at line 1,
+ #col 17.
+\end{verbatim}
+
+If you try to do it this way:
+\begin{verbatim}
+Timed with {}: ${*.5m*voom} ## Wrong!
+\end{verbatim}
+you get:
+\begin{verbatim}
+Timed with {}: ${*.5m*voom}
+\end{verbatim}
+\verb+${+ is not a valid placeholder, so it gets treated as ordinary text.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsection{\#cache}
+\label{cache.directive}
+
+The template:
+\begin{verbatim}
+#cache
+This is a cached region. $voom
+#end cache
+\end{verbatim}
+
+The output:
+\begin{verbatim}
+This is a cached region. Voom!
+\end{verbatim}
+
+The generated code:
+\begin{verbatim}
+ 1 ## START CACHE REGION: at line, col (1, 1) in the source.
+ 2 RECACHE = True
+ 3 if not self._cacheData.has_key('23711421'):
+ 4 pass
+ 5 else:
+ 6 RECACHE = False
+ 7 if RECACHE:
+ 8 orig_trans = trans
+ 9 trans = cacheCollector = DummyTransaction()
+10 write = cacheCollector.response().write
+11 write('This is a cached region. ')
+12 write(filter(VFS(SL,"voom",1))) # generated from '$voom' at line 2,
+ # col 27.
+13 write('\n')
+14 trans = orig_trans
+15 write = trans.response().write
+16 self._cacheData['23711421'] = cacheCollector.response().getvalue()
+17 del cacheCollector
+18 write(self._cacheData['23711421'])
+19 ## END CACHE REGION
+\end{verbatim}
+
+This is the same as the \code{\$*voom} example, except that the plain text
+around the placeholder is inside the second if-block.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsection{\#cache with timer and id}
+\label{cache.directive.timer}
+
+The template:
+\begin{verbatim}
+#cache timer='.5m', id='cache1'
+This is a cached region. $voom
+#end cache
+\end{verbatim}
+
+The output:
+\begin{verbatim}
+This is a cached region. Voom!
+\end{verbatim}
+
+The generated code is the same as the previous example except the first
+if-block:
+\begin{verbatim}
+RECACHE = True
+if not self._cacheData.has_key('13925129'):
+ self._cacheIndex['cache1'] = '13925129'
+ self.__cache13925129__refreshTime = currentTime() + 30.0
+elif currentTime() > self.__cache13925129__refreshTime:
+ self.__cache13925129__refreshTime = currentTime() + 30.0
+else:
+ RECACHE = False
+\end{verbatim}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsection{\#cache with test: expression and method conditions}
+\label{cache.directive.test}
+
+The template:
+\begin{verbatim}
+#cache test=$isDBUpdated
+This is a cached region. $voom
+#end cache
+\end{verbatim}
+
+(Analysis postponed: bug in Cheetah produces invalid Python.)
+
+%The output:
+%\begin{verbatim}
+%\end{verbatim}
+
+%The generated code:
+%\begin{verbatim}
+%\end{verbatim}
+
+
+The template:
+\begin{verbatim}
+#cache id='cache1', test=($isDBUpdated or $someOtherCondition)
+This is a cached region. $voom
+#end cache
+\end{verbatim}
+
+The output:
+\begin{verbatim}
+This is a cached region. Voom!
+\end{verbatim}
+
+The first if-block in the generated code:
+\begin{verbatim}
+RECACHE = True
+if not self._cacheData.has_key('36798144'):
+ self._cacheIndex['cache1'] = '36798144'
+elif (VFS(SL,"isDBUpdated",1) or VFS(SL,"someOtherCondition",1)):
+ RECACHE = True
+else:
+ RECACHE = False
+\end{verbatim}
+The second if-block is the same as in the previous example. If you leave
+out the \code{()} around the test expression, the result is the same, although
+it may be harder for the template maintainer to read.
+
+You can even combine arguments, although this is of questionable value.
+
+The template:
+\begin{verbatim}
+#cache id='cache1', timer='30m', test=$isDBUpdated or $someOtherCondition
+This is a cached region. $voom
+#end cache
+\end{verbatim}
+
+The output:
+\begin{verbatim}
+This is a cached region. Voom!
+\end{verbatim}
+
+The first if-block:
+\begin{verbatim}
+RECACHE = True
+if not self._cacheData.has_key('88939345'):
+ self._cacheIndex['cache1'] = '88939345'
+ self.__cache88939345__refreshTime = currentTime() + 1800.0
+elif currentTime() > self.__cache88939345__refreshTime:
+ self.__cache88939345__refreshTime = currentTime() + 1800.0
+elif VFS(SL,"isDBUpdated",1) or VFS(SL,"someOtherCondition",1):
+ RECACHE = True
+else:
+ RECACHE = False
+\end{verbatim}
+
+We are planning to add a \code{'varyBy'} keyword argument in the future that
+will allow separate cache instances to be created for a variety of conditions,
+such as different query string parameters or browser types. This is inspired by
+ASP.net's varyByParam and varyByBrowser output caching keywords. Since this is
+not implemented yet, I cannot provide examples here.
+
+% Local Variables:
+% TeX-master: "devel_guide"
+% End:
diff --git a/docs/devel_guide_src/comments.tex b/docs/devel_guide_src/comments.tex
new file mode 100755
index 0000000..4a7bdbd
--- /dev/null
+++ b/docs/devel_guide_src/comments.tex
@@ -0,0 +1,100 @@
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\section{Directives: Comments}
+\label{comments}
+
+The template:
+
+\begin{verbatim}
+Text before the comment.
+## The comment.
+Text after the comment.
+#* A multi-line comment spanning several lines.
+ It spans several lines, too.
+*#
+Text after the multi-line comment.
+\end{verbatim}
+
+The output:
+
+\begin{verbatim}
+Text before the comment.
+Text after the comment.
+
+Text after the multi-line comment.
+
+\end{verbatim}
+
+The generated code:
+
+\begin{verbatim}
+ write('Text before the comment.\n')
+ # The comment.
+ write('Text after the comment.\n')
+ # A multi-line comment spanning several lines.
+ # It spans several lines, too.
+ write('\nText after the multi-line comment.\n')
+\end{verbatim}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsection{Docstring and header comments}
+\label{comments.docstring}
+
+The template:
+\begin{verbatim}
+##doc: .respond() method comment.
+##doc-method: Another .respond() method comment.
+##doc-class: A class comment.
+##doc-module: A module comment.
+##header: A header comment.
+\end{verbatim}
+
+The output:
+\begin{verbatim}
+
+\end{verbatim}
+
+The beginning of the generated \code{.respond} method:
+\begin{verbatim}
+def respond(self,
+ trans=None,
+ dummyTrans=False,
+ VFS=valueFromSearchList,
+ VFN=valueForName,
+ getmtime=getmtime,
+ currentTime=time.time):
+
+ """
+ This is the main method generated by Cheetah
+ .respond() method comment.
+ Another .respond() method comment.
+ """
+\end{verbatim}
+
+The class docstring:
+\begin{verbatim}
+"""
+A class comment.
+
+Autogenerated by CHEETAH: The Python-Powered Template Engine
+"""
+\end{verbatim}
+
+The top of the module:
+\begin{verbatim}
+#!/usr/bin/env python
+# A header comment.
+
+"""A module comment.
+
+Autogenerated by CHEETAH: The Python-Powered Template Engine
+ CHEETAH VERSION: 0.9.13a1
+ Generation time: Fri Apr 26 22:39:23 2002
+ Source file: x.tmpl
+ Source file last modified: Fri Apr 26 22:36:23 2002
+"""
+\end{verbatim}
+
+
+% Local Variables:
+% TeX-master: "devel_guide"
+% End:
diff --git a/docs/devel_guide_src/compiler.tex b/docs/devel_guide_src/compiler.tex
new file mode 100755
index 0000000..e27aa94
--- /dev/null
+++ b/docs/devel_guide_src/compiler.tex
@@ -0,0 +1,8 @@
+\section{The compiler}
+\label{compiler}
+
+How templates are compiled: a walk through Compiler.py.
+
+% Local Variables:
+% TeX-master: "devel_guide"
+% End:
diff --git a/docs/devel_guide_src/design.tex b/docs/devel_guide_src/design.tex
new file mode 100755
index 0000000..3008a63
--- /dev/null
+++ b/docs/devel_guide_src/design.tex
@@ -0,0 +1,102 @@
+\section{Design Decisions and Tradeoffs}
+\label{design}
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsection{Delimiters}
+\label{design.Delimiters}
+
+One of the first decisions we encountered was which delimiter syntax to use.
+We decided to follow Velocity's \code{\$placeholder} and \code{\#directive}
+syntax because the former is widely used in other languages for the same
+purpose, and the latter stands out in an HTML or text document. We also
+implemented the \verb+${longPlaceholder}+ syntax like the shells for cases
+where Cheetah or you might be confused where a placeholder ends. Tavis went
+ahead and made \verb+${longPlaceholder}+ and \verb+$[longPlaceholder]+
+interchangeable with it since it was trivial to implement. Finally,
+the \code{\#compiler} directive allows you to change the delimiters if you
+don't like them or if they conflict with the text in your document.
+(Obviously, if your document contains a Perl program listing, you don't
+necessarily want to backslash each and every \code{\$} and \code{\#}, do you?)
+
+The choice of comment delimiters was more arbitrary. \code{\#\#} and
+\code{\#* \ldots *\#} doesn't match any language, but it's reminiscent of
+Python and C while also being consistent with our ``\code{\#} is for
+directives'' convention.
+
+We specifically chose {\em not} to use pseudo HTML tags for placeholders and
+directives, as described more thoroughly in the Cheetah Users' Guide
+introduction. Pseudo HTML tags may be easier to see in a visual editor
+(supposedly), but in text editors they're hard to distinguish from ``real''
+HTML tags unless you look closely, and they're many more keystrokes to type.
+Also, if you make a mistake, the tag will show up as literal text in the
+rendered HTML page where it will be easy to notice and eradicate, rather than
+disappearing as bogus HTML tags do in browsers.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsection{Late binding}
+\label{design.lateBinding}
+
+One of Cheetah's unique features is the name mapper, which lets you write
+\code{\$a.b} without worrying much about the type of \code{a} or \code{b}.
+Prior to version 0.9.7, Cheetah did the entire NameMapper lookup at runtime.
+This provided maximum flexibility at the expense of speed. Doing a NameMapper
+lookup is intrinsically more expensive than an ordinary Python expression
+because Cheetah has to decide what type of container \code{a} is, whether the
+the value is a function (autocall it), issue the appropriate Python incantation
+to look up \code{b} in it, autocall again if necessary, and then convert the
+result to a string.
+
+To maximize run-time (filling-time) performance, Cheetah 0.9.7 pushed much of
+this work back into the compiler. The compiler looked up \code{a} in the
+searchList at compile time, noted its type, and generated an eval'able Python
+expression based on that.
+
+This approach had two significant drawbacks. What if \code{a} later changes
+type before a template filling? Answer: unpredictable exceptions occur. What
+if \code{a} does not exist in the searchList at compile time? Answer: the
+template can't compile.
+
+To prevent these catastrophes, users were required to prepopulate the
+searchList before instantiating the template instance, and then not to change
+\code{a}'s type. Static typing is repugnant in a dynamic language like Python,
+and having to prepopulate the searchList made certain usages impossible. For
+example, you couldn't instantiate the template object without a searchList and
+then set \code{self} attributes to specify the values.
+
+After significant user complaints about the fragility of this system, Tavis
+rewrote placeholder handling, and in version 0.9.8a3 (August 2001), Tavis
+moved the name mapper lookup back into runtime. Performance wasn't crippled
+because he discovered that writing a C version of the name mapper was easier
+than anticipated, and the C version completed the lookup quickly. Now Cheetah
+had ``late binding'', meaning the compiler does not look up \code{a} or care
+whether it exists. This allows users to create \code{a} or change its type
+anytime before a template filling.
+
+The lesson we learned is that it's better to decide what you want and then
+figure out how to do it, rather than assuming that certain goals are
+unattainable due to performance considerations.
+
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsection{Caching framework}
+\label{design.cache}
+
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsection{Webware compatibility and the transaction framework}
+\label{design.webware}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsection{Single inheritance}
+\label{design.singleInheritance}
+
+
+
+
+
+% Local Variables:
+% TeX-master: "devel_guide"
+% End:
diff --git a/docs/devel_guide_src/devel_guide.tex b/docs/devel_guide_src/devel_guide.tex
new file mode 100755
index 0000000..1e795c4
--- /dev/null
+++ b/docs/devel_guide_src/devel_guide.tex
@@ -0,0 +1,55 @@
+\documentclass{howto}
+\usepackage{moreverb} %% Verbatim Code Listings
+
+\title{Cheetah Developers' Guide}
+\release{0.9.15a1}
+
+\author{Mike Orr with assistance from Tavis Rudd}
+\authoraddress{\email{iron@mso.oz.net}}
+
+\begin{document}
+\maketitle
+
+
+\tableofcontents
+
+\copyright{Copyright 2002, Mike Orr.
+ This document may be copied and modified under the terms of the
+ {\bf Open Publication License} \url{http://www.opencontent.org/openpub/} }
+
+
+ %% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+ \include{introduction}
+
+ %% How the directives affect the .py template module.
+ \include{pyModules}
+ \include{placeholders}
+ \include{cache}
+ \include{comments}
+ \include{output}
+ \include{inheritanceEtc}
+ \include{flowControl}
+ \include{errorHandling}
+ \include{parserInstructions}
+
+ %% A walk through the Cheetah source.
+ \include{files}
+ \include{template}
+ \include{parser}
+ \include{compiler}
+
+ %% Other development issues and howtos.
+ \include{history}
+ \include{design}
+ \include{patching}
+ \include{documenting}
+
+ %% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+ \appendix
+ \include{bnf}
+ \include{safeDelegation}
+\end{document}
+
+% Local Variables:
+% TeX-master: "users_guide"
+% End:
diff --git a/docs/devel_guide_src/documenting.tex b/docs/devel_guide_src/documenting.tex
new file mode 100755
index 0000000..51d5153
--- /dev/null
+++ b/docs/devel_guide_src/documenting.tex
@@ -0,0 +1,8 @@
+\section{Documenting Cheetah}
+\label{documenting}
+
+How to build the documentation. Why LaTeX, a minimum LaTeX reference, etc.
+
+% Local Variables:
+% TeX-master: "devel_guide"
+% End:
diff --git a/docs/devel_guide_src/errorHandling.tex b/docs/devel_guide_src/errorHandling.tex
new file mode 100755
index 0000000..02079d5
--- /dev/null
+++ b/docs/devel_guide_src/errorHandling.tex
@@ -0,0 +1,309 @@
+\section{Directives: Error Handling}
+\label{errorHandling}
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsection{\#try and \#raise}
+\label{errorHandling.try}
+
+The template:
+\begin{verbatim}
+#import traceback
+#try
+#raise RuntimeError
+#except RuntimeError
+A runtime error occurred.
+#end try
+
+#try
+#raise RuntimeError("Hahaha!")
+#except RuntimeError
+#echo $sys.exc_info()[1]
+#end try
+
+#try
+#echo 1/0
+#except ZeroDivisionError
+You can't divide by zero, idiot!
+#end try
+\end{verbatim}
+
+The output:
+\begin{verbatim}
+A runtime error occurred.
+
+Hahaha!
+
+You can't divide by zero, idiot!
+\end{verbatim}
+
+The generated code:
+\begin{verbatim}
+try:
+ raise RuntimeError
+except RuntimeError:
+ write('A runtime error occurred.\n')
+write('\n')
+try:
+ raise RuntimeError("Hahaha!")
+except RuntimeError:
+ write(filter(VFN(sys,"exc_info",0)()[1]))
+ write('\n')
+write('\n')
+try:
+ write(filter(1/0))
+ write('\n')
+except ZeroDivisionError:
+ write("You can't divide by zero, idiot!\n")
+\end{verbatim}
+
+\code{\#finally} works just like in Python.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsection{\#assert}
+\label{errorHandling.assert}
+
+The template:
+\begin{verbatim}
+#assert False, "You lose, buster!"
+\end{verbatim}
+
+The output:
+\begin{verbatim}
+Traceback (most recent call last):
+ File "x.py", line 117, in ?
+ x().runAsMainProgram()
+ File "/local/opt/Python/lib/python2.2/site-packages/Webware/Cheetah/
+Template.py", line 331, in runAsMainProgram
+ CmdLineIface(templateObj=self).run()
+ File "/local/opt/Python/lib/python2.2/site-packages/Webware/Cheetah/
+TemplateCmdLineIface.py", line 59, in run
+ print self._template
+ File "x.py", line 91, in respond
+ assert False, "You lose, buster!"
+AssertionError: You lose, buster!
+\end{verbatim}
+
+The generated code:
+\begin{verbatim}
+assert False, "You lose, buster!"
+\end{verbatim}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsection{\#errorCatcher}
+\label{errorHandling.errorCatcher}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection{No error catcher}
+\label{errorHandling.errorCatcher.no}
+
+The template:
+\begin{verbatim}
+$noValue
+\end{verbatim}
+
+The output:
+\begin{verbatim}
+
+Traceback (most recent call last):
+ File "x.py", line 118, in ?
+ x().runAsMainProgram()
+ File "/local/opt/Python/lib/python2.2/site-packages/Webware/Cheetah/
+Template.py", line 331, in runAsMainProgram
+ CmdLineIface(templateObj=self).run()
+ File "/local/opt/Python/lib/python2.2/site-packages/Webware/Cheetah/
+TemplateCmdLineIface.py", line 59, in run
+ print self._template
+ File "x.py", line 91, in respond
+ write(filter(VFS(SL,"noValue",1))) # generated from '$noValue' at line
+1, col 1.
+NameMapper.NotFound: noValue
+\end{verbatim}
+
+The generated code:
+\begin{verbatim}
+write(filter(VFS(SL,"noValue",1))) # generated from '$noValue' at line 1,
+ # col 1.
+write('\n')
+\end{verbatim}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection{Echo and BigEcho}
+\label{errorHandling.errorCatcher.echo}
+
+The template:
+\begin{verbatim}
+#errorCatcher Echo
+$noValue
+#errorCatcher BigEcho
+$noValue
+\end{verbatim}
+
+The output:
+\begin{verbatim}
+$noValue
+===============&lt;$noValue could not be found&gt;===============
+\end{verbatim}
+
+The generated code:
+\begin{verbatim}
+if self._errorCatchers.has_key("Echo"):
+ self._errorCatcher = self._errorCatchers["Echo"]
+else:
+ self._errorCatcher = self._errorCatchers["Echo"] = ErrorCatchers.Echo(self)
+write(filter(self.__errorCatcher1(localsDict=locals())))
+ # generated from '$noValue' at line 2, col 1.
+write('\n')
+if self._errorCatchers.has_key("BigEcho"):
+ self._errorCatcher = self._errorCatchers["BigEcho"]
+else:
+ self._errorCatcher = self._errorCatchers["BigEcho"] = \
+ ErrorCatchers.BigEcho(self)
+write(filter(self.__errorCatcher1(localsDict=locals())))
+ # generated from '$noValue' at line 4, col 1.
+write('\n')
+\end{verbatim}
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection{ListErrors}
+\label{errorHandling.errorCatcher.listErrors}
+
+The template:
+\begin{verbatim}
+#import pprint
+#errorCatcher ListErrors
+$noValue
+$anotherMissingValue.really
+$pprint.pformat($errorCatcher.listErrors)
+## This is really self.errorCatcher().listErrors()
+\end{verbatim}
+
+The output:
+\begin{verbatim}
+$noValue
+$anotherMissingValue.really
+[{'code': 'VFS(SL,"noValue",1)',
+ 'exc_val': <NameMapper.NotFound instance at 0x8170ecc>,
+ 'lineCol': (3, 1),
+ 'rawCode': '$noValue',
+ 'time': 'Wed May 15 00:38:23 2002'},
+ {'code': 'VFS(SL,"anotherMissingValue.really",1)',
+ 'exc_val': <NameMapper.NotFound instance at 0x816d0fc>,
+ 'lineCol': (4, 1),
+ 'rawCode': '$anotherMissingValue.really',
+ 'time': 'Wed May 15 00:38:23 2002'}]
+\end{verbatim}
+
+The generated import:
+\begin{verbatim}
+import pprint
+\end{verbatim}
+
+Then in the generated class, we have our familiar \code{.respond} method
+and several new methods:
+\begin{verbatim}
+def __errorCatcher1(self, localsDict={}):
+ """
+ Generated from $noValue at line, col (3, 1).
+ """
+
+ try:
+ return eval('''VFS(SL,"noValue",1)''', globals(), localsDict)
+ except self._errorCatcher.exceptions(), e:
+ return self._errorCatcher.warn(exc_val=e, code= 'VFS(SL,"noValue",1)' ,
+ rawCode= '$noValue' , lineCol=(3, 1))
+
+def __errorCatcher2(self, localsDict={}):
+ """
+ Generated from $anotherMissingValue.really at line, col (4, 1).
+ """
+
+ try:
+ return eval('''VFS(SL,"anotherMissingValue.really",1)''', globals(),
+ localsDict)
+ except self._errorCatcher.exceptions(), e:
+ return self._errorCatcher.warn(exc_val=e,
+ code= 'VFS(SL,"anotherMissingValue.really",1)' ,
+ rawCode= '$anotherMissingValue.really' , lineCol=(4, 1))
+
+def __errorCatcher3(self, localsDict={}):
+ """
+ Generated from $pprint.pformat($errorCatcher.listErrors) at line, col
+ (5, 1).
+ """
+
+ try:
+ return eval('''VFN(pprint,"pformat",0)(VFS(SL,
+ "errorCatcher.listErrors",1))''', globals(), localsDict)
+ except self._errorCatcher.exceptions(), e:
+ return self._errorCatcher.warn(exc_val=e, code=
+ 'VFN(pprint,"pformat",0)(VFS(SL,"errorCatcher.listErrors",1))' ,
+ rawCode= '$pprint.pformat($errorCatcher.listErrors)' ,
+ lineCol=(5, 1))
+\end{verbatim}
+\begin{verbatim}
+def respond(self,
+ trans=None,
+ dummyTrans=False,
+ VFS=valueFromSearchList,
+ VFN=valueForName,
+ getmtime=getmtime,
+ currentTime=time.time):
+
+
+ """
+ This is the main method generated by Cheetah
+ """
+
+ if not trans:
+ trans = DummyTransaction()
+ dummyTrans = True
+ write = trans.response().write
+ SL = self._searchList
+ filter = self._currentFilter
+ globalSetVars = self._globalSetVars
+
+ ########################################
+ ## START - generated method body
+
+ if exists(self._filePath) and getmtime(self._filePath) > self._fileMtime:
+ self.compile(file=self._filePath)
+ write(getattr(self, self._mainCheetahMethod_for_x)(trans=trans))
+ if dummyTrans:
+ return trans.response().getvalue()
+ else:
+ return ""
+ if self._errorCatchers.has_key("ListErrors"):
+ self._errorCatcher = self._errorCatchers["ListErrors"]
+ else:
+ self._errorCatcher = self._errorCatchers["ListErrors"] = \
+ ErrorCatchers.ListErrors(self)
+ write(filter(self.__errorCatcher1(localsDict=locals())))
+ # generated from '$noValue' at line 3, col 1.
+ write('\n')
+ write(filter(self.__errorCatcher2(localsDict=locals())))
+ # generated from '$anotherMissingValue.really' at line 4, col 1.
+ write('\n')
+ write(filter(self.__errorCatcher3(localsDict=locals())))
+ # generated from '$pprint.pformat($errorCatcher.listErrors)' at line
+ # 5, col 1.
+ write('\n')
+ # This is really self.errorCatcher().listErrors()
+
+ ########################################
+ ## END - generated method body
+
+ if dummyTrans:
+ return trans.response().getvalue()
+ else:
+ return ""
+\end{verbatim}
+
+So whenever an error catcher is active, each placeholder gets wrapped in its
+own method. No wonder error catchers slow down the system!
+
+
+% Local Variables:
+% TeX-master: "devel_guide"
+% End:
diff --git a/docs/devel_guide_src/files.tex b/docs/devel_guide_src/files.tex
new file mode 100755
index 0000000..0ce45a9
--- /dev/null
+++ b/docs/devel_guide_src/files.tex
@@ -0,0 +1,11 @@
+\section{Files}
+\label{files}
+
+This chapter will be an overview of the files in the Cheetah package,
+and how they interrelate in compiling and filling a template. We'll
+also look at files in the Cheetah tarball that don't get copied into
+the package.
+
+% Local Variables:
+% TeX-master: "devel_guide"
+% End:
diff --git a/docs/devel_guide_src/flowControl.tex b/docs/devel_guide_src/flowControl.tex
new file mode 100755
index 0000000..936df67
--- /dev/null
+++ b/docs/devel_guide_src/flowControl.tex
@@ -0,0 +1,360 @@
+\section{Directives: Flow Control}
+\label{flowControl}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsection{\#for}
+\label{flowControl.for}
+
+The template:
+\begin{verbatim}
+#for $i in $range(10)
+$i #slurp
+#end for
+\end{verbatim}
+
+The output:
+\begin{verbatim}
+0 1 2 3 4 5 6 7 8 9
+\end{verbatim}
+
+The generated code:
+\begin{verbatim}
+for i in range(10):
+ write(filter(i)) # generated from '$i' at line 2, col 1.
+ write(' ')
+\end{verbatim}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsection{\#repeat}
+\label{flowControl.repeat}
+
+The template:
+\begin{verbatim}
+#repeat 3
+My bonnie lies over the ocean
+#end repeat
+O, bring back my bonnie to me!
+\end{verbatim}
+
+The output:
+\begin{verbatim}
+My bonnie lies over the ocean
+My bonnie lies over the ocean
+My bonnie lies over the ocean
+O, bring back my bonnie to me!
+\end{verbatim}
+(OK, so the second line should be ``sea'' instead of ``ocean''.)
+
+The generated code:
+\begin{verbatim}
+for __i0 in range(3):
+ write('My bonnie lies over the ocean\n')
+write('O, bring back my bonnie to me!\n')
+\end{verbatim}
+
+Note that a new local variable of the form \code{\_\_i\$num} will be
+used for each instance of \code{repeat} in order to permit nesting.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsection{\#while}
+\label{flowControl.while}
+
+The template:
+\begin{verbatim}
+#set $alive = True
+#while $alive
+I am alive!
+#set $alive = False
+#end while
+\end{verbatim}
+
+The output:
+\begin{verbatim}
+I am alive!
+\end{verbatim}
+
+The generated code:
+\begin{verbatim}
+alive = True
+while alive:
+ write('I am alive!\n')
+ alive = False
+\end{verbatim}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsection{\#if}
+\label{}
+
+The template:
+\begin{verbatim}
+#set $size = 500
+#if $size >= 1500
+It's big
+#else if $size < 1500 and $size > 0
+It's small
+#else
+It's not there
+#end if
+\end{verbatim}
+
+The output:
+\begin{verbatim}
+It's small
+\end{verbatim}
+
+The generated code:
+\begin{verbatim}
+size = 500
+if size >= 1500:
+ write("It's big\n")
+elif size < 1500 and size > 0:
+ write("It's small\n")
+else:
+ write("It's not there\n")
+\end{verbatim}
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsection{\#unless}
+\label{flowControl.unless}
+
+The template:
+\begin{verbatim}
+#set $count = 9
+#unless $count + 5 > 15
+Count is in range.
+#end unless
+\end{verbatim}
+
+The output:
+\begin{verbatim}
+Count is in range.
+\end{verbatim}
+
+The generated code:
+\begin{verbatim}
+ count = 9
+ if not (count + 5 > 15):
+ write('Count is in range.\n')
+\end{verbatim}
+
+{\em Note:} There is a bug in Cheetah 0.9.13. It's forgetting the
+parentheses in the \code{if} expression, which could lead to it calculating
+something different than it should.
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsection{\#break and \#continue}
+\label{flowControl.break}
+
+The template:
+\begin{verbatim}
+#for $i in [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 'James', 'Joe', 'Snow']
+#if $i == 10
+ #continue
+#end if
+#if $i == 'Joe'
+ #break
+#end if
+$i - #slurp
+#end for
+\end{verbatim}
+
+The output:
+\begin{verbatim}
+1 - 2 - 3 - 4 - 5 - 6 - 7 - 8 - 9 - 11 - 12 - James -
+\end{verbatim}
+
+The generated code:
+\begin{verbatim}
+for i in [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 'James', 'Joe', 'Snow']:
+ if i == 10:
+ write('')
+ continue
+ if i == 'Joe':
+ write('')
+ break
+ write(filter(i)) # generated from '$i' at line 8, col 1.
+ write(' - ')
+\end{verbatim}
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsection{\#pass}
+\label{flowControl.pass}
+
+The template:
+\begin{verbatim}
+Let's check the number.
+#set $size = 500
+#if $size >= 1500
+It's big
+#elif $size > 0
+#pass
+#else
+Invalid entry
+#end if
+Done checking the number.
+\end{verbatim}
+
+The output:
+\begin{verbatim}
+Let's check the number.
+Done checking the number.
+\end{verbatim}
+
+The generated code:
+\begin{verbatim}
+write("Let's check the number.\n")
+size = 500
+if size >= 1500:
+ write("It's big\n")
+elif size > 0:
+ pass
+else:
+ write('Invalid entry\n')
+write('Done checking the number.\n')
+\end{verbatim}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsection{\#stop}
+\label{flowControl.stop}
+
+The template:
+\begin{verbatim}
+A cat
+#if 1
+ sat on a mat
+ #stop
+ watching a rat
+#end if
+in a flat.
+\end{verbatim}
+
+The output:
+\begin{verbatim}
+A cat
+ sat on a mat
+\end{verbatim}
+
+The generated code:
+\begin{verbatim}
+write('A cat\n')
+if 1:
+ write(' sat on a mat\n')
+ if dummyTrans:
+ return trans.response().getvalue()
+ else:
+ return ""
+ write(' watching a rat\n')
+write('in a flat.\n')
+\end{verbatim}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsection{\#return}
+\label{flowControl.return}
+
+The template:
+\begin{verbatim}
+1
+$test[1]
+3
+#def test
+1.5
+#if 1
+#return '123'
+#else
+99999
+#end if
+#end def
+\end{verbatim}
+
+The output:
+\begin{verbatim}
+1
+2
+3
+\end{verbatim}
+
+The generated code:
+\begin{verbatim}
+ def test(self,
+ trans=None,
+ dummyTrans=False,
+ VFS=valueFromSearchList,
+ VFN=valueForName,
+ getmtime=getmtime,
+ currentTime=time.time):
+
+
+ """
+ Generated from #def test at line 5, col 1.
+ """
+
+ if not trans:
+ trans = DummyTransaction()
+ dummyTrans = True
+ write = trans.response().write
+ SL = self._searchList
+ filter = self._currentFilter
+ globalSetVars = self._globalSetVars
+
+ ########################################
+ ## START - generated method body
+
+ write('1.5\n')
+ if 1:
+ return '123'
+ else:
+ write('99999\n')
+
+ ########################################
+ ## END - generated method body
+
+ if dummyTrans:
+ return trans.response().getvalue()
+ else:
+ return ""
+\end{verbatim}
+\begin{verbatim}
+ def respond(self,
+ trans=None,
+ dummyTrans=False,
+ VFS=valueFromSearchList,
+ VFN=valueForName,
+ getmtime=getmtime,
+ currentTime=time.time):
+
+
+ """
+ This is the main method generated by Cheetah
+ """
+
+ if not trans:
+ trans = DummyTransaction()
+ dummyTrans = True
+ write = trans.response().write
+ SL = self._searchList
+ filter = self._currentFilter
+ globalSetVars = self._globalSetVars
+
+ ########################################
+ ## START - generated method body
+
+ write('\n1\n')
+ write(filter(VFS(SL,"test",1)[1])) # generated from '$test[1]' at line 3, col 1.
+ write('\n3\n')
+
+ ########################################
+ ## END - generated method body
+
+ if dummyTrans:
+ return trans.response().getvalue()
+ else:
+ return ""
+\end{verbatim}
+
+
+% Local Variables:
+% TeX-master: "devel_guide"
+% End:
diff --git a/docs/devel_guide_src/history.tex b/docs/devel_guide_src/history.tex
new file mode 100755
index 0000000..d5444d5
--- /dev/null
+++ b/docs/devel_guide_src/history.tex
@@ -0,0 +1,92 @@
+\section{History of Cheetah}
+\label{history}
+
+In Spring 2001, several members of the webware-discuss mailing list expressed
+the need for a template engine. Webware like Python is great for organizing
+logic, but they both suffer when you need to do extensive variable
+interpolation into large pieces of text, or to build up a text string from its
+nested parts. Python's \code{\%} operator gets you only so far, the syntax is
+cumbersome, and you have to use a separate format string for each nested part.
+Most of us had used template systems from other platforms--chiefly Zope's DTML,
+PHPLib's Template object and Java's Velocity--and wanted to port something like
+those so it could be used both in Webware servlets and in standalone Python
+programs.
+
+% @@MO: What influence did PSP have on Cheetah?
+
+Since I (Mike Orr) am writing this history, I'll describe how I encountered
+Cheetah. I had written a template module called PlowPlate based on PHPLib's
+Template library. Like PHPLib, it used regular expressions to search
+and destroy--er, replace--placeholders, behaved like a dictionary to
+specify placeholder values, contained no directives, but did have BEGIN and
+END markers which could be used to extract a named block (subtemplate).
+Meanwhile, Tavis Rudd was also on webware-discuss and interested in templates,
+and he lived just a few hours away. So on 12 May 2001 we met in Vancouver at
+a gelato shop on Denman Street and discussed Webware, and he drew on a napkin
+the outline of a template system he was working on.
+
+[Note from Tavis: Mikes got the dates and sequence of things a little out of order,
+but what the hell ...]
+
+Instead of filling the template by search-and-replace, he wanted to break it up
+into parts. This was a primitive form of template compiling: do the
+time-consuming work once and put it to a state where you can fill the template
+quickly multiple times. A template without directives happens to break down
+naturally into a list of alternating text/placeholder pairs. The odd
+subscript values are literal strings; the even subscripts are string keys into
+a dictionary of placeholder values. The project was called TemplateServer.
+
+In a couple months, Tavis decided that instead of compiling to a list, he
+wanted to compile to Python source code: a series of \code{write} calls that
+would output into a file-like object. This was the nucleus that became
+Cheetah. I thought that idea was stupid, but it turned out that this
+not-so-stupid idea blew all the others out of the water in terms of
+performance.
+
+Another thing Tavis pushed hard for from near the beginning was ``display
+logic'', or simple directives like \code{\#for}, \code{\#if} and
+\code{\#echo}. (OK, \code{\#echo} came later, but conceptually it belongs
+here. I thought display logic was even stupider than compiling to Python
+source code because it would just lead to ``DTML hell''--complicated templates
+that are hard to read and maintain, and for which you have to learn (and debug)
+a whole new language when Python does it just fine. But others (hi Chuck!) had
+templates that were maintained by secretaries who didn't know Python, and the
+secretaries needed display logic, so that was that. Finally, after working
+with Cheetah templates (with display logic) and PlowPlate templates (with just
+blocks rather than display logic), I realized Tavis was smarter than I was and
+display logic really did belong in the template.
+
+The next step was making directives for all the Python flow-control
+statements: \code{\#while}, \code{\#try}, \code{\#assert}, etc. Some of
+them we couldn't think of a use for. Nevertheless, they were easy to code,
+and ``somebody'' would probably need them ``someday'', so we may as well
+implement them now.
+
+During all this, Chuck Esterbrook, Ian Bicking and others offered (and still
+offer) their support and suggestions, and Chuck gave us feedback about his use
+of Cheetah--its first deployment in a commercial production environment.
+Later, Edmund Lian became our \#1 bug reporter and suggester as he used Cheetah
+in his web applications.
+
+% @@MO: Write more about the contributions of Chuck, Ian and others. My
+% memory is faulty so I'll have to ask them.
+
+We were going to release 1.0 in January 2002, but we decided to delay it
+until more people used it in real-world situations and gave us feedback
+about what is still needed. This has led to many refinements, and we have
+added (and removed) features according to this feedback. Nevertheless,
+Cheetah has been changing but stable since the late-binding rewrite in
+fall 2001, and anybody who keeps up with the cheetah-discuss mailing list
+will know when changes occur that require modifying one's template, and
+since most people use point releases rather than CVS, they generally have
+a few week's warning about any significant changes.
+
+More detail on Cheetah's history and evolution, and why it is the way it is,
+can be found in our paper for the Python10 conference,
+\url{http://www.cheetahtemplate.org/Py10.html}.
+
+% @@MO: Look through the changelog for important milestones to mention.
+
+% Local Variables:
+% TeX-master: "devel_guide"
+% End:
diff --git a/docs/devel_guide_src/inheritanceEtc.tex b/docs/devel_guide_src/inheritanceEtc.tex
new file mode 100755
index 0000000..0179555
--- /dev/null
+++ b/docs/devel_guide_src/inheritanceEtc.tex
@@ -0,0 +1,232 @@
+\section{Directives: Import, Inheritance, Declaration and Assignment}
+\label{inheritanceEtc}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsection{\#import and \#from}
+\label{inheritanceEtc.import}
+
+The template:
+\begin{verbatim}
+#import math
+\end{verbatim}
+
+This construct does not produce any output.
+
+The generated module, at the bottom of the import section:
+\begin{verbatim}
+import math
+\end{verbatim}
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsection{\#extends}
+\label{inheritanceEtc.extends}
+
+The template:
+\begin{verbatim}
+#extends SomeClass
+\end{verbatim}
+
+The generated import (skipped if \code{SomeClass} has already been
+imported):
+\begin{verbatim}
+from SomeClass import SomeClass
+\end{verbatim}
+
+The generated class:
+\begin{verbatim}
+class x(SomeClass):
+\end{verbatim}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsection{\#implements}
+\label{inheritanceEtc.implements}
+
+The template:
+\begin{verbatim}
+#implements doOutput
+\end{verbatim}
+
+In the generated class, the main method is \code{.doOutput} instead of
+\code{.respond}, and the attribute naming this method is:
+\begin{verbatim}
+_mainCheetahMethod_for_x2= 'doOutput'
+\end{verbatim}
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsection{\#set and \#set global}
+\label{inheritanceEtc.set}
+
+The template:
+\begin{verbatim}
+#set $namesList = ['Moe','Larry','Curly']
+$namesList
+#set global $toes = ['eeny', 'meeny', 'miney', 'moe']
+$toes
+\end{verbatim}
+
+The output:
+\begin{verbatim}
+['Moe', 'Larry', 'Curly']
+['eeny', 'meeny', 'miney', 'moe']
+\end{verbatim}
+
+
+The generated code:
+\begin{verbatim}
+1 namesList = ['Moe','Larry','Curly']
+2 write(filter(namesList)) # generated from '$namesList' at line 2, col 1.
+3 write('\n')
+4 globalSetVars["toes"] = ['eeny', 'meeny', 'miney', 'moe']
+5 write(filter(VFS(SL,"toes",1))) # generated from '$toes' at line 4, col 1.
+6 write('\n')
+\end{verbatim}
+
+\code{globalSetVars} is a local variable referencing \code{.\_globalSetVars}.
+Writes go into it directly, but reads take advantage of the fact that
+\code{.\_globalSetVars} is on the searchList. (In fact, it's the very first
+namespace.)
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsection{\#del}
+\label{inheritanceEtc.del}
+
+The template:
+\begin{verbatim}
+#set $a = 1
+#del $a
+#set $a = 2
+#set $arr = [0, 1, 2]
+#del $a, $arr[1]
+\end{verbatim}
+
+In the generated class:
+\begin{verbatim}
+1 a = 1
+2 del a
+3 a = 2
+4 arr = [0, 1, 2]
+5 del a, arr[1]
+\end{verbatim}
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsection{\#attr}
+\label{inheritanceEtc.attr}
+
+The template:
+\begin{verbatim}
+#attr $namesList = ['Moe', 'Larry', 'Curly']
+\end{verbatim}
+
+In the generated class:
+\begin{verbatim}
+## GENERATED ATTRIBUTES
+
+namesList = ['Moe', 'Larry', 'Curly']
+\end{verbatim}
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsection{\#def}
+\label{inheritanceEtc.def}
+
+The template:
+\begin{verbatim}
+#def printArg($arg)
+The argument is $arg.
+#end def
+My method returned $printArg(5).
+\end{verbatim}
+
+The output:
+\begin{verbatim}
+My method returned The argument is 5.
+.
+\end{verbatim}
+
+Hmm, not exactly what we expected. The method returns a trailing newline
+because we didn't end the last line with \code{\#slurp}. So the second
+period (outside the method) appears on a separate line.
+
+The \code{\#def} generates a method \code{.printArg} whose structure is similar
+to the main method:
+\begin{verbatim}
+def printArg(self,
+ arg,
+ trans=None,
+ dummyTrans=False,
+ VFS=valueFromSearchList,
+ VFN=valueForName,
+ getmtime=getmtime,
+ currentTime=time.time):
+
+
+ """
+ Generated from #def printArg($arg) at line 1, col 1.
+ """
+
+ if not trans:
+ trans = DummyTransaction()
+ dummyTrans = True
+ write = trans.response().write
+ SL = self._searchList
+ filter = self._currentFilter
+ globalSetVars = self._globalSetVars
+
+ ########################################
+ ## START - generated method body
+
+ write('The argument is ')
+ write(filter(arg)) # generated from '$arg' at line 2, col 17.
+ write('.\n')
+
+ ########################################
+ ## END - generated method body
+
+ if dummyTrans:
+ return trans.response().getvalue()
+ else:
+ return ""
+\end{verbatim}
+
+When \code{.printArg} is called from a placeholder, only the arguments the user
+supplied are passed. The other arguments retain their default values.
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsection{\#block}
+\label{inheritanceEtc.block}
+
+The template:
+\begin{verbatim}
+#block content
+This page is under construction.
+#end block
+\end{verbatim}
+
+The output:
+\begin{verbatim}
+This page is under construction.
+\end{verbatim}
+
+This construct generates a method \code{.content} in the same structure
+as \code{.printArg} above, containing the write code:
+\begin{verbatim}
+write('This page is under construction.\n')
+\end{verbatim}
+
+In the main method, the write code is:
+\begin{verbatim}
+self.content(trans=trans) # generated from ('content', '#block content')
+ # at line 1, col 1.
+\end{verbatim}
+
+So a block placeholder implicitly passes the current transaction to the method.
+
+
+% Local Variables:
+% TeX-master: "devel_guide"
+% End:
diff --git a/docs/devel_guide_src/introduction.tex b/docs/devel_guide_src/introduction.tex
new file mode 100755
index 0000000..3403d7e
--- /dev/null
+++ b/docs/devel_guide_src/introduction.tex
@@ -0,0 +1,29 @@
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\section{Introduction}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsection{Who should read this Guide?}
+
+The Cheetah Developers' Guide is for those who want to learn how Cheetah works
+internally, or wish to modify or extend Cheetah. It is assumed that
+you've read the Cheetah Users' Guide and have an intermediate knowledge of
+Python.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsection{Contents}
+
+This Guide takes a behaviorist approach. First we'll look at what the
+Cheetah compiler generates when it compiles a template definition, and
+how it compiles the various \$placeholder features and \#directives.
+Then we'll stroll through the files in the Cheetah source distribution
+and show how each file contributes to the compilation and/or filling of
+templates. Then we'll list every method/attribute inherited by a template
+object. Finally, we'll describe how to submit bugfixes/enhancements to
+Cheetah, and how to add to the documentation.
+
+Appendix A will contain a BNF syntax of the Cheetah template language.
+
+
+% Local Variables:
+% TeX-master: "users_guide"
+% End:
diff --git a/docs/devel_guide_src/output.tex b/docs/devel_guide_src/output.tex
new file mode 100755
index 0000000..1b714c2
--- /dev/null
+++ b/docs/devel_guide_src/output.tex
@@ -0,0 +1,282 @@
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\section{Directives: Output}
+\label{output}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsection{\#echo}
+\label{output.echo}
+
+The template:
+\begin{verbatim}
+Here is my #echo ', '.join(['silly']*5) # example
+\end{verbatim}
+
+The output:
+\begin{verbatim}
+Here is my silly, silly, silly, silly, silly example
+\end{verbatim}
+
+The generated code:
+\begin{verbatim}
+write('Here is my ')
+write(filter(', '.join(['silly']*5) ))
+write(' example\n')
+\end{verbatim}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsection{\#silent}
+\label{output.silent}
+
+The template:
+\begin{verbatim}
+Here is my #silent ', '.join(['silly']*5) # example
+\end{verbatim}
+
+The output:
+\begin{verbatim}
+Here is my example
+\end{verbatim}
+
+The generated code:
+\begin{verbatim}
+ write('Here is my ')
+ ', '.join(['silly']*5)
+ write(' example\n')
+\end{verbatim}
+
+OK, it's not quite covert because that extra space gives it away, but it
+almost succeeds.
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsection{\#raw}
+\label{output.raw}
+
+The template:
+\begin{verbatim}
+Text before raw.
+#raw
+Text in raw. $alligator. $croc.o['dile']. #set $a = $b + $c.
+#end raw
+Text after raw.
+\end{verbatim}
+
+The output:
+\begin{verbatim}
+Text before raw.
+Text in raw. $alligator. $croc.o['dile']. #set $a = $b + $c.
+Text after raw.
+\end{verbatim}
+
+The generated code:
+\begin{verbatim}
+ write('''Text before raw.
+Text in raw. $alligator. $croc.o['dile']. #set $a = $b + $c.
+Text after raw.
+''')
+\end{verbatim}
+
+So we see that \code{\#raw} is really like a quoting mechanism. It says that
+anything inside it is ordinary text, and Cheetah joins a \code{\#raw} section
+with adjacent string literals rather than generating a separate \code{write}
+call.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsection{\#include}
+\label{output.include}
+
+The main template:
+\begin{verbatim}
+#include "y.tmpl"
+\end{verbatim}
+
+The included template y.tmpl:
+\begin{verbatim}
+Let's go $voom!
+\end{verbatim}
+
+The shell command and output:
+\begin{verbatim}
+% voom="VOOM" x.py --env
+Let's go VOOM!
+\end{verbatim}
+
+The generated code:
+\begin{verbatim}
+write(self._includeCheetahSource("y.tmpl", trans=trans, includeFrom="file",
+ raw=0))
+\end{verbatim}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection{\#include raw}
+\label{output.include.raw}
+
+The main template:
+\begin{verbatim}
+#include raw "y.tmpl"
+\end{verbatim}
+
+The shell command and output:
+\begin{verbatim}
+% voom="VOOM" x.py --env
+Let's go $voom!
+\end{verbatim}
+
+The generated code:
+\begin{verbatim}
+write(self._includeCheetahSource("y.tmpl", trans=trans, includeFrom="fil
+e", raw=1))
+\end{verbatim}
+
+That last argument, \code{raw}, makes the difference.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection{\#include from a string or expression (eval)}
+\label{output.include.expression}
+
+The template:
+\begin{verbatim}
+#attr $y = "Let's go $voom!"
+#include source=$y
+#include raw source=$y
+#include source="Bam! Bam!"
+\end{verbatim}
+
+The output:
+\begin{verbatim}
+% voom="VOOM" x.py --env
+Let's go VOOM!Let's go $voom!Bam! Bam!
+\end{verbatim}
+
+The generated code:
+\begin{verbatim}
+write(self._includeCheetahSource(VFS(SL,"y",1), trans=trans,
+ includeFrom="str", raw=0, includeID="481020889808.74"))
+write(self._includeCheetahSource(VFS(SL,"y",1), trans=trans,
+ includeFrom="str", raw=1, includeID="711020889808.75"))
+write(self._includeCheetahSource("Bam! Bam!", trans=trans,
+ includeFrom="str", raw=0, includeID="1001020889808.75"))
+\end{verbatim}
+
+Later in the generated class:
+\begin{verbatim}
+y = "Let's go $voom!"
+\end{verbatim}
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsection{\#slurp}
+\label{output.slurp}
+
+The template:
+\begin{verbatim}
+#for $i in range(5)
+$i
+#end for
+#for $i in range(5)
+$i #slurp
+#end for
+Line after slurp.
+\end{verbatim}
+
+The output:
+\begin{verbatim}
+0
+1
+2
+3
+4
+0 1 2 3 4 Line after slurp.
+\end{verbatim}
+
+The generated code:
+\begin{verbatim}
+for i in range(5):
+ write(filter(i)) # generated from '$i' at line 2, col 1.
+ write('\n')
+for i in range(5):
+ write(filter(i)) # generated from '$i' at line 5, col 1.
+ write(' ')
+write('Line after slurp.\n')
+\end{verbatim}
+
+The space after each number is because of the space before \code{\#slurp} in
+the template definition.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsection{\#filter}
+\label{output.filter}
+
+The template:
+\begin{verbatim}
+#attr $ode = ">> Rubber Ducky, you're the one! You make bathtime so much fun! <<"
+$ode
+#filter WebSafe
+$ode
+#filter MaxLen
+${ode, maxlen=13}
+#filter None
+${ode, maxlen=13}
+\end{verbatim}
+
+The output:
+\begin{verbatim}
+>> Rubber Ducky, you're the one! You make bathtime so much fun! <<
+&gt;&gt; Rubber Ducky, you're the one! You make bathtime so much fun! &lt;&lt;
+>> Rubber Duc
+>> Rubber Ducky, you're the one! You make bathtime so much fun! <<
+\end{verbatim}
+
+The \code{WebSafe} filter escapes characters that have a special meaning in
+HTML. The \code{MaxLen} filter chops off values at the specified length.
+\code{\#filter None} returns to the default filter, which ignores the \code{maxlen}
+argument.
+
+The generated code:
+\begin{verbatim}
+ 1 write(filter(VFS(SL,"ode",1))) # generated from '$ode' at line 2, col 1.
+ 2 write('\n')
+ 3 filterName = 'WebSafe'
+ 4 if self._filters.has_key("WebSafe"):
+ 5 filter = self._currentFilter = self._filters[filterName]
+ 6 else:
+ 7 filter = self._currentFilter = \
+ 8 self._filters[filterName] = getattr(self._filtersLib,
+ filterName)(self).filter
+ 9 write(filter(VFS(SL,"ode",1))) # generated from '$ode' at line 4, col 1.
+10 write('\n')
+11 filterName = 'MaxLen'
+12 if self._filters.has_key("MaxLen"):
+13 filter = self._currentFilter = self._filters[filterName]
+14 else:
+15 filter = self._currentFilter = \
+16 self._filters[filterName] = getattr(self._filtersLib,
+ filterName)(self).filter
+17 write(filter(VFS(SL,"ode",1), maxlen=13)) # generated from
+ #'${ode, maxlen=13}' at line 6, col 1.
+18 write('\n')
+19 filter = self._initialFilter
+20 write(filter(VFS(SL,"ode",1), maxlen=13)) # generated from
+ #'${ode, maxlen=13}' at line 8, col 1.
+21 write('\n')
+\end{verbatim}
+
+As we've seen many times, Cheetah wraps all placeholder lookups in a
+\code{filter} call. (This also applies to non-searchList lookups: local,
+global and builtin variables.) The \code{filter} ``function''
+is actually an alias to the current filter object:
+\begin{verbatim}
+filter = self._currentFilter
+\end{verbatim}
+as set at the top of the main method. Here in lines 3-8 and 11-16 we see
+the filter being changed. Whoops, I lied. \code{filter} is not an alias to
+the filter object itself but to that object's \code{.filter} method. Line 19
+switches back to the default filter.
+
+In line 17 we see the \code{maxlen} argument being passed as a keyword
+argument to \code{filter} (not to \code{VFS}). In line 20 the same thing
+happens although the default filter ignores the argument.
+
+% Local Variables:
+% TeX-master: "devel_guide"
+% End:
diff --git a/docs/devel_guide_src/parser.tex b/docs/devel_guide_src/parser.tex
new file mode 100755
index 0000000..0198b5d
--- /dev/null
+++ b/docs/devel_guide_src/parser.tex
@@ -0,0 +1,9 @@
+\section{The parser}
+\label{parser}
+
+How templates are compiled: a walk through Parser.py's source.
+(Also need to look at Lexer.py, but not too closely.)
+
+% Local Variables:
+% TeX-master: "devel_guide"
+% End:
diff --git a/docs/devel_guide_src/parserInstructions.tex b/docs/devel_guide_src/parserInstructions.tex
new file mode 100644
index 0000000..0af065f
--- /dev/null
+++ b/docs/devel_guide_src/parserInstructions.tex
@@ -0,0 +1,61 @@
+\section{Directives: Parser Instructions}
+\label{parserInstructions}
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsection{\#breakpoint}
+\label{parserInstructions.breakpoint}
+
+
+The template:
+\begin{verbatim}
+Text before breakpoint.
+#breakpoint
+Text after breakpoint.
+#raise RuntimeError
+\end{verbatim}
+
+The output:
+\begin{verbatim}
+Text before breakpoint.
+\end{verbatim}
+
+The generated code:
+\begin{verbatim}
+write('Text before breakpoint.\n')
+\end{verbatim}
+
+Nothing after the breakpoint was compiled.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsection{\#compiler}
+\label{parserInstructions.compiler}
+
+The template:
+\begin{verbatim}
+// Not a comment
+#compiler commentStartToken = '//'
+// A comment
+#compiler reset
+// Not a comment
+\end{verbatim}
+
+The output:
+\begin{verbatim}
+// Not a comment
+// Not a comment
+\end{verbatim}
+
+The generated code:
+\begin{verbatim}
+write('// Not a comment\n')
+# A comment
+write('// Not a comment\n')
+\end{verbatim}
+
+So this didn't affect the generated program, it just affected how the
+template definition was read.
+
+% Local Variables:
+% TeX-master: "devel_guide"
+% End:
diff --git a/docs/devel_guide_src/patching.tex b/docs/devel_guide_src/patching.tex
new file mode 100755
index 0000000..6049068
--- /dev/null
+++ b/docs/devel_guide_src/patching.tex
@@ -0,0 +1,134 @@
+\section{Patching Cheetah}
+\label{patching}
+
+How to commit changes to CVS or submit patches, how to run the test suite.
+Describe distutils and how the regression tests work.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsection{File Requirements}
+\label{patching.fileRequirements}
+
+The code{Template} class contains not only the Cheetah infrastructure, but also
+some convenience methods useful in all templates. More methods may be added if
+it's generally agreed among Cheetah developers that the method is sufficiently
+useful to all types of templates, or at least to all types of HTML-output
+templates. If a method is too long to fit into \code{Template} -- especially
+if it has helper methods -- put it in a mixin class under \code{Cheetah.Utils}
+and inherit it.
+
+Routines for a specific problem domain should be put under
+\code{Cheetah.Tools}, so that it doesn't clutter the namespace unless the user
+asks for it.
+
+Remember: \code{Cheetah.Utils} is for objects required by any part of Cheetah's
+core. \code{Cheetah.Tools} is for completely optional objects. It should
+always be possible to delete \code{Cheetah.Tools} without breaking Cheetah's
+core services.
+
+If a core method needs to look up an attribute defined under
+\code{Cheetah.Tools}, it should use \code{hasattr()} and gracefully provide a
+default if the attribute does not exist (meaning the user has not imported that
+subsystem).
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsection{Testing Changes and Building Regression Tests}
+\label{patching.testing}
+
+Cheetah ships with a regression test suite. To run the built-in tests,
+execute at the shell prompt:
+\begin{verbatim}
+ cheetah test
+\end{verbatim}
+
+Before checking any changes in, run the tests and verify they all pass. That
+way, users can check out the CVS version of Cheetah at any time with a fairly
+high confidence that it will work. If you fix a bug or add a feature, please
+take the time to add a test that exploits the bug/feature. This will help in
+the future, to prevent somebody else from breaking it again without realizing
+it. Users can also run the test suite to verify all the features work on their
+particular platform and computer.
+
+The general procedure for modifying Cheetah is as follows:
+\begin{enumerate}
+\item Write a simple Python program that exploits the bug/feature you're
+ working on. You can either write a regression test (see below), or a
+ separate program that writes the template output to one file and put the
+ expected output in another file; then you can run \code{diff} on the two
+ outputs. (\code{diff} is a utility included on all Unix-like systems. It
+ shows the differences between two files line by line. A precompiled
+ Windows version is at
+ \url{http://gnuwin32.sourceforge.net/packages/diffutils.htm}, and MacOS
+ sources at \url{http://perso.wanadoo.fr/gilles.depeyrot/DevTools\_en.html}.)
+\item Make the change in your Cheetah CVS sandbox or in your installed
+ version of Cheetah. If you make it in the sandbox, you'll have to run
+ \code{python setup.py install} before testing it. If you make it in the
+ installed version, do {\em not} run the installer or it will overwrite your
+ changes!
+\item Run \code{cheetah test} to verify you didn't break anything. Then run
+ your little test program.
+\item Repeat steps 2-3 until everything is correct.
+\item Turn your little program into a regression test as described below.
+\item When \code{cheetah test} runs cleanly with your regression test
+ included, update the \code{CHANGES} file and check in your changes. If you
+ made the changes in your installed copy of Cheetah, you'll have to copy
+ them back into the CVS sandbox first. If you added any files that must be
+ distributed, {\em be sure to} \code{cvs add} them before committing.
+ Otherwise Cheetah will run fine on your computer but fail on anybody
+ else's, and the test suite can't check for this.
+\item Announce the change on the cheetahtemplate-discuss list and provide a
+ tutorial if necessary. The documentation maintainer will update the
+ Users' Guide and Developers' Guide based on this message and on the
+ changelog.
+\end{enumerate}
+
+If you add a directory to Cheetah, you have to mention it in \code{setup.py} or
+it won't be installed.
+
+The tests are in the \code{Cheetah.Tests} package, aka the \code{src/Tests/}
+directory of your CVS sandbox. Most of the tests are in
+\code{SyntaxAndOutput.py}. You can either run all the tests or choose which
+to run:
+\begin{description}
+\item{\code{python Test.py}}
+ Run all the tests. (Equivalent to \code{cheetah test}.)
+\item{\code{python SyntaxAndOutput.py}}
+ Run only the tests in that module.
+\item{\code{python SyntaxAndOutput.py CGI}}
+ Run only the tests in the class \code{CGI} inside the module. The class
+ must be a direct or indirect subclass of
+ \code{unittest\_local\_copy.TestCase}.
+\item{\code{python SyntaxAndOutput.py CGI Indenter}}
+ Run the tests in classes \code{CGI} and \code{Indenter}.
+\item{\code{python SyntaxAndOutput.py CGI.test1}}
+ Run only test \code{test1}, which is a method in the \code{CGI} class.
+\item{etc...}
+\end{description}
+
+To make a SyntaxAndOutput test, first see if your test logically fits into one
+of the existing classes. If so, simply add a method; e.g., \code{test16}.
+The method should not require any arguments except \code{self}, and should
+call \code{.verify(source, expectedOutput)}, where the two arguments are
+a template definition string and a control string. The tester will complain
+if the template output does not match the control string. You have a wide
+variety of placeholder variables to choose from, anything that's included in
+the \code{defaultTestNameSpace} global dictionary. If that's not enough, add
+items to the dictionary, but please keep it from being cluttered with wordy
+esoteric items for a single test).
+
+If your test logically belongs in a separate class, create a subclass of
+\code{OutputTest}. You do not need to do anything else; the test suite will
+automatically find your class in the module. Having a separate class allows
+you to define state variables needed by your tests (see the \code{CGI} class)
+or override \code{.searchList()} (see the \code{Indenter} class) to provide
+your own searchList.
+
+To modify another test module or create your own test module, you'll have to
+study the existing modules, the \code{unittest\_local\_copy} source, and the
+\code{unittest} documentation in the Python Library Reference. Note that we
+are using a hacked version of \code{unittest} to make a more convenient test
+structure for Cheetah. The differences between \code{unittest\_local\_copy}
+and Python's standard \code{unittest} are documented at the top of the module.
+
+% Local Variables:
+% TeX-master: "devel_guide"
+% End:
diff --git a/docs/devel_guide_src/placeholders.tex b/docs/devel_guide_src/placeholders.tex
new file mode 100755
index 0000000..e487d09
--- /dev/null
+++ b/docs/devel_guide_src/placeholders.tex
@@ -0,0 +1,478 @@
+\section{Placeholders}
+\label{placeholders}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsection{Simple placeholders}
+\label{placeholders.simple}
+
+Let's add a few \$placeholders to our template:
+
+\begin{verbatim}
+>>> from Cheetah.Template import Template
+>>> values = {'what': 'surreal', 'punctuation': '?'}
+>>> t = Template("""\
+... Hello, $what world$punctuation
+... One of Python's least-used functions is $xrange.
+... """, [values])
+>>> print t
+Hello, surreal world?
+One of Python's least-used functions is <built-in function xrange>.
+
+>>> print t.generatedModuleCode()
+ 1 #!/usr/bin/env python
+
+ 2 """
+ 3 Autogenerated by CHEETAH: The Python-Powered Template Engine
+ 4 CHEETAH VERSION: 0.9.12
+ 5 Generation time: Sun Apr 21 00:53:01 2002
+ 6 """
+
+ 7 __CHEETAH_genTime__ = 'Sun Apr 21 00:53:01 2002'
+ 8 __CHEETAH_version__ = '0.9.12'
+
+ 9 ##################################################
+ 10 ## DEPENDENCIES
+
+ 11 import sys
+ 12 import os
+ 13 import os.path
+ 14 from os.path import getmtime, exists
+ 15 import time
+ 16 import types
+ 17 from Cheetah.Template import Template
+ 18 from Cheetah.DummyTransaction import DummyTransaction
+ 19 from Cheetah.NameMapper import NotFound, valueForName,
+ valueFromSearchList
+ 20 import Cheetah.Filters as Filters
+ 21 import Cheetah.ErrorCatchers as ErrorCatchers
+
+ 22 ##################################################
+ 23 ## MODULE CONSTANTS
+
+ 24 try:
+ 25 True, False
+ 26 except NameError:
+ 27 True, False = (1==1), (1==0)
+
+ 28 ##################################################
+ 29 ## CLASSES
+
+ 30 class GenTemplate(Template):
+ 31 """
+ 32
+ 33 Autogenerated by CHEETAH: The Python-Powered Template Engine
+ 34 """
+
+ 35 ##################################################
+ 36 ## GENERATED METHODS
+
+\end{verbatim}
+\begin{verbatim}
+
+ 37 def __init__(self, *args, **KWs):
+ 38 """
+ 39
+ 40 """
+
+ 41 Template.__init__(self, *args, **KWs)
+
+ 42 def respond(self,
+ 43 trans=None,
+ 44 dummyTrans=False,
+ 45 VFS=valueFromSearchList,
+ 46 VFN=valueForName,
+ 47 getmtime=getmtime,
+ 48 currentTime=time.time):
+
+
+ 49 """
+ 50 This is the main method generated by Cheetah
+ 51 """
+
+ 52 if not trans:
+ 53 trans = DummyTransaction()
+ 54 dummyTrans = True
+ 55 write = trans.response().write
+ 56 SL = self._searchList
+ 57 filter = self._currentFilter
+ 58 globalSetVars = self._globalSetVars
+ 59
+ 60 ########################################
+ 61 ## START - generated method body
+ 62
+ 63 write('Hello, ')
+ 64 write(filter(VFS(SL,"what",1))) # generated from '$what' at
+ # line 1, col 8.
+ 65 write(' world')
+ 66 write(filter(VFS(SL,"punctuation",1))) # generated from
+ # '$punctuation' at line 1, col 19.
+ 67 write("\nOne of Python's least-used methods is ")
+ 68 write(filter(xrange)) # generated from '$xrange' at line 2,
+ # col 39.
+ 69 write('.\n')
+ 70
+ 71 ########################################
+ 72 ## END - generated method body
+ 73
+ 74 if dummyTrans:
+ 75 return trans.response().getvalue()
+ 76 else:
+ 77 return ""
+\end{verbatim}
+\begin{verbatim}
+ 78
+ 79 ##################################################
+ 80 ## GENERATED ATTRIBUTES
+
+ 81 __str__ = respond
+ 82 _mainCheetahMethod_for_GenTemplate= 'respond'
+
+ 83 # CHEETAH was developed by Tavis Rudd, Chuck Esterbrook, Ian Bicking
+ # and Mike Orr;
+ 84 # with code, advice and input from many other volunteers.
+ 85 # For more information visit http://www.CheetahTemplate.org
+
+ 86 ##################################################
+ 87 ## if run from command line:
+ 88 if __name__ == '__main__':
+ 89 GenTemplate().runAsMainProgram()
+
+\end{verbatim}
+
+(Again, I have added line numbers and split the lines as in the previous
+chapter.)
+
+This generated template module is different from the previous one in several
+trivial respects and one important respect. Trivially,
+\code{.\_filePath} and \code{.\_fileMtime} are not updated in
+\code{.\_\_init\_\_}, so they inherit the value \code{None} from
+\code{Template}. Also, that if-stanza in \code{.respond} that recompiles the
+template if the source file changes is missing -- because there is no source
+file. So this module is several lines shorter than the other one.
+
+But the important way this module is different is that instead of the one
+\code{write} call outputting a string literal, this module has a series of
+\code{write} calls (lines 63-69) outputting successive chunks of the
+template. Regular text has been translated into a string literal, and
+placeholders into function calls. Every placeholder is wrapped inside a
+\code{filter} call to apply the current output filter. (The default
+output filter converts all objects to strings, and \code{None} to \code{""}.)
+
+Placeholders referring to a Python builtin like \code{xrange} (line 68)
+generate a bare variable name. Placeholders to be looked up in the searchList
+have a nested function call; e.g.,
+\begin{verbatim}
+write(filter(VFS(SL,"what",1))) # generated from '$what' at line 1, col 8.
+\end{verbatim}
+\code{VFS}, remember, is a function imported from \code{Cheetah.NameMapper}
+that looks up a value in a searchList. So we pass it the searchList, the
+name to look up, and a boolean (1) indicating we want autocalling. (It's
+\code{1} rather than \code{True} because it's generated from an
+\code{and} expression, and that's what Python 2.2 outputs for true \code{and}
+expressions.)
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsection{Complex placeholders}
+\label{placeholders.complex}
+
+Placeholders can get far more complicated than that. This example shows what
+kind of code the various NameMapper features produce. The formulas are
+taken from Cheetah's test suite, in the
+\code{Cheetah.Tests.SyntaxAndOutput.Placeholders} class.
+
+\begin{verbatim}
+1 placeholder: $aStr
+2 placeholders: $aStr $anInt
+2 placeholders, back-to-back: $aStr$anInt
+1 placeholder enclosed in {}: ${aStr}
+1 escaped placeholder: \$var
+func placeholder - with (): $aFunc()
+func placeholder - with (int): $aFunc(1234)
+func placeholder - with (string): $aFunc('aoeu')
+func placeholder - with ('''\nstring'\n'''): $aFunc('''\naoeu'\n''')
+func placeholder - with (string*int): $aFunc('aoeu'*2)
+func placeholder - with (int*float): $aFunc(2*2.0)
+Python builtin values: $None $True $False
+func placeholder - with ($arg=float): $aFunc($arg=4.0)
+deeply nested argstring: $aFunc( $arg = $aMeth( $arg = $aFunc( 1 ) ) ):
+function with None: $aFunc(None)
+autocalling: $aFunc! $aFunc().
+nested autocalling: $aFunc($aFunc).
+list subscription: $aList[0]
+list slicing: $aList[:2]
+list slicing and subcription combined: $aList[:2][0]
+dict - NameMapper style: $aDict.one
+dict - Python style: $aDict['one']
+dict combined with autocalled string method: $aDict.one.upper
+dict combined with string method: $aDict.one.upper()
+nested dict - NameMapper style: $aDict.nestedDict.two
+nested dict - Python style: $aDict['nestedDict']['two']
+nested dict - alternating style: $aDict['nestedDict'].two
+nested dict - NameMapper style + method: $aDict.nestedDict.two.upper
+nested dict - alternating style + method: $aDict['nestedDict'].two.upper
+nested dict - NameMapper style + method + slice: $aDict.nestedDict.two.upper[:4]
+nested dict - Python style, variable key: $aDict[$anObj.meth('nestedDict')].two
+object method: $anObj.meth1
+object method + complex slice: $anObj.meth1[0: ((4/4*2)*2)/$anObj.meth1(2) ]
+very complex slice: $( anObj.meth1[0: ((4/4*2)*2)/$anObj.meth1(2) ] )
+$_('a call to gettext')
+\end{verbatim}
+
+We'll need a big program to set up the placeholder values. Here it is:
+
+\begin{verbatim}
+#!/usr/bin/env python
+from ComplexExample import ComplexExample
+
+try: # Python >= 2.2.1
+ True, False
+except NameError: # Older Python
+ True, False = (1==1), (1==0)
+
+class DummyClass:
+ _called = False
+ 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 callIt(self, arg=1234):
+ self._called = True
+ self._callArg = arg
+
+def dummyFunc(arg="Scooby"):
+ return arg
+
+defaultTestNameSpace = {
+ 'aStr':'blarg',
+ 'anInt':1,
+ 'aFloat':1.5,
+ 'aList': ['item0','item1','item2'],
+ 'aDict': {'one':'item1',
+ 'two':'item2',
+ 'nestedDict':{1:'nestedItem1',
+ 'two':'nestedItem2'
+ },
+ 'nestedFunc':dummyFunc,
+ },
+ 'aFunc': dummyFunc,
+ 'anObj': DummyClass(),
+ 'aMeth': DummyClass().meth1,
+ '_': lambda x: 'translated ' + x
+}
+
+print ComplexExample( searchList=[defaultTestNameSpace] )
+\end{verbatim}
+
+Here's the output:
+
+\begin{verbatim}
+1 placeholder: blarg
+2 placeholders: blarg 1
+2 placeholders, back-to-back: blarg1
+1 placeholder enclosed in {}: blarg
+1 escaped placeholder: $var
+func placeholder - with (): Scooby
+func placeholder - with (int): 1234
+func placeholder - with (string): aoeu
+func placeholder - with ('''\nstring'\n'''):
+aoeu'
+
+func placeholder - with (string*int): aoeuaoeu
+func placeholder - with (int*float): 4.0
+Python builtin values: 1 0
+func placeholder - with ($arg=float): 4.0
+deeply nested argstring: 1:
+function with None:
+autocalling: Scooby! Scooby.
+nested autocalling: Scooby.
+list subscription: item0
+list slicing: ['item0', 'item1']
+list slicing and subcription combined: item0
+dict - NameMapper style: item1
+dict - Python style: item1
+dict combined with autocalled string method: ITEM1
+dict combined with string method: ITEM1
+nested dict - NameMapper style: nestedItem2
+nested dict - Python style: nestedItem2
+nested dict - alternating style: nestedItem2
+nested dict - NameMapper style + method: NESTEDITEM2
+nested dict - alternating style + method: NESTEDITEM2
+nested dict - NameMapper style + method + slice: NEST
+nested dict - Python style, variable key: nestedItem2
+object method: doo
+object method + complex slice: do
+very complex slice: do
+translated a call to gettext
+
+\end{verbatim}
+
+And here -- tada! -- is the generated module.
+To save space, I've included only the lines containing the \code{write} calls.
+The rest of the module is the same as in the first example, chapter
+\ref{pyModules.example}. I've split some of the lines to make them fit on
+the page.
+
+\begin{verbatim}
+ 1 write('1 placeholder: ')
+ 2 write(filter(VFS(SL,"aStr",1))) # generated from '$aStr' at line 1, col 16.
+ 3 write('\n2 placeholders: ')
+ 4 write(filter(VFS(SL,"aStr",1))) # generated from '$aStr' at line 2, col 17.
+ 5 write(' ')
+ 6 write(filter(VFS(SL,"anInt",1)))
+ # generated from '$anInt' at line 2, col 23.
+ 7 write('\n2 placeholders, back-to-back: ')
+ 8 write(filter(VFS(SL,"aStr",1))) # generated from '$aStr' at line 3, col 31.
+ 9 write(filter(VFS(SL,"anInt",1)))
+ # generated from '$anInt' at line 3, col 36.
+10 write('\n1 placeholder enclosed in {}: ')
+11 write(filter(VFS(SL,"aStr",1))) # generated from '${aStr}' at line 4,
+ # col 31.
+12 write('\n1 escaped placeholder: $var\nfunc placeholder - with (): ')
+13 write(filter(VFS(SL,"aFunc",0)())) # generated from '$aFunc()' at line 6,
+ # col 29.
+14 write('\nfunc placeholder - with (int): ')
+15 write(filter(VFS(SL,"aFunc",0)(1234))) # generated from '$aFunc(1234)' at
+ # line 7, col 32.
+16 write('\nfunc placeholder - with (string): ')
+17 write(filter(VFS(SL,"aFunc",0)('aoeu'))) # generated from "$aFunc('aoeu')"
+ # at line 8, col 35.
+18 write("\nfunc placeholder - with ('''\\nstring'\\n'''): ")
+19 write(filter(VFS(SL,"aFunc",0)('''\naoeu'\n'''))) # generated from
+ # "$aFunc('''\\naoeu'\\n''')" at line 9, col 46.
+20 write('\nfunc placeholder - with (string*int): ')
+21 write(filter(VFS(SL,"aFunc",0)('aoeu'*2))) # generated from
+ # "$aFunc('aoeu'*2)" at line 10, col 39.
+22 write('\nfunc placeholder - with (int*float): ')
+23 write(filter(VFS(SL,"aFunc",0)(2*2.0))) # generated from '$aFunc(2*2.0)'
+ # at line 11, col 38.
+24 write('\nPython builtin values: ')
+25 write(filter(None)) # generated from '$None' at line 12, col 24.
+26 write(' ')
+27 write(filter(True)) # generated from '$True' at line 12, col 30.
+28 write(' ')
+29 write(filter(False)) # generated from '$False' at line 12, col 36.
+30 write('\nfunc placeholder - with ($arg=float): ')
+31 write(filter(VFS(SL,"aFunc",0)(arg=4.0))) # generated from
+ # '$aFunc($arg=4.0)' at line 13, col 40.
+32 write('\ndeeply nested argstring: ')
+33 write(filter(VFS(SL,"aFunc",0)(
+ arg = VFS(SL,"aMeth",0)( arg = VFS(SL,"aFunc",0)( 1 ) ) )))
+ # generated from '$aFunc( $arg = $aMeth( $arg = $aFunc( 1 ) ) )'
+ # at line 14, col 26.
+34 write(':\nfunction with None: ')
+35 write(filter(VFS(SL,"aFunc",0)(None))) # generated from '$aFunc(None)' at
+ # line 15, col 21.
+36 write('\nautocalling: ')
+37 write(filter(VFS(SL,"aFunc",1))) # generated from '$aFunc' at line 16,
+ # col 14.
+38 write('! ')
+39 write(filter(VFS(SL,"aFunc",0)())) # generated from '$aFunc()' at line 16,
+ # col 22.
+\end{verbatim}
+\begin{verbatim}
+40 write('.\nnested autocalling: ')
+41 write(filter(VFS(SL,"aFunc",0)(VFS(SL,"aFunc",1)))) # generated from
+ # '$aFunc($aFunc)' at line 17, col 21.
+42 write('.\nlist subscription: ')
+43 write(filter(VFS(SL,"aList",1)[0])) # generated from '$aList[0]' at line
+ # 18, col 20.
+44 write('\nlist slicing: ')
+45 write(filter(VFS(SL,"aList",1)[:2])) # generated from '$aList[:2]' at
+ # line 19, col 15.
+46 write('\nlist slicing and subcription combined: ')
+47 write(filter(VFS(SL,"aList",1)[:2][0])) # generated from '$aList[:2][0]'
+ # at line 20, col 40.
+48 write('\ndict - NameMapper style: ')
+49 write(filter(VFS(SL,"aDict.one",1))) # generated from '$aDict.one' at line
+ # 21, col 26.
+50 write('\ndict - Python style: ')
+51 write(filter(VFS(SL,"aDict",1)['one'])) # generated from "$aDict['one']"
+ # at line 22, col 22.
+52 write('\ndict combined with autocalled string method: ')
+53 write(filter(VFS(SL,"aDict.one.upper",1))) # generated from
+ # '$aDict.one.upper' at line 23, col 46.
+54 write('\ndict combined with string method: ')
+55 write(filter(VFN(VFS(SL,"aDict.one",1),"upper",0)())) # generated from
+ # '$aDict.one.upper()' at line 24, col 35.
+56 write('\nnested dict - NameMapper style: ')
+57 write(filter(VFS(SL,"aDict.nestedDict.two",1))) # generated from
+ # '$aDict.nestedDict.two' at line 25, col 33.
+58 write('\nnested dict - Python style: ')
+59 write(filter(VFS(SL,"aDict",1)['nestedDict']['two'])) # generated from
+ # "$aDict['nestedDict']['two']" at line 26, col 29.
+60 write('\nnested dict - alternating style: ')
+61 write(filter(VFN(VFS(SL,"aDict",1)['nestedDict'],"two",1))) # generated
+ # from "$aDict['nestedDict'].two" at line 27, col 34.
+62 write('\nnested dict - NameMapper style + method: ')
+63 write(filter(VFS(SL,"aDict.nestedDict.two.upper",1))) # generated from
+ # '$aDict.nestedDict.two.upper' at line 28, col 42.
+64 write('\nnested dict - alternating style + method: ')
+65 write(filter(VFN(VFS(SL,"aDict",1)['nestedDict'],"two.upper",1)))
+ # generated from "$aDict['nestedDict'].two.upper" at line 29, col 43.
+66 write('\nnested dict - NameMapper style + method + slice: ')
+\end{verbatim}
+\begin{verbatim}
+67 write(filter(VFN(VFS(SL,"aDict.nestedDict.two",1),"upper",1)[:4]))
+ # generated from '$aDict.nestedDict.two.upper[:4]' at line 30, col 50.
+68 write('\nnested dict - Python style, variable key: ')
+69 write(filter(VFN(VFS(SL,"aDict",1)
+ [VFN(VFS(SL,"anObj",1),"meth",0)('nestedDict')],"two",1)))
+ # generated from "$aDict[$anObj.meth('nestedDict')].two" at line 31,
+ # col 43.
+70 write('\nobject method: ')
+71 write(filter(VFS(SL,"anObj.meth1",1))) # generated from '$anObj.meth1' at
+ # line 32, col 16.
+72 write('\nobject method + complex slice: ')
+73 write(filter(VFN(VFS(SL,"anObj",1),"meth1",1)
+ [0: ((4/4*2)*2)/VFN(VFS(SL,"anObj",1),"meth1",0)(2) ]))
+ # generated from '$anObj.meth1[0: ((4/4*2)*2)/$anObj.meth1(2) ]'
+ # at line 33, col 32.
+74 write('\nvery complex slice: ')
+75 write(filter(VFN(VFS(SL,"anObj",1),"meth1",1)
+ [0: ((4/4*2)*2)/VFN(VFS(SL,"anObj",1),"meth1",0)(2) ] ))
+ # generated from '$( anObj.meth1[0: ((4/4*2)*2)/$anObj.meth1(2) ] )'
+ # at line 34, col 21.
+76 if False:
+77 _('foo')
+78 write(filter(VFS(SL,"_",0)("a call to gettext")))
+ # generated from "$_('a call to gettext')"
+ # at line 35, col 1.
+79 write('\n')
+\end{verbatim}
+
+For each placeholder lookup, the the innermost level of nesting is a \code{VFS}
+call, which looks up the first (leftmost) placeholder component in the
+searchList. This is wrapped by zero or more \code{VFN} calls, which perform
+Universal Dotted Notation lookup on the next dotted component of the
+placeholder, looking for an attribute or key by that name within the previous
+object (not in the searchList). Autocalling is performed by \code{VFS} and
+\code{VFN}: that's the reason for their third argument.
+
+Explicit function/method arguments, subscripts and keys (which
+are all expressions) are left unchanged, besides expanding any embedded
+\$placeholders in them. This means they must result in valid Python
+expressions, following the standard Python quoting rules.
+
+Built-in Python values (\code{None}, \code{True} and \code{False}) are
+converted to \code{filter(None)}, etc. They use normal Python variable
+lookup rather than \code{VFS}. (Cheetah emulates \code{True} and \code{False}
+using global variables for Python < 2.2.1, when they weren't builtins yet.)
+
+Notice the last line is a call to \code{_} (i.e. \code{gettext}) which is used
+for internationalization (see
+\url{http://docs.python.org/lib/module-gettext.html}). The code is converted
+normally, but an \code{if False} block is used so that gettext can
+successfully mark the string for translation when parsing the generated Python.
+Otherwise, the NameMapper syntax would get in the way of this.
+
+% Local Variables:
+% TeX-master: "devel_guide"
+% End:
diff --git a/docs/devel_guide_src/pyModules.tex b/docs/devel_guide_src/pyModules.tex
new file mode 100755
index 0000000..2aa3236
--- /dev/null
+++ b/docs/devel_guide_src/pyModules.tex
@@ -0,0 +1,246 @@
+\section{.py Template Modules}
+\label{pyModules}
+
+This chapter examines the structure of a .py template module. The following
+few chapters will then show how each placeholder and directive affects the
+generated Python code.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsection{An example}
+\label{pyModules.example}
+
+Our first template follows a long noble tradition in computer tutorials.
+It produces a familiar, friendly greeting. Here's the template:
+
+\begin{verbatim}
+Hello, world!
+\end{verbatim}
+
+... the output:
+
+\begin{verbatim}
+Hello, world!
+\end{verbatim}
+
+... and the .py template module cheetah-compile produced, with line
+numbers added:
+
+% @@MO: Is it possible to print the line numbers gray instead of black?
+
+\begin{verbatim}
+ 1 #!/usr/bin/env python
+
+ 2 """
+ 3 Autogenerated by CHEETAH: The Python-Powered Template Engine
+ 4 CHEETAH VERSION: 0.9.12
+ 5 Generation time: Sat Apr 20 14:27:47 2002
+ 6 Source file: x.tmpl
+ 7 Source file last modified: Wed Apr 17 22:10:59 2002
+ 8 """
+
+ 9 __CHEETAH_genTime__ = 'Sat Apr 20 14:27:47 2002'
+ 10 __CHEETAH_src__ = 'x.tmpl'
+ 11 __CHEETAH_version__ = '0.9.12'
+
+ 12 ##################################################
+ 13 ## DEPENDENCIES
+
+ 14 import sys
+ 15 import os
+ 16 import os.path
+ 17 from os.path import getmtime, exists
+ 18 import time
+ 19 import types
+ 20 from Cheetah.Template import Template
+ 21 from Cheetah.DummyTransaction import DummyTransaction
+ 22 from Cheetah.NameMapper import NotFound, valueForName,
+ valueFromSearchList
+ 23 import Cheetah.Filters as Filters
+ 24 import Cheetah.ErrorCatchers as ErrorCatchers
+
+ 25 ##################################################
+ 26 ## MODULE CONSTANTS
+
+ 27 try:
+ 28 True, False
+ 29 except NameError:
+ 30 True, False = (1==1), (1==0)
+
+ 31 ##################################################
+ 32 ## CLASSES
+
+ 33 class x(Template):
+ 34 """
+ 35
+ 36 Autogenerated by CHEETAH: The Python-Powered Template Engine
+ 37 """
+\end{verbatim}
+\begin{verbatim}
+ 38 ##################################################
+ 39 ## GENERATED METHODS
+
+
+ 40 def __init__(self, *args, **KWs):
+ 41 """
+ 42
+ 43 """
+
+ 44 Template.__init__(self, *args, **KWs)
+ 45 self._filePath = 'x.tmpl'
+ 46 self._fileMtime = 1019106659
+
+ 47 def respond(self,
+ 48 trans=None,
+ 49 dummyTrans=False,
+ 50 VFS=valueFromSearchList,
+ 51 VFN=valueForName,
+ 52 getmtime=getmtime,
+ 53 currentTime=time.time):
+
+
+ 54 """
+ 55 This is the main method generated by Cheetah
+ 56 """
+
+ 57 if not trans:
+ 58 trans = DummyTransaction()
+ 59 dummyTrans = True
+ 60 write = trans.response().write
+ 61 SL = self._searchList
+ 62 filter = self._currentFilter
+ 63 globalSetVars = self._globalSetVars
+ 64
+ 65 ########################################
+ 66 ## START - generated method body
+ 67
+ 68 if exists(self._filePath) and getmtime(self._filePath) > \
+ self._fileMtime:
+ 69 self.compile(file=self._filePath)
+ 70 write(getattr(self, self._mainCheetahMethod_for_x)
+ (trans=trans))
+ 71 if dummyTrans:
+ 72 return trans.response().getvalue()
+ 73 else:
+ 74 return ""
+ 75 write('Hello, world!\n')
+ 76
+ 77 ########################################
+ 78 ## END - generated method body
+ 79
+ 80 if dummyTrans:
+ 81 return trans.response().getvalue()
+ 82 else:
+ 83 return ""
+\end{verbatim}
+\begin{verbatim}
+ 84
+ 85 ##################################################
+ 86 ## GENERATED ATTRIBUTES
+
+
+ 87 __str__ = respond
+
+ 88 _mainCheetahMethod_for_x= 'respond'
+
+
+ 89 # CHEETAH was developed by Tavis Rudd, Chuck Esterbrook, Ian Bicking
+ # and Mike Orr;
+ 90 # with code, advice and input from many other volunteers.
+ 91 # For more information visit http://www.CheetahTemplate.org
+
+ 92 ##################################################
+ 93 ## if run from command line:
+ 94 if __name__ == '__main__':
+ 95 x().runAsMainProgram()
+
+\end{verbatim}
+
+(I added the line numbers for this Guide, and split a few lines to fit the
+page width. The continuation lines don't have line numbers, and I added
+indentation, backslashes and '\#' as necessary to make the result a valid
+Python program.)
+
+The examples were generated from CVS versions of Cheetah between 0.9.12 and
+0.9.14.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsection{A walk through the example}
+\label{pyModules.walk}
+
+Lines 20-24 are the Cheetah-specific imports. Line 33 introduces our generated
+class, \code{x}, a subclass of \code{Template}. It's called x because the
+source file was x.tmpl.
+
+Lines 40-46 are the \code{.\_\_init\_\_} method called when the template is
+instantiated or used as a Webware servlet, or when the module is run as a
+standalone program. We can see it calling its superclass constructor and
+setting \code{.\_filePath} and \code{.\_fileMtime} to the filename and
+modification time (in Unix ticks) of the source .tmpl file.
+
+Lines 47-84 are the main method \code{.respond}, the one that fills the
+template. Normally you call it without arguments, but Webware calls it with a
+Webware \code{Transaction} object representing the current request. Lines
+57-59 set up the \code{trans} variable. If a real or dummy transaction is
+passed in, the method uses it. Otherwise (if the \code{trans} argument is
+\code{None}), the method creates a \code{DummyTransaction} instance.
+\code{dummyTrans} is a flag that just tells whether a dummy transaction is in
+effect; it'll be used at the end of the method.
+
+The other four \code{.respond} arguments aren't anything you'd ever want to
+pass in; they exist solely to speed up access to these frequently-used
+global functions. This is a standard Python trick described in question 4.7
+of the Python FAQ (\url{http://www.python.org/cgi-bin/faqw.py}).
+\code{VFS} and \code{VFN} are the functions that give your template the
+benefits of NameMapper lookup, such as the ability to use the searchList.
+
+Line 60 initializes the \code{write} variable. This important variable is
+discussed below.
+
+Lines 60-63 initialize a few more local variables. \code{SL} is the
+searchList. \code{filter} is the current output filter. \code{globalSetVars}
+are the variables that have been defined with \code{\#set global}.
+
+The comments at lines 65 and 78 delimit the start and end of the code that
+varies with each template. The code outside this region is identical in all
+template modules. That's not quite true -- \code{\#import} for instance
+generates additional \code{import} statements at the top of the module --
+but it's true enough for the most part.
+
+Lines 68-74 exist only if the template source was a named file rather than
+a string or file object. The stanza recompiles the template if the source
+file has changed. Lines 70-74 seem to be redundant with 75-83: both
+fill the template and send the output. The reason the first set of lines
+exists is because the second set may become invalid when the template is
+recompiled. (This is for {\em re}\ compilation only. The initial compilation
+happened in the \code{.\_\_init\_\_} method if the template wasn't
+precompiled.)
+
+Line 75 is the most interesting line in this module. It's a direct
+translation of what we put in the template definition, ``Hello, world!'' Here
+the content is a single string literal. \code{write} looks like an ordinary
+function call, but remember that line 60 made it an alias to
+\code{trans.response().write}, a method in the transaction. The next few
+chapters describe how the different placeholders and directives influence this
+portion of the generated class.
+
+Lines 80-83 finish the template filling. If \code{trans} is a real Webware
+transaction, \code{write} has already sent the output to Webware for handling,
+so we return \code{""}. If \code{trans} is a dummy transaction,
+\code{write} has been accumulating the output in a Python \code{StringIO}
+object rather than sending it anywhere, so we have to return it.
+
+Line 83 is the end of the \code{.respond} method.
+
+Line 87 makes code{.\_\_str\_\_} an alias for the main method, so that you
+can \code{print} it or apply \code{str} to it and it will fill the template.
+Line 88 gives the name of the main method, because sometimes it's not
+\code{.respond}.
+
+Lines 94-95 allow the module to be run directly as a script. Essentially,
+they process the command-line arguments and them make the template fill
+itself.
+
+
+% Local Variables:
+% TeX-master: "devel_guide"
+% End:
diff --git a/docs/devel_guide_src/safeDelegation.tex b/docs/devel_guide_src/safeDelegation.tex
new file mode 100755
index 0000000..dd1a8aa
--- /dev/null
+++ b/docs/devel_guide_src/safeDelegation.tex
@@ -0,0 +1,44 @@
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\section{Safe Delegation}
+\label{safeDelegation}
+
+% @@MO: Does this really belong in the Developers' Guide or any guide?
+% It's more of a wiki Wishlist item, no? Especially since nobody has
+% expressed a need for it.
+
+Safe delegation, as provided by Zope and Allaire's Spectra, is not implemented
+in Cheetah. The core aim has been to help developers and template maintainers
+get things done, without throwing unnecessary complications in their
+way. So you should give write access to your templates only to those whom you
+trust. However, several hooks have been built into Cheetah so that safe
+delegation can be implemented at a later date.
+
+It should be possible to implement safe delegation via a future configuration
+Setting \code{safeDelegationLevel} (0=none, 1=semi-secure, 2-alcatraz). This
+is not implemented but the steps are listed here in case somebody wants to try
+them out and test them.
+
+Of course, you would also need to benchmark your code
+and verify it does not impact performance when safe delegation is off, and
+impacts it only modestly when it is on." All necessary changes can be made
+at compile time, so there should be no performance impact when filling the
+same TO multiple times.
+
+\begin{enumerate}
+
+\item Only give untrusted developers access to the .tmpl files.
+(Verifying what this means. Why can't trusted developers access them?)
+
+\item Disable the \code{\#attr} directive and maybe the \code{\#set} directive.
+
+\item Use Cheetah's directive validation hooks to disallow
+references to \code{self}, etc
+(e.g. \code{\#if \$steal(self.thePrivateVar)} )
+
+\item Implement a validator for the \$placeholders and use it
+to disallow '\_\_' in \$placeholders so that tricks like
+\code{\$obj.\_\_class\_\_.\_\_dict\_\_} are not possible.
+
+\end{enumerate}
+
+
diff --git a/docs/devel_guide_src/template.tex b/docs/devel_guide_src/template.tex
new file mode 100755
index 0000000..1ad21c4
--- /dev/null
+++ b/docs/devel_guide_src/template.tex
@@ -0,0 +1,11 @@
+\section{Template}
+\label{template}
+
+This chapter will mainly walk through the \code{Cheetah.Template} constructor
+and not at what point the template is compiled.
+
+(Also need to look at Transaction,py and Servlet.py.)
+
+% Local Variables:
+% TeX-master: "devel_guide"
+% End: