summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorR. Tyler Ballance <tyler@monkeypox.org>2009-11-16 23:06:19 -0800
committerR. Tyler Ballance <tyler@monkeypox.org>2009-11-16 23:06:19 -0800
commit3f373110b96f7453b8501178c47abfd506b5e126 (patch)
tree6f2a69a749fe97acb1238000d349a933f9b91740
parenta0dca853bab5fe5c935655163e4a6c4c66e16306 (diff)
downloadpython-cheetah-3f373110b96f7453b8501178c47abfd506b5e126.tar.gz
Add the dev_guide from the old LaTeX docs
-rw-r--r--www/dev_guide/bnf.rst6
-rw-r--r--www/dev_guide/cache.rst404
-rw-r--r--www/dev_guide/comments.rst103
-rw-r--r--www/dev_guide/compiler.rst8
-rw-r--r--www/dev_guide/design.rst104
-rw-r--r--www/dev_guide/errorHandling.rst329
-rw-r--r--www/dev_guide/files.rst11
-rw-r--r--www/dev_guide/flowControl.rst394
-rw-r--r--www/dev_guide/history.rst94
-rw-r--r--www/dev_guide/index.rst30
-rw-r--r--www/dev_guide/inheritanceEtc.rst255
-rw-r--r--www/dev_guide/introduction.rst28
-rw-r--r--www/dev_guide/output.rst315
-rw-r--r--www/dev_guide/parser.rst9
-rw-r--r--www/dev_guide/parserInstructions.rst67
-rw-r--r--www/dev_guide/patching.rst149
-rw-r--r--www/dev_guide/placeholders.rst489
-rw-r--r--www/dev_guide/pyModules.rst252
-rw-r--r--www/dev_guide/safeDelegation.rst40
-rw-r--r--www/dev_guide/template.rst11
-rw-r--r--www/index.rst1
-rw-r--r--www/users_guide/index.rst2
22 files changed, 3099 insertions, 2 deletions
diff --git a/www/dev_guide/bnf.rst b/www/dev_guide/bnf.rst
new file mode 100644
index 0000000..ef214b4
--- /dev/null
+++ b/www/dev_guide/bnf.rst
@@ -0,0 +1,6 @@
+A BNF Grammar of Cheetah
+========================
+
+(bnf)
+
+
diff --git a/www/dev_guide/cache.rst b/www/dev_guide/cache.rst
new file mode 100644
index 0000000..07775f2
--- /dev/null
+++ b/www/dev_guide/cache.rst
@@ -0,0 +1,404 @@
+Caching placeholders and #cache
+===============================
+
+(cache)
+
+Dynamic placeholder - no cache
+------------------------------
+
+(cache.dynamic)
+
+The template:
+
+::
+
+ Dynamic variable: $voom
+
+The command line and the output:
+
+::
+
+ % voom='Voom!' python x.py --env
+ Dynamic variable: Voom!
+
+The generated code:
+
+::
+
+ write('Dynamic variable: ')
+ write(filter(VFS(SL,"voom",1))) # generated from '$voom' at line 1, col 20.
+ write('\n')
+
+Just what we expected, like any other dynamic placeholder.
+
+Static placeholder
+------------------
+
+(cache.static)
+
+The template:
+
+::
+
+ Cached variable: $*voom
+
+The command line and output:
+
+::
+
+ % voom='Voom!' python x.py --env
+ Cached variable: Voom!
+
+The generated code, with line numbers:
+
+::
+
+ 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')
+
+That one little star generated a whole lotta code. First, instead
+of an ordinary {VFS} lookup (searchList) lookup, it converted the
+placeholder to a lookup in the {.\_cacheData} dictionary. Cheetah
+also generated a unique key ({'19760169'}) for our cached item -
+this is its cache ID.
+
+Second, Cheetah put a pair of if-blocks before the {write}. The
+first (lines 3-7) determine whether the cache value is missing or
+out of date, and sets local variable {RECACHE} true or false. This
+stanza may look unnecessarily verbose - lines 3-7 could be
+eliminated if line 8 was changed to
+
+::
+
+ if not self._cacheData.has_key('19760169'):
+
+- 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 {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 {write} object into, then read the result and stuff it
+into the cache.
+
+Timed-refresh placeholder
+-------------------------
+
+(cache.timed)
+
+The template:
+
+::
+
+ Timed cache: $*.5m*voom
+
+The command line and the output:
+
+::
+
+ % voom='Voom!' python x.py --env
+ Timed cache: Voom!
+
+The generated method's docstring:
+
+::
+
+ """
+ This is the main method generated by Cheetah
+ This cache will be refreshed every 30.0 seconds.
+ """
+
+The generated code:
+
+::
+
+ 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')
+
+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
+{.\_\_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 {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:
+
+::
+
+ """
+ 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.
+ """
+
+The docstring tells only that "something" will be refreshed every
+60.0 seconds, but doesn't reveal { which} placeholder that is. Only
+if you know the relative order of the placeholders in the template
+can you figure that out.
+
+Timed-refresh placeholder with braces
+-------------------------------------
+
+(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:
+
+::
+
+ Timed with {}: $*.5m*{voom}
+
+The output:
+
+::
+
+ Timed with {}: Voom!
+
+The generated code differs only in the comment. Inside the
+cache-refresh if-block:
+
+::
+
+ write(filter(VFS(SL,"voom",1))) # generated from '$*.5m*{voom}' at line 1,
+ #col 17.
+
+If you try to do it this way:
+
+::
+
+ Timed with {}: ${*.5m*voom} ## Wrong!
+
+you get:
+
+::
+
+ Timed with {}: ${*.5m*voom}
+
+``${`` is not a valid placeholder, so it gets treated as ordinary
+text.
+
+#cache
+------
+
+(cache.directive)
+
+The template:
+
+::
+
+ #cache
+ This is a cached region. $voom
+ #end cache
+
+The output:
+
+::
+
+ This is a cached region. Voom!
+
+The generated code:
+
+::
+
+ 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
+
+This is the same as the {$\*voom} example, except that the plain
+text around the placeholder is inside the second if-block.
+
+#cache with timer and id
+------------------------
+
+(cache.directive.timer)
+
+The template:
+
+::
+
+ #cache timer='.5m', id='cache1'
+ This is a cached region. $voom
+ #end cache
+
+The output:
+
+::
+
+ This is a cached region. Voom!
+
+The generated code is the same as the previous example except the
+first if-block:
+
+::
+
+ 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
+
+#cache with test: expression and method conditions
+--------------------------------------------------
+
+(cache.directive.test)
+
+The template:
+
+::
+
+ #cache test=$isDBUpdated
+ This is a cached region. $voom
+ #end cache
+
+(Analysis postponed: bug in Cheetah produces invalid Python.)
+
+The template:
+
+::
+
+ #cache id='cache1', test=($isDBUpdated or $someOtherCondition)
+ This is a cached region. $voom
+ #end cache
+
+The output:
+
+::
+
+ This is a cached region. Voom!
+
+The first if-block in the generated code:
+
+::
+
+ 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
+
+The second if-block is the same as in the previous example. If you
+leave out the {()} 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:
+
+::
+
+ #cache id='cache1', timer='30m', test=$isDBUpdated or $someOtherCondition
+ This is a cached region. $voom
+ #end cache
+
+The output:
+
+::
+
+ This is a cached region. Voom!
+
+The first if-block:
+
+::
+
+ 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
+
+We are planning to add a {'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.
+
+
diff --git a/www/dev_guide/comments.rst b/www/dev_guide/comments.rst
new file mode 100644
index 0000000..1195974
--- /dev/null
+++ b/www/dev_guide/comments.rst
@@ -0,0 +1,103 @@
+Directives: Comments
+====================
+
+(comments)
+
+The template:
+
+::
+
+ 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.
+
+The output:
+
+::
+
+ Text before the comment.
+ Text after the comment.
+
+ Text after the multi-line comment.
+
+The generated code:
+
+::
+
+ 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')
+
+Docstring and header comments
+-----------------------------
+
+(comments.docstring)
+
+The template:
+
+::
+
+ ##doc: .respond() method comment.
+ ##doc-method: Another .respond() method comment.
+ ##doc-class: A class comment.
+ ##doc-module: A module comment.
+ ##header: A header comment.
+
+The output:
+
+::
+
+
+
+The beginning of the generated {.respond} method:
+
+::
+
+ 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.
+ """
+
+The class docstring:
+
+::
+
+ """
+ A class comment.
+
+ Autogenerated by CHEETAH: The Python-Powered Template Engine
+ """
+
+The top of the module:
+
+::
+
+ #!/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
+ """
+
+
diff --git a/www/dev_guide/compiler.rst b/www/dev_guide/compiler.rst
new file mode 100644
index 0000000..33f3bcd
--- /dev/null
+++ b/www/dev_guide/compiler.rst
@@ -0,0 +1,8 @@
+The compiler
+============
+
+(compiler)
+
+How templates are compiled: a walk through Compiler.py.
+
+
diff --git a/www/dev_guide/design.rst b/www/dev_guide/design.rst
new file mode 100644
index 0000000..1ffabbd
--- /dev/null
+++ b/www/dev_guide/design.rst
@@ -0,0 +1,104 @@
+Design Decisions and Tradeoffs
+==============================
+
+(design)
+
+Delimiters
+----------
+
+(design.Delimiters)
+
+One of the first decisions we encountered was which delimiter
+syntax to use. We decided to follow Velocity's {$placeholder} and
+{#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
+``${longPlaceholder}`` syntax like the shells for cases where
+Cheetah or you might be confused where a placeholder ends. Tavis
+went ahead and made ``${longPlaceholder}`` and
+``$[longPlaceholder]`` interchangeable with it since it was trivial
+to implement. Finally, the {#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 {$} and {#}, do you?)
+
+The choice of comment delimiters was more arbitrary. {##} and {#\*
+... \*#} doesn't match any language, but it's reminiscent of Python
+and C while also being consistent with our "{#} is for directives"
+convention.
+
+We specifically chose { 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.
+
+Late binding
+------------
+
+(design.lateBinding)
+
+One of Cheetah's unique features is the name mapper, which lets you
+write {$a.b} without worrying much about the type of {a} or {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 {a} is, whether the the value is a function (autocall
+it), issue the appropriate Python incantation to look up {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 {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 {a} later
+changes type before a template filling? Answer: unpredictable
+exceptions occur. What if {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 {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 {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 {a} or care
+whether it exists. This allows users to create {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.
+
+Caching framework
+-----------------
+
+(design.cache)
+
+Webware compatibility and the transaction framework
+---------------------------------------------------
+
+(design.webware)
+
+Single inheritance
+------------------
+
+(design.singleInheritance)
+
+
diff --git a/www/dev_guide/errorHandling.rst b/www/dev_guide/errorHandling.rst
new file mode 100644
index 0000000..16aae35
--- /dev/null
+++ b/www/dev_guide/errorHandling.rst
@@ -0,0 +1,329 @@
+Directives: Error Handling
+==========================
+
+(errorHandling)
+
+#try and #raise
+---------------
+
+(errorHandling.try)
+
+The template:
+
+::
+
+ #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
+
+The output:
+
+::
+
+ A runtime error occurred.
+
+ Hahaha!
+
+ You can't divide by zero, idiot!
+
+The generated code:
+
+::
+
+ 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")
+
+{#finally} works just like in Python.
+
+#assert
+-------
+
+(errorHandling.assert)
+
+The template:
+
+::
+
+ #assert False, "You lose, buster!"
+
+The output:
+
+::
+
+ 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!
+
+The generated code:
+
+::
+
+ assert False, "You lose, buster!"
+
+#errorCatcher
+-------------
+
+(errorHandling.errorCatcher)
+
+No error catcher
+~~~~~~~~~~~~~~~~
+
+(errorHandling.errorCatcher.no)
+
+The template:
+
+::
+
+ $noValue
+
+The output:
+
+::
+
+ 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
+
+The generated code:
+
+::
+
+ write(filter(VFS(SL,"noValue",1))) # generated from '$noValue' at line 1,
+ # col 1.
+ write('\n')
+
+Echo and BigEcho
+~~~~~~~~~~~~~~~~
+
+(errorHandling.errorCatcher.echo)
+
+The template:
+
+::
+
+ #errorCatcher Echo
+ $noValue
+ #errorCatcher BigEcho
+ $noValue
+
+The output:
+
+::
+
+ $noValue
+ ===============&lt;$noValue could not be found&gt;===============
+
+The generated code:
+
+::
+
+ 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')
+
+ListErrors
+~~~~~~~~~~
+
+(errorHandling.errorCatcher.listErrors)
+
+The template:
+
+::
+
+ #import pprint
+ #errorCatcher ListErrors
+ $noValue
+ $anotherMissingValue.really
+ $pprint.pformat($errorCatcher.listErrors)
+ ## This is really self.errorCatcher().listErrors()
+
+The output:
+
+::
+
+ $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'}]
+
+The generated import:
+
+::
+
+ import pprint
+
+Then in the generated class, we have our familiar {.respond} method
+and several new methods:
+
+::
+
+ 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))
+
+::
+
+ 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 ""
+
+So whenever an error catcher is active, each placeholder gets
+wrapped in its own method. No wonder error catchers slow down the
+system!
+
+
diff --git a/www/dev_guide/files.rst b/www/dev_guide/files.rst
new file mode 100644
index 0000000..ab8d7a1
--- /dev/null
+++ b/www/dev_guide/files.rst
@@ -0,0 +1,11 @@
+Files
+=====
+
+(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.
+
+
diff --git a/www/dev_guide/flowControl.rst b/www/dev_guide/flowControl.rst
new file mode 100644
index 0000000..8b97f31
--- /dev/null
+++ b/www/dev_guide/flowControl.rst
@@ -0,0 +1,394 @@
+Directives: Flow Control
+========================
+
+(flowControl)
+
+#for
+----
+
+(flowControl.for)
+
+The template:
+
+::
+
+ #for $i in $range(10)
+ $i #slurp
+ #end for
+
+The output:
+
+::
+
+ 0 1 2 3 4 5 6 7 8 9
+
+The generated code:
+
+::
+
+ for i in range(10):
+ write(filter(i)) # generated from '$i' at line 2, col 1.
+ write(' ')
+
+#repeat
+-------
+
+(flowControl.repeat)
+
+The template:
+
+::
+
+ #repeat 3
+ My bonnie lies over the ocean
+ #end repeat
+ O, bring back my bonnie to me!
+
+The output:
+
+::
+
+ 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!
+
+(OK, so the second line should be "sea" instead of "ocean".)
+
+The generated code:
+
+::
+
+ for __i0 in range(3):
+ write('My bonnie lies over the ocean\n')
+ write('O, bring back my bonnie to me!\n')
+
+Note that a new local variable of the form {\_\_i$num} will be used
+for each instance of {repeat} in order to permit nesting.
+
+#while
+------
+
+(flowControl.while)
+
+The template:
+
+::
+
+ #set $alive = True
+ #while $alive
+ I am alive!
+ #set $alive = False
+ #end while
+
+The output:
+
+::
+
+ I am alive!
+
+The generated code:
+
+::
+
+ alive = True
+ while alive:
+ write('I am alive!\n')
+ alive = False
+
+#if
+---
+
+()
+
+The template:
+
+::
+
+ #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
+
+The output:
+
+::
+
+ It's small
+
+The generated code:
+
+::
+
+ 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")
+
+#unless
+-------
+
+(flowControl.unless)
+
+The template:
+
+::
+
+ #set $count = 9
+ #unless $count + 5 > 15
+ Count is in range.
+ #end unless
+
+The output:
+
+::
+
+ Count is in range.
+
+The generated code:
+
+::
+
+ count = 9
+ if not (count + 5 > 15):
+ write('Count is in range.\n')
+
+{ Note:} There is a bug in Cheetah 0.9.13. It's forgetting the
+parentheses in the {if} expression, which could lead to it
+calculating something different than it should.
+
+#break and #continue
+--------------------
+
+(flowControl.break)
+
+The template:
+
+::
+
+ #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
+
+The output:
+
+::
+
+ 1 - 2 - 3 - 4 - 5 - 6 - 7 - 8 - 9 - 11 - 12 - James -
+
+The generated code:
+
+::
+
+ 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(' - ')
+
+#pass
+-----
+
+(flowControl.pass)
+
+The template:
+
+::
+
+ 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.
+
+The output:
+
+::
+
+ Let's check the number.
+ Done checking the number.
+
+The generated code:
+
+::
+
+ 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')
+
+#stop
+-----
+
+(flowControl.stop)
+
+The template:
+
+::
+
+ A cat
+ #if 1
+ sat on a mat
+ #stop
+ watching a rat
+ #end if
+ in a flat.
+
+The output:
+
+::
+
+ A cat
+ sat on a mat
+
+The generated code:
+
+::
+
+ 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')
+
+#return
+-------
+
+(flowControl.return)
+
+The template:
+
+::
+
+ 1
+ $test[1]
+ 3
+ #def test
+ 1.5
+ #if 1
+ #return '123'
+ #else
+ 99999
+ #end if
+ #end def
+
+The output:
+
+::
+
+ 1
+ 2
+ 3
+
+The generated code:
+
+::
+
+ 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 ""
+
+::
+
+ 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 ""
+
+
diff --git a/www/dev_guide/history.rst b/www/dev_guide/history.rst
new file mode 100644
index 0000000..44e2200
--- /dev/null
+++ b/www/dev_guide/history.rst
@@ -0,0 +1,94 @@
+History of Cheetah
+==================
+
+(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 {%}
+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.
+
+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
+{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 {#for}, {#if} and
+{#echo}. (OK, {#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: {#while}, {#try}, {#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.
+
+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,
+http://www.cheetahtemplate.org/Py10.html.
+
+
diff --git a/www/dev_guide/index.rst b/www/dev_guide/index.rst
new file mode 100644
index 0000000..45b74b4
--- /dev/null
+++ b/www/dev_guide/index.rst
@@ -0,0 +1,30 @@
+Cheetah Developer's Guide
+==========================
+
+Overview
+--------
+This guide needs to really be filled out more
+
+.. toctree::
+ :maxdepth: 1
+
+ introduction.rst
+ compiler.rst
+ parser.rst
+ errorHandling.rst
+ placeholders.rst
+ patching.rst
+ flowControl.rst
+ design.rst
+ safeDelegation.rst
+ history.rst
+ output.rst
+ files.rst
+ cache.rst
+ bnf.rst
+ pyModules.rst
+ comments.rst
+ parserInstructions.rst
+ template.rst
+ inheritanceEtc.rst
+
diff --git a/www/dev_guide/inheritanceEtc.rst b/www/dev_guide/inheritanceEtc.rst
new file mode 100644
index 0000000..7dbed40
--- /dev/null
+++ b/www/dev_guide/inheritanceEtc.rst
@@ -0,0 +1,255 @@
+Directives: Import, Inheritance, Declaration and Assignment
+===========================================================
+
+(inheritanceEtc)
+
+#import and #from
+-----------------
+
+(inheritanceEtc.import)
+
+The template:
+
+::
+
+ #import math
+
+This construct does not produce any output.
+
+The generated module, at the bottom of the import section:
+
+::
+
+ import math
+
+#extends
+--------
+
+(inheritanceEtc.extends)
+
+The template:
+
+::
+
+ #extends SomeClass
+
+The generated import (skipped if {SomeClass} has already been
+imported):
+
+::
+
+ from SomeClass import SomeClass
+
+The generated class:
+
+::
+
+ class x(SomeClass):
+
+#implements
+-----------
+
+(inheritanceEtc.implements)
+
+The template:
+
+::
+
+ #implements doOutput
+
+In the generated class, the main method is {.doOutput} instead of
+{.respond}, and the attribute naming this method is:
+
+::
+
+ _mainCheetahMethod_for_x2= 'doOutput'
+
+#set and #set global
+--------------------
+
+(inheritanceEtc.set)
+
+The template:
+
+::
+
+ #set $namesList = ['Moe','Larry','Curly']
+ $namesList
+ #set global $toes = ['eeny', 'meeny', 'miney', 'moe']
+ $toes
+
+The output:
+
+::
+
+ ['Moe', 'Larry', 'Curly']
+ ['eeny', 'meeny', 'miney', 'moe']
+
+The generated code:
+
+::
+
+ 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')
+
+{globalSetVars} is a local variable referencing {.\_globalSetVars}.
+Writes go into it directly, but reads take advantage of the fact
+that {.\_globalSetVars} is on the searchList. (In fact, it's the
+very first namespace.)
+
+#del
+----
+
+(inheritanceEtc.del)
+
+The template:
+
+::
+
+ #set $a = 1
+ #del $a
+ #set $a = 2
+ #set $arr = [0, 1, 2]
+ #del $a, $arr[1]
+
+In the generated class:
+
+::
+
+ 1 a = 1
+ 2 del a
+ 3 a = 2
+ 4 arr = [0, 1, 2]
+ 5 del a, arr[1]
+
+#attr
+-----
+
+(inheritanceEtc.attr)
+
+The template:
+
+::
+
+ #attr $namesList = ['Moe', 'Larry', 'Curly']
+
+In the generated class:
+
+::
+
+ ## GENERATED ATTRIBUTES
+
+ namesList = ['Moe', 'Larry', 'Curly']
+
+#def
+----
+
+(inheritanceEtc.def)
+
+The template:
+
+::
+
+ #def printArg($arg)
+ The argument is $arg.
+ #end def
+ My method returned $printArg(5).
+
+The output:
+
+::
+
+ My method returned The argument is 5.
+ .
+
+Hmm, not exactly what we expected. The method returns a trailing
+newline because we didn't end the last line with {#slurp}. So the
+second period (outside the method) appears on a separate line.
+
+The {#def} generates a method {.printArg} whose structure is
+similar to the main method:
+
+::
+
+ 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 ""
+
+When {.printArg} is called from a placeholder, only the arguments
+the user supplied are passed. The other arguments retain their
+default values.
+
+#block
+------
+
+(inheritanceEtc.block)
+
+The template:
+
+::
+
+ #block content
+ This page is under construction.
+ #end block
+
+The output:
+
+::
+
+ This page is under construction.
+
+This construct generates a method {.content} in the same structure
+as {.printArg} above, containing the write code:
+
+::
+
+ write('This page is under construction.\n')
+
+In the main method, the write code is:
+
+::
+
+ self.content(trans=trans) # generated from ('content', '#block content')
+ # at line 1, col 1.
+
+So a block placeholder implicitly passes the current transaction to
+the method.
+
+
diff --git a/www/dev_guide/introduction.rst b/www/dev_guide/introduction.rst
new file mode 100644
index 0000000..c77e1e5
--- /dev/null
+++ b/www/dev_guide/introduction.rst
@@ -0,0 +1,28 @@
+Introduction
+============
+
+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.
+
+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.
+
+
diff --git a/www/dev_guide/output.rst b/www/dev_guide/output.rst
new file mode 100644
index 0000000..e29270f
--- /dev/null
+++ b/www/dev_guide/output.rst
@@ -0,0 +1,315 @@
+Directives: Output
+==================
+
+(output)
+
+#echo
+-----
+
+(output.echo)
+
+The template:
+
+::
+
+ Here is my #echo ', '.join(['silly']*5) # example
+
+The output:
+
+::
+
+ Here is my silly, silly, silly, silly, silly example
+
+The generated code:
+
+::
+
+ write('Here is my ')
+ write(filter(', '.join(['silly']*5) ))
+ write(' example\n')
+
+#silent
+-------
+
+(output.silent)
+
+The template:
+
+::
+
+ Here is my #silent ', '.join(['silly']*5) # example
+
+The output:
+
+::
+
+ Here is my example
+
+The generated code:
+
+::
+
+ write('Here is my ')
+ ', '.join(['silly']*5)
+ write(' example\n')
+
+OK, it's not quite covert because that extra space gives it away,
+but it almost succeeds.
+
+#raw
+----
+
+(output.raw)
+
+The template:
+
+::
+
+ Text before raw.
+ #raw
+ Text in raw. $alligator. $croc.o['dile']. #set $a = $b + $c.
+ #end raw
+ Text after raw.
+
+The output:
+
+::
+
+ Text before raw.
+ Text in raw. $alligator. $croc.o['dile']. #set $a = $b + $c.
+ Text after raw.
+
+The generated code:
+
+::
+
+ write('''Text before raw.
+ Text in raw. $alligator. $croc.o['dile']. #set $a = $b + $c.
+ Text after raw.
+ ''')
+
+So we see that {#raw} is really like a quoting mechanism. It says
+that anything inside it is ordinary text, and Cheetah joins a
+{#raw} section with adjacent string literals rather than generating
+a separate {write} call.
+
+#include
+--------
+
+(output.include)
+
+The main template:
+
+::
+
+ #include "y.tmpl"
+
+The included template y.tmpl:
+
+::
+
+ Let's go $voom!
+
+The shell command and output:
+
+::
+
+ % voom="VOOM" x.py --env
+ Let's go VOOM!
+
+The generated code:
+
+::
+
+ write(self._includeCheetahSource("y.tmpl", trans=trans, includeFrom="file",
+ raw=0))
+
+#include raw
+~~~~~~~~~~~~
+
+(output.include.raw)
+
+The main template:
+
+::
+
+ #include raw "y.tmpl"
+
+The shell command and output:
+
+::
+
+ % voom="VOOM" x.py --env
+ Let's go $voom!
+
+The generated code:
+
+::
+
+ write(self._includeCheetahSource("y.tmpl", trans=trans, includeFrom="fil
+ e", raw=1))
+
+That last argument, {raw}, makes the difference.
+
+#include from a string or expression (eval)
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+(output.include.expression)
+
+The template:
+
+::
+
+ #attr $y = "Let's go $voom!"
+ #include source=$y
+ #include raw source=$y
+ #include source="Bam! Bam!"
+
+The output:
+
+::
+
+ % voom="VOOM" x.py --env
+ Let's go VOOM!Let's go $voom!Bam! Bam!
+
+The generated code:
+
+::
+
+ 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"))
+
+Later in the generated class:
+
+::
+
+ y = "Let's go $voom!"
+
+#slurp
+------
+
+(output.slurp)
+
+The template:
+
+::
+
+ #for $i in range(5)
+ $i
+ #end for
+ #for $i in range(5)
+ $i #slurp
+ #end for
+ Line after slurp.
+
+The output:
+
+::
+
+ 0
+ 1
+ 2
+ 3
+ 4
+ 0 1 2 3 4 Line after slurp.
+
+The generated code:
+
+::
+
+ 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')
+
+The space after each number is because of the space before {#slurp}
+in the template definition.
+
+#filter
+-------
+
+(output.filter)
+
+The template:
+
+::
+
+ #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}
+
+The output:
+
+::
+
+ >> 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! <<
+
+The {WebSafe} filter escapes characters that have a special meaning
+in HTML. The {MaxLen} filter chops off values at the specified
+length. {#filter None} returns to the default filter, which ignores
+the {maxlen} argument.
+
+The generated code:
+
+::
+
+ 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')
+
+As we've seen many times, Cheetah wraps all placeholder lookups in
+a {filter} call. (This also applies to non-searchList lookups:
+local, global and builtin variables.) The {filter} "function" is
+actually an alias to the current filter object:
+
+::
+
+ filter = self._currentFilter
+
+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. {filter} is not an
+alias to the filter object itself but to that object's {.filter}
+method. Line 19 switches back to the default filter.
+
+In line 17 we see the {maxlen} argument being passed as a keyword
+argument to {filter} (not to {VFS}). In line 20 the same thing
+happens although the default filter ignores the argument.
+
+
diff --git a/www/dev_guide/parser.rst b/www/dev_guide/parser.rst
new file mode 100644
index 0000000..802b43f
--- /dev/null
+++ b/www/dev_guide/parser.rst
@@ -0,0 +1,9 @@
+The parser
+==========
+
+(parser)
+
+How templates are compiled: a walk through Parser.py's source.
+(Also need to look at Lexer.py, but not too closely.)
+
+
diff --git a/www/dev_guide/parserInstructions.rst b/www/dev_guide/parserInstructions.rst
new file mode 100644
index 0000000..805df7c
--- /dev/null
+++ b/www/dev_guide/parserInstructions.rst
@@ -0,0 +1,67 @@
+Directives: Parser Instructions
+===============================
+
+(parserInstructions)
+
+#breakpoint
+-----------
+
+(parserInstructions.breakpoint)
+
+The template:
+
+::
+
+ Text before breakpoint.
+ #breakpoint
+ Text after breakpoint.
+ #raise RuntimeError
+
+The output:
+
+::
+
+ Text before breakpoint.
+
+The generated code:
+
+::
+
+ write('Text before breakpoint.\n')
+
+Nothing after the breakpoint was compiled.
+
+#compiler
+---------
+
+(parserInstructions.compiler)
+
+The template:
+
+::
+
+ // Not a comment
+ #compiler commentStartToken = '//'
+ // A comment
+ #compiler reset
+ // Not a comment
+
+The output:
+
+::
+
+ // Not a comment
+ // Not a comment
+
+The generated code:
+
+::
+
+ write('// Not a comment\n')
+ # A comment
+ write('// Not a comment\n')
+
+So this didn't affect the generated program, it just affected how
+the template definition was read.
+
+
diff --git a/www/dev_guide/patching.rst b/www/dev_guide/patching.rst
new file mode 100644
index 0000000..8e67490
--- /dev/null
+++ b/www/dev_guide/patching.rst
@@ -0,0 +1,149 @@
+Patching Cheetah
+================
+
+(patching)
+
+How to commit changes to CVS or submit patches, how to run the test
+suite. Describe distutils and how the regression tests work.
+
+File Requirements
+-----------------
+
+(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 {Template} -
+especially if it has helper methods - put it in a mixin class under
+{Cheetah.Utils} and inherit it.
+
+Routines for a specific problem domain should be put under
+{Cheetah.Tools}, so that it doesn't clutter the namespace unless
+the user asks for it.
+
+Remember: {Cheetah.Utils} is for objects required by any part of
+Cheetah's core. {Cheetah.Tools} is for completely optional objects.
+It should always be possible to delete {Cheetah.Tools} without
+breaking Cheetah's core services.
+
+If a core method needs to look up an attribute defined under
+{Cheetah.Tools}, it should use {hasattr()} and gracefully provide a
+default if the attribute does not exist (meaning the user has not
+imported that subsystem).
+
+Testing Changes and Building Regression Tests
+---------------------------------------------
+
+(patching.testing)
+
+Cheetah ships with a regression test suite. To run the built-in
+tests, execute at the shell prompt:
+
+::
+
+ cheetah test
+
+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:
+
+
+#. 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 {diff} on the two outputs. ({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
+ http://gnuwin32.sourceforge.net/packages/diffutils.htm, and MacOS
+ sources at
+ http://perso.wanadoo.fr/gilles.depeyrot/DevTools\_en.html.)
+
+#. 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 {python setup.py install} before testing it. If you make it in
+ the installed version, do { not} run the installer or it will
+ overwrite your changes!
+
+#. Run {cheetah test} to verify you didn't break anything. Then run
+ your little test program.
+
+#. Repeat steps 2-3 until everything is correct.
+
+#. Turn your little program into a regression test as described
+ below.
+
+#. When {cheetah test} runs cleanly with your regression test
+ included, update the {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, { be sure to} {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.
+
+#. 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.
+
+
+If you add a directory to Cheetah, you have to mention it in
+{setup.py} or it won't be installed.
+
+The tests are in the {Cheetah.Tests} package, aka the {src/Tests/}
+directory of your CVS sandbox. Most of the tests are in
+{SyntaxAndOutput.py}. You can either run all the tests or choose
+which to run:
+
+ Run all the tests. (Equivalent to {cheetah test}.)
+
+ Run only the tests in that module.
+
+ Run only the tests in the class {CGI} inside the module. The class
+ must be a direct or indirect subclass of
+ {unittest\_local\_copy.TestCase}.
+
+ Run the tests in classes {CGI} and {Indenter}.
+
+ Run only test {test1}, which is a method in the {CGI} class.
+
+
+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., {test16}. The method should not require any arguments except
+{self}, and should call {.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
+{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 {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 {CGI} class) or override {.searchList()} (see
+the {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
+{unittest\_local\_copy} source, and the {unittest} documentation in
+the Python Library Reference. Note that we are using a hacked
+version of {unittest} to make a more convenient test structure for
+Cheetah. The differences between {unittest\_local\_copy} and
+Python's standard {unittest} are documented at the top of the
+module.
+
+
diff --git a/www/dev_guide/placeholders.rst b/www/dev_guide/placeholders.rst
new file mode 100644
index 0000000..13b91d6
--- /dev/null
+++ b/www/dev_guide/placeholders.rst
@@ -0,0 +1,489 @@
+Placeholders
+============
+
+(placeholders)
+
+Simple placeholders
+-------------------
+
+(placeholders.simple)
+
+Let's add a few $placeholders to our template:
+
+::
+
+ >>> 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
+
+
+::
+
+ 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 ""
+
+::
+
+ 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()
+
+
+(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,
+{.\_filePath} and {.\_fileMtime} are not updated in
+{.\_\_init\_\_}, so they inherit the value {None} from {Template}.
+Also, that if-stanza in {.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 {write} call outputting a string literal, this module has a
+series of {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 {filter} call to apply the current output filter.
+(The default output filter converts all objects to strings, and
+{None} to {""}.)
+
+Placeholders referring to a Python builtin like {xrange} (line 68)
+generate a bare variable name. Placeholders to be looked up in the
+searchList have a nested function call; e.g.,
+
+::
+
+ write(filter(VFS(SL,"what",1))) # generated from '$what' at line 1, col 8.
+
+{VFS}, remember, is a function imported from {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 {1} rather than {True} because it's
+generated from an {and} expression, and that's what Python 2.2
+outputs for true {and} expressions.)
+
+Complex placeholders
+--------------------
+
+(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
+{Cheetah.Tests.SyntaxAndOutput.Placeholders} class.
+
+::
+
+ 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')
+
+We'll need a big program to set up the placeholder values. Here it
+is:
+
+::
+
+ #!/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] )
+
+Here's the output:
+
+::
+
+ 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
+
+And here - tada! - is the generated module. To save space, I've
+included only the lines containing the {write} calls. The rest of
+the module is the same as in the first example, chapter
+pyModules.example. I've split some of the lines to make them fit on
+the page.
+
+::
+
+ 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.
+
+::
+
+ 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: ')
+
+::
+
+ 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')
+
+For each placeholder lookup, the the innermost level of nesting is
+a {VFS} call, which looks up the first (leftmost) placeholder
+component in the searchList. This is wrapped by zero or more {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 {VFS} and {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 ({None}, {True} and {False}) are converted
+to {filter(None)}, etc. They use normal Python variable lookup
+rather than {VFS}. (Cheetah emulates {True} and {False} using
+global variables for Python < 2.2.1, when they weren't builtins
+yet.)
+
+Notice the last line is a call to {\_} (i.e. {gettext}) which is
+used for internationalization (see
+http://docs.python.org/lib/module-gettext.html). The code is
+converted normally, but an {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.
+
+
diff --git a/www/dev_guide/pyModules.rst b/www/dev_guide/pyModules.rst
new file mode 100644
index 0000000..ceaf53f
--- /dev/null
+++ b/www/dev_guide/pyModules.rst
@@ -0,0 +1,252 @@
+.py Template Modules
+====================
+
+(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.
+
+An example
+----------
+
+(pyModules.example)
+
+Our first template follows a long noble tradition in computer
+tutorials. It produces a familiar, friendly greeting. Here's the
+template:
+
+::
+
+ Hello, world!
+
+... the output:
+
+::
+
+ Hello, world!
+
+... and the .py template module cheetah-compile produced, with line
+numbers added:
+
+::
+
+ 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 """
+
+::
+
+ 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 ""
+
+::
+
+ 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()
+
+
+(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.
+
+A walk through the example
+--------------------------
+
+(pyModules.walk)
+
+Lines 20-24 are the Cheetah-specific imports. Line 33 introduces
+our generated class, {x}, a subclass of {Template}. It's called x
+because the source file was x.tmpl.
+
+Lines 40-46 are the {.\_\_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 {.\_filePath} and {.\_fileMtime} to the
+filename and modification time (in Unix ticks) of the source .tmpl
+file.
+
+Lines 47-84 are the main method {.respond}, the one that fills the
+template. Normally you call it without arguments, but Webware calls
+it with a Webware {Transaction} object representing the current
+request. Lines 57-59 set up the {trans} variable. If a real or
+dummy transaction is passed in, the method uses it. Otherwise (if
+the {trans} argument is {None}), the method creates a
+{DummyTransaction} instance. {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 {.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
+(http://www.python.org/cgi-bin/faqw.py). {VFS} and {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 {write} variable. This important variable
+is discussed below.
+
+Lines 60-63 initialize a few more local variables. {SL} is the
+searchList. {filter} is the current output filter. {globalSetVars}
+are the variables that have been defined with {#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 -
+{#import} for instance generates additional {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 {
+re} compilation only. The initial compilation happened in the
+{.\_\_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.
+{write} looks like an ordinary function call, but remember that
+line 60 made it an alias to {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 {trans} is a real
+Webware transaction, {write} has already sent the output to Webware
+for handling, so we return {""}. If {trans} is a dummy transaction,
+{write} has been accumulating the output in a Python {StringIO}
+object rather than sending it anywhere, so we have to return it.
+
+Line 83 is the end of the {.respond} method.
+
+Line 87 makes code{.\_\_str\_\_} an alias for the main method, so
+that you can {print} it or apply {str} to it and it will fill the
+template. Line 88 gives the name of the main method, because
+sometimes it's not {.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.
+
+
diff --git a/www/dev_guide/safeDelegation.rst b/www/dev_guide/safeDelegation.rst
new file mode 100644
index 0000000..87f3dc1
--- /dev/null
+++ b/www/dev_guide/safeDelegation.rst
@@ -0,0 +1,40 @@
+Safe Delegation
+===============
+
+(safeDelegation)
+
+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 {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.
+
+
+#. Only give untrusted developers access to the .tmpl files.
+ (Verifying what this means. Why can't trusted developers access
+ them?)
+
+#. Disable the {#attr} directive and maybe the {#set} directive.
+
+#. Use Cheetah's directive validation hooks to disallow references
+ to {self}, etc (e.g. {#if $steal(self.thePrivateVar)} )
+
+#. Implement a validator for the $placeholders and use it to
+ disallow '\_\_' in $placeholders so that tricks like
+ {$obj.\_\_class\_\_.\_\_dict\_\_} are not possible.
+
+
+
diff --git a/www/dev_guide/template.rst b/www/dev_guide/template.rst
new file mode 100644
index 0000000..8dd23d9
--- /dev/null
+++ b/www/dev_guide/template.rst
@@ -0,0 +1,11 @@
+Template
+========
+
+(template)
+
+This chapter will mainly walk through the {Cheetah.Template}
+constructor and not at what point the template is compiled.
+
+(Also need to look at Transaction,py and Servlet.py.)
+
+
diff --git a/www/index.rst b/www/index.rst
index 185e2bb..6059d6b 100644
--- a/www/index.rst
+++ b/www/index.rst
@@ -34,6 +34,7 @@ Contents
users_guide/index.rst
documentation.rst
roadmap.rst
+ dev_guide/index.rst
chep.rst
diff --git a/www/users_guide/index.rst b/www/users_guide/index.rst
index 26af998..93b0ce4 100644
--- a/www/users_guide/index.rst
+++ b/www/users_guide/index.rst
@@ -1,8 +1,6 @@
Cheetah User's Guide
====================
-Contents
----------
.. toctree::
:maxdepth: 2