Need config here for KRKeegan's per-TiVo share patch.
[pyTivo/wgw.git] / Cheetah / Template.py
blob0185b50d4f37e24db17289811c11f7cb9ec2dbfb
1 #!/usr/bin/env python
2 # $Id: Template.py,v 1.181 2006/06/22 20:25:16 hierro 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.181 $
13 Start Date: 2001/03/30
14 Last Revision Date: $Date: 2006/06/22 20:25:16 $
15 """
16 __author__ = "Tavis Rudd <tavis@damnsimple.com>"
17 __revision__ = "$Revision: 1.181 $"[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_'+baseModuleName
109 +'_'
110 +''.join(map(lambda x: '%02d' % x, time.localtime(time.time())[:6]))
111 + str(randrange(10000, 99999)))
112 return finalName
114 # Cache of a cgi.FieldStorage() instance, maintained by .webInput().
115 # This is only relavent to templates used as CGI scripts.
116 _formUsedByWebInput = None
118 # used in Template.compile()
119 def valOrDefault(val, default):
120 if val is not Unspecified: return val
121 else: return default
124 class CompileCacheItem:
125 pass
127 class TemplatePreprocessor:
128 """This is used with the preprocessors argument to Template.compile().
130 See the docstring for Template.compile
132 ** Preprocessors are an advanced topic **
134 def __init__(self, settings):
135 self._settings = settings
137 def preprocess(self, source, file):
138 """Create an intermediate template and return the source code
139 it outputs
141 settings = self._settings
142 if not source: # @@TR: this needs improving
143 if isinstance(file, (str, unicode)): # it's a filename.
144 f = open(file)
145 source = f.read()
146 f.close()
147 elif hasattr(file, 'read'):
148 source = file.read()
149 file = None
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',)]
157 compileKwArgs = {}
158 for arg in possibleKwArgs:
159 if hasattr(settings, arg):
160 compileKwArgs[arg] = getattr(settings, arg)
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
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.
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.
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.
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()
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.
199 2) tmplInstance = Template(src)
200 # or Template(src, namespaces=[namespace,...])
201 outputStr = str(tmplInstance) # or outputStr = tmplInstance.aMethodYouDefined(...args...)
203 Notes on the usage patterns:
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).
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).
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.
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()
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.
240 Class attributes which are used in class methods look like this:
241 klass._CHEETAH_useCompilationCache (_CHEETAH_xxx)
243 Instance attributes look like this:
244 klass._CHEETAH__globalSetVars (_CHEETAH__xxx with 2 underscores)
247 # this is used by ._addCheetahPlumbingCodeToClass()
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',
264 '_getCacheStore',
265 '_getCacheStoreIdPrefix',
266 '_createCacheRegion',
267 'getCacheRegion',
268 'getCacheRegions',
269 'refreshCache',
271 '_handleCheetahInclude',
272 '_getTemplateAPIClassForIncludeDirectiveCompilation',
274 _CHEETAH_requiredCheetahClassMethods = ('subclass',)
275 _CHEETAH_requiredCheetahClassAttributes = ('cacheRegionClass','cacheStore',
276 'cacheStoreIdPrefix','cacheStoreClass')
278 ## the following are used by .compile(). Most are documented in its docstring.
279 _CHEETAH_cacheModuleFilesForTracebacks = False
280 _CHEETAH_cacheDirForModuleFiles = None # change to a dirname
282 _CHEETAH_compileCache = dict() # cache store for compiled code and classes
283 # To do something other than simple in-memory caching you can create an
284 # alternative cache store. It just needs to support the basics of Python's
285 # mapping/dict protocol. E.g.:
286 # class AdvCachingTemplate(Template):
287 # _CHEETAH_compileCache = MemoryOrFileCache()
288 _CHEETAH_compileLock = Lock() # used to prevent race conditions
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 # defaults to DEFAULT_COMPILER_SETTINGS['mainMethodName']:
298 _CHEETAH_defaultMainMethodNameForTemplates = None
299 _CHEETAH_defaultModuleNameForTemplates = 'DynamicallyCompiledCheetahTemplate'
300 _CHEETAH_defaultModuleGlobalsForTemplates = None
301 _CHEETAH_preprocessors = None
302 _CHEETAH_defaultPreprocessorClass = TemplatePreprocessor
304 ## The following attributes are used by instance methods:
305 _CHEETAH_generatedModuleCode = None
306 NonNumericInputError = NonNumericInputError
307 _CHEETAH_cacheRegionClass = CacheRegion
308 _CHEETAH_cacheStoreClass = MemoryCacheStore
309 #_CHEETAH_cacheStoreClass = MemcachedCacheStore
310 _CHEETAH_cacheStore = None
311 _CHEETAH_cacheStoreIdPrefix = None
313 def _getCompilerClass(klass, source=None, file=None):
314 return klass._CHEETAH_compilerClass
315 _getCompilerClass = classmethod(_getCompilerClass)
317 def _getCompilerSettings(klass, source=None, file=None):
318 return klass._CHEETAH_compilerSettings
319 _getCompilerSettings = classmethod(_getCompilerSettings)
321 def compile(klass, source=None, file=None,
322 returnAClass=True,
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,
337 keepRefToGeneratedCode=Unspecified,
341 The core API for compiling Cheetah source code into template classes.
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.
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.
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.
362 Hooks are provided template source preprocessing. See the notes on the
363 'preprocessors' arg.
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.
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)
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.
379 - returnAClass=True
380 If false, return the generated module code rather than a class.
382 - compilerSettings (a dict)
383 Default: Template._CHEETAH_compilerSettings=None
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.
390 - compilerClass (a class)
391 Default: Template._CHEETAH_compilerClass=Cheetah.Compiler.Compiler
393 a subclass of Cheetah.Compiler.Compiler. Mucking with this is a
394 very advanced topic.
396 - moduleName (a string)
397 Default:
398 Template._CHEETAH_defaultModuleNameForTemplates
399 ='DynamicallyCompiledCheetahTemplate'
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.
408 - className (a string)
409 Default: Template._CHEETAH_defaultClassNameForTemplates=None
411 What to name the generated Python class. If the provided value is
412 None, the moduleName is use as the class name.
414 - mainMethodName (a string)
415 Default:
416 Template._CHEETAH_defaultMainMethodNameForTemplates
417 =None (and thus DEFAULT_COMPILER_SETTINGS['mainMethodName'])
419 What to name the main output generating method in the compiled
420 template class.
422 - baseclass (a string or a class)
423 Default: Template._CHEETAH_defaultBaseclassForTemplates=None
425 Specifies the baseclass for the template without manually
426 including an #extends directive in the source. The #extends
427 directive trumps this arg.
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'.
434 If the provided value is a class, Cheetah will handle all the
435 details for you.
437 - moduleGlobals (a dict)
438 Default: Template._CHEETAH_defaultModuleGlobalsForTemplates=None
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!
444 - cacheCompilationResults (True/False)
445 Default: Template._CHEETAH_cacheCompilationResults=True
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.
451 - useCache (True/False)
452 Default: Template._CHEETAH_useCompilationCache=True
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.
459 - cacheModuleFilesForTracebacks (True/False)
460 Default: Template._CHEETAH_cacheModuleFilesForTracebacks=False
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:
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
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:
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
488 - cacheDirForModuleFiles (a string representing a dir path)
489 Default: Template._CHEETAH_cacheDirForModuleFiles=None
491 See notes on cacheModuleFilesForTracebacks.
493 - preprocessors
494 Default: Template._CHEETAH_preprocessors=None
496 ** THIS IS A VERY ADVANCED TOPIC **
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.
503 The major expected use cases are:
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.
519 The'preprocess syntax' is just Cheetah's standard one with
520 alternatives for the $ and # tokens:
522 e.g. '@' and '%' for code like this
523 @aPreprocessVar $aRuntimeVar
524 %if aCompileTimeCondition then yyy else zzz
525 %% preprocessor comment
527 #if aRunTimeCondition then aaa else bbb
528 ## normal comment
529 $aRuntimeVar
531 b) adding #import and #extends directives dynamically based on
532 the source
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).
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:
545 - an object with a .preprocess(source, file) method
546 - a callable with the following signature:
547 source, file = f(source, file)
549 or one of the forms below:
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=['@ %'])
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.
575 klass = Template.compile(src,
576 preprocessors=[ dict(tokens='@ %', searchList=[...]) ] )
579 ##################################################
580 ## normalize and validate args
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
589 if boolTypeAvailable:
590 B = types.BooleanType
592 vt(source, 'source', [N,S,U], 'string or None')
593 vt(file, 'file',[N,S,U,F], 'string, file-like object, or None')
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')
600 cacheCompilationResults = valOrDefault(
601 cacheCompilationResults, klass._CHEETAH_cacheCompilationResults)
602 if boolTypeAvailable:
603 vt(cacheCompilationResults, 'cacheCompilationResults', [I,B], 'boolean')
605 useCache = valOrDefault(useCache, klass._CHEETAH_useCompilationCache)
606 if boolTypeAvailable:
607 vt(cacheCompilationResults, 'cacheCompilationResults', [I,B], 'boolean')
609 compilerSettings = valOrDefault(
610 compilerSettings, klass._getCompilerSettings(source, file) or {})
611 vt(compilerSettings, 'compilerSettings', [D], 'dictionary')
613 compilerClass = valOrDefault(compilerClass, klass._getCompilerClass(source, file))
615 preprocessors = valOrDefault(preprocessors, klass._CHEETAH_preprocessors)
617 keepRefToGeneratedCode = valOrDefault(
618 keepRefToGeneratedCode, klass._CHEETAH_keepRefToGeneratedCode)
619 if boolTypeAvailable:
620 vt(cacheCompilationResults, 'cacheCompilationResults', [I,B], 'boolean')
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
631 className = valOrDefault(
632 className, klass._CHEETAH_defaultClassNameForTemplates)
633 vt(className, 'className', [N,S], 'string or None')
634 className = className or moduleName
636 mainMethodName = valOrDefault(
637 mainMethodName, klass._CHEETAH_defaultMainMethodNameForTemplates)
638 vt(mainMethodName, 'mainMethodName', [N,S], 'string or None')
640 moduleGlobals = valOrDefault(
641 moduleGlobals, klass._CHEETAH_defaultModuleGlobalsForTemplates)
643 cacheModuleFilesForTracebacks = valOrDefault(
644 cacheModuleFilesForTracebacks, klass._CHEETAH_cacheModuleFilesForTracebacks)
645 if boolTypeAvailable:
646 vt(cacheModuleFilesForTracebacks, 'cacheModuleFilesForTracebacks', [I,B], 'boolean')
648 cacheDirForModuleFiles = valOrDefault(
649 cacheDirForModuleFiles, klass._CHEETAH_cacheDirForModuleFiles)
650 vt(cacheDirForModuleFiles, 'cacheDirForModuleFiles', [N,S], 'string or None')
652 except TypeError, reason:
653 raise TypeError(reason)
655 ##################################################
656 ## handle any preprocessors
657 if preprocessors:
658 origSrc = source
659 source, file = klass._preprocessSource(source, file, preprocessors)
661 ##################################################
662 ## compilation, using cache if requested/possible
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 # @@TR: should soft-code this
670 baseclassName = 'CHEETAH_dynamicallyAssignedBaseClass_'+baseclass.__name__
671 baseclassValue = baseclass
674 cacheHash = None
675 cacheItem = None
676 if source or isinstance(file, (str, unicode)):
677 compilerSettingsHash = None
678 if compilerSettings:
679 compilerSettingsHash = hashDict(compilerSettings)
681 moduleGlobalsHash = None
682 if moduleGlobals:
683 moduleGlobalsHash = hashDict(moduleGlobals)
685 fileHash = None
686 if file:
687 fileHash = str(hash(file))+str(os.path.getmtime(file))
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),
702 except:
703 #@@TR: should add some logging to this
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 #print 'DEBUG: found cached copy'
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()
719 if not returnAClass:
720 return generatedModuleCode
721 else:
722 if cacheItem:
723 cacheItem.lastCheckoutTime = time.time()
724 return cacheItem.klass
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
735 try:
736 klass._CHEETAH_compileLock.acquire()
737 uniqueModuleName = _genUniqueModuleName(moduleName)
738 __file__ = uniqueModuleName+'.py' # relative file path with no dir part
739 mod = new.module(uniqueModuleName)
740 sys.modules[uniqueModuleName] = mod
741 finally:
742 klass._CHEETAH_compileLock.release()
744 try:
745 if cacheModuleFilesForTracebacks:
746 if not os.path.exists(cacheDirForModuleFiles):
747 raise Exception('%s does not exist'%
748 cacheDirForModuleFiles)
750 __file__ = os.path.join(cacheDirForModuleFiles, __file__)
751 try:
752 klass._CHEETAH_compileLock.acquire()
753 # @@TR: might want to assert that it doesn't already exist
754 try:
755 open(__file__, 'w').write(generatedModuleCode)
756 # @@TR: should probably restrict the perms, etc.
757 except OSError:
758 # @@ TR: should this optionally raise?
759 traceback.print_exc(file=sys.stderr)
760 finally:
761 klass._CHEETAH_compileLock.release()
763 if moduleGlobals:
764 for k, v in moduleGlobals.items():
765 setattr(mod, k, v)
766 mod.__file__ = __file__
767 if __orig_file__ and os.path.exists(__orig_file__):
768 # this is used in the WebKit filemonitoring code
769 mod.__orig_file__ = __orig_file__
771 if baseclass and baseclassValue:
772 setattr(mod, baseclassName, baseclassValue)
774 try:
775 co = compile(generatedModuleCode, __file__, 'exec')
776 exec co in mod.__dict__
777 except SyntaxError, e:
778 try:
779 parseError = genParserErrorFromPythonException(
780 source, file, generatedModuleCode, exception=e)
781 except:
782 traceback.print_exc()
783 updateLinecache(__file__, generatedModuleCode)
784 e.generatedModuleCode = generatedModuleCode
785 raise e
786 else:
787 raise parseError
788 except Exception, e:
789 updateLinecache(__file__, generatedModuleCode)
790 e.generatedModuleCode = generatedModuleCode
791 raise
793 except:
794 del sys.modules[uniqueModuleName]
795 raise
797 templateClass = getattr(mod, className)
799 if (cacheCompilationResults
800 and cacheHash
801 and not klass._CHEETAH_compileCache.has_key(cacheHash)):
803 cacheItem = CompileCacheItem()
804 cacheItem.cacheTime = cacheItem.lastCheckoutTime = time.time()
805 cacheItem.code = generatedModuleCode
806 cacheItem.klass = templateClass
807 templateClass._CHEETAH_isInCompilationCache = True
808 klass._CHEETAH_compileCache[cacheHash] = cacheItem
809 else:
810 templateClass._CHEETAH_isInCompilationCache = False
812 if keepRefToGeneratedCode or cacheCompilationResults:
813 templateClass._CHEETAH_generatedModuleCode = generatedModuleCode
815 return templateClass
816 compile = classmethod(compile)
818 def subclass(klass, *args, **kws):
819 """Takes the same args as the .compile() classmethod and returns a
820 template that is a subclass of the template this method is called from.
822 T1 = Template.compile(' foo - $meth1 - bar\n#def meth1: this is T1.meth1')
823 T2 = T1.subclass('#implements meth1\n this is T2.meth1')
825 kws['baseclass'] = klass
826 if isinstance(klass, Template):
827 templateAPIClass = klass
828 else:
829 templateAPIClass = Template
830 return templateAPIClass.compile(*args, **kws)
831 subclass = classmethod(subclass)
833 def _preprocessSource(klass, source, file, preprocessors):
834 """Iterates through the .compile() classmethod's preprocessors argument
835 and pipes the source code through each each preprocessor.
837 It returns the tuple (source, file) which is then used by
838 Template.compile to finish the compilation.
840 if not isinstance(preprocessors, (list, tuple)):
841 preprocessors = [preprocessors]
842 for preprocessor in preprocessors:
843 preprocessor = klass._normalizePreprocessorArg(preprocessor)
844 source, file = preprocessor.preprocess(source, file)
845 return source, file
846 _preprocessSource = classmethod(_preprocessSource)
848 def _normalizePreprocessorArg(klass, arg):
849 """Used to convert the items in the .compile() classmethod's
850 preprocessors argument into real source preprocessors. This permits the
851 use of several shortcut forms for defining preprocessors.
854 if hasattr(arg, 'preprocess'):
855 return arg
856 elif callable(arg):
857 class WrapperPreprocessor:
858 def preprocess(self, source, file):
859 return arg(source, file)
860 return WrapperPreprocessor()
861 else:
862 class Settings(object):
863 placeholderToken = None
864 directiveToken = None
865 settings = Settings()
866 if isinstance(arg, str) or isinstance(arg, (list, tuple)):
867 settings.tokens = arg
868 elif isinstance(arg, dict):
869 for k, v in arg.items():
870 setattr(settings, k, v)
871 else:
872 settings = arg
874 settings = klass._normalizePreprocessorSettings(settings)
875 return klass._CHEETAH_defaultPreprocessorClass(settings)
877 _normalizePreprocessorArg = classmethod(_normalizePreprocessorArg)
879 def _normalizePreprocessorSettings(klass, settings):
880 settings.keepRefToGeneratedCode = True
882 def normalizeSearchList(searchList):
883 if not isinstance(searchList, (list, tuple)):
884 searchList = [searchList]
885 return searchList
887 def normalizeTokens(tokens):
888 if isinstance(tokens, str):
889 return tokens.split() # space delimited string e.g.'@ %'
890 elif isinstance(tokens, (list, tuple)):
891 return tokens
892 else:
893 raise PreprocessError('invalid tokens argument: %r'%tokens)
895 if hasattr(settings, 'tokens'):
896 (settings.placeholderToken,
897 settings.directiveToken) = normalizeTokens(settings.tokens)
899 if (not getattr(settings,'compilerSettings', None)
900 and not getattr(settings, 'placeholderToken', None) ):
902 raise TypeError(
903 'Preprocessor requires either a "tokens" or a "compilerSettings" arg.'
904 ' Neither was provided.')
906 if not hasattr(settings, 'templateInitArgs'):
907 settings.templateInitArgs = {}
908 if 'searchList' not in settings.templateInitArgs:
909 if not hasattr(settings, 'searchList') and hasattr(settings, 'namespaces'):
910 settings.searchList = settings.namespaces
911 elif not hasattr(settings, 'searchList'):
912 settings.searchList = []
913 settings.templateInitArgs['searchList'] = settings.searchList
914 settings.templateInitArgs['searchList'] = (
915 normalizeSearchList(settings.templateInitArgs['searchList']))
917 if not hasattr(settings, 'outputTransformer'):
918 settings.outputTransformer = unicode
920 if not hasattr(settings, 'templateAPIClass'):
921 class PreprocessTemplateAPIClass(klass): pass
922 settings.templateAPIClass = PreprocessTemplateAPIClass
924 if not hasattr(settings, 'compilerSettings'):
925 settings.compilerSettings = {}
927 klass._updateSettingsWithPreprocessTokens(
928 compilerSettings=settings.compilerSettings,
929 placeholderToken=settings.placeholderToken,
930 directiveToken=settings.directiveToken
932 return settings
933 _normalizePreprocessorSettings = classmethod(_normalizePreprocessorSettings)
935 def _updateSettingsWithPreprocessTokens(
936 klass, compilerSettings, placeholderToken, directiveToken):
938 if (placeholderToken and 'cheetahVarStartToken' not in compilerSettings):
939 compilerSettings['cheetahVarStartToken'] = placeholderToken
940 if directiveToken:
941 if 'directiveStartToken' not in compilerSettings:
942 compilerSettings['directiveStartToken'] = directiveToken
943 if 'directiveEndToken' not in compilerSettings:
944 compilerSettings['directiveEndToken'] = directiveToken
945 if 'commentStartToken' not in compilerSettings:
946 compilerSettings['commentStartToken'] = directiveToken*2
947 if 'multiLineCommentStartToken' not in compilerSettings:
948 compilerSettings['multiLineCommentStartToken'] = (
949 directiveToken+'*')
950 if 'multiLineCommentEndToken' not in compilerSettings:
951 compilerSettings['multiLineCommentEndToken'] = (
952 '*'+directiveToken)
953 if 'EOLSlurpToken' not in compilerSettings:
954 compilerSettings['EOLSlurpToken'] = directiveToken
955 _updateSettingsWithPreprocessTokens = classmethod(_updateSettingsWithPreprocessTokens)
957 def _addCheetahPlumbingCodeToClass(klass, concreteTemplateClass):
958 """If concreteTemplateClass is not a subclass of Cheetah.Template, add
959 the required cheetah methods and attributes to it.
961 This is called on each new template class after it has been compiled.
962 If concreteTemplateClass is not a subclass of Cheetah.Template but
963 already has method with the same name as one of the required cheetah
964 methods, this will skip that method.
966 for methodname in klass._CHEETAH_requiredCheetahMethods:
967 if not hasattr(concreteTemplateClass, methodname):
968 method = getattr(Template, methodname)
969 newMethod = new.instancemethod(method.im_func, None, concreteTemplateClass)
970 #print methodname, method
971 setattr(concreteTemplateClass, methodname, newMethod)
973 for classMethName in klass._CHEETAH_requiredCheetahClassMethods:
974 if not hasattr(concreteTemplateClass, classMethName):
975 meth = getattr(klass, classMethName)
976 setattr(concreteTemplateClass, classMethName, classmethod(meth.im_func))
978 for attrname in klass._CHEETAH_requiredCheetahClassAttributes:
979 attrname = '_CHEETAH_'+attrname
980 if not hasattr(concreteTemplateClass, attrname):
981 attrVal = getattr(klass, attrname)
982 setattr(concreteTemplateClass, attrname, attrVal)
984 if (not hasattr(concreteTemplateClass, '__str__')
985 or concreteTemplateClass.__str__ is object.__str__):
987 mainMethNameAttr = '_mainCheetahMethod_for_'+concreteTemplateClass.__name__
988 mainMethName = getattr(concreteTemplateClass,mainMethNameAttr, None)
989 if mainMethName:
990 def __str__(self): return getattr(self, mainMethName)()
991 elif (hasattr(concreteTemplateClass, 'respond')
992 and concreteTemplateClass.respond!=Servlet.respond):
993 def __str__(self): return self.respond()
994 else:
995 def __str__(self):
996 if hasattr(self, mainMethNameAttr):
997 return getattr(self,mainMethNameAttr)()
998 elif hasattr(self, 'respond'):
999 return self.respond()
1000 else:
1001 return super(self.__class__, self).__str__()
1003 __str__ = new.instancemethod(__str__, None, concreteTemplateClass)
1004 setattr(concreteTemplateClass, '__str__', __str__)
1006 _addCheetahPlumbingCodeToClass = classmethod(_addCheetahPlumbingCodeToClass)
1008 ## end classmethods ##
1010 def __init__(self, source=None,
1012 namespaces=None, searchList=None,
1013 # use either or. They are aliases for the same thing.
1015 file=None,
1016 filter='RawOrEncodedUnicode', # which filter from Cheetah.Filters
1017 filtersLib=Filters,
1018 errorCatcher=None,
1020 compilerSettings=Unspecified, # control the behaviour of the compiler
1021 _globalSetVars=None, # used internally for #include'd templates
1022 _preBuiltSearchList=None # used internally for #include'd templates
1024 """a) compiles a new template OR b) instantiates an existing template.
1026 Read this docstring carefully as there are two distinct usage patterns.
1027 You should also read this class' main docstring.
1029 a) to compile a new template:
1030 t = Template(source=aSourceString)
1031 # or
1032 t = Template(file='some/path')
1033 # or
1034 t = Template(file=someFileObject)
1035 # or
1036 namespaces = [{'foo':'bar'}]
1037 t = Template(source=aSourceString, namespaces=namespaces)
1038 # or
1039 t = Template(file='some/path', namespaces=namespaces)
1041 print t
1043 b) to create an instance of an existing, precompiled template class:
1044 ## i) first you need a reference to a compiled template class:
1045 tclass = Template.compile(source=src) # or just Template.compile(src)
1046 # or
1047 tclass = Template.compile(file='some/path')
1048 # or
1049 tclass = Template.compile(file=someFileObject)
1050 # or
1051 # if you used the command line compiler or have Cheetah's ImportHooks
1052 # installed your template class is also available via Python's
1053 # standard import mechanism:
1054 from ACompileTemplate import AcompiledTemplate as tclass
1056 ## ii) then you create an instance
1057 t = tclass(namespaces=namespaces)
1058 # or
1059 t = tclass(namespaces=namespaces, filter='RawOrEncodedUnicode')
1060 print t
1062 Arguments:
1063 for usage pattern a)
1064 If you are compiling a new template, you must provide either a
1065 'source' or 'file' arg, but not both:
1066 - source (string or None)
1067 - file (string path, file-like object, or None)
1069 Optional args (see below for more) :
1070 - compilerSettings
1071 Default: Template._CHEETAH_compilerSettings=None
1073 a dictionary of settings to override those defined in
1074 DEFAULT_COMPILER_SETTINGS. See
1075 Cheetah.Template.DEFAULT_COMPILER_SETTINGS and the Users' Guide
1076 for details.
1078 You can pass the source arg in as a positional arg with this usage
1079 pattern. Use keywords for all other args.
1081 for usage pattern b)
1082 Do not use positional args with this usage pattern, unless your
1083 template subclasses something other than Cheetah.Template and you
1084 want to pass positional args to that baseclass. E.g.:
1085 dictTemplate = Template.compile('hello $name from $caller', baseclass=dict)
1086 tmplvars = dict(name='world', caller='me')
1087 print dictTemplate(tmplvars)
1088 This usage requires all Cheetah args to be passed in as keyword args.
1090 optional args for both usage patterns:
1092 - namespaces (aka 'searchList')
1093 Default: None
1095 an optional list of namespaces (dictionaries, objects, modules,
1096 etc.) which Cheetah will search through to find the variables
1097 referenced in $placeholders.
1099 If you provide a single namespace instead of a list, Cheetah will
1100 automatically convert it into a list.
1102 NOTE: Cheetah does NOT force you to use the namespaces search list
1103 and related features. It's on by default, but you can turn if off
1104 using the compiler settings useSearchList=False or
1105 useNameMapper=False.
1107 - filter
1108 Default: 'EncodeUnicode'
1110 Which filter should be used for output filtering. This should
1111 either be a string which is the name of a filter in the
1112 'filtersLib' or a subclass of Cheetah.Filters.Filter. . See the
1113 Users' Guide for more details.
1115 - filtersLib
1116 Default: Cheetah.Filters
1118 A module containing subclasses of Cheetah.Filters.Filter. See the
1119 Users' Guide for more details.
1121 - errorCatcher
1122 Default: None
1124 This is a debugging tool. See the Users' Guide for more details.
1125 Do not use this or the #errorCatcher diretive with live
1126 production systems.
1128 Do NOT mess with the args _globalSetVars or _preBuiltSearchList!
1132 ##################################################
1133 ## Verify argument keywords and types
1135 S = types.StringType; U = types.UnicodeType
1136 L = types.ListType; T = types.TupleType
1137 D = types.DictType; F = types.FileType
1138 C = types.ClassType; M = types.ModuleType
1139 N = types.NoneType
1140 vt = VerifyType.VerifyType
1141 vtc = VerifyType.VerifyTypeClass
1142 try:
1143 vt(source, 'source', [N,S,U], 'string or None')
1144 vt(file, 'file', [N,S,U,F], 'string, file open for reading, or None')
1145 vtc(filter, 'filter', [S,C,type], 'string or class',
1146 Filters.Filter,
1147 '(if class, must be subclass of Cheetah.Filters.Filter)')
1148 vt(filtersLib, 'filtersLib', [S,M], 'string or module',
1149 '(if module, must contain subclasses of Cheetah.Filters.Filter)')
1150 vtc(errorCatcher, 'errorCatcher', [N,S,C,type], 'string, class or None',
1151 ErrorCatchers.ErrorCatcher,
1152 '(if class, must be subclass of Cheetah.ErrorCatchers.ErrorCatcher)')
1153 if compilerSettings is not Unspecified:
1154 vt(compilerSettings, 'compilerSettings', [D], 'dictionary')
1156 except TypeError, reason:
1157 # Re-raise the exception here so that the traceback will end in
1158 # this function rather than in some utility function.
1159 raise TypeError(reason)
1161 if source is not None and file is not None:
1162 raise TypeError("you must supply either a source string or the" +
1163 " 'file' keyword argument, but not both")
1165 ##################################################
1166 ## Do superclass initialization.
1167 Servlet.__init__(self)
1169 ##################################################
1170 ## Do required version check
1171 if not hasattr(self, '_CHEETAH_versionTuple'):
1172 try:
1173 mod = sys.modules[self.__class__.__module__]
1174 compiledVersion = mod.__CHEETAH_version__
1175 compiledVersionTuple = convertVersionStringToTuple(compiledVersion)
1176 if compiledVersionTuple < MinCompatibleVersionTuple:
1177 raise AssertionError(
1178 'This template was compiled with Cheetah version'
1179 ' %s. Templates compiled before version %s must be recompiled.'%(
1180 compiledVersion, MinCompatibleVersion))
1181 except AssertionError:
1182 raise
1183 except:
1184 pass
1186 ##################################################
1187 ## Setup instance state attributes used during the life of template
1188 ## post-compile
1190 self._initCheetahInstance(
1191 searchList=searchList, namespaces=namespaces,
1192 filter=filter, filtersLib=filtersLib,
1193 errorCatcher=errorCatcher,
1194 _globalSetVars=_globalSetVars,
1195 _preBuiltSearchList=_preBuiltSearchList)
1197 ##################################################
1198 ## Now, compile if we're meant to
1199 if (source is not None) or (file is not None):
1200 self._compile(source, file, compilerSettings=compilerSettings)
1202 def generatedModuleCode(self):
1203 """Return the module code the compiler generated, or None if no
1204 compilation took place.
1207 return self._CHEETAH_generatedModuleCode
1209 def generatedClassCode(self):
1210 """Return the class code the compiler generated, or None if no
1211 compilation took place.
1214 return self._CHEETAH_generatedModuleCode[
1215 self._CHEETAH_generatedModuleCode.find('\nclass '):
1216 self._CHEETAH_generatedModuleCode.find('\n## END CLASS DEFINITION')]
1218 def searchList(self):
1219 """Return a reference to the searchlist
1221 return self._CHEETAH__searchList
1223 def errorCatcher(self):
1224 """Return a reference to the current errorCatcher
1226 return self._CHEETAH__errorCatcher
1228 ## cache methods ##
1229 def _getCacheStore(self):
1230 if not self._CHEETAH__cacheStore:
1231 if self._CHEETAH_cacheStore is not None:
1232 self._CHEETAH__cacheStore = self._CHEETAH_cacheStore
1233 else:
1234 # @@TR: might want to provide a way to provide init args
1235 self._CHEETAH__cacheStore = self._CHEETAH_cacheStoreClass()
1237 return self._CHEETAH__cacheStore
1239 def _getCacheStoreIdPrefix(self):
1240 if self._CHEETAH_cacheStoreIdPrefix is not None:
1241 return self._CHEETAH_cacheStoreIdPrefix
1242 else:
1243 return str(id(self))
1245 def _createCacheRegion(self, regionID):
1246 return self._CHEETAH_cacheRegionClass(
1247 regionID=regionID,
1248 templateCacheIdPrefix=self._getCacheStoreIdPrefix(),
1249 cacheStore=self._getCacheStore())
1251 def getCacheRegion(self, regionID, cacheInfo=None, create=True):
1252 cacheRegion = self._CHEETAH__cacheRegions.get(regionID)
1253 if not cacheRegion and create:
1254 cacheRegion = self._createCacheRegion(regionID)
1255 self._CHEETAH__cacheRegions[regionID] = cacheRegion
1256 return cacheRegion
1258 def getCacheRegions(self):
1259 """Returns a dictionary of the 'cache regions' initialized in a
1260 template.
1262 Each #cache directive block or $*cachedPlaceholder is a separate 'cache
1263 region'.
1265 # returns a copy to prevent users mucking it up
1266 return self._CHEETAH__cacheRegions.copy()
1268 def refreshCache(self, cacheRegionId=None, cacheItemId=None):
1269 """Refresh a cache region or a specific cache item within a region.
1272 if not cacheRegionId:
1273 for key, cregion in self.getCacheRegions():
1274 cregion.clear()
1275 else:
1276 cregion = self._CHEETAH__cacheRegions.get(cacheRegionId)
1277 if not cregion:
1278 return
1279 if not cacheItemId: # clear the desired region and all its cacheItems
1280 cregion.clear()
1281 else: # clear one specific cache of a specific region
1282 cache = cregion.getCacheItem(cacheItemId)
1283 if cache:
1284 cache.clear()
1286 ## end cache methods ##
1288 def shutdown(self):
1289 """Break reference cycles before discarding a servlet.
1291 try:
1292 Servlet.shutdown(self)
1293 except:
1294 pass
1295 self._CHEETAH__searchList = None
1296 self.__dict__ = {}
1298 ## utility functions ##
1300 def getVar(self, varName, default=Unspecified, autoCall=True):
1301 """Get a variable from the searchList. If the variable can't be found
1302 in the searchList, it returns the default value if one was given, or
1303 raises NameMapper.NotFound.
1306 try:
1307 return valueFromSearchList(self.searchList(), varName.replace('$',''), autoCall)
1308 except NotFound:
1309 if default is not Unspecified:
1310 return default
1311 else:
1312 raise
1314 def varExists(self, varName, autoCall=True):
1315 """Test if a variable name exists in the searchList.
1317 try:
1318 valueFromSearchList(self.searchList(), varName.replace('$',''), autoCall)
1319 return True
1320 except NotFound:
1321 return False
1324 hasVar = varExists
1327 def i18n(self, message,
1328 plural=None,
1329 n=None,
1331 id=None,
1332 domain=None,
1333 source=None,
1334 target=None,
1335 comment=None
1337 """This is just a stub at this time.
1339 plural = the plural form of the message
1340 n = a sized argument to distinguish between single and plural forms
1342 id = msgid in the translation catalog
1343 domain = translation domain
1344 source = source lang
1345 target = a specific target lang
1346 comment = a comment to the translation team
1348 See the following for some ideas
1349 http://www.zope.org/DevHome/Wikis/DevSite/Projects/ComponentArchitecture/ZPTInternationalizationSupport
1351 Other notes:
1352 - There is no need to replicate the i18n:name attribute from plone / PTL,
1353 as cheetah placeholders serve the same purpose
1358 return message
1360 def getFileContents(self, path):
1361 """A hook for getting the contents of a file. The default
1362 implementation just uses the Python open() function to load local files.
1363 This method could be reimplemented to allow reading of remote files via
1364 various protocols, as PHP allows with its 'URL fopen wrapper'
1367 fp = open(path,'r')
1368 output = fp.read()
1369 fp.close()
1370 return output
1372 def runAsMainProgram(self):
1373 """Allows the Template to function as a standalone command-line program
1374 for static page generation.
1376 Type 'python yourtemplate.py --help to see what it's capabable of.
1379 from TemplateCmdLineIface import CmdLineIface
1380 CmdLineIface(templateObj=self).run()
1382 ##################################################
1383 ## internal methods -- not to be called by end-users
1385 def _initCheetahInstance(self,
1386 searchList=None,
1387 namespaces=None,
1388 filter='RawOrEncodedUnicode', # which filter from Cheetah.Filters
1389 filtersLib=Filters,
1390 errorCatcher=None,
1391 _globalSetVars=None,
1392 _preBuiltSearchList=None):
1393 """Sets up the instance attributes that cheetah templates use at
1394 run-time.
1396 This is automatically called by the __init__ method of compiled
1397 templates.
1399 Note that the names of instance attributes used by Cheetah are prefixed
1400 with '_CHEETAH__' (2 underscores), where class attributes are prefixed
1401 with '_CHEETAH_' (1 underscore).
1403 if getattr(self, '_CHEETAH__instanceInitialized', False):
1404 return
1406 if namespaces is not None:
1407 assert searchList is None, (
1408 'Provide "namespaces" or "searchList", not both!')
1409 searchList = namespaces
1410 if searchList is not None and not isinstance(searchList, (list, tuple)):
1411 searchList = [searchList]
1413 self._CHEETAH__globalSetVars = {}
1414 if _globalSetVars is not None:
1415 # this is intended to be used internally by Nested Templates in #include's
1416 self._CHEETAH__globalSetVars = _globalSetVars
1418 if _preBuiltSearchList is not None:
1419 # happens with nested Template obj creation from #include's
1420 self._CHEETAH__searchList = list(_preBuiltSearchList)
1421 self._CHEETAH__searchList.append(self)
1422 else:
1423 # create our own searchList
1424 self._CHEETAH__searchList = [self._CHEETAH__globalSetVars]
1425 if searchList is not None:
1426 self._CHEETAH__searchList.extend(list(searchList))
1427 self._CHEETAH__searchList.append( self )
1428 self._CHEETAH__cheetahIncludes = {}
1429 self._CHEETAH__cacheRegions = {}
1430 self._CHEETAH__indenter = Indenter()
1431 self._CHEETAH__filtersLib = filtersLib
1432 self._CHEETAH__filters = {}
1433 if type(filter) in StringTypes:
1434 filterName = filter
1435 klass = getattr(self._CHEETAH__filtersLib, filterName)
1436 else:
1437 klass = filter
1438 filterName = klass.__name__
1439 self._CHEETAH__currentFilter = self._CHEETAH__filters[filterName] = klass(self).filter
1440 self._CHEETAH__initialFilter = self._CHEETAH__currentFilter
1441 self._CHEETAH__errorCatchers = {}
1442 if errorCatcher:
1443 if type(errorCatcher) in StringTypes:
1444 errorCatcherClass = getattr(ErrorCatchers, errorCatcher)
1445 elif type(errorCatcher) == ClassType:
1446 errorCatcherClass = errorCatcher
1448 self._CHEETAH__errorCatcher = ec = errorCatcherClass(self)
1449 self._CHEETAH__errorCatchers[errorCatcher.__class__.__name__] = ec
1451 else:
1452 self._CHEETAH__errorCatcher = None
1453 self._CHEETAH__initErrorCatcher = self._CHEETAH__errorCatcher
1455 if not hasattr(self, 'transaction'):
1456 self.transaction = None
1457 self._CHEETAH__instanceInitialized = True
1458 self._CHEETAH__isBuffering = False
1459 self._CHEETAH__isControlledByWebKit = False
1461 self._CHEETAH__cacheStore = None
1462 if self._CHEETAH_cacheStore is not None:
1463 self._CHEETAH__cacheStore = self._CHEETAH_cacheStore
1465 def _compile(self, source=None, file=None, compilerSettings=Unspecified,
1466 moduleName=None, mainMethodName=None):
1467 """Compile the template. This method is automatically called by
1468 Template.__init__ it is provided with 'file' or 'source' args.
1470 USERS SHOULD *NEVER* CALL THIS METHOD THEMSELVES. Use Template.compile
1471 instead.
1473 if compilerSettings is Unspecified:
1474 compilerSettings = self._getCompilerSettings(source, file) or {}
1475 mainMethodName = mainMethodName or self._CHEETAH_defaultMainMethodName
1476 self._fileMtime = None
1477 self._fileDirName = None
1478 self._fileBaseName = None
1479 if file and type(file) in StringTypes:
1480 file = self.serverSidePath(file)
1481 self._fileMtime = os.path.getmtime(file)
1482 self._fileDirName, self._fileBaseName = os.path.split(file)
1483 self._filePath = file
1484 templateClass = self.compile(source, file,
1485 moduleName=moduleName,
1486 mainMethodName=mainMethodName,
1487 compilerSettings=compilerSettings,
1488 keepRefToGeneratedCode=True)
1489 self.__class__ = templateClass
1490 # must initialize it so instance attributes are accessible
1491 templateClass.__init__(self,
1492 #_globalSetVars=self._CHEETAH__globalSetVars,
1493 #_preBuiltSearchList=self._CHEETAH__searchList
1495 if not hasattr(self, 'transaction'):
1496 self.transaction = None
1498 def _handleCheetahInclude(self, srcArg, trans=None, includeFrom='file', raw=False):
1499 """Called at runtime to handle #include directives.
1501 _includeID = srcArg
1502 if not self._CHEETAH__cheetahIncludes.has_key(_includeID):
1503 if not raw:
1504 if includeFrom == 'file':
1505 source = None
1506 if type(srcArg) in StringTypes:
1507 if hasattr(self, 'serverSidePath'):
1508 file = path = self.serverSidePath(srcArg)
1509 else:
1510 file = path = os.path.normpath(srcArg)
1511 else:
1512 file = srcArg ## a file-like object
1513 else:
1514 source = srcArg
1515 file = None
1516 # @@TR: might want to provide some syntax for specifying the
1517 # Template class to be used for compilation so compilerSettings
1518 # can be changed.
1519 compiler = self._getTemplateAPIClassForIncludeDirectiveCompilation(source, file)
1520 nestedTemplateClass = compiler.compile(source=source,file=file)
1521 nestedTemplate = nestedTemplateClass(_preBuiltSearchList=self.searchList(),
1522 _globalSetVars=self._CHEETAH__globalSetVars)
1523 self._CHEETAH__cheetahIncludes[_includeID] = nestedTemplate
1524 else:
1525 if includeFrom == 'file':
1526 path = self.serverSidePath(srcArg)
1527 self._CHEETAH__cheetahIncludes[_includeID] = self.getFileContents(path)
1528 else:
1529 self._CHEETAH__cheetahIncludes[_includeID] = srcArg
1531 if not raw:
1532 self._CHEETAH__cheetahIncludes[_includeID].respond(trans)
1533 else:
1534 trans.response().write(self._CHEETAH__cheetahIncludes[_includeID])
1536 def _getTemplateAPIClassForIncludeDirectiveCompilation(self, source, file):
1537 """Returns the subclass of Template which should be used to compile
1538 #include directives.
1540 This abstraction allows different compiler settings to be used in the
1541 included template than were used in the parent.
1543 if issubclass(self.__class__, Template):
1544 return self.__class__
1545 else:
1546 return Template
1548 ## functions for using templates as CGI scripts
1549 def webInput(self, names, namesMulti=(), default='', src='f',
1550 defaultInt=0, defaultFloat=0.00, badInt=0, badFloat=0.00, debug=False):
1551 """Method for importing web transaction variables in bulk.
1553 This works for GET/POST fields both in Webware servlets and in CGI
1554 scripts, and for cookies and session variables in Webware servlets. If
1555 you try to read a cookie or session variable in a CGI script, you'll get
1556 a RuntimeError. 'In a CGI script' here means 'not running as a Webware
1557 servlet'. If the CGI environment is not properly set up, Cheetah will
1558 act like there's no input.
1560 The public method provided is:
1562 def webInput(self, names, namesMulti=(), default='', src='f',
1563 defaultInt=0, defaultFloat=0.00, badInt=0, badFloat=0.00, debug=False):
1565 This method places the specified GET/POST fields, cookies or session
1566 variables into a dictionary, which is both returned and put at the
1567 beginning of the searchList. It handles:
1569 * single vs multiple values
1570 * conversion to integer or float for specified names
1571 * default values/exceptions for missing or bad values
1572 * printing a snapshot of all values retrieved for debugging
1574 All the 'default*' and 'bad*' arguments have 'use or raise' behavior,
1575 meaning that if they're a subclass of Exception, they're raised. If
1576 they're anything else, that value is substituted for the missing/bad
1577 value.
1580 The simplest usage is:
1582 #silent $webInput(['choice'])
1583 $choice
1585 dic = self.webInput(['choice'])
1586 write(dic['choice'])
1588 Both these examples retrieves the GET/POST field 'choice' and print it.
1589 If you leave off the'#silent', all the values would be printed too. But
1590 a better way to preview the values is
1592 #silent $webInput(['name'], $debug=1)
1594 because this pretty-prints all the values inside HTML <PRE> tags.
1596 ** KLUDGE: 'debug' is supposed to insert into the template output, but it
1597 wasn't working so I changed it to a'print' statement. So the debugging
1598 output will appear wherever standard output is pointed, whether at the
1599 terminal, in a Webware log file, or whatever. ***
1601 Since we didn't specify any coversions, the value is a string. It's a
1602 'single' value because we specified it in 'names' rather than
1603 'namesMulti'. Single values work like this:
1605 * If one value is found, take it.
1606 * If several values are found, choose one arbitrarily and ignore the rest.
1607 * If no values are found, use or raise the appropriate 'default*' value.
1609 Multi values work like this:
1610 * If one value is found, put it in a list.
1611 * If several values are found, leave them in a list.
1612 * If no values are found, use the empty list ([]). The 'default*'
1613 arguments are *not* consulted in this case.
1615 Example: assume 'days' came from a set of checkboxes or a multiple combo
1616 box on a form, and the user chose'Monday', 'Tuesday' and 'Thursday'.
1618 #silent $webInput([], ['days'])
1619 The days you chose are: #slurp
1620 #for $day in $days
1621 $day #slurp
1622 #end for
1624 dic = self.webInput([], ['days'])
1625 write('The days you chose are: ')
1626 for day in dic['days']:
1627 write(day + ' ')
1629 Both these examples print: 'The days you chose are: Monday Tuesday Thursday'.
1631 By default, missing strings are replaced by '' and missing/bad numbers
1632 by zero. (A'bad number' means the converter raised an exception for
1633 it, usually because of non-numeric characters in the value.) This
1634 mimics Perl/PHP behavior, and simplifies coding for many applications
1635 where missing/bad values *should* be blank/zero. In those relatively
1636 few cases where you must distinguish between empty-string/zero on the
1637 one hand and missing/bad on the other, change the appropriate
1638 'default*' and 'bad*' arguments to something like:
1640 * None
1641 * another constant value
1642 * $NonNumericInputError/self.NonNumericInputError
1643 * $ValueError/ValueError
1645 (NonNumericInputError is defined in this class and is useful for
1646 distinguishing between bad input vs a TypeError/ValueError thrown for
1647 some other rason.)
1649 Here's an example using multiple values to schedule newspaper
1650 deliveries. 'checkboxes' comes from a form with checkboxes for all the
1651 days of the week. The days the user previously chose are preselected.
1652 The user checks/unchecks boxes as desired and presses Submit. The value
1653 of 'checkboxes' is a list of checkboxes that were checked when Submit
1654 was pressed. Our task now is to turn on the days the user checked, turn
1655 off the days he unchecked, and leave on or off the days he didn't
1656 change.
1658 dic = self.webInput([], ['dayCheckboxes'])
1659 wantedDays = dic['dayCheckboxes'] # The days the user checked.
1660 for day, on in self.getAllValues():
1661 if not on and wantedDays.has_key(day):
1662 self.TurnOn(day)
1663 # ... Set a flag or insert a database record ...
1664 elif on and not wantedDays.has_key(day):
1665 self.TurnOff(day)
1666 # ... Unset a flag or delete a database record ...
1668 'source' allows you to look up the variables from a number of different
1669 sources:
1670 'f' fields (CGI GET/POST parameters)
1671 'c' cookies
1672 's' session variables
1673 'v' 'values', meaning fields or cookies
1675 In many forms, you're dealing only with strings, which is why the
1676 'default' argument is third and the numeric arguments are banished to
1677 the end. But sometimes you want automatic number conversion, so that
1678 you can do numeric comparisions in your templates without having to
1679 write a bunch of conversion/exception handling code. Example:
1681 #silent $webInput(['name', 'height:int'])
1682 $name is $height cm tall.
1683 #if $height >= 300
1684 Wow, you're tall!
1685 #else
1686 Pshaw, you're short.
1687 #end if
1689 dic = self.webInput(['name', 'height:int'])
1690 name = dic[name]
1691 height = dic[height]
1692 write('%s is %s cm tall.' % (name, height))
1693 if height > 300:
1694 write('Wow, you're tall!')
1695 else:
1696 write('Pshaw, you're short.')
1698 To convert a value to a number, suffix ':int' or ':float' to the name.
1699 The method will search first for a 'height:int' variable and then for a
1700 'height' variable. (It will be called 'height' in the final
1701 dictionary.) If a numeric conversion fails, use or raise 'badInt' or
1702 'badFloat'. Missing values work the same way as for strings, except the
1703 default is 'defaultInt' or 'defaultFloat' instead of 'default'.
1705 If a name represents an uploaded file, the entire file will be read into
1706 memory. For more sophistocated file-upload handling, leave that name
1707 out of the list and do your own handling, or wait for
1708 Cheetah.Utils.UploadFileMixin.
1710 This only in a subclass that also inherits from Webware's Servlet or
1711 HTTPServlet. Otherwise you'll get an AttributeError on 'self.request'.
1713 EXCEPTIONS: ValueError if 'source' is not one of the stated characters.
1714 TypeError if a conversion suffix is not ':int' or ':float'.
1716 FUTURE EXPANSION: a future version of this method may allow source
1717 cascading; e.g., 'vs' would look first in 'values' and then in session
1718 variables.
1720 Meta-Data
1721 ================================================================================
1722 Author: Mike Orr <iron@mso.oz.net>
1723 License: This software is released for unlimited distribution under the
1724 terms of the MIT license. See the LICENSE file.
1725 Version: $Revision: 1.181 $
1726 Start Date: 2002/03/17
1727 Last Revision Date: $Date: 2006/06/22 20:25:16 $
1728 """
1729 src = src.lower()
1730 isCgi = not self._CHEETAH__isControlledByWebKit
1731 if isCgi and src in ('f', 'v'):
1732 global _formUsedByWebInput
1733 if _formUsedByWebInput is None:
1734 _formUsedByWebInput = cgi.FieldStorage()
1735 source, func = 'field', _formUsedByWebInput.getvalue
1736 elif isCgi and src == 'c':
1737 raise RuntimeError("can't get cookies from a CGI script")
1738 elif isCgi and src == 's':
1739 raise RuntimeError("can't get session variables from a CGI script")
1740 elif isCgi and src == 'v':
1741 source, func = 'value', self.request().value
1742 elif isCgi and src == 's':
1743 source, func = 'session', self.request().session().value
1744 elif src == 'f':
1745 source, func = 'field', self.request().field
1746 elif src == 'c':
1747 source, func = 'cookie', self.request().cookie
1748 elif src == 'v':
1749 source, func = 'value', self.request().value
1750 elif src == 's':
1751 source, func = 'session', self.request().session().value
1752 else:
1753 raise TypeError("arg 'src' invalid")
1754 sources = source + 's'
1755 converters = {
1756 '' : _Converter('string', None, default, default ),
1757 'int' : _Converter('int', int, defaultInt, badInt ),
1758 'float': _Converter('float', float, defaultFloat, badFloat), }
1759 #pprint.pprint(locals()); return {}
1760 dic = {} # Destination.
1761 for name in names:
1762 k, v = _lookup(name, func, False, converters)
1763 dic[k] = v
1764 for name in namesMulti:
1765 k, v = _lookup(name, func, True, converters)
1766 dic[k] = v
1767 # At this point, 'dic' contains all the keys/values we want to keep.
1768 # We could split the method into a superclass
1769 # method for Webware/WebwareExperimental and a subclass for Cheetah.
1770 # The superclass would merely 'return dic'. The subclass would
1771 # 'dic = super(ThisClass, self).webInput(names, namesMulti, ...)'
1772 # and then the code below.
1773 if debug:
1774 print "<PRE>\n" + pprint.pformat(dic) + "\n</PRE>\n\n"
1775 self.searchList().insert(0, dic)
1776 return dic
1778 T = Template # Short and sweet for debugging at the >>> prompt.
1781 def genParserErrorFromPythonException(source, file, generatedPyCode, exception):
1783 #print dir(exception)
1785 filename = isinstance(file, (str, unicode)) and file or None
1787 sio = StringIO.StringIO()
1788 traceback.print_exc(1, sio)
1789 formatedExc = sio.getvalue()
1791 if hasattr(exception, 'lineno'):
1792 pyLineno = exception.lineno
1793 else:
1794 pyLineno = int(re.search('[ \t]*File.*line (\d+)', formatedExc).group(1))
1796 lines = generatedPyCode.splitlines()
1798 prevLines = [] # (i, content)
1799 for i in range(1,4):
1800 if pyLineno-i <=0:
1801 break
1802 prevLines.append( (pyLineno+1-i,lines[pyLineno-i]) )
1804 nextLines = [] # (i, content)
1805 for i in range(1,4):
1806 if not pyLineno+i < len(lines):
1807 break
1808 nextLines.append( (pyLineno+i,lines[pyLineno+i]) )
1809 nextLines.reverse()
1810 report = 'Line|Python Code\n'
1811 report += '----|-------------------------------------------------------------\n'
1812 while prevLines:
1813 lineInfo = prevLines.pop()
1814 report += "%(row)-4d|%(line)s\n"% {'row':lineInfo[0], 'line':lineInfo[1]}
1816 if hasattr(exception, 'offset'):
1817 report += ' '*(3+exception.offset) + '^\n'
1819 while nextLines:
1820 lineInfo = nextLines.pop()
1821 report += "%(row)-4d|%(line)s\n"% {'row':lineInfo[0], 'line':lineInfo[1]}
1824 message = [
1825 "Error in the Python code which Cheetah generated for this template:",
1826 '='*80,
1828 str(exception),
1829 '',
1830 report,
1831 '='*80,
1833 cheetahPosMatch = re.search('line (\d+), col (\d+)', formatedExc)
1834 if cheetahPosMatch:
1835 lineno = int(cheetahPosMatch.group(1))
1836 col = int(cheetahPosMatch.group(2))
1837 #if hasattr(exception, 'offset'):
1838 # col = exception.offset
1839 message.append('\nHere is the corresponding Cheetah code:\n')
1840 else:
1841 lineno = None
1842 col = None
1843 cheetahPosMatch = re.search('line (\d+), col (\d+)',
1844 '\n'.join(lines[max(pyLineno-2, 0):]))
1845 if cheetahPosMatch:
1846 lineno = int(cheetahPosMatch.group(1))
1847 col = int(cheetahPosMatch.group(2))
1848 message.append('\nHere is the corresponding Cheetah code.')
1849 message.append('** I had to guess the line & column numbers,'
1850 ' so they are probably incorrect:\n')
1853 message = '\n'.join(message)
1854 reader = SourceReader(source, filename=filename)
1855 return ParseError(reader, message, lineno=lineno,col=col)
1858 # vim: shiftwidth=4 tabstop=4 expandtab