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 |
---|