"Fossies" - the Fresh Open Source Software Archive

Member "cheetah3-3.2.6.post2/Cheetah/Template.py" (20 Apr 2021, 85647 Bytes) of package /linux/www/cheetah3-3.2.6.post2.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) Python source code syntax highlighting (style: standard) with prefixed line numbers. Alternatively you can here view or download the uninterpreted source code file. For more information about "Template.py" see the Fossies "Dox" file reference documentation and the last Fossies "Diffs" side-by-side code changes report: 3-3.2.4_vs_3-3.2.5.

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