root/galaxy-central/eggs/Cheetah-2.2.2-py2.6-macosx-10.6-universal-ucs2.egg/Cheetah/Template.py @ 3

リビジョン 3, 82.2 KB (コミッタ: kohda, 14 年 前)

Install Unix tools  http://hannonlab.cshl.edu/galaxy_unix_tools/galaxy.html

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