summaryrefslogtreecommitdiffstats
path: root/cobbler/Cheetah/Compiler.py
diff options
context:
space:
mode:
Diffstat (limited to 'cobbler/Cheetah/Compiler.py')
-rw-r--r--cobbler/Cheetah/Compiler.py1975
1 files changed, 1975 insertions, 0 deletions
diff --git a/cobbler/Cheetah/Compiler.py b/cobbler/Cheetah/Compiler.py
new file mode 100644
index 0000000..a74267d
--- /dev/null
+++ b/cobbler/Cheetah/Compiler.py
@@ -0,0 +1,1975 @@
+#!/usr/bin/env python
+# $Id: Compiler.py,v 1.148 2006/06/22 00:18:22 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.148 $
+Start Date: 2001/09/19
+Last Revision Date: $Date: 2006/06/22 00:18:22 $
+"""
+__author__ = "Tavis Rudd <tavis@damnsimple.com>"
+__revision__ = "$Revision: 1.148 $"[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.Parser import Parser, ParseError, specialVarRE, \
+ STATIC_CACHE, REFRESH_CACHE, SET_LOCAL, SET_GLOBAL,SET_MODULE
+from Cheetah.Utils.Indenter import indentize # an undocumented preprocessor
+from Cheetah import ErrorCatchers
+from Cheetah import NameMapper
+
+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,
+ # should shorter str constant chunks be printed using repr rather than ''' quotes
+ 'reprShortStrConstants': True,
+ 'reprNewlineThreshold':3,
+ '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,
+
+
+ ## 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
+
+ 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
+ if self.setting('reprShortStrConstants') and \
+ strConst.count('\n') < self.setting('reprNewlineThreshold'):
+ self.addWriteChunk( repr(strConst).replace('\\012','\\n'))
+ else:
+ strConst = strConst.replace('\\','\\\\').replace("'''","'\'\'\'")
+ if strConst[0] == "'":
+ strConst = '\\' + strConst
+ if strConst[-1] == "'":
+ strConst = strConst[:-1] + '\\' + strConst[-1]
+
+ self.addWriteChunk("'''" + strConst + "'''" )
+
+ 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())
+
+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__')
+ 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='+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 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
+
+ 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 = [str(methGen) 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 NameMapper.C_VERSION:
+ # disable warning noise as Cobbler is noarch, hence no C version -- mdehaan
+ #
+ # if not sys.platform.startswith('java'):
+ # warnings.warn(
+ # "\nYou don't have the C version of NameMapper installed! "
+ # "I'm disabling Cheetah's useStackFrames option as it is "
+ # "painfully slow with the Python version of NameMapper. "
+ # "You should get a copy of Cheetah with the compiled C version of NameMapper."
+ # )
+ self.setSetting('useStackFrames', False)
+
+ self._compiled = False
+ self._moduleName = moduleName
+ if not mainClassName:
+ self._mainClassName = moduleName
+ else:
+ self._mainClassName = mainClassName
+ self._mainMethodNameArg = mainMethodName
+ if mainMethodName:
+ self.setSetting('mainMethodName', mainMethodName)
+ self._baseclassName = baseclassName
+
+ self._filePath = None
+ self._fileMtime = None
+
+ if source and file:
+ raise TypeError("Cannot compile from a source string AND file.")
+ elif isinstance(file, types.StringType) or isinstance(file, types.UnicodeType): # it's a filename.
+
+ f = open(file) # Raises IOError.
+ source = f.read()
+ f.close()
+ self._filePath = file
+ self._fileMtime = os.path.getmtime(file)
+ elif hasattr(file, 'read'):
+ source = file.read() # Can't set filename or mtime--they're not accessible.
+ elif file:
+ raise TypeError("'file' argument must be a filename string or file-like object")
+
+
+ if self._filePath:
+ self._fileDirName, self._fileBaseName = os.path.split(self._filePath)
+ self._fileBaseNameRoot, self._fileBaseNameExt = \
+ os.path.splitext(self._fileBaseName)
+
+ if not (isinstance(source, str) or isinstance(source, unicode)):
+ source = str( source )
+ # by converting to string here we allow objects such as other Templates
+ # to be passed in
+
+ # Handle the #indent directive by converting it to other directives.
+ # (Over the long term we'll make it a real directive.)
+ if source == "":
+ warnings.warn("You supplied an empty string for the source!", )
+
+ if source.find('#indent') != -1: #@@TR: undocumented hack
+ source = indentize(source)
+
+ self._parser = self.parserClass(source, filename=self._filePath, compiler=self)
+ self._setupCompilerState()
+
+ def __getattr__(self, name):
+ """Provide one-way access to the methods and attributes of the
+ ClassCompiler, and thereby the MethodCompilers as well.
+
+ WARNING: Use .setMethods to assign the attributes of the ClassCompiler
+ 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._activeClassesList and hasattr(self._activeClassesList[-1], name):
+ return getattr(self._activeClassesList[-1], name)
+ else:
+ raise AttributeError, name
+
+ def _initializeSettings(self):
+ self.updateSettings(copy.deepcopy(DEFAULT_COMPILER_SETTINGS))
+
+ def _setupCompilerState(self):
+ self._activeClassesList = []
+ self._finishedClassesList = [] # listed by ordered
+ self._finishedClassIndex = {} # listed by name
+ self._moduleDef = None
+ self._moduleShBang = '#!/usr/bin/env python'
+ self._moduleEncoding = 'ascii'
+ self._moduleEncodingStr = ''
+ self._moduleHeaderLines = []
+ self._moduleDocStringLines = []
+ self._specialVars = {}
+ self._importStatements = [
+ "import sys",
+ "import os",
+ "import os.path",
+ "from os.path import getmtime, exists",
+ "import time",
+ "import types",
+ "import __builtin__",
+ "from Cheetah.Version import MinCompatibleVersion as RequiredCheetahVersion",
+ "from Cheetah.Version import MinCompatibleVersionTuple as RequiredCheetahVersionTuple",
+ "from Cheetah.Template import Template",
+ "from Cheetah.DummyTransaction import DummyTransaction",
+ "from Cheetah.NameMapper import NotFound, valueForName, valueFromSearchList, valueFromFrameOrSearchList",
+ "from Cheetah.CacheRegion import CacheRegion",
+ "import Cheetah.Filters as Filters",
+ "import Cheetah.ErrorCatchers as ErrorCatchers",
+ ]
+
+ self._importedVarNames = ['sys',
+ 'os',
+ 'os.path',
+ 'time',
+ 'types',
+ 'Template',
+ 'DummyTransaction',
+ 'NotFound',
+ 'Filters',
+ 'ErrorCatchers',
+ 'CacheRegion',
+ ]
+
+ self._moduleConstants = [
+ "try:",
+ " True, False",
+ "except NameError:",
+ " True, False = (1==1), (1==0)",
+ "VFFSL=valueFromFrameOrSearchList",
+ "VFSL=valueFromSearchList",
+ "VFN=valueForName",
+ "currentTime=time.time",
+ ]
+
+ def compile(self):
+ classCompiler = self._spawnClassCompiler(self._mainClassName)
+ if self._baseclassName:
+ classCompiler.setBaseClass(self._baseclassName)
+ self._addActiveClassCompiler(classCompiler)
+ self._parser.parse()
+ self._swallowClassCompiler(self._popActiveClassCompiler())
+ self._compiled = True
+ self._parser.cleanup()
+
+ def _spawnClassCompiler(self, className, klass=None):
+ if klass is None:
+ klass = self.classCompilerClass
+ classCompiler = klass(className,
+ moduleCompiler=self,
+ mainMethodName=self.setting('mainMethodName'),
+ fileName=self._filePath,
+ settingsManager=self,
+ )
+ return classCompiler
+
+ def _addActiveClassCompiler(self, classCompiler):
+ self._activeClassesList.append(classCompiler)
+
+ def _getActiveClassCompiler(self):
+ return self._activeClassesList[-1]
+
+ def _popActiveClassCompiler(self):
+ return self._activeClassesList.pop()
+
+ def _swallowClassCompiler(self, classCompiler):
+ classCompiler.cleanupState()
+ self._finishedClassesList.append( classCompiler )
+ self._finishedClassIndex[classCompiler.className()] = classCompiler
+ return classCompiler
+
+ def _finishedClasses(self):
+ return self._finishedClassesList
+
+ def importedVarNames(self):
+ return self._importedVarNames
+
+ def addImportedVarNames(self, varNames):
+ self._importedVarNames.extend(varNames)
+
+ ## methods for adding stuff to the module and class definitions
+
+ def setBaseClass(self, baseClassName):
+ if self._mainMethodNameArg:
+ self.setMainMethodName(self._mainMethodNameArg)
+ else:
+ self.setMainMethodName(self.setting('mainMethodNameForSubclasses'))
+
+ if self.setting('handlerForExtendsDirective'):
+ handler = self.setting('handlerForExtendsDirective')
+ baseClassName = handler(compiler=self, baseClassName=baseClassName)
+ self._getActiveClassCompiler().setBaseClass(baseClassName)
+ elif (not self.setting('autoImportForExtendsDirective')
+ or baseClassName=='object' or baseClassName in self.importedVarNames()):
+ self._getActiveClassCompiler().setBaseClass(baseClassName)
+ # no need to import
+ else:
+ ##################################################
+ ## If the #extends directive contains a classname or modulename that isn't
+ # in self.importedVarNames() already, we assume that we need to add
+ # an implied 'from ModName import ClassName' where ModName == ClassName.
+ # - This is the case in WebKit servlet modules.
+ # - We also assume that the final . separates the classname from the
+ # module name. This might break if people do something really fancy
+ # with their dots and namespaces.
+ chunks = baseClassName.split('.')
+ if len(chunks)==1:
+ self._getActiveClassCompiler().setBaseClass(baseClassName)
+ if baseClassName not in self.importedVarNames():
+ modName = baseClassName
+ # we assume the class name to be the module name
+ # and that it's not a builtin:
+ importStatement = "from %s import %s" % (modName, baseClassName)
+ self.addImportStatement(importStatement)
+ self.addImportedVarNames( [baseClassName,] )
+ else:
+ needToAddImport = True
+ modName = chunks[0]
+ #print chunks, ':', self.importedVarNames()
+ for chunk in chunks[1:-1]:
+ if modName in self.importedVarNames():
+ needToAddImport = False
+ finalBaseClassName = baseClassName.replace(modName+'.', '')
+ self._getActiveClassCompiler().setBaseClass(finalBaseClassName)
+ break
+ else:
+ modName += '.'+chunk
+ if needToAddImport:
+ modName, finalClassName = '.'.join(chunks[:-1]), chunks[-1]
+ #if finalClassName != chunks[:-1][-1]:
+ if finalClassName != chunks[-2]:
+ # we assume the class name to be the module name
+ modName = '.'.join(chunks)
+ self._getActiveClassCompiler().setBaseClass(finalClassName)
+ importStatement = "from %s import %s" % (modName, finalClassName)
+ self.addImportStatement(importStatement)
+ self.addImportedVarNames( [finalClassName,] )
+
+ def setCompilerSetting(self, key, valueExpr):
+ self.setSetting(key, eval(valueExpr) )
+ self._parser.configureParser()
+
+ def setCompilerSettings(self, keywords, settingsStr):
+ KWs = keywords
+ merge = True
+ if 'nomerge' in KWs:
+ merge = False
+
+ if 'reset' in KWs:
+ # @@TR: this is actually caught by the parser at the moment.
+ # subject to change in the future
+ self._initializeSettings()
+ self._parser.configureParser()
+ return
+ elif 'python' in KWs:
+ settingsReader = self.updateSettingsFromPySrcStr
+ # this comes from SettingsManager
+ else:
+ # this comes from SettingsManager
+ settingsReader = self.updateSettingsFromConfigStr
+
+ settingsReader(settingsStr)
+ self._parser.configureParser()
+
+ def setShBang(self, shBang):
+ self._moduleShBang = shBang
+
+ def setModuleEncoding(self, encoding):
+ self._moduleEncoding = encoding
+ self._moduleEncodingStr = '# -*- coding: %s -*-' %encoding
+
+ def getModuleEncoding(self):
+ return self._moduleEncoding
+
+ def addModuleHeader(self, line):
+ """Adds a header comment to the top of the generated module.
+ """
+ self._moduleHeaderLines.append(line)
+
+ def addModuleDocString(self, line):
+ """Adds a line to the generated module docstring.
+ """
+ self._moduleDocStringLines.append(line)
+
+ def addModuleGlobal(self, line):
+ """Adds a line of global module code. It is inserted after the import
+ statements and Cheetah default module constants.
+ """
+ self._moduleConstants.append(line)
+
+ def addSpecialVar(self, basename, contents, includeUnderscores=True):
+ """Adds module __specialConstant__ to the module globals.
+ """
+ name = includeUnderscores and '__'+basename+'__' or basename
+ self._specialVars[name] = contents.strip()
+
+ def addImportStatement(self, impStatement):
+ self._importStatements.append(impStatement)
+
+ #@@TR 2005-01-01: there's almost certainly a cleaner way to do this!
+ importVarNames = impStatement[impStatement.find('import') + len('import'):].split(',')
+ importVarNames = [var.split()[-1] for var in importVarNames] # handles aliases
+ importVarNames = [var for var in importVarNames if var!='*']
+ self.addImportedVarNames(importVarNames) #used by #extend for auto-imports
+
+ def addAttribute(self, attribName, expr):
+ self._getActiveClassCompiler().addAttribute(attribName + ' =' + expr)
+
+ def addComment(self, comm):
+ if re.match(r'#+$',comm): # skip bar comments
+ return
+
+ specialVarMatch = specialVarRE.match(comm)
+ if specialVarMatch:
+ # @@TR: this is a bit hackish and is being replaced with
+ # #set module varName = ...
+ return self.addSpecialVar(specialVarMatch.group(1),
+ comm[specialVarMatch.end():])
+ elif comm.startswith('doc:'):
+ addLine = self.addMethDocString
+ comm = comm[len('doc:'):].strip()
+ elif comm.startswith('doc-method:'):
+ addLine = self.addMethDocString
+ comm = comm[len('doc-method:'):].strip()
+ elif comm.startswith('doc-module:'):
+ addLine = self.addModuleDocString
+ comm = comm[len('doc-module:'):].strip()
+ elif comm.startswith('doc-class:'):
+ addLine = self.addClassDocString
+ comm = comm[len('doc-class:'):].strip()
+ elif comm.startswith('header:'):
+ addLine = self.addModuleHeader
+ comm = comm[len('header:'):].strip()
+ else:
+ addLine = self.addMethComment
+
+ for line in comm.splitlines():
+ addLine(line)
+
+ ## methods for module code wrapping
+
+ def getModuleCode(self):
+ if not self._compiled:
+ self.compile()
+ if self._moduleDef:
+ return self._moduleDef
+ else:
+ return self.wrapModuleDef()
+
+ __str__ = getModuleCode
+
+ def wrapModuleDef(self):
+ self.addSpecialVar('CHEETAH_docstring', self.setting('defDocStrMsg'))
+ self.addModuleGlobal('__CHEETAH_version__ = %r'%Version)
+ self.addModuleGlobal('__CHEETAH_versionTuple__ = %r'%(VersionTuple,))
+ self.addModuleGlobal('__CHEETAH_genTime__ = %r'%time.time())
+ self.addModuleGlobal('__CHEETAH_genTimestamp__ = %r'%self.timestamp())
+ if self._filePath:
+ timestamp = self.timestamp(self._fileMtime)
+ self.addModuleGlobal('__CHEETAH_src__ = %r'%self._filePath)
+ self.addModuleGlobal('__CHEETAH_srcLastModified__ = %r'%timestamp)
+ else:
+ self.addModuleGlobal('__CHEETAH_src__ = None')
+ self.addModuleGlobal('__CHEETAH_srcLastModified__ = None')
+
+ moduleDef = """%(header)s
+%(docstring)s
+
+##################################################
+## DEPENDENCIES
+%(imports)s
+
+##################################################
+## MODULE CONSTANTS
+%(constants)s
+%(specialVars)s
+
+if __CHEETAH_versionTuple__ < RequiredCheetahVersionTuple:
+ raise AssertionError(
+ 'This template was compiled with Cheetah version'
+ ' %%s. Templates compiled before version %%s must be recompiled.'%%(
+ __CHEETAH_version__, RequiredCheetahVersion))
+
+##################################################
+## CLASSES
+
+%(classes)s
+
+## END CLASS DEFINITION
+
+if not hasattr(%(mainClassName)s, '_initCheetahAttributes'):
+ templateAPIClass = getattr(%(mainClassName)s, '_CHEETAH_templateClass', Template)
+ templateAPIClass._addCheetahPlumbingCodeToClass(%(mainClassName)s)
+
+%(footer)s
+""" % {'header':self.moduleHeader(),
+ 'docstring':self.moduleDocstring(),
+ 'specialVars':self.specialVars(),
+ 'imports':self.importStatements(),
+ 'constants':self.moduleConstants(),
+ 'classes':self.classDefs(),
+ 'footer':self.moduleFooter(),
+ 'mainClassName':self._mainClassName,
+ }
+
+ self._moduleDef = moduleDef
+ return moduleDef
+
+ def timestamp(self, theTime=None):
+ if not theTime:
+ theTime = time.time()
+ return time.asctime(time.localtime(theTime))
+
+ def moduleHeader(self):
+ header = self._moduleShBang + '\n'
+ header += self._moduleEncodingStr + '\n'
+ if self._moduleHeaderLines:
+ offSet = self.setting('commentOffset')
+
+ header += (
+ '#' + ' '*offSet +
+ ('\n#'+ ' '*offSet).join(self._moduleHeaderLines) + '\n')
+
+ return header
+
+ def moduleDocstring(self):
+ if not self._moduleDocStringLines:
+ return ''
+
+ return ('"""' +
+ '\n'.join(self._moduleDocStringLines) +
+ '\n"""\n')
+
+ def specialVars(self):
+ chunks = []
+ theVars = self._specialVars
+ keys = theVars.keys()
+ keys.sort()
+ for key in keys:
+ chunks.append(key + ' = ' + repr(theVars[key]) )
+ return '\n'.join(chunks)
+
+ def importStatements(self):
+ return '\n'.join(self._importStatements)
+
+ def moduleConstants(self):
+ return '\n'.join(self._moduleConstants)
+
+ def classDefs(self):
+ classDefs = [str(klass) for klass in self._finishedClasses() ]
+ return '\n\n'.join(classDefs)
+
+ def moduleFooter(self):
+ return """
+# CHEETAH was developed by Tavis Rudd and Mike Orr
+# with code, advice and input from many other volunteers.
+# For more information visit http://www.CheetahTemplate.org/
+
+##################################################
+## if run from command line:
+if __name__ == '__main__':
+ from Cheetah.TemplateCmdLineIface import CmdLineIface
+ CmdLineIface(templateObj=%(className)s()).run()
+
+""" % {'className':self._mainClassName}
+
+
+##################################################
+## Make Compiler an alias for ModuleCompiler
+
+Compiler = ModuleCompiler