1. Miki Tebeka
  2. pythonwise

Commits

Miki Tebeka  committed 8a41798

Initial upload

  • Participants
  • Branches default
  • Tags initial-upload

Comments (0)

Files changed (139)

File LICENSE.txt

View file
+Copyright (c) 2008, Miki Tebeka <miki.tebeka@gmail.com>
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+* Redistributions of source code must retain the above copyright notice, this
+  list of conditions and the following disclaimer.
+* Redistributions in binary form must reproduce the above copyright notice,
+  this list of conditions and the following disclaimer in the documentation
+  and/or other materials provided with the distribution.
+* Neither the name of the "PythonWise" blog nor the names of its contributors
+  may be used to endorse or promote products derived from this software without
+  specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

File README.txt

View file
+This is the code from my blog at http://pythonwise.blogspot.com
+
+All the code here is under BSD License, see LICENSE.txt file for more details.
+
+Miki Tebeka <miki.tebeka@gmail.com>

File ansiprint

View file
+#!/usr/bin/env python
+'''Print message using ANSI terminal codes'''
+
+__author__ = "Miki Tebeka <miki.tebeka@gmail.com>"
+
+from sys import stdout, stderr
+
+# Format
+bright = 1
+dim = 2
+underline = 4
+blink = 5
+reverse = 7
+hidden = 8
+
+# Forground
+black = 30
+red = 31
+green = 32
+yellow = 33
+blue = 34
+magenta = 35
+cyan = 36
+white = 37
+
+# Background
+on_black = 40
+on_red = 41
+on_green = 42
+on_yellow = 43
+on_blue = 44
+on_magenta = 45
+on_cyan = 46
+on_white = 47
+
+def ansiformat(msg, *args):
+    '''Format msg according to args.
+
+    See http://www.termsys.demon.co.uk/vtansi.htm for more details/
+    '''
+    return "\033[%sm%s\033[0m" % (";".join(["%s" % f for f in args]), msg)
+
+def ansiprint(msg, *args, **kw):
+    '''Print formatted message.
+
+    Should work on ANSI compatible terminal.
+    '''
+
+    if kw.get("stderr", 0):
+        outfo = stderr
+    else:
+        outfo = stdout
+
+    outfo.write(ansiformat(msg, *args))
+    outfo.flush()
+
+if __name__ == "__main__":
+    from sys import argv, exit
+    from os.path import basename
+
+    h = {
+        "bright" : bright,
+        "dim" : dim,
+        "underline" : underline,
+        "blink" : blink,
+        "reverse" : reverse,
+        "hidden" : hidden,
+        "black" : black,
+        "red" : red,
+        "green" : green,
+        "yellow" : yellow,
+        "blue" : blue,
+        "magenta" : magenta,
+        "cyan" : cyan,
+        "white" : white,
+        "on_black" : on_black,
+        "on_red" : on_red,
+        "on_green" : on_green,
+        "on_yellow" : on_yellow,
+        "on_blue" : on_blue,
+        "on_magenta" : on_magenta,
+        "on_cyan" : on_cyan,
+        "on_white" : on_white
+    }
+
+    eg = "e.g. ansiprint hello red on_green underline -> %s" % \
+        ansiformat("hello", red, on_green, underline)
+
+    if len(argv) < 2:
+        print >> stderr, "usage: %s message [format ...]" % basename(argv[0])
+        print >> stderr, eg
+        exit(1)
+
+    for i in argv[2:]:
+        if i not in h:
+            ansiprint("%s: Unknown format\n" % i, red, bright, stderr=True)
+            print >> stderr, "Formats can be:",
+            msg = ", ".join([ansiformat(f, h[f]) for f in h.keys()])
+            print msg
+            print >> stderr, eg
+            exit(1)
+
+    ansiprint(argv[1], *[h[i] for i in argv[2:]])
+    print

File attview

View file
+#!/usr/bin/env python
+'''View attachment with right application'''
+
+__author__ = "Miki Tebeka <miki.tebeka@gmail.com>"
+
+from os import popen, system
+from os.path import isfile
+import re
+
+class ViewError(Exception):
+   pass
+
+def view_attachment(data):
+    # In the .destop file, the file name is %u or %U
+    u_sub = re.compile("%u", re.I).sub
+
+    FILENAME = "/tmp/attview"
+    fo = open(FILENAME, "wb")
+    fo.write(data)
+    fo.close()
+
+    mime_type = popen("file -ib %s" % FILENAME).read().strip()
+    if ";" in mime_type:
+        mime_type = mime_type[:mime_type.find(";")]
+    if mime_type == "application/x-not-regular-file":
+        raise ViewError("can't guess mime type")
+
+    APPS_DIR = "/usr/share/applications"
+    for line in open("%s/defaults.list" % APPS_DIR):
+        if line.startswith(mime_type):
+            mime, appfile = line.strip().split("=")
+            break
+    else:
+        raise ViewError("can't find how to open %s" % mime_type)
+
+    appfile = "%s/%s" % (APPS_DIR, appfile)
+    if not isfile(appfile):
+        raise ViewError("can't find %s" % appfile)
+    for line in open(appfile):
+        line = line.strip()
+        if line.startswith("Exec"):
+            key, cmd = line.split("=")
+            fullcmd = u_sub(FILENAME, cmd)
+            if fullcmd == cmd:
+                fullcmd += " %s" % FILENAME
+            system(fullcmd + "&")
+            break
+    else:
+        raise ViewError("can't find Exec in %s" % appfile)
+
+
+def main(argv=None):
+    from sys import stdin
+    if argv is None:
+        import sys
+        argv = sys.argv
+
+    from optparse import OptionParser
+
+    parser = OptionParser("usage: %prog [FILENAME]")
+
+    opts, args = parser.parse_args(argv[1:])
+    if len(args) not in (0, 1):
+        parser.error("wrong number of arguments") # Will exit
+
+    filename = args[0] if args else "-"
+
+    if filename == "-":
+        data = stdin.read()
+    else:
+        try:
+            data = open(filename, "rb").read()
+        except IOError, e:
+            raise SystemExit("error: %s" % e.strerror)
+
+    try:
+        view_attachment(data)
+    except ViewError, e:
+        raise SystemExit("error: %s" % e)
+
+if __name__ == "__main__":
+    main()

File autobuild-c-ext/greet/__init__.py

View file
+def _build():
+    from os.path import getmtime, isfile, dirname, join
+    from sys import executable
+    from os import system
+
+    from setup import DYNLIB, SRC_FILE
+
+    MODULE_DIR = dirname(__file__)
+
+    def _run(cmd):
+        return system("(cd \"%s\" && %s) > /dev/null 2>&1" % (MODULE_DIR, cmd))
+
+    _full_src = join(MODULE_DIR, SRC_FILE)
+    _full_dynlib = join(MODULE_DIR, DYNLIB)
+
+    if (not isfile(_full_dynlib)) or (getmtime(_full_dynlib) < getmtime(_full_src)):
+        assert _run("%s setup.py build_ext -i" % executable) == 0, "build error" 
+
+_build()
+del _build 
+from _greet import * 

File autobuild-c-ext/greet/_greet.c

View file
+#include <Python.h>
+#include <stdio.h>
+
+static PyObject *
+greet_greet(PyObject *self, PyObject *args)
+{
+   char *name;
+
+   if (!PyArg_ParseTuple(args, "s", &name)) {
+       return NULL;
+   }
+
+   printf("Hello %s\n", name);
+ 
+   return Py_BuildValue("");
+}
+
+static PyMethodDef GreetMethods[] = {
+   { "greet",  greet_greet, METH_VARARGS,
+     "Print a friendly greeting."
+   },
+   {NULL, NULL, 0, NULL}        /* Sentinel */
+};
+
+PyMODINIT_FUNC
+init_greet(void)
+{
+   Py_InitModule("_greet", GreetMethods);
+}

File autobuild-c-ext/greet/setup.py

View file
+from distutils.core import Extension, setup
+
+MODULE_NAME = "_greet"
+DYNLIB = MODULE_NAME + ".so"
+SRC_FILE = MODULE_NAME + ".c"
+
+if __name__ == "__main__":
+    setup(ext_modules=[Extension(MODULE_NAME, [SRC_FILE])])

File autobuild-c-ext/t.py

View file
+#!/usr/bin/env python
+
+import greet
+
+greet.greet("Dude")

File benchmark.py

