| 1 |
|
|---|
| 2 |
|
|---|
| 3 |
"""Provides the core API for Cheetah. |
|---|
| 4 |
|
|---|
| 5 |
|
|---|
| 6 |
|
|---|
| 7 |
|
|---|
| 8 |
|
|---|
| 9 |
|
|---|
| 10 |
|
|---|
| 11 |
|
|---|
| 12 |
|
|---|
| 13 |
|
|---|
| 14 |
|
|---|
| 15 |
|
|---|
| 16 |
__author__ = "Tavis Rudd <tavis@damnsimple.com>" |
|---|
| 17 |
__revision__ = "$Revision: 1.181 $"[11:-2] |
|---|
| 18 |
|
|---|
| 19 |
|
|---|
| 20 |
|
|---|
| 21 |
import sys |
|---|
| 22 |
import re |
|---|
| 23 |
import new |
|---|
| 24 |
import string |
|---|
| 25 |
import os.path |
|---|
| 26 |
import time |
|---|
| 27 |
from random import randrange |
|---|
| 28 |
import imp |
|---|
| 29 |
import inspect |
|---|
| 30 |
import StringIO |
|---|
| 31 |
import traceback |
|---|
| 32 |
import pprint |
|---|
| 33 |
import cgi |
|---|
| 34 |
import types |
|---|
| 35 |
from types import StringType, ClassType |
|---|
| 36 |
try: |
|---|
| 37 |
from types import StringTypes |
|---|
| 38 |
except ImportError: |
|---|
| 39 |
StringTypes = (types.StringType,types.UnicodeType) |
|---|
| 40 |
try: |
|---|
| 41 |
from types import BooleanType |
|---|
| 42 |
boolTypeAvailable = True |
|---|
| 43 |
except ImportError: |
|---|
| 44 |
boolTypeAvailable = False |
|---|
| 45 |
|
|---|
| 46 |
try: |
|---|
| 47 |
from threading import Lock |
|---|
| 48 |
except ImportError: |
|---|
| 49 |
class Lock: |
|---|
| 50 |
def acquire(self): pass |
|---|
| 51 |
def release(self): pass |
|---|
| 52 |
|
|---|
| 53 |
from Cheetah.Version import convertVersionStringToTuple, MinCompatibleVersionTuple |
|---|
| 54 |
from Cheetah.Version import MinCompatibleVersion |
|---|
| 55 |
|
|---|
| 56 |
from Cheetah.Servlet import Servlet |
|---|
| 57 |
|
|---|
| 58 |
from Cheetah.Parser import ParseError, SourceReader |
|---|
| 59 |
from Cheetah.Compiler import Compiler, DEFAULT_COMPILER_SETTINGS |
|---|
| 60 |
from Cheetah import ErrorCatchers |
|---|
| 61 |
from Cheetah import Filters |
|---|
| 62 |
from Cheetah.convertTmplPathToModuleName import convertTmplPathToModuleName |
|---|
| 63 |
from Cheetah.Utils import VerifyType |
|---|
| 64 |
from Cheetah.Utils.Misc import checkKeywords |
|---|
| 65 |
from Cheetah.Utils.Indenter import Indenter |
|---|
| 66 |
|
|---|
| 67 |
from Cheetah.NameMapper import NotFound, valueFromSearchList |
|---|
| 68 |
from Cheetah.CacheStore import MemoryCacheStore, MemcachedCacheStore |
|---|
| 69 |
from Cheetah.CacheRegion import CacheRegion |
|---|
| 70 |
from Cheetah.Utils.WebInputMixin import _Converter, _lookup, NonNumericInputError |
|---|
| 71 |
|
|---|
| 72 |
from Cheetah.Unspecified import Unspecified |
|---|
| 73 |
|
|---|
| 74 |
class Error(Exception): pass |
|---|
| 75 |
class PreprocessError(Error): pass |
|---|
| 76 |
|
|---|
| 77 |
def hashList(l): |
|---|
| 78 |
hashedList = [] |
|---|
| 79 |
for v in l: |
|---|
| 80 |
if isinstance(v, dict): |
|---|
| 81 |
v = hashDict(v) |
|---|
| 82 |
elif isinstance(v, list): |
|---|
| 83 |
v = hashList(v) |
|---|
| 84 |
hashedList.append(v) |
|---|
| 85 |
return hash(tuple(hashedList)) |
|---|
| 86 |
|
|---|
| 87 |
def hashDict(d): |
|---|
| 88 |
items = d.items() |
|---|
| 89 |
items.sort() |
|---|
| 90 |
hashedList = [] |
|---|
| 91 |
for k, v in items: |
|---|
| 92 |
if isinstance(v, dict): |
|---|
| 93 |
v = hashDict(v) |
|---|
| 94 |
elif isinstance(v, list): |
|---|
| 95 |
v = hashList(v) |
|---|
| 96 |
hashedList.append((k,v)) |
|---|
| 97 |
return hash(tuple(hashedList)) |
|---|
| 98 |
|
|---|
| 99 |
|
|---|
| 100 |
|
|---|
| 101 |
|
|---|
| 102 |
def _genUniqueModuleName(baseModuleName): |
|---|
| 103 |
"""The calling code is responsible for concurrency locking. |
|---|
| 104 |
""" |
|---|
| 105 |
if baseModuleName not in sys.modules: |
|---|
| 106 |
finalName = baseModuleName |
|---|
| 107 |
else: |
|---|
| 108 |
finalName = ('cheetah_'+baseModuleName |
|---|
| 109 |
+'_' |
|---|
| 110 |
+''.join(map(lambda x: '%02d' % x, time.localtime(time.time())[:6])) |
|---|
| 111 |
+ str(randrange(10000, 99999))) |
|---|
| 112 |
return finalName |
|---|
| 113 |
|
|---|
| 114 |
|
|---|
| 115 |
|
|---|
| 116 |
_formUsedByWebInput = None |
|---|
| 117 |
|
|---|
| 118 |
|
|---|
| 119 |
def valOrDefault(val, default): |
|---|
| 120 |
if val is not Unspecified: return val |
|---|
| 121 |
else: return default |
|---|
| 122 |
|
|---|
| 123 |
|
|---|
| 124 |
class CompileCacheItem: |
|---|
| 125 |
pass |
|---|
| 126 |
|
|---|
| 127 |
class TemplatePreprocessor: |
|---|
| 128 |
"""This is used with the preprocessors argument to Template.compile(). |
|---|
| 129 |
|
|---|
| 130 |
See the docstring for Template.compile |
|---|
| 131 |
|
|---|
| 132 |
** Preprocessors are an advanced topic ** |
|---|
| 133 |
""" |
|---|
| 134 |
def __init__(self, settings): |
|---|
| 135 |
self._settings = settings |
|---|
| 136 |
|
|---|
| 137 |
def preprocess(self, source, file): |
|---|
| 138 |
"""Create an intermediate template and return the source code |
|---|
| 139 |
it outputs |
|---|
| 140 |
""" |
|---|
| 141 |
settings = self._settings |
|---|
| 142 |
if not source: |
|---|
| 143 |
if isinstance(file, (str, unicode)): |
|---|
| 144 |
f = open(file) |
|---|
| 145 |
source = f.read() |
|---|
| 146 |
f.close() |
|---|
| 147 |
elif hasattr(file, 'read'): |
|---|
| 148 |
source = file.read() |
|---|
| 149 |
file = None |
|---|
| 150 |
|
|---|
| 151 |
templateAPIClass = settings.templateAPIClass |
|---|
| 152 |
possibleKwArgs = [ |
|---|
| 153 |
arg for arg in |
|---|
| 154 |
inspect.getargs(templateAPIClass.compile.im_func.func_code)[0] |
|---|
| 155 |
if arg not in ('klass', 'source', 'file',)] |
|---|
| 156 |
|
|---|
| 157 |
compileKwArgs = {} |
|---|
| 158 |
for arg in possibleKwArgs: |
|---|
| 159 |
if hasattr(settings, arg): |
|---|
| 160 |
compileKwArgs[arg] = getattr(settings, arg) |
|---|
| 161 |
|
|---|
| 162 |
tmplClass = templateAPIClass.compile(source=source, file=file, **compileKwArgs) |
|---|
| 163 |
tmplInstance = tmplClass(**settings.templateInitArgs) |
|---|
| 164 |
outputSource = settings.outputTransformer(tmplInstance) |
|---|
| 165 |
outputFile = None |
|---|
| 166 |
return outputSource, outputFile |
|---|
| 167 |
|
|---|
| 168 |
class Template(Servlet): |
|---|
| 169 |
"""This class provides a) methods used by templates at runtime and b) |
|---|
| 170 |
methods for compiling Cheetah source code into template classes. |
|---|
| 171 |
|
|---|
| 172 |
This documentation assumes you already know Python and the basics of object |
|---|
| 173 |
oriented programming. If you don't know Python, see the sections of the |
|---|
| 174 |
Cheetah Users' Guide for non-programmers. It also assumes you have read |
|---|
| 175 |
about Cheetah's syntax in the Users' Guide. |
|---|
| 176 |
|
|---|
| 177 |
The following explains how to use Cheetah from within Python programs or via |
|---|
| 178 |
the interpreter. If you statically compile your templates on the command |
|---|
| 179 |
line using the 'cheetah' script, this is not relevant to you. Statically |
|---|
| 180 |
compiled Cheetah template modules/classes (e.g. myTemplate.py: |
|---|
| 181 |
MyTemplateClasss) are just like any other Python module or class. Also note, |
|---|
| 182 |
most Python web frameworks (Webware, Aquarium, mod_python, Turbogears, |
|---|
| 183 |
CherryPy, Quixote, etc.) provide plugins that handle Cheetah compilation for |
|---|
| 184 |
you. |
|---|
| 185 |
|
|---|
| 186 |
There are several possible usage patterns: |
|---|
| 187 |
1) tclass = Template.compile(src) |
|---|
| 188 |
t1 = tclass() # or tclass(namespaces=[namespace,...]) |
|---|
| 189 |
t2 = tclass() # or tclass(namespaces=[namespace2,...]) |
|---|
| 190 |
outputStr = str(t1) # or outputStr = t1.aMethodYouDefined() |
|---|
| 191 |
|
|---|
| 192 |
Template.compile provides a rich and very flexible API via its |
|---|
| 193 |
optional arguments so there are many possible variations of this |
|---|
| 194 |
pattern. One example is: |
|---|
| 195 |
tclass = Template.compile('hello $name from $caller', baseclass=dict) |
|---|
| 196 |
print tclass(name='world', caller='me') |
|---|
| 197 |
See the Template.compile() docstring for more details. |
|---|
| 198 |
|
|---|
| 199 |
2) tmplInstance = Template(src) |
|---|
| 200 |
# or Template(src, namespaces=[namespace,...]) |
|---|
| 201 |
outputStr = str(tmplInstance) # or outputStr = tmplInstance.aMethodYouDefined(...args...) |
|---|
| 202 |
|
|---|
| 203 |
Notes on the usage patterns: |
|---|
| 204 |
|
|---|
| 205 |
usage pattern 1) |
|---|
| 206 |
This is the most flexible, but it is slightly more verbose unless you |
|---|
| 207 |
write a wrapper function to hide the plumbing. Under the hood, all |
|---|
| 208 |
other usage patterns are based on this approach. Templates compiled |
|---|
| 209 |
this way can #extend (subclass) any Python baseclass: old-style or |
|---|
| 210 |
new-style (based on object or a builtin type). |
|---|
| 211 |
|
|---|
| 212 |
usage pattern 2) |
|---|
| 213 |
This was Cheetah's original usage pattern. It returns an instance, |
|---|
| 214 |
but you can still access the generated class via |
|---|
| 215 |
tmplInstance.__class__. If you want to use several different |
|---|
| 216 |
namespace 'searchLists' with a single template source definition, |
|---|
| 217 |
you're better off with Template.compile (1). |
|---|
| 218 |
|
|---|
| 219 |
Limitations (use pattern 1 instead): |
|---|
| 220 |
- Templates compiled this way can only #extend subclasses of the |
|---|
| 221 |
new-style 'object' baseclass. Cheetah.Template is a subclass of |
|---|
| 222 |
'object'. You also can not #extend dict, list, or other builtin |
|---|
| 223 |
types. |
|---|
| 224 |
- If your template baseclass' __init__ constructor expects args there |
|---|
| 225 |
is currently no way to pass them in. |
|---|
| 226 |
|
|---|
| 227 |
If you need to subclass a dynamically compiled Cheetah class, do something like this: |
|---|
| 228 |
from Cheetah.Template import Template |
|---|
| 229 |
T1 = Template.compile('$meth1 #def meth1: this is meth1 in T1') |
|---|
| 230 |
T2 = Template.compile('#implements meth1\nthis is meth1 redefined in T2', baseclass=T1) |
|---|
| 231 |
print T1, T1() |
|---|
| 232 |
print T2, T2() |
|---|
| 233 |
|
|---|
| 234 |
|
|---|
| 235 |
Note about class and instance attribute names: |
|---|
| 236 |
Attributes used by Cheetah have a special prefix to avoid confusion with |
|---|
| 237 |
the attributes of the templates themselves or those of template |
|---|
| 238 |
baseclasses. |
|---|
| 239 |
|
|---|
| 240 |
Class attributes which are used in class methods look like this: |
|---|
| 241 |
klass._CHEETAH_useCompilationCache (_CHEETAH_xxx) |
|---|
| 242 |
|
|---|
| 243 |
Instance attributes look like this: |
|---|
| 244 |
klass._CHEETAH__globalSetVars (_CHEETAH__xxx with 2 underscores) |
|---|
| 245 |
""" |
|---|
| 246 |
|
|---|
| 247 |
|
|---|
| 248 |
_CHEETAH_requiredCheetahMethods = ( |
|---|
| 249 |
'_initCheetahInstance', |
|---|
| 250 |
'searchList', |
|---|
| 251 |
'errorCatcher', |
|---|
| 252 |
'getVar', |
|---|
| 253 |
'varExists', |
|---|
| 254 |
'getFileContents', |
|---|
| 255 |
'i18n', |
|---|
| 256 |
'runAsMainProgram', |
|---|
| 257 |
'respond', |
|---|
| 258 |
'shutdown', |
|---|
| 259 |
'webInput', |
|---|
| 260 |
'serverSidePath', |
|---|
| 261 |
'generatedClassCode', |
|---|
| 262 |
'generatedModuleCode', |
|---|
| 263 |
|
|---|
| 264 |
'_getCacheStore', |
|---|
| 265 |
'_getCacheStoreIdPrefix', |
|---|
| 266 |
'_createCacheRegion', |
|---|
| 267 |
'getCacheRegion', |
|---|
| 268 |
'getCacheRegions', |
|---|
| 269 |
'refreshCache', |
|---|
| 270 |
|
|---|
| 271 |
'_handleCheetahInclude', |
|---|
| 272 |
'_getTemplateAPIClassForIncludeDirectiveCompilation', |
|---|
| 273 |
) |
|---|
| 274 |
_CHEETAH_requiredCheetahClassMethods = ('subclass',) |
|---|
| 275 |
_CHEETAH_requiredCheetahClassAttributes = ('cacheRegionClass','cacheStore', |
|---|
| 276 |
'cacheStoreIdPrefix','cacheStoreClass') |
|---|
| 277 |
|
|---|
| 278 |
|
|---|
| 279 |
_CHEETAH_cacheModuleFilesForTracebacks = False |
|---|
| 280 |
_CHEETAH_cacheDirForModuleFiles = None |
|---|
| 281 |
|
|---|
| 282 |
_CHEETAH_compileCache = dict() |
|---|
| 283 |
|
|---|
| 284 |
|
|---|
| 285 |
|
|---|
| 286 |
|
|---|
| 287 |
|
|---|
| 288 |
_CHEETAH_compileLock = Lock() |
|---|
| 289 |
_CHEETAH_defaultMainMethodName = None |
|---|
| 290 |
_CHEETAH_compilerSettings = None |
|---|
| 291 |
_CHEETAH_compilerClass = Compiler |
|---|
| 292 |
_CHEETAH_cacheCompilationResults = True |
|---|
| 293 |
_CHEETAH_useCompilationCache = True |
|---|
| 294 |
_CHEETAH_keepRefToGeneratedCode = True |
|---|
| 295 |
_CHEETAH_defaultBaseclassForTemplates = None |
|---|
| 296 |
_CHEETAH_defaultClassNameForTemplates = None |
|---|
| 297 |
|
|---|
| 298 |
_CHEETAH_defaultMainMethodNameForTemplates = None |
|---|
| 299 |
_CHEETAH_defaultModuleNameForTemplates = 'DynamicallyCompiledCheetahTemplate' |
|---|
| 300 |
_CHEETAH_defaultModuleGlobalsForTemplates = None |
|---|
| 301 |
_CHEETAH_preprocessors = None |
|---|
| 302 |
_CHEETAH_defaultPreprocessorClass = TemplatePreprocessor |
|---|
| 303 |
|
|---|
| 304 |
|
|---|
| 305 |
_CHEETAH_generatedModuleCode = None |
|---|
| 306 |
NonNumericInputError = NonNumericInputError |
|---|
| 307 |
_CHEETAH_cacheRegionClass = CacheRegion |
|---|
| 308 |
_CHEETAH_cacheStoreClass = MemoryCacheStore |
|---|
| 309 |
|
|---|
| 310 |
_CHEETAH_cacheStore = None |
|---|
| 311 |
_CHEETAH_cacheStoreIdPrefix = None |
|---|
| 312 |
|
|---|
| 313 |
def _getCompilerClass(klass, source=None, file=None): |
|---|
| 314 |
return klass._CHEETAH_compilerClass |
|---|
| 315 |
_getCompilerClass = classmethod(_getCompilerClass) |
|---|
| 316 |
|
|---|
| 317 |
def _getCompilerSettings(klass, source=None, file=None): |
|---|
| 318 |
return klass._CHEETAH_compilerSettings |
|---|
| 319 |
_getCompilerSettings = classmethod(_getCompilerSettings) |
|---|
| 320 |
|
|---|
| 321 |
def compile(klass, source=None, file=None, |
|---|
| 322 |
returnAClass=True, |
|---|
| 323 |
|
|---|
| 324 |
compilerSettings=Unspecified, |
|---|
| 325 |
compilerClass=Unspecified, |
|---|
| 326 |
moduleName=None, |
|---|
| 327 |
className=Unspecified, |
|---|
| 328 |
mainMethodName=Unspecified, |
|---|
| 329 |
baseclass=Unspecified, |
|---|
| 330 |
moduleGlobals=Unspecified, |
|---|
| 331 |
cacheCompilationResults=Unspecified, |
|---|
| 332 |
useCache=Unspecified, |
|---|
| 333 |
preprocessors=Unspecified, |
|---|
| 334 |
cacheModuleFilesForTracebacks=Unspecified, |
|---|
| 335 |
cacheDirForModuleFiles=Unspecified, |
|---|
| 336 |
|
|---|
| 337 |
keepRefToGeneratedCode=Unspecified, |
|---|
| 338 |
): |
|---|
| 339 |
|
|---|
| 340 |
""" |
|---|
| 341 |
The core API for compiling Cheetah source code into template classes. |
|---|
| 342 |
|
|---|
| 343 |
This class method compiles Cheetah source code and returns a python |
|---|
| 344 |
class. You then create template instances using that class. All |
|---|
| 345 |
Cheetah's other compilation API's use this method under the hood. |
|---|
| 346 |
|
|---|
| 347 |
Internally, this method a) parses the Cheetah source code and generates |
|---|
| 348 |
Python code defining a module with a single class in it, b) dynamically |
|---|
| 349 |
creates a module object with a unique name, c) execs the generated code |
|---|
| 350 |
in that module's namespace then inserts the module into sys.modules, and |
|---|
| 351 |
d) returns a reference to the generated class. If you want to get the |
|---|
| 352 |
generated python source code instead, pass the argument |
|---|
| 353 |
returnAClass=False. |
|---|
| 354 |
|
|---|
| 355 |
It caches generated code and classes. See the descriptions of the |
|---|
| 356 |
arguments'cacheCompilationResults' and 'useCache' for details. This |
|---|
| 357 |
doesn't mean that templates will automatically recompile themselves when |
|---|
| 358 |
the source file changes. Rather, if you call Template.compile(src) or |
|---|
| 359 |
Template.compile(file=path) repeatedly it will attempt to return a |
|---|
| 360 |
cached class definition instead of recompiling. |
|---|
| 361 |
|
|---|
| 362 |
Hooks are provided template source preprocessing. See the notes on the |
|---|
| 363 |
'preprocessors' arg. |
|---|
| 364 |
|
|---|
| 365 |
If you are an advanced user and need to customize the way Cheetah parses |
|---|
| 366 |
source code or outputs Python code, you should check out the |
|---|
| 367 |
compilerSettings argument. |
|---|
| 368 |
|
|---|
| 369 |
Arguments: |
|---|
| 370 |
You must provide either a 'source' or 'file' arg, but not both: |
|---|
| 371 |
- source (string or None) |
|---|
| 372 |
- file (string path, file-like object, or None) |
|---|
| 373 |
|
|---|
| 374 |
The rest of the arguments are strictly optional. All but the first |
|---|
| 375 |
have defaults in attributes of the Template class which can be |
|---|
| 376 |
overridden in subclasses of this class. Working with most of these is |
|---|
| 377 |
an advanced topic. |
|---|
| 378 |
|
|---|
| 379 |
- returnAClass=True |
|---|
| 380 |
If false, return the generated module code rather than a class. |
|---|
| 381 |
|
|---|
| 382 |
- compilerSettings (a dict) |
|---|
| 383 |
Default: Template._CHEETAH_compilerSettings=None |
|---|
| 384 |
|
|---|
| 385 |
a dictionary of settings to override those defined in |
|---|
| 386 |
DEFAULT_COMPILER_SETTINGS. These can also be overridden in your |
|---|
| 387 |
template source code with the #compiler or #compiler-settings |
|---|
| 388 |
directives. |
|---|
| 389 |
|
|---|
| 390 |
- compilerClass (a class) |
|---|
| 391 |
Default: Template._CHEETAH_compilerClass=Cheetah.Compiler.Compiler |
|---|
| 392 |
|
|---|
| 393 |
a subclass of Cheetah.Compiler.Compiler. Mucking with this is a |
|---|
| 394 |
very advanced topic. |
|---|
| 395 |
|
|---|
| 396 |
- moduleName (a string) |
|---|
| 397 |
Default: |
|---|
| 398 |
Template._CHEETAH_defaultModuleNameForTemplates |
|---|
| 399 |
='DynamicallyCompiledCheetahTemplate' |
|---|
| 400 |
|
|---|
| 401 |
What to name the generated Python module. If the provided value is |
|---|
| 402 |
None and a file arg was given, the moduleName is created from the |
|---|
| 403 |
file path. In all cases if the moduleName provided is already in |
|---|
| 404 |
sys.modules it is passed through a filter that generates a unique |
|---|
| 405 |
variant of the name. |
|---|
| 406 |
|
|---|
| 407 |
|
|---|
| 408 |
- className (a string) |
|---|
| 409 |
Default: Template._CHEETAH_defaultClassNameForTemplates=None |
|---|
| 410 |
|
|---|
| 411 |
What to name the generated Python class. If the provided value is |
|---|
| 412 |
None, the moduleName is use as the class name. |
|---|
| 413 |
|
|---|
| 414 |
- mainMethodName (a string) |
|---|
| 415 |
Default: |
|---|
| 416 |
Template._CHEETAH_defaultMainMethodNameForTemplates |
|---|
| 417 |
=None (and thus DEFAULT_COMPILER_SETTINGS['mainMethodName']) |
|---|
| 418 |
|
|---|
| 419 |
What to name the main output generating method in the compiled |
|---|
| 420 |
template class. |
|---|
| 421 |
|
|---|
| 422 |
- baseclass (a string or a class) |
|---|
| 423 |
Default: Template._CHEETAH_defaultBaseclassForTemplates=None |
|---|
| 424 |
|
|---|
| 425 |
Specifies the baseclass for the template without manually |
|---|
| 426 |
including an #extends directive in the source. The #extends |
|---|
| 427 |
directive trumps this arg. |
|---|
| 428 |
|
|---|
| 429 |
If the provided value is a string you must make sure that a class |
|---|
| 430 |
reference by that name is available to your template, either by |
|---|
| 431 |
using an #import directive or by providing it in the arg |
|---|
| 432 |
'moduleGlobals'. |
|---|
| 433 |
|
|---|
| 434 |
If the provided value is a class, Cheetah will handle all the |
|---|
| 435 |
details for you. |
|---|
| 436 |
|
|---|
| 437 |
- moduleGlobals (a dict) |
|---|
| 438 |
Default: Template._CHEETAH_defaultModuleGlobalsForTemplates=None |
|---|
| 439 |
|
|---|
| 440 |
A dict of vars that will be added to the global namespace of the |
|---|
| 441 |
module the generated code is executed in, prior to the execution |
|---|
| 442 |
of that code. This should be Python values, not code strings! |
|---|
| 443 |
|
|---|
| 444 |
- cacheCompilationResults (True/False) |
|---|
| 445 |
Default: Template._CHEETAH_cacheCompilationResults=True |
|---|
| 446 |
|
|---|
| 447 |
Tells Cheetah to cache the generated code and classes so that they |
|---|
| 448 |
can be reused if Template.compile() is called multiple times with |
|---|
| 449 |
the same source and options. |
|---|
| 450 |
|
|---|
| 451 |
- useCache (True/False) |
|---|
| 452 |
Default: Template._CHEETAH_useCompilationCache=True |
|---|
| 453 |
|
|---|
| 454 |
Should the compilation cache be used? If True and a previous |
|---|
| 455 |
compilation created a cached template class with the same source |
|---|
| 456 |
code, compiler settings and other options, the cached template |
|---|
| 457 |
class will be returned. |
|---|
| 458 |
|
|---|
| 459 |
- cacheModuleFilesForTracebacks (True/False) |
|---|
| 460 |
Default: Template._CHEETAH_cacheModuleFilesForTracebacks=False |
|---|
| 461 |
|
|---|
| 462 |
In earlier versions of Cheetah tracebacks from exceptions that |
|---|
| 463 |
were raised inside dynamically compiled Cheetah templates were |
|---|
| 464 |
opaque because Python didn't have access to a python source file |
|---|
| 465 |
to use in the traceback: |
|---|
| 466 |
|
|---|
| 467 |
File "xxxx.py", line 192, in getTextiledContent |
|---|
| 468 |
content = str(template(searchList=searchList)) |
|---|
| 469 |
File "cheetah_yyyy.py", line 202, in __str__ |
|---|
| 470 |
File "cheetah_yyyy.py", line 187, in respond |
|---|
| 471 |
File "cheetah_yyyy.py", line 139, in writeBody |
|---|
| 472 |
ZeroDivisionError: integer division or modulo by zero |
|---|
| 473 |
|
|---|
| 474 |
It is now possible to keep those files in a cache dir and allow |
|---|
| 475 |
Python to include the actual source lines in tracebacks and makes |
|---|
| 476 |
them much easier to understand: |
|---|
| 477 |
|
|---|
| 478 |
File "xxxx.py", line 192, in getTextiledContent |
|---|
| 479 |
content = str(template(searchList=searchList)) |
|---|
| 480 |
File "/tmp/CheetahCacheDir/cheetah_yyyy.py", line 202, in __str__ |
|---|
| 481 |
def __str__(self): return self.respond() |
|---|
| 482 |
File "/tmp/CheetahCacheDir/cheetah_yyyy.py", line 187, in respond |
|---|
| 483 |
self.writeBody(trans=trans) |
|---|
| 484 |
File "/tmp/CheetahCacheDir/cheetah_yyyy.py", line 139, in writeBody |
|---|
| 485 |
__v = 0/0 # $(0/0) |
|---|
| 486 |
ZeroDivisionError: integer division or modulo by zero |
|---|
| 487 |
|
|---|
| 488 |
- cacheDirForModuleFiles (a string representing a dir path) |
|---|
| 489 |
Default: Template._CHEETAH_cacheDirForModuleFiles=None |
|---|
| 490 |
|
|---|
| 491 |
See notes on cacheModuleFilesForTracebacks. |
|---|
| 492 |
|
|---|
| 493 |
- preprocessors |
|---|
| 494 |
Default: Template._CHEETAH_preprocessors=None |
|---|
| 495 |
|
|---|
| 496 |
** THIS IS A VERY ADVANCED TOPIC ** |
|---|
| 497 |
|
|---|
| 498 |
These are used to transform the source code prior to compilation. |
|---|
| 499 |
They provide a way to use Cheetah as a code generator for Cheetah |
|---|
| 500 |
code. In other words, you use one Cheetah template to output the |
|---|
| 501 |
source code for another Cheetah template. |
|---|
| 502 |
|
|---|
| 503 |
The major expected use cases are: |
|---|
| 504 |
|
|---|
| 505 |
a) 'compile-time caching' aka 'partial template binding', |
|---|
| 506 |
wherein an intermediate Cheetah template is used to output |
|---|
| 507 |
the source for the final Cheetah template. The intermediate |
|---|
| 508 |
template is a mix of a modified Cheetah syntax (the |
|---|
| 509 |
'preprocess syntax') and standard Cheetah syntax. The |
|---|
| 510 |
preprocessor syntax is executed at compile time and outputs |
|---|
| 511 |
Cheetah code which is then compiled in turn. This approach |
|---|
| 512 |
allows one to completely soft-code all the elements in the |
|---|
| 513 |
template which are subject to change yet have it compile to |
|---|
| 514 |
extremely efficient Python code with everything but the |
|---|
| 515 |
elements that must be variable at runtime (per browser |
|---|
| 516 |
request, etc.) compiled as static strings. Examples of this |
|---|
| 517 |
usage pattern will be added to the Cheetah Users' Guide. |
|---|
| 518 |
|
|---|
| 519 |
The'preprocess syntax' is just Cheetah's standard one with |
|---|
| 520 |
alternatives for the $ and # tokens: |
|---|
| 521 |
|
|---|
| 522 |
e.g. '@' and '%' for code like this |
|---|
| 523 |
@aPreprocessVar $aRuntimeVar |
|---|
| 524 |
%if aCompileTimeCondition then yyy else zzz |
|---|
| 525 |
%% preprocessor comment |
|---|
| 526 |
|
|---|
| 527 |
#if aRunTimeCondition then aaa else bbb |
|---|
| 528 |
## normal comment |
|---|
| 529 |
$aRuntimeVar |
|---|
| 530 |
|
|---|
| 531 |
b) adding #import and #extends directives dynamically based on |
|---|
| 532 |
the source |
|---|
| 533 |
|
|---|
| 534 |
If preprocessors are provided, Cheetah pipes the source code |
|---|
| 535 |
through each one in the order provided. Each preprocessor should |
|---|
| 536 |
accept the args (source, file) and should return a tuple (source, |
|---|
| 537 |
file). |
|---|
| 538 |
|
|---|
| 539 |
The argument value should be a list, but a single non-list value |
|---|
| 540 |
is acceptable and will automatically be converted into a list. |
|---|
| 541 |
Each item in the list will be passed through |
|---|
| 542 |
Template._normalizePreprocessor(). The items should either match |
|---|
| 543 |
one of the following forms: |
|---|
| 544 |
|
|---|
| 545 |
- an object with a .preprocess(source, file) method |
|---|
| 546 |
- a callable with the following signature: |
|---|
| 547 |
source, file = f(source, file) |
|---|
| 548 |
|
|---|
| 549 |
or one of the forms below: |
|---|
| 550 |
|
|---|
| 551 |
- a single string denoting the 2 'tokens' for the preprocess |
|---|
| 552 |
syntax. The tokens should be in the order (placeholderToken, |
|---|
| 553 |
directiveToken) and should separated with a space: |
|---|
| 554 |
e.g. '@ %' |
|---|
| 555 |
klass = Template.compile(src, preprocessors='@ %') |
|---|
| 556 |
# or |
|---|
| 557 |
klass = Template.compile(src, preprocessors=['@ %']) |
|---|
| 558 |
|
|---|
| 559 |
- a dict with the following keys or an object with the |
|---|
| 560 |
following attributes (all are optional, but nothing will |
|---|
| 561 |
happen if you don't provide at least one): |
|---|
| 562 |
- tokens: same as the single string described above. You can |
|---|
| 563 |
also provide a tuple of 2 strings. |
|---|
| 564 |
- searchList: the searchList used for preprocess $placeholders |
|---|
| 565 |
- compilerSettings: used in the compilation of the intermediate |
|---|
| 566 |
template |
|---|
| 567 |
- templateAPIClass: an optional subclass of `Template` |
|---|
| 568 |
- outputTransformer: a simple hook for passing in a callable |
|---|
| 569 |
which can do further transformations of the preprocessor |
|---|
| 570 |
output, or do something else like debug logging. The |
|---|
| 571 |
default is str(). |
|---|
| 572 |
+ any keyword arguments to Template.compile which you want to |
|---|
| 573 |
provide for the compilation of the intermediate template. |
|---|
| 574 |
|
|---|
| 575 |
klass = Template.compile(src, |
|---|
| 576 |
preprocessors=[ dict(tokens='@ %', searchList=[...]) ] ) |
|---|
| 577 |
|
|---|
| 578 |
""" |
|---|
| 579 |
|
|---|
| 580 |
|
|---|
| 581 |
try: |
|---|
| 582 |
vt = VerifyType.VerifyType |
|---|
| 583 |
vtc = VerifyType.VerifyTypeClass |
|---|
| 584 |
N = types.NoneType; S = types.StringType; U = types.UnicodeType |
|---|
| 585 |
D = types.DictType; F = types.FileType |
|---|
| 586 |
C = types.ClassType; M = types.ModuleType |
|---|
| 587 |
I = types.IntType |
|---|
| 588 |
|
|---|
| 589 |
if boolTypeAvailable: |
|---|
| 590 |
B = types.BooleanType |
|---|
| 591 |
|
|---|
| 592 |
vt(source, 'source', [N,S,U], 'string or None') |
|---|
| 593 |
vt(file, 'file',[N,S,U,F], 'string, file-like object, or None') |
|---|
| 594 |
|
|---|
| 595 |
baseclass = valOrDefault(baseclass, klass._CHEETAH_defaultBaseclassForTemplates) |
|---|
| 596 |
if isinstance(baseclass, Template): |
|---|
| 597 |
baseclass = baseclass.__class__ |
|---|
| 598 |
vt(baseclass, 'baseclass', [N,S,C,type], 'string, class or None') |
|---|
| 599 |
|
|---|
| 600 |
cacheCompilationResults = valOrDefault( |
|---|
| 601 |
cacheCompilationResults, klass._CHEETAH_cacheCompilationResults) |
|---|
| 602 |
if boolTypeAvailable: |
|---|
| 603 |
vt(cacheCompilationResults, 'cacheCompilationResults', [I,B], 'boolean') |
|---|
| 604 |
|
|---|
| 605 |
useCache = valOrDefault(useCache, klass._CHEETAH_useCompilationCache) |
|---|
| 606 |
if boolTypeAvailable: |
|---|
| 607 |
vt(cacheCompilationResults, 'cacheCompilationResults', [I,B], 'boolean') |
|---|
| 608 |
|
|---|
| 609 |
compilerSettings = valOrDefault( |
|---|
| 610 |
compilerSettings, klass._getCompilerSettings(source, file) or {}) |
|---|
| 611 |
vt(compilerSettings, 'compilerSettings', [D], 'dictionary') |
|---|
| 612 |
|
|---|
| 613 |
compilerClass = valOrDefault(compilerClass, klass._getCompilerClass(source, file)) |
|---|
| 614 |
|
|---|
| 615 |
preprocessors = valOrDefault(preprocessors, klass._CHEETAH_preprocessors) |
|---|
| 616 |
|
|---|
| 617 |
keepRefToGeneratedCode = valOrDefault( |
|---|
| 618 |
keepRefToGeneratedCode, klass._CHEETAH_keepRefToGeneratedCode) |
|---|
| 619 |
if boolTypeAvailable: |
|---|
| 620 |
vt(cacheCompilationResults, 'cacheCompilationResults', [I,B], 'boolean') |
|---|
| 621 |
|
|---|
| 622 |
vt(moduleName, 'moduleName', [N,S], 'string or None') |
|---|
| 623 |
__orig_file__ = None |
|---|
| 624 |
if not moduleName: |
|---|
| 625 |
if file and type(file) in StringTypes: |
|---|
| 626 |
moduleName = convertTmplPathToModuleName(file) |
|---|
| 627 |
__orig_file__ = file |
|---|
| 628 |
else: |
|---|
| 629 |
moduleName = klass._CHEETAH_defaultModuleNameForTemplates |
|---|
| 630 |
|
|---|
| 631 |
className = valOrDefault( |
|---|
| 632 |
className, klass._CHEETAH_defaultClassNameForTemplates) |
|---|
| 633 |
vt(className, 'className', [N,S], 'string or None') |
|---|
| 634 |
className = className or moduleName |
|---|
| 635 |
|
|---|
| 636 |
mainMethodName = valOrDefault( |
|---|
| 637 |
mainMethodName, klass._CHEETAH_defaultMainMethodNameForTemplates) |
|---|
| 638 |
vt(mainMethodName, 'mainMethodName', [N,S], 'string or None') |
|---|
| 639 |
|
|---|
| 640 |
moduleGlobals = valOrDefault( |
|---|
| 641 |
moduleGlobals, klass._CHEETAH_defaultModuleGlobalsForTemplates) |
|---|
| 642 |
|
|---|
| 643 |
cacheModuleFilesForTracebacks = valOrDefault( |
|---|
| 644 |
cacheModuleFilesForTracebacks, klass._CHEETAH_cacheModuleFilesForTracebacks) |
|---|
| 645 |
if boolTypeAvailable: |
|---|
| 646 |
vt(cacheModuleFilesForTracebacks, 'cacheModuleFilesForTracebacks', [I,B], 'boolean') |
|---|
| 647 |
|
|---|
| 648 |
cacheDirForModuleFiles = valOrDefault( |
|---|
| 649 |
cacheDirForModuleFiles, klass._CHEETAH_cacheDirForModuleFiles) |
|---|
| 650 |
vt(cacheDirForModuleFiles, 'cacheDirForModuleFiles', [N,S], 'string or None') |
|---|
| 651 |
|
|---|
| 652 |
except TypeError, reason: |
|---|
| 653 |
raise TypeError(reason) |
|---|
| 654 |
|
|---|
| 655 |
|
|---|
| 656 |
|
|---|
| 657 |
if preprocessors: |
|---|
| 658 |
origSrc = source |
|---|
| 659 |
source, file = klass._preprocessSource(source, file, preprocessors) |
|---|
| 660 |
|
|---|
| 661 |
|
|---|
| 662 |
|
|---|
| 663 |
baseclassValue = None |
|---|
| 664 |
baseclassName = None |
|---|
| 665 |
if baseclass: |
|---|
| 666 |
if type(baseclass) in StringTypes: |
|---|
| 667 |
baseclassName = baseclass |
|---|
| 668 |
elif type(baseclass) in (ClassType, type): |
|---|
| 669 |
|
|---|
| 670 |
baseclassName = 'CHEETAH_dynamicallyAssignedBaseClass_'+baseclass.__name__ |
|---|
| 671 |
baseclassValue = baseclass |
|---|
| 672 |
|
|---|
| 673 |
|
|---|
| 674 |
cacheHash = None |
|---|
| 675 |
cacheItem = None |
|---|
| 676 |
if source or isinstance(file, (str, unicode)): |
|---|
| 677 |
compilerSettingsHash = None |
|---|
| 678 |
if compilerSettings: |
|---|
| 679 |
compilerSettingsHash = hashDict(compilerSettings) |
|---|
| 680 |
|
|---|
| 681 |
moduleGlobalsHash = None |
|---|
| 682 |
if moduleGlobals: |
|---|
| 683 |
moduleGlobalsHash = hashDict(moduleGlobals) |
|---|
| 684 |
|
|---|
| 685 |
fileHash = None |
|---|
| 686 |
if file: |
|---|
| 687 |
fileHash = str(hash(file))+str(os.path.getmtime(file)) |
|---|
| 688 |
|
|---|
| 689 |
try: |
|---|
| 690 |
cacheHash = ''.join([str(v) for v in |
|---|
| 691 |
[hash(source), |
|---|
| 692 |
fileHash, |
|---|
| 693 |
className, |
|---|
| 694 |
moduleName, |
|---|
| 695 |
mainMethodName, |
|---|
| 696 |
hash(compilerClass), |
|---|
| 697 |
hash(baseclass), |
|---|
| 698 |
compilerSettingsHash, |
|---|
| 699 |
moduleGlobalsHash, |
|---|
| 700 |
hash(cacheDirForModuleFiles), |
|---|
| 701 |
]]) |
|---|
| 702 |
except: |
|---|
| 703 |
|
|---|
| 704 |
pass |
|---|
| 705 |
if useCache and cacheHash and klass._CHEETAH_compileCache.has_key(cacheHash): |
|---|
| 706 |
cacheItem = klass._CHEETAH_compileCache[cacheHash] |
|---|
| 707 |
generatedModuleCode = cacheItem.code |
|---|
| 708 |
|
|---|
| 709 |
else: |
|---|
| 710 |
compiler = compilerClass(source, file, |
|---|
| 711 |
moduleName=moduleName, |
|---|
| 712 |
mainClassName=className, |
|---|
| 713 |
baseclassName=baseclassName, |
|---|
| 714 |
mainMethodName=mainMethodName, |
|---|
| 715 |
settings=(compilerSettings or {})) |
|---|
| 716 |
compiler.compile() |
|---|
| 717 |
generatedModuleCode = compiler.getModuleCode() |
|---|
| 718 |
|
|---|
| 719 |
if not returnAClass: |
|---|
| 720 |
return generatedModuleCode |
|---|
| 721 |
else: |
|---|
| 722 |
if cacheItem: |
|---|
| 723 |
cacheItem.lastCheckoutTime = time.time() |
|---|
| 724 |
return cacheItem.klass |
|---|
| 725 |
|
|---|
| 726 |
def updateLinecache(filename, src): |
|---|
| 727 |
import linecache |
|---|
| 728 |
size = len(src) |
|---|
| 729 |
mtime = time.time() |
|---|
| 730 |
lines = src.splitlines() |
|---|
| 731 |
fullname = filename |
|---|
| 732 |
linecache.cache[filename] = size, mtime, lines, fullname |
|---|
| 733 |
|
|---|
| 734 |
|
|---|
| 735 |
try: |
|---|
| 736 |
klass._CHEETAH_compileLock.acquire() |
|---|
| 737 |
uniqueModuleName = _genUniqueModuleName(moduleName) |
|---|
| 738 |
__file__ = uniqueModuleName+'.py' |
|---|
| 739 |
mod = new.module(uniqueModuleName) |
|---|
| 740 |
sys.modules[uniqueModuleName] = mod |
|---|
| 741 |
finally: |
|---|
| 742 |
klass._CHEETAH_compileLock.release() |
|---|
| 743 |
|
|---|
| 744 |
try: |
|---|
| 745 |
if cacheModuleFilesForTracebacks: |
|---|
|
|---|