PKo*<4§Gß½ŸŸCheetah/CacheRegion.py# $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 and Philippe Normand Version: $Revision: 1.3 $ Start Date: 2005/06/20 Last Revision Date: $Date: 2006/01/28 04:19:30 $ """ __author__ = "Tavis Rudd and Philippe Normand " __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] PKñœ¢6݃͵¸ ¸ Cheetah/CacheRegion.pyc;ò ÒðÚCc@sydZdZddd!ZdkZdklZdklZdfd „ƒYZd fd „ƒYZ d fd „ƒYZ dS(seCache 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 and Philippe Normand Version: $Revision: 1.3 $ Start Date: 2005/06/20 Last Revision Date: $Date: 2006/01/28 04:19:30 $ sJTavis Rudd and Philippe Normand s$Revision: 1.3 $i iþÿÿÿN(stime(sMemoryCacheStores CacheItemcBs_tZdZd„Zd„Zd„Zd„Zd„Zd„Zd„Z d„Z d „Z RS( sÅ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 cCs(||_||_t|_d|_dS(Ni(s cacheItemIDsselfs _cacheItemIDs cacheStores _cacheStoresNones _refreshTimes _expiryTime(sselfs cacheItemIDs cacheStore((s8build/bdist.darwin-8.9.1-i386/egg/Cheetah/CacheRegion.pys__init__*s   cCs|iotƒ|ijSdS(N(sselfs _expiryTimes currentTime(sself((s8build/bdist.darwin-8.9.1-i386/egg/Cheetah/CacheRegion.pys hasExpired0scCs ||_dS(N(stimesselfs _expiryTime(sselfstime((s8build/bdist.darwin-8.9.1-i386/egg/Cheetah/CacheRegion.pys setExpiryTime3scCs |iSdS(N(sselfs _expiryTime(sself((s8build/bdist.darwin-8.9.1-i386/egg/Cheetah/CacheRegion.pys getExpiryTime6scCs,tƒ|_|ii|i||iƒdS(N(s currentTimesselfs _refreshTimes _cacheStoressets _cacheItemIDsdatas _expiryTime(sselfsdata((s8build/bdist.darwin-8.9.1-i386/egg/Cheetah/CacheRegion.pyssetData9s cCs |iSdS(N(sselfs _refreshTime(sself((s8build/bdist.darwin-8.9.1-i386/egg/Cheetah/CacheRegion.pysgetRefreshTime=scCs(|ipt‚|ii|iƒSdS(N(sselfs _refreshTimesAssertionErrors _cacheStoresgets _cacheItemID(sself((s8build/bdist.darwin-8.9.1-i386/egg/Cheetah/CacheRegion.pysgetData@scCs|iƒpdSdS(s+Can be overridden to implement edge-cachingsN(sselfsgetData(sself((s8build/bdist.darwin-8.9.1-i386/egg/Cheetah/CacheRegion.pys renderOutputDscCs |ii|iƒt|_dS(N(sselfs _cacheStoresdeletes _cacheItemIDsNones _refreshTime(sself((s8build/bdist.darwin-8.9.1-i386/egg/Cheetah/CacheRegion.pysclearHs( s__name__s __module__s__doc__s__init__s hasExpireds setExpiryTimes getExpiryTimessetDatasgetRefreshTimesgetDatas renderOutputsclear(((s8build/bdist.darwin-8.9.1-i386/egg/Cheetah/CacheRegion.pys CacheItem"s         s_CacheDataStoreWrappercBs/tZd„Zd„Zd„Zdd„ZRS(NcCs||_||_dS(N(s dataStoresselfs _dataStores keyPrefixs _keyPrefix(sselfs dataStores keyPrefix((s8build/bdist.darwin-8.9.1-i386/egg/Cheetah/CacheRegion.pys__init__Ms cCs|ii|i|ƒSdS(N(sselfs _dataStoresgets _keyPrefixskey(sselfskey((s8build/bdist.darwin-8.9.1-i386/egg/Cheetah/CacheRegion.pysgetQscCs|ii|i|ƒdS(N(sselfs _dataStoresdeletes _keyPrefixskey(sselfskey((s8build/bdist.darwin-8.9.1-i386/egg/Cheetah/CacheRegion.pysdeleteTsicCs$|ii|i||d|ƒdS(Nstime(sselfs _dataStoressets _keyPrefixskeysvalstime(sselfskeysvalstime((s8build/bdist.darwin-8.9.1-i386/egg/Cheetah/CacheRegion.pyssetWs(s__name__s __module__s__init__sgetsdeletesset(((s8build/bdist.darwin-8.9.1-i386/egg/Cheetah/CacheRegion.pys_CacheDataStoreWrapperLs   s CacheRegioncBs>tZdZeZded„Zd„Zd„Zd„Z RS(sÑ 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). scCsgt|_||_||_| o tƒ}n||_ t |d|d|dƒ|_ h|_ dS(Ns keyPrefixs:( sTruesselfs_isNewsregionIDs _regionIDstemplateCacheIdPrefixs_templateCacheIdPrefixs cacheStoresMemoryCacheStores _cacheStores_CacheDataStoreWrappers_wrappedCacheDataStores _cacheItems(sselfsregionIDstemplateCacheIdPrefixs cacheStore((s8build/bdist.darwin-8.9.1-i386/egg/Cheetah/CacheRegion.pys__init__fs     !cCs |iSdS(N(sselfs_isNew(sself((s8build/bdist.darwin-8.9.1-i386/egg/Cheetah/CacheRegion.pysisNewqscCs?x8|iiƒD]'}|i|}|iƒ|i|=qWdS(s1 drop all the caches stored in this cache region N(sselfs _cacheItemsskeyss cacheItemIds cacheItemsclear(sselfs cacheItems cacheItemId((s8build/bdist.darwin-8.9.1-i386/egg/Cheetah/CacheRegion.pysclearts   cCsstit|ƒƒiƒ}|ii|ƒ o5|id|d|i ƒ}||i| 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() PKñœ¢6™¦¤tCheetah/CacheStore.pyc;ò ðÚCc@s|dZdklZdklZdefd„ƒYZdefd„ƒYZ de fd„ƒYZ d e fd „ƒYZ d S( sõ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 (stime(sClientsErrorcBstZRS(N(s__name__s __module__(((s7build/bdist.darwin-8.9.1-i386/egg/Cheetah/CacheStore.pysErrorssAbstractCacheStorecBs>tZed„Zed„Zed„Zd„Zd„ZRS(NcCs t‚dS(N(sNotImplementedError(sselfskeysvalstime((s7build/bdist.darwin-8.9.1-i386/egg/Cheetah/CacheStore.pyssetscCs t‚dS(N(sNotImplementedError(sselfskeysvalstime((s7build/bdist.darwin-8.9.1-i386/egg/Cheetah/CacheStore.pysaddscCs t‚dS(N(sNotImplementedError(sselfskeysvalstime((s7build/bdist.darwin-8.9.1-i386/egg/Cheetah/CacheStore.pysreplace scCs t‚dS(N(sNotImplementedError(sselfskey((s7build/bdist.darwin-8.9.1-i386/egg/Cheetah/CacheStore.pysdelete#scCs t‚dS(N(sNotImplementedError(sselfskey((s7build/bdist.darwin-8.9.1-i386/egg/Cheetah/CacheStore.pysget&s(s__name__s __module__sNonessetsaddsreplacesdeletesget(((s7build/bdist.darwin-8.9.1-i386/egg/Cheetah/CacheStore.pysAbstractCacheStores     sMemoryCacheStorecBsPtZd„Zdd„Zdd„Zdd„Zd„Zd„Zd„ZRS( NcCs h|_dS(N(sselfs_data(sself((s7build/bdist.darwin-8.9.1-i386/egg/Cheetah/CacheStore.pys__init__*sicCs||f|i||ii|ƒotd|ƒ‚n||f|i||ii|ƒotd|ƒ‚n||f|i| and Mike Orr Version: $Revision: 1.25 $ Start Date: 2001/03/30 Last Revision Date: $Date: 2006/02/04 00:59:46 $ """ __author__ = "Tavis Rudd and Mike Orr " __revision__ = "$Revision: 1.25 $"[11:-2] import getopt, glob, os, pprint, re, shutil, sys import cPickle as pickle from Cheetah.Version import Version from Cheetah.Template import Template, DEFAULT_COMPILER_SETTINGS from Cheetah.Utils.Misc import mkdirsWithPyInitFiles from Cheetah.Utils.optik import OptionParser optionDashesRE = re.compile( R"^-{1,2}" ) moduleNameRE = re.compile( R"^[a-zA-Z_][a-zA-Z_0-9]*$" ) def fprintfMessage(stream, format, *args): if format[-1:] == '^': format = format[:-1] else: format += '\n' if args: message = format % args else: message = format stream.write(message) class Error(Exception): pass class Bundle: """Wrap the source, destination and backup paths in one neat little class. Used by CheetahWrapper.getBundles(). """ def __init__(self, **kw): self.__dict__.update(kw) def __repr__(self): return "" % self.__dict__ class MyOptionParser(OptionParser): standard_option_list = [] # We use commands for Optik's standard options. def error(self, msg): """Print our usage+error page.""" usage(HELP_PAGE2, msg) def print_usage(self, file=None): """Our usage+error page already has this.""" pass ################################################## ## USAGE FUNCTION & MESSAGES def usage(usageMessage, errorMessage="", out=sys.stderr): """Write help text, an optional error message, and abort the program. """ out.write(WRAPPER_TOP) out.write(usageMessage) exitStatus = 0 if errorMessage: out.write('\n') out.write("*** USAGE ERROR ***: %s\n" % errorMessage) exitStatus = 1 sys.exit(exitStatus) WRAPPER_TOP = """\ __ ____________ __ \ \/ \/ / \/ * * \/ CHEETAH %(Version)s Command-Line Tool \ | / \ ==----== / by Tavis Rudd \__________/ and Mike Orr """ % globals() HELP_PAGE1 = """\ USAGE: ------ cheetah compile [options] [FILES ...] : Compile template definitions cheetah fill [options] [FILES ...] : Fill template definitions cheetah help : Print this help message cheetah options : Print options help message cheetah test [options] : Run Cheetah's regression tests : (same as for unittest) cheetah version : Print Cheetah version number You may abbreviate the command to the first letter; e.g., 'h' == 'help'. If FILES is a single "-", read standard input and write standard output. Run "cheetah options" for the list of valid options. """ HELP_PAGE2 = """\ OPTIONS FOR "compile" AND "fill": --------------------------------- --idir DIR, --odir DIR : input/output directories (default: current dir) --iext EXT, --oext EXT : input/output filename extensions (default for compile: tmpl/py, fill: tmpl/html) -R : recurse subdirectories looking for input files --debug : print lots of diagnostic output to standard error --env : put the environment in the searchList --flat : no destination subdirectories --nobackup : don't make backups --pickle FILE : unpickle FILE and put that object in the searchList --stdout, -p : output to standard output (pipe) --settings : a string representing the compiler settings to use e.g. --settings='useNameMapper=False,useFilters=False' This string is eval'd in Python so it should contain valid Python syntax. --templateAPIClass : a string representing a subclass of Cheetah.Template:Template to use for compilation Run "cheetah help" for the main help screen. """ ################################################## ## CheetahWrapper CLASS class CheetahWrapper: MAKE_BACKUPS = True BACKUP_SUFFIX = ".bak" _templateClass = None _compilerSettings = None def __init__(self): self.progName = None self.command = None self.opts = None self.pathArgs = None self.sourceFiles = [] self.searchList = [] ################################################## ## MAIN ROUTINE def main(self, argv=None): """The main program controller.""" if argv is None: argv = sys.argv # Step 1: Determine the command and arguments. try: self.progName = progName = os.path.basename(argv[0]) self.command = command = optionDashesRE.sub("", argv[1]) if command == 'test': self.testOpts = argv[2:] else: self.parseOpts(argv[2:]) except IndexError: usage(HELP_PAGE1, "not enough command-line arguments") # Step 2: Call the command meths = (self.compile, self.fill, self.help, self.options, self.test, self.version) for meth in meths: methName = meth.__name__ # Or meth.im_func.func_name # Or meth.func_name (Python >= 2.1 only, sometimes works on 2.0) methInitial = methName[0] if command in (methName, methInitial): sys.argv[0] += (" " + methName) # @@MO: I don't necessarily agree sys.argv[0] should be # modified. meth() return # If none of the commands matched. usage(HELP_PAGE1, "unknown command '%s'" % command) def parseOpts(self, args): C, D, W = self.chatter, self.debug, self.warn self.isCompile = isCompile = self.command[0] == 'c' defaultOext = isCompile and ".py" or ".html" parser = MyOptionParser() pao = parser.add_option pao("--idir", action="store", dest="idir", default="") pao("--odir", action="store", dest="odir", default="") pao("--iext", action="store", dest="iext", default=".tmpl") pao("--oext", action="store", dest="oext", default=defaultOext) pao("-R", action="store_true", dest="recurse", default=False) pao("--stdout", "-p", action="store_true", dest="stdout", default=False) pao("--debug", action="store_true", dest="debug", default=False) pao("--env", action="store_true", dest="env", default=False) pao("--pickle", action="store", dest="pickle", default="") pao("--flat", action="store_true", dest="flat", default=False) pao("--nobackup", action="store_true", dest="nobackup", default=False) pao("--settings", action="store", dest="compilerSettingsString", default=None) pao("--templateAPIClass", action="store", dest="templateClassName", default=None) self.opts, self.pathArgs = opts, files = parser.parse_args(args) D("""\ cheetah compile %s Options are %s Files are %s""", args, pprint.pformat(vars(opts)), files) #cleanup trailing path separators seps = [sep for sep in [os.sep, os.altsep] if sep] for attr in ['idir', 'odir']: for sep in seps: path = getattr(opts, attr, None) if path and path.endswith(sep): path = path[:-len(sep)] setattr(opts, attr, path) break self._fixExts() if opts.env: self.searchList.insert(0, os.environ) if opts.pickle: f = open(opts.pickle, 'rb') unpickled = pickle.load(f) f.close() self.searchList.insert(0, unpickled) opts.verbose = not opts.stdout ################################################## ## COMMAND METHODS def compile(self): self._compileOrFill() def fill(self): from Cheetah.ImportHooks import install install() self._compileOrFill() def help(self): usage(HELP_PAGE1, "", sys.stdout) def options(self): usage(HELP_PAGE2, "", sys.stdout) def test(self): # @@MO: Ugly kludge. TEST_WRITE_FILENAME = 'cheetah_test_file_creation_ability.tmp' try: f = open(TEST_WRITE_FILENAME, 'w') except: sys.exit("""\ Cannot run the tests because you don't have write permission in the current directory. The tests need to create temporary files. Change to a directory you do have write permission to and re-run the tests.""") else: f.close() os.remove(TEST_WRITE_FILENAME) # @@MO: End ugly kludge. from Cheetah.Tests import Test import Cheetah.Tests.unittest_local_copy as unittest del sys.argv[1:] # Prevent unittest from misinterpreting options. sys.argv.extend(self.testOpts) #unittest.main(testSuite=Test.testSuite) #unittest.main(testSuite=Test.testSuite) unittest.main(module=Test) def version(self): print Version # If you add a command, also add it to the 'meths' variable in main(). ################################################## ## LOGGING METHODS def chatter(self, format, *args): """Print a verbose message to stdout. But don't if .opts.stdout is true or .opts.verbose is false. """ if self.opts.stdout or not self.opts.verbose: return fprintfMessage(sys.stdout, format, *args) def debug(self, format, *args): """Print a debugging message to stderr, but don't if .debug is false. """ if self.opts.debug: fprintfMessage(sys.stderr, format, *args) def warn(self, format, *args): """Always print a warning message to stderr. """ fprintfMessage(sys.stderr, format, *args) def error(self, format, *args): """Always print a warning message to stderr and exit with an error code. """ fprintfMessage(sys.stderr, format, *args) sys.exit(1) ################################################## ## HELPER METHODS def _fixExts(self): assert self.opts.oext, "oext is empty!" iext, oext = self.opts.iext, self.opts.oext if iext and not iext.startswith("."): self.opts.iext = "." + iext if oext and not oext.startswith("."): self.opts.oext = "." + oext def _compileOrFill(self): C, D, W = self.chatter, self.debug, self.warn opts, files = self.opts, self.pathArgs if files == ["-"]: self._compileOrFillStdin() return elif not files and opts.recurse: which = opts.idir and "idir" or "current" C("Drilling down recursively from %s directory.", which) sourceFiles = [] dir = os.path.join(self.opts.idir, os.curdir) os.path.walk(dir, self._expandSourceFilesWalk, sourceFiles) elif not files: usage(HELP_PAGE1, "Neither files nor -R specified!") else: sourceFiles = self._expandSourceFiles(files, opts.recurse, True) sourceFiles = [os.path.normpath(x) for x in sourceFiles] D("All source files found: %s", sourceFiles) bundles = self._getBundles(sourceFiles) D("All bundles: %s", pprint.pformat(bundles)) if self.opts.flat: self._checkForCollisions(bundles) for b in bundles: self._compileOrFillBundle(b) def _checkForCollisions(self, bundles): """Check for multiple source paths writing to the same destination path. """ C, D, W = self.chatter, self.debug, self.warn isError = False dstSources = {} for b in bundles: if dstSources.has_key(b.dst): dstSources[b.dst].append(b.src) else: dstSources[b.dst] = [b.src] keys = dstSources.keys() keys.sort() for dst in keys: sources = dstSources[dst] if len(sources) > 1: isError = True sources.sort() fmt = "Collision: multiple source files %s map to one destination file %s" W(fmt, sources, dst) if isError: what = self.isCompile and "Compilation" or "Filling" sys.exit("%s aborted due to collisions" % what) def _expandSourceFilesWalk(self, arg, dir, files): """Recursion extension for .expandSourceFiles(). This method is a callback for os.path.walk(). 'arg' is a list to which successful paths will be appended. """ iext = self.opts.iext for f in files: path = os.path.join(dir, f) if path.endswith(iext) and os.path.isfile(path): arg.append(path) elif os.path.islink(path) and os.path.isdir(path): os.path.walk(path, self._expandSourceFilesWalk, arg) # If is directory, do nothing; 'walk' will eventually get it. def _expandSourceFiles(self, files, recurse, addIextIfMissing): """Calculate source paths from 'files' by applying the command-line options. """ C, D, W = self.chatter, self.debug, self.warn idir = self.opts.idir iext = self.opts.iext files = [] for f in self.pathArgs: oldFilesLen = len(files) D("Expanding %s", f) path = os.path.join(idir, f) pathWithExt = path + iext # May or may not be valid. if os.path.isdir(path): if recurse: os.path.walk(path, self._expandSourceFilesWalk, files) else: raise Error("source file '%s' is a directory" % path) elif os.path.isfile(path): files.append(path) elif (addIextIfMissing and not path.endswith(iext) and os.path.isfile(pathWithExt)): files.append(pathWithExt) # Do not recurse directories discovered by iext appending. elif os.path.exists(path): W("Skipping source file '%s', not a plain file.", path) else: W("Skipping source file '%s', not found.", path) if len(files) > oldFilesLen: D(" ... found %s", files[oldFilesLen:]) return files def _getBundles(self, sourceFiles): flat = self.opts.flat idir = self.opts.idir iext = self.opts.iext nobackup = self.opts.nobackup odir = self.opts.odir oext = self.opts.oext idirSlash = idir + os.sep bundles = [] for src in sourceFiles: # 'base' is the subdirectory plus basename. base = src if idir and src.startswith(idirSlash): base = src[len(idirSlash):] if iext and base.endswith(iext): base = base[:-len(iext)] basename = os.path.basename(base) if flat: dst = os.path.join(odir, basename + oext) else: dbn = basename if odir and base.startswith(os.sep): odd = odir while odd != '': idx = base.find(odd) if idx == 0: dbn = base[len(odd):] if dbn[0] == '/': dbn = dbn[1:] break odd = os.path.dirname(odd) if odd == '/': break dst = os.path.join(odir, dbn + oext) else: dst = os.path.join(odir, base + oext) bak = dst + self.BACKUP_SUFFIX b = Bundle(src=src, dst=dst, bak=bak, base=base, basename=basename) bundles.append(b) return bundles def _getTemplateClass(self): C, D, W = self.chatter, self.debug, self.warn modname = None if self._templateClass: return self._templateClass modname = self.opts.templateClassName if not modname: return Template p = modname.rfind('.') if ':' not in modname: self.error('The value of option --templateAPIClass is invalid\n' 'It must be in the form "module:class", ' 'e.g. "Cheetah.Template:Template"') modname, classname = modname.split(':') C('using --templateAPIClass=%s:%s'%(modname, classname)) if p >= 0: mod = getattr(__import__(modname[:p], {}, {}, [modname[p+1:]]), modname[p+1:]) else: mod = __import__(modname, {}, {}, []) klass = getattr(mod, classname, None) if klass: self._templateClass = klass return klass else: self.error('**Template class specified in option --templateAPIClass not found\n' '**Falling back on Cheetah.Template:Template') def _getCompilerSettings(self): if self._compilerSettings: return self._compilerSettings def getkws(**kws): return kws if self.opts.compilerSettingsString: try: exec 'settings = getkws(%s)'%self.opts.compilerSettingsString except: self.error("There's an error in your --settings option." "It must be valid Python syntax.\n" +" --settings='%s'\n"%self.opts.compilerSettingsString +" %s: %s"%sys.exc_info()[:2] ) validKeys = DEFAULT_COMPILER_SETTINGS.keys() if [k for k in settings.keys() if k not in validKeys]: self.error( 'The --setting "%s" is not a valid compiler setting name.'%k) self._compilerSettings = settings return settings else: return {} def _compileOrFillStdin(self): TemplateClass = self._getTemplateClass() compilerSettings = self._getCompilerSettings() if self.isCompile: pysrc = TemplateClass.compile(file=sys.stdin, compilerSettings=compilerSettings, returnAClass=False) output = pysrc else: output = str(TemplateClass(file=sys.stdin, compilerSettings=compilerSettings)) sys.stdout.write(output) def _compileOrFillBundle(self, b): C, D, W = self.chatter, self.debug, self.warn TemplateClass = self._getTemplateClass() compilerSettings = self._getCompilerSettings() src = b.src dst = b.dst base = b.base basename = b.basename dstDir = os.path.dirname(dst) what = self.isCompile and "Compiling" or "Filling" C("%s %s -> %s^", what, src, dst) # No trailing newline. if os.path.exists(dst) and not self.opts.nobackup: bak = b.bak C(" (backup %s)", bak) # On same line as previous message. else: bak = None C("") if self.isCompile: if not moduleNameRE.match(basename): tup = basename, src raise Error("""\ %s: base name %s contains invalid characters. It must be named according to the same rules as Python modules.""" % tup) pysrc = TemplateClass.compile(file=src, returnAClass=False, moduleName=basename, className=basename, compilerSettings=compilerSettings) output = pysrc else: #output = str(TemplateClass(file=src, searchList=self.searchList)) tclass = TemplateClass.compile(file=src, compilerSettings=compilerSettings) output = str(tclass(searchList=self.searchList)) if bak: shutil.copyfile(dst, bak) if dstDir and not os.path.exists(dstDir): if self.isCompile: mkdirsWithPyInitFiles(dstDir) else: os.makedirs(dstDir) if self.opts.stdout: sys.stdout.write(output) else: f = open(dst, 'w') f.write(output) f.close() ################################################## ## if run from the command line if __name__ == '__main__': CheetahWrapper().main() # vim: shiftwidth=4 tabstop=4 expandtab PKñœ¢6Œ HË and Mike Orr Version: $Revision: 1.25 $ Start Date: 2001/03/30 Last Revision Date: $Date: 2006/02/04 00:59:46 $ s@Tavis Rudd and Mike Orr s$Revision: 1.25 $i iþÿÿÿN(sVersion(sTemplatesDEFAULT_COMPILER_SETTINGS(smkdirsWithPyInitFiles(s OptionParsers^-{1,2}s^[a-zA-Z_][a-zA-Z_0-9]*$cGsU|ddjo|d }n |d7}|o||}n|}|i|ƒdS(Niÿÿÿÿs^s (sformatsargssmessagesstreamswrite(sstreamsformatsargssmessage((s;build/bdist.darwin-8.9.1-i386/egg/Cheetah/CheetahWrapper.pysfprintfMessages sErrorcBstZRS(N(s__name__s __module__(((s;build/bdist.darwin-8.9.1-i386/egg/Cheetah/CheetahWrapper.pysError)ssBundlecBs tZdZd„Zd„ZRS(sxWrap the source, destination and backup paths in one neat little class. Used by CheetahWrapper.getBundles(). cKs|ii|ƒdS(N(sselfs__dict__supdateskw(sselfskw((s;build/bdist.darwin-8.9.1-i386/egg/Cheetah/CheetahWrapper.pys__init__1scCsd|iSdS(Ns (sselfs__dict__(sself((s;build/bdist.darwin-8.9.1-i386/egg/Cheetah/CheetahWrapper.pys__repr__4s(s__name__s __module__s__doc__s__init__s__repr__(((s;build/bdist.darwin-8.9.1-i386/egg/Cheetah/CheetahWrapper.pysBundle-s  sMyOptionParsercBs#tZgZd„Zed„ZRS(NcCstt|ƒdS(sPrint our usage+error page.N(susages HELP_PAGE2smsg(sselfsmsg((s;build/bdist.darwin-8.9.1-i386/egg/Cheetah/CheetahWrapper.pyserror;scCsdS(s&Our usage+error page already has this.N((sselfsfile((s;build/bdist.darwin-8.9.1-i386/egg/Cheetah/CheetahWrapper.pys print_usage?s(s__name__s __module__sstandard_option_listserrorsNones print_usage(((s;build/bdist.darwin-8.9.1-i386/egg/Cheetah/CheetahWrapper.pysMyOptionParser8s scCs`|itƒ|i|ƒd}|o(|idƒ|id|ƒd}nti|ƒdS(sGWrite help text, an optional error message, and abort the program. is s*** USAGE ERROR ***: %s iN(soutswrites WRAPPER_TOPs usageMessages exitStatuss errorMessagessyssexit(s usageMessages errorMessagesouts exitStatus((s;build/bdist.darwin-8.9.1-i386/egg/Cheetah/CheetahWrapper.pysusageGs    s2 __ ____________ __ \ \/ \/ / \/ * * \/ CHEETAH %(Version)s Command-Line Tool \ | / \ ==----== / by Tavis Rudd \__________/ and Mike Orr sÕUSAGE: ------ cheetah compile [options] [FILES ...] : Compile template definitions cheetah fill [options] [FILES ...] : Fill template definitions cheetah help : Print this help message cheetah options : Print options help message cheetah test [options] : Run Cheetah's regression tests : (same as for unittest) cheetah version : Print Cheetah version number You may abbreviate the command to the first letter; e.g., 'h' == 'help'. If FILES is a single "-", read standard input and write standard output. Run "cheetah options" for the list of valid options. soOPTIONS FOR "compile" AND "fill": --------------------------------- --idir DIR, --odir DIR : input/output directories (default: current dir) --iext EXT, --oext EXT : input/output filename extensions (default for compile: tmpl/py, fill: tmpl/html) -R : recurse subdirectories looking for input files --debug : print lots of diagnostic output to standard error --env : put the environment in the searchList --flat : no destination subdirectories --nobackup : don't make backups --pickle FILE : unpickle FILE and put that object in the searchList --stdout, -p : output to standard output (pipe) --settings : a string representing the compiler settings to use e.g. --settings='useNameMapper=False,useFilters=False' This string is eval'd in Python so it should contain valid Python syntax. --templateAPIClass : a string representing a subclass of Cheetah.Template:Template to use for compilation Run "cheetah help" for the main help screen. sCheetahWrappercBsòtZeZdZeZeZd„Zed„Z d„Z d„Z d„Z d„Z d„Zd „Zd „Zd „Zd „Zd „Zd„Zd„Zd„Zd„Zd„Zd„Zd„Zd„Zd„Zd„Zd„ZRS(Ns.bakcCs:t|_t|_t|_t|_g|_g|_dS(N(sNonesselfsprogNamescommandsoptsspathArgss sourceFiless searchList(sself((s;build/bdist.darwin-8.9.1-i386/egg/Cheetah/CheetahWrapper.pys__init__s      cCsF|tjo ti}nymtii|dƒ|_}ti d|dƒ|_ }|djo|d|_ n|i |dƒWn t j ottdƒnX|i|i|i|i|i|if}xZ|D]R}|i}|d}|||fjo&tidcd|7<|ƒdSqÛqÛWttd |ƒdS( sThe main program controller.isistestis!not enough command-line argumentss Nsunknown command '%s'(sargvsNonessyssosspathsbasenamesselfsprogNamesoptionDashesREssubscommandstestOptss parseOptss IndexErrorsusages HELP_PAGE1scompilesfillshelpsoptionsstestsversionsmethssmeths__name__smethNames methInitial(sselfsargvsmethNamesmethssprogNamescommands methInitialsmeth((s;build/bdist.darwin-8.9.1-i386/egg/Cheetah/CheetahWrapper.pysmainšs*   *   c CsW|i|i|if\}} } |iddj|_}|odpd} t ƒ}|i }|ddddd d d ƒ|d dddd d d ƒ|dddddd dƒ|dddddd | ƒ|dddddd tƒ|ddddddd tƒ|dddddd tƒ|dddddd tƒ|dddddd d ƒ|ddddd d tƒ|d!dddd"d tƒ|d#dddd$d tƒ|d%dddd&d tƒ|i|ƒ\|_|_\}}| d'|tit|ƒƒ|ƒgi}titigD]}|o||ƒq1q1~}xtd d gD]f} x]|D]U}t|| tƒ} | o | i!|ƒo&| t"|ƒ } t#|| | ƒPqoqoWqbW|i$ƒ|i%o|i&i'dti(ƒn|i)oBt*|i)d(ƒ}t)i,|ƒ}|i.ƒ|i&i'd|ƒn|i/ |_0dS()Niscs.pys.htmls--idirsactionsstoresdestsidirsdefaultss--odirsodirs--iextsiexts.tmpls--oextsoexts-Rs store_truesrecurses--stdouts-psstdouts--debugsdebugs--envsenvs--picklespickles--flatsflats --nobackupsnobackups --settingsscompilerSettingsStrings--templateAPIClassstemplateClassNames.cheetah compile %s Options are %s Files are %ssrb(1sselfschattersdebugswarnsCsDsWscommands isCompiles defaultOextsMyOptionParsersparsers add_optionspaosFalsesNones parse_argssargssoptsspathArgssfilesspprintspformatsvarssappends_[1]sosssepsaltsepssepssattrsgetattrspathsendswithslenssetattrs_fixExtssenvs searchListsinsertsenvironspicklesopensfsloads unpickledsclosesstdoutsverbose(sselfsargss isCompilessepsparsers unpickledspaosfilessCsDs defaultOextsWspathsattrsfs_[1]ssepssopts((s;build/bdist.darwin-8.9.1-i386/egg/Cheetah/CheetahWrapper.pys parseOpts¼sN!  %">      cCs|iƒdS(N(sselfs_compileOrFill(sself((s;build/bdist.darwin-8.9.1-i386/egg/Cheetah/CheetahWrapper.pyscompileïscCs"dkl}|ƒ|iƒdS(N(sinstall(sCheetah.ImportHookssinstallsselfs_compileOrFill(sselfsinstall((s;build/bdist.darwin-8.9.1-i386/egg/Cheetah/CheetahWrapper.pysfillòs cCsttdtiƒdS(Ns(susages HELP_PAGE1ssyssstdout(sself((s;build/bdist.darwin-8.9.1-i386/egg/Cheetah/CheetahWrapper.pyshelp÷scCsttdtiƒdS(Ns(susages HELP_PAGE2ssyssstdout(sself((s;build/bdist.darwin-8.9.1-i386/egg/Cheetah/CheetahWrapper.pysoptionsúscCs”d}yt|dƒ}WntidƒnX|iƒti|ƒdkl }dk i i }tid3tii|iƒ|id|ƒdS(Ns&cheetah_test_file_creation_ability.tmpswsÎCannot run the tests because you don't have write permission in the current directory. The tests need to create temporary files. Change to a directory you do have write permission to and re-run the tests.(sTestismodule(sTEST_WRITE_FILENAMEsopensfssyssexitsclosesossremoves Cheetah.TestssTests!Cheetah.Tests.unittest_local_copysTestssunittest_local_copysunittestsargvsextendsselfstestOptssmain(sselfsfsTEST_WRITE_FILENAMEsTestsunittest((s;build/bdist.darwin-8.9.1-i386/egg/Cheetah/CheetahWrapper.pystestýs    cCs tGHdS(N(sVersion(sself((s;build/bdist.darwin-8.9.1-i386/egg/Cheetah/CheetahWrapper.pysversionscGs:|iip |ii odSntti||ŒdS(stPrint a verbose message to stdout. But don't if .opts.stdout is true or .opts.verbose is false. N(sselfsoptssstdoutsverbosesfprintfMessagessyssformatsargs(sselfsformatsargs((s;build/bdist.darwin-8.9.1-i386/egg/Cheetah/CheetahWrapper.pyschatterscGs(|iiotti||ŒndS(sVPrint a debugging message to stderr, but don't if .debug is false. N(sselfsoptssdebugsfprintfMessagessyssstderrsformatsargs(sselfsformatsargs((s;build/bdist.darwin-8.9.1-i386/egg/Cheetah/CheetahWrapper.pysdebug$s cGstti||ŒdS(s2Always print a warning message to stderr. N(sfprintfMessagessyssstderrsformatsargs(sselfsformatsargs((s;build/bdist.darwin-8.9.1-i386/egg/Cheetah/CheetahWrapper.pyswarn+scGs$tti||ŒtidƒdS(sVAlways print a warning message to stderr and exit with an error code. iN(sfprintfMessagessyssstderrsformatsargssexit(sselfsformatsargs((s;build/bdist.darwin-8.9.1-i386/egg/Cheetah/CheetahWrapper.pyserror0scCs‘|iip td‚|ii|iif\}}|o|idƒ od||i_n|o|idƒ od||i_ndS(Nsoext is empty!s.(sselfsoptssoextsAssertionErrorsiexts startswith(sselfsoextsiext((s;build/bdist.darwin-8.9.1-i386/egg/Cheetah/CheetahWrapper.pys_fixExts:s c Cs¨|i|i|if\}}}|i|if\} }|dgjo|i ƒdSn©| o| i oe| i odpd} |d| ƒg} tii|ii tiƒ}tii||i| ƒn2| ottdƒn|i|| i tƒ} gi}| D]} |tii| ƒƒq~} |d| ƒ|i| ƒ}|dt i!|ƒƒ|ii"o|i#|ƒnx|D]}|i%|ƒqWdS(Ns-sidirscurrents,Drilling down recursively from %s directory.sNeither files nor -R specified!sAll source files found: %ssAll bundles: %s(&sselfschattersdebugswarnsCsDsWsoptsspathArgssfiless_compileOrFillStdinsrecursesidirswhichs sourceFilessosspathsjoinscurdirsdirswalks_expandSourceFilesWalksusages HELP_PAGE1s_expandSourceFilessTruesappends_[1]sxsnormpaths _getBundlessbundlesspprintspformatsflats_checkForCollisionssbs_compileOrFillBundle( sselfsfilessCsbsDsbundless_[1]sdirsWswhichsxs sourceFilessopts((s;build/bdist.darwin-8.9.1-i386/egg/Cheetah/CheetahWrapper.pys_compileOrFillDs.!  3  c Cs$|i|i|if\}}} t} h}xO|D]G}|i |i ƒo||i i|iƒq4|ig||i %s^s (backup %s)ssn%s: base name %s contains invalid characters. It must be named according to the same rules as Python modules.sfiles returnAClasss moduleNames classNamescompilerSettingss searchListsw(0sselfschattersdebugswarnsCsDsWs_getTemplateClasss TemplateClasss_getCompilerSettingsscompilerSettingssbssrcsdstsbasesbasenamesosspathsdirnamesdstDirs isCompileswhatsexistssoptssnobackupsbaksNones moduleNameREsmatchstupsErrorscompilesFalsespysrcsoutputstclasssstrs searchListsshutilscopyfilesmkdirsWithPyInitFilessmakedirssstdoutssysswritesopensfsclose(sselfsbsdstsdstDirsbasenamespysrcswhatscompilerSettingssCsDstups TemplateClasssWssrcsfsbasesoutputstclasssbak((s;build/bdist.darwin-8.9.1-i386/egg/Cheetah/CheetahWrapper.pys_compileOrFillBundlesJ!      !         (s__name__s __module__sTrues MAKE_BACKUPSs BACKUP_SUFFIXsNones_templateClasss_compilerSettingss__init__smains parseOptsscompilesfillshelpsoptionsstestsversionschattersdebugswarnserrors_fixExtss_compileOrFills_checkForCollisionss_expandSourceFilesWalks_expandSourceFiless _getBundless_getTemplateClasss_getCompilerSettingss_compileOrFillStdins_compileOrFillBundle(((s;build/bdist.darwin-8.9.1-i386/egg/Cheetah/CheetahWrapper.pysCheetahWrapper‰s6 " 3            ! * "  s__main__(&s__doc__s __author__s __revision__sgetoptsglobsosspprintsresshutilssysscPicklespicklesCheetah.VersionsVersionsCheetah.TemplatesTemplatesDEFAULT_COMPILER_SETTINGSsCheetah.Utils.MiscsmkdirsWithPyInitFilessCheetah.Utils.optiks OptionParserscompilesoptionDashesREs moduleNameREsfprintfMessages ExceptionsErrorsBundlesMyOptionParsersstderrsusagesglobalss WRAPPER_TOPs HELP_PAGE1s HELP_PAGE2sCheetahWrappers__name__smain(soptionDashesREs HELP_PAGE2s WRAPPER_TOPsVersionsTemplatesshutilsMyOptionParsers __revision__smkdirsWithPyInitFilesspprintsBundlesresusagesgetopts OptionParsersglobs __author__ssyssCheetahWrappers HELP_PAGE1sErrorsossfprintfMessages moduleNameREsDEFAULT_COMPILER_SETTINGSspickle((s;build/bdist.darwin-8.9.1-i386/egg/Cheetah/CheetahWrapper.pys?s, ?       ÿà PK‘„6üÀ·L¢6¢6Cheetah/Compiler.py#!/usr/bin/env python # $Id: Compiler.py,v 1.155 2007/04/04 00:28:35 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 Version: $Revision: 1.155 $ Start Date: 2001/09/19 Last Revision Date: $Date: 2007/04/04 00:28:35 $ """ __author__ = "Tavis Rudd " __revision__ = "$Revision: 1.155 $"[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\n'), 'blockMarkerEnd':('\n\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 then else """ 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()) 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 self._placeholderToErrorCatcherMap = {} def cleanupState(self): while self._activeMethodsList: methCompiler = self._popActiveMethodCompiler() self._swallowMethodCompiler(methCompiler) self._setupInitMethod() if self._mainMethodName == 'respond': if self.setting('setup__str__method'): self._generatedAttribs.append('def __str__(self): return self.respond()') self.addAttribute('_mainCheetahMethod_for_' + self._className + '= ' + repr(self._mainMethodName) ) def _setupInitMethod(self): __init__ = self._spawnMethodCompiler('__init__', klass=self.methodCompilerClassForInit) __init__.setMethodSignature("def __init__(self, *args, **KWs)") __init__.addChunk("%s.__init__(self, *args, **KWs)" % self._baseClass) __init__.addChunk(_initMethod_initCheetah%{'className':self._className}) for chunk in self._initMethChunks: __init__.addChunk(chunk) __init__.cleanupState() self._swallowMethodCompiler(__init__, pos=0) def _addSourceFileMonitoring(self, fileName): # @@TR: this stuff needs auditing for Cheetah 2.0 # the first bit is added to init self.addChunkToInit('self._filePath = ' + repr(fileName)) self.addChunkToInit('self._fileMtime = ' + str(getmtime(fileName)) ) # the rest is added to the main output method of the class ('mainMethod') self.addChunk('if exists(self._filePath) and ' + 'getmtime(self._filePath) > self._fileMtime:') self.indent() self.addChunk('self._compile(file=self._filePath, moduleName='+self._className + ')') self.addChunk( 'write(getattr(self, self._mainCheetahMethod_for_' + self._className + ')(trans=trans))') self.addStop() self.dedent() def setClassName(self, name): self._className = name def className(self): return self._className def setBaseClass(self, baseClassName): self._baseClass = baseClassName def setMainMethodName(self, methodName): if methodName == self._mainMethodName: return ## change the name in the methodCompiler and add new reference mainMethod = self._methodsIndex[self._mainMethodName] mainMethod.setMethodName(methodName) self._methodsIndex[methodName] = mainMethod ## make sure that fileUpdate code still works properly: chunkToChange = ('write(self.' + self._mainMethodName + '(trans=trans))') chunks = mainMethod._methodBodyChunks if chunkToChange in chunks: for i in range(len(chunks)): if chunks[i] == chunkToChange: chunks[i] = ('write(self.' + methodName + '(trans=trans))') ## get rid of the old reference and update self._mainMethodName del self._methodsIndex[self._mainMethodName] self._mainMethodName = methodName def setMainMethodArgs(self, argsList): mainMethodCompiler = self._methodsIndex[self._mainMethodName] for argName, defVal in argsList: mainMethodCompiler.addMethArg(argName, defVal) def _spawnMethodCompiler(self, methodName, klass=None, initialMethodComment=None): if klass is None: klass = self.methodCompilerClass decorator = None if self._decoratorForNextMethod: decorator = self._decoratorForNextMethod self._decoratorForNextMethod = None methodCompiler = klass(methodName, classCompiler=self, decorator=decorator, initialMethodComment=initialMethodComment) self._methodsIndex[methodName] = methodCompiler return methodCompiler def _setActiveMethodCompiler(self, methodCompiler): self._activeMethodsList.append(methodCompiler) def _getActiveMethodCompiler(self): return self._activeMethodsList[-1] def _popActiveMethodCompiler(self): return self._activeMethodsList.pop() def _swallowMethodCompiler(self, methodCompiler, pos=None): methodCompiler.cleanupState() if pos==None: self._finishedMethodsList.append( methodCompiler ) else: self._finishedMethodsList.insert(pos, methodCompiler) return methodCompiler def startMethodDef(self, methodName, argsList, parserComment): methodCompiler = self._spawnMethodCompiler( methodName, initialMethodComment=parserComment) self._setActiveMethodCompiler(methodCompiler) for argName, defVal in argsList: methodCompiler.addMethArg(argName, defVal) def _finishedMethods(self): return self._finishedMethodsList def addDecorator(self, decoratorExpr): """Set the decorator to be used with the next method in the source. See _spawnMethodCompiler() and MethodCompiler for the details of how this is used. """ self._decoratorForNextMethod = decoratorExpr def addClassDocString(self, line): self._classDocStringLines.append( line.replace('%','%%')) def addChunkToInit(self,chunk): self._initMethChunks.append(chunk) def addAttribute(self, attribExpr): ## first test to make sure that the user hasn't used any fancy Cheetah syntax # (placeholders, directives, etc.) inside the expression if attribExpr.find('VFN(') != -1 or attribExpr.find('VFFSL(') != -1: raise ParseError(self, 'Invalid #attr directive.' + ' It should only contain simple Python literals.') ## now add the attribute self._generatedAttribs.append(attribExpr) def addSuper(self, argsList, parserComment=None): className = self._className #self._baseClass methodName = self._getActiveMethodCompiler().methodName() argStringChunks = [] for arg in argsList: chunk = arg[0] if not arg[1] == None: chunk += '=' + arg[1] argStringChunks.append(chunk) argString = ','.join(argStringChunks) self.addFilteredChunk( 'super(%(className)s, self).%(methodName)s(%(argString)s)'%locals()) def addErrorCatcherCall(self, codeChunk, rawCode='', lineCol=''): if self._placeholderToErrorCatcherMap.has_key(rawCode): methodName = self._placeholderToErrorCatcherMap[rawCode] if not self.setting('outputRowColComments'): self._methodsIndex[methodName].addMethDocString( 'plus at line %s, col %s'%lineCol) return methodName self._errorCatcherCount += 1 methodName = '__errorCatcher' + str(self._errorCatcherCount) self._placeholderToErrorCatcherMap[rawCode] = methodName catcherMeth = self._spawnMethodCompiler( methodName, klass=MethodCompiler, initialMethodComment=('## CHEETAH: Generated from ' + rawCode + ' at line %s, col %s'%lineCol + '.') ) catcherMeth.setMethodSignature('def ' + methodName + '(self, localsDict={})') # is this use of localsDict right? catcherMeth.addChunk('try:') catcherMeth.indent() catcherMeth.addChunk("return eval('''" + codeChunk + "''', globals(), localsDict)") catcherMeth.dedent() catcherMeth.addChunk('except self._CHEETAH__errorCatcher.exceptions(), e:') catcherMeth.indent() catcherMeth.addChunk("return self._CHEETAH__errorCatcher.warn(exc_val=e, code= " + repr(codeChunk) + " , rawCode= " + repr(rawCode) + " , lineCol=" + str(lineCol) +")") catcherMeth.cleanupState() self._swallowMethodCompiler(catcherMeth) return methodName def closeDef(self): self.commitStrConst() methCompiler = self._popActiveMethodCompiler() self._swallowMethodCompiler(methCompiler) def closeBlock(self): self.commitStrConst() methCompiler = self._popActiveMethodCompiler() methodName = methCompiler.methodName() if self.setting('includeBlockMarkers'): endMarker = self.setting('blockMarkerEnd') methCompiler.addStrConst(endMarker[0] + methodName + endMarker[1]) self._swallowMethodCompiler(methCompiler) #metaData = self._blockMetaData[methodName] #rawDirective = metaData['raw'] #lineCol = metaData['lineCol'] ## insert the code to call the block, caching if #cache directive is on codeChunk = 'self.' + methodName + '(trans=trans)' self.addChunk(codeChunk) #self.appendToPrevChunk(' # generated from ' + repr(rawDirective) ) #if self.setting('outputRowColComments'): # self.appendToPrevChunk(' at line %s, col %s' % lineCol + '.') ## code wrapping methods def classDef(self): if self._classDef: return self._classDef else: return self.wrapClassDef() __str__ = classDef __unicode__ = classDef def wrapClassDef(self): ind = self.setting('indentationStep') classDefChunks = [self.classSignature(), self.classDocstring(), ] def addMethods(): classDefChunks.extend([ ind + '#'*50, ind + '## CHEETAH GENERATED METHODS', '\n', self.methodDefs(), ]) def addAttributes(): classDefChunks.extend([ ind + '#'*50, ind + '## CHEETAH GENERATED ATTRIBUTES', '\n', self.attributes(), ]) if self.setting('outputMethodsBeforeAttributes'): addMethods() addAttributes() else: addAttributes() addMethods() classDef = '\n'.join(classDefChunks) self._classDef = classDef return classDef def classSignature(self): return "class %s(%s):" % (self.className(), self._baseClass) def classDocstring(self): if not self._classDocStringLines: return '' ind = self.setting('indentationStep') docStr = ('%(ind)s"""\n%(ind)s' + '\n%(ind)s'.join(self._classDocStringLines) + '\n%(ind)s"""\n' ) % {'ind':ind} return docStr def methodDefs(self): methodDefs = [methGen.methodDef() for methGen in self._finishedMethods()] return '\n\n'.join(methodDefs) def attributes(self): attribs = [self.setting('indentationStep') + str(attrib) for attrib in self._generatedAttribs ] return '\n\n'.join(attribs) class AutoClassCompiler(ClassCompiler): pass ################################################## ## MODULE COMPILERS class ModuleCompiler(SettingsManager, GenUtils): parserClass = Parser classCompilerClass = AutoClassCompiler def __init__(self, source=None, file=None, moduleName='DynamicallyCompiledCheetahTemplate', mainClassName=None, # string mainMethodName=None, # string baseclassName=None, # string extraImportStatements=None, # list of strings settings=None # dict ): SettingsManager.__init__(self) if settings: self.updateSettings(settings) # disable useStackFrames if the C version of NameMapper isn't compiled # it's painfully slow in the Python version and bites Windows users all # the time: if not NameMap