Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP

We’re showing branches in this repository, but you can also compare across forks.

...
  • 8 commits
  • 45 files changed
  • 0 commit comments
  • 2 contributors
Commits on Feb 09, 2012
Buck Evan bukzor whitespace fixes, only c5c9bf1
Commits on Feb 10, 2012
Buck Evan bukzor fix tabs b8c7795
Commits on Feb 22, 2012
Steven Moy smoy Cheetah patches:
Add 'untaint' hook.
Enable #filter to use python scope variables
    (rather than magic cheetah lookup)
Some pyflakes cleanup.

All of this needs unit testing still.
e358ac9
Steven Moy smoy optimize repeated imports 0afd07c
Steven Moy smoy fix small edge cases with the new untainting system 5b94f66
Steven Moy smoy Updated static-content generation system. 9c94391
Steven Moy smoy add name and date to my TODOs e4a5c14
Steven Moy smoy get Cheetah/Tests/Test.py passing 8496af6
Showing with 1,220 additions and 1,159 deletions.
  1. +4 −4 SetupConfig.py
  2. +6 −6 SetupTools.py
  3. +9 −9 cheetah/CacheRegion.py
  4. +8 −8 cheetah/CacheStore.py
  5. +22 −22 cheetah/CheetahWrapper.py
  6. +199 −176 cheetah/Compiler.py
  7. +2 −2 cheetah/DirectiveAnalyzer.py
  8. +3 −3 cheetah/Django.py
  9. +17 −5 cheetah/DummyTransaction.py
  10. +6 −6 cheetah/ErrorCatchers.py
  11. +41 −41 cheetah/FileUtils.py
  12. +15 −15 cheetah/Filters.py
  13. +7 −7 cheetah/ImportHooks.py
  14. +27 −27 cheetah/ImportManager.py
  15. +13 −13 cheetah/Macros/I18n.py
  16. +247 −233 cheetah/Parser.py
  17. +36 −36 cheetah/SettingsManager.py
  18. +27 −27 cheetah/SourceReader.py
  19. +199 −187 cheetah/Template.py
  20. +6 −6 cheetah/TemplateCmdLineIface.py
  21. +19 −19 cheetah/Templates/SkeletonPage.py
  22. +26 −26 cheetah/Templates/_SkeletonPage.py
  23. +6 −6 cheetah/Tests/CheetahWrapper.py
  24. +3 −3 cheetah/Tests/Filters.py
  25. +44 −44 cheetah/Tests/NameMapper.py
  26. +2 −2 cheetah/Tests/Parser.py
  27. +11 −11 cheetah/Tests/Performance.py
  28. +10 −10 cheetah/Tests/Regressions.py
  29. +119 −119 cheetah/Tests/SyntaxAndOutput.py
  30. +11 −11 cheetah/Tests/Template.py
  31. +1 −1  cheetah/Tests/Test.py
  32. +4 −4 cheetah/Tests/Unicode.py
  33. +10 −10 cheetah/Tests/xmlrunner.py
  34. +5 −5 cheetah/Tools/CGITemplate.py
  35. +17 −17 cheetah/Tools/MondoReport.py
  36. +11 −11 cheetah/Tools/SiteHierarchy.py
  37. +6 −6 cheetah/Tools/turbocheetah/cheetahsupport.py
  38. +2 −2 cheetah/Tools/turbocheetah/tests/test_template.py
  39. +1 −1  cheetah/Unspecified.py
  40. +4 −4 cheetah/Utils/Indenter.py
  41. +3 −3 cheetah/Utils/Misc.py
  42. +4 −4 cheetah/Utils/WebInputMixin.py
  43. +5 −5 cheetah/Utils/statprof.py
  44. +1 −1  cheetah/__init__.py
  45. +1 −1  setup.py
