[3] | 1 | ''' |
---|
| 2 | Compiler classes for Cheetah: |
---|
| 3 | ModuleCompiler aka 'Compiler' |
---|
| 4 | ClassCompiler |
---|
| 5 | MethodCompiler |
---|
| 6 | |
---|
| 7 | If you are trying to grok this code start with ModuleCompiler.__init__, |
---|
| 8 | ModuleCompiler.compile, and ModuleCompiler.__getattr__. |
---|
| 9 | ''' |
---|
| 10 | |
---|
| 11 | import sys |
---|
| 12 | import os |
---|
| 13 | import os.path |
---|
| 14 | from os.path import getmtime, exists |
---|
| 15 | import re |
---|
| 16 | import types |
---|
| 17 | import time |
---|
| 18 | import random |
---|
| 19 | import warnings |
---|
| 20 | import copy |
---|
| 21 | |
---|
| 22 | from Cheetah.Version import Version, VersionTuple |
---|
| 23 | from Cheetah.SettingsManager import SettingsManager |
---|
| 24 | from Cheetah.Utils.Indenter import indentize # an undocumented preprocessor |
---|
| 25 | from Cheetah import ErrorCatchers |
---|
| 26 | from Cheetah import NameMapper |
---|
| 27 | from Cheetah.Parser import Parser, ParseError, specialVarRE, \ |
---|
| 28 | STATIC_CACHE, REFRESH_CACHE, SET_LOCAL, SET_GLOBAL,SET_MODULE, \ |
---|
| 29 | unicodeDirectiveRE, encodingDirectiveRE,escapedNewlineRE |
---|
| 30 | |
---|
| 31 | from Cheetah.NameMapper import NotFound, valueForName, valueFromSearchList, valueFromFrameOrSearchList |
---|
| 32 | VFFSL=valueFromFrameOrSearchList |
---|
| 33 | VFSL=valueFromSearchList |
---|
| 34 | VFN=valueForName |
---|
| 35 | currentTime=time.time |
---|
| 36 | |
---|
| 37 | class Error(Exception): pass |
---|
| 38 | |
---|
| 39 | # Settings format: (key, default, docstring) |
---|
| 40 | _DEFAULT_COMPILER_SETTINGS = [ |
---|
| 41 | ('useNameMapper', True, 'Enable NameMapper for dotted notation and searchList support'), |
---|
| 42 | ('useSearchList', True, 'Enable the searchList, requires useNameMapper=True, if disabled, first portion of the $variable is a global, builtin, or local variable that doesn\'t need looking up in the searchList'), |
---|
| 43 | ('allowSearchListAsMethArg', True, ''), |
---|
| 44 | ('useAutocalling', True, 'Detect and call callable objects in searchList, requires useNameMapper=True'), |
---|
| 45 | ('useStackFrames', True, 'Used for NameMapper.valueFromFrameOrSearchList rather than NameMapper.valueFromSearchList'), |
---|
| 46 | ('useErrorCatcher', False, 'Turn on the #errorCatcher directive for catching NameMapper errors, etc'), |
---|
| 47 | ('alwaysFilterNone', True, 'Filter out None prior to calling the #filter'), |
---|
| 48 | ('useFilters', True, 'If False, pass output through str()'), |
---|
| 49 | ('includeRawExprInFilterArgs', True, ''), |
---|
| 50 | ('useLegacyImportMode', True, 'All #import statements are relocated to the top of the generated Python module'), |
---|
| 51 | ('prioritizeSearchListOverSelf', False, 'When iterating the searchList, look into the searchList passed into the initializer instead of Template members first'), |
---|
| 52 | |
---|
| 53 | ('autoAssignDummyTransactionToSelf', False, ''), |
---|
| 54 | ('useKWsDictArgForPassingTrans', True, ''), |
---|
| 55 | |
---|
| 56 | ('commentOffset', 1, ''), |
---|
| 57 | ('outputRowColComments', True, ''), |
---|
| 58 | ('includeBlockMarkers', False, 'Wrap #block\'s in a comment in the template\'s output'), |
---|
| 59 | ('blockMarkerStart', ('\n<!-- START BLOCK: ',' -->\n'), ''), |
---|
| 60 | ('blockMarkerEnd', ('\n<!-- END BLOCK: ',' -->\n'), ''), |
---|
| 61 | ('defDocStrMsg', 'Autogenerated by Cheetah: The Python-Powered Template Engine', ''), |
---|
| 62 | ('setup__str__method', False, ''), |
---|
| 63 | ('mainMethodName', 'respond', ''), |
---|
| 64 | ('mainMethodNameForSubclasses', 'writeBody', ''), |
---|
| 65 | ('indentationStep', ' ' * 4, ''), |
---|
| 66 | ('initialMethIndentLevel', 2, ''), |
---|
| 67 | ('monitorSrcFile', False, ''), |
---|
| 68 | ('outputMethodsBeforeAttributes', True, ''), |
---|
| 69 | ('addTimestampsToCompilerOutput', True, ''), |
---|
| 70 | |
---|
| 71 | ## Customizing the #extends directive |
---|
| 72 | ('autoImportForExtendsDirective', True, ''), |
---|
| 73 | ('handlerForExtendsDirective', None, ''), |
---|
| 74 | |
---|
| 75 | ('disabledDirectives', [], 'List of directive keys to disable (without starting "#")'), |
---|
| 76 | ('enabledDirectives', [], 'List of directive keys to enable (without starting "#")'), |
---|
| 77 | ('disabledDirectiveHooks', [], 'callable(parser, directiveKey)'), |
---|
| 78 | ('preparseDirectiveHooks', [], 'callable(parser, directiveKey)'), |
---|
| 79 | ('postparseDirectiveHooks', [], 'callable(parser, directiveKey)'), |
---|
| 80 | ('preparsePlaceholderHooks', [], 'callable(parser)'), |
---|
| 81 | ('postparsePlaceholderHooks', [], 'callable(parser)'), |
---|
| 82 | ('expressionFilterHooks', [], '''callable(parser, expr, exprType, rawExpr=None, startPos=None), exprType is the name of the directive, "psp" or "placeholder" The filters *must* return the expr or raise an expression, they can modify the expr if needed'''), |
---|
| 83 | ('templateMetaclass', None, 'Strictly optional, only will work with new-style basecalsses as well'), |
---|
| 84 | ('i18NFunctionName', 'self.i18n', ''), |
---|
| 85 | |
---|
| 86 | ('cheetahVarStartToken', '$', ''), |
---|
| 87 | ('commentStartToken', '##', ''), |
---|
| 88 | ('multiLineCommentStartToken', '#*', ''), |
---|
| 89 | ('multiLineCommentEndToken', '*#', ''), |
---|
| 90 | ('gobbleWhitespaceAroundMultiLineComments', True, ''), |
---|
| 91 | ('directiveStartToken', '#', ''), |
---|
| 92 | ('directiveEndToken', '#', ''), |
---|
| 93 | ('allowWhitespaceAfterDirectiveStartToken', False, ''), |
---|
| 94 | ('PSPStartToken', '<%', ''), |
---|
| 95 | ('PSPEndToken', '%>', ''), |
---|
| 96 | ('EOLSlurpToken', '#', ''), |
---|
| 97 | ('gettextTokens', ["_", "N_", "ngettext"], ''), |
---|
| 98 | ('allowExpressionsInExtendsDirective', False, ''), |
---|
| 99 | ('allowEmptySingleLineMethods', False, ''), |
---|
| 100 | ('allowNestedDefScopes', True, ''), |
---|
| 101 | ('allowPlaceholderFilterArgs', True, ''), |
---|
| 102 | ] |
---|
| 103 | |
---|
| 104 | DEFAULT_COMPILER_SETTINGS = dict([(v[0], v[1]) for v in _DEFAULT_COMPILER_SETTINGS]) |
---|
| 105 | |
---|
| 106 | |
---|
| 107 | |
---|
| 108 | class GenUtils(object): |
---|
| 109 | """An abstract baseclass for the Compiler classes that provides methods that |
---|
| 110 | perform generic utility functions or generate pieces of output code from |
---|
| 111 | information passed in by the Parser baseclass. These methods don't do any |
---|
| 112 | parsing themselves. |
---|
| 113 | """ |
---|
| 114 | |
---|
| 115 | def genTimeInterval(self, timeString): |
---|
| 116 | ##@@ TR: need to add some error handling here |
---|
| 117 | if timeString[-1] == 's': |
---|
| 118 | interval = float(timeString[:-1]) |
---|
| 119 | elif timeString[-1] == 'm': |
---|
| 120 | interval = float(timeString[:-1])*60 |
---|
| 121 | elif timeString[-1] == 'h': |
---|
| 122 | interval = float(timeString[:-1])*60*60 |
---|
| 123 | elif timeString[-1] == 'd': |
---|
| 124 | interval = float(timeString[:-1])*60*60*24 |
---|
| 125 | elif timeString[-1] == 'w': |
---|
| 126 | interval = float(timeString[:-1])*60*60*24*7 |
---|
| 127 | else: # default to minutes |
---|
| 128 | interval = float(timeString)*60 |
---|
| 129 | return interval |
---|
| 130 | |
---|
| 131 | def genCacheInfo(self, cacheTokenParts): |
---|
| 132 | """Decipher a placeholder cachetoken |
---|
| 133 | """ |
---|
| 134 | cacheInfo = {} |
---|
| 135 | if cacheTokenParts['REFRESH_CACHE']: |
---|
| 136 | cacheInfo['type'] = REFRESH_CACHE |
---|
| 137 | cacheInfo['interval'] = self.genTimeInterval(cacheTokenParts['interval']) |
---|
| 138 | elif cacheTokenParts['STATIC_CACHE']: |
---|
| 139 | cacheInfo['type'] = STATIC_CACHE |
---|
| 140 | return cacheInfo # is empty if no cache |
---|
| 141 | |
---|
| 142 | def genCacheInfoFromArgList(self, argList): |
---|
| 143 | cacheInfo = {'type':REFRESH_CACHE} |
---|
| 144 | for key, val in argList: |
---|
| 145 | if val[0] in '"\'': |
---|
| 146 | val = val[1:-1] |
---|
| 147 | |
---|
| 148 | if key == 'timer': |
---|
| 149 | key = 'interval' |
---|
| 150 | val = self.genTimeInterval(val) |
---|
| 151 | |
---|
| 152 | cacheInfo[key] = val |
---|
| 153 | return cacheInfo |
---|
| 154 | |
---|
| 155 | def genCheetahVar(self, nameChunks, plain=False): |
---|
| 156 | if nameChunks[0][0] in self.setting('gettextTokens'): |
---|
| 157 | self.addGetTextVar(nameChunks) |
---|
| 158 | if self.setting('useNameMapper') and not plain: |
---|
| 159 | return self.genNameMapperVar(nameChunks) |
---|
| 160 | else: |
---|
| 161 | return self.genPlainVar(nameChunks) |
---|
| 162 | |
---|
| 163 | def addGetTextVar(self, nameChunks): |
---|
| 164 | """Output something that gettext can recognize. |
---|
| 165 | |
---|
| 166 | This is a harmless side effect necessary to make gettext work when it |
---|
| 167 | is scanning compiled templates for strings marked for translation. |
---|
| 168 | |
---|
| 169 | @@TR: another marginally more efficient approach would be to put the |
---|
| 170 | output in a dummy method that is never called. |
---|
| 171 | """ |
---|
| 172 | # @@TR: this should be in the compiler not here |
---|
| 173 | self.addChunk("if False:") |
---|
| 174 | self.indent() |
---|
| 175 | self.addChunk(self.genPlainVar(nameChunks[:])) |
---|
| 176 | self.dedent() |
---|
| 177 | |
---|
| 178 | def genPlainVar(self, nameChunks): |
---|
| 179 | """Generate Python code for a Cheetah $var without using NameMapper |
---|
| 180 | (Unified Dotted Notation with the SearchList). |
---|
| 181 | """ |
---|
| 182 | nameChunks.reverse() |
---|
| 183 | chunk = nameChunks.pop() |
---|
| 184 | pythonCode = chunk[0] + chunk[2] |
---|
| 185 | while nameChunks: |
---|
| 186 | chunk = nameChunks.pop() |
---|
| 187 | pythonCode = (pythonCode + '.' + chunk[0] + chunk[2]) |
---|
| 188 | return pythonCode |
---|
| 189 | |
---|
| 190 | def genNameMapperVar(self, nameChunks): |
---|
| 191 | """Generate valid Python code for a Cheetah $var, using NameMapper |
---|
| 192 | (Unified Dotted Notation with the SearchList). |
---|
| 193 | |
---|
| 194 | nameChunks = list of var subcomponents represented as tuples |
---|
| 195 | [ (name,useAC,remainderOfExpr), |
---|
| 196 | ] |
---|
| 197 | where: |
---|
| 198 | name = the dotted name base |
---|
| 199 | useAC = where NameMapper should use autocalling on namemapperPart |
---|
| 200 | remainderOfExpr = any arglist, index, or slice |
---|
| 201 | |
---|
| 202 | If remainderOfExpr contains a call arglist (e.g. '(1234)') then useAC |
---|
| 203 | is False, otherwise it defaults to True. It is overridden by the global |
---|
| 204 | setting 'useAutocalling' if this setting is False. |
---|
| 205 | |
---|
| 206 | EXAMPLE |
---|
| 207 | ------------------------------------------------------------------------ |
---|
| 208 | if the raw Cheetah Var is |
---|
| 209 | $a.b.c[1].d().x.y.z |
---|
| 210 | |
---|
| 211 | nameChunks is the list |
---|
| 212 | [ ('a.b.c',True,'[1]'), # A |
---|
| 213 | ('d',False,'()'), # B |
---|
| 214 | ('x.y.z',True,''), # C |
---|
| 215 | ] |
---|
| 216 | |
---|
| 217 | When this method is fed the list above it returns |
---|
| 218 | VFN(VFN(VFFSL(SL, 'a.b.c',True)[1], 'd',False)(), 'x.y.z',True) |
---|
| 219 | which can be represented as |
---|
| 220 | VFN(B`, name=C[0], executeCallables=(useAC and C[1]))C[2] |
---|
| 221 | where: |
---|
| 222 | VFN = NameMapper.valueForName |
---|
| 223 | VFFSL = NameMapper.valueFromFrameOrSearchList |
---|
| 224 | VFSL = NameMapper.valueFromSearchList # optionally used instead of VFFSL |
---|
| 225 | SL = self.searchList() |
---|
| 226 | useAC = self.setting('useAutocalling') # True in this example |
---|
| 227 | |
---|
| 228 | A = ('a.b.c',True,'[1]') |
---|
| 229 | B = ('d',False,'()') |
---|
| 230 | C = ('x.y.z',True,'') |
---|
| 231 | |
---|
| 232 | C` = VFN( VFN( VFFSL(SL, 'a.b.c',True)[1], |
---|
| 233 | 'd',False)(), |
---|
| 234 | 'x.y.z',True) |
---|
| 235 | = VFN(B`, name='x.y.z', executeCallables=True) |
---|
| 236 | |
---|
| 237 | B` = VFN(A`, name=B[0], executeCallables=(useAC and B[1]))B[2] |
---|
| 238 | A` = VFFSL(SL, name=A[0], executeCallables=(useAC and A[1]))A[2] |
---|
| 239 | |
---|
| 240 | |
---|
| 241 | Note, if the compiler setting useStackFrames=False (default is true) |
---|
| 242 | then |
---|
| 243 | A` = VFSL([locals()]+SL+[globals(), __builtin__], name=A[0], executeCallables=(useAC and A[1]))A[2] |
---|
| 244 | This option allows Cheetah to be used with Psyco, which doesn't support |
---|
| 245 | stack frame introspection. |
---|
| 246 | """ |
---|
| 247 | defaultUseAC = self.setting('useAutocalling') |
---|
| 248 | useSearchList = self.setting('useSearchList') |
---|
| 249 | |
---|
| 250 | nameChunks.reverse() |
---|
| 251 | name, useAC, remainder = nameChunks.pop() |
---|
| 252 | |
---|
| 253 | if not useSearchList: |
---|
| 254 | firstDotIdx = name.find('.') |
---|
| 255 | if firstDotIdx != -1 and firstDotIdx < len(name): |
---|
| 256 | beforeFirstDot, afterDot = name[:firstDotIdx], name[firstDotIdx+1:] |
---|
| 257 | pythonCode = ('VFN(' + beforeFirstDot + |
---|
| 258 | ',"' + afterDot + |
---|
| 259 | '",' + repr(defaultUseAC and useAC) + ')' |
---|
| 260 | + remainder) |
---|
| 261 | else: |
---|
| 262 | pythonCode = name+remainder |
---|
| 263 | elif self.setting('useStackFrames'): |
---|
| 264 | pythonCode = ('VFFSL(SL,' |
---|
| 265 | '"'+ name + '",' |
---|
| 266 | + repr(defaultUseAC and useAC) + ')' |
---|
| 267 | + remainder) |
---|
| 268 | else: |
---|
| 269 | pythonCode = ('VFSL([locals()]+SL+[globals(), __builtin__],' |
---|
| 270 | '"'+ name + '",' |
---|
| 271 | + repr(defaultUseAC and useAC) + ')' |
---|
| 272 | + remainder) |
---|
| 273 | ## |
---|
| 274 | while nameChunks: |
---|
| 275 | name, useAC, remainder = nameChunks.pop() |
---|
| 276 | pythonCode = ('VFN(' + pythonCode + |
---|
| 277 | ',"' + name + |
---|
| 278 | '",' + repr(defaultUseAC and useAC) + ')' |
---|
| 279 | + remainder) |
---|
| 280 | return pythonCode |
---|
| 281 | |
---|
| 282 | ################################################## |
---|
| 283 | ## METHOD COMPILERS |
---|
| 284 | |
---|
| 285 | class MethodCompiler(GenUtils): |
---|
| 286 | def __init__(self, methodName, classCompiler, |
---|
| 287 | initialMethodComment=None, |
---|
| 288 | decorators=None): |
---|
| 289 | self._settingsManager = classCompiler |
---|
| 290 | self._classCompiler = classCompiler |
---|
| 291 | self._moduleCompiler = classCompiler._moduleCompiler |
---|
| 292 | self._methodName = methodName |
---|
| 293 | self._initialMethodComment = initialMethodComment |
---|
| 294 | self._setupState() |
---|
| 295 | self._decorators = decorators or [] |
---|
| 296 | |
---|
| 297 | def setting(self, key): |
---|
| 298 | return self._settingsManager.setting(key) |
---|
| 299 | |
---|
| 300 | def _setupState(self): |
---|
| 301 | self._indent = self.setting('indentationStep') |
---|
| 302 | self._indentLev = self.setting('initialMethIndentLevel') |
---|
| 303 | self._pendingStrConstChunks = [] |
---|
| 304 | self._methodSignature = None |
---|
| 305 | self._methodDef = None |
---|
| 306 | self._docStringLines = [] |
---|
| 307 | self._methodBodyChunks = [] |
---|
| 308 | |
---|
| 309 | self._cacheRegionsStack = [] |
---|
| 310 | self._callRegionsStack = [] |
---|
| 311 | self._captureRegionsStack = [] |
---|
| 312 | self._filterRegionsStack = [] |
---|
| 313 | |
---|
| 314 | self._isErrorCatcherOn = False |
---|
| 315 | |
---|
| 316 | self._hasReturnStatement = False |
---|
| 317 | self._isGenerator = False |
---|
| 318 | |
---|
| 319 | |
---|
| 320 | def cleanupState(self): |
---|
| 321 | """Called by the containing class compiler instance |
---|
| 322 | """ |
---|
| 323 | pass |
---|
| 324 | |
---|
| 325 | def methodName(self): |
---|
| 326 | return self._methodName |
---|
| 327 | |
---|
| 328 | def setMethodName(self, name): |
---|
| 329 | self._methodName = name |
---|
| 330 | |
---|
| 331 | ## methods for managing indentation |
---|
| 332 | |
---|
| 333 | def indentation(self): |
---|
| 334 | return self._indent * self._indentLev |
---|
| 335 | |
---|
| 336 | def indent(self): |
---|
| 337 | self._indentLev +=1 |
---|
| 338 | |
---|
| 339 | def dedent(self): |
---|
| 340 | if self._indentLev: |
---|
| 341 | self._indentLev -=1 |
---|
| 342 | else: |
---|
| 343 | raise Error('Attempt to dedent when the indentLev is 0') |
---|
| 344 | |
---|
| 345 | ## methods for final code wrapping |
---|
| 346 | |
---|
| 347 | def methodDef(self): |
---|
| 348 | if self._methodDef: |
---|
| 349 | return self._methodDef |
---|
| 350 | else: |
---|
| 351 | return self.wrapCode() |
---|
| 352 | |
---|
| 353 | __str__ = methodDef |
---|
| 354 | __unicode__ = methodDef |
---|
| 355 | |
---|
| 356 | def wrapCode(self): |
---|
| 357 | self.commitStrConst() |
---|
| 358 | methodDefChunks = ( |
---|
| 359 | self.methodSignature(), |
---|
| 360 | '\n', |
---|
| 361 | self.docString(), |
---|
| 362 | self.methodBody() ) |
---|
| 363 | methodDef = ''.join(methodDefChunks) |
---|
| 364 | self._methodDef = methodDef |
---|
| 365 | return methodDef |
---|
| 366 | |
---|
| 367 | def methodSignature(self): |
---|
| 368 | return self._indent + self._methodSignature + ':' |
---|
| 369 | |
---|
| 370 | def setMethodSignature(self, signature): |
---|
| 371 | self._methodSignature = signature |
---|
| 372 | |
---|
| 373 | def methodBody(self): |
---|
| 374 | return ''.join( self._methodBodyChunks ) |
---|
| 375 | |
---|
| 376 | def docString(self): |
---|
| 377 | if not self._docStringLines: |
---|
| 378 | return '' |
---|
| 379 | |
---|
| 380 | ind = self._indent*2 |
---|
| 381 | docStr = (ind + '"""\n' + ind + |
---|
| 382 | ('\n' + ind).join([ln.replace('"""',"'''") for ln in self._docStringLines]) + |
---|
| 383 | '\n' + ind + '"""\n') |
---|
| 384 | return docStr |
---|
| 385 | |
---|
| 386 | ## methods for adding code |
---|
| 387 | def addMethDocString(self, line): |
---|
| 388 | self._docStringLines.append(line.replace('%','%%')) |
---|
| 389 | |
---|
| 390 | def addChunk(self, chunk): |
---|
| 391 | self.commitStrConst() |
---|
| 392 | chunk = "\n" + self.indentation() + chunk |
---|
| 393 | self._methodBodyChunks.append(chunk) |
---|
| 394 | |
---|
| 395 | def appendToPrevChunk(self, appendage): |
---|
| 396 | self._methodBodyChunks[-1] = self._methodBodyChunks[-1] + appendage |
---|
| 397 | |
---|
| 398 | def addWriteChunk(self, chunk): |
---|
| 399 | self.addChunk('write(' + chunk + ')') |
---|
| 400 | |
---|
| 401 | def addFilteredChunk(self, chunk, filterArgs=None, rawExpr=None, lineCol=None): |
---|
| 402 | if filterArgs is None: |
---|
| 403 | filterArgs = '' |
---|
| 404 | if self.setting('includeRawExprInFilterArgs') and rawExpr: |
---|
| 405 | filterArgs += ', rawExpr=%s'%repr(rawExpr) |
---|
| 406 | |
---|
| 407 | if self.setting('alwaysFilterNone'): |
---|
| 408 | if rawExpr and rawExpr.find('\n')==-1 and rawExpr.find('\r')==-1: |
---|
| 409 | self.addChunk("_v = %s # %r"%(chunk, rawExpr)) |
---|
| 410 | if lineCol: |
---|
| 411 | self.appendToPrevChunk(' on line %s, col %s'%lineCol) |
---|
| 412 | else: |
---|
| 413 | self.addChunk("_v = %s"%chunk) |
---|
| 414 | |
---|
| 415 | if self.setting('useFilters'): |
---|
| 416 | self.addChunk("if _v is not None: write(_filter(_v%s))"%filterArgs) |
---|
| 417 | else: |
---|
| 418 | self.addChunk("if _v is not None: write(str(_v))") |
---|
| 419 | else: |
---|
| 420 | if self.setting('useFilters'): |
---|
| 421 | self.addChunk("write(_filter(%s%s))"%(chunk,filterArgs)) |
---|
| 422 | else: |
---|
| 423 | self.addChunk("write(str(%s))"%chunk) |
---|
| 424 | |
---|
| 425 | def _appendToPrevStrConst(self, strConst): |
---|
| 426 | if self._pendingStrConstChunks: |
---|
| 427 | self._pendingStrConstChunks.append(strConst) |
---|
| 428 | else: |
---|
| 429 | self._pendingStrConstChunks = [strConst] |
---|
| 430 | |
---|
| 431 | def _unescapeCheetahVars(self, theString): |
---|
| 432 | """Unescape any escaped Cheetah \$vars in the string. |
---|
| 433 | """ |
---|
| 434 | |
---|
| 435 | token = self.setting('cheetahVarStartToken') |
---|
| 436 | return theString.replace('\\' + token, token) |
---|
| 437 | |
---|
| 438 | def _unescapeDirectives(self, theString): |
---|
| 439 | """Unescape any escaped Cheetah \$vars in the string. |
---|
| 440 | """ |
---|
| 441 | |
---|
| 442 | token = self.setting('directiveStartToken') |
---|
| 443 | return theString.replace('\\' + token, token) |
---|
| 444 | |
---|
| 445 | def commitStrConst(self): |
---|
| 446 | """Add the code for outputting the pending strConst without chopping off |
---|
| 447 | any whitespace from it. |
---|
| 448 | """ |
---|
| 449 | if self._pendingStrConstChunks: |
---|
| 450 | strConst = self._unescapeCheetahVars(''.join(self._pendingStrConstChunks)) |
---|
| 451 | strConst = self._unescapeDirectives(strConst) |
---|
| 452 | self._pendingStrConstChunks = [] |
---|
| 453 | if not strConst: |
---|
| 454 | return |
---|
| 455 | else: |
---|
| 456 | reprstr = repr(strConst).replace('\\012','\n') |
---|
| 457 | i = 0 |
---|
| 458 | out = [] |
---|
| 459 | if reprstr.startswith('u'): |
---|
| 460 | i = 1 |
---|
| 461 | out = ['u'] |
---|
| 462 | body = escapedNewlineRE.sub('\n', reprstr[i+1:-1]) |
---|
| 463 | |
---|
| 464 | if reprstr[i]=="'": |
---|
| 465 | out.append("'''") |
---|
| 466 | out.append(body) |
---|
| 467 | out.append("'''") |
---|
| 468 | else: |
---|
| 469 | out.append('"""') |
---|
| 470 | out.append(body) |
---|
| 471 | out.append('"""') |
---|
| 472 | self.addWriteChunk(''.join(out)) |
---|
| 473 | |
---|
| 474 | def handleWSBeforeDirective(self): |
---|
| 475 | """Truncate the pending strCont to the beginning of the current line. |
---|
| 476 | """ |
---|
| 477 | if self._pendingStrConstChunks: |
---|
| 478 | src = self._pendingStrConstChunks[-1] |
---|
| 479 | BOL = max(src.rfind('\n')+1, src.rfind('\r')+1, 0) |
---|
| 480 | if BOL < len(src): |
---|
| 481 | self._pendingStrConstChunks[-1] = src[:BOL] |
---|
| 482 | |
---|
| 483 | |
---|
| 484 | |
---|
| 485 | def isErrorCatcherOn(self): |
---|
| 486 | return self._isErrorCatcherOn |
---|
| 487 | |
---|
| 488 | def turnErrorCatcherOn(self): |
---|
| 489 | self._isErrorCatcherOn = True |
---|
| 490 | |
---|
| 491 | def turnErrorCatcherOff(self): |
---|
| 492 | self._isErrorCatcherOn = False |
---|
| 493 | |
---|
| 494 | # @@TR: consider merging the next two methods into one |
---|
| 495 | def addStrConst(self, strConst): |
---|
| 496 | self._appendToPrevStrConst(strConst) |
---|
| 497 | |
---|
| 498 | def addRawText(self, text): |
---|
| 499 | self.addStrConst(text) |
---|
| 500 | |
---|
| 501 | def addMethComment(self, comm): |
---|
| 502 | offSet = self.setting('commentOffset') |
---|
| 503 | self.addChunk('#' + ' '*offSet + comm) |
---|
| 504 | |
---|
| 505 | def addPlaceholder(self, expr, filterArgs, rawPlaceholder, |
---|
| 506 | cacheTokenParts, lineCol, |
---|
| 507 | silentMode=False): |
---|
| 508 | cacheInfo = self.genCacheInfo(cacheTokenParts) |
---|
| 509 | if cacheInfo: |
---|
| 510 | cacheInfo['ID'] = repr(rawPlaceholder)[1:-1] |
---|
| 511 | self.startCacheRegion(cacheInfo, lineCol, rawPlaceholder=rawPlaceholder) |
---|
| 512 | |
---|
| 513 | if self.isErrorCatcherOn(): |
---|
| 514 | methodName = self._classCompiler.addErrorCatcherCall( |
---|
| 515 | expr, rawCode=rawPlaceholder, lineCol=lineCol) |
---|
| 516 | expr = 'self.' + methodName + '(localsDict=locals())' |
---|
| 517 | |
---|
| 518 | if silentMode: |
---|
| 519 | self.addChunk('try:') |
---|
| 520 | self.indent() |
---|
| 521 | self.addFilteredChunk(expr, filterArgs, rawPlaceholder, lineCol=lineCol) |
---|
| 522 | self.dedent() |
---|
| 523 | self.addChunk('except NotFound: pass') |
---|
| 524 | else: |
---|
| 525 | self.addFilteredChunk(expr, filterArgs, rawPlaceholder, lineCol=lineCol) |
---|
| 526 | |
---|
| 527 | if self.setting('outputRowColComments'): |
---|
| 528 | self.appendToPrevChunk(' # from line %s, col %s' % lineCol + '.') |
---|
| 529 | if cacheInfo: |
---|
| 530 | self.endCacheRegion() |
---|
| 531 | |
---|
| 532 | def addSilent(self, expr): |
---|
| 533 | self.addChunk( expr ) |
---|
| 534 | |
---|
| 535 | def addEcho(self, expr, rawExpr=None): |
---|
| 536 | self.addFilteredChunk(expr, rawExpr=rawExpr) |
---|
| 537 | |
---|
| 538 | def addSet(self, expr, exprComponents, setStyle): |
---|
| 539 | if setStyle is SET_GLOBAL: |
---|
| 540 | (LVALUE, OP, RVALUE) = (exprComponents.LVALUE, |
---|
| 541 | exprComponents.OP, |
---|
| 542 | exprComponents.RVALUE) |
---|
| 543 | # we need to split the LVALUE to deal with globalSetVars |
---|
| 544 | splitPos1 = LVALUE.find('.') |
---|
| 545 | splitPos2 = LVALUE.find('[') |
---|
| 546 | if splitPos1 > 0 and splitPos2==-1: |
---|
| 547 | splitPos = splitPos1 |
---|
| 548 | elif splitPos1 > 0 and splitPos1 < max(splitPos2,0): |
---|
| 549 | splitPos = splitPos1 |
---|
| 550 | else: |
---|
| 551 | splitPos = splitPos2 |
---|
| 552 | |
---|
| 553 | if splitPos >0: |
---|
| 554 | primary = LVALUE[:splitPos] |
---|
| 555 | secondary = LVALUE[splitPos:] |
---|
| 556 | else: |
---|
| 557 | primary = LVALUE |
---|
| 558 | secondary = '' |
---|
| 559 | LVALUE = 'self._CHEETAH__globalSetVars["' + primary + '"]' + secondary |
---|
| 560 | expr = LVALUE + ' ' + OP + ' ' + RVALUE.strip() |
---|
| 561 | |
---|
| 562 | if setStyle is SET_MODULE: |
---|
| 563 | self._moduleCompiler.addModuleGlobal(expr) |
---|
| 564 | else: |
---|
| 565 | self.addChunk(expr) |
---|
| 566 | |
---|
| 567 | def addInclude(self, sourceExpr, includeFrom, isRaw): |
---|
| 568 | self.addChunk('self._handleCheetahInclude(' + sourceExpr + |
---|
| 569 | ', trans=trans, ' + |
---|
| 570 | 'includeFrom="' + includeFrom + '", raw=' + |
---|
| 571 | repr(isRaw) + ')') |
---|
| 572 | |
---|
| 573 | def addWhile(self, expr, lineCol=None): |
---|
| 574 | self.addIndentingDirective(expr, lineCol=lineCol) |
---|
| 575 | |
---|
| 576 | def addFor(self, expr, lineCol=None): |
---|
| 577 | self.addIndentingDirective(expr, lineCol=lineCol) |
---|
| 578 | |
---|
| 579 | def addRepeat(self, expr, lineCol=None): |
---|
| 580 | #the _repeatCount stuff here allows nesting of #repeat directives |
---|
| 581 | self._repeatCount = getattr(self, "_repeatCount", -1) + 1 |
---|
| 582 | self.addFor('for __i%s in range(%s)' % (self._repeatCount,expr), lineCol=lineCol) |
---|
| 583 | |
---|
| 584 | def addIndentingDirective(self, expr, lineCol=None): |
---|
| 585 | if expr and not expr[-1] == ':': |
---|
| 586 | expr = expr + ':' |
---|
| 587 | self.addChunk( expr ) |
---|
| 588 | if lineCol: |
---|
| 589 | self.appendToPrevChunk(' # generated from line %s, col %s'%lineCol ) |
---|
| 590 | self.indent() |
---|
| 591 | |
---|
| 592 | def addReIndentingDirective(self, expr, dedent=True, lineCol=None): |
---|
| 593 | self.commitStrConst() |
---|
| 594 | if dedent: |
---|
| 595 | self.dedent() |
---|
| 596 | if not expr[-1] == ':': |
---|
| 597 | expr = expr + ':' |
---|
| 598 | |
---|
| 599 | self.addChunk( expr ) |
---|
| 600 | if lineCol: |
---|
| 601 | self.appendToPrevChunk(' # generated from line %s, col %s'%lineCol ) |
---|
| 602 | self.indent() |
---|
| 603 | |
---|
| 604 | def addIf(self, expr, lineCol=None): |
---|
| 605 | """For a full #if ... #end if directive |
---|
| 606 | """ |
---|
| 607 | self.addIndentingDirective(expr, lineCol=lineCol) |
---|
| 608 | |
---|
| 609 | def addOneLineIf(self, expr, lineCol=None): |
---|
| 610 | """For a full #if ... #end if directive |
---|
| 611 | """ |
---|
| 612 | self.addIndentingDirective(expr, lineCol=lineCol) |
---|
| 613 | |
---|
| 614 | def addTernaryExpr(self, conditionExpr, trueExpr, falseExpr, lineCol=None): |
---|
| 615 | """For a single-lie #if ... then .... else ... directive |
---|
| 616 | <condition> then <trueExpr> else <falseExpr> |
---|
| 617 | """ |
---|
| 618 | self.addIndentingDirective(conditionExpr, lineCol=lineCol) |
---|
| 619 | self.addFilteredChunk(trueExpr) |
---|
| 620 | self.dedent() |
---|
| 621 | self.addIndentingDirective('else') |
---|
| 622 | self.addFilteredChunk(falseExpr) |
---|
| 623 | self.dedent() |
---|
| 624 | |
---|
| 625 | def addElse(self, expr, dedent=True, lineCol=None): |
---|
| 626 | expr = re.sub(r'else[ \f\t]+if','elif', expr) |
---|
| 627 | self.addReIndentingDirective(expr, dedent=dedent, lineCol=lineCol) |
---|
| 628 | |
---|
| 629 | def addElif(self, expr, dedent=True, lineCol=None): |
---|
| 630 | self.addElse(expr, dedent=dedent, lineCol=lineCol) |
---|
| 631 | |
---|
| 632 | def addUnless(self, expr, lineCol=None): |
---|
| 633 | self.addIf('if not (' + expr + ')') |
---|
| 634 | |
---|
| 635 | def addClosure(self, functionName, argsList, parserComment): |
---|
| 636 | argStringChunks = [] |
---|
| 637 | for arg in argsList: |
---|
| 638 | chunk = arg[0] |
---|
| 639 | if not arg[1] == None: |
---|
| 640 | chunk += '=' + arg[1] |
---|
| 641 | argStringChunks.append(chunk) |
---|
| 642 | signature = "def " + functionName + "(" + ','.join(argStringChunks) + "):" |
---|
| 643 | self.addIndentingDirective(signature) |
---|
| 644 | self.addChunk('#'+parserComment) |
---|
| 645 | |
---|
| 646 | def addTry(self, expr, lineCol=None): |
---|
| 647 | self.addIndentingDirective(expr, lineCol=lineCol) |
---|
| 648 | |
---|
| 649 | def addExcept(self, expr, dedent=True, lineCol=None): |
---|
| 650 | self.addReIndentingDirective(expr, dedent=dedent, lineCol=lineCol) |
---|
| 651 | |
---|
| 652 | def addFinally(self, expr, dedent=True, lineCol=None): |
---|
| 653 | self.addReIndentingDirective(expr, dedent=dedent, lineCol=lineCol) |
---|
| 654 | |
---|
| 655 | def addReturn(self, expr): |
---|
| 656 | assert not self._isGenerator |
---|
| 657 | self.addChunk(expr) |
---|
| 658 | self._hasReturnStatement = True |
---|
| 659 | |
---|
| 660 | def addYield(self, expr): |
---|
| 661 | assert not self._hasReturnStatement |
---|
| 662 | self._isGenerator = True |
---|
| 663 | if expr.replace('yield','').strip(): |
---|
| 664 | self.addChunk(expr) |
---|
| 665 | else: |
---|
| 666 | self.addChunk('if _dummyTrans:') |
---|
| 667 | self.indent() |
---|
| 668 | self.addChunk('yield trans.response().getvalue()') |
---|
| 669 | self.addChunk('trans = DummyTransaction()') |
---|
| 670 | self.addChunk('write = trans.response().write') |
---|
| 671 | self.dedent() |
---|
| 672 | self.addChunk('else:') |
---|
| 673 | self.indent() |
---|
| 674 | self.addChunk( |
---|
| 675 | 'raise TypeError("This method cannot be called with a trans arg")') |
---|
| 676 | self.dedent() |
---|
| 677 | |
---|
| 678 | |
---|
| 679 | def addPass(self, expr): |
---|
| 680 | self.addChunk(expr) |
---|
| 681 | |
---|
| 682 | def addDel(self, expr): |
---|
| 683 | self.addChunk(expr) |
---|
| 684 | |
---|
| 685 | def addAssert(self, expr): |
---|
| 686 | self.addChunk(expr) |
---|
| 687 | |
---|
| 688 | def addRaise(self, expr): |
---|
| 689 | self.addChunk(expr) |
---|
| 690 | |
---|
| 691 | def addBreak(self, expr): |
---|
| 692 | self.addChunk(expr) |
---|
| 693 | |
---|
| 694 | def addContinue(self, expr): |
---|
| 695 | self.addChunk(expr) |
---|
| 696 | |
---|
| 697 | def addPSP(self, PSP): |
---|
| 698 | self.commitStrConst() |
---|
| 699 | autoIndent = False |
---|
| 700 | if PSP[0] == '=': |
---|
| 701 | PSP = PSP[1:] |
---|
| 702 | if PSP: |
---|
| 703 | self.addWriteChunk('_filter(' + PSP + ')') |
---|
| 704 | return |
---|
| 705 | |
---|
| 706 | elif PSP.lower() == 'end': |
---|
| 707 | self.dedent() |
---|
| 708 | return |
---|
| 709 | elif PSP[-1] == '$': |
---|
| 710 | autoIndent = True |
---|
| 711 | PSP = PSP[:-1] |
---|
| 712 | elif PSP[-1] == ':': |
---|
| 713 | autoIndent = True |
---|
| 714 | |
---|
| 715 | for line in PSP.splitlines(): |
---|
| 716 | self.addChunk(line) |
---|
| 717 | |
---|
| 718 | if autoIndent: |
---|
| 719 | self.indent() |
---|
| 720 | |
---|
| 721 | def nextCacheID(self): |
---|
| 722 | return ('_'+str(random.randrange(100, 999)) |
---|
| 723 | + str(random.randrange(10000, 99999))) |
---|
| 724 | |
---|
| 725 | def startCacheRegion(self, cacheInfo, lineCol, rawPlaceholder=None): |
---|
| 726 | |
---|
| 727 | # @@TR: we should add some runtime logging to this |
---|
| 728 | |
---|
| 729 | ID = self.nextCacheID() |
---|
| 730 | interval = cacheInfo.get('interval',None) |
---|
| 731 | test = cacheInfo.get('test',None) |
---|
| 732 | customID = cacheInfo.get('id',None) |
---|
| 733 | if customID: |
---|
| 734 | ID = customID |
---|
| 735 | varyBy = cacheInfo.get('varyBy', repr(ID)) |
---|
| 736 | self._cacheRegionsStack.append(ID) # attrib of current methodCompiler |
---|
| 737 | |
---|
| 738 | # @@TR: add this to a special class var as well |
---|
| 739 | self.addChunk('') |
---|
| 740 | |
---|
| 741 | self.addChunk('## START CACHE REGION: ID='+ID+ |
---|
| 742 | '. line %s, col %s'%lineCol + ' in the source.') |
---|
| 743 | |
---|
| 744 | self.addChunk('_RECACHE_%(ID)s = False'%locals()) |
---|
| 745 | self.addChunk('_cacheRegion_%(ID)s = self.getCacheRegion(regionID='%locals() |
---|
| 746 | + repr(ID) |
---|
| 747 | + ', cacheInfo=%r'%cacheInfo |
---|
| 748 | + ')') |
---|
| 749 | self.addChunk('if _cacheRegion_%(ID)s.isNew():'%locals()) |
---|
| 750 | self.indent() |
---|
| 751 | self.addChunk('_RECACHE_%(ID)s = True'%locals()) |
---|
| 752 | self.dedent() |
---|
| 753 | |
---|
| 754 | self.addChunk('_cacheItem_%(ID)s = _cacheRegion_%(ID)s.getCacheItem('%locals() |
---|
| 755 | +varyBy+')') |
---|
| 756 | |
---|
| 757 | self.addChunk('if _cacheItem_%(ID)s.hasExpired():'%locals()) |
---|
| 758 | self.indent() |
---|
| 759 | self.addChunk('_RECACHE_%(ID)s = True'%locals()) |
---|
| 760 | self.dedent() |
---|
| 761 | |
---|
| 762 | if test: |
---|
| 763 | self.addChunk('if ' + test + ':') |
---|
| 764 | self.indent() |
---|
| 765 | self.addChunk('_RECACHE_%(ID)s = True'%locals()) |
---|
| 766 | self.dedent() |
---|
| 767 | |
---|
| 768 | self.addChunk('if (not _RECACHE_%(ID)s) and _cacheItem_%(ID)s.getRefreshTime():'%locals()) |
---|
| 769 | self.indent() |
---|
| 770 | #self.addChunk('print "DEBUG"+"-"*50') |
---|
| 771 | self.addChunk('try:') |
---|
| 772 | self.indent() |
---|
| 773 | self.addChunk('_output = _cacheItem_%(ID)s.renderOutput()'%locals()) |
---|
| 774 | self.dedent() |
---|
| 775 | self.addChunk('except KeyError:') |
---|
| 776 | self.indent() |
---|
| 777 | self.addChunk('_RECACHE_%(ID)s = True'%locals()) |
---|
| 778 | #self.addChunk('print "DEBUG"+"*"*50') |
---|
| 779 | self.dedent() |
---|
| 780 | self.addChunk('else:') |
---|
| 781 | self.indent() |
---|
| 782 | self.addWriteChunk('_output') |
---|
| 783 | self.addChunk('del _output') |
---|
| 784 | self.dedent() |
---|
| 785 | |
---|
| 786 | self.dedent() |
---|
| 787 | |
---|
| 788 | self.addChunk('if _RECACHE_%(ID)s or not _cacheItem_%(ID)s.getRefreshTime():'%locals()) |
---|
| 789 | self.indent() |
---|
| 790 | self.addChunk('_orig_trans%(ID)s = trans'%locals()) |
---|
| 791 | self.addChunk('trans = _cacheCollector_%(ID)s = DummyTransaction()'%locals()) |
---|
| 792 | self.addChunk('write = _cacheCollector_%(ID)s.response().write'%locals()) |
---|
| 793 | if interval: |
---|
| 794 | self.addChunk(("_cacheItem_%(ID)s.setExpiryTime(currentTime() +"%locals()) |
---|
| 795 | + str(interval) + ")") |
---|
| 796 | |
---|
| 797 | def endCacheRegion(self): |
---|
| 798 | ID = self._cacheRegionsStack.pop() |
---|
| 799 | self.addChunk('trans = _orig_trans%(ID)s'%locals()) |
---|
| 800 | self.addChunk('write = trans.response().write') |
---|
| 801 | self.addChunk('_cacheData = _cacheCollector_%(ID)s.response().getvalue()'%locals()) |
---|
| 802 | self.addChunk('_cacheItem_%(ID)s.setData(_cacheData)'%locals()) |
---|
| 803 | self.addWriteChunk('_cacheData') |
---|
| 804 | self.addChunk('del _cacheData') |
---|
| 805 | self.addChunk('del _cacheCollector_%(ID)s'%locals()) |
---|
| 806 | self.addChunk('del _orig_trans%(ID)s'%locals()) |
---|
| 807 | self.dedent() |
---|
| 808 | self.addChunk('## END CACHE REGION: '+ID) |
---|
| 809 | self.addChunk('') |
---|
| 810 | |
---|
| 811 | def nextCallRegionID(self): |
---|
| 812 | return self.nextCacheID() |
---|
| 813 | |
---|
| 814 | def startCallRegion(self, functionName, args, lineCol, regionTitle='CALL'): |
---|
| 815 | class CallDetails(object): |
---|
| 816 | pass |
---|
| 817 | callDetails = CallDetails() |
---|
| 818 | callDetails.ID = ID = self.nextCallRegionID() |
---|
| 819 | callDetails.functionName = functionName |
---|
| 820 | callDetails.args = args |
---|
| 821 | callDetails.lineCol = lineCol |
---|
| 822 | callDetails.usesKeywordArgs = False |
---|
| 823 | self._callRegionsStack.append((ID, callDetails)) # attrib of current methodCompiler |
---|
| 824 | |
---|
| 825 | self.addChunk('## START %(regionTitle)s REGION: '%locals() |
---|
| 826 | +ID |
---|
| 827 | +' of '+functionName |
---|
| 828 | +' at line %s, col %s'%lineCol + ' in the source.') |
---|
| 829 | self.addChunk('_orig_trans%(ID)s = trans'%locals()) |
---|
| 830 | self.addChunk('_wasBuffering%(ID)s = self._CHEETAH__isBuffering'%locals()) |
---|
| 831 | self.addChunk('self._CHEETAH__isBuffering = True') |
---|
| 832 | self.addChunk('trans = _callCollector%(ID)s = DummyTransaction()'%locals()) |
---|
| 833 | self.addChunk('write = _callCollector%(ID)s.response().write'%locals()) |
---|
| 834 | |
---|
| 835 | def setCallArg(self, argName, lineCol): |
---|
| 836 | ID, callDetails = self._callRegionsStack[-1] |
---|
| 837 | argName = str(argName) |
---|
| 838 | if callDetails.usesKeywordArgs: |
---|
| 839 | self._endCallArg() |
---|
| 840 | else: |
---|
| 841 | callDetails.usesKeywordArgs = True |
---|
| 842 | self.addChunk('_callKws%(ID)s = {}'%locals()) |
---|
| 843 | self.addChunk('_currentCallArgname%(ID)s = %(argName)r'%locals()) |
---|
| 844 | callDetails.currentArgname = argName |
---|
| 845 | |
---|
| 846 | def _endCallArg(self): |
---|
| 847 | ID, callDetails = self._callRegionsStack[-1] |
---|
| 848 | currCallArg = callDetails.currentArgname |
---|
| 849 | self.addChunk(('_callKws%(ID)s[%(currCallArg)r] =' |
---|
| 850 | ' _callCollector%(ID)s.response().getvalue()')%locals()) |
---|
| 851 | self.addChunk('del _callCollector%(ID)s'%locals()) |
---|
| 852 | self.addChunk('trans = _callCollector%(ID)s = DummyTransaction()'%locals()) |
---|
| 853 | self.addChunk('write = _callCollector%(ID)s.response().write'%locals()) |
---|
| 854 | |
---|
| 855 | def endCallRegion(self, regionTitle='CALL'): |
---|
| 856 | ID, callDetails = self._callRegionsStack[-1] |
---|
| 857 | functionName, initialKwArgs, lineCol = ( |
---|
| 858 | callDetails.functionName, callDetails.args, callDetails.lineCol) |
---|
| 859 | |
---|
| 860 | def reset(ID=ID): |
---|
| 861 | self.addChunk('trans = _orig_trans%(ID)s'%locals()) |
---|
| 862 | self.addChunk('write = trans.response().write') |
---|
| 863 | self.addChunk('self._CHEETAH__isBuffering = _wasBuffering%(ID)s '%locals()) |
---|
| 864 | self.addChunk('del _wasBuffering%(ID)s'%locals()) |
---|
| 865 | self.addChunk('del _orig_trans%(ID)s'%locals()) |
---|
| 866 | |
---|
| 867 | if not callDetails.usesKeywordArgs: |
---|
| 868 | reset() |
---|
| 869 | self.addChunk('_callArgVal%(ID)s = _callCollector%(ID)s.response().getvalue()'%locals()) |
---|
| 870 | self.addChunk('del _callCollector%(ID)s'%locals()) |
---|
| 871 | if initialKwArgs: |
---|
| 872 | initialKwArgs = ', '+initialKwArgs |
---|
| 873 | self.addFilteredChunk('%(functionName)s(_callArgVal%(ID)s%(initialKwArgs)s)'%locals()) |
---|
| 874 | self.addChunk('del _callArgVal%(ID)s'%locals()) |
---|
| 875 | else: |
---|
| 876 | if initialKwArgs: |
---|
| 877 | initialKwArgs = initialKwArgs+', ' |
---|
| 878 | self._endCallArg() |
---|
| 879 | reset() |
---|
| 880 | self.addFilteredChunk('%(functionName)s(%(initialKwArgs)s**_callKws%(ID)s)'%locals()) |
---|
| 881 | self.addChunk('del _callKws%(ID)s'%locals()) |
---|
| 882 | self.addChunk('## END %(regionTitle)s REGION: '%locals() |
---|
| 883 | +ID |
---|
| 884 | +' of '+functionName |
---|
| 885 | +' at line %s, col %s'%lineCol + ' in the source.') |
---|
| 886 | self.addChunk('') |
---|
| 887 | self._callRegionsStack.pop() # attrib of current methodCompiler |
---|
| 888 | |
---|
| 889 | def nextCaptureRegionID(self): |
---|
| 890 | return self.nextCacheID() |
---|
| 891 | |
---|
| 892 | def startCaptureRegion(self, assignTo, lineCol): |
---|
| 893 | class CaptureDetails: pass |
---|
| 894 | captureDetails = CaptureDetails() |
---|
| 895 | captureDetails.ID = ID = self.nextCaptureRegionID() |
---|
| 896 | captureDetails.assignTo = assignTo |
---|
| 897 | captureDetails.lineCol = lineCol |
---|
| 898 | |
---|
| 899 | self._captureRegionsStack.append((ID,captureDetails)) # attrib of current methodCompiler |
---|
| 900 | self.addChunk('## START CAPTURE REGION: '+ID |
---|
| 901 | +' '+assignTo |
---|
| 902 | +' at line %s, col %s'%lineCol + ' in the source.') |
---|
| 903 | self.addChunk('_orig_trans%(ID)s = trans'%locals()) |
---|
| 904 | self.addChunk('_wasBuffering%(ID)s = self._CHEETAH__isBuffering'%locals()) |
---|
| 905 | self.addChunk('self._CHEETAH__isBuffering = True') |
---|
| 906 | self.addChunk('trans = _captureCollector%(ID)s = DummyTransaction()'%locals()) |
---|
| 907 | self.addChunk('write = _captureCollector%(ID)s.response().write'%locals()) |
---|
| 908 | |
---|
| 909 | def endCaptureRegion(self): |
---|
| 910 | ID, captureDetails = self._captureRegionsStack.pop() |
---|
| 911 | assignTo, lineCol = (captureDetails.assignTo, captureDetails.lineCol) |
---|
| 912 | self.addChunk('trans = _orig_trans%(ID)s'%locals()) |
---|
| 913 | self.addChunk('write = trans.response().write') |
---|
| 914 | self.addChunk('self._CHEETAH__isBuffering = _wasBuffering%(ID)s '%locals()) |
---|
| 915 | self.addChunk('%(assignTo)s = _captureCollector%(ID)s.response().getvalue()'%locals()) |
---|
| 916 | self.addChunk('del _orig_trans%(ID)s'%locals()) |
---|
| 917 | self.addChunk('del _captureCollector%(ID)s'%locals()) |
---|
| 918 | self.addChunk('del _wasBuffering%(ID)s'%locals()) |
---|
| 919 | |
---|
| 920 | def setErrorCatcher(self, errorCatcherName): |
---|
| 921 | self.turnErrorCatcherOn() |
---|
| 922 | |
---|
| 923 | self.addChunk('if self._CHEETAH__errorCatchers.has_key("' + errorCatcherName + '"):') |
---|
| 924 | self.indent() |
---|
| 925 | self.addChunk('self._CHEETAH__errorCatcher = self._CHEETAH__errorCatchers["' + |
---|
| 926 | errorCatcherName + '"]') |
---|
| 927 | self.dedent() |
---|
| 928 | self.addChunk('else:') |
---|
| 929 | self.indent() |
---|
| 930 | self.addChunk('self._CHEETAH__errorCatcher = self._CHEETAH__errorCatchers["' |
---|
| 931 | + errorCatcherName + '"] = ErrorCatchers.' |
---|
| 932 | + errorCatcherName + '(self)' |
---|
| 933 | ) |
---|
| 934 | self.dedent() |
---|
| 935 | |
---|
| 936 | def nextFilterRegionID(self): |
---|
| 937 | return self.nextCacheID() |
---|
| 938 | |
---|
| 939 | def setTransform(self, transformer, isKlass): |
---|
| 940 | self.addChunk('trans = TransformerTransaction()') |
---|
| 941 | self.addChunk('trans._response = trans.response()') |
---|
| 942 | self.addChunk('trans._response._filter = %s' % transformer) |
---|
| 943 | self.addChunk('write = trans._response.write') |
---|
| 944 | |
---|
| 945 | def setFilter(self, theFilter, isKlass): |
---|
| 946 | class FilterDetails: |
---|
| 947 | pass |
---|
| 948 | filterDetails = FilterDetails() |
---|
| 949 | filterDetails.ID = ID = self.nextFilterRegionID() |
---|
| 950 | filterDetails.theFilter = theFilter |
---|
| 951 | filterDetails.isKlass = isKlass |
---|
| 952 | self._filterRegionsStack.append((ID, filterDetails)) # attrib of current methodCompiler |
---|
| 953 | |
---|
| 954 | self.addChunk('_orig_filter%(ID)s = _filter'%locals()) |
---|
| 955 | if isKlass: |
---|
| 956 | self.addChunk('_filter = self._CHEETAH__currentFilter = ' + theFilter.strip() + |
---|
| 957 | '(self).filter') |
---|
| 958 | else: |
---|
| 959 | if theFilter.lower() == 'none': |
---|
| 960 | self.addChunk('_filter = self._CHEETAH__initialFilter') |
---|
| 961 | else: |
---|
| 962 | # is string representing the name of a builtin filter |
---|
| 963 | self.addChunk('filterName = ' + repr(theFilter)) |
---|
| 964 | self.addChunk('if self._CHEETAH__filters.has_key("' + theFilter + '"):') |
---|
| 965 | self.indent() |
---|
| 966 | self.addChunk('_filter = self._CHEETAH__currentFilter = self._CHEETAH__filters[filterName]') |
---|
| 967 | self.dedent() |
---|
| 968 | self.addChunk('else:') |
---|
| 969 | self.indent() |
---|
| 970 | self.addChunk('_filter = self._CHEETAH__currentFilter' |
---|
| 971 | +' = \\\n\t\t\tself._CHEETAH__filters[filterName] = ' |
---|
| 972 | + 'getattr(self._CHEETAH__filtersLib, filterName)(self).filter') |
---|
| 973 | self.dedent() |
---|
| 974 | |
---|
| 975 | def closeFilterBlock(self): |
---|
| 976 | ID, filterDetails = self._filterRegionsStack.pop() |
---|
| 977 | #self.addChunk('_filter = self._CHEETAH__initialFilter') |
---|
| 978 | #self.addChunk('_filter = _orig_filter%(ID)s'%locals()) |
---|
| 979 | self.addChunk('_filter = self._CHEETAH__currentFilter = _orig_filter%(ID)s'%locals()) |
---|
| 980 | |
---|
| 981 | class AutoMethodCompiler(MethodCompiler): |
---|
| 982 | |
---|
| 983 | def _setupState(self): |
---|
| 984 | MethodCompiler._setupState(self) |
---|
| 985 | self._argStringList = [ ("self",None) ] |
---|
| 986 | self._streamingEnabled = True |
---|
| 987 | self._isClassMethod = None |
---|
| 988 | self._isStaticMethod = None |
---|
| 989 | |
---|
| 990 | def _useKWsDictArgForPassingTrans(self): |
---|
| 991 | alreadyHasTransArg = [argname for argname,defval in self._argStringList |
---|
| 992 | if argname=='trans'] |
---|
| 993 | return (self.methodName()!='respond' |
---|
| 994 | and not alreadyHasTransArg |
---|
| 995 | and self.setting('useKWsDictArgForPassingTrans')) |
---|
| 996 | |
---|
| 997 | def isClassMethod(self): |
---|
| 998 | if self._isClassMethod is None: |
---|
| 999 | self._isClassMethod = '@classmethod' in self._decorators |
---|
| 1000 | return self._isClassMethod |
---|
| 1001 | |
---|
| 1002 | def isStaticMethod(self): |
---|
| 1003 | if self._isStaticMethod is None: |
---|
| 1004 | self._isStaticMethod = '@staticmethod' in self._decorators |
---|
| 1005 | return self._isStaticMethod |
---|
| 1006 | |
---|
| 1007 | def cleanupState(self): |
---|
| 1008 | MethodCompiler.cleanupState(self) |
---|
| 1009 | self.commitStrConst() |
---|
| 1010 | if self._cacheRegionsStack: |
---|
| 1011 | self.endCacheRegion() |
---|
| 1012 | if self._callRegionsStack: |
---|
| 1013 | self.endCallRegion() |
---|
| 1014 | |
---|
| 1015 | if self._streamingEnabled: |
---|
| 1016 | kwargsName = None |
---|
| 1017 | positionalArgsListName = None |
---|
| 1018 | for argname,defval in self._argStringList: |
---|
| 1019 | if argname.strip().startswith('**'): |
---|
| 1020 | kwargsName = argname.strip().replace('**','') |
---|
| 1021 | break |
---|
| 1022 | elif argname.strip().startswith('*'): |
---|
| 1023 | positionalArgsListName = argname.strip().replace('*','') |
---|
| 1024 | |
---|
| 1025 | if not kwargsName and self._useKWsDictArgForPassingTrans(): |
---|
| 1026 | kwargsName = 'KWS' |
---|
| 1027 | self.addMethArg('**KWS', None) |
---|
| 1028 | self._kwargsName = kwargsName |
---|
| 1029 | |
---|
| 1030 | if not self._useKWsDictArgForPassingTrans(): |
---|
| 1031 | if not kwargsName and not positionalArgsListName: |
---|
| 1032 | self.addMethArg('trans', 'None') |
---|
| 1033 | else: |
---|
| 1034 | self._streamingEnabled = False |
---|
| 1035 | |
---|
| 1036 | self._indentLev = self.setting('initialMethIndentLevel') |
---|
| 1037 | mainBodyChunks = self._methodBodyChunks |
---|
| 1038 | self._methodBodyChunks = [] |
---|
| 1039 | self._addAutoSetupCode() |
---|
| 1040 | self._methodBodyChunks.extend(mainBodyChunks) |
---|
| 1041 | self._addAutoCleanupCode() |
---|
| 1042 | |
---|
| 1043 | def _addAutoSetupCode(self): |
---|
| 1044 | if self._initialMethodComment: |
---|
| 1045 | self.addChunk(self._initialMethodComment) |
---|
| 1046 | |
---|
| 1047 | if self._streamingEnabled and not self.isClassMethod() and not self.isStaticMethod(): |
---|
| 1048 | if self._useKWsDictArgForPassingTrans() and self._kwargsName: |
---|
| 1049 | self.addChunk('trans = %s.get("trans")'%self._kwargsName) |
---|
| 1050 | self.addChunk('if (not trans and not self._CHEETAH__isBuffering' |
---|
| 1051 | ' and not callable(self.transaction)):') |
---|
| 1052 | self.indent() |
---|
| 1053 | self.addChunk('trans = self.transaction' |
---|
| 1054 | ' # is None unless self.awake() was called') |
---|
| 1055 | self.dedent() |
---|
| 1056 | self.addChunk('if not trans:') |
---|
| 1057 | self.indent() |
---|
| 1058 | self.addChunk('trans = DummyTransaction()') |
---|
| 1059 | if self.setting('autoAssignDummyTransactionToSelf'): |
---|
| 1060 | self.addChunk('self.transaction = trans') |
---|
| 1061 | self.addChunk('_dummyTrans = True') |
---|
| 1062 | self.dedent() |
---|
| 1063 | self.addChunk('else: _dummyTrans = False') |
---|
| 1064 | else: |
---|
| 1065 | self.addChunk('trans = DummyTransaction()') |
---|
| 1066 | self.addChunk('_dummyTrans = True') |
---|
| 1067 | self.addChunk('write = trans.response().write') |
---|
| 1068 | if self.setting('useNameMapper'): |
---|
| 1069 | argNames = [arg[0] for arg in self._argStringList] |
---|
| 1070 | allowSearchListAsMethArg = self.setting('allowSearchListAsMethArg') |
---|
| 1071 | if allowSearchListAsMethArg and 'SL' in argNames: |
---|
| 1072 | pass |
---|
| 1073 | elif allowSearchListAsMethArg and 'searchList' in argNames: |
---|
| 1074 | self.addChunk('SL = searchList') |
---|
| 1075 | elif not self.isClassMethod() and not self.isStaticMethod(): |
---|
| 1076 | self.addChunk('SL = self._CHEETAH__searchList') |
---|
| 1077 | else: |
---|
| 1078 | self.addChunk('SL = [KWS]') |
---|
| 1079 | if self.setting('useFilters'): |
---|
| 1080 | if self.isClassMethod() or self.isStaticMethod(): |
---|
| 1081 | self.addChunk('_filter = lambda x, **kwargs: unicode(x)') |
---|
| 1082 | else: |
---|
| 1083 | self.addChunk('_filter = self._CHEETAH__currentFilter') |
---|
| 1084 | self.addChunk('') |
---|
| 1085 | self.addChunk("#" *40) |
---|
| 1086 | self.addChunk('## START - generated method body') |
---|
| 1087 | self.addChunk('') |
---|
| 1088 | |
---|
| 1089 | def _addAutoCleanupCode(self): |
---|
| 1090 | self.addChunk('') |
---|
| 1091 | self.addChunk("#" *40) |
---|
| 1092 | self.addChunk('## END - generated method body') |
---|
| 1093 | self.addChunk('') |
---|
| 1094 | |
---|
| 1095 | if not self._isGenerator: |
---|
| 1096 | self.addStop() |
---|
| 1097 | self.addChunk('') |
---|
| 1098 | |
---|
| 1099 | def addStop(self, expr=None): |
---|
| 1100 | self.addChunk('return _dummyTrans and trans.response().getvalue() or ""') |
---|
| 1101 | |
---|
| 1102 | def addMethArg(self, name, defVal=None): |
---|
| 1103 | self._argStringList.append( (name,defVal) ) |
---|
| 1104 | |
---|
| 1105 | def methodSignature(self): |
---|
| 1106 | argStringChunks = [] |
---|
| 1107 | for arg in self._argStringList: |
---|
| 1108 | chunk = arg[0] |
---|
| 1109 | if chunk == 'self' and self.isClassMethod(): |
---|
| 1110 | chunk = 'cls' |
---|
| 1111 | if chunk == 'self' and self.isStaticMethod(): |
---|
| 1112 | # Skip the "self" method for @staticmethod decorators |
---|
| 1113 | continue |
---|
| 1114 | if not arg[1] == None: |
---|
| 1115 | chunk += '=' + arg[1] |
---|
| 1116 | argStringChunks.append(chunk) |
---|
| 1117 | argString = (', ').join(argStringChunks) |
---|
| 1118 | |
---|
| 1119 | output = [] |
---|
| 1120 | if self._decorators: |
---|
| 1121 | output.append(''.join([self._indent + decorator + '\n' |
---|
| 1122 | for decorator in self._decorators])) |
---|
| 1123 | output.append(self._indent + "def " |
---|
| 1124 | + self.methodName() + "(" + |
---|
| 1125 | argString + "):\n\n") |
---|
| 1126 | return ''.join(output) |
---|
| 1127 | |
---|
| 1128 | |
---|
| 1129 | ################################################## |
---|
| 1130 | ## CLASS COMPILERS |
---|
| 1131 | |
---|
| 1132 | _initMethod_initCheetah = """\ |
---|
| 1133 | if not self._CHEETAH__instanceInitialized: |
---|
| 1134 | cheetahKWArgs = {} |
---|
| 1135 | allowedKWs = 'searchList namespaces filter filtersLib errorCatcher'.split() |
---|
| 1136 | for k,v in KWs.items(): |
---|
| 1137 | if k in allowedKWs: cheetahKWArgs[k] = v |
---|
| 1138 | self._initCheetahInstance(**cheetahKWArgs) |
---|
| 1139 | """.replace('\n','\n'+' '*8) |
---|
| 1140 | |
---|
| 1141 | class ClassCompiler(GenUtils): |
---|
| 1142 | methodCompilerClass = AutoMethodCompiler |
---|
| 1143 | methodCompilerClassForInit = MethodCompiler |
---|
| 1144 | |
---|
| 1145 | def __init__(self, className, mainMethodName='respond', |
---|
| 1146 | moduleCompiler=None, |
---|
| 1147 | fileName=None, |
---|
| 1148 | settingsManager=None): |
---|
| 1149 | |
---|
| 1150 | self._settingsManager = settingsManager |
---|
| 1151 | self._fileName = fileName |
---|
| 1152 | self._className = className |
---|
| 1153 | self._moduleCompiler = moduleCompiler |
---|
| 1154 | self._mainMethodName = mainMethodName |
---|
| 1155 | self._setupState() |
---|
| 1156 | methodCompiler = self._spawnMethodCompiler( |
---|
| 1157 | mainMethodName, |
---|
| 1158 | initialMethodComment='## CHEETAH: main method generated for this template') |
---|
| 1159 | |
---|
| 1160 | self._setActiveMethodCompiler(methodCompiler) |
---|
| 1161 | if fileName and self.setting('monitorSrcFile'): |
---|
| 1162 | self._addSourceFileMonitoring(fileName) |
---|
| 1163 | |
---|
| 1164 | def setting(self, key): |
---|
| 1165 | return self._settingsManager.setting(key) |
---|
| 1166 | |
---|
| 1167 | def __getattr__(self, name): |
---|
| 1168 | """Provide access to the methods and attributes of the MethodCompiler |
---|
| 1169 | at the top of the activeMethods stack: one-way namespace sharing |
---|
| 1170 | |
---|
| 1171 | |
---|
| 1172 | WARNING: Use .setMethods to assign the attributes of the MethodCompiler |
---|
| 1173 | from the methods of this class!!! or you will be assigning to attributes |
---|
| 1174 | of this object instead.""" |
---|
| 1175 | |
---|
| 1176 | if self.__dict__.has_key(name): |
---|
| 1177 | return self.__dict__[name] |
---|
| 1178 | elif hasattr(self.__class__, name): |
---|
| 1179 | return getattr(self.__class__, name) |
---|
| 1180 | elif self._activeMethodsList and hasattr(self._activeMethodsList[-1], name): |
---|
| 1181 | return getattr(self._activeMethodsList[-1], name) |
---|
| 1182 | else: |
---|
| 1183 | raise AttributeError, name |
---|
| 1184 | |
---|
| 1185 | def _setupState(self): |
---|
| 1186 | self._classDef = None |
---|
| 1187 | self._decoratorsForNextMethod = [] |
---|
| 1188 | self._activeMethodsList = [] # stack while parsing/generating |
---|
| 1189 | self._finishedMethodsList = [] # store by order |
---|
| 1190 | self._methodsIndex = {} # store by name |
---|
| 1191 | self._baseClass = 'Template' |
---|
| 1192 | self._classDocStringLines = [] |
---|
| 1193 | # printed after methods in the gen class def: |
---|
| 1194 | self._generatedAttribs = ['_CHEETAH__instanceInitialized = False'] |
---|
| 1195 | self._generatedAttribs.append('_CHEETAH_version = __CHEETAH_version__') |
---|
| 1196 | self._generatedAttribs.append( |
---|
| 1197 | '_CHEETAH_versionTuple = __CHEETAH_versionTuple__') |
---|
| 1198 | |
---|
| 1199 | if self.setting('addTimestampsToCompilerOutput'): |
---|
| 1200 | self._generatedAttribs.append('_CHEETAH_genTime = __CHEETAH_genTime__') |
---|
| 1201 | self._generatedAttribs.append('_CHEETAH_genTimestamp = __CHEETAH_genTimestamp__') |
---|
| 1202 | |
---|
| 1203 | self._generatedAttribs.append('_CHEETAH_src = __CHEETAH_src__') |
---|
| 1204 | self._generatedAttribs.append( |
---|
| 1205 | '_CHEETAH_srcLastModified = __CHEETAH_srcLastModified__') |
---|
| 1206 | |
---|
| 1207 | if self.setting('templateMetaclass'): |
---|
| 1208 | self._generatedAttribs.append('__metaclass__ = '+self.setting('templateMetaclass')) |
---|
| 1209 | self._initMethChunks = [] |
---|
| 1210 | self._blockMetaData = {} |
---|
| 1211 | self._errorCatcherCount = 0 |
---|
| 1212 | self._placeholderToErrorCatcherMap = {} |
---|
| 1213 | |
---|
| 1214 | def cleanupState(self): |
---|
| 1215 | while self._activeMethodsList: |
---|
| 1216 | methCompiler = self._popActiveMethodCompiler() |
---|
| 1217 | self._swallowMethodCompiler(methCompiler) |
---|
| 1218 | self._setupInitMethod() |
---|
| 1219 | if self._mainMethodName == 'respond': |
---|
| 1220 | if self.setting('setup__str__method'): |
---|
| 1221 | self._generatedAttribs.append('def __str__(self): return self.respond()') |
---|
| 1222 | self.addAttribute('_mainCheetahMethod_for_' + self._className + |
---|
| 1223 | '= ' + repr(self._mainMethodName) ) |
---|
| 1224 | |
---|
| 1225 | def _setupInitMethod(self): |
---|
| 1226 | __init__ = self._spawnMethodCompiler('__init__', |
---|
| 1227 | klass=self.methodCompilerClassForInit) |
---|
| 1228 | __init__.setMethodSignature("def __init__(self, *args, **KWs)") |
---|
| 1229 | __init__.addChunk('super(%s, self).__init__(*args, **KWs)' % self._className) |
---|
| 1230 | __init__.addChunk(_initMethod_initCheetah % {'className' : self._className}) |
---|
| 1231 | for chunk in self._initMethChunks: |
---|
| 1232 | __init__.addChunk(chunk) |
---|
| 1233 | __init__.cleanupState() |
---|
| 1234 | self._swallowMethodCompiler(__init__, pos=0) |
---|
| 1235 | |
---|
| 1236 | def _addSourceFileMonitoring(self, fileName): |
---|
| 1237 | # @@TR: this stuff needs auditing for Cheetah 2.0 |
---|
| 1238 | # the first bit is added to init |
---|
| 1239 | self.addChunkToInit('self._filePath = ' + repr(fileName)) |
---|
| 1240 | self.addChunkToInit('self._fileMtime = ' + str(getmtime(fileName)) ) |
---|
| 1241 | |
---|
| 1242 | # the rest is added to the main output method of the class ('mainMethod') |
---|
| 1243 | self.addChunk('if exists(self._filePath) and ' + |
---|
| 1244 | 'getmtime(self._filePath) > self._fileMtime:') |
---|
| 1245 | self.indent() |
---|
| 1246 | self.addChunk('self._compile(file=self._filePath, moduleName='+self._className + ')') |
---|
| 1247 | self.addChunk( |
---|
| 1248 | 'write(getattr(self, self._mainCheetahMethod_for_' + self._className + |
---|
| 1249 | ')(trans=trans))') |
---|
| 1250 | self.addStop() |
---|
| 1251 | self.dedent() |
---|
| 1252 | |
---|
| 1253 | def setClassName(self, name): |
---|
| 1254 | self._className = name |
---|
| 1255 | |
---|
| 1256 | def className(self): |
---|
| 1257 | return self._className |
---|
| 1258 | |
---|
| 1259 | def setBaseClass(self, baseClassName): |
---|
| 1260 | self._baseClass = baseClassName |
---|
| 1261 | |
---|
| 1262 | def setMainMethodName(self, methodName): |
---|
| 1263 | if methodName == self._mainMethodName: |
---|
| 1264 | return |
---|
| 1265 | ## change the name in the methodCompiler and add new reference |
---|
| 1266 | mainMethod = self._methodsIndex[self._mainMethodName] |
---|
| 1267 | mainMethod.setMethodName(methodName) |
---|
| 1268 | self._methodsIndex[methodName] = mainMethod |
---|
| 1269 | |
---|
| 1270 | ## make sure that fileUpdate code still works properly: |
---|
| 1271 | chunkToChange = ('write(self.' + self._mainMethodName + '(trans=trans))') |
---|
| 1272 | chunks = mainMethod._methodBodyChunks |
---|
| 1273 | if chunkToChange in chunks: |
---|
| 1274 | for i in range(len(chunks)): |
---|
| 1275 | if chunks[i] == chunkToChange: |
---|
| 1276 | chunks[i] = ('write(self.' + methodName + '(trans=trans))') |
---|
| 1277 | ## get rid of the old reference and update self._mainMethodName |
---|
| 1278 | del self._methodsIndex[self._mainMethodName] |
---|
| 1279 | self._mainMethodName = methodName |
---|
| 1280 | |
---|
| 1281 | def setMainMethodArgs(self, argsList): |
---|
| 1282 | mainMethodCompiler = self._methodsIndex[self._mainMethodName] |
---|
| 1283 | for argName, defVal in argsList: |
---|
| 1284 | mainMethodCompiler.addMethArg(argName, defVal) |
---|
| 1285 | |
---|
| 1286 | |
---|
| 1287 | def _spawnMethodCompiler(self, methodName, klass=None, |
---|
| 1288 | initialMethodComment=None): |
---|
| 1289 | if klass is None: |
---|
| 1290 | klass = self.methodCompilerClass |
---|
| 1291 | |
---|
| 1292 | decorators = self._decoratorsForNextMethod or [] |
---|
| 1293 | self._decoratorsForNextMethod = [] |
---|
| 1294 | methodCompiler = klass(methodName, classCompiler=self, |
---|
| 1295 | decorators=decorators, |
---|
| 1296 | initialMethodComment=initialMethodComment) |
---|
| 1297 | self._methodsIndex[methodName] = methodCompiler |
---|
| 1298 | return methodCompiler |
---|
| 1299 | |
---|
| 1300 | def _setActiveMethodCompiler(self, methodCompiler): |
---|
| 1301 | self._activeMethodsList.append(methodCompiler) |
---|
| 1302 | |
---|
| 1303 | def _getActiveMethodCompiler(self): |
---|
| 1304 | return self._activeMethodsList[-1] |
---|
| 1305 | |
---|
| 1306 | def _popActiveMethodCompiler(self): |
---|
| 1307 | return self._activeMethodsList.pop() |
---|
| 1308 | |
---|
| 1309 | def _swallowMethodCompiler(self, methodCompiler, pos=None): |
---|
| 1310 | methodCompiler.cleanupState() |
---|
| 1311 | if pos==None: |
---|
| 1312 | self._finishedMethodsList.append( methodCompiler ) |
---|
| 1313 | else: |
---|
| 1314 | self._finishedMethodsList.insert(pos, methodCompiler) |
---|
| 1315 | return methodCompiler |
---|
| 1316 | |
---|
| 1317 | def startMethodDef(self, methodName, argsList, parserComment): |
---|
| 1318 | methodCompiler = self._spawnMethodCompiler( |
---|
| 1319 | methodName, initialMethodComment=parserComment) |
---|
| 1320 | self._setActiveMethodCompiler(methodCompiler) |
---|
| 1321 | for argName, defVal in argsList: |
---|
| 1322 | methodCompiler.addMethArg(argName, defVal) |
---|
| 1323 | |
---|
| 1324 | def _finishedMethods(self): |
---|
| 1325 | return self._finishedMethodsList |
---|
| 1326 | |
---|
| 1327 | def addDecorator(self, decoratorExpr): |
---|
| 1328 | """Set the decorator to be used with the next method in the source. |
---|
| 1329 | |
---|
| 1330 | See _spawnMethodCompiler() and MethodCompiler for the details of how |
---|
| 1331 | this is used. |
---|
| 1332 | """ |
---|
| 1333 | self._decoratorsForNextMethod.append(decoratorExpr) |
---|
| 1334 | |
---|
| 1335 | def addClassDocString(self, line): |
---|
| 1336 | self._classDocStringLines.append( line.replace('%','%%')) |
---|
| 1337 | |
---|
| 1338 | def addChunkToInit(self,chunk): |
---|
| 1339 | self._initMethChunks.append(chunk) |
---|
| 1340 | |
---|
| 1341 | def addAttribute(self, attribExpr): |
---|
| 1342 | ## first test to make sure that the user hasn't used any fancy Cheetah syntax |
---|
| 1343 | # (placeholders, directives, etc.) inside the expression |
---|
| 1344 | if attribExpr.find('VFN(') != -1 or attribExpr.find('VFFSL(') != -1: |
---|
| 1345 | raise ParseError(self, |
---|
| 1346 | 'Invalid #attr directive.' + |
---|
| 1347 | ' It should only contain simple Python literals.') |
---|
| 1348 | ## now add the attribute |
---|
| 1349 | self._generatedAttribs.append(attribExpr) |
---|
| 1350 | |
---|
| 1351 | def addSuper(self, argsList, parserComment=None): |
---|
| 1352 | className = self._className #self._baseClass |
---|
| 1353 | methodName = self._getActiveMethodCompiler().methodName() |
---|
| 1354 | |
---|
| 1355 | argStringChunks = [] |
---|
| 1356 | for arg in argsList: |
---|
| 1357 | chunk = arg[0] |
---|
| 1358 | if not arg[1] == None: |
---|
| 1359 | chunk += '=' + arg[1] |
---|
| 1360 | argStringChunks.append(chunk) |
---|
| 1361 | argString = ','.join(argStringChunks) |
---|
| 1362 | |
---|
| 1363 | self.addFilteredChunk( |
---|
| 1364 | 'super(%(className)s, self).%(methodName)s(%(argString)s)'%locals()) |
---|
| 1365 | |
---|
| 1366 | def addErrorCatcherCall(self, codeChunk, rawCode='', lineCol=''): |
---|
| 1367 | if self._placeholderToErrorCatcherMap.has_key(rawCode): |
---|
| 1368 | methodName = self._placeholderToErrorCatcherMap[rawCode] |
---|
| 1369 | if not self.setting('outputRowColComments'): |
---|
| 1370 | self._methodsIndex[methodName].addMethDocString( |
---|
| 1371 | 'plus at line %s, col %s'%lineCol) |
---|
| 1372 | return methodName |
---|
| 1373 | |
---|
| 1374 | self._errorCatcherCount += 1 |
---|
| 1375 | methodName = '__errorCatcher' + str(self._errorCatcherCount) |
---|
| 1376 | self._placeholderToErrorCatcherMap[rawCode] = methodName |
---|
| 1377 | |
---|
| 1378 | catcherMeth = self._spawnMethodCompiler( |
---|
| 1379 | methodName, |
---|
| 1380 | klass=MethodCompiler, |
---|
| 1381 | initialMethodComment=('## CHEETAH: Generated from ' + rawCode + |
---|
| 1382 | ' at line %s, col %s'%lineCol + '.') |
---|
| 1383 | ) |
---|
| 1384 | catcherMeth.setMethodSignature('def ' + methodName + |
---|
| 1385 | '(self, localsDict={})') |
---|
| 1386 | # is this use of localsDict right? |
---|
| 1387 | catcherMeth.addChunk('try:') |
---|
| 1388 | catcherMeth.indent() |
---|
| 1389 | catcherMeth.addChunk("return eval('''" + codeChunk + |
---|
| 1390 | "''', globals(), localsDict)") |
---|
| 1391 | catcherMeth.dedent() |
---|
| 1392 | catcherMeth.addChunk('except self._CHEETAH__errorCatcher.exceptions(), e:') |
---|
| 1393 | catcherMeth.indent() |
---|
| 1394 | catcherMeth.addChunk("return self._CHEETAH__errorCatcher.warn(exc_val=e, code= " + |
---|
| 1395 | repr(codeChunk) + " , rawCode= " + |
---|
| 1396 | repr(rawCode) + " , lineCol=" + str(lineCol) +")") |
---|
| 1397 | |
---|
| 1398 | catcherMeth.cleanupState() |
---|
| 1399 | |
---|
| 1400 | self._swallowMethodCompiler(catcherMeth) |
---|
| 1401 | return methodName |
---|
| 1402 | |
---|
| 1403 | def closeDef(self): |
---|
| 1404 | self.commitStrConst() |
---|
| 1405 | methCompiler = self._popActiveMethodCompiler() |
---|
| 1406 | self._swallowMethodCompiler(methCompiler) |
---|
| 1407 | |
---|
| 1408 | def closeBlock(self): |
---|
| 1409 | self.commitStrConst() |
---|
| 1410 | methCompiler = self._popActiveMethodCompiler() |
---|
| 1411 | methodName = methCompiler.methodName() |
---|
| 1412 | if self.setting('includeBlockMarkers'): |
---|
| 1413 | endMarker = self.setting('blockMarkerEnd') |
---|
| 1414 | methCompiler.addStrConst(endMarker[0] + methodName + endMarker[1]) |
---|
| 1415 | self._swallowMethodCompiler(methCompiler) |
---|
| 1416 | |
---|
| 1417 | #metaData = self._blockMetaData[methodName] |
---|
| 1418 | #rawDirective = metaData['raw'] |
---|
| 1419 | #lineCol = metaData['lineCol'] |
---|
| 1420 | |
---|
| 1421 | ## insert the code to call the block, caching if #cache directive is on |
---|
| 1422 | codeChunk = 'self.' + methodName + '(trans=trans)' |
---|
| 1423 | self.addChunk(codeChunk) |
---|
| 1424 | |
---|
| 1425 | #self.appendToPrevChunk(' # generated from ' + repr(rawDirective) ) |
---|
| 1426 | #if self.setting('outputRowColComments'): |
---|
| 1427 | # self.appendToPrevChunk(' at line %s, col %s' % lineCol + '.') |
---|
| 1428 | |
---|
| 1429 | |
---|
| 1430 | ## code wrapping methods |
---|
| 1431 | |
---|
| 1432 | def classDef(self): |
---|
| 1433 | if self._classDef: |
---|
| 1434 | return self._classDef |
---|
| 1435 | else: |
---|
| 1436 | return self.wrapClassDef() |
---|
| 1437 | |
---|
| 1438 | __str__ = classDef |
---|
| 1439 | __unicode__ = classDef |
---|
| 1440 | |
---|
| 1441 | def wrapClassDef(self): |
---|
| 1442 | ind = self.setting('indentationStep') |
---|
| 1443 | classDefChunks = [self.classSignature(), |
---|
| 1444 | self.classDocstring(), |
---|
| 1445 | ] |
---|
| 1446 | def addMethods(): |
---|
| 1447 | classDefChunks.extend([ |
---|
| 1448 | ind + '#'*50, |
---|
| 1449 | ind + '## CHEETAH GENERATED METHODS', |
---|
| 1450 | '\n', |
---|
| 1451 | self.methodDefs(), |
---|
| 1452 | ]) |
---|
| 1453 | def addAttributes(): |
---|
| 1454 | classDefChunks.extend([ |
---|
| 1455 | ind + '#'*50, |
---|
| 1456 | ind + '## CHEETAH GENERATED ATTRIBUTES', |
---|
| 1457 | '\n', |
---|
| 1458 | self.attributes(), |
---|
| 1459 | ]) |
---|
| 1460 | if self.setting('outputMethodsBeforeAttributes'): |
---|
| 1461 | addMethods() |
---|
| 1462 | addAttributes() |
---|
| 1463 | else: |
---|
| 1464 | addAttributes() |
---|
| 1465 | addMethods() |
---|
| 1466 | |
---|
| 1467 | classDef = '\n'.join(classDefChunks) |
---|
| 1468 | self._classDef = classDef |
---|
| 1469 | return classDef |
---|
| 1470 | |
---|
| 1471 | |
---|
| 1472 | def classSignature(self): |
---|
| 1473 | return "class %s(%s):" % (self.className(), self._baseClass) |
---|
| 1474 | |
---|
| 1475 | def classDocstring(self): |
---|
| 1476 | if not self._classDocStringLines: |
---|
| 1477 | return '' |
---|
| 1478 | ind = self.setting('indentationStep') |
---|
| 1479 | docStr = ('%(ind)s"""\n%(ind)s' + |
---|
| 1480 | '\n%(ind)s'.join(self._classDocStringLines) + |
---|
| 1481 | '\n%(ind)s"""\n' |
---|
| 1482 | ) % {'ind':ind} |
---|
| 1483 | return docStr |
---|
| 1484 | |
---|
| 1485 | def methodDefs(self): |
---|
| 1486 | methodDefs = [methGen.methodDef() for methGen in self._finishedMethods()] |
---|
| 1487 | return '\n\n'.join(methodDefs) |
---|
| 1488 | |
---|
| 1489 | def attributes(self): |
---|
| 1490 | attribs = [self.setting('indentationStep') + str(attrib) |
---|
| 1491 | for attrib in self._generatedAttribs ] |
---|
| 1492 | return '\n\n'.join(attribs) |
---|
| 1493 | |
---|
| 1494 | class AutoClassCompiler(ClassCompiler): |
---|
| 1495 | pass |
---|
| 1496 | |
---|
| 1497 | ################################################## |
---|
| 1498 | ## MODULE COMPILERS |
---|
| 1499 | |
---|
| 1500 | class ModuleCompiler(SettingsManager, GenUtils): |
---|
| 1501 | |
---|
| 1502 | parserClass = Parser |
---|
| 1503 | classCompilerClass = AutoClassCompiler |
---|
| 1504 | |
---|
| 1505 | def __init__(self, source=None, file=None, |
---|
| 1506 | moduleName='DynamicallyCompiledCheetahTemplate', |
---|
| 1507 | mainClassName=None, # string |
---|
| 1508 | mainMethodName=None, # string |
---|
| 1509 | baseclassName=None, # string |
---|
| 1510 | extraImportStatements=None, # list of strings |
---|
| 1511 | settings=None # dict |
---|
| 1512 | ): |
---|
| 1513 | super(ModuleCompiler, self).__init__() |
---|
| 1514 | if settings: |
---|
| 1515 | self.updateSettings(settings) |
---|
| 1516 | # disable useStackFrames if the C version of NameMapper isn't compiled |
---|
| 1517 | # it's painfully slow in the Python version and bites Windows users all |
---|
| 1518 | # the time: |
---|
| 1519 | if not NameMapper.C_VERSION: |
---|
| 1520 | if not sys.platform.startswith('java'): |
---|
| 1521 | warnings.warn( |
---|
| 1522 | "\nYou don't have the C version of NameMapper installed! " |
---|
| 1523 | "I'm disabling Cheetah's useStackFrames option as it is " |
---|
| 1524 | "painfully slow with the Python version of NameMapper. " |
---|
| 1525 | "You should get a copy of Cheetah with the compiled C version of NameMapper." |
---|
| 1526 | ) |
---|
| 1527 | self.setSetting('useStackFrames', False) |
---|
| 1528 | |
---|
| 1529 | self._compiled = False |
---|
| 1530 | self._moduleName = moduleName |
---|
| 1531 | if not mainClassName: |
---|
| 1532 | self._mainClassName = moduleName |
---|
| 1533 | else: |
---|
| 1534 | self._mainClassName = mainClassName |
---|
| 1535 | self._mainMethodNameArg = mainMethodName |
---|
| 1536 | if mainMethodName: |
---|
| 1537 | self.setSetting('mainMethodName', mainMethodName) |
---|
| 1538 | self._baseclassName = baseclassName |
---|
| 1539 | |
---|
| 1540 | self._filePath = None |
---|
| 1541 | self._fileMtime = None |
---|
| 1542 | |
---|
| 1543 | if source and file: |
---|
| 1544 | raise TypeError("Cannot compile from a source string AND file.") |
---|
| 1545 | elif isinstance(file, basestring): # it's a filename. |
---|
| 1546 | f = open(file) # Raises IOError. |
---|
| 1547 | source = f.read() |
---|
| 1548 | f.close() |
---|
| 1549 | self._filePath = file |
---|
| 1550 | self._fileMtime = os.path.getmtime(file) |
---|
| 1551 | elif hasattr(file, 'read'): |
---|
| 1552 | source = file.read() # Can't set filename or mtime--they're not accessible. |
---|
| 1553 | elif file: |
---|
| 1554 | raise TypeError("'file' argument must be a filename string or file-like object") |
---|
| 1555 | |
---|
| 1556 | if self._filePath: |
---|
| 1557 | self._fileDirName, self._fileBaseName = os.path.split(self._filePath) |
---|
| 1558 | self._fileBaseNameRoot, self._fileBaseNameExt = os.path.splitext(self._fileBaseName) |
---|
| 1559 | |
---|
| 1560 | if not isinstance(source, basestring): |
---|
| 1561 | source = unicode(source) |
---|
| 1562 | # by converting to string here we allow objects such as other Templates |
---|
| 1563 | # to be passed in |
---|
| 1564 | |
---|
| 1565 | # Handle the #indent directive by converting it to other directives. |
---|
| 1566 | # (Over the long term we'll make it a real directive.) |
---|
| 1567 | if source == "": |
---|
| 1568 | warnings.warn("You supplied an empty string for the source!", ) |
---|
| 1569 | |
---|
| 1570 | else: |
---|
| 1571 | unicodeMatch = unicodeDirectiveRE.search(source) |
---|
| 1572 | encodingMatch = encodingDirectiveRE.match(source) |
---|
| 1573 | if unicodeMatch: |
---|
| 1574 | if encodingMatch: |
---|
| 1575 | raise ParseError( |
---|
| 1576 | self, "#encoding and #unicode are mutually exclusive! " |
---|
| 1577 | "Use one or the other.") |
---|
| 1578 | source = unicodeDirectiveRE.sub('', source) |
---|
| 1579 | if isinstance(source, str): |
---|
| 1580 | encoding = unicodeMatch.group(1) or 'ascii' |
---|
| 1581 | source = unicode(source, encoding) |
---|
| 1582 | elif encodingMatch: |
---|
| 1583 | encodings = encodingMatch.groups() |
---|
| 1584 | if len(encodings): |
---|
| 1585 | encoding = encodings[0] |
---|
| 1586 | source = source.decode(encoding) |
---|
| 1587 | else: |
---|
| 1588 | source = unicode(source) |
---|
| 1589 | |
---|
| 1590 | if source.find('#indent') != -1: #@@TR: undocumented hack |
---|
| 1591 | source = indentize(source) |
---|
| 1592 | |
---|
| 1593 | self._parser = self.parserClass(source, filename=self._filePath, compiler=self) |
---|
| 1594 | self._setupCompilerState() |
---|
| 1595 | |
---|
| 1596 | def __getattr__(self, name): |
---|
| 1597 | """Provide one-way access to the methods and attributes of the |
---|
| 1598 | ClassCompiler, and thereby the MethodCompilers as well. |
---|
| 1599 | |
---|
| 1600 | WARNING: Use .setMethods to assign the attributes of the ClassCompiler |
---|
| 1601 | from the methods of this class!!! or you will be assigning to attributes |
---|
| 1602 | of this object instead. |
---|
| 1603 | """ |
---|
| 1604 | if self.__dict__.has_key(name): |
---|
| 1605 | return self.__dict__[name] |
---|
| 1606 | elif hasattr(self.__class__, name): |
---|
| 1607 | return getattr(self.__class__, name) |
---|
| 1608 | elif self._activeClassesList and hasattr(self._activeClassesList[-1], name): |
---|
| 1609 | return getattr(self._activeClassesList[-1], name) |
---|
| 1610 | else: |
---|
| 1611 | raise AttributeError, name |
---|
| 1612 | |
---|
| 1613 | def _initializeSettings(self): |
---|
| 1614 | self.updateSettings(copy.deepcopy(DEFAULT_COMPILER_SETTINGS)) |
---|
| 1615 | |
---|
| 1616 | def _setupCompilerState(self): |
---|
| 1617 | self._activeClassesList = [] |
---|
| 1618 | self._finishedClassesList = [] # listed by ordered |
---|
| 1619 | self._finishedClassIndex = {} # listed by name |
---|
| 1620 | self._moduleDef = None |
---|
| 1621 | self._moduleShBang = '#!/usr/bin/env python' |
---|
| 1622 | self._moduleEncoding = 'ascii' |
---|
| 1623 | self._moduleEncodingStr = '' |
---|
| 1624 | self._moduleHeaderLines = [] |
---|
| 1625 | self._moduleDocStringLines = [] |
---|
| 1626 | self._specialVars = {} |
---|
| 1627 | self._importStatements = [ |
---|
| 1628 | "import sys", |
---|
| 1629 | "import os", |
---|
| 1630 | "import os.path", |
---|
| 1631 | "import __builtin__", |
---|
| 1632 | "from os.path import getmtime, exists", |
---|
| 1633 | "import time", |
---|
| 1634 | "import types", |
---|
| 1635 | "from Cheetah.Version import MinCompatibleVersion as RequiredCheetahVersion", |
---|
| 1636 | "from Cheetah.Version import MinCompatibleVersionTuple as RequiredCheetahVersionTuple", |
---|
| 1637 | "from Cheetah.Template import Template", |
---|
| 1638 | "from Cheetah.DummyTransaction import *", |
---|
| 1639 | "from Cheetah.NameMapper import NotFound, valueForName, valueFromSearchList, valueFromFrameOrSearchList", |
---|
| 1640 | "from Cheetah.CacheRegion import CacheRegion", |
---|
| 1641 | "import Cheetah.Filters as Filters", |
---|
| 1642 | "import Cheetah.ErrorCatchers as ErrorCatchers", |
---|
| 1643 | ] |
---|
| 1644 | |
---|
| 1645 | self._importedVarNames = ['sys', |
---|
| 1646 | 'os', |
---|
| 1647 | 'os.path', |
---|
| 1648 | 'time', |
---|
| 1649 | 'types', |
---|
| 1650 | 'Template', |
---|
| 1651 | 'DummyTransaction', |
---|
| 1652 | 'NotFound', |
---|
| 1653 | 'Filters', |
---|
| 1654 | 'ErrorCatchers', |
---|
| 1655 | 'CacheRegion', |
---|
| 1656 | ] |
---|
| 1657 | |
---|
| 1658 | self._moduleConstants = [ |
---|
| 1659 | "VFFSL=valueFromFrameOrSearchList", |
---|
| 1660 | "VFSL=valueFromSearchList", |
---|
| 1661 | "VFN=valueForName", |
---|
| 1662 | "currentTime=time.time", |
---|
| 1663 | ] |
---|
| 1664 | |
---|
| 1665 | def compile(self): |
---|
| 1666 | classCompiler = self._spawnClassCompiler(self._mainClassName) |
---|
| 1667 | if self._baseclassName: |
---|
| 1668 | classCompiler.setBaseClass(self._baseclassName) |
---|
| 1669 | self._addActiveClassCompiler(classCompiler) |
---|
| 1670 | self._parser.parse() |
---|
| 1671 | self._swallowClassCompiler(self._popActiveClassCompiler()) |
---|
| 1672 | self._compiled = True |
---|
| 1673 | self._parser.cleanup() |
---|
| 1674 | |
---|
| 1675 | def _spawnClassCompiler(self, className, klass=None): |
---|
| 1676 | if klass is None: |
---|
| 1677 | klass = self.classCompilerClass |
---|
| 1678 | classCompiler = klass(className, |
---|
| 1679 | moduleCompiler=self, |
---|
| 1680 | mainMethodName=self.setting('mainMethodName'), |
---|
| 1681 | fileName=self._filePath, |
---|
| 1682 | settingsManager=self, |
---|
| 1683 | ) |
---|
| 1684 | return classCompiler |
---|
| 1685 | |
---|
| 1686 | def _addActiveClassCompiler(self, classCompiler): |
---|
| 1687 | self._activeClassesList.append(classCompiler) |
---|
| 1688 | |
---|
| 1689 | def _getActiveClassCompiler(self): |
---|
| 1690 | return self._activeClassesList[-1] |
---|
| 1691 | |
---|
| 1692 | def _popActiveClassCompiler(self): |
---|
| 1693 | return self._activeClassesList.pop() |
---|
| 1694 | |
---|
| 1695 | def _swallowClassCompiler(self, classCompiler): |
---|
| 1696 | classCompiler.cleanupState() |
---|
| 1697 | self._finishedClassesList.append( classCompiler ) |
---|
| 1698 | self._finishedClassIndex[classCompiler.className()] = classCompiler |
---|
| 1699 | return classCompiler |
---|
| 1700 | |
---|
| 1701 | def _finishedClasses(self): |
---|
| 1702 | return self._finishedClassesList |
---|
| 1703 | |
---|
| 1704 | def importedVarNames(self): |
---|
| 1705 | return self._importedVarNames |
---|
| 1706 | |
---|
| 1707 | def addImportedVarNames(self, varNames, raw_statement=None): |
---|
| 1708 | settings = self.settings() |
---|
| 1709 | if not varNames: |
---|
| 1710 | return |
---|
| 1711 | if not settings.get('useLegacyImportMode'): |
---|
| 1712 | if raw_statement and getattr(self, '_methodBodyChunks'): |
---|
| 1713 | self.addChunk(raw_statement) |
---|
| 1714 | else: |
---|
| 1715 | self._importedVarNames.extend(varNames) |
---|
| 1716 | |
---|
| 1717 | ## methods for adding stuff to the module and class definitions |
---|
| 1718 | |
---|
| 1719 | def setBaseClass(self, baseClassName): |
---|
| 1720 | if self._mainMethodNameArg: |
---|
| 1721 | self.setMainMethodName(self._mainMethodNameArg) |
---|
| 1722 | else: |
---|
| 1723 | self.setMainMethodName(self.setting('mainMethodNameForSubclasses')) |
---|
| 1724 | |
---|
| 1725 | if self.setting('handlerForExtendsDirective'): |
---|
| 1726 | handler = self.setting('handlerForExtendsDirective') |
---|
| 1727 | baseClassName = handler(compiler=self, baseClassName=baseClassName) |
---|
| 1728 | self._getActiveClassCompiler().setBaseClass(baseClassName) |
---|
| 1729 | elif (not self.setting('autoImportForExtendsDirective') |
---|
| 1730 | or baseClassName=='object' or baseClassName in self.importedVarNames()): |
---|
| 1731 | self._getActiveClassCompiler().setBaseClass(baseClassName) |
---|
| 1732 | # no need to import |
---|
| 1733 | else: |
---|
| 1734 | ################################################## |
---|
| 1735 | ## If the #extends directive contains a classname or modulename that isn't |
---|
| 1736 | # in self.importedVarNames() already, we assume that we need to add |
---|
| 1737 | # an implied 'from ModName import ClassName' where ModName == ClassName. |
---|
| 1738 | # - This is the case in WebKit servlet modules. |
---|
| 1739 | # - We also assume that the final . separates the classname from the |
---|
| 1740 | # module name. This might break if people do something really fancy |
---|
| 1741 | # with their dots and namespaces. |
---|
| 1742 | baseclasses = baseClassName.split(',') |
---|
| 1743 | for klass in baseclasses: |
---|
| 1744 | chunks = klass.split('.') |
---|
| 1745 | if len(chunks)==1: |
---|
| 1746 | self._getActiveClassCompiler().setBaseClass(klass) |
---|
| 1747 | if klass not in self.importedVarNames(): |
---|
| 1748 | modName = klass |
---|
| 1749 | # we assume the class name to be the module name |
---|
| 1750 | # and that it's not a builtin: |
---|
| 1751 | importStatement = "from %s import %s" % (modName, klass) |
---|
| 1752 | self.addImportStatement(importStatement) |
---|
| 1753 | self.addImportedVarNames((klass,)) |
---|
| 1754 | else: |
---|
| 1755 | needToAddImport = True |
---|
| 1756 | modName = chunks[0] |
---|
| 1757 | #print chunks, ':', self.importedVarNames() |
---|
| 1758 | for chunk in chunks[1:-1]: |
---|
| 1759 | if modName in self.importedVarNames(): |
---|
| 1760 | needToAddImport = False |
---|
| 1761 | finalBaseClassName = klass.replace(modName+'.', '') |
---|
| 1762 | self._getActiveClassCompiler().setBaseClass(finalBaseClassName) |
---|
| 1763 | break |
---|
| 1764 | else: |
---|
| 1765 | modName += '.'+chunk |
---|
| 1766 | if needToAddImport: |
---|
| 1767 | modName, finalClassName = '.'.join(chunks[:-1]), chunks[-1] |
---|
| 1768 | #if finalClassName != chunks[:-1][-1]: |
---|
| 1769 | if finalClassName != chunks[-2]: |
---|
| 1770 | # we assume the class name to be the module name |
---|
| 1771 | modName = '.'.join(chunks) |
---|
| 1772 | self._getActiveClassCompiler().setBaseClass(finalClassName) |
---|
| 1773 | importStatement = "from %s import %s" % (modName, finalClassName) |
---|
| 1774 | self.addImportStatement(importStatement) |
---|
| 1775 | self.addImportedVarNames( [finalClassName,] ) |
---|
| 1776 | |
---|
| 1777 | def setCompilerSetting(self, key, valueExpr): |
---|
| 1778 | self.setSetting(key, eval(valueExpr) ) |
---|
| 1779 | self._parser.configureParser() |
---|
| 1780 | |
---|
| 1781 | def setCompilerSettings(self, keywords, settingsStr): |
---|
| 1782 | KWs = keywords |
---|
| 1783 | merge = True |
---|
| 1784 | if 'nomerge' in KWs: |
---|
| 1785 | merge = False |
---|
| 1786 | |
---|
| 1787 | if 'reset' in KWs: |
---|
| 1788 | # @@TR: this is actually caught by the parser at the moment. |
---|
| 1789 | # subject to change in the future |
---|
| 1790 | self._initializeSettings() |
---|
| 1791 | self._parser.configureParser() |
---|
| 1792 | return |
---|
| 1793 | elif 'python' in KWs: |
---|
| 1794 | settingsReader = self.updateSettingsFromPySrcStr |
---|
| 1795 | # this comes from SettingsManager |
---|
| 1796 | else: |
---|
| 1797 | # this comes from SettingsManager |
---|
| 1798 | settingsReader = self.updateSettingsFromConfigStr |
---|
| 1799 | |
---|
| 1800 | settingsReader(settingsStr) |
---|
| 1801 | self._parser.configureParser() |
---|
| 1802 | |
---|
| 1803 | def setShBang(self, shBang): |
---|
| 1804 | self._moduleShBang = shBang |
---|
| 1805 | |
---|
| 1806 | def setModuleEncoding(self, encoding): |
---|
| 1807 | self._moduleEncoding = encoding |
---|
| 1808 | |
---|
| 1809 | def getModuleEncoding(self): |
---|
| 1810 | return self._moduleEncoding |
---|
| 1811 | |
---|
| 1812 | def addModuleHeader(self, line): |
---|
| 1813 | """Adds a header comment to the top of the generated module. |
---|
| 1814 | """ |
---|
| 1815 | self._moduleHeaderLines.append(line) |
---|
| 1816 | |
---|
| 1817 | def addModuleDocString(self, line): |
---|
| 1818 | """Adds a line to the generated module docstring. |
---|
| 1819 | """ |
---|
| 1820 | self._moduleDocStringLines.append(line) |
---|
| 1821 | |
---|
| 1822 | def addModuleGlobal(self, line): |
---|
| 1823 | """Adds a line of global module code. It is inserted after the import |
---|
| 1824 | statements and Cheetah default module constants. |
---|
| 1825 | """ |
---|
| 1826 | self._moduleConstants.append(line) |
---|
| 1827 | |
---|
| 1828 | def addSpecialVar(self, basename, contents, includeUnderscores=True): |
---|
| 1829 | """Adds module __specialConstant__ to the module globals. |
---|
| 1830 | """ |
---|
| 1831 | name = includeUnderscores and '__'+basename+'__' or basename |
---|
| 1832 | self._specialVars[name] = contents.strip() |
---|
| 1833 | |
---|
| 1834 | def addImportStatement(self, impStatement): |
---|
| 1835 | settings = self.settings() |
---|
| 1836 | if not self._methodBodyChunks or settings.get('useLegacyImportMode'): |
---|
| 1837 | # In the case where we are importing inline in the middle of a source block |
---|
| 1838 | # we don't want to inadvertantly import the module at the top of the file either |
---|
| 1839 | self._importStatements.append(impStatement) |
---|
| 1840 | |
---|
| 1841 | #@@TR 2005-01-01: there's almost certainly a cleaner way to do this! |
---|
| 1842 | importVarNames = impStatement[impStatement.find('import') + len('import'):].split(',') |
---|
| 1843 | importVarNames = [var.split()[-1] for var in importVarNames] # handles aliases |
---|
| 1844 | importVarNames = [var for var in importVarNames if not var == '*'] |
---|
| 1845 | self.addImportedVarNames(importVarNames, raw_statement=impStatement) #used by #extend for auto-imports |
---|
| 1846 | |
---|
| 1847 | def addAttribute(self, attribName, expr): |
---|
| 1848 | self._getActiveClassCompiler().addAttribute(attribName + ' =' + expr) |
---|
| 1849 | |
---|
| 1850 | def addComment(self, comm): |
---|
| 1851 | if re.match(r'#+$',comm): # skip bar comments |
---|
| 1852 | return |
---|
| 1853 | |
---|
| 1854 | specialVarMatch = specialVarRE.match(comm) |
---|
| 1855 | if specialVarMatch: |
---|
| 1856 | # @@TR: this is a bit hackish and is being replaced with |
---|
| 1857 | # #set module varName = ... |
---|
| 1858 | return self.addSpecialVar(specialVarMatch.group(1), |
---|
| 1859 | comm[specialVarMatch.end():]) |
---|
| 1860 | elif comm.startswith('doc:'): |
---|
| 1861 | addLine = self.addMethDocString |
---|
| 1862 | comm = comm[len('doc:'):].strip() |
---|
| 1863 | elif comm.startswith('doc-method:'): |
---|
| 1864 | addLine = self.addMethDocString |
---|
| 1865 | comm = comm[len('doc-method:'):].strip() |
---|
| 1866 | elif comm.startswith('doc-module:'): |
---|
| 1867 | addLine = self.addModuleDocString |
---|
| 1868 | comm = comm[len('doc-module:'):].strip() |
---|
| 1869 | elif comm.startswith('doc-class:'): |
---|
| 1870 | addLine = self.addClassDocString |
---|
| 1871 | comm = comm[len('doc-class:'):].strip() |
---|
| 1872 | elif comm.startswith('header:'): |
---|
| 1873 | addLine = self.addModuleHeader |
---|
| 1874 | comm = comm[len('header:'):].strip() |
---|
| 1875 | else: |
---|
| 1876 | addLine = self.addMethComment |
---|
| 1877 | |
---|
| 1878 | for line in comm.splitlines(): |
---|
| 1879 | addLine(line) |
---|
| 1880 | |
---|
| 1881 | ## methods for module code wrapping |
---|
| 1882 | |
---|
| 1883 | def getModuleCode(self): |
---|
| 1884 | if not self._compiled: |
---|
| 1885 | self.compile() |
---|
| 1886 | if self._moduleDef: |
---|
| 1887 | return self._moduleDef |
---|
| 1888 | else: |
---|
| 1889 | return self.wrapModuleDef() |
---|
| 1890 | |
---|
| 1891 | __str__ = getModuleCode |
---|
| 1892 | |
---|
| 1893 | def wrapModuleDef(self): |
---|
| 1894 | self.addSpecialVar('CHEETAH_docstring', self.setting('defDocStrMsg')) |
---|
| 1895 | self.addModuleGlobal('__CHEETAH_version__ = %r'%Version) |
---|
| 1896 | self.addModuleGlobal('__CHEETAH_versionTuple__ = %r'%(VersionTuple,)) |
---|
| 1897 | if self.setting('addTimestampsToCompilerOutput'): |
---|
| 1898 | self.addModuleGlobal('__CHEETAH_genTime__ = %r'%time.time()) |
---|
| 1899 | self.addModuleGlobal('__CHEETAH_genTimestamp__ = %r'%self.timestamp()) |
---|
| 1900 | if self._filePath: |
---|
| 1901 | timestamp = self.timestamp(self._fileMtime) |
---|
| 1902 | self.addModuleGlobal('__CHEETAH_src__ = %r'%self._filePath) |
---|
| 1903 | self.addModuleGlobal('__CHEETAH_srcLastModified__ = %r'%timestamp) |
---|
| 1904 | else: |
---|
| 1905 | self.addModuleGlobal('__CHEETAH_src__ = None') |
---|
| 1906 | self.addModuleGlobal('__CHEETAH_srcLastModified__ = None') |
---|
| 1907 | |
---|
| 1908 | moduleDef = """%(header)s |
---|
| 1909 | %(docstring)s |
---|
| 1910 | |
---|
| 1911 | ################################################## |
---|
| 1912 | ## DEPENDENCIES |
---|
| 1913 | %(imports)s |
---|
| 1914 | |
---|
| 1915 | ################################################## |
---|
| 1916 | ## MODULE CONSTANTS |
---|
| 1917 | %(constants)s |
---|
| 1918 | %(specialVars)s |
---|
| 1919 | |
---|
| 1920 | if __CHEETAH_versionTuple__ < RequiredCheetahVersionTuple: |
---|
| 1921 | raise AssertionError( |
---|
| 1922 | 'This template was compiled with Cheetah version' |
---|
| 1923 | ' %%s. Templates compiled before version %%s must be recompiled.'%%( |
---|
| 1924 | __CHEETAH_version__, RequiredCheetahVersion)) |
---|
| 1925 | |
---|
| 1926 | ################################################## |
---|
| 1927 | ## CLASSES |
---|
| 1928 | |
---|
| 1929 | %(classes)s |
---|
| 1930 | |
---|
| 1931 | ## END CLASS DEFINITION |
---|
| 1932 | |
---|
| 1933 | if not hasattr(%(mainClassName)s, '_initCheetahAttributes'): |
---|
| 1934 | templateAPIClass = getattr(%(mainClassName)s, '_CHEETAH_templateClass', Template) |
---|
| 1935 | templateAPIClass._addCheetahPlumbingCodeToClass(%(mainClassName)s) |
---|
| 1936 | |
---|
| 1937 | %(footer)s |
---|
| 1938 | """ % {'header':self.moduleHeader(), |
---|
| 1939 | 'docstring':self.moduleDocstring(), |
---|
| 1940 | 'specialVars':self.specialVars(), |
---|
| 1941 | 'imports':self.importStatements(), |
---|
| 1942 | 'constants':self.moduleConstants(), |
---|
| 1943 | 'classes':self.classDefs(), |
---|
| 1944 | 'footer':self.moduleFooter(), |
---|
| 1945 | 'mainClassName':self._mainClassName, |
---|
| 1946 | } |
---|
| 1947 | |
---|
| 1948 | self._moduleDef = moduleDef |
---|
| 1949 | return moduleDef |
---|
| 1950 | |
---|
| 1951 | def timestamp(self, theTime=None): |
---|
| 1952 | if not theTime: |
---|
| 1953 | theTime = time.time() |
---|
| 1954 | return time.asctime(time.localtime(theTime)) |
---|
| 1955 | |
---|
| 1956 | def moduleHeader(self): |
---|
| 1957 | header = self._moduleShBang + '\n' |
---|
| 1958 | header += self._moduleEncodingStr + '\n' |
---|
| 1959 | if self._moduleHeaderLines: |
---|
| 1960 | offSet = self.setting('commentOffset') |
---|
| 1961 | |
---|
| 1962 | header += ( |
---|
| 1963 | '#' + ' '*offSet + |
---|
| 1964 | ('\n#'+ ' '*offSet).join(self._moduleHeaderLines) + '\n') |
---|
| 1965 | |
---|
| 1966 | return header |
---|
| 1967 | |
---|
| 1968 | def moduleDocstring(self): |
---|
| 1969 | if not self._moduleDocStringLines: |
---|
| 1970 | return '' |
---|
| 1971 | |
---|
| 1972 | return ('"""' + |
---|
| 1973 | '\n'.join(self._moduleDocStringLines) + |
---|
| 1974 | '\n"""\n') |
---|
| 1975 | |
---|
| 1976 | def specialVars(self): |
---|
| 1977 | chunks = [] |
---|
| 1978 | theVars = self._specialVars |
---|
| 1979 | keys = theVars.keys() |
---|
| 1980 | keys.sort() |
---|
| 1981 | for key in keys: |
---|
| 1982 | chunks.append(key + ' = ' + repr(theVars[key]) ) |
---|
| 1983 | return '\n'.join(chunks) |
---|
| 1984 | |
---|
| 1985 | def importStatements(self): |
---|
| 1986 | return '\n'.join(self._importStatements) |
---|
| 1987 | |
---|
| 1988 | def moduleConstants(self): |
---|
| 1989 | return '\n'.join(self._moduleConstants) |
---|
| 1990 | |
---|
| 1991 | def classDefs(self): |
---|
| 1992 | classDefs = [klass.classDef() for klass in self._finishedClasses()] |
---|
| 1993 | return '\n\n'.join(classDefs) |
---|
| 1994 | |
---|
| 1995 | def moduleFooter(self): |
---|
| 1996 | return """ |
---|
| 1997 | # CHEETAH was developed by Tavis Rudd and Mike Orr |
---|
| 1998 | # with code, advice and input from many other volunteers. |
---|
| 1999 | # For more information visit http://www.CheetahTemplate.org/ |
---|
| 2000 | |
---|
| 2001 | ################################################## |
---|
| 2002 | ## if run from command line: |
---|
| 2003 | if __name__ == '__main__': |
---|
| 2004 | from Cheetah.TemplateCmdLineIface import CmdLineIface |
---|
| 2005 | CmdLineIface(templateObj=%(className)s()).run() |
---|
| 2006 | |
---|
| 2007 | """ % {'className':self._mainClassName} |
---|
| 2008 | |
---|
| 2009 | |
---|
| 2010 | ################################################## |
---|
| 2011 | ## Make Compiler an alias for ModuleCompiler |
---|
| 2012 | |
---|
| 2013 | Compiler = ModuleCompiler |
---|