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
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 $
16 __author__
= "Tavis Rudd <tavis@damnsimple.com>"
17 __revision__
= "$Revision: 1.185 $"[11:-2]
19 ################################################################################
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
26 import time
# used in the cache refresh code
27 from random
import randrange
33 import cgi
# Used by .webInput() if the template is a CGI script.
35 from types
import StringType
, ClassType
37 from types
import StringTypes
39 StringTypes
= (types
.StringType
,types
.UnicodeType
)
41 from types
import BooleanType
42 boolTypeAvailable
= True
44 boolTypeAvailable
= False
47 from threading
import 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
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
80 if isinstance(v
, dict):
82 elif isinstance(v
, list):
85 return hash(tuple(hashedList
))
92 if isinstance(v
, dict):
94 elif isinstance(v
, list):
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
108 finalName
= ('cheetah_%s_%s_%s'%(baseModuleName
,
109 str(time
.time()).replace('.','_'),
110 str(randrange(10000, 99999))))
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
122 def updateLinecache(filename
, src
):
126 lines
= src
.splitlines()
128 linecache
.cache
[filename
] = size
, mtime
, lines
, fullname
130 class CompileCacheItem
:
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
147 settings
= self
._settings
148 if not source
: # @@TR: this needs improving
149 if isinstance(file, (str, unicode)): # it's a filename.
153 elif hasattr(file, 'read'):
157 templateAPIClass
= settings
.templateAPIClass
160 inspect
.getargs(templateAPIClass
.compile.im_func
.func_code
)[0]
161 if arg
not in ('klass', 'source', 'file',)]
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
)
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
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:
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).
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
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)
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
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',
267 'generatedClassCode',
268 'generatedModuleCode',
271 '_getCacheStoreIdPrefix',
272 '_createCacheRegion',
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,
330 compilerSettings
=Unspecified
,
331 compilerClass
=Unspecified
,
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
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
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.
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
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
396 - compilerClass (a class)
397 Default: Template._CHEETAH_compilerClass=Cheetah.Compiler.Compiler
399 a subclass of Cheetah.Compiler.Compiler. Mucking with this is a
402 - moduleName (a string)
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
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)
422 Template._CHEETAH_defaultMainMethodNameForTemplates
423 =None (and thus DEFAULT_COMPILER_SETTINGS['mainMethodName'])
425 What to name the main output generating method in the compiled
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
440 If the provided value is a class, Cheetah will handle all the
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
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.
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
537 b) adding #import and #extends directives dynamically based on
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,
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:
561 klass = Template.compile(src, preprocessors='@ %')
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
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
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
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
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')
631 if file and type(file) in StringTypes
:
632 moduleName
= convertTmplPathToModuleName(file)
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
665 source
, file = klass
._preprocessSource
(source
, file, preprocessors
)
667 ##################################################
668 ## compilation, using cache if requested/possible
669 baseclassValue
= None
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
682 if source
or isinstance(file, (str, unicode)):
683 compilerSettingsHash
= None
685 compilerSettingsHash
= hashDict(compilerSettings
)
687 moduleGlobalsHash
= None
689 moduleGlobalsHash
= hashDict(moduleGlobals
)
693 fileHash
= str(hash(file))+str(os
.path
.getmtime(file))
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
701 cacheHash
= ''.join([str(v
) for v
in
709 compilerSettingsHash
,
711 hash(cacheDirForModuleFiles
),
714 #@@TR: should add some logging to this
716 if useCache
and cacheHash
and cacheHash
in klass
._CHEETAH
_compileCache
:
717 cacheItem
= klass
._CHEETAH
_compileCache
[cacheHash
]
718 generatedModuleCode
= cacheItem
.code
720 compiler
= compilerClass(source
, file,
721 moduleName
=moduleName
,
722 mainClassName
=className
,
723 baseclassName
=baseclassName
,
724 mainMethodName
=mainMethodName
,
725 settings
=(compilerSettings
or {}))
727 generatedModuleCode
= compiler
.getModuleCode()
730 return generatedModuleCode
733 cacheItem
.lastCheckoutTime
= time
.time()
734 return cacheItem
.klass
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
748 open(__file__
, 'w').write(generatedModuleCode
)
749 # @@TR: should probably restrict the perms, etc.
751 # @@ TR: should this optionally raise?
752 traceback
.print_exc(file=sys
.stderr
)
754 mod
= new
.module(uniqueModuleName
)
756 for k
, v
in moduleGlobals
.items():
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
)
767 co
= compile(generatedModuleCode
, __file__
, 'exec')
768 exec co
in mod
.__dict
__
769 except SyntaxError, e
:
771 parseError
= genParserErrorFromPythonException(
772 source
, file, generatedModuleCode
, exception
=e
)
774 traceback
.print_exc()
775 updateLinecache(__file__
, generatedModuleCode
)
776 e
.generatedModuleCode
= generatedModuleCode
781 updateLinecache(__file__
, generatedModuleCode
)
782 e
.generatedModuleCode
= generatedModuleCode
785 sys
.modules
[uniqueModuleName
] = mod
787 klass
._CHEETAH
_compileLock
.release()
789 templateClass
= getattr(mod
, className
)
791 if (cacheCompilationResults
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
802 templateClass
._CHEETAH
_isInCompilationCache
= False
804 if keepRefToGeneratedCode
or cacheCompilationResults
:
805 templateClass
._CHEETAH
_generatedModuleCode
= generatedModuleCode
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
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)
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'):
849 class WrapperPreprocessor
:
850 def preprocess(self
, source
, file):
851 return arg(source
, file)
852 return WrapperPreprocessor()
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
)
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
]
879 def normalizeTokens(tokens
):
880 if isinstance(tokens
, str):
881 return tokens
.split() # space delimited string e.g.'@ %'
882 elif isinstance(tokens
, (list, tuple)):
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) ):
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
925 _normalizePreprocessorSettings
= classmethod(_normalizePreprocessorSettings
)
927 def _updateSettingsWithPreprocessTokens(
928 klass
, compilerSettings
, placeholderToken
, directiveToken
):
930 if (placeholderToken
and 'cheetahVarStartToken' not in compilerSettings
):
931 compilerSettings
['cheetahVarStartToken'] = placeholderToken
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'] = (
942 if 'multiLineCommentEndToken' not in compilerSettings
:
943 compilerSettings
['multiLineCommentEndToken'] = (
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)
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()
988 if hasattr(self
, mainMethNameAttr
):
989 return getattr(self
,mainMethNameAttr
)()
990 elif hasattr(self
, 'respond'):
991 return self
.respond()
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.
1008 filter='RawOrEncodedUnicode', # which filter from Cheetah.Filters
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)
1024 t = Template(file='some/path')
1026 t = Template(file=someFileObject)
1028 namespaces = [{'foo':'bar'}]
1029 t = Template(source=aSourceString, namespaces=namespaces)
1031 t = Template(file='some/path', namespaces=namespaces)
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)
1039 tclass = Template.compile(file='some/path')
1041 tclass = Template.compile(file=someFileObject)
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)
1051 t = tclass(namespaces=namespaces, filter='RawOrEncodedUnicode')
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) :
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
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')
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.
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.
1108 Default: Cheetah.Filters
1110 A module containing subclasses of Cheetah.Filters.Filter. See the
1111 Users' Guide for more details.
1116 This is a debugging tool. See the Users' Guide for more details.
1117 Do not use this or the #errorCatcher diretive with live
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
1132 vt
= VerifyType
.VerifyType
1133 vtc
= VerifyType
.VerifyTypeClass
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',
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'):
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:
1178 ##################################################
1179 ## Setup instance state attributes used during the life of template
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
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
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
1235 return str(id(self
))
1237 def _createCacheRegion(self
, regionID
):
1238 return self
._CHEETAH
_cacheRegionClass
(
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
1250 def getCacheRegions(self
):
1251 """Returns a dictionary of the 'cache regions' initialized in a
1254 Each #cache directive block or $*cachedPlaceholder is a separate 'cache
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():
1268 cregion
= self
._CHEETAH
__cacheRegions
.get(cacheRegionId
)
1271 if not cacheItemId
: # clear the desired region and all its cacheItems
1273 else: # clear one specific cache of a specific region
1274 cache
= cregion
.getCacheItem(cacheItemId
)
1278 ## end cache methods ##
1281 """Break reference cycles before discarding a servlet.
1284 Servlet
.shutdown(self
)
1287 self
._CHEETAH
__searchList
= None
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.
1299 return valueFromSearchList(self
.searchList(), varName
.replace('$',''), autoCall
)
1301 if default
is not Unspecified
:
1306 def varExists(self
, varName
, autoCall
=True):
1307 """Test if a variable name exists in the searchList.
1310 valueFromSearchList(self
.searchList(), varName
.replace('$',''), autoCall
)
1319 def i18n(self
, message
,
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
1344 - There is no need to replicate the i18n:name attribute from plone / PTL,
1345 as cheetah placeholders serve the same purpose
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'
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
,
1380 filter='RawOrEncodedUnicode', # which filter from Cheetah.Filters
1383 _globalSetVars
=None,
1384 _preBuiltSearchList
=None):
1385 """Sets up the instance attributes that cheetah templates use at
1388 This is automatically called by the __init__ method of compiled
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):
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
)
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
:
1427 klass
= getattr(self
._CHEETAH
__filtersLib
, filterName
)
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
= {}
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
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
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.
1494 if not self
._CHEETAH
__cheetahIncludes
.has_key(_includeID
):
1496 if includeFrom
== 'file':
1498 if type(srcArg
) in StringTypes
:
1499 if hasattr(self
, 'serverSidePath'):
1500 file = path
= self
.serverSidePath(srcArg
)
1502 file = path
= os
.path
.normpath(srcArg
)
1504 file = srcArg
## a file-like object
1508 # @@TR: might want to provide some syntax for specifying the
1509 # Template class to be used for compilation so compilerSettings
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
1517 # this is the only really safe way to use
1519 nestedTemplate
._CHEETAH
__initialFilter
= self
._CHEETAH
__initialFilter
1520 nestedTemplate
._CHEETAH
__currentFilter
= self
._CHEETAH
__initialFilter
1521 self
._CHEETAH
__cheetahIncludes
[_includeID
] = nestedTemplate
1523 if includeFrom
== 'file':
1524 path
= self
.serverSidePath(srcArg
)
1525 self
._CHEETAH
__cheetahIncludes
[_includeID
] = self
.getFileContents(path
)
1527 self
._CHEETAH
__cheetahIncludes
[_includeID
] = srcArg
1530 self
._CHEETAH
__cheetahIncludes
[_includeID
].respond(trans
)
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
__
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
1578 The simplest usage is:
1580 #silent $webInput(['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
1622 dic = self.webInput([], ['days'])
1623 write('The days you chose are: ')
1624 for day in dic['days']:
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:
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
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
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):
1661 # ... Set a flag or insert a database record ...
1662 elif on and not wantedDays.has_key(day):
1664 # ... Unset a flag or delete a database record ...
1666 'source' allows you to look up the variables from a number of different
1668 'f' fields (CGI GET/POST parameters)
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.
1684 Pshaw, you're short.
1687 dic = self.webInput(['name', 'height:int'])
1689 height = dic[height]
1690 write('%s is %s cm tall.' % (name, height))
1692 write('Wow, you're tall!')
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
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 $
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
1743 source
, func
= 'field', self
.request().field
1745 source
, func
= 'cookie', self
.request().cookie
1747 source
, func
= 'value', self
.request().value
1749 source
, func
= 'session', self
.request().session().value
1751 raise TypeError("arg 'src' invalid")
1752 sources
= source
+ 's'
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.
1760 k
, v
= _lookup(name
, func
, False, converters
)
1762 for name
in namesMulti
:
1763 k
, v
= _lookup(name
, func
, True, converters
)
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.
1772 print "<PRE>\n" + pprint
.pformat(dic
) + "\n</PRE>\n\n"
1773 self
.searchList().insert(0, 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
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):
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
):
1806 nextLines
.append( (pyLineno
+i
,lines
[pyLineno
+i
]) )
1808 report
= 'Line|Python Code\n'
1809 report
+= '----|-------------------------------------------------------------\n'
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'
1818 lineInfo
= nextLines
.pop()
1819 report
+= "%(row)-4d|%(line)s\n"% {'row':lineInfo
[0], 'line':lineInfo
[1]}
1823 "Error in the Python code which Cheetah generated for this template:",
1831 cheetahPosMatch
= re
.search('line (\d+), col (\d+)', formatedExc
)
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')
1841 cheetahPosMatch
= re
.search('line (\d+), col (\d+)',
1842 '\n'.join(lines
[max(pyLineno
-2, 0):]))
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