View file
+#!/usr/bin/env python
+def fast_fib(n):
+    if n < 2:
+        return 1
+
+    a, b = 1, 1
+    for i in range(n - 1):
+        a, b = b, a + b
+
+    return b
+
+def benchmark():
+    from timeit import Timer
+
+    benchmark.index = 20
+    timer = Timer("fast_fib(benchmark.index)",
+                  "from __main__ import fast_fib, benchmark")
+    num_runs = 100
+
+    print timer.timeit(num_runs) / num_runs
+
+def main(argv=None):
+    if argv is None:
+        import sys
+        argv = sys.argv
+
+    from optparse import OptionParser
+
+    parser = OptionParser("usage: %prog [options] INDEX")
+    parser.add_option("--benchmark", help="run benchmark",
+                      dest="benchmark", action="store_true", default=0)
+
+    opts, args = parser.parse_args(argv[1:])
+
+    if opts.benchmark:
+        benchmark()
+        raise SystemExit()
+
+    if len(args) != 1:
+        parser.error("wrong number of arguments") # Will exit
+
+    # Do main program stuff here
+    try:
+        print fast_fib(int(args[0]))
+    except ValueError:
+        raise SystemExit("error: %s - bad number" % args[0])
+
+if __name__ == "__main__":
+    main()

File bits.py

View file
+def mask(size):
+    '''Mask for `size' bits
+
+    >>> mask(3)
+    7
+    '''
+    return (1L << size) - 1
+
+def num2bits(num, width=32):
+    '''String represntation (in bits) of a number
+
+    >>> num2bits(3, 5) 
+    '00011'
+    '''
+    s = ""
+    for bit in range(width - 1, -1, -1):
+        if num & (1L << bit):
+            s += "1"
+        else:
+            s += "0"
+    return s
+
+def get_bit(value, bit):
+    '''Get value of bit
+
+    >>> num2bits(5, 5)
+    '00101'
+    >>> get_bit(5, 0)
+    1
+    >>> get_bit(5, 1)
+    0
+    '''
+    return (value >> bit) & 1
+
+def get_range(value, start, end):
+    '''Get range of bits
+
+    >>> num2bits(5, 5)
+    '00101'
+    >>> get_range(5, 0, 1)
+    1
+    >>> get_range(5, 1, 2)
+    2
+    '''
+    return (value >> start) & mask(end - start + 1)
+
+def set_bit(num, bit, value):
+    '''Set bit `bit' in num to `value' 
+
+    >>> i = 5
+    >>> set_bit(i, 1, 1)
+    7
+    >>> set_bit(i, 0, 0)
+    4
+    '''
+    if value:
+        return num | (1L << bit)
+    else:
+        return num & (~(1L << bit))
+
+def sign_extend(num, size):
+    '''Sign exten number who is `size' bits wide
+    
+    >>> sign_extend(5, 2)
+    1
+    >>> sign_extend(5, 3)
+    -3
+    '''
+    m = mask(size - 1)
+    res = num & m
+    # Positive
+    if (num & (1L << (size - 1))) == 0:
+        return res
+
+    # Negative, 2's complement
+    res = ~res
+    res &= m
+    res += 1
+    return -res

File cheetah-demo/Cheetah/CacheRegion.py

View file
+# $Id: CacheRegion.py,v 1.3 2006/01/28 04:19:30 tavis_rudd Exp $
+"""Cache holder classes for Cheetah:
+
+Cache regions are defined using the #cache Cheetah directive. Each
+cache region can be viewed as a dictionary (keyed by cacheRegionID)
+handling at least one cache item (the default one). It's possible to add
+cacheItems in a region by using the `varyBy` #cache directive parameter as
+in the following example::
+   #def getArticle
+      this is the article content.
+   #end def
+
+   #cache varyBy=$getArticleID()
+      $getArticle($getArticleID())
+   #end cache
+
+The code above will generate a CacheRegion and add new cacheItem for each value
+of $getArticleID().
+
+Meta-Data
+================================================================================
+Author: Tavis Rudd <tavis@damnsimple.com> and Philippe Normand <phil@base-art.net> 
+Version: $Revision: 1.3 $
+Start Date: 2005/06/20
+Last Revision Date: $Date: 2006/01/28 04:19:30 $
+"""
+__author__ = "Tavis Rudd <tavis@damnsimple.com> and Philippe Normand <phil@base-art.net>"
+__revision__ = "$Revision: 1.3 $"[11:-2]
+
+import md5
+from time import time as currentTime
+from Cheetah.CacheStore import MemoryCacheStore
+
+class CacheItem:
+    """A CacheItem is a container storing:
+
+        - cacheID (string)
+        - refreshTime (timestamp or None) : last time the cache was refreshed
+        - data (string) : the content of the cache
+    """
+    
+    def __init__(self, cacheItemID, cacheStore):
+        self._cacheItemID = cacheItemID
+        self._cacheStore = cacheStore
+        self._refreshTime = None
+        self._expiryTime = 0
+
+    def hasExpired(self):
+        return (self._expiryTime and currentTime() > self._expiryTime)
+    
+    def setExpiryTime(self, time):
+        self._expiryTime = time
+
+    def getExpiryTime(self):
+        return self._expiryTime
+
+    def setData(self, data):
+        self._refreshTime = currentTime()
+        self._cacheStore.set(self._cacheItemID, data, self._expiryTime)
+
+    def getRefreshTime(self):
+        return self._refreshTime
+
+    def getData(self):
+        assert self._refreshTime
+        return self._cacheStore.get(self._cacheItemID)
+
+    def renderOutput(self):
+        """Can be overridden to implement edge-caching"""
+        return self.getData() or ""
+
+    def clear(self):
+        self._cacheStore.delete(self._cacheItemID)
+        self._refreshTime = None
+
+class _CacheDataStoreWrapper:
+    def __init__(self, dataStore, keyPrefix):
+        self._dataStore = dataStore
+        self._keyPrefix = keyPrefix
+        
+    def get(self, key):
+        return self._dataStore.get(self._keyPrefix+key)
+
+    def delete(self, key):
+        self._dataStore.delete(self._keyPrefix+key)
+
+    def set(self, key, val, time=0):        
+        self._dataStore.set(self._keyPrefix+key, val, time=time)
+
+class CacheRegion:
+    """ A `CacheRegion` stores some `CacheItem` instances.
+
+    This implementation stores the data in the memory of the current process.
+    If you need a more advanced data store, create a cacheStore class that works
+    with Cheetah's CacheStore protocol and provide it as the cacheStore argument
+    to __init__.  For example you could use
+    Cheetah.CacheStore.MemcachedCacheStore, a wrapper around the Python
+    memcached API (http://www.danga.com/memcached).
+    """
+    _cacheItemClass = CacheItem
+    
+    def __init__(self, regionID, templateCacheIdPrefix='', cacheStore=None):
+        self._isNew = True
+        self._regionID = regionID
+        self._templateCacheIdPrefix = templateCacheIdPrefix
+        if not cacheStore:
+            cacheStore = MemoryCacheStore()
+        self._cacheStore = cacheStore
+        self._wrappedCacheDataStore = _CacheDataStoreWrapper(
+            cacheStore, keyPrefix=templateCacheIdPrefix+':'+regionID+':')
+        self._cacheItems = {}
+
+    def isNew(self):
+        return self._isNew
+        
+    def clear(self):
+        " drop all the caches stored in this cache region "
+        for cacheItemId in self._cacheItems.keys():
+            cacheItem = self._cacheItems[cacheItemId]
+            cacheItem.clear()
+            del self._cacheItems[cacheItemId]
+        
+    def getCacheItem(self, cacheItemID):
+        """ Lazy access to a cacheItem
+
+            Try to find a cache in the stored caches. If it doesn't
+            exist, it's created.
+            
+            Returns a `CacheItem` instance.
+        """
+        cacheItemID = md5.new(str(cacheItemID)).hexdigest()
+        
+        if not self._cacheItems.has_key(cacheItemID):
+            cacheItem = self._cacheItemClass(
+                cacheItemID=cacheItemID, cacheStore=self._wrappedCacheDataStore)
+            self._cacheItems[cacheItemID] = cacheItem
+            self._isNew = False
+        return self._cacheItems[cacheItemID]

File cheetah-demo/Cheetah/CacheStore.py