8 SetupConfig.py
View
@@ -8,7 +8,7 @@
author_email = "cheetahtemplate-discuss@lists.sf.net"
url = "http://www.cheetahtemplate.org/"
packages = ['Cheetah',
- 'Cheetah.Macros',
+ 'Cheetah.Macros',
'Cheetah.Templates',
'Cheetah.Tests',
'Cheetah.Tools',
@@ -38,11 +38,11 @@
from distutils.core import Extension
ext_modules=[
- Extension("Cheetah._namemapper",
+ Extension("Cheetah._namemapper",
[os.path.join('cheetah', 'c', '_namemapper.c')]),
- # Extension("Cheetah._verifytype",
+ # Extension("Cheetah._verifytype",
# [os.path.join('cheetah', 'c', '_verifytype.c')]),
- # Extension("Cheetah._filters",
+ # Extension("Cheetah._filters",
# [os.path.join('cheetah', 'c', '_filters.c')]),
# Extension('Cheetah._template',
# [os.path.join('cheetah', 'c', '_template.c')]),
12 SetupTools.py
View
@@ -13,7 +13,7 @@
if not os.getenv('CHEETAH_INSTALL_WITHOUT_SETUPTOOLS'):
try:
from setuptools import setup
- except ImportError:
+ except ImportError:
from distutils.core import setup
from distutils.core import Command
@@ -56,7 +56,7 @@ def build_extension(self, ext):
except ext_errors, x:
raise BuildFailed(x)
-
+
class mod_install_data(install_data):
"""A modified version of the disutils install_data command that allows data
files to be included directly in the installed Python package tree.
@@ -75,11 +75,11 @@ def run (self):
if not self.dry_run:
self.mkpath(self.install_dir)
data_files = self.get_inputs()
-
+
for entry in data_files:
if not isinstance(entry, basestring):
raise ValueError('The entries in "data_files" must be strings')
-
+
entry = string.join(string.split(entry, '/'), os.sep)
# entry is a filename or glob pattern
if entry.startswith('recursive:'):
@@ -89,7 +89,7 @@ def run (self):
filenames = findFiles(dir, globPatterns)
else:
filenames = glob.glob(entry)
-
+
for filename in filenames:
## generate the dstPath from the filename
# - deal with 'package_dir' translations
@@ -116,7 +116,7 @@ def run (self):
else:
outfile = dstPath
self.outfiles.append(outfile)
-
+
##################################################
## FUNCTIONS ##
18 cheetah/CacheRegion.py
View
@@ -35,7 +35,7 @@ class CacheItem(object):
- refreshTime (timestamp or None) : last time the cache was refreshed
- data (string) : the content of the cache
'''
-
+
def __init__(self, cacheItemID, cacheStore):
self._cacheItemID = cacheItemID
self._cacheStore = cacheStore
@@ -44,7 +44,7 @@ def __init__(self, cacheItemID, cacheStore):
def hasExpired(self):
return (self._expiryTime and time.time() > self._expiryTime)
-
+
def setExpiryTime(self, time):
self._expiryTime = time
@@ -74,14 +74,14 @@ class _CacheDataStoreWrapper(object):
def __init__(self, dataStore, keyPrefix):
self._dataStore = dataStore
self._keyPrefix = keyPrefix
-
+
def get(self, key):
return self._dataStore.get(self._keyPrefix+key)
def delete(self, key):
self._dataStore.delete(self._keyPrefix+key)
- def set(self, key, val, time=0):
+ def set(self, key, val, time=0):
self._dataStore.set(self._keyPrefix+key, val, time=time)
class CacheRegion(object):
@@ -96,7 +96,7 @@ class CacheRegion(object):
memcached API (http://www.danga.com/memcached).
'''
_cacheItemClass = CacheItem
-
+
def __init__(self, regionID, templateCacheIdPrefix='', cacheStore=None):
self._isNew = True
self._regionID = regionID
@@ -110,24 +110,24 @@ def __init__(self, regionID, templateCacheIdPrefix='', cacheStore=None):
def isNew(self):
return self._isNew
-
+
def clear(self):
" drop all the caches stored in this cache region "
for cacheItemId in self._cacheItems.keys():
cacheItem = self._cacheItems[cacheItemId]
cacheItem.clear()
del self._cacheItems[cacheItemId]
-
+
def getCacheItem(self, cacheItemID):
""" Lazy access to a cacheItem
Try to find a cache in the stored caches. If it doesn't
exist, it's created.
-
+
Returns a `CacheItem` instance.
"""
cacheItemID = md5(str(cacheItemID)).hexdigest()
-
+
if cacheItemID not in self._cacheItems:
cacheItem = self._cacheItemClass(
cacheItemID=cacheItemID, cacheStore=self._wrappedCacheDataStore)
16 cheetah/CacheStore.py
View
@@ -55,7 +55,7 @@ def replace(self, key, val, time=0):
def delete(self, key):
del self._data[key]
-
+
def get(self, key):
(val, exptime) = self._data[key]
if exptime and time.time() > exptime:
@@ -65,8 +65,8 @@ def get(self, key):
return val
def clear(self):
- self._data.clear()
-
+ self._data.clear()
+
class MemcachedCacheStore(AbstractCacheStore):
servers = ('127.0.0.1:11211')
def __init__(self, servers=None, debug=False):
@@ -79,22 +79,22 @@ def set(self, key, val, time=0):
self._client.set(key, val, time)
def add(self, key, val, time=0):
- res = self._client.add(key, val, time)
+ res = self._client.add(key, val, time)
if not res:
raise Error('a value for key %r is already in the cache'%key)
self._data[key] = (val, time)
def replace(self, key, val, time=0):
- res = self._client.replace(key, val, time)
+ res = self._client.replace(key, val, time)
if not res:
raise Error('a value for key %r is already in the cache'%key)
self._data[key] = (val, time)
def delete(self, key):
- res = self._client.delete(key, time=0)
+ res = self._client.delete(key, time=0)
if not res:
raise KeyError(key)
-
+
def get(self, key):
val = self._client.get(key)
if val is None:
@@ -103,4 +103,4 @@ def get(self, key):
return val
def clear(self):
- self._client.flush_all()
+ self._client.flush_all()
44 cheetah/CheetahWrapper.py
View
@@ -25,7 +25,7 @@
optionDashesRE = re.compile( R"^-{1,2}" )
moduleNameRE = re.compile( R"^[a-zA-Z_][a-zA-Z_0-9]*$" )
-
+
def fprintfMessage(stream, format, *args):
if format[-1:] == '^':
format = format[:-1]
@@ -66,16 +66,16 @@ def usage(usageMessage, errorMessage="", out=sys.stderr):
out.write("*** USAGE ERROR ***: %s\n" % errorMessage)
exitStatus = 1
sys.exit(exitStatus)
-
+
WRAPPER_TOP = """\
__ ____________ __
\ \/ \/ /
\/ * * \/ CHEETAH %(Version)s Command-Line Tool
- \ | /
+ \ | /
\ ==----== / by Tavis Rudd <tavis@damnsimple.com>
\__________/ and Mike Orr <sluggoster@gmail.com>
-
+
""" % globals()
@@ -102,7 +102,7 @@ class CheetahWrapper(object):
MAKE_BACKUPS = True
BACKUP_SUFFIX = ".bak"
_templateClass = None
- _compilerSettings = None
+ _compilerSettings = None
def __init__(self):
self.progName = None
@@ -143,7 +143,7 @@ def main(self, argv=None):
methInitial = methName[0]
if command in (methName, methInitial):
sys.argv[0] += (" " + methName)
- # @@MO: I don't necessarily agree sys.argv[0] should be
+ # @@MO: I don't necessarily agree sys.argv[0] should be
# modified.
meth()
return
@@ -196,7 +196,7 @@ def parseOpts(self, args):
if opts.print_settings:
- print()
+ print()
print('>> Available Cheetah compiler settings:')
from Cheetah.Compiler import _DEFAULT_COMPILER_SETTINGS
listing = _DEFAULT_COMPILER_SETTINGS
@@ -290,14 +290,14 @@ def debug(self, format, *args):
"""
if self.opts.debug:
fprintfMessage(sys.stderr, format, *args)
-
+
def warn(self, format, *args):
"""Always print a warning message to stderr.
"""
fprintfMessage(sys.stderr, format, *args)
def error(self, format, *args):
- """Always print a warning message to stderr and exit with an error code.
+ """Always print a warning message to stderr and exit with an error code.
"""
fprintfMessage(sys.stderr, format, *args)
sys.exit(1)
@@ -313,13 +313,13 @@ def _fixExts(self):
self.opts.iext = "." + iext
if oext and not oext.startswith("."):
self.opts.oext = "." + oext
-
+
def _compileOrFill(self):
C, D, W = self.chatter, self.debug, self.warn
opts, files = self.opts, self.pathArgs
- if files == ["-"]:
+ if files == ["-"]:
self._compileOrFillStdin()
return
elif not files and opts.recurse:
@@ -404,7 +404,7 @@ def _checkForCollisions(self, bundles):
if isError:
what = self.isCompile and "Compilation" or "Filling"
sys.exit("%s aborted due to collisions" % what)
-
+
def _expandSourceFilesWalk(self, arg, dir, files):
"""Recursion extension for .expandSourceFiles().
@@ -422,13 +422,13 @@ def _expandSourceFilesWalk(self, arg, dir, files):
def _expandSourceFiles(self, files, recurse, addIextIfMissing):
- """Calculate source paths from 'files' by applying the
+ """Calculate source paths from 'files' by applying the
command-line options.
"""
C, D, W = self.chatter, self.debug, self.warn
idir = self.opts.idir
iext = self.opts.iext
- files = []
+ files = []
for f in self.pathArgs:
oldFilesLen = len(files)
D("Expanding %s", f)
@@ -441,7 +441,7 @@ def _expandSourceFiles(self, files, recurse, addIextIfMissing):
raise Error("source file '%s' is a directory" % path)
elif os.path.isfile(path):
files.append(path)
- elif (addIextIfMissing and not path.endswith(iext) and
+ elif (addIextIfMissing and not path.endswith(iext) and
os.path.isfile(pathWithExt)):
files.append(pathWithExt)
# Do not recurse directories discovered by iext appending.
@@ -511,11 +511,11 @@ def _getTemplateClass(self):
self.error('The value of option --templateAPIClass is invalid\n'
'It must be in the form "module:class", '
'e.g. "Cheetah.Template:Template"')
-
+
modname, classname = modname.split(':')
C('using --templateAPIClass=%s:%s'%(modname, classname))
-
+
if p >= 0:
mod = getattr(__import__(modname[:p], {}, {}, [modname[p+1:]]), modname[p+1:])
else:
@@ -539,18 +539,18 @@ def getkws(**kws):
if self.opts.compilerSettingsString:
try:
exec('settings = getkws(%s)'%self.opts.compilerSettingsString)
- except:
+ except:
self.error("There's an error in your --settings option."
"It must be valid Python syntax.\n"
+" --settings='%s'\n"%self.opts.compilerSettingsString
- +" %s: %s"%sys.exc_info()[:2]
+ +" %s: %s"%sys.exc_info()[:2]
)
validKeys = DEFAULT_COMPILER_SETTINGS.keys()
if [k for k in settings.keys() if k not in validKeys]:
self.error(
'The --setting "%s" is not a valid compiler setting name.'%k)
-
+
self._compilerSettings = settings
return settings
else:
@@ -601,7 +601,7 @@ def _compileOrFillBundle(self, b):
#output = str(TemplateClass(file=src, searchList=self.searchList))
tclass = TemplateClass.compile(file=src, compilerSettings=compilerSettings)
output = str(tclass(searchList=self.searchList))
-
+
if bak:
shutil.copyfile(dst, bak)
if dstDir and not os.path.exists(dstDir):
@@ -615,7 +615,7 @@ def _compileOrFillBundle(self, b):
f = open(dst, 'w')
f.write(output)
f.close()
-
+
# Called when invoked as `cheetah`
def _cheetah():
375 cheetah/Compiler.py
View
@@ -1,3 +1,4 @@
+# vim: softtabstop=4 shiftwidth=4 expandtab
'''
Compiler classes for Cheetah:
ModuleCompiler aka 'Compiler'
@@ -11,25 +12,26 @@
import sys
import os
import os.path
-from os.path import getmtime, exists
+from os.path import getmtime
import re
-import types
import time
import random
import warnings
import copy
import codecs
+#TODO(buck|2011-11-22): remove all this random crap. I'm pretty certain it's unecessary.
+random.seed(0) # random, but deterministic
+
from Cheetah.Version import Version, VersionTuple
from Cheetah.SettingsManager import SettingsManager
from Cheetah.Utils.Indenter import indentize # an undocumented preprocessor
-from Cheetah import ErrorCatchers
from Cheetah import NameMapper
from Cheetah.Parser import Parser, ParseError, specialVarRE, \
- STATIC_CACHE, REFRESH_CACHE, SET_LOCAL, SET_GLOBAL, SET_MODULE, \
+ STATIC_CACHE, REFRESH_CACHE, SET_GLOBAL, SET_MODULE, \
unicodeDirectiveRE, encodingDirectiveRE, escapedNewlineRE
-from Cheetah.NameMapper import NotFound, valueForName, valueFromSearchList, valueFromFrameOrSearchList
+from Cheetah.NameMapper import valueForName, valueFromSearchList, valueFromFrameOrSearchList
VFFSL=valueFromFrameOrSearchList
VFSL=valueFromSearchList
VFN=valueForName
@@ -151,13 +153,13 @@ def genCacheInfoFromArgList(self, argList):
if key == 'timer':
key = 'interval'
val = self.genTimeInterval(val)
-
+
cacheInfo[key] = val
return cacheInfo
-
+
def genCheetahVar(self, nameChunks, plain=False):
if nameChunks[0][0] in self.setting('gettextTokens'):
- self.addGetTextVar(nameChunks)
+ self.addGetTextVar(nameChunks)
if self.setting('useNameMapper') and not plain:
return self.genNameMapperVar(nameChunks)
else:
@@ -165,7 +167,7 @@ def genCheetahVar(self, nameChunks, plain=False):
def addGetTextVar(self, nameChunks):
"""Output something that gettext can recognize.
-
+
This is a harmless side effect necessary to make gettext work when it
is scanning compiled templates for strings marked for translation.
@@ -178,7 +180,7 @@ def addGetTextVar(self, nameChunks):
self.addChunk(self.genPlainVar(nameChunks[:]))
self.dedent()
- def genPlainVar(self, nameChunks):
+ def genPlainVar(self, nameChunks):
"""Generate Python code for a Cheetah $var without using NameMapper
(Unified Dotted Notation with the SearchList).
"""
@@ -210,13 +212,13 @@ def genNameMapperVar(self, nameChunks):
------------------------------------------------------------------------
if the raw Cheetah Var is
$a.b.c[1].d().x.y.z
-
+
nameChunks is the list
[ ('a.b.c',True,'[1]'), # A
('d',False,'()'), # B
('x.y.z',True,''), # C
]
-
+
When this method is fed the list above it returns
VFN(VFN(VFFSL(SL, 'a.b.c',True)[1], 'd',False)(), 'x.y.z',True)
which can be represented as
@@ -227,7 +229,7 @@ def genNameMapperVar(self, nameChunks):
VFSL = NameMapper.valueFromSearchList # optionally used instead of VFFSL
SL = self.searchList()
useAC = self.setting('useAutocalling') # True in this example
-
+
A = ('a.b.c',True,'[1]')
B = ('d',False,'()')
C = ('x.y.z',True,'')
@@ -236,7 +238,7 @@ def genNameMapperVar(self, nameChunks):
'd',False)(),
'x.y.z',True)
= VFN(B`, name='x.y.z', executeCallables=True)
-
+
B` = VFN(A`, name=B[0], executeCallables=(useAC and B[1]))B[2]
A` = VFFSL(SL, name=A[0], executeCallables=(useAC and A[1]))A[2]
@@ -252,7 +254,7 @@ def genNameMapperVar(self, nameChunks):
nameChunks.reverse()
name, useAC, remainder = nameChunks.pop()
-
+
if not useSearchList:
firstDotIdx = name.find('.')
if firstDotIdx != -1 and firstDotIdx < len(name):
@@ -273,7 +275,7 @@ def genNameMapperVar(self, nameChunks):
'"'+ name + '",'
+ repr(defaultUseAC and useAC) + ')'
+ remainder)
- ##
+ ##
while nameChunks:
name, useAC, remainder = nameChunks.pop()
pythonCode = ('VFN(' + pythonCode +
@@ -281,7 +283,7 @@ def genNameMapperVar(self, nameChunks):
'",' + repr(defaultUseAC and useAC) + ')'
+ remainder)
return pythonCode
-
+
##################################################
## METHOD COMPILERS
@@ -318,8 +320,8 @@ def _setupState(self):
self._hasReturnStatement = False
self._isGenerator = False
-
-
+
+
def cleanupState(self):
"""Called by the containing class compiler instance
"""
@@ -330,15 +332,15 @@ def methodName(self):
def setMethodName(self, name):
self._methodName = name
-
+
## methods for managing indentation
-
+
def indentation(self):
return self._indent * self._indentLev
-
+
def indent(self):
self._indentLev +=1
-
+
def dedent(self):
if self._indentLev:
self._indentLev -=1
@@ -355,7 +357,7 @@ def methodDef(self):
__str__ = methodDef
__unicode__ = methodDef
-
+
def wrapCode(self):
self.commitStrConst()
methodDefChunks = (
@@ -379,8 +381,8 @@ def methodBody(self):
def docString(self):
if not self._docStringLines:
return ''
-
- ind = self._indent*2
+
+ ind = self._indent*2
docStr = (ind + '"""\n' + ind +
('\n' + ind).join([ln.replace('"""', "'''") for ln in self._docStringLines]) +
'\n' + ind + '"""\n')
@@ -389,7 +391,7 @@ def docString(self):
## methods for adding code
def addMethDocString(self, line):
self._docStringLines.append(line.replace('%', '%%'))
-
+
def addChunk(self, chunk):
self.commitStrConst()
chunk = "\n" + self.indentation() + chunk
@@ -414,7 +416,7 @@ def addFilteredChunk(self, chunk, filterArgs=None, rawExpr=None, lineCol=None):
self.appendToPrevChunk(' on line %s, col %s'%lineCol)
else:
self.addChunk("_v = %s"%chunk)
-
+
if self.setting('useFilters'):
self.addChunk("if _v is not None: write(_filter(_v%s))"%filterArgs)
else:
@@ -450,7 +452,7 @@ def commitStrConst(self):
i = 1
out = ['u']
body = escapedNewlineRE.sub('\\1\n', reprstr[i+1:-1])
-
+
if reprstr[i]=="'":
out.append("'''")
out.append(body)
@@ -459,7 +461,11 @@ def commitStrConst(self):
out.append('"""')
out.append(body)
out.append('"""')
- self.addWriteChunk(''.join(out))
+
+ if self.setting('useFilters'):
+ self.addWriteChunk('_untaint(' + ''.join(out) + ')')
+ else:
+ self.addWriteChunk(''.join(out))
def handleWSBeforeDirective(self):
"""Truncate the pending strCont to the beginning of the current line.
@@ -474,20 +480,20 @@ def handleWSBeforeDirective(self):
def isErrorCatcherOn(self):
return self._isErrorCatcherOn
-
+
def turnErrorCatcherOn(self):
self._isErrorCatcherOn = True
def turnErrorCatcherOff(self):
self._isErrorCatcherOn = False
-
+
# @@TR: consider merging the next two methods into one
def addStrConst(self, strConst):
self._appendToPrevStrConst(strConst)
def addRawText(self, text):
self.addStrConst(text)
-
+
def addMethComment(self, comm):
offSet = self.setting('commentOffset')
self.addChunk('#' + ' '*offSet + comm)
@@ -503,14 +509,14 @@ def addPlaceholder(self, expr, filterArgs, rawPlaceholder,
if self.isErrorCatcherOn():
methodName = self._classCompiler.addErrorCatcherCall(
expr, rawCode=rawPlaceholder, lineCol=lineCol)
- expr = 'self.' + methodName + '(localsDict=locals())'
+ expr = 'self.' + methodName + '(localsDict=locals())'
if silentMode:
self.addChunk('try:')
- self.indent()
+ self.indent()
self.addFilteredChunk(expr, filterArgs, rawPlaceholder, lineCol=lineCol)
self.dedent()
- self.addChunk('except NotFound: pass')
+ self.addChunk('except NotFound: pass')
else:
self.addFilteredChunk(expr, filterArgs, rawPlaceholder, lineCol=lineCol)
@@ -524,7 +530,7 @@ def addSilent(self, expr):
def addEcho(self, expr, rawExpr=None):
self.addFilteredChunk(expr, rawExpr=rawExpr)
-
+
def addSet(self, expr, exprComponents, setStyle):
if setStyle is SET_GLOBAL:
(LVALUE, OP, RVALUE) = (exprComponents.LVALUE,
@@ -545,7 +551,7 @@ def addSet(self, expr, exprComponents, setStyle):
secondary = LVALUE[splitPos:]
else:
primary = LVALUE
- secondary = ''
+ secondary = ''
LVALUE = 'self._CHEETAH__globalSetVars["' + primary + '"]' + secondary
expr = LVALUE + ' ' + OP + ' ' + RVALUE.strip()
@@ -562,12 +568,12 @@ def addInclude(self, sourceExpr, includeFrom, isRaw):
def addWhile(self, expr, lineCol=None):
self.addIndentingDirective(expr, lineCol=lineCol)
-
+
def addFor(self, expr, lineCol=None):
self.addIndentingDirective(expr, lineCol=lineCol)
def addRepeat(self, expr, lineCol=None):
- #the _repeatCount stuff here allows nesting of #repeat directives
+ #the _repeatCount stuff here allows nesting of #repeat directives
self._repeatCount = getattr(self, "_repeatCount", -1) + 1
self.addFor('for __i%s in range(%s)' % (self._repeatCount, expr), lineCol=lineCol)
@@ -585,7 +591,7 @@ def addReIndentingDirective(self, expr, dedent=True, lineCol=None):
self.dedent()
if not expr[-1] == ':':
expr = expr + ':'
-
+
self.addChunk( expr )
if lineCol:
self.appendToPrevChunk(' # generated from line %s, col %s'%lineCol )
@@ -605,10 +611,10 @@ def addTernaryExpr(self, conditionExpr, trueExpr, falseExpr, lineCol=None):
"""For a single-lie #if ... then .... else ... directive
<condition> then <trueExpr> else <falseExpr>
"""
- self.addIndentingDirective(conditionExpr, lineCol=lineCol)
+ self.addIndentingDirective(conditionExpr, lineCol=lineCol)
self.addFilteredChunk(trueExpr)
self.dedent()
- self.addIndentingDirective('else')
+ self.addIndentingDirective('else')
self.addFilteredChunk(falseExpr)
self.dedent()
@@ -618,7 +624,7 @@ def addElse(self, expr, dedent=True, lineCol=None):
def addElif(self, expr, dedent=True, lineCol=None):
self.addElse(expr, dedent=dedent, lineCol=lineCol)
-
+
def addUnless(self, expr, lineCol=None):
self.addIf('if not (' + expr + ')')
@@ -635,13 +641,13 @@ def addClosure(self, functionName, argsList, parserComment):
def addTry(self, expr, lineCol=None):
self.addIndentingDirective(expr, lineCol=lineCol)
-
+
def addExcept(self, expr, dedent=True, lineCol=None):
self.addReIndentingDirective(expr, dedent=dedent, lineCol=lineCol)
-
+
def addFinally(self, expr, dedent=True, lineCol=None):
self.addReIndentingDirective(expr, dedent=dedent, lineCol=lineCol)
-
+
def addReturn(self, expr):
assert not self._isGenerator
self.addChunk(expr)
@@ -664,7 +670,7 @@ def addYield(self, expr):
self.addChunk(
'raise TypeError("This method cannot be called with a trans arg")')
self.dedent()
-
+
def addPass(self, expr):
self.addChunk(expr)
@@ -692,7 +698,7 @@ def addPSP(self, PSP):
if PSP:
self.addWriteChunk('_filter(' + PSP + ')')
return
-
+
elif PSP.lower() == 'end':
self.dedent()
return
@@ -701,21 +707,21 @@ def addPSP(self, PSP):
PSP = PSP[:-1]
elif PSP[-1] == ':':
autoIndent = True
-
+
for line in PSP.splitlines():
self.addChunk(line)
-
+
if autoIndent:
self.indent()
-
+
def nextCacheID(self):
- return ('_'+str(random.randrange(100, 999))
+ return ('_'+str(random.randrange(100, 999))
+ str(random.randrange(10000, 99999)))
-
+
def startCacheRegion(self, cacheInfo, lineCol, rawPlaceholder=None):
# @@TR: we should add some runtime logging to this
-
+
ID = self.nextCacheID()
interval = cacheInfo.get('interval', None)
test = cacheInfo.get('test', None)
@@ -730,7 +736,7 @@ def startCacheRegion(self, cacheInfo, lineCol, rawPlaceholder=None):
self.addChunk('## START CACHE REGION: ID='+ID+
'. line %s, col %s'%lineCol + ' in the source.')
-
+
self.addChunk('_RECACHE_%(ID)s = False'%locals())
self.addChunk('_cacheRegion_%(ID)s = self.getCacheRegion(regionID='%locals()
+ repr(ID)
@@ -740,7 +746,7 @@ def startCacheRegion(self, cacheInfo, lineCol, rawPlaceholder=None):
self.indent()
self.addChunk('_RECACHE_%(ID)s = True'%locals())
self.dedent()
-
+
self.addChunk('_cacheItem_%(ID)s = _cacheRegion_%(ID)s.getCacheItem('%locals()
+varyBy+')')
@@ -748,7 +754,7 @@ def startCacheRegion(self, cacheInfo, lineCol, rawPlaceholder=None):
self.indent()
self.addChunk('_RECACHE_%(ID)s = True'%locals())
self.dedent()
-
+
if test:
self.addChunk('if ' + test + ':')
self.indent()
@@ -760,20 +766,20 @@ def startCacheRegion(self, cacheInfo, lineCol, rawPlaceholder=None):
#self.addChunk('print "DEBUG"+"-"*50')
self.addChunk('try:')
self.indent()
- self.addChunk('_output = _cacheItem_%(ID)s.renderOutput()'%locals())
- self.dedent()
+ self.addChunk('_output = _cacheItem_%(ID)s.renderOutput()'%locals())
+ self.dedent()
self.addChunk('except KeyError:')
self.indent()
self.addChunk('_RECACHE_%(ID)s = True'%locals())
#self.addChunk('print "DEBUG"+"*"*50')
- self.dedent()
+ self.dedent()
self.addChunk('else:')
self.indent()
self.addWriteChunk('_output')
self.addChunk('del _output')
- self.dedent()
+ self.dedent()
- self.dedent()
+ self.dedent()
self.addChunk('if _RECACHE_%(ID)s or not _cacheItem_%(ID)s.getRefreshTime():'%locals())
self.indent()
@@ -783,7 +789,7 @@ def startCacheRegion(self, cacheInfo, lineCol, rawPlaceholder=None):
if interval:
self.addChunk(("_cacheItem_%(ID)s.setExpiryTime(currentTime() +"%locals())
+ str(interval) + ")")
-
+
def endCacheRegion(self):
ID = self._cacheRegionsStack.pop()
self.addChunk('trans = _orig_trans%(ID)s'%locals())
@@ -791,7 +797,7 @@ def endCacheRegion(self):
self.addChunk('_cacheData = _cacheCollector_%(ID)s.response().getvalue()'%locals())
self.addChunk('_cacheItem_%(ID)s.setData(_cacheData)'%locals())
self.addWriteChunk('_cacheData')
- self.addChunk('del _cacheData')
+ self.addChunk('del _cacheData')
self.addChunk('del _cacheCollector_%(ID)s'%locals())
self.addChunk('del _orig_trans%(ID)s'%locals())
self.dedent()
@@ -832,7 +838,7 @@ def setCallArg(self, argName, lineCol):
self.addChunk('_callKws%(ID)s = {}'%locals())
self.addChunk('_currentCallArgname%(ID)s = %(argName)r'%locals())
callDetails.currentArgname = argName
-
+
def _endCallArg(self):
ID, callDetails = self._callRegionsStack[-1]
currCallArg = callDetails.currentArgname
@@ -841,7 +847,7 @@ def _endCallArg(self):
self.addChunk('del _callCollector%(ID)s'%locals())
self.addChunk('trans = _callCollector%(ID)s = DummyTransaction()'%locals())
self.addChunk('write = _callCollector%(ID)s.response().write'%locals())
-
+
def endCallRegion(self, regionTitle='CALL'):
ID, callDetails = self._callRegionsStack[-1]
functionName, initialKwArgs, lineCol = (
@@ -859,7 +865,7 @@ def reset(ID=ID):
self.addChunk('_callArgVal%(ID)s = _callCollector%(ID)s.response().getvalue()'%locals())
self.addChunk('del _callCollector%(ID)s'%locals())
if initialKwArgs:
- initialKwArgs = ', '+initialKwArgs
+ initialKwArgs = ', '+initialKwArgs
self.addFilteredChunk('%(functionName)s(_callArgVal%(ID)s%(initialKwArgs)s)'%locals())
self.addChunk('del _callArgVal%(ID)s'%locals())
else:
@@ -872,7 +878,7 @@ def reset(ID=ID):
self.addChunk('## END %(regionTitle)s REGION: '%locals()
+ID
+' of '+functionName
- +' at line %s, col %s'%lineCol + ' in the source.')
+ +' at line %s, col %s'%lineCol + ' in the source.')
self.addChunk('')
self._callRegionsStack.pop() # attrib of current methodCompiler
@@ -885,7 +891,7 @@ class CaptureDetails: pass
captureDetails.ID = ID = self.nextCaptureRegionID()
captureDetails.assignTo = assignTo
captureDetails.lineCol = lineCol
-
+
self._captureRegionsStack.append((ID, captureDetails)) # attrib of current methodCompiler
self.addChunk('## START CAPTURE REGION: '+ID
+' '+assignTo
@@ -906,9 +912,9 @@ def endCaptureRegion(self):
self.addChunk('del _orig_trans%(ID)s'%locals())
self.addChunk('del _captureCollector%(ID)s'%locals())
self.addChunk('del _wasBuffering%(ID)s'%locals())
-
+
def setErrorCatcher(self, errorCatcherName):
- self.turnErrorCatcherOn()
+ self.turnErrorCatcherOn()
self.addChunk('if self._CHEETAH__errorCatchers.has_key("' + errorCatcherName + '"):')
self.indent()
@@ -931,14 +937,15 @@ def setTransform(self, transformer, isKlass):
self.addChunk('trans._response = trans.response()')
self.addChunk('trans._response._filter = %s' % transformer)
self.addChunk('write = trans._response.write')
-
- def setFilter(self, theFilter, isKlass):
- class FilterDetails:
+
+ def setFilter(self, theFilter, untaint, isKlass):
+ class FilterDetails:
pass
filterDetails = FilterDetails()
filterDetails.ID = ID = self.nextFilterRegionID()
filterDetails.theFilter = theFilter
filterDetails.isKlass = isKlass
+ filterDetails.untaint = untaint
self._filterRegionsStack.append((ID, filterDetails)) # attrib of current methodCompiler
self.addChunk('_orig_filter%(ID)s = _filter'%locals())
@@ -955,18 +962,31 @@ class FilterDetails:
self.indent()
self.addChunk('_filter = self._CHEETAH__currentFilter = self._CHEETAH__filters[filterName]')
self.dedent()
+ self.addChunk('elif hasattr(self._CHEETAH__filtersLib, filterName):')
+ self.indent()
+ self.addChunk('_filter = self._CHEETAH__currentFilter = \\')
+ self.addChunk(' self._CHEETAH__filters[filterName] = \\')
+ self.addChunk(' getattr(self._CHEETAH__filtersLib, filterName)(self).filter')
+ self.dedent()
self.addChunk('else:')
self.indent()
- self.addChunk('_filter = self._CHEETAH__currentFilter'
- +' = \\\n\t\t\tself._CHEETAH__filters[filterName] = '
- + 'getattr(self._CHEETAH__filtersLib, filterName)(self).filter')
+ self.addChunk('_filter = self._CHEETAH__currentFilter = \\')
+ self.addChunk(' self._CHEETAH__filters[filterName] = \\')
+ self.addChunk(' ' + theFilter)
self.dedent()
-
+
+ if untaint is not None:
+ self.addChunk('_orig_untaint = _untaint')
+ self.addChunk('_untaint = self._CHEETAH__untaint = ' + untaint)
+
+
def closeFilterBlock(self):
ID, filterDetails = self._filterRegionsStack.pop()
#self.addChunk('_filter = self._CHEETAH__initialFilter')
#self.addChunk('_filter = _orig_filter%(ID)s'%locals())
self.addChunk('_filter = self._CHEETAH__currentFilter = _orig_filter%(ID)s'%locals())
+ if filterDetails.untaint is not None:
+ self.addChunk('_untaint = self._CHEETAH__untaint = _orig_untaint')
class AutoMethodCompiler(MethodCompiler):
@@ -993,7 +1013,7 @@ def isStaticMethod(self):
if self._isStaticMethod is None:
self._isStaticMethod = '@staticmethod' in self._decorators
return self._isStaticMethod
-
+
def cleanupState(self):
MethodCompiler.cleanupState(self)
self.commitStrConst()
@@ -1001,7 +1021,7 @@ def cleanupState(self):
self.endCacheRegion()
if self._callRegionsStack:
self.endCallRegion()
-
+
if self._streamingEnabled:
kwargsName = None
positionalArgsListName = None
@@ -1011,7 +1031,7 @@ def cleanupState(self):
break
elif argname.strip().startswith('*'):
positionalArgsListName = argname.strip().replace('*', '')
-
+
if not kwargsName and self._useKWsDictArgForPassingTrans():
kwargsName = 'KWS'
self.addMethArg('**KWS', None)
@@ -1019,24 +1039,24 @@ def cleanupState(self):
if not self._useKWsDictArgForPassingTrans():
if not kwargsName and not positionalArgsListName:
- self.addMethArg('trans', 'None')
+ self.addMethArg('trans', 'None')
else:
self._streamingEnabled = False
-
+
self._indentLev = self.setting('initialMethIndentLevel')
mainBodyChunks = self._methodBodyChunks
self._methodBodyChunks = []
self._addAutoSetupCode()
self._methodBodyChunks.extend(mainBodyChunks)
self._addAutoCleanupCode()
-
+
def _addAutoSetupCode(self):
if self._initialMethodComment:
self.addChunk(self._initialMethodComment)
-
+
if self._streamingEnabled and not self.isClassMethod() and not self.isStaticMethod():
if self._useKWsDictArgForPassingTrans() and self._kwargsName:
- self.addChunk('trans = %s.get("trans")'%self._kwargsName)
+ self.addChunk('trans = %s.get("trans")'%self._kwargsName)
self.addChunk('if (not trans and not self._CHEETAH__isBuffering'
' and not callable(self.transaction)):')
self.indent()
@@ -1047,7 +1067,7 @@ def _addAutoSetupCode(self):
self.indent()
self.addChunk('trans = DummyTransaction()')
if self.setting('autoAssignDummyTransactionToSelf'):
- self.addChunk('self.transaction = trans')
+ self.addChunk('self.transaction = trans')
self.addChunk('_dummyTrans = True')
self.dedent()
self.addChunk('else: _dummyTrans = False')
@@ -1057,20 +1077,22 @@ def _addAutoSetupCode(self):
self.addChunk('write = trans.response().write')
if self.setting('useNameMapper'):
argNames = [arg[0] for arg in self._argStringList]
- allowSearchListAsMethArg = self.setting('allowSearchListAsMethArg')
+ allowSearchListAsMethArg = self.setting('allowSearchListAsMethArg')
if allowSearchListAsMethArg and 'SL' in argNames:
pass
elif allowSearchListAsMethArg and 'searchList' in argNames:
self.addChunk('SL = searchList')
elif not self.isClassMethod() and not self.isStaticMethod():
- self.addChunk('SL = self._CHEETAH__searchList')
+ self.addChunk('SL = self._CHEETAH__searchList')
else:
self.addChunk('SL = [KWS]')
if self.setting('useFilters'):
if self.isClassMethod() or self.isStaticMethod():
self.addChunk('_filter = lambda x, **kwargs: unicode(x)')
+ self.addChunk('_untaint = lambda x: x')
else:
self.addChunk('_filter = self._CHEETAH__currentFilter')
+ self.addChunk('_untaint = self._CHEETAH__untaint')
self.addChunk('')
self.addChunk("#" *40)
self.addChunk('## START - generated method body')
@@ -1085,13 +1107,16 @@ def _addAutoCleanupCode(self):
if not self._isGenerator:
self.addStop()
self.addChunk('')
-
+
def addStop(self, expr=None):
- self.addChunk('return _dummyTrans and trans.response().getvalue() or ""')
+ if self.setting('useFilters'):
+ self.addChunk('return _dummyTrans and trans.response().getvalue() or _untaint(u"")')
+ else:
+ self.addChunk('return _dummyTrans and trans.response().getvalue() or u""')
def addMethArg(self, name, defVal=None):
self._argStringList.append( (name, defVal) )
-
+
def methodSignature(self):
argStringChunks = []
for arg in self._argStringList:
@@ -1131,7 +1156,7 @@ def methodSignature(self):
class ClassCompiler(GenUtils):
methodCompilerClass = AutoMethodCompiler
methodCompilerClassForInit = MethodCompiler
-
+
def __init__(self, className, mainMethodName='respond',
moduleCompiler=None,
fileName=None,
@@ -1158,11 +1183,11 @@ def __getattr__(self, name):
"""Provide access to the methods and attributes of the MethodCompiler
at the top of the activeMethods stack: one-way namespace sharing
-
+
WARNING: Use .setMethods to assign the attributes of the MethodCompiler
from the methods of this class!!! or you will be assigning to attributes
of this object instead."""
-
+
if name in self.__dict__:
return self.__dict__[name]
elif hasattr(self.__class__, name):
@@ -1196,7 +1221,7 @@ def _setupState(self):
if self.setting('templateMetaclass'):
self._generatedAttribs.append('__metaclass__ = '+self.setting('templateMetaclass'))
- self._initMethChunks = []
+ self._initMethChunks = []
self._blockMetaData = {}
self._errorCatcherCount = 0
self._placeholderToErrorCatcherMap = {}
@@ -1224,7 +1249,7 @@ def _setupInitMethod(self):
self._swallowMethodCompiler(__init__, pos=0)
def _addSourceFileMonitoring(self, fileName):
- # @@TR: this stuff needs auditing for Cheetah 2.0
+ # @@TR: this stuff needs auditing for Cheetah 2.0
# the first bit is added to init
self.addChunkToInit('self._filePath = ' + repr(fileName))
self.addChunkToInit('self._fileMtime = ' + str(getmtime(fileName)) )
@@ -1236,10 +1261,10 @@ def _addSourceFileMonitoring(self, fileName):
self.addChunk('self._compile(file=self._filePath, moduleName='+self._className + ')')
self.addChunk(
'write(getattr(self, self._mainCheetahMethod_for_' + self._className +
- ')(trans=trans))')
+ ')(trans=trans))')
self.addStop()
self.dedent()
-
+
def setClassName(self, name):
self._className = name
@@ -1248,7 +1273,7 @@ def className(self):
def setBaseClass(self, baseClassName):
self._baseClass = baseClassName
-
+
def setMainMethodName(self, methodName):
if methodName == self._mainMethodName:
return
@@ -1264,7 +1289,7 @@ def setMainMethodName(self, methodName):
for i in range(len(chunks)):
if chunks[i] == chunkToChange:
chunks[i] = ('write(self.' + methodName + '(trans=trans))')
- ## get rid of the old reference and update self._mainMethodName
+ ## get rid of the old reference and update self._mainMethodName
del self._methodsIndex[self._mainMethodName]
self._mainMethodName = methodName
@@ -1272,9 +1297,9 @@ def setMainMethodArgs(self, argsList):
mainMethodCompiler = self._methodsIndex[self._mainMethodName]
for argName, defVal in argsList:
mainMethodCompiler.addMethArg(argName, defVal)
-
-
- def _spawnMethodCompiler(self, methodName, klass=None,
+
+
+ def _spawnMethodCompiler(self, methodName, klass=None,
initialMethodComment=None):
if klass is None:
klass = self.methodCompilerClass
@@ -1307,10 +1332,10 @@ def _swallowMethodCompiler(self, methodCompiler, pos=None):
def startMethodDef(self, methodName, argsList, parserComment):
methodCompiler = self._spawnMethodCompiler(
methodName, initialMethodComment=parserComment)
- self._setActiveMethodCompiler(methodCompiler)
+ self._setActiveMethodCompiler(methodCompiler)
for argName, defVal in argsList:
methodCompiler.addMethArg(argName, defVal)
-
+
def _finishedMethods(self):
return self._finishedMethodsList
@@ -1323,14 +1348,14 @@ def addDecorator(self, decoratorExpr):
self._decoratorsForNextMethod.append(decoratorExpr)
def addClassDocString(self, line):
- self._classDocStringLines.append( line.replace('%', '%%'))
+ self._classDocStringLines.append( line.replace('%', '%%'))
def addChunkToInit(self, chunk):
self._initMethChunks.append(chunk)
def addAttribute(self, attribExpr):
## first test to make sure that the user hasn't used any fancy Cheetah syntax
- # (placeholders, directives, etc.) inside the expression
+ # (placeholders, directives, etc.) inside the expression
if attribExpr.find('VFN(') != -1 or attribExpr.find('VFFSL(') != -1:
raise ParseError(self,
'Invalid #attr directive.' +
@@ -1338,7 +1363,7 @@ def addAttribute(self, attribExpr):
## now add the attribute
self._generatedAttribs.append(attribExpr)
- def addSuper(self, argsList, parserComment=None):
+ def addSuper(self, argsList, parserComment=None):
className = self._className #self._baseClass
methodName = self._getActiveMethodCompiler().methodName()
@@ -1364,13 +1389,13 @@ def addErrorCatcherCall(self, codeChunk, rawCode='', lineCol=''):
self._errorCatcherCount += 1
methodName = '__errorCatcher' + str(self._errorCatcherCount)
self._placeholderToErrorCatcherMap[rawCode] = methodName
-
+
catcherMeth = self._spawnMethodCompiler(
methodName,
klass=MethodCompiler,
initialMethodComment=('## CHEETAH: Generated from ' + rawCode +
' at line %s, col %s'%lineCol + '.')
- )
+ )
catcherMeth.setMethodSignature('def ' + methodName +
'(self, localsDict={})')
# is this use of localsDict right?
@@ -1380,13 +1405,13 @@ def addErrorCatcherCall(self, codeChunk, rawCode='', lineCol=''):
"''', globals(), localsDict)")
catcherMeth.dedent()
catcherMeth.addChunk('except self._CHEETAH__errorCatcher.exceptions(), e:')
- catcherMeth.indent()
+ catcherMeth.indent()
catcherMeth.addChunk("return self._CHEETAH__errorCatcher.warn(exc_val=e, code= " +
repr(codeChunk) + " , rawCode= " +
repr(rawCode) + " , lineCol=" + str(lineCol) +")")
-
+
catcherMeth.cleanupState()
-
+
self._swallowMethodCompiler(catcherMeth)
return methodName
@@ -1403,22 +1428,22 @@ def closeBlock(self):
endMarker = self.setting('blockMarkerEnd')
methCompiler.addStrConst(endMarker[0] + methodName + endMarker[1])
self._swallowMethodCompiler(methCompiler)
-
- #metaData = self._blockMetaData[methodName]
+
+ #metaData = self._blockMetaData[methodName]
#rawDirective = metaData['raw']
#lineCol = metaData['lineCol']
-
+
## insert the code to call the block, caching if #cache directive is on
codeChunk = 'self.' + methodName + '(trans=trans)'
self.addChunk(codeChunk)
-
+
#self.appendToPrevChunk(' # generated from ' + repr(rawDirective) )
#if self.setting('outputRowColComments'):
# self.appendToPrevChunk(' at line %s, col %s' % lineCol + '.')
## code wrapping methods
-
+
def classDef(self):
if self._classDef:
return self._classDef
@@ -1427,7 +1452,7 @@ def classDef(self):
__str__ = classDef
__unicode__ = classDef
-
+
def wrapClassDef(self):
ind = self.setting('indentationStep')
classDefChunks = [self.classSignature(),
@@ -1446,14 +1471,14 @@ def addAttributes():
ind + '## CHEETAH GENERATED ATTRIBUTES',
'\n',
self.attributes(),
- ])
+ ])
if self.setting('outputMethodsBeforeAttributes'):
addMethods()
addAttributes()
else:
addAttributes()
addMethods()
-
+
classDef = '\n'.join(classDefChunks)
self._classDef = classDef
return classDef
@@ -1461,7 +1486,7 @@ def addAttributes():
def classSignature(self):
return "class %s(%s):" % (self.className(), self._baseClass)
-
+
def classDocstring(self):
if not self._classDocStringLines:
return ''
@@ -1484,7 +1509,7 @@ def attributes(self):
attribs = [self.setting('indentationStep') + unicode(attrib)
for attrib in self._generatedAttribs ]
return '\n\n'.join(attribs)
-
+
class AutoClassCompiler(ClassCompiler):
pass
@@ -1495,9 +1520,9 @@ class ModuleCompiler(SettingsManager, GenUtils):
parserClass = Parser
classCompilerClass = AutoClassCompiler
-
+
def __init__(self, source=None, file=None,
- moduleName='DynamicallyCompiledCheetahTemplate',
+ moduleName='DynamicallyCompiledCheetahTemplate',
mainClassName=None, # string
mainMethodName=None, # string
baseclassName=None, # string
@@ -1518,7 +1543,7 @@ def __init__(self, source=None, file=None,
"painfully slow with the Python version of NameMapper. "
"You should get a copy of Cheetah with the compiled C version of NameMapper."
)
- self.setSetting('useStackFrames', False)
+ self.setSetting('useStackFrames', False)
self._compiled = False
self._moduleName = moduleName
@@ -1530,10 +1555,10 @@ def __init__(self, source=None, file=None,
if mainMethodName:
self.setSetting('mainMethodName', mainMethodName)
self._baseclassName = baseclassName
-
+
self._filePath = None
self._fileMtime = None
-
+
if source and file:
raise TypeError("Cannot compile from a source string AND file.")
elif isinstance(file, basestring): # it's a filename.
@@ -1552,7 +1577,7 @@ def __init__(self, source=None, file=None,
source = file.read() # Can't set filename or mtime--they're not accessible.
elif file:
raise TypeError("'file' argument must be a filename string or file-like object")
-
+
if self._filePath:
self._fileDirName, self._fileBaseName = os.path.split(self._filePath)
self._fileBaseNameRoot, self._fileBaseNameExt = os.path.splitext(self._fileBaseName)
@@ -1566,7 +1591,7 @@ def __init__(self, source=None, file=None,
# (Over the long term we'll make it a real directive.)
if source == "":
warnings.warn("You supplied an empty string for the source!", )
-
+
else:
unicodeMatch = unicodeDirectiveRE.search(source)
encodingMatch = encodingDirectiveRE.search(source)
@@ -1592,7 +1617,7 @@ def __init__(self, source=None, file=None,
self._parser = self.parserClass(source, filename=self._filePath, compiler=self)
self._setupCompilerState()
-
+
def __getattr__(self, name):
"""Provide one-way access to the methods and attributes of the
ClassCompiler, and thereby the MethodCompilers as well.
@@ -1612,10 +1637,10 @@ def __getattr__(self, name):
def _initializeSettings(self):
self.updateSettings(copy.deepcopy(DEFAULT_COMPILER_SETTINGS))
-
+
def _setupCompilerState(self):
self._activeClassesList = []
- self._finishedClassesList = [] # listed by ordered
+ self._finishedClassesList = [] # listed by ordered
self._finishedClassIndex = {} # listed by name
self._moduleDef = None
self._moduleShBang = '#!/usr/bin/env python'
@@ -1635,7 +1660,7 @@ def _setupCompilerState(self):
"from os.path import getmtime, exists",
"import time",
"import types",
- "from Cheetah.Version import MinCompatibleVersion as RequiredCheetahVersion",
+ "from Cheetah.Version import MinCompatibleVersion as RequiredCheetahVersion",
"from Cheetah.Version import MinCompatibleVersionTuple as RequiredCheetahVersionTuple",
"from Cheetah.Template import Template",
"from Cheetah.DummyTransaction import *",
@@ -1643,9 +1668,9 @@ def _setupCompilerState(self):
"from Cheetah.CacheRegion import CacheRegion",
"import Cheetah.Filters as Filters",
"import Cheetah.ErrorCatchers as ErrorCatchers",
- ]
+ ]
- self._importedVarNames = ['sys',
+ self._importedVarNames = set(['sys',
'os',
'os.path',
'time',
@@ -1656,17 +1681,17 @@ def _setupCompilerState(self):
'Filters',
'ErrorCatchers',
'CacheRegion',
- ]
-
+ ])
+
self._moduleConstants = [
"VFFSL=valueFromFrameOrSearchList",
"VFSL=valueFromSearchList",
"VFN=valueForName",
"currentTime=time.time",
]
-
+
def compile(self):
- classCompiler = self._spawnClassCompiler(self._mainClassName)
+ classCompiler = self._spawnClassCompiler(self._mainClassName)
if self._baseclassName:
classCompiler.setBaseClass(self._baseclassName)
self._addActiveClassCompiler(classCompiler)
@@ -1674,7 +1699,7 @@ def compile(self):
self._swallowClassCompiler(self._popActiveClassCompiler())
self._compiled = True
self._parser.cleanup()
-
+
def _spawnClassCompiler(self, className, klass=None):
if klass is None:
klass = self.classCompilerClass
@@ -1706,16 +1731,16 @@ def _finishedClasses(self):
def importedVarNames(self):
return self._importedVarNames
-
+
def addImportedVarNames(self, varNames, raw_statement=None):
settings = self.settings()
if not varNames:
- return
+ return
if not settings.get('useLegacyImportMode'):
if raw_statement and getattr(self, '_methodBodyChunks'):
self.addChunk(raw_statement)
else:
- self._importedVarNames.extend(varNames)
+ self._importedVarNames.update(varNames)
## methods for adding stuff to the module and class definitions
@@ -1724,7 +1749,7 @@ def setBaseClass(self, baseClassName):
self.setMainMethodName(self._mainMethodNameArg)
else:
self.setMainMethodName(self.setting('mainMethodNameForSubclasses'))
-
+
if self.setting('handlerForExtendsDirective'):
handler = self.setting('handlerForExtendsDirective')
baseClassName = handler(compiler=self, baseClassName=baseClassName)
@@ -1740,7 +1765,7 @@ def setBaseClass(self, baseClassName):
# an implied 'from ModName import ClassName' where ModName == ClassName.
# - This is the case in WebKit servlet modules.
# - We also assume that the final . separates the classname from the
- # module name. This might break if people do something really fancy
+ # module name. This might break if people do something really fancy
# with their dots and namespaces.
baseclasses = baseClassName.split(',')
for klass in baseclasses:
@@ -1765,30 +1790,27 @@ def setBaseClass(self, baseClassName):
self._getActiveClassCompiler().setBaseClass(finalBaseClassName)
break
else:
- modName += '.'+chunk
+ modName += '.'+chunk
if needToAddImport:
- modName, finalClassName = '.'.join(chunks[:-1]), chunks[-1]
+ modName, finalClassName = '.'.join(chunks[:-1]), chunks[-1]
#if finalClassName != chunks[:-1][-1]:
if finalClassName != chunks[-2]:
# we assume the class name to be the module name
modName = '.'.join(chunks)
- self._getActiveClassCompiler().setBaseClass(finalClassName)
+ self._getActiveClassCompiler().setBaseClass(finalClassName)
importStatement = "from %s import %s" % (modName, finalClassName)
self.addImportStatement(importStatement)
- self.addImportedVarNames( [finalClassName,] )
-
+ self.addImportedVarNames( [finalClassName,] )
+
def setCompilerSetting(self, key, valueExpr):
self.setSetting(key, eval(valueExpr) )
self._parser.configureParser()
def setCompilerSettings(self, keywords, settingsStr):
KWs = keywords
- merge = True
- if 'nomerge' in KWs:
- merge = False
-
+
if 'reset' in KWs:
- # @@TR: this is actually caught by the parser at the moment.
+ # @@TR: this is actually caught by the parser at the moment.
# subject to change in the future
self._initializeSettings()
self._parser.configureParser()
@@ -1802,10 +1824,10 @@ def setCompilerSettings(self, keywords, settingsStr):
settingsReader(settingsStr)
self._parser.configureParser()
-
+
def setShBang(self, shBang):
self._moduleShBang = shBang
-
+
def setModuleEncoding(self, encoding):
self._moduleEncoding = encoding
@@ -1816,8 +1838,8 @@ def addModuleHeader(self, line):
"""Adds a header comment to the top of the generated module.
"""
self._moduleHeaderLines.append(line)
-
- def addModuleDocString(self, line):
+
+ def addModuleDocString(self, line):
"""Adds a line to the generated module docstring.
"""
self._moduleDocStringLines.append(line)
@@ -1839,7 +1861,8 @@ def addImportStatement(self, impStatement):
if not self._methodBodyChunks or settings.get('useLegacyImportMode'):
# In the case where we are importing inline in the middle of a source block
# we don't want to inadvertantly import the module at the top of the file either
- self._importStatements.append(impStatement)
+ if impStatement not in self._importStatements:
+ self._importStatements.append(impStatement)
#@@TR 2005-01-01: there's almost certainly a cleaner way to do this!
importVarNames = impStatement[impStatement.find('import') + len('import'):].split(',')
@@ -1849,11 +1872,11 @@ def addImportStatement(self, impStatement):
def addAttribute(self, attribName, expr):
self._getActiveClassCompiler().addAttribute(attribName + ' =' + expr)
-
+
def addComment(self, comm):
if re.match(r'#+$', comm): # skip bar comments
return
-
+
specialVarMatch = specialVarRE.match(comm)
if specialVarMatch:
# @@TR: this is a bit hackish and is being replaced with
@@ -1882,7 +1905,7 @@ def addComment(self, comm):
addLine(line)
## methods for module code wrapping
-
+
def getModuleCode(self):
if not self._compiled:
self.compile()
@@ -1890,13 +1913,13 @@ def getModuleCode(self):
return self._moduleDef
else:
return self.wrapModuleDef()
-
+
__str__ = getModuleCode
def wrapModuleDef(self):
self.addSpecialVar('CHEETAH_docstring', self.setting('defDocStrMsg'))
self.addModuleGlobal('__CHEETAH_version__ = %r'%Version)
- self.addModuleGlobal('__CHEETAH_versionTuple__ = %r'%(VersionTuple,))
+ self.addModuleGlobal('__CHEETAH_versionTuple__ = %r'%(VersionTuple,))
if self.setting('addTimestampsToCompilerOutput'):
self.addModuleGlobal('__CHEETAH_genTime__ = %r'%time.time())
self.addModuleGlobal('__CHEETAH_genTimestamp__ = %r'%self.timestamp())
@@ -1949,7 +1972,7 @@ def wrapModuleDef(self):
'footer': self.moduleFooter(),
'mainClassName': self._mainClassName,
}
-
+
self._moduleDef = moduleDef
return moduleDef
@@ -1957,15 +1980,15 @@ def timestamp(self, theTime=None):
if not theTime:
theTime = time.time()
return time.asctime(time.localtime(theTime))
-
+
def moduleHeader(self):
header = self._moduleShBang + '\n'
header += self._moduleEncodingStr + '\n'
if self._moduleHeaderLines:
offSet = self.setting('commentOffset')
-
+
header += (
- '#' + ' '*offSet +
+ '#' + ' '*offSet +
('\n#'+ ' '*offSet).join(self._moduleHeaderLines) + '\n')
return header
@@ -1973,7 +1996,7 @@ def moduleHeader(self):
def moduleDocstring(self):
if not self._moduleDocStringLines:
return ''
-
+
return ('"""' +
'\n'.join(self._moduleDocStringLines) +
'\n"""\n')
@@ -1985,10 +2008,10 @@ def specialVars(self):
for key in keys:
chunks.append(key + ' = ' + repr(theVars[key]) )
return '\n'.join(chunks)
-
+
def importStatements(self):
return '\n'.join(self._importStatements)
-
+
def moduleConstants(self):
return '\n'.join(self._moduleConstants)
@@ -2013,5 +2036,5 @@ def moduleFooter(self):
##################################################
## Make Compiler an alias for ModuleCompiler
-
+
Compiler = ModuleCompiler
4 cheetah/DirectiveAnalyzer.py
View
@@ -74,9 +74,9 @@ def main():
op = OptionParser()
op.add_option('-f', '--file', dest='file', default=None,
help='Specify a single file to analyze')
- op.add_option('-d', '--dir', dest='dir', default=None,
+ op.add_option('-d', '--dir', dest='dir', default=None,
help='Specify a directory of templates to analyze')
- op.add_option('--suffix', default='tmpl', dest='suffix',
+ op.add_option('--suffix', default='tmpl', dest='suffix',
help='Specify a custom template file suffix for the -d option (default: "tmpl")')
opts, args = op.parse_args()
6 cheetah/Django.py
View
@@ -2,11 +2,11 @@
def render(template_file, **kwargs):
'''
- Cheetah.Django.render() takes the template filename
- (the filename should be a file in your Django
+ Cheetah.Django.render() takes the template filename
+ (the filename should be a file in your Django
TEMPLATE_DIRS)
- Any additional keyword arguments are passed into the
+ Any additional keyword arguments are passed into the
template are propogated into the template's searchList
'''
import django.http
22 cheetah/DummyTransaction.py
View
@@ -4,7 +4,7 @@
of real Webware transactions when the Template obj is not used directly as a
Webware servlet.
-Warning: This may be deprecated in the future, please do not rely on any
+Warning: This may be deprecated in the future, please do not rely on any
specific DummyTransaction or DummyResponse behavior
'''
@@ -19,7 +19,7 @@ class DummyResponse(object):
A dummy Response class is used by Cheetah in place of real Webware
Response objects when the Template obj is not used directly as a Webware
servlet
- '''
+ '''
def __init__(self):
self._outputChunks = []
@@ -54,16 +54,28 @@ def writeln(self, txt):
def getvalue(self, outputChunks=None):
chunks = outputChunks or self._outputChunks
try:
- return u''.join(chunks)
+ return concat(chunks)
except UnicodeDecodeError, ex:
logging.debug('Trying to work around a UnicodeDecodeError in getvalue()')
logging.debug('...perhaps you could fix "%s" while you\'re debugging')
- return ''.join((self.safeConvert(c) for c in chunks))
+ return concat(self.safeConvert(c) for c in chunks)
def writelines(self, *lines):
## not used
[self.writeln(ln) for ln in lines]
-
+
+def concat(strings):
+ strings = iter(strings)
+
+ try:
+ result = strings.next()
+ except StopIteration:
+ return u''
+
+ for string in strings:
+ result += string
+ return result
+
class DummyTransaction(object):
'''
12 cheetah/ErrorCatchers.py
View
@@ -19,13 +19,13 @@ class Error(Exception):
class ErrorCatcher:
_exceptionsToCatch = (NotFound,)
-
+
def __init__(self, templateObj):
pass
-
+
def exceptions(self):
return self._exceptionsToCatch
-
+
def warn(self, exc_val, code, rawCode, lineCol):
return rawCode
## make an alias
@@ -37,12 +37,12 @@ def warn(self, exc_val, code, rawCode, lineCol):
class KeyError(ErrorCatcher):
def warn(self, exc_val, code, rawCode, lineCol):
- raise KeyError("no '%s' in this Template Object's Search List" % rawCode)
+ raise KeyError("no '%s' in this Template Object's Search List" % rawCode)
class ListErrors(ErrorCatcher):
"""Accumulate a list of errors."""
_timeFormat = "%c"
-
+
def __init__(self, templateObj):
ErrorCatcher.__init__(self, templateObj)
self._errors = []
@@ -54,7 +54,7 @@ def warn(self, exc_val, code, rawCode, lineCol):
time.localtime(time.time()))
self._errors.append(dict)
return rawCode
-
+
def listErrors(self):
"""Return the list of errors."""
return self._errors
82 cheetah/FileUtils.py
View
@@ -15,9 +15,9 @@ def findFiles(*args, **kw):
This function is a wrapper around the FileFinder class. See its docstring
for details about the accepted arguments, etc."""
-
+
return FileFinder(*args, **kw).files()
-
+
def replaceStrInFiles(files, theStr, repl):
"""Replace all instances of 'theStr' with 'repl' for each file in the 'files'
@@ -27,7 +27,7 @@ def replaceStrInFiles(files, theStr, repl):
This function is a wrapper around the FindAndReplace class. See its
docstring for more details."""
-
+
pattern = _escapeRegexChars(theStr)
return FindAndReplace(files, pattern, repl).results()
@@ -48,26 +48,26 @@ def replaceRegexInFiles(files, pattern, repl):
## CLASSES
class FileFinder:
-
+
"""Traverses a directory tree and finds all files in it that match one of
the specified glob patterns."""
-
+
def __init__(self, rootPath,
globPatterns=('*',),
ignoreBasenames=('CVS', '.svn'),
ignoreDirs=(),
):
-
+
self._rootPath = rootPath
self._globPatterns = globPatterns
self._ignoreBasenames = ignoreBasenames
self._ignoreDirs = ignoreDirs
self._files = []
-
+
self.walkDirTree(rootPath)
-
+
def walkDirTree(self, dir='.',
-
+
listdir=os.listdir,
isdir=os.path.isdir,
join=os.path.join,
@@ -76,17 +76,17 @@ def walkDirTree(self, dir='.',
"""Recursively walk through a directory tree and find matching files."""
processDir = self.processDir
filterDir = self.filterDir
-
+
pendingDirs = [dir]
addDir = pendingDirs.append
getDir = pendingDirs.pop
-
+
while pendingDirs:
dir = getDir()
## process this dir
processDir(dir)
-
- ## and add sub-dirs
+
+ ## and add sub-dirs
for baseName in listdir(dir):
fullPath = join(dir, baseName)
if isdir(fullPath):
@@ -94,17 +94,17 @@ def walkDirTree(self, dir='.',
addDir( fullPath )
def filterDir(self, baseName, fullPath):
-
+
"""A hook for filtering out certain dirs. """
-
- return not (baseName in self._ignoreBasenames or
+
+ return not (baseName in self._ignoreBasenames or
fullPath in self._ignoreDirs)
-
+
def processDir(self, dir, glob=glob):
extend = self._files.extend
for pattern in self._globPatterns:
extend( glob(os.path.join(dir, pattern)) )
-
+
def files(self):
return self._files
@@ -113,10 +113,10 @@ class _GenSubberFunc:
"""Converts a 'sub' string in the form that one feeds to re.sub (backrefs,
groups, etc.) into a function that can be used to do the substitutions in
the FindAndReplace class."""
-
+
backrefRE = re.compile(r'\\([1-9][0-9]*)')
groupRE = re.compile(r'\\g<([a-zA-Z_][a-zA-Z_]*)>')
-
+
def __init__(self, replaceStr):
self._src = replaceStr
self._pos = 0
@@ -125,10 +125,10 @@ def __init__(self, replaceStr):
def src(self):
return self._src