root/Cheetah/Template.py

Revision f17b49bd2a9cb5c693518283252cdbca4d04136b, 79.7 kB (checked in by Jason Michalski <armooo@armooo.net>, 2 years ago)

Lets try the import again

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