View file
+"""Provides several CacheStore backends for Cheetah's caching framework.  The
+methods provided by these classes have the same semantics as those in the
+python-memcached API, except for their return values:
+
+set(key, val, time=0)
+  set the value unconditionally
+add(key, val, time=0)
+  set only if the server doesn't already have this key
+replace(key, val, time=0)
+  set only if the server already have this key
+get(key, val)
+  returns val or raises a KeyError
+delete(key)
+  deletes or raises a KeyError
+
+"""
+from time import time as currentTime
+
+from Cheetah.Utils.memcache import Client as MemcachedClient
+
+class Error(Exception):
+    pass
+
+class AbstractCacheStore(object):
+
+    def set(self, key, val, time=None):
+        raise NotImplementedError
+
+    def add(self, key, val, time=None):
+        raise NotImplementedError
+
+    def replace(self, key, val, time=None):
+        raise NotImplementedError
+
+    def delete(self, key):
+        raise NotImplementedError
+
+    def get(self, key):
+        raise NotImplementedError
+
+class MemoryCacheStore(AbstractCacheStore):
+    def __init__(self):
+        self._data = {}
+
+    def set(self, key, val, time=0):
+        self._data[key] = (val, time)
+
+    def add(self, key, val, time=0):
+        if self._data.has_key(key):
+            raise Error('a value for key %r is already in the cache'%key)
+        self._data[key] = (val, time)
+
+    def replace(self, key, val, time=0):
+        if self._data.has_key(key):
+            raise Error('a value for key %r is already in the cache'%key)
+        self._data[key] = (val, time)
+
+    def delete(self, key):
+        del self._data[key]
+        
+    def get(self, key):
+        (val, exptime) = self._data[key]
+        if exptime and currentTime() > exptime:
+            del self._data[key]
+            raise KeyError(key)
+        else:
+            return val
+
+    def clear(self):
+        self._data.clear()        
+                  
+class MemcachedCacheStore(AbstractCacheStore):
+    servers = ('127.0.0.1:11211')
+    def __init__(self, servers=None, debug=False):
+        if servers is None:
+            servers = self.servers
+            
+        self._client = MemcachedClient(servers, debug)
+
+    def set(self, key, val, time=0):
+        self._client.set(key, val, time)
+
+    def add(self, key, val, time=0):
+        res = self._client.add(key, val, time)        
+        if not res:
+            raise Error('a value for key %r is already in the cache'%key)
+        self._data[key] = (val, time)
+
+    def replace(self, key, val, time=0):
+        res = self._client.replace(key, val, time)        
+        if not res:
+            raise Error('a value for key %r is already in the cache'%key)
+        self._data[key] = (val, time)
+
+    def delete(self, key):
+        res = self._client.delete(key, time=0)        
+        if not res:
+            raise KeyError(key)
+        
+    def get(self, key):
+        val = self._client.get(key)
+        if val is None:
+            raise KeyError(key)
+        else:
+            return val
+
+    def clear(self):
+        self._client.flush_all()        

File cheetah-demo/Cheetah/Compiler.py

