Restore pre-Python-2.7.9 behavior. Neither individual TiVos' nor
[pyTivo/wmcbrine.git] / Cheetah / Template.py
blob7a549ee75b30f193248c60a2e28f2b104015fd3b
1 #!/usr/bin/env python
2 # $Id: Template.py,v 1.185 2007/10/02 01:36:26 tavis_rudd Exp $
3 """Provides the core API for Cheetah.
5 See the docstring in the Template class and the Users' Guide for more information
7 Meta-Data
8 ================================================================================
9 Author: Tavis Rudd <tavis@damnsimple.com>
10 License: This software is released for unlimited distribution under the
11 terms of the MIT license. See the LICENSE file.
12 Version: $Revision: 1.185 $
13 Start Date: 2001/03/30
14 Last Revision Date: $Date: 2007/10/02 01:36:26 $
15 """
16 __author__ = "Tavis Rudd <tavis@damnsimple.com>"
17 __revision__ = "$Revision: 1.185 $"[11:-2]
19 ################################################################################
20 ## DEPENDENCIES
21 import sys # used in the error handling code
22 import re # used to define the internal delims regex
23 import new # used to bind methods and create dummy modules
24 import string
25 import os.path
26 import time # used in the cache refresh code
27 from random import randrange
28 import imp
29 import inspect
30 import StringIO
31 import traceback
32 import pprint
33 import cgi # Used by .webInput() if the template is a CGI script.
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
46 try:
47 from threading import Lock
48 except ImportError:
49 class Lock:
50 def acquire(self): pass
51 def release(self): pass
53 from Cheetah.Version import convertVersionStringToTuple, MinCompatibleVersionTuple
54 from Cheetah.Version import MinCompatibleVersion
55 # Base classes for Template
56 from Cheetah.Servlet import Servlet
57 # More intra-package imports ...
58 from Cheetah.Parser import ParseError, SourceReader
59 from Cheetah.Compiler import Compiler, DEFAULT_COMPILER_SETTINGS
60 from Cheetah import ErrorCatchers # for placeholder tags
61 from Cheetah import Filters # the output filters
62 from Cheetah.convertTmplPathToModuleName import convertTmplPathToModuleName
63 from Cheetah.Utils import VerifyType # Used in Template.__init__
64 from Cheetah.Utils.Misc import checkKeywords # Used in Template.__init__
65 from Cheetah.Utils.Indenter import Indenter # Used in Template.__init__ and for
66 # placeholders
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
72 from Cheetah.Unspecified import Unspecified
74 class Error(Exception): pass
75 class PreprocessError(Error): pass
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))
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))
99 ################################################################################
100 ## MODULE GLOBALS AND CONSTANTS
102 def _genUniqueModuleName(baseModuleName):
103 """The calling code is responsible for concurrency locking.
105 if baseModuleName not in sys.modules:
106 finalName = baseModuleName
107 else:
108 finalName = ('cheetah_%s_%s_%s'%(baseModuleName,
109 str(time.time()).replace('.','_'),
110 str(randrange(10000, 99999))))
111 return finalName
113 # Cache of a cgi.FieldStorage() instance, maintained by .webInput().
114 # This is only relavent to templates used as CGI scripts.
115 _formUsedByWebInput = None
117 # used in Template.compile()
118 def valOrDefault(val, default):
119 if val is not Unspecified: return val
120 else: return default
122 def updateLinecache(filename, src):
123 import linecache
124 size = len(src)
125 mtime = time.time()
126 lines = src.splitlines()
127 fullname = filename
128 linecache.cache[filename] = size, mtime, lines, fullname
130 class CompileCacheItem:
131 pass
133 class TemplatePreprocessor:
134 """This is used with the preprocessors argument to Template.compile().
136 See the docstring for Template.compile
138 ** Preprocessors are an advanced topic **
140 def __init__(self, settings):
141 self._settings = settings
143 def preprocess(self, source, file):
144 """Create an intermediate template and return the source code
145 it outputs
147 settings = self._settings
148 if not source: # @@TR: this needs improving
149 if isinstance(file, (str, unicode)): # it's a filename.
150 f = open(file)
151 source = f.read()
152 f.close()
153 elif hasattr(file, 'read'):
154 source = file.read()
155 file = None
157 templateAPIClass = settings.templateAPIClass
158 possibleKwArgs = [
159 arg for arg in
160 inspect.getargs(templateAPIClass.compile.im_func.func_code)[0]
161 if arg not in ('klass', 'source', 'file',)]
163 compileKwArgs = {}
164 for arg in possibleKwArgs:
165 if hasattr(settings, arg):
166 compileKwArgs[arg] = getattr(settings, arg)
168 tmplClass = templateAPIClass.compile(source=source, file=file, **compileKwArgs)
169 tmplInstance = tmplClass(**settings.templateInitArgs)
170 outputSource = settings.outputTransformer(tmplInstance)
171 outputFile = None
172 return outputSource, outputFile
174 class Template(Servlet):
175 """This class provides a) methods used by templates at runtime and b)
176 methods for compiling Cheetah source code into template classes.
178 This documentation assumes you already know Python and the basics of object
179 oriented programming. If you don't know Python, see the sections of the
180 Cheetah Users' Guide for non-programmers. It also assumes you have read
181 about Cheetah's syntax in the Users' Guide.
183 The following explains how to use Cheetah from within Python programs or via
184 the interpreter. If you statically compile your templates on the command
185 line using the 'cheetah' script, this is not relevant to you. Statically
186 compiled Cheetah template modules/classes (e.g. myTemplate.py:
187 MyTemplateClasss) are just like any other Python module or class. Also note,
188 most Python web frameworks (Webware, Aquarium, mod_python, Turbogears,
189 CherryPy, Quixote, etc.) provide plugins that handle Cheetah compilation for
190 you.
192 There are several possible usage patterns:
193 1) tclass = Template.compile(src)
194 t1 = tclass() # or tclass(namespaces=[namespace,...])
195 t2 = tclass() # or tclass(namespaces=[namespace2,...])
196 outputStr = str(t1) # or outputStr = t1.aMethodYouDefined()
198 Template.compile provides a rich and very flexible API via its
199 optional arguments so there are many possible variations of this
200 pattern. One example is:
201 tclass = Template.compile('hello $name from $caller', baseclass=dict)
202 print tclass(name='world', caller='me')
203 See the Template.compile() docstring for more details.
205 2) tmplInstance = Template(src)
206 # or Template(src, namespaces=[namespace,...])
207 outputStr = str(tmplInstance) # or outputStr = tmplInstance.aMethodYouDefined(...args...)
209 Notes on the usage patterns:
211 usage pattern 1)
212 This is the most flexible, but it is slightly more verbose unless you
213 write a wrapper function to hide the plumbing. Under the hood, all
214 other usage patterns are based on this approach. Templates compiled
215 this way can #extend (subclass) any Python baseclass: old-style or
216 new-style (based on object or a builtin type).
218 usage pattern 2)
219 This was Cheetah's original usage pattern. It returns an instance,
220 but you can still access the generated class via
221 tmplInstance.__class__. If you want to use several different
222 namespace 'searchLists' with a single template source definition,
223 you're better off with Template.compile (1).
225 Limitations (use pattern 1 instead):
226 - Templates compiled this way can only #extend subclasses of the
227 new-style 'object' baseclass. Cheetah.Template is a subclass of
228 'object'. You also can not #extend dict, list, or other builtin
229 types.
230 - If your template baseclass' __init__ constructor expects args there
231 is currently no way to pass them in.
233 If you need to subclass a dynamically compiled Cheetah class, do something like this:
234 from Cheetah.Template import Template
235 T1 = Template.compile('$meth1 #def meth1: this is meth1 in T1')
236 T2 = Template.compile('#implements meth1\nthis is meth1 redefined in T2', baseclass=T1)
237 print T1, T1()
238 print T2, T2()
241 Note about class and instance attribute names:
242 Attributes used by Cheetah have a special prefix to avoid confusion with
243 the attributes of the templates themselves or those of template
244 baseclasses.
246 Class attributes which are used in class methods look like this:
247 klass._CHEETAH_useCompilationCache (_CHEETAH_xxx)
249 Instance attributes look like this:
250 klass._CHEETAH__globalSetVars (_CHEETAH__xxx with 2 underscores)
253 # this is used by ._addCheetahPlumbingCodeToClass()
254 _CHEETAH_requiredCheetahMethods = (
255 '_initCheetahInstance',
256 'searchList',
257 'errorCatcher',
258 'getVar',
259 'varExists',
260 'getFileContents',
261 'i18n',
262 'runAsMainProgram',
263 'respond',
264 'shutdown',
265 'webInput',
266 'serverSidePath',
267 'generatedClassCode',
268 'generatedModuleCode',
270 '_getCacheStore',
271 '_getCacheStoreIdPrefix',
272 '_createCacheRegion',
273 'getCacheRegion',
274 'getCacheRegions',
275 'refreshCache',
277 '_handleCheetahInclude',
278 '_getTemplateAPIClassForIncludeDirectiveCompilation',
280 _CHEETAH_requiredCheetahClassMethods = ('subclass',)
281 _CHEETAH_requiredCheetahClassAttributes = ('cacheRegionClass','cacheStore',
282 'cacheStoreIdPrefix','cacheStoreClass')
284 ## the following are used by .compile(). Most are documented in its docstring.
285 _CHEETAH_cacheModuleFilesForTracebacks = False
286 _CHEETAH_cacheDirForModuleFiles = None # change to a dirname
288 _CHEETAH_compileCache = dict() # cache store for compiled code and classes
289 # To do something other than simple in-memory caching you can create an
290 # alternative cache store. It just needs to support the basics of Python's
291 # mapping/dict protocol. E.g.:
292 # class AdvCachingTemplate(Template):
293 # _CHEETAH_compileCache = MemoryOrFileCache()
294 _CHEETAH_compileLock = Lock() # used to prevent race conditions
295 _CHEETAH_defaultMainMethodName = None
296 _CHEETAH_compilerSettings = None
297 _CHEETAH_compilerClass = Compiler
298 _CHEETAH_cacheCompilationResults = True
299 _CHEETAH_useCompilationCache = True
300 _CHEETAH_keepRefToGeneratedCode = True
301 _CHEETAH_defaultBaseclassForTemplates = None
302 _CHEETAH_defaultClassNameForTemplates = None
303 # defaults to DEFAULT_COMPILER_SETTINGS['mainMethodName']:
304 _CHEETAH_defaultMainMethodNameForTemplates = None
305 _CHEETAH_defaultModuleNameForTemplates = 'DynamicallyCompiledCheetahTemplate'
306 _CHEETAH_defaultModuleGlobalsForTemplates = None
307 _CHEETAH_preprocessors = None
308 _CHEETAH_defaultPreprocessorClass = TemplatePreprocessor
310 ## The following attributes are used by instance methods:
311 _CHEETAH_generatedModuleCode = None
312 NonNumericInputError = NonNumericInputError
313 _CHEETAH_cacheRegionClass = CacheRegion
314 _CHEETAH_cacheStoreClass = MemoryCacheStore
315 #_CHEETAH_cacheStoreClass = MemcachedCacheStore
316 _CHEETAH_cacheStore = None
317 _CHEETAH_cacheStoreIdPrefix = None
319 def _getCompilerClass(klass, source=None, file=None):
320 return klass._CHEETAH_compilerClass
321 _getCompilerClass = classmethod(_getCompilerClass)
323 def _getCompilerSettings(klass, source=None, file=None):
324 return klass._CHEETAH_compilerSettings
325 _getCompilerSettings = classmethod(_getCompilerSettings)
327 def compile(klass, source=None, file=None,
328 returnAClass=True,
330 compilerSettings=Unspecified,
331 compilerClass=Unspecified,
332 moduleName=None,
333 className=Unspecified,
334 mainMethodName=Unspecified,
335 baseclass=Unspecified,
336 moduleGlobals=Unspecified,
337 cacheCompilationResults=Unspecified,
338 useCache=Unspecified,
339 preprocessors=Unspecified,
340 cacheModuleFilesForTracebacks=Unspecified,
341 cacheDirForModuleFiles=Unspecified,
343 keepRefToGeneratedCode=Unspecified,
347 The core API for compiling Cheetah source code into template classes.
349 This class method compiles Cheetah source code and returns a python
350 class. You then create template instances using that class. All
351 Cheetah's other compilation API's use this method under the hood.
353 Internally, this method a) parses the Cheetah source code and generates
354 Python code defining a module with a single class in it, b) dynamically
355 creates a module object with a unique name, c) execs the generated code
356 in that module's namespace then inserts the module into sys.modules, and
357 d) returns a reference to the generated class. If you want to get the
358 generated python source code instead, pass the argument
359 returnAClass=False.
361 It caches generated code and classes. See the descriptions of the
362 arguments'cacheCompilationResults' and 'useCache' for details. This
363 doesn't mean that templates will automatically recompile themselves when
364 the source file changes. Rather, if you call Template.compile(src) or
365 Template.compile(file=path) repeatedly it will attempt to return a
366 cached class definition instead of recompiling.
368 Hooks are provided template source preprocessing. See the notes on the
369 'preprocessors' arg.
371 If you are an advanced user and need to customize the way Cheetah parses
372 source code or outputs Python code, you should check out the
373 compilerSettings argument.
375 Arguments:
376 You must provide either a 'source' or 'file' arg, but not both:
377 - source (string or None)
378 - file (string path, file-like object, or None)
380 The rest of the arguments are strictly optional. All but the first
381 have defaults in attributes of the Template class which can be
382 overridden in subclasses of this class. Working with most of these is
383 an advanced topic.
385 - returnAClass=True
386 If false, return the generated module code rather than a class.
388 - compilerSettings (a dict)
389 Default: Template._CHEETAH_compilerSettings=None
391 a dictionary of settings to override those defined in
392 DEFAULT_COMPILER_SETTINGS. These can also be overridden in your
393 template source code with the #compiler or #compiler-settings
394 directives.
396 - compilerClass (a class)
397 Default: Template._CHEETAH_compilerClass=Cheetah.Compiler.Compiler
399 a subclass of Cheetah.Compiler.Compiler. Mucking with this is a
400 very advanced topic.
402 - moduleName (a string)
403 Default:
404 Template._CHEETAH_defaultModuleNameForTemplates
405 ='DynamicallyCompiledCheetahTemplate'
407 What to name the generated Python module. If the provided value is
408 None and a file arg was given, the moduleName is created from the
409 file path. In all cases if the moduleName provided is already in
410 sys.modules it is passed through a filter that generates a unique
411 variant of the name.
414 - className (a string)
415 Default: Template._CHEETAH_defaultClassNameForTemplates=None
417 What to name the generated Python class. If the provided value is
418 None, the moduleName is use as the class name.
420 - mainMethodName (a string)
421 Default:
422 Template._CHEETAH_defaultMainMethodNameForTemplates
423 =None (and thus DEFAULT_COMPILER_SETTINGS['mainMethodName'])
425 What to name the main output generating method in the compiled
426 template class.
428 - baseclass (a string or a class)
429 Default: Template._CHEETAH_defaultBaseclassForTemplates=None
431 Specifies the baseclass for the template without manually
432 including an #extends directive in the source. The #extends
433 directive trumps this arg.
435 If the provided value is a string you must make sure that a class
436 reference by that name is available to your template, either by
437 using an #import directive or by providing it in the arg
438 'moduleGlobals'.
440 If the provided value is a class, Cheetah will handle all the
441 details for you.
443 - moduleGlobals (a dict)
444 Default: Template._CHEETAH_defaultModuleGlobalsForTemplates=None
446 A dict of vars that will be added to the global namespace of the
447 module the generated code is executed in, prior to the execution
448 of that code. This should be Python values, not code strings!
450 - cacheCompilationResults (True/False)
451 Default: Template._CHEETAH_cacheCompilationResults=True
453 Tells Cheetah to cache the generated code and classes so that they
454 can be reused if Template.compile() is called multiple times with
455 the same source and options.
457 - useCache (True/False)
458 Default: Template._CHEETAH_useCompilationCache=True
460 Should the compilation cache be used? If True and a previous
461 compilation created a cached template class with the same source
462 code, compiler settings and other options, the cached template
463 class will be returned.
465 - cacheModuleFilesForTracebacks (True/False)
466 Default: Template._CHEETAH_cacheModuleFilesForTracebacks=False
468 In earlier versions of Cheetah tracebacks from exceptions that
469 were raised inside dynamically compiled Cheetah templates were
470 opaque because Python didn't have access to a python source file
471 to use in the traceback:
473 File "xxxx.py", line 192, in getTextiledContent
474 content = str(template(searchList=searchList))
475 File "cheetah_yyyy.py", line 202, in __str__
476 File "cheetah_yyyy.py", line 187, in respond
477 File "cheetah_yyyy.py", line 139, in writeBody
478 ZeroDivisionError: integer division or modulo by zero
480 It is now possible to keep those files in a cache dir and allow
481 Python to include the actual source lines in tracebacks and makes
482 them much easier to understand:
484 File "xxxx.py", line 192, in getTextiledContent
485 content = str(template(searchList=searchList))
486 File "/tmp/CheetahCacheDir/cheetah_yyyy.py", line 202, in __str__
487 def __str__(self): return self.respond()
488 File "/tmp/CheetahCacheDir/cheetah_yyyy.py", line 187, in respond
489 self.writeBody(trans=trans)
490 File "/tmp/CheetahCacheDir/cheetah_yyyy.py", line 139, in writeBody
491 __v = 0/0 # $(0/0)
492 ZeroDivisionError: integer division or modulo by zero
494 - cacheDirForModuleFiles (a string representing a dir path)
495 Default: Template._CHEETAH_cacheDirForModuleFiles=None
497 See notes on cacheModuleFilesForTracebacks.
499 - preprocessors
500 Default: Template._CHEETAH_preprocessors=None
502 ** THIS IS A VERY ADVANCED TOPIC **
504 These are used to transform the source code prior to compilation.
505 They provide a way to use Cheetah as a code generator for Cheetah
506 code. In other words, you use one Cheetah template to output the
507 source code for another Cheetah template.
509 The major expected use cases are:
511 a) 'compile-time caching' aka 'partial template binding',
512 wherein an intermediate Cheetah template is used to output
513 the source for the final Cheetah template. The intermediate
514 template is a mix of a modified Cheetah syntax (the
515 'preprocess syntax') and standard Cheetah syntax. The
516 preprocessor syntax is executed at compile time and outputs
517 Cheetah code which is then compiled in turn. This approach
518 allows one to completely soft-code all the elements in the
519 template which are subject to change yet have it compile to
520 extremely efficient Python code with everything but the
521 elements that must be variable at runtime (per browser
522 request, etc.) compiled as static strings. Examples of this
523 usage pattern will be added to the Cheetah Users' Guide.
525 The'preprocess syntax' is just Cheetah's standard one with
526 alternatives for the $ and # tokens:
528 e.g. '@' and '%' for code like this
529 @aPreprocessVar $aRuntimeVar
530 %if aCompileTimeCondition then yyy else zzz
531 %% preprocessor comment
533 #if aRunTimeCondition then aaa else bbb
534 ## normal comment
535 $aRuntimeVar
537 b) adding #import and #extends directives dynamically based on
538 the source
540 If preprocessors are provided, Cheetah pipes the source code
541 through each one in the order provided. Each preprocessor should
542 accept the args (source, file) and should return a tuple (source,
543 file).
545 The argument value should be a list, but a single non-list value
546 is acceptable and will automatically be converted into a list.
547 Each item in the list will be passed through
548 Template._normalizePreprocessor(). The items should either match
549 one of the following forms:
551 - an object with a .preprocess(source, file) method
552 - a callable with the following signature:
553 source, file = f(source, file)
555 or one of the forms below:
557 - a single string denoting the 2 'tokens' for the preprocess
558 syntax. The tokens should be in the order (placeholderToken,
559 directiveToken) and should separated with a space:
560 e.g. '@ %'
561 klass = Template.compile(src, preprocessors='@ %')
562 # or
563 klass = Template.compile(src, preprocessors=['@ %'])
565 - a dict with the following keys or an object with the
566 following attributes (all are optional, but nothing will
567 happen if you don't provide at least one):
568 - tokens: same as the single string described above. You can
569 also provide a tuple of 2 strings.
570 - searchList: the searchList used for preprocess $placeholders
571 - compilerSettings: used in the compilation of the intermediate
572 template
573 - templateAPIClass: an optional subclass of `Template`
574 - outputTransformer: a simple hook for passing in a callable
575 which can do further transformations of the preprocessor
576 output, or do something else like debug logging. The
577 default is str().
578 + any keyword arguments to Template.compile which you want to
579 provide for the compilation of the intermediate template.
581 klass = Template.compile(src,
582 preprocessors=[ dict(tokens='@ %', searchList=[...]) ] )
585 ##################################################
586 ## normalize and validate args
587 try:
588 vt = VerifyType.VerifyType
589 vtc = VerifyType.VerifyTypeClass
590 N = types.NoneType; S = types.StringType; U = types.UnicodeType
591 D = types.DictType; F = types.FileType
592 C = types.ClassType; M = types.ModuleType
593 I = types.IntType
595 if boolTypeAvailable:
596 B = types.BooleanType
598 vt(source, 'source', [N,S,U], 'string or None')
599 vt(file, 'file',[N,S,U,F], 'string, file-like object, or None')
601 baseclass = valOrDefault(baseclass, klass._CHEETAH_defaultBaseclassForTemplates)
602 if isinstance(baseclass, Template):
603 baseclass = baseclass.__class__
604 vt(baseclass, 'baseclass', [N,S,C,type], 'string, class or None')
606 cacheCompilationResults = valOrDefault(
607 cacheCompilationResults, klass._CHEETAH_cacheCompilationResults)
608 if boolTypeAvailable:
609 vt(cacheCompilationResults, 'cacheCompilationResults', [I,B], 'boolean')
611 useCache = valOrDefault(useCache, klass._CHEETAH_useCompilationCache)
612 if boolTypeAvailable:
613 vt(cacheCompilationResults, 'cacheCompilationResults', [I,B], 'boolean')
615 compilerSettings = valOrDefault(
616 compilerSettings, klass._getCompilerSettings(source, file) or {})
617 vt(compilerSettings, 'compilerSettings', [D], 'dictionary')
619 compilerClass = valOrDefault(compilerClass, klass._getCompilerClass(source, file))
621 preprocessors = valOrDefault(preprocessors, klass._CHEETAH_preprocessors)
623 keepRefToGeneratedCode = valOrDefault(
624 keepRefToGeneratedCode, klass._CHEETAH_keepRefToGeneratedCode)
625 if boolTypeAvailable:
626 vt(cacheCompilationResults, 'cacheCompilationResults', [I,B], 'boolean')
628 vt(moduleName, 'moduleName', [N,S], 'string or None')
629 __orig_file__ = None
630 if not moduleName:
631 if file and type(file) in StringTypes:
632 moduleName = convertTmplPathToModuleName(file)
633 __orig_file__ = file
634 else:
635 moduleName = klass._CHEETAH_defaultModuleNameForTemplates
637 className = valOrDefault(
638 className, klass._CHEETAH_defaultClassNameForTemplates)
639 vt(className, 'className', [N,S], 'string or None')
640 className = className or moduleName
642 mainMethodName = valOrDefault(
643 mainMethodName, klass._CHEETAH_defaultMainMethodNameForTemplates)
644 vt(mainMethodName, 'mainMethodName', [N,S], 'string or None')
646 moduleGlobals = valOrDefault(
647 moduleGlobals, klass._CHEETAH_defaultModuleGlobalsForTemplates)
649 cacheModuleFilesForTracebacks = valOrDefault(
650 cacheModuleFilesForTracebacks, klass._CHEETAH_cacheModuleFilesForTracebacks)
651 if boolTypeAvailable:
652 vt(cacheModuleFilesForTracebacks, 'cacheModuleFilesForTracebacks', [I,B], 'boolean')
654 cacheDirForModuleFiles = valOrDefault(
655 cacheDirForModuleFiles, klass._CHEETAH_cacheDirForModuleFiles)
656 vt(cacheDirForModuleFiles, 'cacheDirForModuleFiles', [N,S], 'string or None')
658 except TypeError, reason:
659 raise TypeError(reason)
661 ##################################################
662 ## handle any preprocessors
663 if preprocessors:
664 origSrc = source
665 source, file = klass._preprocessSource(source, file, preprocessors)
667 ##################################################
668 ## compilation, using cache if requested/possible
669 baseclassValue = None
670 baseclassName = None
671 if baseclass:
672 if type(baseclass) in StringTypes:
673 baseclassName = baseclass
674 elif type(baseclass) in (ClassType, type):
675 # @@TR: should soft-code this
676 baseclassName = 'CHEETAH_dynamicallyAssignedBaseClass_'+baseclass.__name__
677 baseclassValue = baseclass
680 cacheHash = None
681 cacheItem = None
682 if source or isinstance(file, (str, unicode)):
683 compilerSettingsHash = None
684 if compilerSettings:
685 compilerSettingsHash = hashDict(compilerSettings)
687 moduleGlobalsHash = None
688 if moduleGlobals:
689 moduleGlobalsHash = hashDict(moduleGlobals)
691 fileHash = None
692 if file:
693 fileHash = str(hash(file))+str(os.path.getmtime(file))
695 try:
696 # @@TR: find some way to create a cacheHash that is consistent
697 # between process restarts. It would allow for caching the
698 # compiled module on disk and thereby reduce the startup time
699 # for applications that use a lot of dynamically compiled
700 # templates.
701 cacheHash = ''.join([str(v) for v in
702 [hash(source),
703 fileHash,
704 className,
705 moduleName,
706 mainMethodName,
707 hash(compilerClass),
708 hash(baseclass),
709 compilerSettingsHash,
710 moduleGlobalsHash,
711 hash(cacheDirForModuleFiles),
713 except:
714 #@@TR: should add some logging to this
715 pass
716 if useCache and cacheHash and cacheHash in klass._CHEETAH_compileCache:
717 cacheItem = klass._CHEETAH_compileCache[cacheHash]
718 generatedModuleCode = cacheItem.code
719 else:
720 compiler = compilerClass(source, file,
721 moduleName=moduleName,
722 mainClassName=className,
723 baseclassName=baseclassName,
724 mainMethodName=mainMethodName,
725 settings=(compilerSettings or {}))
726 compiler.compile()
727 generatedModuleCode = compiler.getModuleCode()
729 if not returnAClass:
730 return generatedModuleCode
731 else:
732 if cacheItem:
733 cacheItem.lastCheckoutTime = time.time()
734 return cacheItem.klass
736 try:
737 klass._CHEETAH_compileLock.acquire()
738 uniqueModuleName = _genUniqueModuleName(moduleName)
739 __file__ = uniqueModuleName+'.py' # relative file path with no dir part
741 if cacheModuleFilesForTracebacks:
742 if not os.path.exists(cacheDirForModuleFiles):
743 raise Exception('%s does not exist'%cacheDirForModuleFiles)
745 __file__ = os.path.join(cacheDirForModuleFiles, __file__)
746 # @@TR: might want to assert that it doesn't already exist
747 try:
748 open(__file__, 'w').write(generatedModuleCode)
749 # @@TR: should probably restrict the perms, etc.
750 except OSError:
751 # @@ TR: should this optionally raise?
752 traceback.print_exc(file=sys.stderr)
754 mod = new.module(uniqueModuleName)
755 if moduleGlobals:
756 for k, v in moduleGlobals.items():
757 setattr(mod, k, v)
758 mod.__file__ = __file__
759 if __orig_file__ and os.path.exists(__orig_file__):
760 # this is used in the WebKit filemonitoring code
761 mod.__orig_file__ = __orig_file__
763 if baseclass and baseclassValue:
764 setattr(mod, baseclassName, baseclassValue)
766 try:
767 co = compile(generatedModuleCode, __file__, 'exec')
768 exec co in mod.__dict__
769 except SyntaxError, e:
770 try:
771 parseError = genParserErrorFromPythonException(
772 source, file, generatedModuleCode, exception=e)
773 except:
774 traceback.print_exc()
775 updateLinecache(__file__, generatedModuleCode)
776 e.generatedModuleCode = generatedModuleCode
777 raise e
778 else:
779 raise parseError
780 except Exception, e:
781 updateLinecache(__file__, generatedModuleCode)
782 e.generatedModuleCode = generatedModuleCode
783 raise
785 sys.modules[uniqueModuleName] = mod
786 finally:
787 klass._CHEETAH_compileLock.release()
789 templateClass = getattr(mod, className)
791 if (cacheCompilationResults
792 and cacheHash
793 and cacheHash not in klass._CHEETAH_compileCache):
795 cacheItem = CompileCacheItem()
796 cacheItem.cacheTime = cacheItem.lastCheckoutTime = time.time()
797 cacheItem.code = generatedModuleCode
798 cacheItem.klass = templateClass
799 templateClass._CHEETAH_isInCompilationCache = True
800 klass._CHEETAH_compileCache[cacheHash] = cacheItem
801 else:
802 templateClass._CHEETAH_isInCompilationCache = False
804 if keepRefToGeneratedCode or cacheCompilationResults:
805 templateClass._CHEETAH_generatedModuleCode = generatedModuleCode
807 return templateClass
808 compile = classmethod(compile)
810 def subclass(klass, *args, **kws):
811 """Takes the same args as the .compile() classmethod and returns a
812 template that is a subclass of the template this method is called from.
814 T1 = Template.compile(' foo - $meth1 - bar\n#def meth1: this is T1.meth1')
815 T2 = T1.subclass('#implements meth1\n this is T2.meth1')
817 kws['baseclass'] = klass
818 if isinstance(klass, Template):
819 templateAPIClass = klass
820 else:
821 templateAPIClass = Template
822 return templateAPIClass.compile(*args, **kws)
823 subclass = classmethod(subclass)
825 def _preprocessSource(klass, source, file, preprocessors):
826 """Iterates through the .compile() classmethod's preprocessors argument
827 and pipes the source code through each each preprocessor.
829 It returns the tuple (source, file) which is then used by
830 Template.compile to finish the compilation.
832 if not isinstance(preprocessors, (list, tuple)):
833 preprocessors = [preprocessors]
834 for preprocessor in preprocessors:
835 preprocessor = klass._normalizePreprocessorArg(preprocessor)
836 source, file = preprocessor.preprocess(source, file)
837 return source, file
838 _preprocessSource = classmethod(_preprocessSource)
840 def _normalizePreprocessorArg(klass, arg):
841 """Used to convert the items in the .compile() classmethod's
842 preprocessors argument into real source preprocessors. This permits the
843 use of several shortcut forms for defining preprocessors.
846 if hasattr(arg, 'preprocess'):
847 return arg
848 elif callable(arg):
849 class WrapperPreprocessor:
850 def preprocess(self, source, file):
851 return arg(source, file)
852 return WrapperPreprocessor()
853 else:
854 class Settings(object):
855 placeholderToken = None
856 directiveToken = None
857 settings = Settings()
858 if isinstance(arg, str) or isinstance(arg, (list, tuple)):
859 settings.tokens = arg
860 elif isinstance(arg, dict):
861 for k, v in arg.items():
862 setattr(settings, k, v)
863 else:
864 settings = arg
866 settings = klass._normalizePreprocessorSettings(settings)
867 return klass._CHEETAH_defaultPreprocessorClass(settings)
869 _normalizePreprocessorArg = classmethod(_normalizePreprocessorArg)
871 def _normalizePreprocessorSettings(klass, settings):
872 settings.keepRefToGeneratedCode = True
874 def normalizeSearchList(searchList):
875 if not isinstance(searchList, (list, tuple)):
876 searchList = [searchList]
877 return searchList
879 def normalizeTokens(tokens):
880 if isinstance(tokens, str):
881 return tokens.split() # space delimited string e.g.'@ %'
882 elif isinstance(tokens, (list, tuple)):
883 return tokens
884 else:
885 raise PreprocessError('invalid tokens argument: %r'%tokens)
887 if hasattr(settings, 'tokens'):
888 (settings.placeholderToken,
889 settings.directiveToken) = normalizeTokens(settings.tokens)
891 if (not getattr(settings,'compilerSettings', None)
892 and not getattr(settings, 'placeholderToken', None) ):
894 raise TypeError(
895 'Preprocessor requires either a "tokens" or a "compilerSettings" arg.'
896 ' Neither was provided.')
898 if not hasattr(settings, 'templateInitArgs'):
899 settings.templateInitArgs = {}
900 if 'searchList' not in settings.templateInitArgs:
901 if not hasattr(settings, 'searchList') and hasattr(settings, 'namespaces'):
902 settings.searchList = settings.namespaces
903 elif not hasattr(settings, 'searchList'):
904 settings.searchList = []
905 settings.templateInitArgs['searchList'] = settings.searchList
906 settings.templateInitArgs['searchList'] = (
907 normalizeSearchList(settings.templateInitArgs['searchList']))
909 if not hasattr(settings, 'outputTransformer'):
910 settings.outputTransformer = unicode
912 if not hasattr(settings, 'templateAPIClass'):
913 class PreprocessTemplateAPIClass(klass): pass
914 settings.templateAPIClass = PreprocessTemplateAPIClass
916 if not hasattr(settings, 'compilerSettings'):
917 settings.compilerSettings = {}
919 klass._updateSettingsWithPreprocessTokens(
920 compilerSettings=settings.compilerSettings,
921 placeholderToken=settings.placeholderToken,
922 directiveToken=settings.directiveToken
924 return settings
925 _normalizePreprocessorSettings = classmethod(_normalizePreprocessorSettings)
927 def _updateSettingsWithPreprocessTokens(
928 klass, compilerSettings, placeholderToken, directiveToken):
930 if (placeholderToken and 'cheetahVarStartToken' not in compilerSettings):
931 compilerSettings['cheetahVarStartToken'] = placeholderToken
932 if directiveToken:
933 if 'directiveStartToken' not in compilerSettings:
934 compilerSettings['directiveStartToken'] = directiveToken
935 if 'directiveEndToken' not in compilerSettings:
936 compilerSettings['directiveEndToken'] = directiveToken
937 if 'commentStartToken' not in compilerSettings:
938 compilerSettings['commentStartToken'] = directiveToken*2
939 if 'multiLineCommentStartToken' not in compilerSettings:
940 compilerSettings['multiLineCommentStartToken'] = (
941 directiveToken+'*')
942 if 'multiLineCommentEndToken' not in compilerSettings:
943 compilerSettings['multiLineCommentEndToken'] = (
944 '*'+directiveToken)
945 if 'EOLSlurpToken' not in compilerSettings:
946 compilerSettings['EOLSlurpToken'] = directiveToken
947 _updateSettingsWithPreprocessTokens = classmethod(_updateSettingsWithPreprocessTokens)
949 def _addCheetahPlumbingCodeToClass(klass, concreteTemplateClass):
950 """If concreteTemplateClass is not a subclass of Cheetah.Template, add
951 the required cheetah methods and attributes to it.
953 This is called on each new template class after it has been compiled.
954 If concreteTemplateClass is not a subclass of Cheetah.Template but
955 already has method with the same name as one of the required cheetah
956 methods, this will skip that method.
958 for methodname in klass._CHEETAH_requiredCheetahMethods:
959 if not hasattr(concreteTemplateClass, methodname):
960 method = getattr(Template, methodname)
961 newMethod = new.instancemethod(method.im_func, None, concreteTemplateClass)
962 #print methodname, method
963 setattr(concreteTemplateClass, methodname, newMethod)
965 for classMethName in klass._CHEETAH_requiredCheetahClassMethods:
966 if not hasattr(concreteTemplateClass, classMethName):
967 meth = getattr(klass, classMethName)
968 setattr(concreteTemplateClass, classMethName, classmethod(meth.im_func))
970 for attrname in klass._CHEETAH_requiredCheetahClassAttributes:
971 attrname = '_CHEETAH_'+attrname
972 if not hasattr(concreteTemplateClass, attrname):
973 attrVal = getattr(klass, attrname)
974 setattr(concreteTemplateClass, attrname, attrVal)
976 if (not hasattr(concreteTemplateClass, '__str__')
977 or concreteTemplateClass.__str__ is object.__str__):
979 mainMethNameAttr = '_mainCheetahMethod_for_'+concreteTemplateClass.__name__
980 mainMethName = getattr(concreteTemplateClass,mainMethNameAttr, None)
981 if mainMethName:
982 def __str__(self): return getattr(self, mainMethName)()
983 elif (hasattr(concreteTemplateClass, 'respond')
984 and concreteTemplateClass.respond!=Servlet.respond):
985 def __str__(self): return self.respond()
986 else:
987 def __str__(self):
988 if hasattr(self, mainMethNameAttr):
989 return getattr(self,mainMethNameAttr)()
990 elif hasattr(self, 'respond'):
991 return self.respond()
992 else:
993 return super(self.__class__, self).__str__()
995 __str__ = new.instancemethod(__str__, None, concreteTemplateClass)
996 setattr(concreteTemplateClass, '__str__', __str__)
998 _addCheetahPlumbingCodeToClass = classmethod(_addCheetahPlumbingCodeToClass)
1000 ## end classmethods ##
1002 def __init__(self, source=None,
1004 namespaces=None, searchList=None,
1005 # use either or. They are aliases for the same thing.
1007 file=None,
1008 filter='RawOrEncodedUnicode', # which filter from Cheetah.Filters
1009 filtersLib=Filters,
1010 errorCatcher=None,
1012 compilerSettings=Unspecified, # control the behaviour of the compiler
1013 _globalSetVars=None, # used internally for #include'd templates
1014 _preBuiltSearchList=None # used internally for #include'd templates
1016 """a) compiles a new template OR b) instantiates an existing template.
1018 Read this docstring carefully as there are two distinct usage patterns.
1019 You should also read this class' main docstring.
1021 a) to compile a new template:
1022 t = Template(source=aSourceString)
1023 # or
1024 t = Template(file='some/path')
1025 # or
1026 t = Template(file=someFileObject)
1027 # or
1028 namespaces = [{'foo':'bar'}]
1029 t = Template(source=aSourceString, namespaces=namespaces)
1030 # or
1031 t = Template(file='some/path', namespaces=namespaces)
1033 print t
1035 b) to create an instance of an existing, precompiled template class:
1036 ## i) first you need a reference to a compiled template class:
1037 tclass = Template.compile(source=src) # or just Template.compile(src)
1038 # or
1039 tclass = Template.compile(file='some/path')
1040 # or
1041 tclass = Template.compile(file=someFileObject)
1042 # or
1043 # if you used the command line compiler or have Cheetah's ImportHooks
1044 # installed your template class is also available via Python's
1045 # standard import mechanism:
1046 from ACompileTemplate import AcompiledTemplate as tclass
1048 ## ii) then you create an instance
1049 t = tclass(namespaces=namespaces)
1050 # or
1051 t = tclass(namespaces=namespaces, filter='RawOrEncodedUnicode')
1052 print t
1054 Arguments:
1055 for usage pattern a)
1056 If you are compiling a new template, you must provide either a
1057 'source' or 'file' arg, but not both:
1058 - source (string or None)
1059 - file (string path, file-like object, or None)
1061 Optional args (see below for more) :
1062 - compilerSettings
1063 Default: Template._CHEETAH_compilerSettings=None
1065 a dictionary of settings to override those defined in
1066 DEFAULT_COMPILER_SETTINGS. See
1067 Cheetah.Template.DEFAULT_COMPILER_SETTINGS and the Users' Guide
1068 for details.
1070 You can pass the source arg in as a positional arg with this usage
1071 pattern. Use keywords for all other args.
1073 for usage pattern b)
1074 Do not use positional args with this usage pattern, unless your
1075 template subclasses something other than Cheetah.Template and you
1076 want to pass positional args to that baseclass. E.g.:
1077 dictTemplate = Template.compile('hello $name from $caller', baseclass=dict)
1078 tmplvars = dict(name='world', caller='me')
1079 print dictTemplate(tmplvars)
1080 This usage requires all Cheetah args to be passed in as keyword args.
1082 optional args for both usage patterns:
1084 - namespaces (aka 'searchList')
1085 Default: None
1087 an optional list of namespaces (dictionaries, objects, modules,
1088 etc.) which Cheetah will search through to find the variables
1089 referenced in $placeholders.
1091 If you provide a single namespace instead of a list, Cheetah will
1092 automatically convert it into a list.
1094 NOTE: Cheetah does NOT force you to use the namespaces search list
1095 and related features. It's on by default, but you can turn if off
1096 using the compiler settings useSearchList=False or
1097 useNameMapper=False.
1099 - filter
1100 Default: 'EncodeUnicode'
1102 Which filter should be used for output filtering. This should
1103 either be a string which is the name of a filter in the
1104 'filtersLib' or a subclass of Cheetah.Filters.Filter. . See the
1105 Users' Guide for more details.
1107 - filtersLib
1108 Default: Cheetah.Filters
1110 A module containing subclasses of Cheetah.Filters.Filter. See the
1111 Users' Guide for more details.
1113 - errorCatcher
1114 Default: None
1116 This is a debugging tool. See the Users' Guide for more details.
1117 Do not use this or the #errorCatcher diretive with live
1118 production systems.
1120 Do NOT mess with the args _globalSetVars or _preBuiltSearchList!
1124 ##################################################
1125 ## Verify argument keywords and types
1127 S = types.StringType; U = types.UnicodeType
1128 L = types.ListType; T = types.TupleType
1129 D = types.DictType; F = types.FileType
1130 C = types.ClassType; M = types.ModuleType
1131 N = types.NoneType
1132 vt = VerifyType.VerifyType
1133 vtc = VerifyType.VerifyTypeClass
1134 try:
1135 vt(source, 'source', [N,S,U], 'string or None')
1136 vt(file, 'file', [N,S,U,F], 'string, file open for reading, or None')
1137 vtc(filter, 'filter', [S,C,type], 'string or class',
1138 Filters.Filter,
1139 '(if class, must be subclass of Cheetah.Filters.Filter)')
1140 vt(filtersLib, 'filtersLib', [S,M], 'string or module',
1141 '(if module, must contain subclasses of Cheetah.Filters.Filter)')
1142 vtc(errorCatcher, 'errorCatcher', [N,S,C,type], 'string, class or None',
1143 ErrorCatchers.ErrorCatcher,
1144 '(if class, must be subclass of Cheetah.ErrorCatchers.ErrorCatcher)')
1145 if compilerSettings is not Unspecified:
1146 vt(compilerSettings, 'compilerSettings', [D], 'dictionary')
1148 except TypeError, reason:
1149 # Re-raise the exception here so that the traceback will end in
1150 # this function rather than in some utility function.
1151 raise TypeError(reason)
1153 if source is not None and file is not None:
1154 raise TypeError("you must supply either a source string or the" +
1155 " 'file' keyword argument, but not both")
1157 ##################################################
1158 ## Do superclass initialization.
1159 Servlet.__init__(self)
1161 ##################################################
1162 ## Do required version check
1163 if not hasattr(self, '_CHEETAH_versionTuple'):
1164 try:
1165 mod = sys.modules[self.__class__.__module__]
1166 compiledVersion = mod.__CHEETAH_version__
1167 compiledVersionTuple = convertVersionStringToTuple(compiledVersion)
1168 if compiledVersionTuple < MinCompatibleVersionTuple:
1169 raise AssertionError(
1170 'This template was compiled with Cheetah version'
1171 ' %s. Templates compiled before version %s must be recompiled.'%(
1172 compiledVersion, MinCompatibleVersion))
1173 except AssertionError:
1174 raise
1175 except:
1176 pass
1178 ##################################################
1179 ## Setup instance state attributes used during the life of template
1180 ## post-compile
1182 self._initCheetahInstance(
1183 searchList=searchList, namespaces=namespaces,
1184 filter=filter, filtersLib=filtersLib,
1185 errorCatcher=errorCatcher,
1186 _globalSetVars=_globalSetVars,
1187 _preBuiltSearchList=_preBuiltSearchList)
1189 ##################################################
1190 ## Now, compile if we're meant to
1191 if (source is not None) or (file is not None):
1192 self._compile(source, file, compilerSettings=compilerSettings)
1194 def generatedModuleCode(self):
1195 """Return the module code the compiler generated, or None if no
1196 compilation took place.
1199 return self._CHEETAH_generatedModuleCode
1201 def generatedClassCode(self):
1202 """Return the class code the compiler generated, or None if no
1203 compilation took place.
1206 return self._CHEETAH_generatedModuleCode[
1207 self._CHEETAH_generatedModuleCode.find('\nclass '):
1208 self._CHEETAH_generatedModuleCode.find('\n## END CLASS DEFINITION')]
1210 def searchList(self):
1211 """Return a reference to the searchlist
1213 return self._CHEETAH__searchList
1215 def errorCatcher(self):
1216 """Return a reference to the current errorCatcher
1218 return self._CHEETAH__errorCatcher
1220 ## cache methods ##
1221 def _getCacheStore(self):
1222 if not self._CHEETAH__cacheStore:
1223 if self._CHEETAH_cacheStore is not None:
1224 self._CHEETAH__cacheStore = self._CHEETAH_cacheStore
1225 else:
1226 # @@TR: might want to provide a way to provide init args
1227 self._CHEETAH__cacheStore = self._CHEETAH_cacheStoreClass()
1229 return self._CHEETAH__cacheStore
1231 def _getCacheStoreIdPrefix(self):
1232 if self._CHEETAH_cacheStoreIdPrefix is not None:
1233 return self._CHEETAH_cacheStoreIdPrefix
1234 else:
1235 return str(id(self))
1237 def _createCacheRegion(self, regionID):
1238 return self._CHEETAH_cacheRegionClass(
1239 regionID=regionID,
1240 templateCacheIdPrefix=self._getCacheStoreIdPrefix(),
1241 cacheStore=self._getCacheStore())
1243 def getCacheRegion(self, regionID, cacheInfo=None, create=True):
1244 cacheRegion = self._CHEETAH__cacheRegions.get(regionID)
1245 if not cacheRegion and create:
1246 cacheRegion = self._createCacheRegion(regionID)
1247 self._CHEETAH__cacheRegions[regionID] = cacheRegion
1248 return cacheRegion
1250 def getCacheRegions(self):
1251 """Returns a dictionary of the 'cache regions' initialized in a
1252 template.
1254 Each #cache directive block or $*cachedPlaceholder is a separate 'cache
1255 region'.
1257 # returns a copy to prevent users mucking it up
1258 return self._CHEETAH__cacheRegions.copy()
1260 def refreshCache(self, cacheRegionId=None, cacheItemId=None):
1261 """Refresh a cache region or a specific cache item within a region.
1264 if not cacheRegionId:
1265 for key, cregion in self.getCacheRegions():
1266 cregion.clear()
1267 else:
1268 cregion = self._CHEETAH__cacheRegions.get(cacheRegionId)
1269 if not cregion:
1270 return
1271 if not cacheItemId: # clear the desired region and all its cacheItems
1272 cregion.clear()
1273 else: # clear one specific cache of a specific region
1274 cache = cregion.getCacheItem(cacheItemId)
1275 if cache:
1276 cache.clear()
1278 ## end cache methods ##
1280 def shutdown(self):
1281 """Break reference cycles before discarding a servlet.
1283 try:
1284 Servlet.shutdown(self)
1285 except:
1286 pass
1287 self._CHEETAH__searchList = None
1288 self.__dict__ = {}
1290 ## utility functions ##
1292 def getVar(self, varName, default=Unspecified, autoCall=True):
1293 """Get a variable from the searchList. If the variable can't be found
1294 in the searchList, it returns the default value if one was given, or
1295 raises NameMapper.NotFound.
1298 try:
1299 return valueFromSearchList(self.searchList(), varName.replace('$',''), autoCall)
1300 except NotFound:
1301 if default is not Unspecified:
1302 return default
1303 else:
1304 raise
1306 def varExists(self, varName, autoCall=True):
1307 """Test if a variable name exists in the searchList.
1309 try:
1310 valueFromSearchList(self.searchList(), varName.replace('$',''), autoCall)
1311 return True
1312 except NotFound:
1313 return False
1316 hasVar = varExists
1319 def i18n(self, message,
1320 plural=None,
1321 n=None,
1323 id=None,
1324 domain=None,
1325 source=None,
1326 target=None,
1327 comment=None
1329 """This is just a stub at this time.
1331 plural = the plural form of the message
1332 n = a sized argument to distinguish between single and plural forms
1334 id = msgid in the translation catalog
1335 domain = translation domain
1336 source = source lang
1337 target = a specific target lang
1338 comment = a comment to the translation team
1340 See the following for some ideas
1341 http://www.zope.org/DevHome/Wikis/DevSite/Projects/ComponentArchitecture/ZPTInternationalizationSupport
1343 Other notes:
1344 - There is no need to replicate the i18n:name attribute from plone / PTL,
1345 as cheetah placeholders serve the same purpose
1350 return message
1352 def getFileContents(self, path):
1353 """A hook for getting the contents of a file. The default
1354 implementation just uses the Python open() function to load local files.
1355 This method could be reimplemented to allow reading of remote files via
1356 various protocols, as PHP allows with its 'URL fopen wrapper'
1359 fp = open(path,'r')
1360 output = fp.read()
1361 fp.close()
1362 return output
1364 def runAsMainProgram(self):
1365 """Allows the Template to function as a standalone command-line program
1366 for static page generation.
1368 Type 'python yourtemplate.py --help to see what it's capabable of.
1371 from TemplateCmdLineIface import CmdLineIface
1372 CmdLineIface(templateObj=self).run()
1374 ##################################################
1375 ## internal methods -- not to be called by end-users
1377 def _initCheetahInstance(self,
1378 searchList=None,
1379 namespaces=None,
1380 filter='RawOrEncodedUnicode', # which filter from Cheetah.Filters
1381 filtersLib=Filters,
1382 errorCatcher=None,
1383 _globalSetVars=None,
1384 _preBuiltSearchList=None):
1385 """Sets up the instance attributes that cheetah templates use at
1386 run-time.
1388 This is automatically called by the __init__ method of compiled
1389 templates.
1391 Note that the names of instance attributes used by Cheetah are prefixed
1392 with '_CHEETAH__' (2 underscores), where class attributes are prefixed
1393 with '_CHEETAH_' (1 underscore).
1395 if getattr(self, '_CHEETAH__instanceInitialized', False):
1396 return
1398 if namespaces is not None:
1399 assert searchList is None, (
1400 'Provide "namespaces" or "searchList", not both!')
1401 searchList = namespaces
1402 if searchList is not None and not isinstance(searchList, (list, tuple)):
1403 searchList = [searchList]
1405 self._CHEETAH__globalSetVars = {}
1406 if _globalSetVars is not None:
1407 # this is intended to be used internally by Nested Templates in #include's
1408 self._CHEETAH__globalSetVars = _globalSetVars
1410 if _preBuiltSearchList is not None:
1411 # happens with nested Template obj creation from #include's
1412 self._CHEETAH__searchList = list(_preBuiltSearchList)
1413 self._CHEETAH__searchList.append(self)
1414 else:
1415 # create our own searchList
1416 self._CHEETAH__searchList = [self._CHEETAH__globalSetVars]
1417 if searchList is not None:
1418 self._CHEETAH__searchList.extend(list(searchList))
1419 self._CHEETAH__searchList.append( self )
1420 self._CHEETAH__cheetahIncludes = {}
1421 self._CHEETAH__cacheRegions = {}
1422 self._CHEETAH__indenter = Indenter()
1423 self._CHEETAH__filtersLib = filtersLib
1424 self._CHEETAH__filters = {}
1425 if type(filter) in StringTypes:
1426 filterName = filter
1427 klass = getattr(self._CHEETAH__filtersLib, filterName)
1428 else:
1429 klass = filter
1430 filterName = klass.__name__
1431 self._CHEETAH__currentFilter = self._CHEETAH__filters[filterName] = klass(self).filter
1432 self._CHEETAH__initialFilter = self._CHEETAH__currentFilter
1433 self._CHEETAH__errorCatchers = {}
1434 if errorCatcher:
1435 if type(errorCatcher) in StringTypes:
1436 errorCatcherClass = getattr(ErrorCatchers, errorCatcher)
1437 elif type(errorCatcher) == ClassType:
1438 errorCatcherClass = errorCatcher
1440 self._CHEETAH__errorCatcher = ec = errorCatcherClass(self)
1441 self._CHEETAH__errorCatchers[errorCatcher.__class__.__name__] = ec
1443 else:
1444 self._CHEETAH__errorCatcher = None
1445 self._CHEETAH__initErrorCatcher = self._CHEETAH__errorCatcher
1447 if not hasattr(self, 'transaction'):
1448 self.transaction = None
1449 self._CHEETAH__instanceInitialized = True
1450 self._CHEETAH__isBuffering = False
1451 self._CHEETAH__isControlledByWebKit = False
1453 self._CHEETAH__cacheStore = None
1454 if self._CHEETAH_cacheStore is not None:
1455 self._CHEETAH__cacheStore = self._CHEETAH_cacheStore
1457 def _compile(self, source=None, file=None, compilerSettings=Unspecified,
1458 moduleName=None, mainMethodName=None):
1459 """Compile the template. This method is automatically called by
1460 Template.__init__ it is provided with 'file' or 'source' args.
1462 USERS SHOULD *NEVER* CALL THIS METHOD THEMSELVES. Use Template.compile
1463 instead.
1465 if compilerSettings is Unspecified:
1466 compilerSettings = self._getCompilerSettings(source, file) or {}
1467 mainMethodName = mainMethodName or self._CHEETAH_defaultMainMethodName
1468 self._fileMtime = None
1469 self._fileDirName = None
1470 self._fileBaseName = None
1471 if file and type(file) in StringTypes:
1472 file = self.serverSidePath(file)
1473 self._fileMtime = os.path.getmtime(file)
1474 self._fileDirName, self._fileBaseName = os.path.split(file)
1475 self._filePath = file
1476 templateClass = self.compile(source, file,
1477 moduleName=moduleName,
1478 mainMethodName=mainMethodName,
1479 compilerSettings=compilerSettings,
1480 keepRefToGeneratedCode=True)
1481 self.__class__ = templateClass
1482 # must initialize it so instance attributes are accessible
1483 templateClass.__init__(self,
1484 #_globalSetVars=self._CHEETAH__globalSetVars,
1485 #_preBuiltSearchList=self._CHEETAH__searchList
1487 if not hasattr(self, 'transaction'):
1488 self.transaction = None
1490 def _handleCheetahInclude(self, srcArg, trans=None, includeFrom='file', raw=False):
1491 """Called at runtime to handle #include directives.
1493 _includeID = srcArg
1494 if not self._CHEETAH__cheetahIncludes.has_key(_includeID):
1495 if not raw:
1496 if includeFrom == 'file':
1497 source = None
1498 if type(srcArg) in StringTypes:
1499 if hasattr(self, 'serverSidePath'):
1500 file = path = self.serverSidePath(srcArg)
1501 else:
1502 file = path = os.path.normpath(srcArg)
1503 else:
1504 file = srcArg ## a file-like object
1505 else:
1506 source = srcArg
1507 file = None
1508 # @@TR: might want to provide some syntax for specifying the
1509 # Template class to be used for compilation so compilerSettings
1510 # can be changed.
1511 compiler = self._getTemplateAPIClassForIncludeDirectiveCompilation(source, file)
1512 nestedTemplateClass = compiler.compile(source=source,file=file)
1513 nestedTemplate = nestedTemplateClass(_preBuiltSearchList=self.searchList(),
1514 _globalSetVars=self._CHEETAH__globalSetVars)
1515 # Set the inner template filters to the initial filter of the
1516 # outer template:
1517 # this is the only really safe way to use
1518 # filter='WebSafe'.
1519 nestedTemplate._CHEETAH__initialFilter = self._CHEETAH__initialFilter
1520 nestedTemplate._CHEETAH__currentFilter = self._CHEETAH__initialFilter
1521 self._CHEETAH__cheetahIncludes[_includeID] = nestedTemplate
1522 else:
1523 if includeFrom == 'file':
1524 path = self.serverSidePath(srcArg)
1525 self._CHEETAH__cheetahIncludes[_includeID] = self.getFileContents(path)
1526 else:
1527 self._CHEETAH__cheetahIncludes[_includeID] = srcArg
1529 if not raw:
1530 self._CHEETAH__cheetahIncludes[_includeID].respond(trans)
1531 else:
1532 trans.response().write(self._CHEETAH__cheetahIncludes[_includeID])
1534 def _getTemplateAPIClassForIncludeDirectiveCompilation(self, source, file):
1535 """Returns the subclass of Template which should be used to compile
1536 #include directives.
1538 This abstraction allows different compiler settings to be used in the
1539 included template than were used in the parent.
1541 if issubclass(self.__class__, Template):
1542 return self.__class__
1543 else:
1544 return Template
1546 ## functions for using templates as CGI scripts
1547 def webInput(self, names, namesMulti=(), default='', src='f',
1548 defaultInt=0, defaultFloat=0.00, badInt=0, badFloat=0.00, debug=False):
1549 """Method for importing web transaction variables in bulk.
1551 This works for GET/POST fields both in Webware servlets and in CGI
1552 scripts, and for cookies and session variables in Webware servlets. If
1553 you try to read a cookie or session variable in a CGI script, you'll get
1554 a RuntimeError. 'In a CGI script' here means 'not running as a Webware
1555 servlet'. If the CGI environment is not properly set up, Cheetah will
1556 act like there's no input.
1558 The public method provided is:
1560 def webInput(self, names, namesMulti=(), default='', src='f',
1561 defaultInt=0, defaultFloat=0.00, badInt=0, badFloat=0.00, debug=False):
1563 This method places the specified GET/POST fields, cookies or session
1564 variables into a dictionary, which is both returned and put at the
1565 beginning of the searchList. It handles:
1567 * single vs multiple values
1568 * conversion to integer or float for specified names
1569 * default values/exceptions for missing or bad values
1570 * printing a snapshot of all values retrieved for debugging
1572 All the 'default*' and 'bad*' arguments have 'use or raise' behavior,
1573 meaning that if they're a subclass of Exception, they're raised. If
1574 they're anything else, that value is substituted for the missing/bad
1575 value.
1578 The simplest usage is:
1580 #silent $webInput(['choice'])
1581 $choice
1583 dic = self.webInput(['choice'])
1584 write(dic['choice'])
1586 Both these examples retrieves the GET/POST field 'choice' and print it.
1587 If you leave off the'#silent', all the values would be printed too. But
1588 a better way to preview the values is
1590 #silent $webInput(['name'], $debug=1)
1592 because this pretty-prints all the values inside HTML <PRE> tags.
1594 ** KLUDGE: 'debug' is supposed to insert into the template output, but it
1595 wasn't working so I changed it to a'print' statement. So the debugging
1596 output will appear wherever standard output is pointed, whether at the
1597 terminal, in a Webware log file, or whatever. ***
1599 Since we didn't specify any coversions, the value is a string. It's a
1600 'single' value because we specified it in 'names' rather than
1601 'namesMulti'. Single values work like this:
1603 * If one value is found, take it.
1604 * If several values are found, choose one arbitrarily and ignore the rest.
1605 * If no values are found, use or raise the appropriate 'default*' value.
1607 Multi values work like this:
1608 * If one value is found, put it in a list.
1609 * If several values are found, leave them in a list.
1610 * If no values are found, use the empty list ([]). The 'default*'
1611 arguments are *not* consulted in this case.
1613 Example: assume 'days' came from a set of checkboxes or a multiple combo
1614 box on a form, and the user chose'Monday', 'Tuesday' and 'Thursday'.
1616 #silent $webInput([], ['days'])
1617 The days you chose are: #slurp
1618 #for $day in $days
1619 $day #slurp
1620 #end for
1622 dic = self.webInput([], ['days'])
1623 write('The days you chose are: ')
1624 for day in dic['days']:
1625 write(day + ' ')
1627 Both these examples print: 'The days you chose are: Monday Tuesday Thursday'.
1629 By default, missing strings are replaced by '' and missing/bad numbers
1630 by zero. (A'bad number' means the converter raised an exception for
1631 it, usually because of non-numeric characters in the value.) This
1632 mimics Perl/PHP behavior, and simplifies coding for many applications
1633 where missing/bad values *should* be blank/zero. In those relatively
1634 few cases where you must distinguish between empty-string/zero on the
1635 one hand and missing/bad on the other, change the appropriate
1636 'default*' and 'bad*' arguments to something like:
1638 * None
1639 * another constant value
1640 * $NonNumericInputError/self.NonNumericInputError
1641 * $ValueError/ValueError
1643 (NonNumericInputError is defined in this class and is useful for
1644 distinguishing between bad input vs a TypeError/ValueError thrown for
1645 some other rason.)
1647 Here's an example using multiple values to schedule newspaper
1648 deliveries. 'checkboxes' comes from a form with checkboxes for all the
1649 days of the week. The days the user previously chose are preselected.
1650 The user checks/unchecks boxes as desired and presses Submit. The value
1651 of 'checkboxes' is a list of checkboxes that were checked when Submit
1652 was pressed. Our task now is to turn on the days the user checked, turn
1653 off the days he unchecked, and leave on or off the days he didn't
1654 change.
1656 dic = self.webInput([], ['dayCheckboxes'])
1657 wantedDays = dic['dayCheckboxes'] # The days the user checked.
1658 for day, on in self.getAllValues():
1659 if not on and wantedDays.has_key(day):
1660 self.TurnOn(day)
1661 # ... Set a flag or insert a database record ...
1662 elif on and not wantedDays.has_key(day):
1663 self.TurnOff(day)
1664 # ... Unset a flag or delete a database record ...
1666 'source' allows you to look up the variables from a number of different
1667 sources:
1668 'f' fields (CGI GET/POST parameters)
1669 'c' cookies
1670 's' session variables
1671 'v' 'values', meaning fields or cookies
1673 In many forms, you're dealing only with strings, which is why the
1674 'default' argument is third and the numeric arguments are banished to
1675 the end. But sometimes you want automatic number conversion, so that
1676 you can do numeric comparisions in your templates without having to
1677 write a bunch of conversion/exception handling code. Example:
1679 #silent $webInput(['name', 'height:int'])
1680 $name is $height cm tall.
1681 #if $height >= 300
1682 Wow, you're tall!
1683 #else
1684 Pshaw, you're short.
1685 #end if
1687 dic = self.webInput(['name', 'height:int'])
1688 name = dic[name]
1689 height = dic[height]
1690 write('%s is %s cm tall.' % (name, height))
1691 if height > 300:
1692 write('Wow, you're tall!')
1693 else:
1694 write('Pshaw, you're short.')
1696 To convert a value to a number, suffix ':int' or ':float' to the name.
1697 The method will search first for a 'height:int' variable and then for a
1698 'height' variable. (It will be called 'height' in the final
1699 dictionary.) If a numeric conversion fails, use or raise 'badInt' or
1700 'badFloat'. Missing values work the same way as for strings, except the
1701 default is 'defaultInt' or 'defaultFloat' instead of 'default'.
1703 If a name represents an uploaded file, the entire file will be read into
1704 memory. For more sophistocated file-upload handling, leave that name
1705 out of the list and do your own handling, or wait for
1706 Cheetah.Utils.UploadFileMixin.
1708 This only in a subclass that also inherits from Webware's Servlet or
1709 HTTPServlet. Otherwise you'll get an AttributeError on 'self.request'.
1711 EXCEPTIONS: ValueError if 'source' is not one of the stated characters.
1712 TypeError if a conversion suffix is not ':int' or ':float'.
1714 FUTURE EXPANSION: a future version of this method may allow source
1715 cascading; e.g., 'vs' would look first in 'values' and then in session
1716 variables.
1718 Meta-Data
1719 ================================================================================
1720 Author: Mike Orr <iron@mso.oz.net>
1721 License: This software is released for unlimited distribution under the
1722 terms of the MIT license. See the LICENSE file.
1723 Version: $Revision: 1.185 $
1724 Start Date: 2002/03/17
1725 Last Revision Date: $Date: 2007/10/02 01:36:26 $
1726 """
1727 src = src.lower()
1728 isCgi = not self._CHEETAH__isControlledByWebKit
1729 if isCgi and src in ('f', 'v'):
1730 global _formUsedByWebInput
1731 if _formUsedByWebInput is None:
1732 _formUsedByWebInput = cgi.FieldStorage()
1733 source, func = 'field', _formUsedByWebInput.getvalue
1734 elif isCgi and src == 'c':
1735 raise RuntimeError("can't get cookies from a CGI script")
1736 elif isCgi and src == 's':
1737 raise RuntimeError("can't get session variables from a CGI script")
1738 elif isCgi and src == 'v':
1739 source, func = 'value', self.request().value
1740 elif isCgi and src == 's':
1741 source, func = 'session', self.request().session().value
1742 elif src == 'f':
1743 source, func = 'field', self.request().field
1744 elif src == 'c':
1745 source, func = 'cookie', self.request().cookie
1746 elif src == 'v':
1747 source, func = 'value', self.request().value
1748 elif src == 's':
1749 source, func = 'session', self.request().session().value
1750 else:
1751 raise TypeError("arg 'src' invalid")
1752 sources = source + 's'
1753 converters = {
1754 '' : _Converter('string', None, default, default ),
1755 'int' : _Converter('int', int, defaultInt, badInt ),
1756 'float': _Converter('float', float, defaultFloat, badFloat), }
1757 #pprint.pprint(locals()); return {}
1758 dic = {} # Destination.
1759 for name in names:
1760 k, v = _lookup(name, func, False, converters)
1761 dic[k] = v
1762 for name in namesMulti:
1763 k, v = _lookup(name, func, True, converters)
1764 dic[k] = v
1765 # At this point, 'dic' contains all the keys/values we want to keep.
1766 # We could split the method into a superclass
1767 # method for Webware/WebwareExperimental and a subclass for Cheetah.
1768 # The superclass would merely 'return dic'. The subclass would
1769 # 'dic = super(ThisClass, self).webInput(names, namesMulti, ...)'
1770 # and then the code below.
1771 if debug:
1772 print "<PRE>\n" + pprint.pformat(dic) + "\n</PRE>\n\n"
1773 self.searchList().insert(0, dic)
1774 return dic
1776 T = Template # Short and sweet for debugging at the >>> prompt.
1779 def genParserErrorFromPythonException(source, file, generatedPyCode, exception):
1781 #print dir(exception)
1783 filename = isinstance(file, (str, unicode)) and file or None
1785 sio = StringIO.StringIO()
1786 traceback.print_exc(1, sio)
1787 formatedExc = sio.getvalue()
1789 if hasattr(exception, 'lineno'):
1790 pyLineno = exception.lineno
1791 else:
1792 pyLineno = int(re.search('[ \t]*File.*line (\d+)', formatedExc).group(1))
1794 lines = generatedPyCode.splitlines()
1796 prevLines = [] # (i, content)
1797 for i in range(1,4):
1798 if pyLineno-i <=0:
1799 break
1800 prevLines.append( (pyLineno+1-i,lines[pyLineno-i]) )
1802 nextLines = [] # (i, content)
1803 for i in range(1,4):
1804 if not pyLineno+i < len(lines):
1805 break
1806 nextLines.append( (pyLineno+i,lines[pyLineno+i]) )
1807 nextLines.reverse()
1808 report = 'Line|Python Code\n'
1809 report += '----|-------------------------------------------------------------\n'
1810 while prevLines:
1811 lineInfo = prevLines.pop()
1812 report += "%(row)-4d|%(line)s\n"% {'row':lineInfo[0], 'line':lineInfo[1]}
1814 if hasattr(exception, 'offset'):
1815 report += ' '*(3+(exception.offset or 0)) + '^\n'
1817 while nextLines:
1818 lineInfo = nextLines.pop()
1819 report += "%(row)-4d|%(line)s\n"% {'row':lineInfo[0], 'line':lineInfo[1]}
1822 message = [
1823 "Error in the Python code which Cheetah generated for this template:",
1824 '='*80,
1826 str(exception),
1827 '',
1828 report,
1829 '='*80,
1831 cheetahPosMatch = re.search('line (\d+), col (\d+)', formatedExc)
1832 if cheetahPosMatch:
1833 lineno = int(cheetahPosMatch.group(1))
1834 col = int(cheetahPosMatch.group(2))
1835 #if hasattr(exception, 'offset'):
1836 # col = exception.offset
1837 message.append('\nHere is the corresponding Cheetah code:\n')
1838 else:
1839 lineno = None
1840 col = None
1841 cheetahPosMatch = re.search('line (\d+), col (\d+)',
1842 '\n'.join(lines[max(pyLineno-2, 0):]))
1843 if cheetahPosMatch:
1844 lineno = int(cheetahPosMatch.group(1))
1845 col = int(cheetahPosMatch.group(2))
1846 message.append('\nHere is the corresponding Cheetah code.')
1847 message.append('** I had to guess the line & column numbers,'
1848 ' so they are probably incorrect:\n')
1851 message = '\n'.join(message)
1852 reader = SourceReader(source, filename=filename)
1853 return ParseError(reader, message, lineno=lineno,col=col)
1856 # vim: shiftwidth=4 tabstop=4 expandtab