View file
+#!/usr/bin/env python
+# $Id: Compiler.py,v 1.156 2007/10/30 20:17:09 tavis_rudd Exp $
+"""Compiler classes for Cheetah:
+ModuleCompiler aka 'Compiler'
+ClassCompiler
+MethodCompiler
+
+If you are trying to grok this code start with ModuleCompiler.__init__,
+ModuleCompiler.compile, and ModuleCompiler.__getattr__.
+
+Meta-Data
+================================================================================
+Author: Tavis Rudd <tavis@damnsimple.com>
+Version: $Revision: 1.156 $
+Start Date: 2001/09/19
+Last Revision Date: $Date: 2007/10/30 20:17:09 $
+"""
+__author__ = "Tavis Rudd <tavis@damnsimple.com>"
+__revision__ = "$Revision: 1.156 $"[11:-2]
+
+import sys
+import os
+import os.path
+from os.path import getmtime, exists
+import re
+import types
+import time
+import random
+import warnings
+import __builtin__
+import copy
+
+from Cheetah.Version import Version, VersionTuple
+from Cheetah.SettingsManager import SettingsManager
+from Cheetah.Utils.Indenter import indentize # an undocumented preprocessor
+from Cheetah import ErrorCatchers
+from Cheetah import NameMapper
+from Cheetah.Parser import Parser, ParseError, specialVarRE, \
+     STATIC_CACHE, REFRESH_CACHE, SET_LOCAL, SET_GLOBAL,SET_MODULE, \
+     unicodeDirectiveRE, encodingDirectiveRE,escapedNewlineRE
+
+from Cheetah.NameMapper import NotFound, valueForName, valueFromSearchList, valueFromFrameOrSearchList
+VFFSL=valueFromFrameOrSearchList
+VFSL=valueFromSearchList
+VFN=valueForName
+currentTime=time.time
+
+class Error(Exception): pass
+
+DEFAULT_COMPILER_SETTINGS = {
+    ## controlling the handling of Cheetah $placeholders
+    'useNameMapper': True,      # Unified dotted notation and the searchList
+    'useSearchList': True,      # if false, assume the first
+                                # portion of the $variable (before the first dot) is a global,
+                                # builtin, or local var that doesn't need
+                                # looking up in the searchlist BUT use
+                                # namemapper on the rest of the lookup
+    'allowSearchListAsMethArg': True,
+    'useAutocalling': True, # detect and call callable()'s, requires NameMapper
+    'useStackFrames': True, # use NameMapper.valueFromFrameOrSearchList
+    # rather than NameMapper.valueFromSearchList
+    'useErrorCatcher':False,
+    'alwaysFilterNone':True, # filter out None, before the filter is called
+    'useFilters':True, # use str instead if =False
+    'includeRawExprInFilterArgs':True,
+    
+    #'lookForTransactionAttr':False,
+    'autoAssignDummyTransactionToSelf':False,
+    'useKWsDictArgForPassingTrans':True,
+    
+    ## controlling the aesthetic appearance / behaviour of generated code    
+    'commentOffset': 1,
+    'outputRowColComments':True,
+    # should #block's be wrapped in a comment in the template's output
+    'includeBlockMarkers': False,   
+    'blockMarkerStart':('\n<!-- START BLOCK: ',' -->\n'),
+    'blockMarkerEnd':('\n<!-- END BLOCK: ',' -->\n'),           
+    'defDocStrMsg':'Autogenerated by CHEETAH: The Python-Powered Template Engine',
+    'setup__str__method': False, 
+    'mainMethodName':'respond',
+    'mainMethodNameForSubclasses':'writeBody',
+    'indentationStep': ' '*4,
+    'initialMethIndentLevel': 2,
+    'monitorSrcFile':False,
+    'outputMethodsBeforeAttributes': True,
+    'addTimestampsToCompilerOutput': True,
+
+    ## customizing the #extends directive
+    'autoImportForExtendsDirective':True,
+    'handlerForExtendsDirective':None, # baseClassName = handler(compiler, baseClassName)
+                                      # a callback hook for customizing the
+                                      # #extends directive.  It can manipulate
+                                      # the compiler's state if needed.
+    # also see allowExpressionsInExtendsDirective
+    
+
+    # input filtering/restriction
+    # use lower case keys here!!
+    'disabledDirectives':[], # list of directive keys, without the start token
+    'enabledDirectives':[], # list of directive keys, without the start token
+
+    'disabledDirectiveHooks':[], # callable(parser, directiveKey)
+    'preparseDirectiveHooks':[], # callable(parser, directiveKey)
+    'postparseDirectiveHooks':[], # callable(parser, directiveKey)
+    'preparsePlaceholderHooks':[], # callable(parser)
+    'postparsePlaceholderHooks':[], # callable(parser)
+    # the above hooks don't need to return anything
+
+    'expressionFilterHooks':[], # callable(parser, expr, exprType, rawExpr=None, startPos=None)
+    # exprType is the name of the directive, 'psp', or 'placeholder'. all
+    # lowercase.  The filters *must* return the expr or raise an exception.
+    # They can modify the expr if needed.
+
+    'templateMetaclass':None, # strictly optional. Only works with new-style baseclasses
+
+
+    'i18NFunctionName':'self.i18n',
+    
+    ## These are used in the parser, but I've put them here for the time being to
+    ## facilitate separating the parser and compiler:    
+    'cheetahVarStartToken':'$',
+    'commentStartToken':'##',
+    'multiLineCommentStartToken':'#*',
+    'multiLineCommentEndToken':'*#',
+    'gobbleWhitespaceAroundMultiLineComments':True,
+    'directiveStartToken':'#',
+    'directiveEndToken':'#',
+    'allowWhitespaceAfterDirectiveStartToken':False,    
+    'PSPStartToken':'<%',
+    'PSPEndToken':'%>',
+    'EOLSlurpToken':'#',
+    'gettextTokens': ["_", "N_", "ngettext"],
+    'allowExpressionsInExtendsDirective': False, # the default restricts it to
+                                        # accepting dotted names    
+    'allowEmptySingleLineMethods': False,
+    'allowNestedDefScopes': True,
+    'allowPlaceholderFilterArgs': True,
+
+    ## See Parser.initDirectives() for the use of the next 3
+    #'directiveNamesAndParsers':{}
+    #'endDirectiveNamesAndHandlers':{}
+    #'macroDirectives':{}
+
+    }
+
+
+
+class GenUtils:
+    """An abstract baseclass for the Compiler classes that provides methods that
+    perform generic utility functions or generate pieces of output code from
+    information passed in by the Parser baseclass.  These methods don't do any
+    parsing themselves.
+    """
+
+    def genTimeInterval(self, timeString):
+        ##@@ TR: need to add some error handling here
+        if timeString[-1] == 's':
+            interval = float(timeString[:-1])
+        elif timeString[-1] == 'm':
+            interval = float(timeString[:-1])*60
+        elif timeString[-1] == 'h':
+            interval = float(timeString[:-1])*60*60
+        elif timeString[-1] == 'd':
+            interval = float(timeString[:-1])*60*60*24
+        elif timeString[-1] == 'w':
+            interval = float(timeString[:-1])*60*60*24*7
+        else:                       # default to minutes
+            interval = float(timeString)*60
+        return interval
+
+    def genCacheInfo(self, cacheTokenParts):
+        """Decipher a placeholder cachetoken
+        """
+        cacheInfo = {}
+        if cacheTokenParts['REFRESH_CACHE']:
+            cacheInfo['type'] = REFRESH_CACHE
+            cacheInfo['interval'] = self.genTimeInterval(cacheTokenParts['interval'])
+        elif cacheTokenParts['STATIC_CACHE']:
+            cacheInfo['type'] = STATIC_CACHE
+        return cacheInfo                # is empty if no cache
+
+    def genCacheInfoFromArgList(self, argList):
+        cacheInfo = {'type':REFRESH_CACHE}
+        for key, val in argList:
+            if val[0] in '"\'':
+                val = val[1:-1]
+
+            if key == 'timer':
+                key = 'interval'
+                val = self.genTimeInterval(val)
+                
+            cacheInfo[key] = val
+        return cacheInfo
+        
+    def genCheetahVar(self, nameChunks, plain=False):
+        if nameChunks[0][0] in self.setting('gettextTokens'):
+            self.addGetTextVar(nameChunks) 
+        if self.setting('useNameMapper') and not plain:
+            return self.genNameMapperVar(nameChunks)
+        else:
+            return self.genPlainVar(nameChunks)
+
+    def addGetTextVar(self, nameChunks):
+        """Output something that gettext can recognize.
+        
+        This is a harmless side effect necessary to make gettext work when it
+        is scanning compiled templates for strings marked for translation.
+
+        @@TR: another marginally more efficient approach would be to put the
+        output in a dummy method that is never called.
+        """
+        # @@TR: this should be in the compiler not here
+        self.addChunk("if False:")
+        self.indent()
+        self.addChunk(self.genPlainVar(nameChunks[:]))
+        self.dedent()
+
+    def genPlainVar(self, nameChunks):        
+        """Generate Python code for a Cheetah $var without using NameMapper
+        (Unified Dotted Notation with the SearchList).
+        """
+        nameChunks.reverse()
+        chunk = nameChunks.pop()
+        pythonCode = chunk[0] + chunk[2]
+        while nameChunks:
+            chunk = nameChunks.pop()
+            pythonCode = (pythonCode + '.' + chunk[0] + chunk[2])
+        return pythonCode
+
+    def genNameMapperVar(self, nameChunks):
+        """Generate valid Python code for a Cheetah $var, using NameMapper
+        (Unified Dotted Notation with the SearchList).
+
+        nameChunks = list of var subcomponents represented as tuples
+          [ (name,useAC,remainderOfExpr),
+          ]
+        where:
+          name = the dotted name base
+          useAC = where NameMapper should use autocalling on namemapperPart
+          remainderOfExpr = any arglist, index, or slice
+
+        If remainderOfExpr contains a call arglist (e.g. '(1234)') then useAC
+        is False, otherwise it defaults to True. It is overridden by the global
+        setting 'useAutocalling' if this setting is False.
+
+        EXAMPLE
+        ------------------------------------------------------------------------
+        if the raw Cheetah Var is
+          $a.b.c[1].d().x.y.z
+          
+        nameChunks is the list
+          [ ('a.b.c',True,'[1]'), # A
+            ('d',False,'()'),     # B
+            ('x.y.z',True,''),    # C
+          ]
+        
+        When this method is fed the list above it returns
+          VFN(VFN(VFFSL(SL, 'a.b.c',True)[1], 'd',False)(), 'x.y.z',True)
+        which can be represented as
+          VFN(B`, name=C[0], executeCallables=(useAC and C[1]))C[2]
+        where:
+          VFN = NameMapper.valueForName
+          VFFSL = NameMapper.valueFromFrameOrSearchList
+          VFSL = NameMapper.valueFromSearchList # optionally used instead of VFFSL
+          SL = self.searchList()
+          useAC = self.setting('useAutocalling') # True in this example
+          
+          A = ('a.b.c',True,'[1]')
+          B = ('d',False,'()')
+          C = ('x.y.z',True,'')
+
+          C` = VFN( VFN( VFFSL(SL, 'a.b.c',True)[1],
+                         'd',False)(),
+                    'x.y.z',True)
+             = VFN(B`, name='x.y.z', executeCallables=True)
+             
+          B` = VFN(A`, name=B[0], executeCallables=(useAC and B[1]))B[2]
+          A` = VFFSL(SL, name=A[0], executeCallables=(useAC and A[1]))A[2]
+
+
+        Note, if the compiler setting useStackFrames=False (default is true)
+        then
+          A` = VFSL([locals()]+SL+[globals(), __builtin__], name=A[0], executeCallables=(useAC and A[1]))A[2]
+        This option allows Cheetah to be used with Psyco, which doesn't support
+        stack frame introspection.
+        """
+        defaultUseAC = self.setting('useAutocalling')
+        useSearchList = self.setting('useSearchList')
+
+        nameChunks.reverse()
+        name, useAC, remainder = nameChunks.pop()
+        
+        if not useSearchList:
+            firstDotIdx = name.find('.')
+            if firstDotIdx != -1 and firstDotIdx < len(name):
+                beforeFirstDot, afterDot = name[:firstDotIdx], name[firstDotIdx+1:]
+                pythonCode = ('VFN(' + beforeFirstDot +
+                              ',"' + afterDot +
+                              '",' + repr(defaultUseAC and useAC) + ')'
+                              + remainder)
+            else:
+                pythonCode = name+remainder
+        elif self.setting('useStackFrames'):
+            pythonCode = ('VFFSL(SL,'
+                          '"'+ name + '",'
+                          + repr(defaultUseAC and useAC) + ')'
+                          + remainder)
+        else:
+            pythonCode = ('VFSL([locals()]+SL+[globals(), __builtin__],'
+                          '"'+ name + '",'
+                          + repr(defaultUseAC and useAC) + ')'
+                          + remainder)
+        ##    
+        while nameChunks:
+            name, useAC, remainder = nameChunks.pop()
+            pythonCode = ('VFN(' + pythonCode +
+                          ',"' + name +
+                          '",' + repr(defaultUseAC and useAC) + ')'
+                          + remainder)
+        return pythonCode
+    
+##################################################
+## METHOD COMPILERS
+
+class MethodCompiler(GenUtils):
+    def __init__(self, methodName, classCompiler,
+                 initialMethodComment=None,
+                 decorator=None):
+        self._settingsManager = classCompiler
+        self._classCompiler = classCompiler
+        self._moduleCompiler = classCompiler._moduleCompiler
+        self._methodName = methodName
+        self._initialMethodComment = initialMethodComment
+        self._setupState()
+        self._decorator = decorator
+
+    def setting(self, key):
+        return self._settingsManager.setting(key)
+
+    def _setupState(self):
+        self._indent = self.setting('indentationStep')
+        self._indentLev = self.setting('initialMethIndentLevel')
+        self._pendingStrConstChunks = []
+        self._methodSignature = None
+        self._methodDef = None
+        self._docStringLines = []
+        self._methodBodyChunks = []
+
+        self._cacheRegionsStack = []
+        self._callRegionsStack = []
+        self._captureRegionsStack = []
+        self._filterRegionsStack = []
+
+        self._isErrorCatcherOn = False
+
+        self._hasReturnStatement = False
+        self._isGenerator = False
+        
+        
+    def cleanupState(self):
+        """Called by the containing class compiler instance
+        """
+        pass
+
+    def methodName(self):
+        return self._methodName
+
+    def setMethodName(self, name):
+        self._methodName = name
+        
+    ## methods for managing indentation
+    
+    def indentation(self):
+        return self._indent * self._indentLev
+    
+    def indent(self):
+        self._indentLev +=1
+        
+    def dedent(self):
+        if self._indentLev:
+            self._indentLev -=1
+        else:
+            raise Error('Attempt to dedent when the indentLev is 0')
+
+    ## methods for final code wrapping
+
+    def methodDef(self):
+        if self._methodDef:
+            return self._methodDef
+        else:
+            return self.wrapCode()
+
+    __str__ = methodDef
+    __unicode__ = methodDef
+    
+    def wrapCode(self):
+        self.commitStrConst()
+        methodDefChunks = (
+            self.methodSignature(),
+            '\n',
+            self.docString(),
+            self.methodBody() )
+        methodDef = ''.join(methodDefChunks)
+        self._methodDef = methodDef
+        return methodDef
+
+    def methodSignature(self):
+        return self._indent + self._methodSignature + ':'
+
+    def setMethodSignature(self, signature):
+        self._methodSignature = signature
+
+    def methodBody(self):
+        return ''.join( self._methodBodyChunks )
+
+    def docString(self):
+        if not self._docStringLines:
+            return ''
+        
+        ind = self._indent*2        
+        docStr = (ind + '"""\n' + ind +
+                  ('\n' + ind).join([ln.replace('"""',"'''") for ln in self._docStringLines]) +
+                  '\n' + ind + '"""\n')
+        return  docStr
+
+    ## methods for adding code
+    def addMethDocString(self, line):
+        self._docStringLines.append(line.replace('%','%%'))
+       
+    def addChunk(self, chunk):
+        self.commitStrConst()
+        chunk = "\n" + self.indentation() + chunk
+        self._methodBodyChunks.append(chunk)
+
+    def appendToPrevChunk(self, appendage):
+        self._methodBodyChunks[-1] = self._methodBodyChunks[-1] + appendage
+
+    def addWriteChunk(self, chunk):
+        self.addChunk('write(' + chunk + ')')
+
+    def addFilteredChunk(self, chunk, filterArgs=None, rawExpr=None, lineCol=None):
+        if filterArgs is None:
+            filterArgs = ''
+        if self.setting('includeRawExprInFilterArgs') and rawExpr:
+            filterArgs += ', rawExpr=%s'%repr(rawExpr)
+
+        if self.setting('alwaysFilterNone'):
+            if rawExpr and rawExpr.find('\n')==-1 and rawExpr.find('\r')==-1:
+                self.addChunk("_v = %s # %r"%(chunk, rawExpr))
+                if lineCol:
+                    self.appendToPrevChunk(' on line %s, col %s'%lineCol)
+            else:
+                self.addChunk("_v = %s"%chunk)
+                
+            if self.setting('useFilters'):
+                self.addChunk("if _v is not None: write(_filter(_v%s))"%filterArgs)
+            else:
+                self.addChunk("if _v is not None: write(str(_v))")
+        else:
+            if self.setting('useFilters'):
+                self.addChunk("write(_filter(%s%s))"%(chunk,filterArgs))
+            else:
+                self.addChunk("write(str(%s))"%chunk)
+
+    def _appendToPrevStrConst(self, strConst):
+        if self._pendingStrConstChunks:
+            self._pendingStrConstChunks.append(strConst)
+        else:
+            self._pendingStrConstChunks = [strConst]
+
+    def _unescapeCheetahVars(self, theString):
+        """Unescape any escaped Cheetah \$vars in the string.
+        """
+        
+        token = self.setting('cheetahVarStartToken')
+        return theString.replace('\\' + token, token)
+
+    def _unescapeDirectives(self, theString):
+        """Unescape any escaped Cheetah \$vars in the string.
+        """
+        
+        token = self.setting('directiveStartToken')
+        return theString.replace('\\' + token, token)
+        
+    def commitStrConst(self):
+        """Add the code for outputting the pending strConst without chopping off
+        any whitespace from it.
+        """
+        if self._pendingStrConstChunks:
+            strConst = self._unescapeCheetahVars(''.join(self._pendingStrConstChunks))
+            strConst = self._unescapeDirectives(strConst)
+            self._pendingStrConstChunks = []
+            if not strConst:
+                return
+            else:
+                reprstr = repr(strConst).replace('\\012','\n')
+                i = 0
+                out = []
+                if reprstr.startswith('u'):
+                    i = 1
+                    out = ['u']
+                body = escapedNewlineRE.sub('\n', reprstr[i+1:-1])
+                
+                if reprstr[i]=="'":
+                    out.append("'''")
+                    out.append(body)
+                    out.append("'''")
+                else:
+                    out.append('"""')
+                    out.append(body)
+                    out.append('"""')
+                self.addWriteChunk(''.join(out))
+
+    def handleWSBeforeDirective(self):
+        """Truncate the pending strCont to the beginning of the current line.
+        """
+        if self._pendingStrConstChunks:
+            src = self._pendingStrConstChunks[-1]
+            BOL = max(src.rfind('\n')+1, src.rfind('\r')+1, 0)
+            if BOL < len(src):
+                self._pendingStrConstChunks[-1] = src[:BOL]
+
+
+
+    def isErrorCatcherOn(self):
+        return self._isErrorCatcherOn
+    
+    def turnErrorCatcherOn(self):
+        self._isErrorCatcherOn = True
+
+    def turnErrorCatcherOff(self):
+        self._isErrorCatcherOn = False
+            
+    # @@TR: consider merging the next two methods into one
+    def addStrConst(self, strConst):
+        self._appendToPrevStrConst(strConst)
+
+    def addRawText(self, text):
+        self.addStrConst(text)
+        
+    def addMethComment(self, comm):
+        offSet = self.setting('commentOffset')
+        self.addChunk('#' + ' '*offSet + comm)
+
+    def addPlaceholder(self, expr, filterArgs, rawPlaceholder,
+                       cacheTokenParts, lineCol,
+                       silentMode=False):
+        cacheInfo = self.genCacheInfo(cacheTokenParts)
+        if cacheInfo:
+            cacheInfo['ID'] = repr(rawPlaceholder)[1:-1]
+            self.startCacheRegion(cacheInfo, lineCol, rawPlaceholder=rawPlaceholder)
+
+        if self.isErrorCatcherOn():
+            methodName = self._classCompiler.addErrorCatcherCall(
+                expr, rawCode=rawPlaceholder, lineCol=lineCol)
+            expr = 'self.' + methodName + '(localsDict=locals())' 
+
+        if silentMode:
+            self.addChunk('try:')
+            self.indent()            
+            self.addFilteredChunk(expr, filterArgs, rawPlaceholder, lineCol=lineCol)
+            self.dedent()
+            self.addChunk('except NotFound: pass')            
+        else:
+            self.addFilteredChunk(expr, filterArgs, rawPlaceholder, lineCol=lineCol)
+
+        if self.setting('outputRowColComments'):
+            self.appendToPrevChunk(' # from line %s, col %s' % lineCol + '.')
+        if cacheInfo:
+            self.endCacheRegion()
+
+    def addSilent(self, expr):
+        self.addChunk( expr )
+
+    def addEcho(self, expr, rawExpr=None):
+        self.addFilteredChunk(expr, rawExpr=rawExpr)
+        
+    def addSet(self, expr, exprComponents, setStyle):
+        if setStyle is SET_GLOBAL:
+            (LVALUE, OP, RVALUE) = (exprComponents.LVALUE,
+                                    exprComponents.OP,
+                                    exprComponents.RVALUE)
+            # we need to split the LVALUE to deal with globalSetVars
+            splitPos1 = LVALUE.find('.')
+            splitPos2 = LVALUE.find('[')
+            if splitPos1 > 0 and splitPos2==-1:
+                splitPos = splitPos1
+            elif splitPos1 > 0 and splitPos1 < max(splitPos2,0):
+                splitPos = splitPos1
+            else:
+                splitPos = splitPos2
+
+            if splitPos >0:
+                primary = LVALUE[:splitPos]
+                secondary = LVALUE[splitPos:]
+            else:
+                primary = LVALUE
+                secondary = ''            
+            LVALUE = 'self._CHEETAH__globalSetVars["' + primary + '"]' + secondary
+            expr = LVALUE + ' ' + OP + ' ' + RVALUE.strip()
+
+        if setStyle is SET_MODULE:
+            self._moduleCompiler.addModuleGlobal(expr)
+        else:
+            self.addChunk(expr)
+
+    def addInclude(self, sourceExpr, includeFrom, isRaw):
+        self.addChunk('self._handleCheetahInclude(' + sourceExpr +
+                           ', trans=trans, ' +
+                           'includeFrom="' + includeFrom + '", raw=' +
+                           repr(isRaw) + ')')
+
+    def addWhile(self, expr, lineCol=None):
+        self.addIndentingDirective(expr, lineCol=lineCol)
+        
+    def addFor(self, expr, lineCol=None):
+        self.addIndentingDirective(expr, lineCol=lineCol)
+
+    def addRepeat(self, expr, lineCol=None):
+        #the _repeatCount stuff here allows nesting of #repeat directives        
+        self._repeatCount = getattr(self, "_repeatCount", -1) + 1
+        self.addFor('for __i%s in range(%s)' % (self._repeatCount,expr), lineCol=lineCol)
+
+    def addIndentingDirective(self, expr, lineCol=None):
+        if expr and not expr[-1] == ':':
+            expr = expr  + ':'
+        self.addChunk( expr )
+        if lineCol:
+            self.appendToPrevChunk(' # generated from line %s, col %s'%lineCol )
+        self.indent()
+
+    def addReIndentingDirective(self, expr, dedent=True, lineCol=None):
+        self.commitStrConst()
+        if dedent:
+            self.dedent()
+        if not expr[-1] == ':':
+            expr = expr  + ':'
+            
+        self.addChunk( expr )
+        if lineCol:
+            self.appendToPrevChunk(' # generated from line %s, col %s'%lineCol )
+        self.indent()
+
+    def addIf(self, expr, lineCol=None):
+        """For a full #if ... #end if directive
+        """
+        self.addIndentingDirective(expr, lineCol=lineCol)
+
+    def addOneLineIf(self, expr, lineCol=None):
+        """For a full #if ... #end if directive
+        """
+        self.addIndentingDirective(expr, lineCol=lineCol)
+
+    def addTernaryExpr(self, conditionExpr, trueExpr, falseExpr, lineCol=None):
+        """For a single-lie #if ... then .... else ... directive
+        <condition> then <trueExpr> else <falseExpr>
+        """
+        self.addIndentingDirective(conditionExpr, lineCol=lineCol)            
+        self.addFilteredChunk(trueExpr)
+        self.dedent()
+        self.addIndentingDirective('else')            
+        self.addFilteredChunk(falseExpr)
+        self.dedent()
+
+    def addElse(self, expr, dedent=True, lineCol=None):
+        expr = re.sub(r'else[ \f\t]+if','elif', expr)
+        self.addReIndentingDirective(expr, dedent=dedent, lineCol=lineCol)
+
+    def addElif(self, expr, dedent=True, lineCol=None):
+        self.addElse(expr, dedent=dedent, lineCol=lineCol)
+        
+    def addUnless(self, expr, lineCol=None):
+        self.addIf('if not (' + expr + ')')
+
+    def addClosure(self, functionName, argsList, parserComment):
+        argStringChunks = []
+        for arg in argsList:
+            chunk = arg[0]
+            if not arg[1] == None:
+                chunk += '=' + arg[1]
+            argStringChunks.append(chunk)
+        signature = "def " + functionName + "(" + ','.join(argStringChunks) + "):"
+        self.addIndentingDirective(signature)
+        self.addChunk('#'+parserComment)
+
+    def addTry(self, expr, lineCol=None):
+        self.addIndentingDirective(expr, lineCol=lineCol)
+        
+    def addExcept(self, expr, dedent=True, lineCol=None):
+        self.addReIndentingDirective(expr, dedent=dedent, lineCol=lineCol)
+        
+    def addFinally(self, expr, dedent=True, lineCol=None):
+        self.addReIndentingDirective(expr, dedent=dedent, lineCol=lineCol)
+            
+    def addReturn(self, expr):
+        assert not self._isGenerator
+        self.addChunk(expr)
+        self._hasReturnStatement = True
+
+    def addYield(self, expr):
+        assert not self._hasReturnStatement
+        self._isGenerator = True
+        if expr.replace('yield','').strip():
+            self.addChunk(expr)
+        else:
+            self.addChunk('if _dummyTrans:')
+            self.indent()
+            self.addChunk('yield trans.response().getvalue()')
+            self.addChunk('trans = DummyTransaction()')
+            self.addChunk('write = trans.response().write')
+            self.dedent()
+            self.addChunk('else:')
+            self.indent()
+            self.addChunk(
+                'raise TypeError("This method cannot be called with a trans arg")')
+            self.dedent()
+            
+
+    def addPass(self, expr):
+        self.addChunk(expr)
+
+    def addDel(self, expr):
+        self.addChunk(expr)
+
+    def addAssert(self, expr):
+        self.addChunk(expr)
+
+    def addRaise(self, expr):
+        self.addChunk(expr)
+
+    def addBreak(self, expr):
+        self.addChunk(expr)
+
+    def addContinue(self, expr):
+        self.addChunk(expr)
+
+    def addPSP(self, PSP):
+        self.commitStrConst()
+        autoIndent = False
+        if PSP[0] == '=':
+            PSP = PSP[1:]
+            if PSP:
+                self.addWriteChunk('_filter(' + PSP + ')')
+            return
+                    
+        elif PSP.lower() == 'end':
+            self.dedent()
+            return
+        elif PSP[-1] == '$':
+            autoIndent = True
+            PSP = PSP[:-1]
+        elif PSP[-1] == ':':
+            autoIndent = True
+            
+        for line in PSP.splitlines():
+            self.addChunk(line)
+            
+        if autoIndent:
+            self.indent()
+    
+    def nextCacheID(self):
+        return ('_'+str(random.randrange(100, 999)) 
+                + str(random.randrange(10000, 99999)))
+        
+    def startCacheRegion(self, cacheInfo, lineCol, rawPlaceholder=None):
+
+        # @@TR: we should add some runtime logging to this
+        
+        ID = self.nextCacheID()
+        interval = cacheInfo.get('interval',None)
+        test = cacheInfo.get('test',None)
+        customID = cacheInfo.get('id',None)
+        if customID:
+            ID = customID
+        varyBy = cacheInfo.get('varyBy', repr(ID))
+        self._cacheRegionsStack.append(ID) # attrib of current methodCompiler
+
+        # @@TR: add this to a special class var as well
+        self.addChunk('')
+
+        self.addChunk('## START CACHE REGION: ID='+ID+
+                      '. line %s, col %s'%lineCol + ' in the source.')
+        
+        self.addChunk('_RECACHE_%(ID)s = False'%locals())
+        self.addChunk('_cacheRegion_%(ID)s = self.getCacheRegion(regionID='%locals()
+                      + repr(ID)
+                      + ', cacheInfo=%r'%cacheInfo
+                      + ')')
+        self.addChunk('if _cacheRegion_%(ID)s.isNew():'%locals())
+        self.indent()
+        self.addChunk('_RECACHE_%(ID)s = True'%locals())
+        self.dedent()
+        
+        self.addChunk('_cacheItem_%(ID)s = _cacheRegion_%(ID)s.getCacheItem('%locals()
+                      +varyBy+')')
+
+        self.addChunk('if _cacheItem_%(ID)s.hasExpired():'%locals())
+        self.indent()
+        self.addChunk('_RECACHE_%(ID)s = True'%locals())
+        self.dedent()
+            
+        if test:
+            self.addChunk('if ' + test + ':')
+            self.indent()
+            self.addChunk('_RECACHE_%(ID)s = True'%locals())
+            self.dedent()
+
+        self.addChunk('if (not _RECACHE_%(ID)s) and _cacheItem_%(ID)s.getRefreshTime():'%locals())
+        self.indent()
+        #self.addChunk('print "DEBUG"+"-"*50')
+        self.addChunk('try:')
+        self.indent()
+        self.addChunk('_output = _cacheItem_%(ID)s.renderOutput()'%locals())        
+        self.dedent()                
+        self.addChunk('except KeyError:')
+        self.indent()
+        self.addChunk('_RECACHE_%(ID)s = True'%locals())
+        #self.addChunk('print "DEBUG"+"*"*50')
+        self.dedent()                
+        self.addChunk('else:')
+        self.indent()
+        self.addWriteChunk('_output')
+        self.addChunk('del _output')
+        self.dedent()                
+
+        self.dedent()                
+
+        self.addChunk('if _RECACHE_%(ID)s or not _cacheItem_%(ID)s.getRefreshTime():'%locals())
+        self.indent()
+        self.addChunk('_orig_trans%(ID)s = trans'%locals())
+        self.addChunk('trans = _cacheCollector_%(ID)s = DummyTransaction()'%locals())
+        self.addChunk('write = _cacheCollector_%(ID)s.response().write'%locals())
+        if interval:
+            self.addChunk(("_cacheItem_%(ID)s.setExpiryTime(currentTime() +"%locals())
+                          + str(interval) + ")")
+        
+    def endCacheRegion(self):
+        ID = self._cacheRegionsStack.pop()
+        self.addChunk('trans = _orig_trans%(ID)s'%locals())
+        self.addChunk('write = trans.response().write')
+        self.addChunk('_cacheData = _cacheCollector_%(ID)s.response().getvalue()'%locals())
+        self.addChunk('_cacheItem_%(ID)s.setData(_cacheData)'%locals())
+        self.addWriteChunk('_cacheData')
+        self.addChunk('del _cacheData')        
+        self.addChunk('del _cacheCollector_%(ID)s'%locals())
+        self.addChunk('del _orig_trans%(ID)s'%locals())
+        self.dedent()
+        self.addChunk('## END CACHE REGION: '+ID)
+        self.addChunk('')
+
+    def nextCallRegionID(self):
+        return self.nextCacheID()
+
+    def startCallRegion(self, functionName, args, lineCol, regionTitle='CALL'):
+        class CallDetails: pass
+        callDetails = CallDetails()
+        callDetails.ID = ID = self.nextCallRegionID()
+        callDetails.functionName = functionName
+        callDetails.args = args
+        callDetails.lineCol = lineCol
+        callDetails.usesKeywordArgs = False
+        self._callRegionsStack.append((ID, callDetails)) # attrib of current methodCompiler
+
+        self.addChunk('## START %(regionTitle)s REGION: '%locals()
+                      +ID
+                      +' of '+functionName
+                      +' at line %s, col %s'%lineCol + ' in the source.')
+        self.addChunk('_orig_trans%(ID)s = trans'%locals())
+        self.addChunk('_wasBuffering%(ID)s = self._CHEETAH__isBuffering'%locals())
+        self.addChunk('self._CHEETAH__isBuffering = True')
+        self.addChunk('trans = _callCollector%(ID)s = DummyTransaction()'%locals())
+        self.addChunk('write = _callCollector%(ID)s.response().write'%locals())
+
+    def setCallArg(self, argName, lineCol):
+        ID, callDetails = self._callRegionsStack[-1]
+        if callDetails.usesKeywordArgs:
+            self._endCallArg()
+        else:
+            callDetails.usesKeywordArgs = True
+            self.addChunk('_callKws%(ID)s = {}'%locals())
+            self.addChunk('_currentCallArgname%(ID)s = %(argName)r'%locals())
+        callDetails.currentArgname = argName
+        
+    def _endCallArg(self):
+        ID, callDetails = self._callRegionsStack[-1]
+        currCallArg = callDetails.currentArgname
+        self.addChunk(('_callKws%(ID)s[%(currCallArg)r] ='
+                       ' _callCollector%(ID)s.response().getvalue()')%locals())
+        self.addChunk('del _callCollector%(ID)s'%locals())
+        self.addChunk('trans = _callCollector%(ID)s = DummyTransaction()'%locals())
+        self.addChunk('write = _callCollector%(ID)s.response().write'%locals())
+    
+    def endCallRegion(self, regionTitle='CALL'):
+        ID, callDetails = self._callRegionsStack[-1]
+        functionName, initialKwArgs, lineCol = (
+            callDetails.functionName, callDetails.args, callDetails.lineCol)
+
+        def reset(ID=ID):
+            self.addChunk('trans = _orig_trans%(ID)s'%locals())
+            self.addChunk('write = trans.response().write')
+            self.addChunk('self._CHEETAH__isBuffering = _wasBuffering%(ID)s '%locals())
+            self.addChunk('del _wasBuffering%(ID)s'%locals())
+            self.addChunk('del _orig_trans%(ID)s'%locals())
+
+        if not callDetails.usesKeywordArgs:
+            reset()
+            self.addChunk('_callArgVal%(ID)s = _callCollector%(ID)s.response().getvalue()'%locals())
+            self.addChunk('del _callCollector%(ID)s'%locals())
+            if initialKwArgs:
+                initialKwArgs = ', '+initialKwArgs           
+            self.addFilteredChunk('%(functionName)s(_callArgVal%(ID)s%(initialKwArgs)s)'%locals())
+            self.addChunk('del _callArgVal%(ID)s'%locals())
+        else:
+            if initialKwArgs:
+                initialKwArgs = initialKwArgs+', '
+            self._endCallArg()
+            reset()
+            self.addFilteredChunk('%(functionName)s(%(initialKwArgs)s**_callKws%(ID)s)'%locals())
+            self.addChunk('del _callKws%(ID)s'%locals())
+        self.addChunk('## END %(regionTitle)s REGION: '%locals()
+                      +ID
+                      +' of '+functionName
+                      +' at line %s, col %s'%lineCol + ' in the source.')        
+        self.addChunk('')
+        self._callRegionsStack.pop() # attrib of current methodCompiler
+
+    def nextCaptureRegionID(self):
+        return self.nextCacheID()
+
+    def startCaptureRegion(self, assignTo, lineCol):
+        class CaptureDetails: pass
+        captureDetails = CaptureDetails()
+        captureDetails.ID = ID = self.nextCaptureRegionID()
+        captureDetails.assignTo = assignTo
+        captureDetails.lineCol = lineCol
+        
+        self._captureRegionsStack.append((ID,captureDetails)) # attrib of current methodCompiler
+        self.addChunk('## START CAPTURE REGION: '+ID
+                      +' '+assignTo
+                      +' at line %s, col %s'%lineCol + ' in the source.')
+        self.addChunk('_orig_trans%(ID)s = trans'%locals())
+        self.addChunk('_wasBuffering%(ID)s = self._CHEETAH__isBuffering'%locals())
+        self.addChunk('self._CHEETAH__isBuffering = True')
+        self.addChunk('trans = _captureCollector%(ID)s = DummyTransaction()'%locals())
+        self.addChunk('write = _captureCollector%(ID)s.response().write'%locals())
+
+    def endCaptureRegion(self):
+        ID, captureDetails = self._captureRegionsStack.pop()
+        assignTo, lineCol = (captureDetails.assignTo, captureDetails.lineCol)
+        self.addChunk('trans = _orig_trans%(ID)s'%locals())
+        self.addChunk('write = trans.response().write')
+        self.addChunk('self._CHEETAH__isBuffering = _wasBuffering%(ID)s '%locals())
+        self.addChunk('%(assignTo)s = _captureCollector%(ID)s.response().getvalue()'%locals())
+        self.addChunk('del _orig_trans%(ID)s'%locals())
+        self.addChunk('del _captureCollector%(ID)s'%locals())
+        self.addChunk('del _wasBuffering%(ID)s'%locals())
+        
+    def setErrorCatcher(self, errorCatcherName):
+        self.turnErrorCatcherOn()        
+
+        self.addChunk('if self._CHEETAH__errorCatchers.has_key("' + errorCatcherName + '"):')
+        self.indent()
+        self.addChunk('self._CHEETAH__errorCatcher = self._CHEETAH__errorCatchers["' +
+            errorCatcherName + '"]')
+        self.dedent()
+        self.addChunk('else:')
+        self.indent()
+        self.addChunk('self._CHEETAH__errorCatcher = self._CHEETAH__errorCatchers["'
+                      + errorCatcherName + '"] = ErrorCatchers.'
+                      + errorCatcherName + '(self)'
+                      )
+        self.dedent()
+
+    def nextFilterRegionID(self):
+        return self.nextCacheID()
+        
+    def setFilter(self, theFilter, isKlass):
+        class FilterDetails: pass
+        filterDetails = FilterDetails()
+        filterDetails.ID = ID = self.nextFilterRegionID()
+        filterDetails.theFilter = theFilter
+        filterDetails.isKlass = isKlass
+        self._filterRegionsStack.append((ID, filterDetails)) # attrib of current methodCompiler
+
+        self.addChunk('_orig_filter%(ID)s = _filter'%locals())
+        if isKlass:
+            self.addChunk('_filter = self._CHEETAH__currentFilter = ' + theFilter.strip() +
+                          '(self).filter')
+        else:
+            if theFilter.lower() == 'none':
+                self.addChunk('_filter = self._CHEETAH__initialFilter')
+            else:
+                # is string representing the name of a builtin filter
+                self.addChunk('filterName = ' + repr(theFilter))
+                self.addChunk('if self._CHEETAH__filters.has_key("' + theFilter + '"):')
+                self.indent()
+                self.addChunk('_filter = self._CHEETAH__currentFilter = self._CHEETAH__filters[filterName]')
+                self.dedent()
+                self.addChunk('else:')
+                self.indent()
+                self.addChunk('_filter = self._CHEETAH__currentFilter'
+                              +' = \\\n\t\t\tself._CHEETAH__filters[filterName] = '
+                              + 'getattr(self._CHEETAH__filtersLib, filterName)(self).filter')
+                self.dedent()
+                
+    def closeFilterBlock(self):
+        ID, filterDetails = self._filterRegionsStack.pop()
+        #self.addChunk('_filter = self._CHEETAH__initialFilter')
+        #self.addChunk('_filter = _orig_filter%(ID)s'%locals())
+        self.addChunk('_filter = self._CHEETAH__currentFilter = _orig_filter%(ID)s'%locals())
+
+class AutoMethodCompiler(MethodCompiler):
+
+    def _setupState(self):
+        MethodCompiler._setupState(self)
+        self._argStringList = [ ("self",None) ]
+        self._streamingEnabled = True
+
+    def _useKWsDictArgForPassingTrans(self):
+        alreadyHasTransArg = [argname for argname,defval in self._argStringList
+                              if argname=='trans']
+        return (self.methodName()!='respond'
+                and not alreadyHasTransArg
+                and self.setting('useKWsDictArgForPassingTrans'))
+    
+    def cleanupState(self):
+        MethodCompiler.cleanupState(self)
+        self.commitStrConst()
+        if self._cacheRegionsStack:
+            self.endCacheRegion()
+        if self._callRegionsStack:
+            self.endCallRegion()
+            
+        if self._streamingEnabled:
+            kwargsName = None
+            positionalArgsListName = None
+            for argname,defval in self._argStringList:
+                if argname.strip().startswith('**'):
+                    kwargsName = argname.strip().replace('**','')
+                    break
+                elif argname.strip().startswith('*'):
+                    positionalArgsListName = argname.strip().replace('*','')
+                    
+            if not kwargsName and self._useKWsDictArgForPassingTrans():
+                kwargsName = 'KWS'
+                self.addMethArg('**KWS', None)
+            self._kwargsName = kwargsName
+
+            if not self._useKWsDictArgForPassingTrans():
+                if not kwargsName and not positionalArgsListName:
+                    self.addMethArg('trans', 'None')       
+                else:
+                    self._streamingEnabled = False
+                
+        self._indentLev = self.setting('initialMethIndentLevel')
+        mainBodyChunks = self._methodBodyChunks
+        self._methodBodyChunks = []
+        self._addAutoSetupCode()
+        self._methodBodyChunks.extend(mainBodyChunks)
+        self._addAutoCleanupCode()
+        
+    def _addAutoSetupCode(self):
+        if self._initialMethodComment:
+            self.addChunk(self._initialMethodComment)
+            
+        if self._streamingEnabled:
+            if self._useKWsDictArgForPassingTrans() and self._kwargsName:
+                self.addChunk('trans = %s.get("trans")'%self._kwargsName)            
+            self.addChunk('if (not trans and not self._CHEETAH__isBuffering'
+                          ' and not callable(self.transaction)):')
+            self.indent()
+            self.addChunk('trans = self.transaction'
+                          ' # is None unless self.awake() was called')
+            self.dedent()
+            self.addChunk('if not trans:')
+            self.indent()
+            self.addChunk('trans = DummyTransaction()')
+            if self.setting('autoAssignDummyTransactionToSelf'):
+                self.addChunk('self.transaction = trans')            
+            self.addChunk('_dummyTrans = True')
+            self.dedent()
+            self.addChunk('else: _dummyTrans = False')
+        else:
+            self.addChunk('trans = DummyTransaction()')
+            self.addChunk('_dummyTrans = True')
+        self.addChunk('write = trans.response().write')
+        if self.setting('useNameMapper'):
+            argNames = [arg[0] for arg in self._argStringList]
+            allowSearchListAsMethArg = self.setting('allowSearchListAsMethArg')            
+            if allowSearchListAsMethArg and 'SL' in argNames:
+                pass
+            elif allowSearchListAsMethArg and 'searchList' in argNames:
+                self.addChunk('SL = searchList')
+            else:
+                self.addChunk('SL = self._CHEETAH__searchList')                
+        if self.setting('useFilters'):
+            self.addChunk('_filter = self._CHEETAH__currentFilter')
+        self.addChunk('')
+        self.addChunk("#" *40)
+        self.addChunk('## START - generated method body')
+        self.addChunk('')
+
+    def _addAutoCleanupCode(self):
+        self.addChunk('')
+        self.addChunk("#" *40)
+        self.addChunk('## END - generated method body')
+        self.addChunk('')
+
+        if not self._isGenerator:
+            self.addStop()
+        self.addChunk('')
+        
+    def addStop(self, expr=None):
+        self.addChunk('return _dummyTrans and trans.response().getvalue() or ""')
+
+    def addMethArg(self, name, defVal=None):
+        self._argStringList.append( (name,defVal) )
+        
+    def methodSignature(self):
+        argStringChunks = []
+        for arg in self._argStringList:
+            chunk = arg[0]
+            if not arg[1] == None:
+                chunk += '=' + arg[1]
+            argStringChunks.append(chunk)
+        argString = (', ').join(argStringChunks)
+
+        output = []
+        if self._decorator:
+            output.append(self._indent + self._decorator+'\n')
+        output.append(self._indent + "def "
+                      + self.methodName() + "(" +
+                      argString + "):\n\n")
+        return ''.join(output)
+
+
+##################################################
+## CLASS COMPILERS
+
+_initMethod_initCheetah = """\
+if not self._CHEETAH__instanceInitialized:
+    cheetahKWArgs = {}
+    allowedKWs = 'searchList namespaces filter filtersLib errorCatcher'.split()
+    for k,v in KWs.items():
+        if k in allowedKWs: cheetahKWArgs[k] = v
+    self._initCheetahInstance(**cheetahKWArgs)
+""".replace('\n','\n'+' '*8)
+
+class ClassCompiler(GenUtils):
+    methodCompilerClass = AutoMethodCompiler
+    methodCompilerClassForInit = MethodCompiler
+        
+    def __init__(self, className, mainMethodName='respond',
+                 moduleCompiler=None,
+                 fileName=None,
+                 settingsManager=None):
+
+        self._settingsManager = settingsManager
+        self._fileName = fileName
+        self._className = className
+        self._moduleCompiler = moduleCompiler
+        self._mainMethodName = mainMethodName
+        self._setupState()
+        methodCompiler = self._spawnMethodCompiler(
+            mainMethodName,
+            initialMethodComment='## CHEETAH: main method generated for this template')
+
+        self._setActiveMethodCompiler(methodCompiler)
+        if fileName and self.setting('monitorSrcFile'):
+            self._addSourceFileMonitoring(fileName)
+
+    def setting(self, key):
+        return self._settingsManager.setting(key)
+
+    def __getattr__(self, name):
+        """Provide access to the methods and attributes of the MethodCompiler
+        at the top of the activeMethods stack: one-way namespace sharing
+
+        
+        WARNING: Use .setMethods to assign the attributes of the MethodCompiler
+        from the methods of this class!!! or you will be assigning to attributes
+        of this object instead."""
+        
+        if self.__dict__.has_key(name):
+            return self.__dict__[name]
+        elif hasattr(self.__class__, name):
+            return getattr(self.__class__, name)
+        elif self._activeMethodsList and hasattr(self._activeMethodsList[-1], name):
+            return getattr(self._activeMethodsList[-1], name)
+        else:
+            raise AttributeError, name
+
+    def _setupState(self):
+        self._classDef = None
+        self._decoratorForNextMethod = None
+        self._activeMethodsList = []        # stack while parsing/generating
+        self._finishedMethodsList = []      # store by order
+        self._methodsIndex = {}      # store by name
+        self._baseClass = 'Template'
+        self._classDocStringLines = []
+        # printed after methods in the gen class def:
+        self._generatedAttribs = ['_CHEETAH__instanceInitialized = False']
+        self._generatedAttribs.append('_CHEETAH_version = __CHEETAH_version__')
+        self._generatedAttribs.append(
+            '_CHEETAH_versionTuple = __CHEETAH_versionTuple__')
+
+        if self.setting('addTimestampsToCompilerOutput'):
+            self._generatedAttribs.append('_CHEETAH_genTime = __CHEETAH_genTime__')
+            self._generatedAttribs.append('_CHEETAH_genTimestamp = __CHEETAH_genTimestamp__')
+
+        self._generatedAttribs.append('_CHEETAH_src = __CHEETAH_src__')
+        self._generatedAttribs.append(
+            '_CHEETAH_srcLastModified = __CHEETAH_srcLastModified__')
+
+        if self.setting('templateMetaclass'):
+            self._generatedAttribs.append('__metaclass__ = '+self.setting('templateMetaclass'))
+        self._initMethChunks = []        
+        self._blockMetaData = {}
+        self._errorCatcherCount = 0