changed template engine

This commit is contained in:
Fabian Graßl 2010-10-07 15:07:04 +02:00
parent 35a1b04c5a
commit a28a6f7d5b
70 changed files with 109 additions and 21162 deletions

File diff suppressed because it is too large Load Diff

View File

@ -1,136 +0,0 @@
# $Id: CacheRegion.py,v 1.3 2006/01/28 04:19:30 tavis_rudd Exp $
'''
Cache holder classes for Cheetah:
Cache regions are defined using the #cache Cheetah directive. Each
cache region can be viewed as a dictionary (keyed by cacheRegionID)
handling at least one cache item (the default one). It's possible to add
cacheItems in a region by using the `varyBy` #cache directive parameter as
in the following example::
#def getArticle
this is the article content.
#end def
#cache varyBy=$getArticleID()
$getArticle($getArticleID())
#end cache
The code above will generate a CacheRegion and add new cacheItem for each value
of $getArticleID().
'''
try:
from hashlib import md5
except ImportError:
from md5 import md5
import time
import Cheetah.CacheStore
class CacheItem(object):
'''
A CacheItem is a container storing:
- cacheID (string)
- 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
self._refreshTime = None
self._expiryTime = 0
def hasExpired(self):
return (self._expiryTime and time.time() > self._expiryTime)
def setExpiryTime(self, time):
self._expiryTime = time
def getExpiryTime(self):
return self._expiryTime
def setData(self, data):
self._refreshTime = time.time()
self._cacheStore.set(self._cacheItemID, data, self._expiryTime)
def getRefreshTime(self):
return self._refreshTime
def getData(self):
assert self._refreshTime
return self._cacheStore.get(self._cacheItemID)
def renderOutput(self):
"""Can be overridden to implement edge-caching"""
return self.getData() or ""
def clear(self):
self._cacheStore.delete(self._cacheItemID)
self._refreshTime = None
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):
self._dataStore.set(self._keyPrefix+key, val, time=time)
class CacheRegion(object):
'''
A `CacheRegion` stores some `CacheItem` instances.
This implementation stores the data in the memory of the current process.
If you need a more advanced data store, create a cacheStore class that works
with Cheetah's CacheStore protocol and provide it as the cacheStore argument
to __init__. For example you could use
Cheetah.CacheStore.MemcachedCacheStore, a wrapper around the Python
memcached API (http://www.danga.com/memcached).
'''
_cacheItemClass = CacheItem
def __init__(self, regionID, templateCacheIdPrefix='', cacheStore=None):
self._isNew = True
self._regionID = regionID
self._templateCacheIdPrefix = templateCacheIdPrefix
if not cacheStore:
cacheStore = Cheetah.CacheStore.MemoryCacheStore()
self._cacheStore = cacheStore
self._wrappedCacheDataStore = _CacheDataStoreWrapper(
cacheStore, keyPrefix=templateCacheIdPrefix+':'+regionID+':')
self._cacheItems = {}
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)
self._cacheItems[cacheItemID] = cacheItem
self._isNew = False
return self._cacheItems[cacheItemID]

View File

@ -1,106 +0,0 @@
'''
Provides several CacheStore backends for Cheetah's caching framework. The
methods provided by these classes have the same semantics as those in the
python-memcached API, except for their return values:
set(key, val, time=0)
set the value unconditionally
add(key, val, time=0)
set only if the server doesn't already have this key
replace(key, val, time=0)
set only if the server already have this key
get(key, val)
returns val or raises a KeyError
delete(key)
deletes or raises a KeyError
'''
import time
class Error(Exception):
pass
class AbstractCacheStore(object):
def set(self, key, val, time=None):
raise NotImplementedError
def add(self, key, val, time=None):
raise NotImplementedError
def replace(self, key, val, time=None):
raise NotImplementedError
def delete(self, key):
raise NotImplementedError
def get(self, key):
raise NotImplementedError
class MemoryCacheStore(AbstractCacheStore):
def __init__(self):
self._data = {}
def set(self, key, val, time=0):
self._data[key] = (val, time)
def add(self, key, val, time=0):
if key in self._data:
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):
if key in self._data:
raise Error('a value for key %r is already in the cache'%key)
self._data[key] = (val, time)
def delete(self, key):
del self._data[key]
def get(self, key):
(val, exptime) = self._data[key]
if exptime and time.time() > exptime:
del self._data[key]
raise KeyError(key)
else:
return val
def clear(self):
self._data.clear()
class MemcachedCacheStore(AbstractCacheStore):
servers = ('127.0.0.1:11211')
def __init__(self, servers=None, debug=False):
if servers is None:
servers = self.servers
from memcache import Client as MemcachedClient
self._client = MemcachedClient(servers, debug)
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)
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)
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)
if not res:
raise KeyError(key)
def get(self, key):
val = self._client.get(key)
if val is None:
raise KeyError(key)
else:
return val
def clear(self):
self._client.flush_all()

View File

@ -1,632 +0,0 @@
# $Id: CheetahWrapper.py,v 1.26 2007/10/02 01:22:04 tavis_rudd Exp $
"""Cheetah command-line interface.
2002-09-03 MSO: Total rewrite.
2002-09-04 MSO: Bugfix, compile command was using wrong output ext.
2002-11-08 MSO: Another rewrite.
Meta-Data
================================================================================
Author: Tavis Rudd <tavis@damnsimple.com> and Mike Orr <sluggoster@gmail.com>>
Version: $Revision: 1.26 $
Start Date: 2001/03/30
Last Revision Date: $Date: 2007/10/02 01:22:04 $
"""
__author__ = "Tavis Rudd <tavis@damnsimple.com> and Mike Orr <sluggoster@gmail.com>"
__revision__ = "$Revision: 1.26 $"[11:-2]
import getopt, glob, os, pprint, re, shutil, sys
import cPickle as pickle
from optparse import OptionParser
from Cheetah.Version import Version
from Cheetah.Template import Template, DEFAULT_COMPILER_SETTINGS
from Cheetah.Utils.Misc import mkdirsWithPyInitFiles
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]
else:
format += '\n'
if args:
message = format % args
else:
message = format
stream.write(message)
class Error(Exception):
pass
class Bundle:
"""Wrap the source, destination and backup paths in one neat little class.
Used by CheetahWrapper.getBundles().
"""
def __init__(self, **kw):
self.__dict__.update(kw)
def __repr__(self):
return "<Bundle %r>" % self.__dict__
##################################################
## USAGE FUNCTION & MESSAGES
def usage(usageMessage, errorMessage="", out=sys.stderr):
"""Write help text, an optional error message, and abort the program.
"""
out.write(WRAPPER_TOP)
out.write(usageMessage)
exitStatus = 0
if errorMessage:
out.write('\n')
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()
HELP_PAGE1 = """\
USAGE:
------
cheetah compile [options] [FILES ...] : Compile template definitions
cheetah fill [options] [FILES ...] : Fill template definitions
cheetah help : Print this help message
cheetah options : Print options help message
cheetah test [options] : Run Cheetah's regression tests
: (same as for unittest)
cheetah version : Print Cheetah version number
You may abbreviate the command to the first letter; e.g., 'h' == 'help'.
If FILES is a single "-", read standard input and write standard output.
Run "cheetah options" for the list of valid options.
"""
##################################################
## CheetahWrapper CLASS
class CheetahWrapper(object):
MAKE_BACKUPS = True
BACKUP_SUFFIX = ".bak"
_templateClass = None
_compilerSettings = None
def __init__(self):
self.progName = None
self.command = None
self.opts = None
self.pathArgs = None
self.sourceFiles = []
self.searchList = []
self.parser = None
##################################################
## MAIN ROUTINE
def main(self, argv=None):
"""The main program controller."""
if argv is None:
argv = sys.argv
# Step 1: Determine the command and arguments.
try:
self.progName = progName = os.path.basename(argv[0])
self.command = command = optionDashesRE.sub("", argv[1])
if command == 'test':
self.testOpts = argv[2:]
else:
self.parseOpts(argv[2:])
except IndexError:
usage(HELP_PAGE1, "not enough command-line arguments")
# Step 2: Call the command
meths = (self.compile, self.fill, self.help, self.options,
self.test, self.version)
for meth in meths:
methName = meth.__name__
# Or meth.im_func.func_name
# Or meth.func_name (Python >= 2.1 only, sometimes works on 2.0)
methInitial = methName[0]
if command in (methName, methInitial):
sys.argv[0] += (" " + methName)
# @@MO: I don't necessarily agree sys.argv[0] should be
# modified.
meth()
return
# If none of the commands matched.
usage(HELP_PAGE1, "unknown command '%s'" % command)
def parseOpts(self, args):
C, D, W = self.chatter, self.debug, self.warn
self.isCompile = isCompile = self.command[0] == 'c'
defaultOext = isCompile and ".py" or ".html"
self.parser = OptionParser()
pao = self.parser.add_option
pao("--idir", action="store", dest="idir", default='', help='Input directory (defaults to current directory)')
pao("--odir", action="store", dest="odir", default="", help='Output directory (defaults to current directory)')
pao("--iext", action="store", dest="iext", default=".tmpl", help='File input extension (defaults: compile: .tmpl, fill: .tmpl)')
pao("--oext", action="store", dest="oext", default=defaultOext, help='File output extension (defaults: compile: .py, fill: .html)')
pao("-R", action="store_true", dest="recurse", default=False, help='Recurse through subdirectories looking for input files')
pao("--stdout", "-p", action="store_true", dest="stdout", default=False, help='Send output to stdout instead of writing to a file')
pao("--quiet", action="store_false", dest="verbose", default=True, help='Do not print informational messages to stdout')
pao("--debug", action="store_true", dest="debug", default=False, help='Print diagnostic/debug information to stderr')
pao("--env", action="store_true", dest="env", default=False, help='Pass the environment into the search list')
pao("--pickle", action="store", dest="pickle", default="", help='Unpickle FILE and pass it through in the search list')
pao("--flat", action="store_true", dest="flat", default=False, help='Do not build destination subdirectories')
pao("--nobackup", action="store_true", dest="nobackup", default=False, help='Do not make backup files when generating new ones')
pao("--settings", action="store", dest="compilerSettingsString", default=None, help='String of compiler settings to pass through, e.g. --settings="useNameMapper=False,useFilters=False"')
pao('--print-settings', action='store_true', dest='print_settings', help='Print out the list of available compiler settings')
pao("--templateAPIClass", action="store", dest="templateClassName", default=None, help='Name of a subclass of Cheetah.Template.Template to use for compilation, e.g. MyTemplateClass')
pao("--parallel", action="store", type="int", dest="parallel", default=1, help='Compile/fill templates in parallel, e.g. --parallel=4')
pao('--shbang', dest='shbang', default='#!/usr/bin/env python', help='Specify the shbang to place at the top of compiled templates, e.g. --shbang="#!/usr/bin/python2.6"')
opts, files = self.parser.parse_args(args)
self.opts = opts
if sys.platform == "win32":
new_files = []
for spec in files:
file_list = glob.glob(spec)
if file_list:
new_files.extend(file_list)
else:
new_files.append(spec)
files = new_files
self.pathArgs = files
D("""\
cheetah compile %s
Options are
%s
Files are %s""", args, pprint.pformat(vars(opts)), files)
if opts.print_settings:
print()
print('>> Available Cheetah compiler settings:')
from Cheetah.Compiler import _DEFAULT_COMPILER_SETTINGS
listing = _DEFAULT_COMPILER_SETTINGS
listing.sort(key=lambda l: l[0][0].lower())
for l in listing:
print('\t%s (default: "%s")\t%s' % l)
sys.exit(0)
#cleanup trailing path separators
seps = [sep for sep in [os.sep, os.altsep] if sep]
for attr in ['idir', 'odir']:
for sep in seps:
path = getattr(opts, attr, None)
if path and path.endswith(sep):
path = path[:-len(sep)]
setattr(opts, attr, path)
break
self._fixExts()
if opts.env:
self.searchList.insert(0, os.environ)
if opts.pickle:
f = open(opts.pickle, 'rb')
unpickled = pickle.load(f)
f.close()
self.searchList.insert(0, unpickled)
##################################################
## COMMAND METHODS
def compile(self):
self._compileOrFill()
def fill(self):
from Cheetah.ImportHooks import install
install()
self._compileOrFill()
def help(self):
usage(HELP_PAGE1, "", sys.stdout)
def options(self):
return self.parser.print_help()
def test(self):
# @@MO: Ugly kludge.
TEST_WRITE_FILENAME = 'cheetah_test_file_creation_ability.tmp'
try:
f = open(TEST_WRITE_FILENAME, 'w')
except:
sys.exit("""\
Cannot run the tests because you don't have write permission in the current
directory. The tests need to create temporary files. Change to a directory
you do have write permission to and re-run the tests.""")
else:
f.close()
os.remove(TEST_WRITE_FILENAME)
# @@MO: End ugly kludge.
from Cheetah.Tests import Test
import unittest
verbosity = 1
if '-q' in self.testOpts:
verbosity = 0
if '-v' in self.testOpts:
verbosity = 2
runner = unittest.TextTestRunner(verbosity=verbosity)
runner.run(unittest.TestSuite(Test.suites))
def version(self):
print(Version)
# If you add a command, also add it to the 'meths' variable in main().
##################################################
## LOGGING METHODS
def chatter(self, format, *args):
"""Print a verbose message to stdout. But don't if .opts.stdout is
true or .opts.verbose is false.
"""
if self.opts.stdout or not self.opts.verbose:
return
fprintfMessage(sys.stdout, format, *args)
def debug(self, format, *args):
"""Print a debugging message to stderr, but don't if .debug is
false.
"""
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.
"""
fprintfMessage(sys.stderr, format, *args)
sys.exit(1)
##################################################
## HELPER METHODS
def _fixExts(self):
assert self.opts.oext, "oext is empty!"
iext, oext = self.opts.iext, self.opts.oext
if iext and not iext.startswith("."):
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 == ["-"]:
self._compileOrFillStdin()
return
elif not files and opts.recurse:
which = opts.idir and "idir" or "current"
C("Drilling down recursively from %s directory.", which)
sourceFiles = []
dir = os.path.join(self.opts.idir, os.curdir)
os.path.walk(dir, self._expandSourceFilesWalk, sourceFiles)
elif not files:
usage(HELP_PAGE1, "Neither files nor -R specified!")
else:
sourceFiles = self._expandSourceFiles(files, opts.recurse, True)
sourceFiles = [os.path.normpath(x) for x in sourceFiles]
D("All source files found: %s", sourceFiles)
bundles = self._getBundles(sourceFiles)
D("All bundles: %s", pprint.pformat(bundles))
if self.opts.flat:
self._checkForCollisions(bundles)
# In parallel mode a new process is forked for each template
# compilation, out of a pool of size self.opts.parallel. This is not
# really optimal in all cases (e.g. probably wasteful for small
# templates), but seems to work well in real life for me.
#
# It also won't work for Windows users, but I'm not going to lose any
# sleep over that.
if self.opts.parallel > 1:
bad_child_exit = 0
pid_pool = set()
def child_wait():
pid, status = os.wait()
pid_pool.remove(pid)
return os.WEXITSTATUS(status)
while bundles:
b = bundles.pop()
pid = os.fork()
if pid:
pid_pool.add(pid)
else:
self._compileOrFillBundle(b)
sys.exit(0)
if len(pid_pool) == self.opts.parallel:
bad_child_exit = child_wait()
if bad_child_exit:
break
while pid_pool:
child_exit = child_wait()
if not bad_child_exit:
bad_child_exit = child_exit
if bad_child_exit:
sys.exit("Child process failed, exited with code %d" % bad_child_exit)
else:
for b in bundles:
self._compileOrFillBundle(b)
def _checkForCollisions(self, bundles):
"""Check for multiple source paths writing to the same destination
path.
"""
C, D, W = self.chatter, self.debug, self.warn
isError = False
dstSources = {}
for b in bundles:
if b.dst in dstSources:
dstSources[b.dst].append(b.src)
else:
dstSources[b.dst] = [b.src]
keys = sorted(dstSources.keys())
for dst in keys:
sources = dstSources[dst]
if len(sources) > 1:
isError = True
sources.sort()
fmt = "Collision: multiple source files %s map to one destination file %s"
W(fmt, sources, dst)
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().
This method is a callback for os.path.walk().
'arg' is a list to which successful paths will be appended.
"""
iext = self.opts.iext
for f in files:
path = os.path.join(dir, f)
if path.endswith(iext) and os.path.isfile(path):
arg.append(path)
elif os.path.islink(path) and os.path.isdir(path):
os.path.walk(path, self._expandSourceFilesWalk, arg)
# If is directory, do nothing; 'walk' will eventually get it.
def _expandSourceFiles(self, files, recurse, addIextIfMissing):
"""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 = []
for f in self.pathArgs:
oldFilesLen = len(files)
D("Expanding %s", f)
path = os.path.join(idir, f)
pathWithExt = path + iext # May or may not be valid.
if os.path.isdir(path):
if recurse:
os.path.walk(path, self._expandSourceFilesWalk, files)
else:
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
os.path.isfile(pathWithExt)):
files.append(pathWithExt)
# Do not recurse directories discovered by iext appending.
elif os.path.exists(path):
W("Skipping source file '%s', not a plain file.", path)
else:
W("Skipping source file '%s', not found.", path)
if len(files) > oldFilesLen:
D(" ... found %s", files[oldFilesLen:])
return files
def _getBundles(self, sourceFiles):
flat = self.opts.flat
idir = self.opts.idir
iext = self.opts.iext
nobackup = self.opts.nobackup
odir = self.opts.odir
oext = self.opts.oext
idirSlash = idir + os.sep
bundles = []
for src in sourceFiles:
# 'base' is the subdirectory plus basename.
base = src
if idir and src.startswith(idirSlash):
base = src[len(idirSlash):]
if iext and base.endswith(iext):
base = base[:-len(iext)]
basename = os.path.basename(base)
if flat:
dst = os.path.join(odir, basename + oext)
else:
dbn = basename
if odir and base.startswith(os.sep):
odd = odir
while odd != '':
idx = base.find(odd)
if idx == 0:
dbn = base[len(odd):]
if dbn[0] == '/':
dbn = dbn[1:]
break
odd = os.path.dirname(odd)
if odd == '/':
break
dst = os.path.join(odir, dbn + oext)
else:
dst = os.path.join(odir, base + oext)
bak = dst + self.BACKUP_SUFFIX
b = Bundle(src=src, dst=dst, bak=bak, base=base, basename=basename)
bundles.append(b)
return bundles
def _getTemplateClass(self):
C, D, W = self.chatter, self.debug, self.warn
modname = None
if self._templateClass:
return self._templateClass
modname = self.opts.templateClassName
if not modname:
return Template
p = modname.rfind('.')
if ':' not in modname:
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:
mod = __import__(modname, {}, {}, [])
klass = getattr(mod, classname, None)
if klass:
self._templateClass = klass
return klass
else:
self.error('**Template class specified in option --templateAPIClass not found\n'
'**Falling back on Cheetah.Template:Template')
def _getCompilerSettings(self):
if self._compilerSettings:
return self._compilerSettings
def getkws(**kws):
return kws
if self.opts.compilerSettingsString:
try:
exec('settings = getkws(%s)'%self.opts.compilerSettingsString)
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]
)
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:
return {}
def _compileOrFillStdin(self):
TemplateClass = self._getTemplateClass()
compilerSettings = self._getCompilerSettings()
if self.isCompile:
pysrc = TemplateClass.compile(file=sys.stdin,
compilerSettings=compilerSettings,
returnAClass=False)
output = pysrc
else:
output = str(TemplateClass(file=sys.stdin, compilerSettings=compilerSettings))
sys.stdout.write(output)
def _compileOrFillBundle(self, b):
C, D, W = self.chatter, self.debug, self.warn
TemplateClass = self._getTemplateClass()
compilerSettings = self._getCompilerSettings()
src = b.src
dst = b.dst
base = b.base
basename = b.basename
dstDir = os.path.dirname(dst)
what = self.isCompile and "Compiling" or "Filling"
C("%s %s -> %s^", what, src, dst) # No trailing newline.
if os.path.exists(dst) and not self.opts.nobackup:
bak = b.bak
C(" (backup %s)", bak) # On same line as previous message.
else:
bak = None
C("")
if self.isCompile:
if not moduleNameRE.match(basename):
tup = basename, src
raise Error("""\
%s: base name %s contains invalid characters. It must
be named according to the same rules as Python modules.""" % tup)
pysrc = TemplateClass.compile(file=src, returnAClass=False,
moduleName=basename,
className=basename,
commandlineopts=self.opts,
compilerSettings=compilerSettings)
output = pysrc
else:
#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):
if self.isCompile:
mkdirsWithPyInitFiles(dstDir)
else:
os.makedirs(dstDir)
if self.opts.stdout:
sys.stdout.write(output)
else:
f = open(dst, 'w')
f.write(output)
f.close()
# Called when invoked as `cheetah`
def _cheetah():
CheetahWrapper().main()
# Called when invoked as `cheetah-compile`
def _cheetah_compile():
sys.argv.insert(1, "compile")
CheetahWrapper().main()
##################################################
## if run from the command line
if __name__ == '__main__': CheetahWrapper().main()
# vim: shiftwidth=4 tabstop=4 expandtab

File diff suppressed because it is too large Load Diff

View File

@ -1,98 +0,0 @@
#!/usr/bin/env python
import os
import pprint
try:
from functools import reduce
except ImportError:
# Assume we have reduce
pass
from Cheetah import Parser
from Cheetah import Compiler
from Cheetah import Template
class Analyzer(Parser.Parser):
def __init__(self, *args, **kwargs):
self.calls = {}
super(Analyzer, self).__init__(*args, **kwargs)
def eatDirective(self):
directive = self.matchDirective()
try:
self.calls[directive] += 1
except KeyError:
self.calls[directive] = 1
super(Analyzer, self).eatDirective()
class AnalysisCompiler(Compiler.ModuleCompiler):
parserClass = Analyzer
def analyze(source):
klass = Template.Template.compile(source, compilerClass=AnalysisCompiler)
return klass._CHEETAH_compilerInstance._parser.calls
def main_file(f):
fd = open(f, 'r')
try:
print u'>>> Analyzing %s' % f
calls = analyze(fd.read())
return calls
finally:
fd.close()
def _find_templates(directory, suffix):
for root, dirs, files in os.walk(directory):
for f in files:
if not f.endswith(suffix):
continue
yield root + os.path.sep + f
def _analyze_templates(iterable):
for template in iterable:
yield main_file(template)
def main_dir(opts):
results = _analyze_templates(_find_templates(opts.dir, opts.suffix))
totals = {}
for series in results:
if not series:
continue
for k, v in series.iteritems():
try:
totals[k] += v
except KeyError:
totals[k] = v
return totals
def main():
from optparse import OptionParser
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,
help='Specify a directory of templates to analyze')
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()
if not opts.file and not opts.dir:
op.print_help()
return
results = None
if opts.file:
results = main_file(opts.file)
if opts.dir:
results = main_dir(opts)
pprint.pprint(results)
if __name__ == '__main__':
main()

View File

@ -1,16 +0,0 @@
import Cheetah.Template
def render(template_file, **kwargs):
'''
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
template are propogated into the template's searchList
'''
import django.http
import django.template.loader
source, loader = django.template.loader.find_template_source(template_file)
t = Cheetah.Template.Template(source, searchList=[kwargs])
return django.http.HttpResponse(t.__str__())

View File

@ -1,108 +0,0 @@
'''
Provides dummy Transaction and Response classes is used by Cheetah in place
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
specific DummyTransaction or DummyResponse behavior
'''
import logging
import types
class DummyResponseFailure(Exception):
pass
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 = []
def flush(self):
pass
def safeConvert(self, chunk):
# Exceptionally gross, but the safest way
# I've found to ensure I get a legit unicode object
if not chunk:
return u''
if isinstance(chunk, unicode):
return chunk
try:
return chunk.decode('utf-8', 'strict')
except UnicodeDecodeError:
try:
return chunk.decode('latin-1', 'strict')
except UnicodeDecodeError:
return chunk.decode('ascii', 'ignore')
except AttributeError:
return unicode(chunk, errors='ignore')
return chunk
def write(self, value):
self._outputChunks.append(value)
def writeln(self, txt):
write(txt)
write('\n')
def getvalue(self, outputChunks=None):
chunks = outputChunks or self._outputChunks
try:
return u''.join(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))
def writelines(self, *lines):
## not used
[self.writeln(ln) for ln in lines]
class DummyTransaction(object):
'''
A dummy Transaction class is used by Cheetah in place of real Webware
transactions when the Template obj is not used directly as a Webware
servlet.
It only provides a response object and method. All other methods and
attributes make no sense in this context.
'''
def __init__(self, *args, **kwargs):
self._response = None
def response(self, resp=None):
if self._response is None:
self._response = resp or DummyResponse()
return self._response
class TransformerResponse(DummyResponse):
def __init__(self, *args, **kwargs):
super(TransformerResponse, self).__init__(*args, **kwargs)
self._filter = None
def getvalue(self, **kwargs):
output = super(TransformerResponse, self).getvalue(**kwargs)
if self._filter:
_filter = self._filter
if isinstance(_filter, type):
_filter = _filter()
return _filter.filter(output)
return output
class TransformerTransaction(object):
def __init__(self, *args, **kwargs):
self._response = None
def response(self):
if self._response:
return self._response
return TransformerResponse()

View File

@ -1,62 +0,0 @@
# $Id: ErrorCatchers.py,v 1.7 2005/01/03 19:59:07 tavis_rudd Exp $
"""ErrorCatcher class for Cheetah Templates
Meta-Data
================================================================================
Author: Tavis Rudd <tavis@damnsimple.com>
Version: $Revision: 1.7 $
Start Date: 2001/08/01
Last Revision Date: $Date: 2005/01/03 19:59:07 $
"""
__author__ = "Tavis Rudd <tavis@damnsimple.com>"
__revision__ = "$Revision: 1.7 $"[11:-2]
import time
from Cheetah.NameMapper import NotFound
class Error(Exception):
pass
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
Echo = ErrorCatcher
class BigEcho(ErrorCatcher):
def warn(self, exc_val, code, rawCode, lineCol):
return "="*15 + "&lt;" + rawCode + " could not be found&gt;" + "="*15
class KeyError(ErrorCatcher):
def warn(self, exc_val, code, rawCode, lineCol):
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 = []
def warn(self, exc_val, code, rawCode, lineCol):
dict = locals().copy()
del dict['self']
dict['time'] = time.strftime(self._timeFormat,
time.localtime(time.time()))
self._errors.append(dict)
return rawCode
def listErrors(self):
"""Return the list of errors."""
return self._errors

View File

@ -1,357 +0,0 @@
from glob import glob
import os
from os import listdir
import os.path
import re
from tempfile import mktemp
def _escapeRegexChars(txt,
escapeRE=re.compile(r'([\$\^\*\+\.\?\{\}\[\]\(\)\|\\])')):
return escapeRE.sub(r'\\\1', txt)
def findFiles(*args, **kw):
"""Recursively find all the files matching a glob pattern.
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'
list. Returns a dictionary with data about the matches found.
This is like string.replace() on a multi-file basis.
This function is a wrapper around the FindAndReplace class. See its
docstring for more details."""
pattern = _escapeRegexChars(theStr)
return FindAndReplace(files, pattern, repl).results()
def replaceRegexInFiles(files, pattern, repl):
"""Replace all instances of regex 'pattern' with 'repl' for each file in the
'files' list. Returns a dictionary with data about the matches found.
This is like re.sub on a multi-file basis.
This function is a wrapper around the FindAndReplace class. See its
docstring for more details."""
return FindAndReplace(files, pattern, repl).results()
##################################################
## 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,
):
"""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
for baseName in listdir(dir):
fullPath = join(dir, baseName)
if isdir(fullPath):
if filterDir(baseName, fullPath):
addDir( fullPath )
def filterDir(self, baseName, fullPath):
"""A hook for filtering out certain dirs. """
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
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
self._codeChunks = []
self.parse()
def src(self):
return self._src
def pos(self):
return self._pos
def setPos(self, pos):
self._pos = pos
def atEnd(self):
return self._pos >= len(self._src)
def advance(self, offset=1):
self._pos += offset
def readTo(self, to, start=None):
if start == None:
start = self._pos
self._pos = to
if self.atEnd():
return self._src[start:]
else:
return self._src[start:to]
## match and get methods
def matchBackref(self):
return self.backrefRE.match(self.src(), self.pos())
def getBackref(self):
m = self.matchBackref()
self.setPos(m.end())
return m.group(1)
def matchGroup(self):
return self.groupRE.match(self.src(), self.pos())
def getGroup(self):
m = self.matchGroup()
self.setPos(m.end())
return m.group(1)
## main parse loop and the eat methods
def parse(self):
while not self.atEnd():
if self.matchBackref():
self.eatBackref()
elif self.matchGroup():
self.eatGroup()
else:
self.eatStrConst()
def eatStrConst(self):
startPos = self.pos()
while not self.atEnd():
if self.matchBackref() or self.matchGroup():
break
else:
self.advance()
strConst = self.readTo(self.pos(), start=startPos)
self.addChunk(repr(strConst))
def eatBackref(self):
self.addChunk( 'm.group(' + self.getBackref() + ')' )
def eatGroup(self):
self.addChunk( 'm.group("' + self.getGroup() + '")' )
def addChunk(self, chunk):
self._codeChunks.append(chunk)
## code wrapping methods
def codeBody(self):
return ', '.join(self._codeChunks)
def code(self):
return "def subber(m):\n\treturn ''.join([%s])\n" % (self.codeBody())
def subberFunc(self):
exec(self.code())
return subber
class FindAndReplace:
"""Find and replace all instances of 'patternOrRE' with 'replacement' for
each file in the 'files' list. This is a multi-file version of re.sub().
'patternOrRE' can be a raw regex pattern or
a regex object as generated by the re module. 'replacement' can be any
string that would work with patternOrRE.sub(replacement, fileContents).
"""
def __init__(self, files, patternOrRE, replacement,
recordResults=True):
if isinstance(patternOrRE, basestring):
self._regex = re.compile(patternOrRE)
else:
self._regex = patternOrRE
if isinstance(replacement, basestring):
self._subber = _GenSubberFunc(replacement).subberFunc()
else:
self._subber = replacement
self._pattern = pattern = self._regex.pattern
self._files = files
self._results = {}
self._recordResults = recordResults
## see if we should use pgrep to do the file matching
self._usePgrep = False
if (os.popen3('pgrep')[2].read()).startswith('Usage:'):
## now check to make sure pgrep understands the pattern
tmpFile = mktemp()
open(tmpFile, 'w').write('#')
if not (os.popen3('pgrep "' + pattern + '" ' + tmpFile)[2].read()):
# it didn't print an error msg so we're ok
self._usePgrep = True
os.remove(tmpFile)
self._run()
def results(self):
return self._results
def _run(self):
regex = self._regex
subber = self._subDispatcher
usePgrep = self._usePgrep
pattern = self._pattern
for file in self._files:
if not os.path.isfile(file):
continue # skip dirs etc.
self._currFile = file
found = False
if 'orig' in locals():
del orig
if self._usePgrep:
if os.popen('pgrep "' + pattern + '" ' + file ).read():
found = True
else:
orig = open(file).read()
if regex.search(orig):
found = True
if found:
if 'orig' not in locals():
orig = open(file).read()
new = regex.sub(subber, orig)
open(file, 'w').write(new)
def _subDispatcher(self, match):
if self._recordResults:
if self._currFile not in self._results:
res = self._results[self._currFile] = {}
res['count'] = 0
res['matches'] = []
else:
res = self._results[self._currFile]
res['count'] += 1
res['matches'].append({'contents': match.group(),
'start': match.start(),
'end': match.end(),
}
)
return self._subber(match)
class SourceFileStats:
"""
"""
_fileStats = None
def __init__(self, files):
self._fileStats = stats = {}
for file in files:
stats[file] = self.getFileStats(file)
def rawStats(self):
return self._fileStats
def summary(self):
codeLines = 0
blankLines = 0
commentLines = 0
totalLines = 0
for fileStats in self.rawStats().values():
codeLines += fileStats['codeLines']
blankLines += fileStats['blankLines']
commentLines += fileStats['commentLines']
totalLines += fileStats['totalLines']
stats = {'codeLines': codeLines,
'blankLines': blankLines,
'commentLines': commentLines,
'totalLines': totalLines,
}
return stats
def printStats(self):
pass
def getFileStats(self, fileName):
codeLines = 0
blankLines = 0
commentLines = 0
commentLineRe = re.compile(r'\s#.*$')
blankLineRe = re.compile('\s$')
lines = open(fileName).read().splitlines()
totalLines = len(lines)
for line in lines:
if commentLineRe.match(line):
commentLines += 1
elif blankLineRe.match(line):
blankLines += 1
else:
codeLines += 1
stats = {'codeLines': codeLines,
'blankLines': blankLines,
'commentLines': commentLines,
'totalLines': totalLines,
}
return stats

View File

@ -1,212 +0,0 @@
'''
Filters for the #filter directive as well as #transform
#filter results in output filters Cheetah's $placeholders .
#transform results in a filter on the entirety of the output
'''
import sys
# Additional entities WebSafe knows how to transform. No need to include
# '<', '>' or '&' since those will have been done already.
webSafeEntities = {' ': '&nbsp;', '"': '&quot;'}
class Filter(object):
"""A baseclass for the Cheetah Filters."""
def __init__(self, template=None):
"""Setup a reference to the template that is using the filter instance.
This reference isn't used by any of the standard filters, but is
available to Filter subclasses, should they need it.
Subclasses should call this method.
"""
self.template = template
def filter(self, val, encoding=None, str=str, **kw):
'''
Pass Unicode strings through unmolested, unless an encoding is specified.
'''
if val is None:
return u''
if isinstance(val, unicode):
# ignore the encoding and return the unicode object
return val
else:
try:
return unicode(val)
except UnicodeDecodeError:
# we could put more fallbacks here, but we'll just pass the str
# on and let DummyTransaction worry about it
return str(val)
RawOrEncodedUnicode = Filter
EncodeUnicode = Filter
class Markdown(EncodeUnicode):
'''
Markdown will change regular strings to Markdown
(http://daringfireball.net/projects/markdown/)
Such that:
My Header
=========
Becaomes:
<h1>My Header</h1>
and so on.
Markdown is meant to be used with the #transform
tag, as it's usefulness with #filter is marginal at
best
'''
def filter(self, value, **kwargs):
# This is a bit of a hack to allow outright embedding of the markdown module
try:
import markdown
except ImportError:
print('>>> Exception raised importing the "markdown" module')
print('>>> Are you sure you have the ElementTree module installed?')
print(' http://effbot.org/downloads/#elementtree')
raise
encoded = super(Markdown, self).filter(value, **kwargs)
return markdown.markdown(encoded)
class CodeHighlighter(EncodeUnicode):
'''
The CodeHighlighter filter depends on the "pygments" module which you can
download and install from: http://pygments.org
What the CodeHighlighter assumes the string that it's receiving is source
code and uses pygments.lexers.guess_lexer() to try to guess which parser
to use when highlighting it.
CodeHighlighter will return the HTML and CSS to render the code block, syntax
highlighted, in a browser
NOTE: I had an issue installing pygments on Linux/amd64/Python 2.6 dealing with
importing of pygments.lexers, I was able to correct the failure by adding:
raise ImportError
to line 39 of pygments/plugin.py (since importing pkg_resources was causing issues)
'''
def filter(self, source, **kwargs):
encoded = super(CodeHighlighter, self).filter(source, **kwargs)
try:
from pygments import highlight
from pygments import lexers
from pygments import formatters
except ImportError, ex:
print('<%s> - Failed to import pygments! (%s)' % (self.__class__.__name__, ex))
print('-- You may need to install it from: http://pygments.org')
return encoded
lexer = None
try:
lexer = lexers.guess_lexer(source)
except lexers.ClassNotFound:
lexer = lexers.PythonLexer()
formatter = formatters.HtmlFormatter(cssclass='code_highlighter')
encoded = highlight(encoded, lexer, formatter)
css = formatter.get_style_defs('.code_highlighter')
return '''<style type="text/css"><!--
%(css)s
--></style>%(source)s''' % {'css' : css, 'source' : encoded}
class MaxLen(Filter):
def filter(self, val, **kw):
"""Replace None with '' and cut off at maxlen."""
output = super(MaxLen, self).filter(val, **kw)
if 'maxlen' in kw and len(output) > kw['maxlen']:
return output[:kw['maxlen']]
return output
class WebSafe(Filter):
"""Escape HTML entities in $placeholders.
"""
def filter(self, val, **kw):
s = super(WebSafe, self).filter(val, **kw)
# These substitutions are copied from cgi.escape().
s = s.replace("&", "&amp;") # Must be done first!
s = s.replace("<", "&lt;")
s = s.replace(">", "&gt;")
# Process the additional transformations if any.
if 'also' in kw:
also = kw['also']
entities = webSafeEntities # Global variable.
for k in also:
if k in entities:
v = entities[k]
else:
v = "&#%s;" % ord(k)
s = s.replace(k, v)
return s
class Strip(Filter):
"""Strip leading/trailing whitespace but preserve newlines.
This filter goes through the value line by line, removing leading and
trailing whitespace on each line. It does not strip newlines, so every
input line corresponds to one output line, with its trailing newline intact.
We do not use val.split('\n') because that would squeeze out consecutive
blank lines. Instead, we search for each newline individually. This
makes us unable to use the fast C .split method, but it makes the filter
much more widely useful.
This filter is intended to be usable both with the #filter directive and
with the proposed #sed directive (which has not been ratified yet.)
"""
def filter(self, val, **kw):
s = super(Strip, self).filter(val, **kw)
result = []
start = 0 # The current line will be s[start:end].
while True: # Loop through each line.
end = s.find('\n', start) # Find next newline.
if end == -1: # If no more newlines.
break
chunk = s[start:end].strip()
result.append(chunk)
result.append('\n')
start = end + 1
# Write the unfinished portion after the last newline, if any.
chunk = s[start:].strip()
result.append(chunk)
return "".join(result)
class StripSqueeze(Filter):
"""Canonicalizes every chunk of whitespace to a single space.
Strips leading/trailing whitespace. Removes all newlines, so multi-line
input is joined into one ling line with NO trailing newline.
"""
def filter(self, val, **kw):
s = super(StripSqueeze, self).filter(val, **kw)
s = s.split()
return " ".join(s)
##################################################
## MAIN ROUTINE -- testing
def test():
s1 = "abc <=> &"
s2 = " asdf \n\t 1 2 3\n"
print("WebSafe INPUT:", repr(s1))
print(" WebSafe:", repr(WebSafe().filter(s1)))
print()
print(" Strip INPUT:", repr(s2))
print(" Strip:", repr(Strip().filter(s2)))
print("StripSqueeze:", repr(StripSqueeze().filter(s2)))
print("Unicode:", repr(EncodeUnicode().filter(u'aoeu12345\u1234')))
if __name__ == "__main__":
test()
# vim: shiftwidth=4 tabstop=4 expandtab

View File

@ -1,129 +0,0 @@
#!/usr/bin/env python
"""
Provides some import hooks to allow Cheetah's .tmpl files to be imported
directly like Python .py modules.
To use these:
import Cheetah.ImportHooks
Cheetah.ImportHooks.install()
"""
import sys
import os.path
import types
import __builtin__
import imp
from threading import RLock
import string
import traceback
import types
from Cheetah import ImportManager
from Cheetah.ImportManager import DirOwner
from Cheetah.Compiler import Compiler
from Cheetah.convertTmplPathToModuleName import convertTmplPathToModuleName
_installed = False
##################################################
## HELPER FUNCS
_cacheDir = []
def setCacheDir(cacheDir):
global _cacheDir
_cacheDir.append(cacheDir)
##################################################
## CLASSES
class CheetahDirOwner(DirOwner):
_lock = RLock()
_acquireLock = _lock.acquire
_releaseLock = _lock.release
templateFileExtensions = ('.tmpl',)
def getmod(self, name):
self._acquireLock()
try:
mod = DirOwner.getmod(self, name)
if mod:
return mod
for ext in self.templateFileExtensions:
tmplPath = os.path.join(self.path, name + ext)
if os.path.exists(tmplPath):
try:
return self._compile(name, tmplPath)
except:
# @@TR: log the error
exc_txt = traceback.format_exc()
exc_txt =' '+(' \n'.join(exc_txt.splitlines()))
raise ImportError(
'Error while compiling Cheetah module'
' %(name)s, original traceback follows:\n%(exc_txt)s'%locals())
##
return None
finally:
self._releaseLock()
def _compile(self, name, tmplPath):
## @@ consider adding an ImportError raiser here
code = str(Compiler(file=tmplPath, moduleName=name,
mainClassName=name))
if _cacheDir:
__file__ = os.path.join(_cacheDir[0],
convertTmplPathToModuleName(tmplPath)) + '.py'
try:
open(__file__, 'w').write(code)
except OSError:
## @@ TR: need to add some error code here
traceback.print_exc(file=sys.stderr)
__file__ = tmplPath
else:
__file__ = tmplPath
co = compile(code+'\n', __file__, 'exec')
mod = types.ModuleType(name)
mod.__file__ = co.co_filename
if _cacheDir:
mod.__orig_file__ = tmplPath # @@TR: this is used in the WebKit
# filemonitoring code
mod.__co__ = co
return mod
##################################################
## FUNCTIONS
def install(templateFileExtensions=('.tmpl',)):
"""Install the Cheetah Import Hooks"""
global _installed
if not _installed:
CheetahDirOwner.templateFileExtensions = templateFileExtensions
import __builtin__
if isinstance(__builtin__.__import__, types.BuiltinFunctionType):
global __oldimport__
__oldimport__ = __builtin__.__import__
ImportManager._globalOwnerTypes.insert(0, CheetahDirOwner)
#ImportManager._globalOwnerTypes.append(CheetahDirOwner)
global _manager
_manager=ImportManager.ImportManager()
_manager.setThreaded()
_manager.install()
def uninstall():
"""Uninstall the Cheetah Import Hooks"""
global _installed
if not _installed:
import __builtin__
if isinstance(__builtin__.__import__, types.MethodType):
__builtin__.__import__ = __oldimport__
global _manager
del _manager
if __name__ == '__main__':
install()

View File

@ -1,541 +0,0 @@
"""
Provides an emulator/replacement for Python's standard import system.
@@TR: Be warned that Import Hooks are in the deepest, darkest corner of Python's
jungle. If you need to start hacking with this, be prepared to get lost for a
while. Also note, this module predates the newstyle import hooks in Python 2.3
http://www.python.org/peps/pep-0302.html.
This is a hacked/documented version of Gordon McMillan's iu.py. I have:
- made it a little less terse
- added docstrings and explanatations
- standardized the variable naming scheme
- reorganized the code layout to enhance readability
"""
import sys
import imp
import marshal
_installed = False
# _globalOwnerTypes is defined at the bottom of this file
_os_stat = _os_path_join = _os_getcwd = _os_path_dirname = None
##################################################
## FUNCTIONS
def _os_bootstrap():
"""Set up 'os' module replacement functions for use during import bootstrap."""
names = sys.builtin_module_names
join = dirname = None
if 'posix' in names:
sep = '/'
from posix import stat, getcwd
elif 'nt' in names:
sep = '\\'
from nt import stat, getcwd
elif 'dos' in names:
sep = '\\'
from dos import stat, getcwd
elif 'os2' in names:
sep = '\\'
from os2 import stat, getcwd
elif 'mac' in names:
from mac import stat, getcwd
def join(a, b):
if a == '':
return b
if ':' not in a:
a = ':' + a
if a[-1:] != ':':
a = a + ':'
return a + b
else:
raise ImportError('no os specific module found')
if join is None:
def join(a, b, sep=sep):
if a == '':
return b
lastchar = a[-1:]
if lastchar == '/' or lastchar == sep:
return a + b
return a + sep + b
if dirname is None:
def dirname(a, sep=sep):
for i in range(len(a)-1, -1, -1):
c = a[i]
if c == '/' or c == sep:
return a[:i]
return ''
global _os_stat
_os_stat = stat
global _os_path_join
_os_path_join = join
global _os_path_dirname
_os_path_dirname = dirname
global _os_getcwd
_os_getcwd = getcwd
_os_bootstrap()
def packageName(s):
for i in range(len(s)-1, -1, -1):
if s[i] == '.':
break
else:
return ''
return s[:i]
def nameSplit(s):
rslt = []
i = j = 0
for j in range(len(s)):
if s[j] == '.':
rslt.append(s[i:j])
i = j+1
if i < len(s):
rslt.append(s[i:])
return rslt
def getPathExt(fnm):
for i in range(len(fnm)-1, -1, -1):
if fnm[i] == '.':
return fnm[i:]
return ''
def pathIsDir(pathname):
"Local replacement for os.path.isdir()."
try:
s = _os_stat(pathname)
except OSError:
return None
return (s[0] & 0170000) == 0040000
def getDescr(fnm):
ext = getPathExt(fnm)
for (suffix, mode, typ) in imp.get_suffixes():
if suffix == ext:
return (suffix, mode, typ)
##################################################
## CLASSES
class Owner:
"""An Owner does imports from a particular piece of turf That is, there's
an Owner for each thing on sys.path There are owners for directories and
.pyz files. There could be owners for zip files, or even URLs. A
shadowpath (a dictionary mapping the names in sys.path to their owners) is
used so that sys.path (or a package's __path__) is still a bunch of strings,
"""
def __init__(self, path):
self.path = path
def __str__(self):
return self.path
def getmod(self, nm):
return None
class DirOwner(Owner):
def __init__(self, path):
if path == '':
path = _os_getcwd()
if not pathIsDir(path):
raise ValueError("%s is not a directory" % path)
Owner.__init__(self, path)
def getmod(self, nm,
getsuffixes=imp.get_suffixes, loadco=marshal.loads, newmod=imp.new_module):
pth = _os_path_join(self.path, nm)
possibles = [(pth, 0, None)]
if pathIsDir(pth):
possibles.insert(0, (_os_path_join(pth, '__init__'), 1, pth))
py = pyc = None
for pth, ispkg, pkgpth in possibles:
for ext, mode, typ in getsuffixes():
attempt = pth+ext
try:
st = _os_stat(attempt)
except:
pass
else:
if typ == imp.C_EXTENSION:
fp = open(attempt, 'rb')
mod = imp.load_module(nm, fp, attempt, (ext, mode, typ))
mod.__file__ = attempt
return mod
elif typ == imp.PY_SOURCE:
py = (attempt, st)
else:
pyc = (attempt, st)
if py or pyc:
break
if py is None and pyc is None:
return None
while True:
if pyc is None or py and pyc[1][8] < py[1][8]:
try:
co = compile(open(py[0], 'r').read()+'\n', py[0], 'exec')
break
except SyntaxError, e:
print("Invalid syntax in %s" % py[0])
print(e.args)
raise
elif pyc:
stuff = open(pyc[0], 'rb').read()
try:
co = loadco(stuff[8:])
break
except (ValueError, EOFError):
pyc = None
else:
return None
mod = newmod(nm)
mod.__file__ = co.co_filename
if ispkg:
mod.__path__ = [pkgpth]
subimporter = PathImportDirector(mod.__path__)
mod.__importsub__ = subimporter.getmod
mod.__co__ = co
return mod
class ImportDirector(Owner):
"""ImportDirectors live on the metapath There's one for builtins, one for
frozen modules, and one for sys.path Windows gets one for modules gotten
from the Registry Mac would have them for PY_RESOURCE modules etc. A
generalization of Owner - their concept of 'turf' is broader"""
pass
class BuiltinImportDirector(ImportDirector):
"""Directs imports of builtin modules"""
def __init__(self):
self.path = 'Builtins'
def getmod(self, nm, isbuiltin=imp.is_builtin):
if isbuiltin(nm):
mod = imp.load_module(nm, None, nm, ('', '', imp.C_BUILTIN))
return mod
return None
class FrozenImportDirector(ImportDirector):
"""Directs imports of frozen modules"""
def __init__(self):
self.path = 'FrozenModules'
def getmod(self, nm,
isFrozen=imp.is_frozen, loadMod=imp.load_module):
if isFrozen(nm):
mod = loadMod(nm, None, nm, ('', '', imp.PY_FROZEN))
if hasattr(mod, '__path__'):
mod.__importsub__ = lambda name, pname=nm, owner=self: owner.getmod(pname+'.'+name)
return mod
return None
class RegistryImportDirector(ImportDirector):
"""Directs imports of modules stored in the Windows Registry"""
def __init__(self):
self.path = "WindowsRegistry"
self.map = {}
try:
import win32api
## import win32con
except ImportError:
pass
else:
HKEY_CURRENT_USER = -2147483647
HKEY_LOCAL_MACHINE = -2147483646
KEY_ALL_ACCESS = 983103
subkey = r"Software\Python\PythonCore\%s\Modules" % sys.winver
for root in (HKEY_CURRENT_USER, HKEY_LOCAL_MACHINE):
try:
hkey = win32api.RegOpenKeyEx(root, subkey, 0, KEY_ALL_ACCESS)
except:
pass
else:
numsubkeys, numvalues, lastmodified = win32api.RegQueryInfoKey(hkey)
for i in range(numsubkeys):
subkeyname = win32api.RegEnumKey(hkey, i)
hskey = win32api.RegOpenKeyEx(hkey, subkeyname, 0, KEY_ALL_ACCESS)
val = win32api.RegQueryValueEx(hskey, '')
desc = getDescr(val[0])
self.map[subkeyname] = (val[0], desc)
hskey.Close()
hkey.Close()
break
def getmod(self, nm):
stuff = self.map.get(nm)
if stuff:
fnm, desc = stuff
fp = open(fnm, 'rb')
mod = imp.load_module(nm, fp, fnm, desc)
mod.__file__ = fnm
return mod
return None
class PathImportDirector(ImportDirector):
"""Directs imports of modules stored on the filesystem."""
def __init__(self, pathlist=None, importers=None, ownertypes=None):
if pathlist is None:
self.path = sys.path
else:
self.path = pathlist
if ownertypes == None:
self._ownertypes = _globalOwnerTypes
else:
self._ownertypes = ownertypes
if importers:
self._shadowPath = importers
else:
self._shadowPath = {}
self._inMakeOwner = False
self._building = {}
def getmod(self, nm):
mod = None
for thing in self.path:
if isinstance(thing, basestring):
owner = self._shadowPath.get(thing, -1)
if owner == -1:
owner = self._shadowPath[thing] = self._makeOwner(thing)
if owner:
mod = owner.getmod(nm)
else:
mod = thing.getmod(nm)
if mod:
break
return mod
def _makeOwner(self, path):
if self._building.get(path):
return None
self._building[path] = 1
owner = None
for klass in self._ownertypes:
try:
# this may cause an import, which may cause recursion
# hence the protection
owner = klass(path)
except:
pass
else:
break
del self._building[path]
return owner
#=================ImportManager============================#
# The one-and-only ImportManager
# ie, the builtin import
UNTRIED = -1
class ImportManager:
# really the equivalent of builtin import
def __init__(self):
self.metapath = [
BuiltinImportDirector(),
FrozenImportDirector(),
RegistryImportDirector(),
PathImportDirector()
]
self.threaded = 0
self.rlock = None
self.locker = None
self.setThreaded()
def setThreaded(self):
thread = sys.modules.get('thread', None)
if thread and not self.threaded:
self.threaded = 1
self.rlock = thread.allocate_lock()
self._get_ident = thread.get_ident
def install(self):
import __builtin__
__builtin__.__import__ = self.importHook
__builtin__.reload = self.reloadHook
def importHook(self, name, globals=None, locals=None, fromlist=None, level=-1):
'''
NOTE: Currently importHook will accept the keyword-argument "level"
but it will *NOT* use it (currently). Details about the "level" keyword
argument can be found here: http://www.python.org/doc/2.5.2/lib/built-in-funcs.html
'''
# first see if we could be importing a relative name
#print "importHook(%s, %s, locals, %s)" % (name, globals['__name__'], fromlist)
_sys_modules_get = sys.modules.get
contexts = [None]
if globals:
importernm = globals.get('__name__', '')
if importernm:
if hasattr(_sys_modules_get(importernm), '__path__'):
contexts.insert(0, importernm)
else:
pkgnm = packageName(importernm)
if pkgnm:
contexts.insert(0, pkgnm)
# so contexts is [pkgnm, None] or just [None]
# now break the name being imported up so we get:
# a.b.c -> [a, b, c]
nmparts = nameSplit(name)
_self_doimport = self.doimport
threaded = self.threaded
for context in contexts:
ctx = context
for i in range(len(nmparts)):
nm = nmparts[i]
#print " importHook trying %s in %s" % (nm, ctx)
if ctx:
fqname = ctx + '.' + nm
else:
fqname = nm
if threaded:
self._acquire()
mod = _sys_modules_get(fqname, UNTRIED)
if mod is UNTRIED:
mod = _self_doimport(nm, ctx, fqname)
if threaded:
self._release()
if mod:
ctx = fqname
else:
break
else:
# no break, point i beyond end
i = i + 1
if i:
break
if i<len(nmparts):
if ctx and hasattr(sys.modules[ctx], nmparts[i]):
#print "importHook done with %s %s %s (case 1)" % (name, globals['__name__'], fromlist)
return sys.modules[nmparts[0]]
del sys.modules[fqname]
raise ImportError("No module named %s" % fqname)
if fromlist is None:
#print "importHook done with %s %s %s (case 2)" % (name, globals['__name__'], fromlist)
if context:
return sys.modules[context+'.'+nmparts[0]]
return sys.modules[nmparts[0]]
bottommod = sys.modules[ctx]
if hasattr(bottommod, '__path__'):
fromlist = list(fromlist)
i = 0
while i < len(fromlist):
nm = fromlist[i]
if nm == '*':
fromlist[i:i+1] = list(getattr(bottommod, '__all__', []))
if i >= len(fromlist):
break
nm = fromlist[i]
i = i + 1
if not hasattr(bottommod, nm):
if self.threaded:
self._acquire()
mod = self.doimport(nm, ctx, ctx+'.'+nm)
if self.threaded:
self._release()
if not mod:
raise ImportError("%s not found in %s" % (nm, ctx))
#print "importHook done with %s %s %s (case 3)" % (name, globals['__name__'], fromlist)
return bottommod
def doimport(self, nm, parentnm, fqname):
# Not that nm is NEVER a dotted name at this point
#print "doimport(%s, %s, %s)" % (nm, parentnm, fqname)
if parentnm:
parent = sys.modules[parentnm]
if hasattr(parent, '__path__'):
importfunc = getattr(parent, '__importsub__', None)
if not importfunc:
subimporter = PathImportDirector(parent.__path__)
importfunc = parent.__importsub__ = subimporter.getmod
mod = importfunc(nm)
if mod:
setattr(parent, nm, mod)
else:
#print "..parent not a package"
return None
else:
# now we're dealing with an absolute import
for director in self.metapath:
mod = director.getmod(nm)
if mod:
break
if mod:
mod.__name__ = fqname
sys.modules[fqname] = mod
if hasattr(mod, '__co__'):
co = mod.__co__
del mod.__co__
exec(co, mod.__dict__)
if fqname == 'thread' and not self.threaded:
## print "thread detected!"
self.setThreaded()
else:
sys.modules[fqname] = None
#print "..found %s" % mod
return mod
def reloadHook(self, mod):
fqnm = mod.__name__
nm = nameSplit(fqnm)[-1]
parentnm = packageName(fqnm)
newmod = self.doimport(nm, parentnm, fqnm)
mod.__dict__.update(newmod.__dict__)
## return newmod
def _acquire(self):
if self.rlock.locked():
if self.locker == self._get_ident():
self.lockcount = self.lockcount + 1
## print "_acquire incrementing lockcount to", self.lockcount
return
self.rlock.acquire()
self.locker = self._get_ident()
self.lockcount = 0
## print "_acquire first time!"
def _release(self):
if self.lockcount:
self.lockcount = self.lockcount - 1
## print "_release decrementing lockcount to", self.lockcount
else:
self.rlock.release()
## print "_release releasing lock!"
##################################################
## MORE CONSTANTS & GLOBALS
_globalOwnerTypes = [
DirOwner,
Owner,
]

View File

@ -1,16 +0,0 @@
Copyright 2001-2005, The Cheetah Development Team: Tavis Rudd, Mike Orr,
Chuck Esterbrook, Ian Bicking.
Permission to use, copy, modify, and distribute this software for any purpose
and without fee is hereby granted, provided that the above copyright notice
appear in all copies and that both that copyright notice and this permission
notice appear in supporting documentation, and that the names of the authors not
be used in advertising or publicity pertaining to distribution of the software
without specific, written prior permission.
THE AUTHORS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHORS
BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF
CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.

View File

@ -1,67 +0,0 @@
import gettext
_ = gettext.gettext
class I18n(object):
def __init__(self, parser):
pass
## junk I'm playing with to test the macro framework
# def parseArgs(self, parser, startPos):
# parser.getWhiteSpace()
# args = parser.getExpression(useNameMapper=False,
# pyTokensToBreakAt=[':']).strip()
# return args
#
# def convertArgStrToDict(self, args, parser=None, startPos=None):
# def getArgs(*pargs, **kws):
# return pargs, kws
# exec 'positionalArgs, kwArgs = getArgs(%(args)s)'%locals()
# return kwArgs
def __call__(self,
src, # aka message,
plural=None,
n=None, # should be a string representing the name of the
# '$var' rather than $var itself
id=None,
domain=None,
source=None,
target=None,
comment=None,
# args that are automatically supplied by the parser when the
# macro is called:
parser=None,
macros=None,
isShortForm=False,
EOLCharsInShortForm=None,
startPos=None,
endPos=None,
):
"""This is just a stub at this time.
plural = the plural form of the message
n = a sized argument to distinguish between single and plural forms
id = msgid in the translation catalog
domain = translation domain
source = source lang
target = a specific target lang
comment = a comment to the translation team
See the following for some ideas
http://www.zope.org/DevHome/Wikis/DevSite/Projects/ComponentArchitecture/ZPTInternationalizationSupport
Other notes:
- There is no need to replicate the i18n:name attribute from plone / PTL,
as cheetah placeholders serve the same purpose
"""
#print macros['i18n']
src = _(src)
if isShortForm and endPos<len(parser):
return src+EOLCharsInShortForm
else:
return src

View File

@ -1 +0,0 @@
#

View File

@ -1,366 +0,0 @@
#!/usr/bin/env python
"""This module supports Cheetah's optional NameMapper syntax.
Overview
================================================================================
NameMapper provides a simple syntax for accessing Python data structures,
functions, and methods from Cheetah. It's called NameMapper because it 'maps'
simple 'names' in Cheetah templates to possibly more complex syntax in Python.
Its purpose is to make working with Cheetah easy for non-programmers.
Specifically, non-programmers using Cheetah should NOT need to be taught (a)
what the difference is between an object and a dictionary, (b) what functions
and methods are, and (c) what 'self' is. A further aim (d) is to buffer the
code in Cheetah templates from changes in the implementation of the Python data
structures behind them.
Consider this scenario:
You are building a customer information system. The designers with you want to
use information from your system on the client's website --AND-- they want to
understand the display code and so they can maintian it themselves.
You write a UI class with a 'customers' method that returns a dictionary of all
the customer objects. Each customer object has an 'address' method that returns
the a dictionary with information about the customer's address. The designers
want to be able to access that information.
Using PSP, the display code for the website would look something like the
following, assuming your servlet subclasses the class you created for managing
customer information:
<%= self.customer()[ID].address()['city'] %> (42 chars)
Using Cheetah's NameMapper syntax it could be any of the following:
$self.customers()[$ID].address()['city'] (39 chars)
--OR--
$customers()[$ID].address()['city']
--OR--
$customers()[$ID].address().city
--OR--
$customers()[$ID].address.city
--OR--
$customers()[$ID].address.city
--OR--
$customers[$ID].address.city (27 chars)
Which of these would you prefer to explain to the designers, who have no
programming experience? The last form is 15 characters shorter than the PSP
and, conceptually, is far more accessible. With PHP or ASP, the code would be
even messier than the PSP
This is a rather extreme example and, of course, you could also just implement
'$getCustomer($ID).city' and obey the Law of Demeter (search Google for more on that).
But good object orientated design isn't the point here.
Details
================================================================================
The parenthesized letters below correspond to the aims in the second paragraph.
DICTIONARY ACCESS (a)
---------------------
NameMapper allows access to items in a dictionary using the same dotted notation
used to access object attributes in Python. This aspect of NameMapper is known
as 'Unified Dotted Notation'.
For example, with Cheetah it is possible to write:
$customers()['kerr'].address() --OR-- $customers().kerr.address()
where the second form is in NameMapper syntax.
This only works with dictionary keys that are also valid python identifiers:
regex = '[a-zA-Z_][a-zA-Z_0-9]*'
AUTOCALLING (b,d)
-----------------
NameMapper automatically detects functions and methods in Cheetah $vars and calls
them if the parentheses have been left off.
For example if 'a' is an object, 'b' is a method
$a.b
is equivalent to
$a.b()
If b returns a dictionary, then following variations are possible
$a.b.c --OR-- $a.b().c --OR-- $a.b()['c']
where 'c' is a key in the dictionary that a.b() returns.
Further notes:
* NameMapper autocalls the function or method without any arguments. Thus
autocalling can only be used with functions or methods that either have no
arguments or have default values for all arguments.
* NameMapper only autocalls functions and methods. Classes and callable object instances
will not be autocalled.
* Autocalling can be disabled using Cheetah's 'useAutocalling' setting.
LEAVING OUT 'self' (c,d)
------------------------
NameMapper makes it possible to access the attributes of a servlet in Cheetah
without needing to include 'self' in the variable names. See the NAMESPACE
CASCADING section below for details.
NAMESPACE CASCADING (d)
--------------------
...
Implementation details
================================================================================
* NameMapper's search order is dictionary keys then object attributes
* NameMapper.NotFound is raised if a value can't be found for a name.
Performance and the C version
================================================================================
Cheetah comes with both a C version and a Python version of NameMapper. The C
version is significantly faster and the exception tracebacks are much easier to
read. It's still slower than standard Python syntax, but you won't notice the
difference in realistic usage scenarios.
Cheetah uses the optimized C version (_namemapper.c) if it has
been compiled or falls back to the Python version if not.
"""
__author__ = "Tavis Rudd <tavis@damnsimple.com>," +\
"\nChuck Esterbrook <echuck@mindspring.com>"
from pprint import pformat
import inspect
_INCLUDE_NAMESPACE_REPR_IN_NOTFOUND_EXCEPTIONS = False
_ALLOW_WRAPPING_OF_NOTFOUND_EXCEPTIONS = True
__all__ = ['NotFound',
'hasKey',
'valueForKey',
'valueForName',
'valueFromSearchList',
'valueFromFrameOrSearchList',
'valueFromFrame',
]
if not hasattr(inspect.imp, 'get_suffixes'):
# This is to fix broken behavior of the inspect module under the
# Google App Engine, see the following issue:
# http://bugs.communitycheetah.org/view.php?id=10
setattr(inspect.imp, 'get_suffixes', lambda: [('.py', 'U', 1)])
## N.B. An attempt is made at the end of this module to import C versions of
## these functions. If _namemapper.c has been compiled succesfully and the
## import goes smoothly, the Python versions defined here will be replaced with
## the C versions.
class NotFound(LookupError):
pass
def _raiseNotFoundException(key, namespace):
excString = "cannot find '%s'"%key
if _INCLUDE_NAMESPACE_REPR_IN_NOTFOUND_EXCEPTIONS:
excString += ' in the namespace %s'%pformat(namespace)
raise NotFound(excString)
def _wrapNotFoundException(exc, fullName, namespace):
if not _ALLOW_WRAPPING_OF_NOTFOUND_EXCEPTIONS:
raise
else:
excStr = exc.args[0]
if excStr.find('while searching')==-1: # only wrap once!
excStr +=" while searching for '%s'"%fullName
if _INCLUDE_NAMESPACE_REPR_IN_NOTFOUND_EXCEPTIONS:
excStr += ' in the namespace %s'%pformat(namespace)
exc.args = (excStr,)
raise
def _isInstanceOrClass(obj):
if isinstance(obj, type):
# oldstyle
return True
if hasattr(obj, "__class__"):
# newstyle
if hasattr(obj, 'mro'):
# type/class
return True
elif (hasattr(obj, 'im_func') or hasattr(obj, 'func_code') or hasattr(obj, '__self__')):
# method, func, or builtin func
return False
elif hasattr(obj, '__init__'):
# instance
return True
return False
def hasKey(obj, key):
"""Determine if 'obj' has 'key' """
if hasattr(obj, 'has_key') and key in obj:
return True
elif hasattr(obj, key):
return True
else:
return False
def valueForKey(obj, key):
if hasattr(obj, 'has_key') and key in obj:
return obj[key]
elif hasattr(obj, key):
return getattr(obj, key)
else:
_raiseNotFoundException(key, obj)
def _valueForName(obj, name, executeCallables=False):
nameChunks=name.split('.')
for i in range(len(nameChunks)):
key = nameChunks[i]
if hasattr(obj, 'has_key') and key in obj:
nextObj = obj[key]
else:
try:
nextObj = getattr(obj, key)
except AttributeError:
_raiseNotFoundException(key, obj)
if executeCallables and hasattr(nextObj, '__call__') and not _isInstanceOrClass(nextObj):
obj = nextObj()
else:
obj = nextObj
return obj
def valueForName(obj, name, executeCallables=False):
try:
return _valueForName(obj, name, executeCallables)
except NotFound, e:
_wrapNotFoundException(e, fullName=name, namespace=obj)
def valueFromSearchList(searchList, name, executeCallables=False):
key = name.split('.')[0]
for namespace in searchList:
if hasKey(namespace, key):
return _valueForName(namespace, name,
executeCallables=executeCallables)
_raiseNotFoundException(key, searchList)
def _namespaces(callerFrame, searchList=None):
yield callerFrame.f_locals
if searchList:
for namespace in searchList:
yield namespace
yield callerFrame.f_globals
yield __builtins__
def valueFromFrameOrSearchList(searchList, name, executeCallables=False,
frame=None):
def __valueForName():
try:
return _valueForName(namespace, name, executeCallables=executeCallables)
except NotFound, e:
_wrapNotFoundException(e, fullName=name, namespace=searchList)
try:
if not frame:
frame = inspect.stack()[1][0]
key = name.split('.')[0]
for namespace in _namespaces(frame, searchList):
if hasKey(namespace, key):
return __valueForName()
_raiseNotFoundException(key, searchList)
finally:
del frame
def valueFromFrame(name, executeCallables=False, frame=None):
# @@TR consider implementing the C version the same way
# at the moment it provides a seperate but mirror implementation
# to valueFromFrameOrSearchList
try:
if not frame:
frame = inspect.stack()[1][0]
return valueFromFrameOrSearchList(searchList=None,
name=name,
executeCallables=executeCallables,
frame=frame)
finally:
del frame
def hasName(obj, name):
#Not in the C version
"""Determine if 'obj' has the 'name' """
key = name.split('.')[0]
if not hasKey(obj, key):
return False
try:
valueForName(obj, name)
return True
except NotFound:
return False
try:
from Cheetah._namemapper import NotFound, valueForKey, valueForName, \
valueFromSearchList, valueFromFrameOrSearchList, valueFromFrame
# it is possible with Jython or Windows, for example, that _namemapper.c hasn't been compiled
C_VERSION = True
except:
C_VERSION = False
##################################################
## CLASSES
class Mixin:
"""@@ document me"""
def valueForName(self, name):
return valueForName(self, name)
def valueForKey(self, key):
return valueForKey(self, key)
##################################################
## if run from the command line ##
def example():
class A(Mixin):
classVar = 'classVar val'
def method(self,arg='method 1 default arg'):
return arg
def method2(self, arg='meth 2 default arg'):
return {'item1':arg}
def method3(self, arg='meth 3 default'):
return arg
class B(A):
classBvar = 'classBvar val'
a = A()
a.one = 'valueForOne'
def function(whichOne='default'):
values = {
'default': 'default output',
'one': 'output option one',
'two': 'output option two'
}
return values[whichOne]
a.dic = {
'func': function,
'method': a.method3,
'item': 'itemval',
'subDict': {'nestedMethod':a.method3}
}
b = 'this is local b'
print(valueForKey(a.dic, 'subDict'))
print(valueForName(a, 'dic.item'))
print(valueForName(vars(), 'b'))
print(valueForName(__builtins__, 'dir')())
print(valueForName(vars(), 'a.classVar'))
print(valueForName(vars(), 'a.dic.func', executeCallables=True))
print(valueForName(vars(), 'a.method2.item1', executeCallables=True))
if __name__ == '__main__':
example()

File diff suppressed because it is too large Load Diff

View File

@ -1,51 +0,0 @@
Cheetah is an open source template engine and code generation tool.
It can be used standalone or combined with other tools and frameworks. Web
development is its principle use, but Cheetah is very flexible and is also being
used to generate C++ game code, Java, sql, form emails and even Python code.
Documentation
================================================================================
For a high-level introduction to Cheetah please refer to the User\'s Guide
at http://cheetahtemplate.org/learn.html
Mailing list
================================================================================
cheetahtemplate-discuss@lists.sourceforge.net
Subscribe at http://lists.sourceforge.net/lists/listinfo/cheetahtemplate-discuss
Credits
================================================================================
http://cheetahtemplate.org/credits.html
Praise
================================================================================
"I\'m enamored with Cheetah" - Sam Ruby, senior member of IBM Emerging
Technologies Group & director of Apache Software Foundation
"Give Cheetah a try. You won\'t regret it. ... Cheetah is a truly powerful
system. ... Cheetah is a serious contender for the 'best of breed' Python
templating." - Alex Martelli
"People with a strong PHP background absolutely love Cheetah for being Smarty,
but much, much better." - Marek Baczynski
"I am using Smarty and I know it very well, but compiled Cheetah Templates with
its inheritance approach is much powerful and easier to use than Smarty." -
Jaroslaw Zabiello
"There is no better solution than Cheetah" - Wilk
"A cheetah template can inherit from a python class, or a cheetah template, and
a Python class can inherit from a cheetah template. This brings the full power
of OO programming facilities to the templating system, and simply blows away
other templating systems" - Mike Meyer
"Cheetah has successfully been introduced as a replacement for the overweight
XSL Templates for code generation. Despite the power of XSL (and notably XPath
expressions), code generation is better suited to Cheetah as templates are much
easier to implement and manage." - The FEAR development team
(http://fear.sourceforge.net/docs/latest/guide/Build.html#id2550573)
"I\'ve used Cheetah quite a bit and it\'s a very good package" - Kevin Dangoor,
lead developer of TurboGears.

View File

@ -1,48 +0,0 @@
#!/usr/bin/env python
'''
Provides an abstract Servlet baseclass for Cheetah's Template class
'''
import sys
import os.path
class Servlet(object):
"""
This class is an abstract baseclass for Cheetah.Template.Template.
"""
transaction = None
application = None
request = None
session = None
def respond(self, trans=None):
raise NotImplementedError("""\
couldn't find the template's main method. If you are using #extends
without #implements, try adding '#implements respond' to your template
definition.""")
def sleep(self, transaction):
super(Servlet, self).sleep(transaction)
self.session = None
self.request = None
self._request = None
self.response = None
self.transaction = None
def shutdown(self):
pass
def serverSidePath(self, path=None,
normpath=os.path.normpath,
abspath=os.path.abspath
):
if path:
return normpath(abspath(path.replace("\\", '/')))
elif hasattr(self, '_filePath') and self._filePath:
return normpath(abspath(self._filePath))
else:
return None
# vim: shiftwidth=4 tabstop=4 expandtab

View File

@ -1,284 +0,0 @@
import sys
import os.path
import copy as copyModule
from ConfigParser import ConfigParser
import re
from tokenize import Intnumber, Floatnumber, Number
import types
import time
from StringIO import StringIO # not cStringIO because of unicode support
import imp # used by SettingsManager.updateSettingsFromPySrcFile()
numberRE = re.compile(Number)
complexNumberRE = re.compile('[\(]*' +Number + r'[ \t]*\+[ \t]*' + Number + '[\)]*')
##################################################
## FUNCTIONS ##
def mergeNestedDictionaries(dict1, dict2, copy=False, deepcopy=False):
"""Recursively merge the values of dict2 into dict1.
This little function is very handy for selectively overriding settings in a
settings dictionary that has a nested structure.
"""
if copy:
dict1 = copyModule.copy(dict1)
elif deepcopy:
dict1 = copyModule.deepcopy(dict1)
for key, val in dict2.iteritems():
if key in dict1 and isinstance(val, dict) and isinstance(dict1[key], dict):
dict1[key] = mergeNestedDictionaries(dict1[key], val)
else:
dict1[key] = val
return dict1
def stringIsNumber(S):
"""Return True if theString represents a Python number, False otherwise.
This also works for complex numbers and numbers with +/- in front."""
S = S.strip()
if S[0] in '-+' and len(S) > 1:
S = S[1:].strip()
match = complexNumberRE.match(S)
if not match:
match = numberRE.match(S)
if not match or (match.end() != len(S)):
return False
else:
return True
def convStringToNum(theString):
"""Convert a string representation of a Python number to the Python version"""
if not stringIsNumber(theString):
raise Error(theString + ' cannot be converted to a Python number')
return eval(theString, {}, {})
class Error(Exception):
pass
class NoDefault(object):
pass
class ConfigParserCaseSensitive(ConfigParser):
"""A case sensitive version of the standard Python ConfigParser."""
def optionxform(self, optionstr):
"""Don't change the case as is done in the default implemenation."""
return optionstr
class _SettingsCollector(object):
"""An abstract base class that provides the methods SettingsManager uses to
collect settings from config files and strings.
This class only collects settings it doesn't modify the _settings dictionary
of SettingsManager instances in any way.
"""
_ConfigParserClass = ConfigParserCaseSensitive
def readSettingsFromModule(self, mod, ignoreUnderscored=True):
"""Returns all settings from a Python module.
"""
S = {}
attrs = vars(mod)
for k, v in attrs.iteritems():
if (ignoreUnderscored and k.startswith('_')):
continue
else:
S[k] = v
return S
def readSettingsFromPySrcStr(self, theString):
"""Return a dictionary of the settings in a Python src string."""
globalsDict = {'True': (1==1),
'False': (0==1),
}
newSettings = {'self':self}
exec((theString+os.linesep), globalsDict, newSettings)
del newSettings['self']
module = types.ModuleType('temp_settings_module')
module.__dict__.update(newSettings)
return self.readSettingsFromModule(module)
def readSettingsFromConfigFileObj(self, inFile, convert=True):
"""Return the settings from a config file that uses the syntax accepted by
Python's standard ConfigParser module (like Windows .ini files).
NOTE:
this method maintains case unlike the ConfigParser module, unless this
class was initialized with the 'caseSensitive' keyword set to False.
All setting values are initially parsed as strings. However, If the
'convert' arg is True this method will do the following value
conversions:
* all Python numeric literals will be coverted from string to number
* The string 'None' will be converted to the Python value None
* The string 'True' will be converted to a Python truth value
* The string 'False' will be converted to a Python false value
* Any string starting with 'python:' will be treated as a Python literal
or expression that needs to be eval'd. This approach is useful for
declaring lists and dictionaries.
If a config section titled 'Globals' is present the options defined
under it will be treated as top-level settings.
"""
p = self._ConfigParserClass()
p.readfp(inFile)
sects = p.sections()
newSettings = {}
sects = p.sections()
newSettings = {}
for s in sects:
newSettings[s] = {}
for o in p.options(s):
if o != '__name__':
newSettings[s][o] = p.get(s, o)
## loop through new settings -> deal with global settings, numbers,
## booleans and None ++ also deal with 'importSettings' commands
for sect, subDict in newSettings.items():
for key, val in subDict.items():
if convert:
if val.lower().startswith('python:'):
subDict[key] = eval(val[7:], {}, {})
if val.lower() == 'none':
subDict[key] = None
if val.lower() == 'true':
subDict[key] = True
if val.lower() == 'false':
subDict[key] = False
if stringIsNumber(val):
subDict[key] = convStringToNum(val)
## now deal with any 'importSettings' commands
if key.lower() == 'importsettings':
if val.find(';') < 0:
importedSettings = self.readSettingsFromPySrcFile(val)
else:
path = val.split(';')[0]
rest = ''.join(val.split(';')[1:]).strip()
parentDict = self.readSettingsFromPySrcFile(path)
importedSettings = eval('parentDict["' + rest + '"]')
subDict.update(mergeNestedDictionaries(subDict,
importedSettings))
if sect.lower() == 'globals':
newSettings.update(newSettings[sect])
del newSettings[sect]
return newSettings
class SettingsManager(_SettingsCollector):
"""A mixin class that provides facilities for managing application settings.
SettingsManager is designed to work well with nested settings dictionaries
of any depth.
"""
def __init__(self):
super(SettingsManager, self).__init__()
self._settings = {}
self._initializeSettings()
def _defaultSettings(self):
return {}
def _initializeSettings(self):
"""A hook that allows for complex setting initialization sequences that
involve references to 'self' or other settings. For example:
self._settings['myCalcVal'] = self._settings['someVal'] * 15
This method should be called by the class' __init__() method when needed.
The dummy implementation should be reimplemented by subclasses.
"""
pass
## core post startup methods
def setting(self, name, default=NoDefault):
"""Get a setting from self._settings, with or without a default value."""
if default is NoDefault:
return self._settings[name]
else:
return self._settings.get(name, default)
def hasSetting(self, key):
"""True/False"""
return key in self._settings
def setSetting(self, name, value):
"""Set a setting in self._settings."""
self._settings[name] = value
def settings(self):
"""Return a reference to the settings dictionary"""
return self._settings
def copySettings(self):
"""Returns a shallow copy of the settings dictionary"""
return copyModule.copy(self._settings)
def deepcopySettings(self):
"""Returns a deep copy of the settings dictionary"""
return copyModule.deepcopy(self._settings)
def updateSettings(self, newSettings, merge=True):
"""Update the settings with a selective merge or a complete overwrite."""
if merge:
mergeNestedDictionaries(self._settings, newSettings)
else:
self._settings.update(newSettings)
## source specific update methods
def updateSettingsFromPySrcStr(self, theString, merge=True):
"""Update the settings from a code in a Python src string."""
newSettings = self.readSettingsFromPySrcStr(theString)
self.updateSettings(newSettings,
merge=newSettings.get('mergeSettings', merge) )
def updateSettingsFromConfigFileObj(self, inFile, convert=True, merge=True):
"""See the docstring for .updateSettingsFromConfigFile()
The caller of this method is responsible for closing the inFile file
object."""
newSettings = self.readSettingsFromConfigFileObj(inFile, convert=convert)
self.updateSettings(newSettings,
merge=newSettings.get('mergeSettings', merge))
def updateSettingsFromConfigStr(self, configStr, convert=True, merge=True):
"""See the docstring for .updateSettingsFromConfigFile()
"""
configStr = '[globals]\n' + configStr
inFile = StringIO(configStr)
newSettings = self.readSettingsFromConfigFileObj(inFile, convert=convert)
self.updateSettings(newSettings,
merge=newSettings.get('mergeSettings', merge))

View File

@ -1,267 +0,0 @@
"""SourceReader class for Cheetah's Parser and CodeGenerator
"""
import re
import sys
EOLre = re.compile(r'[ \f\t]*(?:\r\n|\r|\n)')
EOLZre = re.compile(r'(?:\r\n|\r|\n|\Z)')
ENCODINGsearch = re.compile("coding[=:]\s*([-\w.]+)").search
class Error(Exception):
pass
class SourceReader(object):
def __init__(self, src, filename=None, breakPoint=None, encoding=None):
self._src = src
self._filename = filename
self._srcLen = len(src)
if breakPoint == None:
self._breakPoint = self._srcLen
else:
self.setBreakPoint(breakPoint)
self._pos = 0
self._bookmarks = {}
self._posTobookmarkMap = {}
## collect some meta-information
self._EOLs = []
pos = 0
while pos < len(self):
EOLmatch = EOLZre.search(src, pos)
self._EOLs.append(EOLmatch.start())
pos = EOLmatch.end()
self._BOLs = []
for pos in self._EOLs:
BOLpos = self.findBOL(pos)
self._BOLs.append(BOLpos)
def src(self):
return self._src
def filename(self):
return self._filename
def __len__(self):
return self._breakPoint
def __getitem__(self, i):
if not isinstance(i, int):
self.checkPos(i.stop)
else:
self.checkPos(i)
return self._src[i]
def __getslice__(self, i, j):
i = max(i, 0); j = max(j, 0)
return self._src[i:j]
def splitlines(self):
if not hasattr(self, '_srcLines'):
self._srcLines = self._src.splitlines()
return self._srcLines
def lineNum(self, pos=None):
if pos == None:
pos = self._pos
for i in range(len(self._BOLs)):
if pos >= self._BOLs[i] and pos <= self._EOLs[i]:
return i
def getRowCol(self, pos=None):
if pos == None:
pos = self._pos
lineNum = self.lineNum(pos)
BOL, EOL = self._BOLs[lineNum], self._EOLs[lineNum]
return lineNum+1, pos-BOL+1
def getRowColLine(self, pos=None):
if pos == None:
pos = self._pos
row, col = self.getRowCol(pos)
return row, col, self.splitlines()[row-1]
def getLine(self, pos):
if pos == None:
pos = self._pos
lineNum = self.lineNum(pos)
return self.splitlines()[lineNum]
def pos(self):
return self._pos
def setPos(self, pos):
self.checkPos(pos)
self._pos = pos
def validPos(self, pos):
return pos <= self._breakPoint and pos >=0
def checkPos(self, pos):
if not pos <= self._breakPoint:
raise Error("pos (" + str(pos) + ") is invalid: beyond the stream's end (" +
str(self._breakPoint-1) + ")" )
elif not pos >=0:
raise Error("pos (" + str(pos) + ") is invalid: less than 0" )
def breakPoint(self):
return self._breakPoint
def setBreakPoint(self, pos):
if pos > self._srcLen:
raise Error("New breakpoint (" + str(pos) +
") is invalid: beyond the end of stream's source string (" +
str(self._srcLen) + ")" )
elif not pos >= 0:
raise Error("New breakpoint (" + str(pos) + ") is invalid: less than 0" )
self._breakPoint = pos
def setBookmark(self, name):
self._bookmarks[name] = self._pos
self._posTobookmarkMap[self._pos] = name
def hasBookmark(self, name):
return name in self._bookmarks
def gotoBookmark(self, name):
if not self.hasBookmark(name):
raise Error("Invalid bookmark (" + name + ") is invalid: does not exist")
pos = self._bookmarks[name]
if not self.validPos(pos):
raise Error("Invalid bookmark (" + name + ', '+
str(pos) + ") is invalid: pos is out of range" )
self._pos = pos
def atEnd(self):
return self._pos >= self._breakPoint
def atStart(self):
return self._pos == 0
def peek(self, offset=0):
self.checkPos(self._pos+offset)
pos = self._pos + offset
return self._src[pos]
def getc(self):
pos = self._pos
if self.validPos(pos+1):
self._pos += 1
return self._src[pos]
def ungetc(self, c=None):
if not self.atStart():
raise Error('Already at beginning of stream')
self._pos -= 1
if not c==None:
self._src[self._pos] = c
def advance(self, offset=1):
self.checkPos(self._pos + offset)
self._pos += offset
def rev(self, offset=1):
self.checkPos(self._pos - offset)
self._pos -= offset
def read(self, offset):
self.checkPos(self._pos + offset)
start = self._pos
self._pos += offset
return self._src[start:self._pos]
def readTo(self, to, start=None):
self.checkPos(to)
if start == None:
start = self._pos
self._pos = to
return self._src[start:to]
def readToEOL(self, start=None, gobble=True):
EOLmatch = EOLZre.search(self.src(), self.pos())
if gobble:
pos = EOLmatch.end()
else:
pos = EOLmatch.start()
return self.readTo(to=pos, start=start)
def find(self, it, pos=None):
if pos == None:
pos = self._pos
return self._src.find(it, pos )
def startswith(self, it, pos=None):
if self.find(it, pos) == self.pos():
return True
else:
return False
def rfind(self, it, pos):
if pos == None:
pos = self._pos
return self._src.rfind(it, pos)
def findBOL(self, pos=None):
if pos == None:
pos = self._pos
src = self.src()
return max(src.rfind('\n', 0, pos)+1, src.rfind('\r', 0, pos)+1, 0)
def findEOL(self, pos=None, gobble=False):
if pos == None:
pos = self._pos
match = EOLZre.search(self.src(), pos)
if gobble:
return match.end()
else:
return match.start()
def isLineClearToPos(self, pos=None):
if pos == None:
pos = self.pos()
self.checkPos(pos)
src = self.src()
BOL = self.findBOL()
return BOL == pos or src[BOL:pos].isspace()
def matches(self, strOrRE):
if isinstance(strOrRE, (str, unicode)):
return self.startswith(strOrRE, pos=self.pos())
else: # assume an re object
return strOrRE.match(self.src(), self.pos())
def matchWhiteSpace(self, WSchars=' \f\t'):
return (not self.atEnd()) and self.peek() in WSchars
def getWhiteSpace(self, max=None, WSchars=' \f\t'):
if not self.matchWhiteSpace(WSchars):
return ''
start = self.pos()
breakPoint = self.breakPoint()
if max is not None:
breakPoint = min(breakPoint, self.pos()+max)
while self.pos() < breakPoint:
self.advance()
if not self.matchWhiteSpace(WSchars):
break
return self.src()[start:self.pos()]
def matchNonWhiteSpace(self, WSchars=' \f\t\n\r'):
return self.atEnd() or not self.peek() in WSchars
def getNonWhiteSpace(self, WSchars=' \f\t\n\r'):
if not self.matchNonWhiteSpace(WSchars):
return ''
start = self.pos()
while self.pos() < self.breakPoint():
self.advance()
if not self.matchNonWhiteSpace(WSchars):
break
return self.src()[start:self.pos()]

File diff suppressed because it is too large Load Diff

View File

@ -1,107 +0,0 @@
# $Id: TemplateCmdLineIface.py,v 1.13 2006/01/10 20:34:35 tavis_rudd Exp $
"""Provides a command line interface to compiled Cheetah template modules.
Meta-Data
================================================================================
Author: Tavis Rudd <tavis@damnsimple.com>
Version: $Revision: 1.13 $
Start Date: 2001/12/06
Last Revision Date: $Date: 2006/01/10 20:34:35 $
"""
__author__ = "Tavis Rudd <tavis@damnsimple.com>"
__revision__ = "$Revision: 1.13 $"[11:-2]
import sys
import os
import getopt
import os.path
try:
from cPickle import load
except ImportError:
from pickle import load
from Cheetah.Version import Version
class Error(Exception):
pass
class CmdLineIface:
"""A command line interface to compiled Cheetah template modules."""
def __init__(self, templateObj,
scriptName=os.path.basename(sys.argv[0]),
cmdLineArgs=sys.argv[1:]):
self._template = templateObj
self._scriptName = scriptName
self._cmdLineArgs = cmdLineArgs
def run(self):
"""The main program controller."""
self._processCmdLineArgs()
print(self._template)
def _processCmdLineArgs(self):
try:
self._opts, self._args = getopt.getopt(
self._cmdLineArgs, 'h', ['help',
'env',
'pickle=',
])
except getopt.GetoptError, v:
# print help information and exit:
print(v)
print(self.usage())
sys.exit(2)
for o, a in self._opts:
if o in ('-h', '--help'):
print(self.usage())
sys.exit()
if o == '--env':
self._template.searchList().insert(0, os.environ)
if o == '--pickle':
if a == '-':
unpickled = load(sys.stdin)
self._template.searchList().insert(0, unpickled)
else:
f = open(a)
unpickled = load(f)
f.close()
self._template.searchList().insert(0, unpickled)
def usage(self):
return """Cheetah %(Version)s template module command-line interface
Usage
-----
%(scriptName)s [OPTION]
Options
-------
-h, --help Print this help information
--env Use shell ENVIRONMENT variables to fill the
$placeholders in the template.
--pickle <file> Use a variables from a dictionary stored in Python
pickle file to fill $placeholders in the template.
If <file> is - stdin is used:
'%(scriptName)s --pickle -'
Description
-----------
This interface allows you to execute a Cheetah template from the command line
and collect the output. It can prepend the shell ENVIRONMENT or a pickled
Python dictionary to the template's $placeholder searchList, overriding the
defaults for the $placeholders.
""" % {'scriptName': self._scriptName,
'Version': Version,
}
# vim: shiftwidth=4 tabstop=4 expandtab

View File

@ -1,272 +0,0 @@
"""A Skeleton HTML page template, that provides basic structure and utility methods.
"""
##################################################
## DEPENDENCIES
import sys
import os
import os.path
from os.path import getmtime, exists
import time
import types
import __builtin__
from Cheetah.Version import MinCompatibleVersion as RequiredCheetahVersion
from Cheetah.Version import MinCompatibleVersionTuple as RequiredCheetahVersionTuple
from Cheetah.Template import Template
from Cheetah.DummyTransaction import DummyTransaction
from Cheetah.NameMapper import NotFound, valueForName, valueFromSearchList, valueFromFrameOrSearchList
from Cheetah.CacheRegion import CacheRegion
import Cheetah.Filters as Filters
import Cheetah.ErrorCatchers as ErrorCatchers
from Cheetah.Templates._SkeletonPage import _SkeletonPage
##################################################
## MODULE CONSTANTS
try:
True, False
except NameError:
True, False = (1==1), (1==0)
VFFSL=valueFromFrameOrSearchList
VFSL=valueFromSearchList
VFN=valueForName
currentTime=time.time
__CHEETAH_version__ = '2.0rc6'
__CHEETAH_versionTuple__ = (2, 0, 0, 'candidate', 6)
__CHEETAH_genTime__ = 1139107954.3640411
__CHEETAH_genTimestamp__ = 'Sat Feb 4 18:52:34 2006'
__CHEETAH_src__ = 'src/Templates/SkeletonPage.tmpl'
__CHEETAH_srcLastModified__ = 'Mon Oct 7 11:37:30 2002'
__CHEETAH_docstring__ = 'Autogenerated by CHEETAH: The Python-Powered Template Engine'
if __CHEETAH_versionTuple__ < RequiredCheetahVersionTuple:
raise AssertionError(
'This template was compiled with Cheetah version'
' %s. Templates compiled before version %s must be recompiled.'%(
__CHEETAH_version__, RequiredCheetahVersion))
##################################################
## CLASSES
class SkeletonPage(_SkeletonPage):
##################################################
## CHEETAH GENERATED METHODS
def __init__(self, *args, **KWs):
_SkeletonPage.__init__(self, *args, **KWs)
if not self._CHEETAH__instanceInitialized:
cheetahKWArgs = {}
allowedKWs = 'searchList namespaces filter filtersLib errorCatcher'.split()
for k, v in KWs.items():
if k in allowedKWs: cheetahKWArgs[k] = v
self._initCheetahInstance(**cheetahKWArgs)
def writeHeadTag(self, **KWS):
## CHEETAH: generated from #block writeHeadTag at line 22, col 1.
trans = KWS.get("trans")
if (not trans and not self._CHEETAH__isBuffering and not hasattr(self.transaction, '__call__')):
trans = self.transaction # is None unless self.awake() was called
if not trans:
trans = DummyTransaction()
_dummyTrans = True
else: _dummyTrans = False
write = trans.response().write
SL = self._CHEETAH__searchList
_filter = self._CHEETAH__currentFilter
########################################
## START - generated method body
write('<head>\n<title>')
_v = VFFSL(SL, "title", True) # '$title' on line 24, col 8
if _v is not None: write(_filter(_v, rawExpr='$title')) # from line 24, col 8.
write('</title>\n')
_v = VFFSL(SL, "metaTags", True) # '$metaTags' on line 25, col 1
if _v is not None: write(_filter(_v, rawExpr='$metaTags')) # from line 25, col 1.
write(' \n')
_v = VFFSL(SL, "stylesheetTags", True) # '$stylesheetTags' on line 26, col 1
if _v is not None: write(_filter(_v, rawExpr='$stylesheetTags')) # from line 26, col 1.
write(' \n')
_v = VFFSL(SL, "javascriptTags", True) # '$javascriptTags' on line 27, col 1
if _v is not None: write(_filter(_v, rawExpr='$javascriptTags')) # from line 27, col 1.
write('\n</head>\n')
########################################
## END - generated method body
return _dummyTrans and trans.response().getvalue() or ""
def writeBody(self, **KWS):
## CHEETAH: generated from #block writeBody at line 36, col 1.
trans = KWS.get("trans")
if (not trans and not self._CHEETAH__isBuffering and not hasattr(self.transaction, '__call__')):
trans = self.transaction # is None unless self.awake() was called
if not trans:
trans = DummyTransaction()
_dummyTrans = True
else: _dummyTrans = False
write = trans.response().write
SL = self._CHEETAH__searchList
_filter = self._CHEETAH__currentFilter
########################################
## START - generated method body
write('This skeleton page has no flesh. Its body needs to be implemented.\n')
########################################
## END - generated method body
return _dummyTrans and trans.response().getvalue() or ""
def respond(self, trans=None):
## CHEETAH: main method generated for this template
if (not trans and not self._CHEETAH__isBuffering and not hasattr(self.transaction, '__call__')):
trans = self.transaction # is None unless self.awake() was called
if not trans:
trans = DummyTransaction()
_dummyTrans = True
else: _dummyTrans = False
write = trans.response().write
SL = self._CHEETAH__searchList
_filter = self._CHEETAH__currentFilter
########################################
## START - generated method body
## START CACHE REGION: ID=header. line 6, col 1 in the source.
_RECACHE_header = False
_cacheRegion_header = self.getCacheRegion(regionID='header', cacheInfo={'type': 2, 'id': 'header'})
if _cacheRegion_header.isNew():
_RECACHE_header = True
_cacheItem_header = _cacheRegion_header.getCacheItem('header')
if _cacheItem_header.hasExpired():
_RECACHE_header = True
if (not _RECACHE_header) and _cacheItem_header.getRefreshTime():
try:
_output = _cacheItem_header.renderOutput()
except KeyError:
_RECACHE_header = True
else:
write(_output)
del _output
if _RECACHE_header or not _cacheItem_header.getRefreshTime():
_orig_transheader = trans
trans = _cacheCollector_header = DummyTransaction()
write = _cacheCollector_header.response().write
_v = VFFSL(SL, "docType", True) # '$docType' on line 7, col 1
if _v is not None: write(_filter(_v, rawExpr='$docType')) # from line 7, col 1.
write('\n')
_v = VFFSL(SL, "htmlTag", True) # '$htmlTag' on line 8, col 1
if _v is not None: write(_filter(_v, rawExpr='$htmlTag')) # from line 8, col 1.
write('''
<!-- This document was autogenerated by Cheetah(http://CheetahTemplate.org).
Do not edit it directly!
Copyright ''')
_v = VFFSL(SL, "currentYr", True) # '$currentYr' on line 12, col 11
if _v is not None: write(_filter(_v, rawExpr='$currentYr')) # from line 12, col 11.
write(' - ')
_v = VFFSL(SL, "siteCopyrightName", True) # '$siteCopyrightName' on line 12, col 24
if _v is not None: write(_filter(_v, rawExpr='$siteCopyrightName')) # from line 12, col 24.
write(' - All Rights Reserved.\nFeel free to copy any javascript or html you like on this site,\nprovided you remove all links and/or references to ')
_v = VFFSL(SL, "siteDomainName", True) # '$siteDomainName' on line 14, col 52
if _v is not None: write(_filter(_v, rawExpr='$siteDomainName')) # from line 14, col 52.
write('''
However, please do not copy any content or images without permission.
''')
_v = VFFSL(SL, "siteCredits", True) # '$siteCredits' on line 17, col 1
if _v is not None: write(_filter(_v, rawExpr='$siteCredits')) # from line 17, col 1.
write('''
-->
''')
self.writeHeadTag(trans=trans)
write('\n')
trans = _orig_transheader
write = trans.response().write
_cacheData = _cacheCollector_header.response().getvalue()
_cacheItem_header.setData(_cacheData)
write(_cacheData)
del _cacheData
del _cacheCollector_header
del _orig_transheader
## END CACHE REGION: header
write('\n')
_v = VFFSL(SL, "bodyTag", True) # '$bodyTag' on line 34, col 1
if _v is not None: write(_filter(_v, rawExpr='$bodyTag')) # from line 34, col 1.
write('\n\n')
self.writeBody(trans=trans)
write('''
</body>
</html>
''')
########################################
## END - generated method body
return _dummyTrans and trans.response().getvalue() or ""
##################################################
## CHEETAH GENERATED ATTRIBUTES
_CHEETAH__instanceInitialized = False
_CHEETAH_version = __CHEETAH_version__
_CHEETAH_versionTuple = __CHEETAH_versionTuple__
_CHEETAH_genTime = __CHEETAH_genTime__
_CHEETAH_genTimestamp = __CHEETAH_genTimestamp__
_CHEETAH_src = __CHEETAH_src__
_CHEETAH_srcLastModified = __CHEETAH_srcLastModified__
_mainCheetahMethod_for_SkeletonPage= 'respond'
## END CLASS DEFINITION
if not hasattr(SkeletonPage, '_initCheetahAttributes'):
templateAPIClass = getattr(SkeletonPage, '_CHEETAH_templateClass', Template)
templateAPIClass._addCheetahPlumbingCodeToClass(SkeletonPage)
# CHEETAH was developed by Tavis Rudd and Mike Orr
# with code, advice and input from many other volunteers.
# For more information visit http://www.CheetahTemplate.org/
##################################################
## if run from command line:
if __name__ == '__main__':
from Cheetah.TemplateCmdLineIface import CmdLineIface
CmdLineIface(templateObj=SkeletonPage()).run()

View File

@ -1,44 +0,0 @@
##doc-module: A Skeleton HTML page template, that provides basic structure and utility methods.
################################################################################
#extends Cheetah.Templates._SkeletonPage
#implements respond
################################################################################
#cache id='header'
$docType
$htmlTag
<!-- This document was autogenerated by Cheetah(http://CheetahTemplate.org).
Do not edit it directly!
Copyright $currentYr - $siteCopyrightName - All Rights Reserved.
Feel free to copy any javascript or html you like on this site,
provided you remove all links and/or references to $siteDomainName
However, please do not copy any content or images without permission.
$siteCredits
-->
#block writeHeadTag
<head>
<title>$title</title>
$metaTags
$stylesheetTags
$javascriptTags
</head>
#end block writeHeadTag
#end cache header
#################
$bodyTag
#block writeBody
This skeleton page has no flesh. Its body needs to be implemented.
#end block writeBody
</body>
</html>

View File

@ -1,215 +0,0 @@
# $Id: _SkeletonPage.py,v 1.13 2002/10/01 17:52:02 tavis_rudd Exp $
"""A baseclass for the SkeletonPage template
Meta-Data
==========
Author: Tavis Rudd <tavis@damnsimple.com>,
Version: $Revision: 1.13 $
Start Date: 2001/04/05
Last Revision Date: $Date: 2002/10/01 17:52:02 $
"""
__author__ = "Tavis Rudd <tavis@damnsimple.com>"
__revision__ = "$Revision: 1.13 $"[11:-2]
##################################################
## DEPENDENCIES ##
import time, types, os, sys
# intra-package imports ...
from Cheetah.Template import Template
##################################################
## GLOBALS AND CONSTANTS ##
True = (1==1)
False = (0==1)
##################################################
## CLASSES ##
class _SkeletonPage(Template):
"""A baseclass for the SkeletonPage template"""
docType = '<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" ' + \
'"http://www.w3.org/TR/html4/loose.dtd">'
# docType = '<!DOCTYPE HTML PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" ' + \
#'"http://www.w3.org/TR/xhtml1l/DTD/transitional.dtd">'
title = ''
siteDomainName = 'www.example.com'
siteCredits = 'Designed & Implemented by Tavis Rudd'
siteCopyrightName = "Tavis Rudd"
htmlTag = '<html>'
def __init__(self, *args, **KWs):
Template.__init__(self, *args, **KWs)
self._metaTags = {'HTTP-EQUIV':{'keywords': 'Cheetah',
'Content-Type': 'text/html; charset=iso-8859-1',
},
'NAME':{'generator':'Cheetah: The Python-Powered Template Engine'}
}
# metaTags = {'HTTP_EQUIV':{'test':1234}, 'NAME':{'test':1234,'test2':1234} }
self._stylesheets = {}
# stylesheets = {'.cssClassName':'stylesheetCode'}
self._stylesheetsOrder = []
# stylesheetsOrder = ['.cssClassName',]
self._stylesheetLibs = {}
# stylesheetLibs = {'libName':'libSrcPath'}
self._javascriptLibs = {}
self._javascriptTags = {}
# self._javascriptLibs = {'libName':'libSrcPath'}
self._bodyTagAttribs = {}
def metaTags(self):
"""Return a formatted vesion of the self._metaTags dictionary, using the
formatMetaTags function from Cheetah.Macros.HTML"""
return self.formatMetaTags(self._metaTags)
def stylesheetTags(self):
"""Return a formatted version of the self._stylesheetLibs and
self._stylesheets dictionaries. The keys in self._stylesheets must
be listed in the order that they should appear in the list
self._stylesheetsOrder, to ensure that the style rules are defined in
the correct order."""
stylesheetTagsTxt = ''
for title, src in self._stylesheetLibs.items():
stylesheetTagsTxt += '<link rel="stylesheet" type="text/css" href="' + str(src) + '" />\n'
if not self._stylesheetsOrder:
return stylesheetTagsTxt
stylesheetTagsTxt += '<style type="text/css"><!--\n'
for identifier in self._stylesheetsOrder:
if identifier not in self._stylesheets:
warning = '# the identifier ' + identifier + \
'was in stylesheetsOrder, but not in stylesheets'
print(warning)
stylesheetTagsTxt += warning
continue
attribsDict = self._stylesheets[identifier]
cssCode = ''
attribCode = ''
for k, v in attribsDict.items():
attribCode += str(k) + ': ' + str(v) + '; '
attribCode = attribCode[:-2] # get rid of the last semicolon
cssCode = '\n' + identifier + ' {' + attribCode + '}'
stylesheetTagsTxt += cssCode
stylesheetTagsTxt += '\n//--></style>\n'
return stylesheetTagsTxt
def javascriptTags(self):
"""Return a formatted version of the javascriptTags and
javascriptLibs dictionaries. Each value in javascriptTags
should be a either a code string to include, or a list containing the
JavaScript version number and the code string. The keys can be anything.
The same applies for javascriptLibs, but the string should be the
SRC filename rather than a code string."""
javascriptTagsTxt = []
for key, details in self._javascriptTags.iteritems():
if not isinstance(details, (list, tuple)):
details = ['', details]
javascriptTagsTxt += ['<script language="JavaScript', str(details[0]),
'" type="text/javascript"><!--\n',
str(details[0]), '\n//--></script>\n']
for key, details in self._javascriptLibs.iteritems():
if not isinstance(details, (list, tuple)):
details = ['', details]
javascriptTagsTxt += ['<script language="JavaScript', str(details[0]),
'" type="text/javascript" src="',
str(details[1]), '" />\n']
return ''.join(javascriptTagsTxt)
def bodyTag(self):
"""Create a body tag from the entries in the dict bodyTagAttribs."""
return self.formHTMLTag('body', self._bodyTagAttribs)
def imgTag(self, src, alt='', width=None, height=None, border=0):
"""Dynamically generate an image tag. Cheetah will try to convert the
src argument to a WebKit serverSidePath relative to the servlet's
location. If width and height aren't specified they are calculated using
PIL or ImageMagick if available."""
src = self.normalizePath(src)
if not width or not height:
try: # see if the dimensions can be calc'd with PIL
import Image
im = Image.open(src)
calcWidth, calcHeight = im.size
del im
if not width: width = calcWidth
if not height: height = calcHeight
except:
try: # try imageMagick instead
calcWidth, calcHeight = os.popen(
'identify -format "%w,%h" ' + src).read().split(',')
if not width: width = calcWidth
if not height: height = calcHeight
except:
pass
if width and height:
return ''.join(['<img src="', src, '" width="', str(width), '" height="', str(height),
'" alt="', alt, '" border="', str(border), '" />'])
elif width:
return ''.join(['<img src="', src, '" width="', str(width),
'" alt="', alt, '" border="', str(border), '" />'])
elif height:
return ''.join(['<img src="', src, '" height="', str(height),
'" alt="', alt, '" border="', str(border), '" />'])
else:
return ''.join(['<img src="', src, '" alt="', alt, '" border="', str(border), '" />'])
def currentYr(self):
"""Return a string representing the current yr."""
return time.strftime("%Y", time.localtime(time.time()))
def currentDate(self, formatString="%b %d, %Y"):
"""Return a string representing the current localtime."""
return time.strftime(formatString, time.localtime(time.time()))
def spacer(self, width=1,height=1):
return '<img src="spacer.gif" width="%s" height="%s" alt="" />'% (str(width), str(height))
def formHTMLTag(self, tagName, attributes={}):
"""returns a string containing an HTML <tag> """
tagTxt = ['<', tagName.lower()]
for name, val in attributes.items():
tagTxt += [' ', name.lower(), '="', str(val), '"']
tagTxt.append('>')
return ''.join(tagTxt)
def formatMetaTags(self, metaTags):
"""format a dict of metaTag definitions into an HTML version"""
metaTagsTxt = []
if 'HTTP-EQUIV' in metaTags:
for http_equiv, contents in metaTags['HTTP-EQUIV'].items():
metaTagsTxt += ['<meta http-equiv="', str(http_equiv), '" content="',
str(contents), '" />\n']
if 'NAME' in metaTags:
for name, contents in metaTags['NAME'].items():
metaTagsTxt += ['<meta name="', str(name), '" content="', str(contents),
'" />\n']
return ''.join(metaTagsTxt)

View File

@ -1 +0,0 @@

View File

@ -1,29 +0,0 @@
#!/usr/bin/env python
import unittest
from Cheetah import DirectiveAnalyzer
class AnalyzerTests(unittest.TestCase):
def test_set(self):
template = '''
#set $foo = "bar"
Hello ${foo}!
'''
calls = DirectiveAnalyzer.analyze(template)
self.assertEquals(1, calls.get('set'))
def test_compilersettings(self):
template = '''
#compiler-settings
useNameMapper = False
#end compiler-settings
'''
calls = DirectiveAnalyzer.analyze(template)
self.assertEquals(1, calls.get('compiler-settings'))
if __name__ == '__main__':
unittest.main()

View File

@ -1,579 +0,0 @@
#!/usr/bin/env python
'''
Tests for the 'cheetah' command.
Besides unittest usage, recognizes the following command-line options:
--list CheetahWrapper.py
List all scenarios that are tested. The argument is the path
of this script.
--nodelete
Don't delete scratch directory at end.
--output
Show the output of each subcommand. (Normally suppressed.)
'''
import os
import os.path
import pdb
import re # Used by listTests.
import shutil
import sys
import tempfile
import unittest
from optparse import OptionParser
from Cheetah.CheetahWrapper import CheetahWrapper # Used by NoBackup.
try:
from subprocess import Popen, PIPE, STDOUT
class Popen4(Popen):
def __init__(self, cmd, bufsize=-1, shell=True, close_fds=True,
stdin=PIPE, stdout=PIPE, stderr=STDOUT, **kwargs):
super(Popen4, self).__init__(cmd, bufsize=bufsize, shell=shell,
close_fds=close_fds, stdin=stdin, stdout=stdout,
stderr=stderr, **kwargs)
self.tochild = self.stdin
self.fromchild = self.stdout
self.childerr = self.stderr
except ImportError:
from popen2 import Popen4
DELETE = True # True to clean up after ourselves, False for debugging.
OUTPUT = False # Normally False, True for debugging.
BACKUP_SUFFIX = CheetahWrapper.BACKUP_SUFFIX
def warn(msg):
sys.stderr.write(msg + '\n')
class CFBase(unittest.TestCase):
"""Base class for "cheetah compile" and "cheetah fill" unit tests.
"""
srcDir = '' # Nonblank to create source directory.
subdirs = ('child', 'child/grandkid') # Delete in reverse order.
srcFiles = ('a.tmpl', 'child/a.tmpl', 'child/grandkid/a.tmpl')
expectError = False # Used by --list option.
def inform(self, message):
if self.verbose:
print(message)
def setUp(self):
"""Create the top-level directories, subdirectories and .tmpl
files.
"""
self.cmd = self.locate_cheetah('cheetah')
pythonPath = os.getcwd()
if not os.environ.get('PYTHONPATH'):
os.environ['PYTHONPATH'] = pythonPath
else:
os.environ['PYTHONPATH'] = '%s:%s' % (os.environ['PYTHONPATH'], pythonPath)
I = self.inform
# Step 1: Create the scratch directory and chdir into it.
self.scratchDir = scratchDir = tempfile.mktemp()
os.mkdir(scratchDir)
self.origCwd = os.getcwd()
os.chdir(scratchDir)
if self.srcDir:
os.mkdir(self.srcDir)
# Step 2: Create source subdirectories.
for dir in self.subdirs:
os.mkdir(dir)
# Step 3: Create the .tmpl files, each in its proper directory.
for fil in self.srcFiles:
f = open(fil, 'w')
f.write("Hello, world!\n")
f.close()
def tearDown(self):
os.chdir(self.origCwd)
if DELETE:
shutil.rmtree(self.scratchDir, True) # Ignore errors.
if os.path.exists(self.scratchDir):
warn("Warning: unable to delete scratch directory %s")
else:
warn("Warning: not deleting scratch directory %s" % self.scratchDir)
def _checkDestFileHelper(self, path, expected,
allowSurroundingText, errmsg):
"""Low-level helper to check a destination file.
in : path, string, the destination path.
expected, string, the expected contents.
allowSurroundingtext, bool, allow the result to contain
additional text around the 'expected' substring?
errmsg, string, the error message. It may contain the
following "%"-operator keys: path, expected, result.
out: None
"""
path = os.path.abspath(path)
exists = os.path.exists(path)
msg = "destination file missing: %s" % path
self.failUnless(exists, msg)
f = open(path, 'r')
result = f.read()
f.close()
if allowSurroundingText:
success = result.find(expected) != -1
else:
success = result == expected
msg = errmsg % locals()
self.failUnless(success, msg)
def checkCompile(self, path):
# Raw string to prevent "\n" from being converted to a newline.
#expected = R"write('Hello, world!\n')"
expected = "Hello, world!" # might output a u'' string
errmsg = """\
destination file %(path)s doesn't contain expected substring:
%(expected)r"""
self._checkDestFileHelper(path, expected, True, errmsg)
def checkFill(self, path):
expected = "Hello, world!\n"
errmsg = """\
destination file %(path)s contains wrong result.
Expected %(expected)r
Found %(result)r"""
self._checkDestFileHelper(path, expected, False, errmsg)
def checkSubdirPyInit(self, path):
"""Verify a destination subdirectory exists and contains an
__init__.py file.
"""
exists = os.path.exists(path)
msg = "destination subdirectory %s misssing" % path
self.failUnless(exists, msg)
initPath = os.path.join(path, "__init__.py")
exists = os.path.exists(initPath)
msg = "destination init file missing: %s" % initPath
self.failUnless(exists, msg)
def checkNoBackup(self, path):
"""Verify 'path' does not exist. (To check --nobackup.)
"""
exists = os.path.exists(path)
msg = "backup file exists in spite of --nobackup: %s" % path
self.failIf(exists, msg)
def locate_cheetah(self, cmd):
paths = os.getenv('PATH')
if not paths:
return cmd
parts = cmd.split(' ')
paths = paths.split(':')
for p in paths:
p = os.path.join(p, cmd)
p = os.path.abspath(p)
if os.path.isfile(p):
return p
return cmd
def assertWin32Subprocess(self, cmd):
_in, _out = os.popen4(cmd)
_in.close()
output = _out.read()
rc = _out.close()
if rc is None:
rc = 0
return rc, output
def assertPosixSubprocess(self, cmd):
cmd = cmd.replace('cheetah', self.cmd)
process = Popen4(cmd, env=os.environ)
process.tochild.close()
output = process.fromchild.read()
status = process.wait()
process.fromchild.close()
return status, output
def assertSubprocess(self, cmd, nonzero=False):
status, output = None, None
if sys.platform == 'win32':
status, output = self.assertWin32Subprocess(cmd)
else:
status, output = self.assertPosixSubprocess(cmd)
if not nonzero:
self.failUnlessEqual(status, 0, '''Subprocess exited with a non-zero status (%d)
%s''' % (status, output))
else:
self.failIfEqual(status, 0, '''Subprocess exited with a zero status (%d)
%s''' % (status, output))
return output
def go(self, cmd, expectedStatus=0, expectedOutputSubstring=None):
"""Run a "cheetah compile" or "cheetah fill" subcommand.
in : cmd, string, the command to run.
expectedStatus, int, subcommand's expected output status.
0 if the subcommand is expected to succeed, 1-255 otherwise.
expectedOutputSubstring, string, substring which much appear
in the standard output or standard error. None to skip this
test.
out: None.
"""
output = self.assertSubprocess(cmd)
if expectedOutputSubstring is not None:
msg = "substring %r not found in subcommand output: %s" % \
(expectedOutputSubstring, cmd)
substringTest = output.find(expectedOutputSubstring) != -1
self.failUnless(substringTest, msg)
class CFIdirBase(CFBase):
"""Subclass for tests with --idir.
"""
srcDir = 'SRC'
subdirs = ('SRC/child', 'SRC/child/grandkid') # Delete in reverse order.
srcFiles = ('SRC/a.tmpl', 'SRC/child/a.tmpl', 'SRC/child/grandkid/a.tmpl')
##################################################
## TEST CASE CLASSES
class OneFile(CFBase):
def testCompile(self):
self.go("cheetah compile a.tmpl")
self.checkCompile("a.py")
def testFill(self):
self.go("cheetah fill a.tmpl")
self.checkFill("a.html")
def testText(self):
self.go("cheetah fill --oext txt a.tmpl")
self.checkFill("a.txt")
class OneFileNoExtension(CFBase):
def testCompile(self):
self.go("cheetah compile a")
self.checkCompile("a.py")
def testFill(self):
self.go("cheetah fill a")
self.checkFill("a.html")
def testText(self):
self.go("cheetah fill --oext txt a")
self.checkFill("a.txt")
class SplatTmpl(CFBase):
def testCompile(self):
self.go("cheetah compile *.tmpl")
self.checkCompile("a.py")
def testFill(self):
self.go("cheetah fill *.tmpl")
self.checkFill("a.html")
def testText(self):
self.go("cheetah fill --oext txt *.tmpl")
self.checkFill("a.txt")
class ThreeFilesWithSubdirectories(CFBase):
def testCompile(self):
self.go("cheetah compile a.tmpl child/a.tmpl child/grandkid/a.tmpl")
self.checkCompile("a.py")
self.checkCompile("child/a.py")
self.checkCompile("child/grandkid/a.py")
def testFill(self):
self.go("cheetah fill a.tmpl child/a.tmpl child/grandkid/a.tmpl")
self.checkFill("a.html")
self.checkFill("child/a.html")
self.checkFill("child/grandkid/a.html")
def testText(self):
self.go("cheetah fill --oext txt a.tmpl child/a.tmpl child/grandkid/a.tmpl")
self.checkFill("a.txt")
self.checkFill("child/a.txt")
self.checkFill("child/grandkid/a.txt")
class ThreeFilesWithSubdirectoriesNoExtension(CFBase):
def testCompile(self):
self.go("cheetah compile a child/a child/grandkid/a")
self.checkCompile("a.py")
self.checkCompile("child/a.py")
self.checkCompile("child/grandkid/a.py")
def testFill(self):
self.go("cheetah fill a child/a child/grandkid/a")
self.checkFill("a.html")
self.checkFill("child/a.html")
self.checkFill("child/grandkid/a.html")
def testText(self):
self.go("cheetah fill --oext txt a child/a child/grandkid/a")
self.checkFill("a.txt")
self.checkFill("child/a.txt")
self.checkFill("child/grandkid/a.txt")
class SplatTmplWithSubdirectories(CFBase):
def testCompile(self):
self.go("cheetah compile *.tmpl child/*.tmpl child/grandkid/*.tmpl")
self.checkCompile("a.py")
self.checkCompile("child/a.py")
self.checkCompile("child/grandkid/a.py")
def testFill(self):
self.go("cheetah fill *.tmpl child/*.tmpl child/grandkid/*.tmpl")
self.checkFill("a.html")
self.checkFill("child/a.html")
self.checkFill("child/grandkid/a.html")
def testText(self):
self.go("cheetah fill --oext txt *.tmpl child/*.tmpl child/grandkid/*.tmpl")
self.checkFill("a.txt")
self.checkFill("child/a.txt")
self.checkFill("child/grandkid/a.txt")
class OneFileWithOdir(CFBase):
def testCompile(self):
self.go("cheetah compile --odir DEST a.tmpl")
self.checkSubdirPyInit("DEST")
self.checkCompile("DEST/a.py")
def testFill(self):
self.go("cheetah fill --odir DEST a.tmpl")
self.checkFill("DEST/a.html")
def testText(self):
self.go("cheetah fill --odir DEST --oext txt a.tmpl")
self.checkFill("DEST/a.txt")
class VarietyWithOdir(CFBase):
def testCompile(self):
self.go("cheetah compile --odir DEST a.tmpl child/a child/grandkid/*.tmpl")
self.checkSubdirPyInit("DEST")
self.checkSubdirPyInit("DEST/child")
self.checkSubdirPyInit("DEST/child/grandkid")
self.checkCompile("DEST/a.py")
self.checkCompile("DEST/child/a.py")
self.checkCompile("DEST/child/grandkid/a.py")
def testFill(self):
self.go("cheetah fill --odir DEST a.tmpl child/a child/grandkid/*.tmpl")
self.checkFill("DEST/a.html")
self.checkFill("DEST/child/a.html")
self.checkFill("DEST/child/grandkid/a.html")
def testText(self):
self.go("cheetah fill --odir DEST --oext txt a.tmpl child/a child/grandkid/*.tmpl")
self.checkFill("DEST/a.txt")
self.checkFill("DEST/child/a.txt")
self.checkFill("DEST/child/grandkid/a.txt")
class RecurseExplicit(CFBase):
def testCompile(self):
self.go("cheetah compile -R child")
self.checkCompile("child/a.py")
self.checkCompile("child/grandkid/a.py")
def testFill(self):
self.go("cheetah fill -R child")
self.checkFill("child/a.html")
self.checkFill("child/grandkid/a.html")
def testText(self):
self.go("cheetah fill -R --oext txt child")
self.checkFill("child/a.txt")
self.checkFill("child/grandkid/a.txt")
class RecurseImplicit(CFBase):
def testCompile(self):
self.go("cheetah compile -R")
self.checkCompile("child/a.py")
self.checkCompile("child/grandkid/a.py")
def testFill(self):
self.go("cheetah fill -R")
self.checkFill("a.html")
self.checkFill("child/a.html")
self.checkFill("child/grandkid/a.html")
def testText(self):
self.go("cheetah fill -R --oext txt")
self.checkFill("a.txt")
self.checkFill("child/a.txt")
self.checkFill("child/grandkid/a.txt")
class RecurseExplicitWIthOdir(CFBase):
def testCompile(self):
self.go("cheetah compile -R --odir DEST child")
self.checkSubdirPyInit("DEST/child")
self.checkSubdirPyInit("DEST/child/grandkid")
self.checkCompile("DEST/child/a.py")
self.checkCompile("DEST/child/grandkid/a.py")
def testFill(self):
self.go("cheetah fill -R --odir DEST child")
self.checkFill("DEST/child/a.html")
self.checkFill("DEST/child/grandkid/a.html")
def testText(self):
self.go("cheetah fill -R --odir DEST --oext txt child")
self.checkFill("DEST/child/a.txt")
self.checkFill("DEST/child/grandkid/a.txt")
class Flat(CFBase):
def testCompile(self):
self.go("cheetah compile --flat child/a.tmpl")
self.checkCompile("a.py")
def testFill(self):
self.go("cheetah fill --flat child/a.tmpl")
self.checkFill("a.html")
def testText(self):
self.go("cheetah fill --flat --oext txt child/a.tmpl")
self.checkFill("a.txt")
class FlatRecurseCollision(CFBase):
expectError = True
def testCompile(self):
self.assertSubprocess("cheetah compile -R --flat", nonzero=True)
def testFill(self):
self.assertSubprocess("cheetah fill -R --flat", nonzero=True)
def testText(self):
self.assertSubprocess("cheetah fill -R --flat", nonzero=True)
class IdirRecurse(CFIdirBase):
def testCompile(self):
self.go("cheetah compile -R --idir SRC child")
self.checkSubdirPyInit("child")
self.checkSubdirPyInit("child/grandkid")
self.checkCompile("child/a.py")
self.checkCompile("child/grandkid/a.py")
def testFill(self):
self.go("cheetah fill -R --idir SRC child")
self.checkFill("child/a.html")
self.checkFill("child/grandkid/a.html")
def testText(self):
self.go("cheetah fill -R --idir SRC --oext txt child")
self.checkFill("child/a.txt")
self.checkFill("child/grandkid/a.txt")
class IdirOdirRecurse(CFIdirBase):
def testCompile(self):
self.go("cheetah compile -R --idir SRC --odir DEST child")
self.checkSubdirPyInit("DEST/child")
self.checkSubdirPyInit("DEST/child/grandkid")
self.checkCompile("DEST/child/a.py")
self.checkCompile("DEST/child/grandkid/a.py")
def testFill(self):
self.go("cheetah fill -R --idir SRC --odir DEST child")
self.checkFill("DEST/child/a.html")
self.checkFill("DEST/child/grandkid/a.html")
def testText(self):
self.go("cheetah fill -R --idir SRC --odir DEST --oext txt child")
self.checkFill("DEST/child/a.txt")
self.checkFill("DEST/child/grandkid/a.txt")
class IdirFlatRecurseCollision(CFIdirBase):
expectError = True
def testCompile(self):
self.assertSubprocess("cheetah compile -R --flat --idir SRC", nonzero=True)
def testFill(self):
self.assertSubprocess("cheetah fill -R --flat --idir SRC", nonzero=True)
def testText(self):
self.assertSubprocess("cheetah fill -R --flat --idir SRC --oext txt", nonzero=True)
class NoBackup(CFBase):
"""Run the command twice each time and verify a backup file is
*not* created.
"""
def testCompile(self):
self.go("cheetah compile --nobackup a.tmpl")
self.go("cheetah compile --nobackup a.tmpl")
self.checkNoBackup("a.py" + BACKUP_SUFFIX)
def testFill(self):
self.go("cheetah fill --nobackup a.tmpl")
self.go("cheetah fill --nobackup a.tmpl")
self.checkNoBackup("a.html" + BACKUP_SUFFIX)
def testText(self):
self.go("cheetah fill --nobackup --oext txt a.tmpl")
self.go("cheetah fill --nobackup --oext txt a.tmpl")
self.checkNoBackup("a.txt" + BACKUP_SUFFIX)
def listTests(cheetahWrapperFile):
"""cheetahWrapperFile, string, path of this script.
XXX TODO: don't print test where expectError is true.
"""
rx = re.compile( R'self\.go\("(.*?)"\)' )
f = open(cheetahWrapperFile)
while True:
lin = f.readline()
if not lin:
break
m = rx.search(lin)
if m:
print(m.group(1))
f.close()
def main():
global DELETE, OUTPUT
parser = OptionParser()
parser.add_option("--list", action="store", dest="listTests")
parser.add_option("--nodelete", action="store_true")
parser.add_option("--output", action="store_true")
# The following options are passed to unittest.
parser.add_option("-e", "--explain", action="store_true")
parser.add_option("-v", "--verbose", action="store_true")
parser.add_option("-q", "--quiet", action="store_true")
opts, files = parser.parse_args()
if opts.nodelete:
DELETE = False
if opts.output:
OUTPUT = True
if opts.listTests:
listTests(opts.listTests)
else:
# Eliminate script-specific command-line arguments to prevent
# errors in unittest.
del sys.argv[1:]
for opt in ("explain", "verbose", "quiet"):
if getattr(opts, opt):
sys.argv.append("--" + opt)
sys.argv.extend(files)
unittest.main()
if __name__ == '__main__':
main()
# vim: sw=4 ts=4 expandtab

View File

@ -1,39 +0,0 @@
#!/usr/bin/env python
import unittest
import Cheetah
import Cheetah.Parser
import Cheetah.Template
class Chep_2_Conditionalized_Import_Behavior(unittest.TestCase):
def test_ModuleLevelImport(self):
''' Verify module level (traditional) import behavior '''
pass
def test_InlineImport(self):
''' Verify (new) inline import behavior works '''
template = '''
#def funky($s)
#try
#import urllib
#except ImportError
#pass
#end try
#return urllib.quote($s)
#end def
'''
try:
template = Cheetah.Template.Template.compile(template)
except Cheetah.Parser.ParseError, ex:
self.fail('Failed to properly generate code %s' % ex)
template = template()
rc = tepmlate.funky('abc def')
assert rc == 'abc+def'
def test_LegacyMode(self):
''' Verify disabling of CHEP #2 works '''
pass
if __name__ == '__main__':
unittest.main()

View File

@ -1,70 +0,0 @@
#!/usr/bin/env python
import sys
import unittest
import Cheetah.Template
import Cheetah.Filters
majorVer, minorVer = sys.version_info[0], sys.version_info[1]
versionTuple = (majorVer, minorVer)
class BasicMarkdownFilterTest(unittest.TestCase):
'''
Test that our markdown filter works
'''
def test_BasicHeader(self):
template = '''
#from Cheetah.Filters import Markdown
#transform Markdown
$foo
Header
======
'''
expected = '''<p>bar</p>
<h1>Header</h1>'''
try:
template = Cheetah.Template.Template(template, searchList=[{'foo' : 'bar'}])
template = str(template)
assert template == expected
except ImportError, ex:
print('>>> We probably failed to import markdown, bummer %s' % ex)
return
except Exception, ex:
if ex.__class__.__name__ == 'MarkdownException' and majorVer == 2 and minorVer < 5:
print('>>> NOTE: Support for the Markdown filter will be broken for you. Markdown says: %s' % ex)
return
raise
class BasicCodeHighlighterFilterTest(unittest.TestCase):
'''
Test that our code highlighter filter works
'''
def test_Python(self):
template = '''
#from Cheetah.Filters import CodeHighlighter
#transform CodeHighlighter
def foo(self):
return '$foo'
'''
template = Cheetah.Template.Template(template, searchList=[{'foo' : 'bar'}])
template = str(template)
assert template, (template, 'We should have some content here...')
def test_Html(self):
template = '''
#from Cheetah.Filters import CodeHighlighter
#transform CodeHighlighter
<html><head></head><body>$foo</body></html>
'''
template = Cheetah.Template.Template(template, searchList=[{'foo' : 'bar'}])
template = str(template)
assert template, (template, 'We should have some content here...')
if __name__ == '__main__':
unittest.main()

View File

@ -1,20 +0,0 @@
#!/usr/bin/env python
import unittest
from Cheetah import SettingsManager
class SettingsManagerTests(unittest.TestCase):
def test_mergeDictionaries(self):
left = {'foo' : 'bar', 'abc' : {'a' : 1, 'b' : 2, 'c' : (3,)}}
right = {'xyz' : (10, 9)}
expect = {'xyz': (10, 9), 'foo': 'bar', 'abc': {'a': 1, 'c': (3,), 'b': 2}}
result = SettingsManager.mergeNestedDictionaries(left, right)
self.assertEquals(result, expect)
if __name__ == '__main__':
unittest.main()

View File

@ -1,548 +0,0 @@
#!/usr/bin/env python
import sys
import types
import os
import os.path
import unittest
from Cheetah.NameMapper import NotFound, valueForKey, \
valueForName, valueFromSearchList, valueFromFrame, valueFromFrameOrSearchList
class DummyClass(object):
classVar1 = 123
def __init__(self):
self.instanceVar1 = 123
def __str__(self):
return 'object'
def meth(self, arg="arff"):
return str(arg)
def meth1(self, arg="doo"):
return arg
def meth2(self, arg1="a1", arg2="a2"):
raise ValueError
def meth3(self):
"""Tests a bug that Jeff Johnson reported on Oct 1, 2001"""
x = 'A string'
try:
for i in [1, 2, 3, 4]:
if x == 2:
pass
if x == 'xx':
pass
return x
except:
raise
class DummyClassGetAttrRaises(object):
def __getattr__(self, name):
raise ValueError
def dummyFunc(arg="Scooby"):
return arg
def funcThatRaises():
raise ValueError
testNamespace = {
'aStr': 'blarg',
'anInt': 1,
'aFloat': 1.5,
'aDict': {'one': 'item1',
'two': 'item2',
'nestedDict': {'one': 'nestedItem1',
'two': 'nestedItem2',
'funcThatRaises': funcThatRaises,
'aClass': DummyClass,
},
'nestedFunc': dummyFunc,
},
'aClass': DummyClass,
'aFunc': dummyFunc,
'anObj': DummyClass(),
'anObjThatRaises': DummyClassGetAttrRaises(),
'aMeth': DummyClass().meth1,
'none': None,
'emptyString': '',
'funcThatRaises': funcThatRaises,
}
autoCallResults = {'aFunc': 'Scooby',
'aMeth': 'doo',
}
results = testNamespace.copy()
results.update({'anObj.meth1': 'doo',
'aDict.one': 'item1',
'aDict.nestedDict': testNamespace['aDict']['nestedDict'],
'aDict.nestedDict.one': 'nestedItem1',
'aDict.nestedDict.aClass': DummyClass,
'aDict.nestedFunc': 'Scooby',
'aClass.classVar1': 123,
'anObj.instanceVar1': 123,
'anObj.meth3': 'A string',
})
for k in testNamespace.keys():
# put them in the globals for the valueFromFrame tests
exec('%s = testNamespace[k]'%k)
##################################################
## TEST BASE CLASSES
class NameMapperTest(unittest.TestCase):
failureException = NotFound
_testNamespace = testNamespace
_results = results
def namespace(self):
return self._testNamespace
def VFN(self, name, autocall=True):
return valueForName(self.namespace(), name, autocall)
def VFS(self, searchList, name, autocall=True):
return valueFromSearchList(searchList, name, autocall)
# alias to be overriden later
get = VFN
def check(self, name):
got = self.get(name)
if name in autoCallResults:
expected = autoCallResults[name]
else:
expected = self._results[name]
assert got == expected
##################################################
## TEST CASE CLASSES
class VFN(NameMapperTest):
def test1(self):
"""string in dict lookup"""
self.check('aStr')
def test2(self):
"""string in dict lookup in a loop"""
for i in range(10):
self.check('aStr')
def test3(self):
"""int in dict lookup"""
self.check('anInt')
def test4(self):
"""int in dict lookup in a loop"""
for i in range(10):
self.check('anInt')
def test5(self):
"""float in dict lookup"""
self.check('aFloat')
def test6(self):
"""float in dict lookup in a loop"""
for i in range(10):
self.check('aFloat')
def test7(self):
"""class in dict lookup"""
self.check('aClass')
def test8(self):
"""class in dict lookup in a loop"""
for i in range(10):
self.check('aClass')
def test9(self):
"""aFunc in dict lookup"""
self.check('aFunc')
def test10(self):
"""aFunc in dict lookup in a loop"""
for i in range(10):
self.check('aFunc')
def test11(self):
"""aMeth in dict lookup"""
self.check('aMeth')
def test12(self):
"""aMeth in dict lookup in a loop"""
for i in range(10):
self.check('aMeth')
def test13(self):
"""aMeth in dict lookup"""
self.check('aMeth')
def test14(self):
"""aMeth in dict lookup in a loop"""
for i in range(10):
self.check('aMeth')
def test15(self):
"""anObj in dict lookup"""
self.check('anObj')
def test16(self):
"""anObj in dict lookup in a loop"""
for i in range(10):
self.check('anObj')
def test17(self):
"""aDict in dict lookup"""
self.check('aDict')
def test18(self):
"""aDict in dict lookup in a loop"""
for i in range(10):
self.check('aDict')
def test17(self):
"""aDict in dict lookup"""
self.check('aDict')
def test18(self):
"""aDict in dict lookup in a loop"""
for i in range(10):
self.check('aDict')
def test19(self):
"""aClass.classVar1 in dict lookup"""
self.check('aClass.classVar1')
def test20(self):
"""aClass.classVar1 in dict lookup in a loop"""
for i in range(10):
self.check('aClass.classVar1')
def test23(self):
"""anObj.instanceVar1 in dict lookup"""
self.check('anObj.instanceVar1')
def test24(self):
"""anObj.instanceVar1 in dict lookup in a loop"""
for i in range(10):
self.check('anObj.instanceVar1')
## tests 22, 25, and 26 removed when the underscored lookup was removed
def test27(self):
"""anObj.meth1 in dict lookup"""
self.check('anObj.meth1')
def test28(self):
"""anObj.meth1 in dict lookup in a loop"""
for i in range(10):
self.check('anObj.meth1')
def test29(self):
"""aDict.one in dict lookup"""
self.check('aDict.one')
def test30(self):
"""aDict.one in dict lookup in a loop"""
for i in range(10):
self.check('aDict.one')
def test31(self):
"""aDict.nestedDict in dict lookup"""
self.check('aDict.nestedDict')
def test32(self):
"""aDict.nestedDict in dict lookup in a loop"""
for i in range(10):
self.check('aDict.nestedDict')
def test33(self):
"""aDict.nestedDict.one in dict lookup"""
self.check('aDict.nestedDict.one')
def test34(self):
"""aDict.nestedDict.one in dict lookup in a loop"""
for i in range(10):
self.check('aDict.nestedDict.one')
def test35(self):
"""aDict.nestedFunc in dict lookup"""
self.check('aDict.nestedFunc')
def test36(self):
"""aDict.nestedFunc in dict lookup in a loop"""
for i in range(10):
self.check('aDict.nestedFunc')
def test37(self):
"""aDict.nestedFunc in dict lookup - without autocalling"""
assert self.get('aDict.nestedFunc', False) == dummyFunc
def test38(self):
"""aDict.nestedFunc in dict lookup in a loop - without autocalling"""
for i in range(10):
assert self.get('aDict.nestedFunc', False) == dummyFunc
def test39(self):
"""aMeth in dict lookup - without autocalling"""
assert self.get('aMeth', False) == self.namespace()['aMeth']
def test40(self):
"""aMeth in dict lookup in a loop - without autocalling"""
for i in range(10):
assert self.get('aMeth', False) == self.namespace()['aMeth']
def test41(self):
"""anObj.meth3 in dict lookup"""
self.check('anObj.meth3')
def test42(self):
"""aMeth in dict lookup in a loop"""
for i in range(10):
self.check('anObj.meth3')
def test43(self):
"""NotFound test"""
def test(self=self):
self.get('anObj.methX')
self.assertRaises(NotFound, test)
def test44(self):
"""NotFound test in a loop"""
def test(self=self):
self.get('anObj.methX')
for i in range(10):
self.assertRaises(NotFound, test)
def test45(self):
"""Other exception from meth test"""
def test(self=self):
self.get('anObj.meth2')
self.assertRaises(ValueError, test)
def test46(self):
"""Other exception from meth test in a loop"""
def test(self=self):
self.get('anObj.meth2')
for i in range(10):
self.assertRaises(ValueError, test)
def test47(self):
"""None in dict lookup"""
self.check('none')
def test48(self):
"""None in dict lookup in a loop"""
for i in range(10):
self.check('none')
def test49(self):
"""EmptyString in dict lookup"""
self.check('emptyString')
def test50(self):
"""EmptyString in dict lookup in a loop"""
for i in range(10):
self.check('emptyString')
def test51(self):
"""Other exception from func test"""
def test(self=self):
self.get('funcThatRaises')
self.assertRaises(ValueError, test)
def test52(self):
"""Other exception from func test in a loop"""
def test(self=self):
self.get('funcThatRaises')
for i in range(10):
self.assertRaises(ValueError, test)
def test53(self):
"""Other exception from func test"""
def test(self=self):
self.get('aDict.nestedDict.funcThatRaises')
self.assertRaises(ValueError, test)
def test54(self):
"""Other exception from func test in a loop"""
def test(self=self):
self.get('aDict.nestedDict.funcThatRaises')
for i in range(10):
self.assertRaises(ValueError, test)
def test55(self):
"""aDict.nestedDict.aClass in dict lookup"""
self.check('aDict.nestedDict.aClass')
def test56(self):
"""aDict.nestedDict.aClass in dict lookup in a loop"""
for i in range(10):
self.check('aDict.nestedDict.aClass')
def test57(self):
"""aDict.nestedDict.aClass in dict lookup - without autocalling"""
assert self.get('aDict.nestedDict.aClass', False) == DummyClass
def test58(self):
"""aDict.nestedDict.aClass in dict lookup in a loop - without autocalling"""
for i in range(10):
assert self.get('aDict.nestedDict.aClass', False) == DummyClass
def test59(self):
"""Other exception from func test -- but without autocalling shouldn't raise"""
self.get('aDict.nestedDict.funcThatRaises', False)
def test60(self):
"""Other exception from func test in a loop -- but without autocalling shouldn't raise"""
for i in range(10):
self.get('aDict.nestedDict.funcThatRaises', False)
def test61(self):
"""Accessing attribute where __getattr__ raises shouldn't segfault if something follows it"""
def test(self=self):
self.get('anObjThatRaises.willraise.anything')
self.assertRaises(ValueError, test)
class VFS(VFN):
_searchListLength = 1
def searchList(self):
lng = self._searchListLength
if lng == 1:
return [self.namespace()]
elif lng == 2:
return [self.namespace(), {'dummy':1234}]
elif lng == 3:
# a tuple for kicks
return ({'dummy':1234}, self.namespace(), {'dummy':1234})
elif lng == 4:
# a generator for more kicks
return self.searchListGenerator()
def searchListGenerator(self):
class Test:
pass
for i in [Test(), {'dummy':1234}, self.namespace(), {'dummy':1234}]:
yield i
def get(self, name, autocall=True):
return self.VFS(self.searchList(), name, autocall)
class VFS_2namespaces(VFS):
_searchListLength = 2
class VFS_3namespaces(VFS):
_searchListLength = 3
class VFS_4namespaces(VFS):
_searchListLength = 4
class VFF(VFN):
def get(self, name, autocall=True):
ns = self._testNamespace
aStr = ns['aStr']
aFloat = ns['aFloat']
none = 'some'
return valueFromFrame(name, autocall)
def setUp(self):
"""Mod some of the data
"""
self._testNamespace = ns = self._testNamespace.copy()
self._results = res = self._results.copy()
ns['aStr'] = res['aStr'] = 'BLARG'
ns['aFloat'] = res['aFloat'] = 0.1234
res['none'] = 'some'
res['True'] = True
res['False'] = False
res['None'] = None
res['eval'] = eval
def test_VFF_1(self):
"""Builtins"""
self.check('True')
self.check('None')
self.check('False')
assert self.get('eval', False)==eval
assert self.get('range', False)==range
class VFFSL(VFS):
_searchListLength = 1
def setUp(self):
"""Mod some of the data
"""
self._testNamespace = ns = self._testNamespace.copy()
self._results = res = self._results.copy()
ns['aStr'] = res['aStr'] = 'BLARG'
ns['aFloat'] = res['aFloat'] = 0.1234
res['none'] = 'some'
del ns['anInt'] # will be picked up by globals
def VFFSL(self, searchList, name, autocall=True):
anInt = 1
none = 'some'
return valueFromFrameOrSearchList(searchList, name, autocall)
def get(self, name, autocall=True):
return self.VFFSL(self.searchList(), name, autocall)
class VFFSL_2(VFFSL):
_searchListLength = 2
class VFFSL_3(VFFSL):
_searchListLength = 3
class VFFSL_4(VFFSL):
_searchListLength = 4
if sys.platform.startswith('java'):
del VFF, VFFSL, VFFSL_2, VFFSL_3, VFFSL_4
class MapBuiltins(unittest.TestCase):
def test_int(self):
from Cheetah.Template import Template
t = Template('''
#def intify(val)
#return $int(val)
#end def''', compilerSettings={'useStackFrames' : False})
self.assertEquals(5, t.intify('5'))
##################################################
## if run from the command line ##
if __name__ == '__main__':
unittest.main()

View File

@ -1,49 +0,0 @@
#!/usr/bin/env python
import unittest
from Cheetah import Parser
class ArgListTest(unittest.TestCase):
def setUp(self):
super(ArgListTest, self).setUp()
self.al = Parser.ArgList()
def test_merge1(self):
'''
Testing the ArgList case results from Template.Preprocessors.test_complexUsage
'''
self.al.add_argument('arg')
expect = [('arg', None)]
self.assertEquals(expect, self.al.merge())
def test_merge2(self):
'''
Testing the ArgList case results from SyntaxAndOutput.BlockDirective.test4
'''
self.al.add_argument('a')
self.al.add_default('999')
self.al.next()
self.al.add_argument('b')
self.al.add_default('444')
expect = [(u'a', u'999'), (u'b', u'444')]
self.assertEquals(expect, self.al.merge())
def test_merge3(self):
'''
Testing the ArgList case results from SyntaxAndOutput.BlockDirective.test13
'''
self.al.add_argument('arg')
self.al.add_default("'This is my block'")
expect = [('arg', "'This is my block'")]
self.assertEquals(expect, self.al.merge())
if __name__ == '__main__':
unittest.main()

View File

@ -1,243 +0,0 @@
#!/usr/bin/env python
import hotshot
import hotshot.stats
import os
import sys
import unittest
from test import pystone
import time
import Cheetah.NameMapper
import Cheetah.Template
# This can be turned on with the `--debug` flag when running the test
# and will cause the tests to all just dump out how long they took
# insteasd of asserting on duration
DEBUG = False
# TOLERANCE in Pystones
kPS = 1000
TOLERANCE = 0.5*kPS
class DurationError(AssertionError):
pass
_pystone_calibration_mark = None
def _pystone_calibration():
global _pystone_calibration_mark
if not _pystone_calibration_mark:
_pystone_calibration_mark = pystone.pystones(loops=pystone.LOOPS)
return _pystone_calibration_mark
def perftest(max_num_pystones, current_pystone=None):
'''
Performance test decorator based off the 'timedtest'
decorator found in this Active State recipe:
http://code.activestate.com/recipes/440700/
'''
if not isinstance(max_num_pystones, float):
max_num_pystones = float(max_num_pystones)
if not current_pystone:
current_pystone = _pystone_calibration()
def _test(function):
def wrapper(*args, **kw):
start_time = time.time()
try:
return function(*args, **kw)
finally:
total_time = time.time() - start_time
if total_time == 0:
pystone_total_time = 0
else:
pystone_rate = current_pystone[0] / current_pystone[1]
pystone_total_time = total_time / pystone_rate
global DEBUG
if DEBUG:
print('The test "%s" took: %s pystones' % (function.func_name,
pystone_total_time))
else:
if pystone_total_time > (max_num_pystones + TOLERANCE):
raise DurationError((('Test too long (%.2f Ps, '
'need at most %.2f Ps)')
% (pystone_total_time,
max_num_pystones)))
return wrapper
return _test
class DynamicTemplatePerformanceTest(unittest.TestCase):
loops = 10
#@perftest(1200)
def test_BasicDynamic(self):
template = '''
#def foo(arg1, arg2)
#pass
#end def
'''
for i in range(self.loops):
klass = Cheetah.Template.Template.compile(template)
assert klass
test_BasicDynamic = perftest(1200)(test_BasicDynamic)
class PerformanceTest(unittest.TestCase):
iterations = 100000
display = False
save = False
def runTest(self):
self.prof = hotshot.Profile('%s.prof' % self.__class__.__name__)
self.prof.start()
for i in range(self.iterations):
if hasattr(self, 'performanceSample'):
self.display = True
self.performanceSample()
self.prof.stop()
self.prof.close()
if self.display:
print('>>> %s (%d iterations) ' % (self.__class__.__name__,
self.iterations))
stats = hotshot.stats.load('%s.prof' % self.__class__.__name__)
#stats.strip_dirs()
stats.sort_stats('time', 'calls')
stats.print_stats(50)
if not self.save:
os.unlink('%s.prof' % self.__class__.__name__)
class DynamicMethodCompilationTest(PerformanceTest):
def performanceSample(self):
template = '''
#import sys
#import os
#def testMethod()
#set foo = [1, 2, 3, 4]
#return $foo[0]
#end def
'''
template = Cheetah.Template.Template.compile(template,
keepRefToGeneratedCode=False)
template = template()
value = template.testMethod()
class BunchOfWriteCalls(PerformanceTest):
iterations = 1000
def performanceSample(self):
template = '''
#import sys
#import os
#for i in range(1000)
$i
#end for
'''
template = Cheetah.Template.Template.compile(template,
keepRefToGeneratedCode=False)
template = template()
value = template.respond()
del value
class DynamicSimpleCompilationTest(PerformanceTest):
def performanceSample(self):
template = '''
#import sys
#import os
#set foo = [1,2,3,4]
Well hello there! This is basic.
Here's an array too: $foo
'''
template = Cheetah.Template.Template.compile(template,
keepRefToGeneratedCode=False)
template = template()
template = unicode(template)
class FilterTest(PerformanceTest):
template = None
def setUp(self):
super(FilterTest, self).setUp()
template = '''
#import sys
#import os
#set foo = [1, 2, 3, 4]
$foo, $foo, $foo
'''
template = Cheetah.Template.Template.compile(template,
keepRefToGeneratedCode=False)
self.template = template()
def performanceSample(self):
value = unicode(self.template)
class LongCompileTest(PerformanceTest):
''' Test the compilation on a sufficiently large template '''
def compile(self, template):
return Cheetah.Template.Template.compile(template, keepRefToGeneratedCode=False)
def performanceSample(self):
template = '''
#import sys
#import Cheetah.Template
#extends Cheetah.Template.Template
#def header()
<center><h2>This is my header</h2></center>
#end def
#def footer()
#return "Huzzah"
#end def
#def scripts()
#pass
#end def
#def respond()
<html>
<head>
<title>${title}</title>
$scripts()
</head>
<body>
$header()
#for $i in $range(10)
This is just some stupid page!
<br/>
#end for
<br/>
$footer()
</body>
</html>
#end def
'''
return self.compile(template)
class LongCompile_CompilerSettingsTest(LongCompileTest):
def compile(self, template):
return Cheetah.Template.Template.compile(template, keepRefToGeneratedCode=False,
compilerSettings={'useStackFrames' : True, 'useAutocalling' : True})
class LongCompileAndRun(LongCompileTest):
def performanceSample(self):
template = super(LongCompileAndRun, self).performanceSample()
template = template(searchList=[{'title' : 'foo'}])
template = template.respond()
if __name__ == '__main__':
if '--debug' in sys.argv:
DEBUG = True
sys.argv = [arg for arg in sys.argv if not arg == '--debug']
unittest.main()

View File

@ -1,247 +0,0 @@
#!/usr/bin/env python
import Cheetah.NameMapper
import Cheetah.Template
import sys
import unittest
majorVer, minorVer = sys.version_info[0], sys.version_info[1]
versionTuple = (majorVer, minorVer)
def isPython23():
''' Python 2.3 is still supported by Cheetah, but doesn't support decorators '''
return majorVer == 2 and minorVer < 4
class GetAttrException(Exception):
pass
class CustomGetAttrClass(object):
def __getattr__(self, name):
raise GetAttrException('FAIL, %s' % name)
class GetAttrTest(unittest.TestCase):
'''
Test for an issue occurring when __getatttr__() raises an exception
causing NameMapper to raise a NotFound exception
'''
def test_ValidException(self):
o = CustomGetAttrClass()
try:
print(o.attr)
except GetAttrException, e:
# expected
return
except:
self.fail('Invalid exception raised: %s' % e)
self.fail('Should have had an exception raised')
def test_NotFoundException(self):
template = '''
#def raiseme()
$obj.attr
#end def'''
template = Cheetah.Template.Template.compile(template, compilerSettings={}, keepRefToGeneratedCode=True)
template = template(searchList=[{'obj' : CustomGetAttrClass()}])
assert template, 'We should have a valid template object by now'
self.failUnlessRaises(GetAttrException, template.raiseme)
class InlineImportTest(unittest.TestCase):
def test_FromFooImportThing(self):
'''
Verify that a bug introduced in v2.1.0 where an inline:
#from module import class
would result in the following code being generated:
import class
'''
template = '''
#def myfunction()
#if True
#from os import path
#return 17
Hello!
#end if
#end def
'''
template = Cheetah.Template.Template.compile(template, compilerSettings={'useLegacyImportMode' : False}, keepRefToGeneratedCode=True)
template = template(searchList=[{}])
assert template, 'We should have a valid template object by now'
rc = template.myfunction()
assert rc == 17, (template, 'Didn\'t get a proper return value')
def test_ImportFailModule(self):
template = '''
#try
#import invalidmodule
#except
#set invalidmodule = dict(FOO='BAR!')
#end try
$invalidmodule.FOO
'''
template = Cheetah.Template.Template.compile(template, compilerSettings={'useLegacyImportMode' : False}, keepRefToGeneratedCode=True)
template = template(searchList=[{}])
assert template, 'We should have a valid template object by now'
assert str(template), 'We weren\'t able to properly generate the result from the template'
def test_ProperImportOfBadModule(self):
template = '''
#from invalid import fail
This should totally $fail
'''
self.failUnlessRaises(ImportError, Cheetah.Template.Template.compile, template, compilerSettings={'useLegacyImportMode' : False}, keepRefToGeneratedCode=True)
def test_AutoImporting(self):
template = '''
#extends FakeyTemplate
Boo!
'''
self.failUnlessRaises(ImportError, Cheetah.Template.Template.compile, template)
def test_StuffBeforeImport_Legacy(self):
template = '''
###
### I like comments before import
###
#extends Foo
Bar
'''
self.failUnlessRaises(ImportError, Cheetah.Template.Template.compile, template, compilerSettings={'useLegacyImportMode' : True}, keepRefToGeneratedCode=True)
class Mantis_Issue_11_Regression_Test(unittest.TestCase):
'''
Test case for bug outlined in Mantis issue #11:
Output:
Traceback (most recent call last):
File "test.py", line 12, in <module>
t.respond()
File "DynamicallyCompiledCheetahTemplate.py", line 86, in respond
File "/usr/lib64/python2.6/cgi.py", line 1035, in escape
s = s.replace("&", "&") # Must be done first!
'''
def test_FailingBehavior(self):
import cgi
template = Cheetah.Template.Template("$escape($request)", searchList=[{'escape' : cgi.escape, 'request' : 'foobar'}])
assert template
self.failUnlessRaises(AttributeError, template.respond)
def test_FailingBehaviorWithSetting(self):
import cgi
template = Cheetah.Template.Template("$escape($request)",
searchList=[{'escape' : cgi.escape, 'request' : 'foobar'}],
compilerSettings={'prioritizeSearchListOverSelf' : True})
assert template
assert template.respond()
class Mantis_Issue_21_Regression_Test(unittest.TestCase):
'''
Test case for bug outlined in issue #21
Effectively @staticmethod and @classmethod
decorated methods in templates don't
properly define the _filter local, which breaks
when using the NameMapper
'''
def runTest(self):
if isPython23():
return
template = '''
#@staticmethod
#def testMethod()
This is my $output
#end def
'''
template = Cheetah.Template.Template.compile(template)
assert template
assert template.testMethod(output='bug') # raises a NameError: global name '_filter' is not defined
class Mantis_Issue_22_Regression_Test(unittest.TestCase):
'''
Test case for bug outlined in issue #22
When using @staticmethod and @classmethod
in conjunction with the #filter directive
the generated code for the #filter is reliant
on the `self` local, breaking the function
'''
def test_NoneFilter(self):
# XXX: Disabling this test for now
return
if isPython23():
return
template = '''
#@staticmethod
#def testMethod()
#filter None
This is my $output
#end filter
#end def
'''
template = Cheetah.Template.Template.compile(template)
assert template
assert template.testMethod(output='bug')
def test_DefinedFilter(self):
# XXX: Disabling this test for now
return
if isPython23():
return
template = '''
#@staticmethod
#def testMethod()
#filter Filter
This is my $output
#end filter
#end def
'''
# The generated code for the template's testMethod() should look something
# like this in the 'error' case:
'''
@staticmethod
def testMethod(**KWS):
## CHEETAH: generated from #def testMethod() at line 3, col 13.
trans = DummyTransaction()
_dummyTrans = True
write = trans.response().write
SL = [KWS]
_filter = lambda x, **kwargs: unicode(x)
########################################
## START - generated method body
_orig_filter_18517345 = _filter
filterName = u'Filter'
if self._CHEETAH__filters.has_key("Filter"):
_filter = self._CHEETAH__currentFilter = self._CHEETAH__filters[filterName]
else:
_filter = self._CHEETAH__currentFilter = \
self._CHEETAH__filters[filterName] = getattr(self._CHEETAH__filtersLib, filterName)(self).filter
write(u' This is my ')
_v = VFFSL(SL,"output",True) # u'$output' on line 5, col 32
if _v is not None: write(_filter(_v, rawExpr=u'$output')) # from line 5, col 32.
########################################
## END - generated method body
return _dummyTrans and trans.response().getvalue() or ""
'''
template = Cheetah.Template.Template.compile(template)
assert template
assert template.testMethod(output='bug')
if __name__ == '__main__':
unittest.main()

File diff suppressed because it is too large Load Diff

View File

@ -1,363 +0,0 @@
#!/usr/bin/env python
import pdb
import sys
import types
import os
import os.path
import tempfile
import shutil
import unittest
from Cheetah.Template import Template
majorVer, minorVer = sys.version_info[0], sys.version_info[1]
versionTuple = (majorVer, minorVer)
class TemplateTest(unittest.TestCase):
pass
class ClassMethods_compile(TemplateTest):
"""I am using the same Cheetah source for each test to root out clashes
caused by the compile caching in Template.compile().
"""
def test_basicUsage(self):
klass = Template.compile(source='$foo')
t = klass(namespaces={'foo':1234})
assert str(t)=='1234'
def test_baseclassArg(self):
klass = Template.compile(source='$foo', baseclass=dict)
t = klass({'foo':1234})
assert str(t)=='1234'
klass2 = Template.compile(source='$foo', baseclass=klass)
t = klass2({'foo':1234})
assert str(t)=='1234'
klass3 = Template.compile(source='#implements dummy\n$bar', baseclass=klass2)
t = klass3({'foo':1234})
assert str(t)=='1234'
klass4 = Template.compile(source='$foo', baseclass='dict')
t = klass4({'foo':1234})
assert str(t)=='1234'
def test_moduleFileCaching(self):
if versionTuple < (2, 3):
return
tmpDir = tempfile.mkdtemp()
try:
#print tmpDir
assert os.path.exists(tmpDir)
klass = Template.compile(source='$foo',
cacheModuleFilesForTracebacks=True,
cacheDirForModuleFiles=tmpDir)
mod = sys.modules[klass.__module__]
#print mod.__file__
assert os.path.exists(mod.__file__)
assert os.path.dirname(mod.__file__)==tmpDir
finally:
shutil.rmtree(tmpDir, True)
def test_classNameArg(self):
klass = Template.compile(source='$foo', className='foo123')
assert klass.__name__=='foo123'
t = klass(namespaces={'foo':1234})
assert str(t)=='1234'
def test_moduleNameArg(self):
klass = Template.compile(source='$foo', moduleName='foo99')
mod = sys.modules['foo99']
assert klass.__name__=='foo99'
t = klass(namespaces={'foo':1234})
assert str(t)=='1234'
klass = Template.compile(source='$foo',
moduleName='foo1',
className='foo2')
mod = sys.modules['foo1']
assert klass.__name__=='foo2'
t = klass(namespaces={'foo':1234})
assert str(t)=='1234'
def test_mainMethodNameArg(self):
klass = Template.compile(source='$foo',
className='foo123',
mainMethodName='testMeth')
assert klass.__name__=='foo123'
t = klass(namespaces={'foo':1234})
#print t.generatedClassCode()
assert str(t)=='1234'
assert t.testMeth()=='1234'
klass = Template.compile(source='$foo',
moduleName='fooXXX',
className='foo123',
mainMethodName='testMeth',
baseclass=dict)
assert klass.__name__=='foo123'
t = klass({'foo':1234})
#print t.generatedClassCode()
assert str(t)=='1234'
assert t.testMeth()=='1234'
def test_moduleGlobalsArg(self):
klass = Template.compile(source='$foo',
moduleGlobals={'foo':1234})
t = klass()
assert str(t)=='1234'
klass2 = Template.compile(source='$foo', baseclass='Test1',
moduleGlobals={'Test1':dict})
t = klass2({'foo':1234})
assert str(t)=='1234'
klass3 = Template.compile(source='$foo', baseclass='Test1',
moduleGlobals={'Test1':dict, 'foo':1234})
t = klass3()
assert str(t)=='1234'
def test_keepRefToGeneratedCodeArg(self):
klass = Template.compile(source='$foo',
className='unique58',
cacheCompilationResults=False,
keepRefToGeneratedCode=False)
t = klass(namespaces={'foo':1234})
assert str(t)=='1234'
assert not t.generatedModuleCode()
klass2 = Template.compile(source='$foo',
className='unique58',
keepRefToGeneratedCode=True)
t = klass2(namespaces={'foo':1234})
assert str(t)=='1234'
assert t.generatedModuleCode()
klass3 = Template.compile(source='$foo',
className='unique58',
keepRefToGeneratedCode=False)
t = klass3(namespaces={'foo':1234})
assert str(t)=='1234'
# still there as this class came from the cache
assert t.generatedModuleCode()
def test_compilationCache(self):
klass = Template.compile(source='$foo',
className='unique111',
cacheCompilationResults=False)
t = klass(namespaces={'foo':1234})
assert str(t)=='1234'
assert not klass._CHEETAH_isInCompilationCache
# this time it will place it in the cache
klass = Template.compile(source='$foo',
className='unique111',
cacheCompilationResults=True)
t = klass(namespaces={'foo':1234})
assert str(t)=='1234'
assert klass._CHEETAH_isInCompilationCache
# by default it will be in the cache
klass = Template.compile(source='$foo',
className='unique999099')
t = klass(namespaces={'foo':1234})
assert str(t)=='1234'
assert klass._CHEETAH_isInCompilationCache
class ClassMethods_subclass(TemplateTest):
def test_basicUsage(self):
klass = Template.compile(source='$foo', baseclass=dict)
t = klass({'foo':1234})
assert str(t)=='1234'
klass2 = klass.subclass(source='$foo')
t = klass2({'foo':1234})
assert str(t)=='1234'
klass3 = klass2.subclass(source='#implements dummy\n$bar')
t = klass3({'foo':1234})
assert str(t)=='1234'
class Preprocessors(TemplateTest):
def test_basicUsage1(self):
src='''\
%set foo = @a
$(@foo*10)
@a'''
src = '\n'.join([ln.strip() for ln in src.splitlines()])
preprocessors = {'tokens':'@ %',
'namespaces':{'a':99}
}
klass = Template.compile(src, preprocessors=preprocessors)
assert str(klass())=='990\n99'
def test_normalizePreprocessorArgVariants(self):
src='%set foo = 12\n%%comment\n$(@foo*10)'
class Settings1: tokens = '@ %'
Settings1 = Settings1()
from Cheetah.Template import TemplatePreprocessor
settings = Template._normalizePreprocessorSettings(Settings1)
preprocObj = TemplatePreprocessor(settings)
def preprocFunc(source, file):
return '$(12*10)', None
class TemplateSubclass(Template):
pass
compilerSettings = {'cheetahVarStartToken': '@',
'directiveStartToken': '%',
'commentStartToken': '%%',
}
for arg in ['@ %',
{'tokens':'@ %'},
{'compilerSettings':compilerSettings},
{'compilerSettings':compilerSettings,
'templateInitArgs':{}},
{'tokens':'@ %',
'templateAPIClass':TemplateSubclass},
Settings1,
preprocObj,
preprocFunc,
]:
klass = Template.compile(src, preprocessors=arg)
assert str(klass())=='120'
def test_complexUsage(self):
src='''\
%set foo = @a
%def func1: #def func(arg): $arg("***")
%% comment
$(@foo*10)
@func1
$func(lambda x:c"--$x--@a")'''
src = '\n'.join([ln.strip() for ln in src.splitlines()])
for arg in [{'tokens':'@ %', 'namespaces':{'a':99} },
{'tokens':'@ %', 'namespaces':{'a':99} },
]:
klass = Template.compile(src, preprocessors=arg)
t = klass()
assert str(t)=='990\n--***--99'
def test_i18n(self):
src='''\
%i18n: This is a $string that needs translation
%i18n id="foo", domain="root": This is a $string that needs translation
'''
src = '\n'.join([ln.strip() for ln in src.splitlines()])
klass = Template.compile(src, preprocessors='@ %', baseclass=dict)
t = klass({'string':'bit of text'})
#print str(t), repr(str(t))
assert str(t)==('This is a bit of text that needs translation\n'*2)[:-1]
class TryExceptImportTest(TemplateTest):
def test_FailCase(self):
''' Test situation where an inline #import statement will get relocated '''
source = '''
#def myFunction()
Ahoy!
#try
#import sys
#except ImportError
$print "This will never happen!"
#end try
#end def
'''
# This should raise an IndentationError (if the bug exists)
klass = Template.compile(source=source, compilerSettings={'useLegacyImportMode' : False})
t = klass(namespaces={'foo' : 1234})
class ClassMethodSupport(TemplateTest):
def test_BasicDecorator(self):
if sys.version_info[0] == 2 and sys.version_info[1] == 3:
print('This version of Python doesn\'t support decorators, skipping tests')
return
template = '''
#@classmethod
#def myClassMethod()
#return '$foo = %s' % $foo
#end def
'''
template = Template.compile(source=template)
try:
rc = template.myClassMethod(foo='bar')
assert rc == '$foo = bar', (rc, 'Template class method didn\'t return what I expected')
except AttributeError, ex:
self.fail(ex)
class StaticMethodSupport(TemplateTest):
def test_BasicDecorator(self):
if sys.version_info[0] == 2 and sys.version_info[1] == 3:
print('This version of Python doesn\'t support decorators, skipping tests')
return
template = '''
#@staticmethod
#def myStaticMethod()
#return '$foo = %s' % $foo
#end def
'''
template = Template.compile(source=template)
try:
rc = template.myStaticMethod(foo='bar')
assert rc == '$foo = bar', (rc, 'Template class method didn\'t return what I expected')
except AttributeError, ex:
self.fail(ex)
class Useless(object):
def boink(self):
return [1, 2, 3]
class MultipleInheritanceSupport(TemplateTest):
def runTest(self):
template = '''
#extends Template, Useless
#def foo()
#return [4,5] + $boink()
#end def
'''
template = Template.compile(template,
moduleGlobals={'Useless' : Useless},
compilerSettings={'autoImportForExtendsDirective' : False})
template = template()
result = template.foo()
assert result == [4, 5, 1, 2, 3], (result, 'Unexpected result')
class SubclassSearchListTest(TemplateTest):
'''
Verify that if we subclass Template, we can still
use attributes on that subclass in the searchList
'''
def runTest(self):
class Sub(Template):
greeting = 'Hola'
tmpl = Sub('''When we meet, I say "${greeting}"''')
self.assertEquals(unicode(tmpl), 'When we meet, I say "Hola"')
##################################################
## if run from the command line ##
if __name__ == '__main__':
unittest.main()

View File

@ -1,53 +0,0 @@
#!/usr/bin/env python
'''
Core module of Cheetah's Unit-testing framework
TODO
================================================================================
# combo tests
# negative test cases for expected exceptions
# black-box vs clear-box testing
# do some tests that run the Template for long enough to check that the refresh code works
'''
import sys
import unittest
from Cheetah.Tests import SyntaxAndOutput
from Cheetah.Tests import NameMapper
from Cheetah.Tests import Misc
from Cheetah.Tests import Filters
from Cheetah.Tests import Template
from Cheetah.Tests import Cheps
from Cheetah.Tests import Parser
from Cheetah.Tests import Regressions
from Cheetah.Tests import Unicode
from Cheetah.Tests import CheetahWrapper
from Cheetah.Tests import Analyzer
SyntaxAndOutput.install_eols()
suites = [
unittest.findTestCases(SyntaxAndOutput),
unittest.findTestCases(NameMapper),
unittest.findTestCases(Filters),
unittest.findTestCases(Template),
#unittest.findTestCases(Cheps),
unittest.findTestCases(Regressions),
unittest.findTestCases(Unicode),
unittest.findTestCases(Misc),
unittest.findTestCases(Parser),
unittest.findTestCases(Analyzer),
]
if not sys.platform.startswith('java'):
suites.append(unittest.findTestCases(CheetahWrapper))
if __name__ == '__main__':
runner = unittest.TextTestRunner()
if 'xml' in sys.argv:
import xmlrunner
runner = xmlrunner.XMLTestRunner(filename='Cheetah-Tests.xml')
results = runner.run(unittest.TestSuite(suites))

View File

@ -1,237 +0,0 @@
#!/usr/bin/env python
# -*- encoding: utf8 -*-
from Cheetah.Template import Template
from Cheetah import CheetahWrapper
from Cheetah import DummyTransaction
import imp
import os
import sys
import tempfile
import unittest
class CommandLineTest(unittest.TestCase):
def createAndCompile(self, source):
sourcefile = '-'
while sourcefile.find('-') != -1:
sourcefile = tempfile.mktemp()
fd = open('%s.tmpl' % sourcefile, 'w')
fd.write(source)
fd.close()
wrap = CheetahWrapper.CheetahWrapper()
wrap.main(['cheetah', 'compile', '--quiet', '--nobackup', sourcefile])
module_path, module_name = os.path.split(sourcefile)
module = loadModule(module_name, [module_path])
template = getattr(module, module_name)
return template
class JBQ_UTF8_Test1(unittest.TestCase):
def runTest(self):
t = Template.compile(source="""Main file with |$v|
$other""")
otherT = Template.compile(source="Other template with |$v|")
other = otherT()
t.other = other
t.v = u'Unicode String'
t.other.v = u'Unicode String'
assert unicode(t())
class JBQ_UTF8_Test2(unittest.TestCase):
def runTest(self):
t = Template.compile(source="""Main file with |$v|
$other""")
otherT = Template.compile(source="Other template with |$v|")
other = otherT()
t.other = other
t.v = u'Unicode String with eacute é'
t.other.v = u'Unicode String'
assert unicode(t())
class JBQ_UTF8_Test3(unittest.TestCase):
def runTest(self):
t = Template.compile(source="""Main file with |$v|
$other""")
otherT = Template.compile(source="Other template with |$v|")
other = otherT()
t.other = other
t.v = u'Unicode String with eacute é'
t.other.v = u'Unicode String and an eacute é'
assert unicode(t())
class JBQ_UTF8_Test4(unittest.TestCase):
def runTest(self):
t = Template.compile(source="""#encoding utf-8
Main file with |$v| and eacute in the template é""")
t.v = 'Unicode String'
assert unicode(t())
class JBQ_UTF8_Test5(unittest.TestCase):
def runTest(self):
t = Template.compile(source="""#encoding utf-8
Main file with |$v| and eacute in the template é""")
t.v = u'Unicode String'
assert unicode(t())
def loadModule(moduleName, path=None):
if path:
assert isinstance(path, list)
try:
mod = sys.modules[moduleName]
except KeyError:
fp = None
try:
fp, pathname, description = imp.find_module(moduleName, path)
mod = imp.load_module(moduleName, fp, pathname, description)
finally:
if fp:
fp.close()
return mod
class JBQ_UTF8_Test6(unittest.TestCase):
def runTest(self):
source = """#encoding utf-8
#set $someUnicodeString = u"Bébé"
Main file with |$v| and eacute in the template é"""
t = Template.compile(source=source)
t.v = u'Unicode String'
assert unicode(t())
class JBQ_UTF8_Test7(CommandLineTest):
def runTest(self):
source = """#encoding utf-8
#set $someUnicodeString = u"Bébé"
Main file with |$v| and eacute in the template é"""
template = self.createAndCompile(source)
template.v = u'Unicode String'
assert unicode(template())
class JBQ_UTF8_Test8(CommandLineTest):
def testStaticCompile(self):
source = """#encoding utf-8
#set $someUnicodeString = u"Bébé"
$someUnicodeString"""
template = self.createAndCompile(source)()
a = unicode(template).encode("utf-8")
self.assertEquals("Bébé", a)
def testDynamicCompile(self):
source = """#encoding utf-8
#set $someUnicodeString = u"Bébé"
$someUnicodeString"""
template = Template(source = source)
a = unicode(template).encode("utf-8")
self.assertEquals("Bébé", a)
class EncodeUnicodeCompatTest(unittest.TestCase):
"""
Taken initially from Red Hat's bugzilla #529332
https://bugzilla.redhat.com/show_bug.cgi?id=529332
"""
def runTest(self):
t = Template("""Foo ${var}""", filter='EncodeUnicode')
t.var = u"Text with some non-ascii characters: åäö"
rc = t.respond()
assert isinstance(rc, unicode), ('Template.respond() should return unicode', rc)
rc = str(t)
assert isinstance(rc, str), ('Template.__str__() should return a UTF-8 encoded string', rc)
class Unicode_in_SearchList_Test(CommandLineTest):
def test_BasicASCII(self):
source = '''This is $adjective'''
template = self.createAndCompile(source)
assert template and issubclass(template, Template)
template = template(searchList=[{'adjective' : u'neat'}])
assert template.respond()
def test_Thai(self):
# The string is something in Thai
source = '''This is $foo $adjective'''
template = self.createAndCompile(source)
assert template and issubclass(template, Template)
template = template(searchList=[{'foo' : 'bar',
'adjective' : u'\u0e22\u0e34\u0e19\u0e14\u0e35\u0e15\u0e49\u0e2d\u0e19\u0e23\u0e31\u0e1a'}])
assert template.respond()
def test_Thai_utf8(self):
utf8 = '\xe0\xb8\xa2\xe0\xb8\xb4\xe0\xb8\x99\xe0\xb8\x94\xe0\xb8\xb5\xe0\xb8\x95\xe0\xb9\x89\xe0\xb8\xad\xe0\xb8\x99\xe0\xb8\xa3\xe0\xb8\xb1\xe0\xb8\x9a'
source = '''This is $adjective'''
template = self.createAndCompile(source)
assert template and issubclass(template, Template)
template = template(searchList=[{'adjective' : utf8}])
assert template.respond()
class InlineSpanishTest(unittest.TestCase):
def setUp(self):
super(InlineSpanishTest, self).setUp()
self.template = '''
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<title>Pagina del vendedor</title>
</head>
<body>
$header
<h2>Bienvenido $nombre.</h2>
<br /><br /><br />
<center>
Usted tiene $numpedidos_noconf <a href="">pedidós</a> sin confirmar.
<br /><br />
Bodega tiene fecha para $numpedidos_bodega <a href="">pedidos</a>.
</center>
</body>
</html>
'''
def test_failure(self):
""" Test a template lacking a proper #encoding tag """
self.failUnlessRaises(UnicodeDecodeError, Template, self.template, searchList=[{'header' : '',
'nombre' : '', 'numpedidos_bodega' : '',
'numpedidos_noconf' : ''}])
def test_success(self):
""" Test a template with a proper #encoding tag """
template = '#encoding utf-8\n%s' % self.template
template = Template(template, searchList=[{'header' : '',
'nombre' : '', 'numpedidos_bodega' : '',
'numpedidos_noconf' : ''}])
self.assertTrue(unicode(template))
if __name__ == '__main__':
unittest.main()

View File

@ -1 +0,0 @@
#

View File

@ -1,381 +0,0 @@
"""
XML Test Runner for PyUnit
"""
# Written by Sebastian Rittau <srittau@jroger.in-berlin.de> and placed in
# the Public Domain. With contributions by Paolo Borelli.
__revision__ = "$Id: /private/python/stdlib/xmlrunner.py 16654 2007-11-12T12:46:35.368945Z srittau $"
import os.path
import re
import sys
import time
import traceback
import unittest
from StringIO import StringIO
from xml.sax.saxutils import escape
from StringIO import StringIO
class _TestInfo(object):
"""Information about a particular test.
Used by _XMLTestResult.
"""
def __init__(self, test, time):
_pieces = test.id().split('.')
(self._class, self._method) = ('.'.join(_pieces[:-1]), _pieces[-1])
self._time = time
self._error = None
self._failure = None
def print_report(self, stream):
"""Print information about this test case in XML format to the
supplied stream.
"""
stream.write(' <testcase classname="%(class)s" name="%(method)s" time="%(time).4f">' % \
{
"class": self._class,
"method": self._method,
"time": self._time,
})
if self._failure != None:
self._print_error(stream, 'failure', self._failure)
if self._error != None:
self._print_error(stream, 'error', self._error)
stream.write('</testcase>\n')
def _print_error(self, stream, tagname, error):
"""Print information from a failure or error to the supplied stream."""
text = escape(str(error[1]))
stream.write('\n')
stream.write(' <%s type="%s">%s\n' \
% (tagname, issubclass(error[0], Exception) and error[0].__name__ or str(error[0]), text))
tb_stream = StringIO()
traceback.print_tb(error[2], None, tb_stream)
stream.write(escape(tb_stream.getvalue()))
stream.write(' </%s>\n' % tagname)
stream.write(' ')
# Module level functions since Python 2.3 doesn't grok decorators
def create_success(test, time):
"""Create a _TestInfo instance for a successful test."""
return _TestInfo(test, time)
def create_failure(test, time, failure):
"""Create a _TestInfo instance for a failed test."""
info = _TestInfo(test, time)
info._failure = failure
return info
def create_error(test, time, error):
"""Create a _TestInfo instance for an erroneous test."""
info = _TestInfo(test, time)
info._error = error
return info
class _XMLTestResult(unittest.TestResult):
"""A test result class that stores result as XML.
Used by XMLTestRunner.
"""
def __init__(self, classname):
unittest.TestResult.__init__(self)
self._test_name = classname
self._start_time = None
self._tests = []
self._error = None
self._failure = None
def startTest(self, test):
unittest.TestResult.startTest(self, test)
self._error = None
self._failure = None
self._start_time = time.time()
def stopTest(self, test):
time_taken = time.time() - self._start_time
unittest.TestResult.stopTest(self, test)
if self._error:
info = create_error(test, time_taken, self._error)
elif self._failure:
info = create_failure(test, time_taken, self._failure)
else:
info = create_success(test, time_taken)
self._tests.append(info)
def addError(self, test, err):
unittest.TestResult.addError(self, test, err)
self._error = err
def addFailure(self, test, err):
unittest.TestResult.addFailure(self, test, err)
self._failure = err
def print_report(self, stream, time_taken, out, err):
"""Prints the XML report to the supplied stream.
The time the tests took to perform as well as the captured standard
output and standard error streams must be passed in.a
"""
stream.write('<testsuite errors="%(e)d" failures="%(f)d" ' % \
{ "e": len(self.errors), "f": len(self.failures) })
stream.write('name="%(n)s" tests="%(t)d" time="%(time).3f">\n' % \
{
"n": self._test_name,
"t": self.testsRun,
"time": time_taken,
})
for info in self._tests:
info.print_report(stream)
stream.write(' <system-out><![CDATA[%s]]></system-out>\n' % out)
stream.write(' <system-err><![CDATA[%s]]></system-err>\n' % err)
stream.write('</testsuite>\n')
class XMLTestRunner(object):
"""A test runner that stores results in XML format compatible with JUnit.
XMLTestRunner(stream=None) -> XML test runner
The XML file is written to the supplied stream. If stream is None, the
results are stored in a file called TEST-<module>.<class>.xml in the
current working directory (if not overridden with the path property),
where <module> and <class> are the module and class name of the test class.
"""
def __init__(self, *args, **kwargs):
self._stream = kwargs.get('stream')
self._filename = kwargs.get('filename')
self._path = "."
def run(self, test):
"""Run the given test case or test suite."""
class_ = test.__class__
classname = class_.__module__ + "." + class_.__name__
if self._stream == None:
filename = "TEST-%s.xml" % classname
if self._filename:
filename = self._filename
stream = file(os.path.join(self._path, filename), "w")
stream.write('<?xml version="1.0" encoding="utf-8"?>\n')
else:
stream = self._stream
result = _XMLTestResult(classname)
start_time = time.time()
# TODO: Python 2.5: Use the with statement
old_stdout = sys.stdout
old_stderr = sys.stderr
sys.stdout = StringIO()
sys.stderr = StringIO()
try:
test(result)
try:
out_s = sys.stdout.getvalue()
except AttributeError:
out_s = ""
try:
err_s = sys.stderr.getvalue()
except AttributeError:
err_s = ""
finally:
sys.stdout = old_stdout
sys.stderr = old_stderr
time_taken = time.time() - start_time
result.print_report(stream, time_taken, out_s, err_s)
if self._stream == None:
stream.close()
return result
def _set_path(self, path):
self._path = path
path = property(lambda self: self._path, _set_path, None,
"""The path where the XML files are stored.
This property is ignored when the XML file is written to a file
stream.""")
class XMLTestRunnerTest(unittest.TestCase):
def setUp(self):
self._stream = StringIO()
def _try_test_run(self, test_class, expected):
"""Run the test suite against the supplied test class and compare the
XML result against the expected XML string. Fail if the expected
string doesn't match the actual string. All time attribute in the
expected string should have the value "0.000". All error and failure
messages are reduced to "Foobar".
"""
runner = XMLTestRunner(self._stream)
runner.run(unittest.makeSuite(test_class))
got = self._stream.getvalue()
# Replace all time="X.YYY" attributes by time="0.000" to enable a
# simple string comparison.
got = re.sub(r'time="\d+\.\d+"', 'time="0.000"', got)
# Likewise, replace all failure and error messages by a simple "Foobar"
# string.
got = re.sub(r'(?s)<failure (.*?)>.*?</failure>', r'<failure \1>Foobar</failure>', got)
got = re.sub(r'(?s)<error (.*?)>.*?</error>', r'<error \1>Foobar</error>', got)
self.assertEqual(expected, got)
def test_no_tests(self):
"""Regression test: Check whether a test run without any tests
matches a previous run.
"""
class TestTest(unittest.TestCase):
pass
self._try_test_run(TestTest, """<testsuite errors="0" failures="0" name="unittest.TestSuite" tests="0" time="0.000">
<system-out><![CDATA[]]></system-out>
<system-err><![CDATA[]]></system-err>
</testsuite>
""")
def test_success(self):
"""Regression test: Check whether a test run with a successful test
matches a previous run.
"""
class TestTest(unittest.TestCase):
def test_foo(self):
pass
self._try_test_run(TestTest, """<testsuite errors="0" failures="0" name="unittest.TestSuite" tests="1" time="0.000">
<testcase classname="__main__.TestTest" name="test_foo" time="0.000"></testcase>
<system-out><![CDATA[]]></system-out>
<system-err><![CDATA[]]></system-err>
</testsuite>
""")
def test_failure(self):
"""Regression test: Check whether a test run with a failing test
matches a previous run.
"""
class TestTest(unittest.TestCase):
def test_foo(self):
self.assert_(False)
self._try_test_run(TestTest, """<testsuite errors="0" failures="1" name="unittest.TestSuite" tests="1" time="0.000">
<testcase classname="__main__.TestTest" name="test_foo" time="0.000">
<failure type="exceptions.AssertionError">Foobar</failure>
</testcase>
<system-out><![CDATA[]]></system-out>
<system-err><![CDATA[]]></system-err>
</testsuite>
""")
def test_error(self):
"""Regression test: Check whether a test run with a erroneous test
matches a previous run.
"""
class TestTest(unittest.TestCase):
def test_foo(self):
raise IndexError()
self._try_test_run(TestTest, """<testsuite errors="1" failures="0" name="unittest.TestSuite" tests="1" time="0.000">
<testcase classname="__main__.TestTest" name="test_foo" time="0.000">
<error type="exceptions.IndexError">Foobar</error>
</testcase>
<system-out><![CDATA[]]></system-out>
<system-err><![CDATA[]]></system-err>
</testsuite>
""")
def test_stdout_capture(self):
"""Regression test: Check whether a test run with output to stdout
matches a previous run.
"""
class TestTest(unittest.TestCase):
def test_foo(self):
print("Test")
self._try_test_run(TestTest, """<testsuite errors="0" failures="0" name="unittest.TestSuite" tests="1" time="0.000">
<testcase classname="__main__.TestTest" name="test_foo" time="0.000"></testcase>
<system-out><![CDATA[Test
]]></system-out>
<system-err><![CDATA[]]></system-err>
</testsuite>
""")
def test_stderr_capture(self):
"""Regression test: Check whether a test run with output to stderr
matches a previous run.
"""
class TestTest(unittest.TestCase):
def test_foo(self):
sys.stderr.write('Test\n')
self._try_test_run(TestTest, """<testsuite errors="0" failures="0" name="unittest.TestSuite" tests="1" time="0.000">
<testcase classname="__main__.TestTest" name="test_foo" time="0.000"></testcase>
<system-out><![CDATA[]]></system-out>
<system-err><![CDATA[Test
]]></system-err>
</testsuite>
""")
class NullStream(object):
"""A file-like object that discards everything written to it."""
def write(self, buffer):
pass
def test_unittests_changing_stdout(self):
"""Check whether the XMLTestRunner recovers gracefully from unit tests
that change stdout, but don't change it back properly.
"""
class TestTest(unittest.TestCase):
def test_foo(self):
sys.stdout = XMLTestRunnerTest.NullStream()
runner = XMLTestRunner(self._stream)
runner.run(unittest.makeSuite(TestTest))
def test_unittests_changing_stderr(self):
"""Check whether the XMLTestRunner recovers gracefully from unit tests
that change stderr, but don't change it back properly.
"""
class TestTest(unittest.TestCase):
def test_foo(self):
sys.stderr = XMLTestRunnerTest.NullStream()
runner = XMLTestRunner(self._stream)
runner.run(unittest.makeSuite(TestTest))
class XMLTestProgram(unittest.TestProgram):
def runTests(self):
if self.testRunner is None:
self.testRunner = XMLTestRunner()
unittest.TestProgram.runTests(self)
main = XMLTestProgram
if __name__ == "__main__":
main(module=None)

View File

@ -1,77 +0,0 @@
# $Id: CGITemplate.py,v 1.6 2006/01/29 02:09:59 tavis_rudd Exp $
"""A subclass of Cheetah.Template for use in CGI scripts.
Usage in a template:
#extends Cheetah.Tools.CGITemplate
#implements respond
$cgiHeaders#slurp
Usage in a template inheriting a Python class:
1. The template
#extends MyPythonClass
#implements respond
$cgiHeaders#slurp
2. The Python class
from Cheetah.Tools import CGITemplate
class MyPythonClass(CGITemplate):
def cgiHeadersHook(self):
return "Content-Type: text/html; charset=koi8-r\n\n"
To read GET/POST variables, use the .webInput method defined in
Cheetah.Utils.WebInputMixin (available in all templates without importing
anything), use Python's 'cgi' module, or make your own arrangements.
This class inherits from Cheetah.Template to make it usable in Cheetah's
single-inheritance model.
Meta-Data
================================================================================
Author: Mike Orr <iron@mso.oz.net>
License: This software is released for unlimited distribution under the
terms of the MIT license. See the LICENSE file.
Version: $Revision: 1.6 $
Start Date: 2001/10/03
Last Revision Date: $Date: 2006/01/29 02:09:59 $
"""
__author__ = "Mike Orr <iron@mso.oz.net>"
__revision__ = "$Revision: 1.6 $"[11:-2]
import os
from Cheetah.Template import Template
class CGITemplate(Template):
"""Methods useful in CGI scripts.
Any class that inherits this mixin must also inherit Cheetah.Servlet.
"""
def cgiHeaders(self):
"""Outputs the CGI headers if this is a CGI script.
Usage: $cgiHeaders#slurp
Override .cgiHeadersHook() if you want to customize the headers.
"""
if self.isCgi():
return self.cgiHeadersHook()
def cgiHeadersHook(self):
"""Override if you want to customize the CGI headers.
"""
return "Content-type: text/html\n\n"
def isCgi(self):
"""Is this a CGI script?
"""
env = 'REQUEST_METHOD' in os.environ
wk = self._CHEETAH__isControlledByWebKit
return env and not wk
# vim: shiftwidth=4 tabstop=4 expandtab

View File

@ -1,464 +0,0 @@
"""
@@TR: This code is pretty much unsupported.
MondoReport.py -- Batching module for Python and Cheetah.
Version 2001-Nov-18. Doesn't do much practical yet, but the companion
testMondoReport.py passes all its tests.
-Mike Orr (Iron)
TODO: BatchRecord.prev/next/prev_batches/next_batches/query, prev.query,
next.query.
How about Report: .page(), .all(), .summary()? Or PageBreaker.
"""
import operator
try:
from functools import reduce
except ImportError:
# If functools doesn't exist, we must be on an old
# enough version that has reduce() in builtins
pass
try:
from Cheetah.NameMapper import valueForKey as lookup_func
except ImportError:
def lookup_func(obj, name):
if hasattr(obj, name):
return getattr(obj, name)
else:
return obj[name] # Raises KeyError.
########## PUBLIC GENERIC FUNCTIONS ##############################
class NegativeError(ValueError):
pass
def isNumeric(v):
return isinstance(v, (int, float))
def isNonNegative(v):
ret = isNumeric(v)
if ret and v < 0:
raise NegativeError(v)
def isNotNone(v):
return v is not None
def Roman(n):
n = int(n) # Raises TypeError.
if n < 1:
raise ValueError("roman numeral for zero or negative undefined: " + n)
roman = ''
while n >= 1000:
n = n - 1000
roman = roman + 'M'
while n >= 500:
n = n - 500
roman = roman + 'D'
while n >= 100:
n = n - 100
roman = roman + 'C'
while n >= 50:
n = n - 50
roman = roman + 'L'
while n >= 10:
n = n - 10
roman = roman + 'X'
while n >= 5:
n = n - 5
roman = roman + 'V'
while n < 5 and n >= 1:
n = n - 1
roman = roman + 'I'
roman = roman.replace('DCCCC', 'CM')
roman = roman.replace('CCCC', 'CD')
roman = roman.replace('LXXXX', 'XC')
roman = roman.replace('XXXX', 'XL')
roman = roman.replace('VIIII', 'IX')
roman = roman.replace('IIII', 'IV')
return roman
def sum(lis):
return reduce(operator.add, lis, 0)
def mean(lis):
"""Always returns a floating-point number.
"""
lis_len = len(lis)
if lis_len == 0:
return 0.00 # Avoid ZeroDivisionError (not raised for floats anyway)
total = float( sum(lis) )
return total / lis_len
def median(lis):
lis = sorted(lis[:])
return lis[int(len(lis)/2)]
def variance(lis):
raise NotImplementedError()
def variance_n(lis):
raise NotImplementedError()
def standardDeviation(lis):
raise NotImplementedError()
def standardDeviation_n(lis):
raise NotImplementedError()
class IndexFormats:
"""Eight ways to display a subscript index.
("Fifty ways to leave your lover....")
"""
def __init__(self, index, item=None):
self._index = index
self._number = index + 1
self._item = item
def index(self):
return self._index
__call__ = index
def number(self):
return self._number
def even(self):
return self._number % 2 == 0
def odd(self):
return not self.even()
def even_i(self):
return self._index % 2 == 0
def odd_i(self):
return not self.even_i()
def letter(self):
return self.Letter().lower()
def Letter(self):
n = ord('A') + self._index
return chr(n)
def roman(self):
return self.Roman().lower()
def Roman(self):
return Roman(self._number)
def item(self):
return self._item
########## PRIVATE CLASSES ##############################
class ValuesGetterMixin:
def __init__(self, origList):
self._origList = origList
def _getValues(self, field=None, criteria=None):
if field:
ret = [lookup_func(elm, field) for elm in self._origList]
else:
ret = self._origList
if criteria:
ret = list(filter(criteria, ret))
return ret
class RecordStats(IndexFormats, ValuesGetterMixin):
"""The statistics that depend on the current record.
"""
def __init__(self, origList, index):
record = origList[index] # Raises IndexError.
IndexFormats.__init__(self, index, record)
ValuesGetterMixin.__init__(self, origList)
def length(self):
return len(self._origList)
def first(self):
return self._index == 0
def last(self):
return self._index >= len(self._origList) - 1
def _firstOrLastValue(self, field, currentIndex, otherIndex):
currentValue = self._origList[currentIndex] # Raises IndexError.
try:
otherValue = self._origList[otherIndex]
except IndexError:
return True
if field:
currentValue = lookup_func(currentValue, field)
otherValue = lookup_func(otherValue, field)
return currentValue != otherValue
def firstValue(self, field=None):
return self._firstOrLastValue(field, self._index, self._index - 1)
def lastValue(self, field=None):
return self._firstOrLastValue(field, self._index, self._index + 1)
# firstPage and lastPage not implemented. Needed?
def percentOfTotal(self, field=None, suffix='%', default='N/A', decimals=2):
rec = self._origList[self._index]
if field:
val = lookup_func(rec, field)
else:
val = rec
try:
lis = self._getValues(field, isNumeric)
except NegativeError:
return default
total = sum(lis)
if total == 0.00: # Avoid ZeroDivisionError.
return default
val = float(val)
try:
percent = (val / total) * 100
except ZeroDivisionError:
return default
if decimals == 0:
percent = int(percent)
else:
percent = round(percent, decimals)
if suffix:
return str(percent) + suffix # String.
else:
return percent # Numeric.
def __call__(self): # Overrides IndexFormats.__call__
"""This instance is not callable, so we override the super method.
"""
raise NotImplementedError()
def prev(self):
if self._index == 0:
return None
else:
length = self.length()
start = self._index - length
return PrevNextPage(self._origList, length, start)
def next(self):
if self._index + self.length() == self.length():
return None
else:
length = self.length()
start = self._index + length
return PrevNextPage(self._origList, length, start)
def prevPages(self):
raise NotImplementedError()
def nextPages(self):
raise NotImplementedError()
prev_batches = prevPages
next_batches = nextPages
def summary(self):
raise NotImplementedError()
def _prevNextHelper(self, start, end, size, orphan, sequence):
"""Copied from Zope's DT_InSV.py's "opt" function.
"""
if size < 1:
if start > 0 and end > 0 and end >= start:
size=end+1-start
else: size=7
if start > 0:
try: sequence[start-1]
except: start=len(sequence)
# if start > l: start=l
if end > 0:
if end < start: end=start
else:
end=start+size-1
try: sequence[end+orphan-1]
except: end=len(sequence)
# if l - end < orphan: end=l
elif end > 0:
try: sequence[end-1]
except: end=len(sequence)
# if end > l: end=l
start=end+1-size
if start - 1 < orphan: start=1
else:
start=1
end=start+size-1
try: sequence[end+orphan-1]
except: end=len(sequence)
# if l - end < orphan: end=l
return start, end, size
class Summary(ValuesGetterMixin):
"""The summary statistics, that don't depend on the current record.
"""
def __init__(self, origList):
ValuesGetterMixin.__init__(self, origList)
def sum(self, field=None):
lis = self._getValues(field, isNumeric)
return sum(lis)
total = sum
def count(self, field=None):
lis = self._getValues(field, isNotNone)
return len(lis)
def min(self, field=None):
lis = self._getValues(field, isNotNone)
return min(lis) # Python builtin function min.
def max(self, field=None):
lis = self._getValues(field, isNotNone)
return max(lis) # Python builtin function max.
def mean(self, field=None):
"""Always returns a floating point number.
"""
lis = self._getValues(field, isNumeric)
return mean(lis)
average = mean
def median(self, field=None):
lis = self._getValues(field, isNumeric)
return median(lis)
def variance(self, field=None):
raiseNotImplementedError()
def variance_n(self, field=None):
raiseNotImplementedError()
def standardDeviation(self, field=None):
raiseNotImplementedError()
def standardDeviation_n(self, field=None):
raiseNotImplementedError()
class PrevNextPage:
def __init__(self, origList, size, start):
end = start + size
self.start = IndexFormats(start, origList[start])
self.end = IndexFormats(end, origList[end])
self.length = size
########## MAIN PUBLIC CLASS ##############################
class MondoReport:
_RecordStatsClass = RecordStats
_SummaryClass = Summary
def __init__(self, origlist):
self._origList = origlist
def page(self, size, start, overlap=0, orphan=0):
"""Returns list of ($r, $a, $b)
"""
if overlap != 0:
raise NotImplementedError("non-zero overlap")
if orphan != 0:
raise NotImplementedError("non-zero orphan")
origList = self._origList
origList_len = len(origList)
start = max(0, start)
end = min( start + size, len(self._origList) )
mySlice = origList[start:end]
ret = []
for rel in range(size):
abs_ = start + rel
r = mySlice[rel]
a = self._RecordStatsClass(origList, abs_)
b = self._RecordStatsClass(mySlice, rel)
tup = r, a, b
ret.append(tup)
return ret
batch = page
def all(self):
origList_len = len(self._origList)
return self.page(origList_len, 0, 0, 0)
def summary(self):
return self._SummaryClass(self._origList)
"""
**********************************
Return a pageful of records from a sequence, with statistics.
in : origlist, list or tuple. The entire set of records. This is
usually a list of objects or a list of dictionaries.
page, int >= 0. Which page to display.
size, int >= 1. How many records per page.
widow, int >=0. Not implemented.
orphan, int >=0. Not implemented.
base, int >=0. Number of first page (usually 0 or 1).
out: list of (o, b) pairs. The records for the current page. 'o' is
the original element from 'origlist' unchanged. 'b' is a Batch
object containing meta-info about 'o'.
exc: IndexError if 'page' or 'size' is < 1. If 'origlist' is empty or
'page' is too high, it returns an empty list rather than raising
an error.
origlist_len = len(origlist)
start = (page + base) * size
end = min(start + size, origlist_len)
ret = []
# widow, orphan calculation: adjust 'start' and 'end' up and down,
# Set 'widow', 'orphan', 'first_nonwidow', 'first_nonorphan' attributes.
for i in range(start, end):
o = origlist[i]
b = Batch(origlist, size, i)
tup = o, b
ret.append(tup)
return ret
def prev(self):
# return a PrevNextPage or None
def next(self):
# return a PrevNextPage or None
def prev_batches(self):
# return a list of SimpleBatch for the previous batches
def next_batches(self):
# return a list of SimpleBatch for the next batches
########## PUBLIC MIXIN CLASS FOR CHEETAH TEMPLATES ##############
class MondoReportMixin:
def batch(self, origList, size=None, start=0, overlap=0, orphan=0):
bat = MondoReport(origList)
return bat.batch(size, start, overlap, orphan)
def batchstats(self, origList):
bat = MondoReport(origList)
return bat.stats()
"""
# vim: shiftwidth=4 tabstop=4 expandtab textwidth=79

View File

@ -1,391 +0,0 @@
MondoReport Documentation
Version 0.01 alpha 24-Nov-2001. iron@mso.oz.net or mso@oz.net.
Copyright (c) 2001 Mike Orr. License: same as Python or Cheetah.
* * * * *
STATUS: previous/next batches and query string are not implemented yet.
Sorting not designed yet. Considering "click on this column header to sort by
this field" and multiple ascending/descending sort fields for a future version.
Tested with Python 2.2b1. May work with Python 2.1 or 2.0.
* * * * *
OVERVIEW
MondoReport -- provide information about a list that is useful in generating
any kind of report. The module consists of one main public class, and some
generic functions you may find useful in other programs. This file contains an
overview, syntax reference and examples. The module is designed both for
standalone use and for integration with the Cheetah template system
(http://www.cheetahtemplate.org/), so the examples are in both Python and
Cheetah. The main uses of MondoReport are:
(A) to iterate through a list. In this sense MR is a for-loop enhancer,
providing information that would be verbose to calculate otherwise.
(B) to separate a list into equal-size "pages" (or "batches"--the two terms are
interchangeable) and only display the current page, plus limited information
about the previous and next pages.
(C) to extract summary statistics about a certain column ("field") in the list.
* * * * *
MAIN PUBLIC CLASS
To create a MondoReport instance, supply a list to operate on.
mr = MondoReport(origList)
The list may be a list of anything, but if you use the 'field' argument in any
of the methods below, the elements must be instances or dictionaries.
MondoReport assumes it's operating on an unchanging list. Do not modify the
list or any of its elements until you are completely finished with the
ModoReport object and its sub-objects. Otherwise, you may get an exception or
incorrect results.
MondoReport instances have three methods:
.page(size, start, overlap=0, orphan=0
sort=None, reverse=False) => list of (r, a, b).
'size' is an integer >= 1. 'start', 'overlap' and 'orphan' are integers >= 0.
The list returned contains one triple for each record in the current page. 'r'
is the original record. 'a' is a BatchRecord instance for the current record
in relation to all records in the origList. 'b' is a BatchRecord instance for
the current record in relation to all the records in that batch/page. (There
is a .batch method that's identical to .page.)
The other options aren't implemented yet, but 'overlap' duplicates this many
records on adjacent batches. 'orphan' moves this many records or fewer, if
they are on a page alone, onto the neighboring page. 'sort' (string) specifies
a field to sort the records by. It may be suffixed by ":desc" to sort in
descending order. 'reverse' (boolean) reverses the sort order. If both
":desc" and 'reverse' are specified, they will cancel each other out. This
sorting/reversal happens on a copy of the origList, and all objects returned
by this method use the sorted list, except when resorting the next time.
To do more complicated sorting, such as a hierarchy of columns, do it to the
original list before creating the ModoReport object.
.all(sort=None, reverse=False) => list of (r, a).
Same, but the current page spans the entire origList.
.summary() => Summary instance.
Summary statistics for the entire origList.
In Python, use .page or .all in a for loop:
from Cheetah.Tools.MondoReport import MondoReport
mr = MondoReport(myList)
for r, a, b in mr.page(20, 40):
# Do something with r, a and b. The current page is the third page,
# with twenty records corresponding to origList[40:60].
if not myList:
# Warn the user there are no records in the list.
It works the same way in Cheetah, just convert to Cheetah syntax. This example
assumes the template doubles as a Webware servlet, so we use the servlet's
'$request' method to look up the CGI parameter 'start'. The default value is 0
for the first page.
#from Cheetah.Tools.MondoReport import MondoReport
#set $mr = $MondoReport($bigList)
#set $start = $request.field("start", 0)
#for $o, $a, $b in $mr.page(20, $start)
... do something with $o, $a and $b ...
#end for
#unless $bigList
This is displayed if the original list has no elements.
It's equivalent to the "else" part Zope DTML's <dtml-in>.
#end unless
* * * * *
USING 'r' RECORDS
Use 'r' just as you would the original element. For instance:
print r.attribute # If r is an instance.
print r['key'] # If r is a dictionary.
print r # If r is numeric or a string.
In Cheetah, you can take advantage of Universal Dotted Notation and autocalling:
$r.name ## 'name' may be an attribute or key of 'r'. If 'r' and/or
## 'name' is a function or method, it will be called without
## arguments.
$r.attribute
$r['key']
$r
$r().attribute()['key']()
If origList is a list of name/value pairs (2-tuples or 2-lists), you may
prefer to do this:
for (key, value), a, b in mr.page(20, 40):
print key, "=>", value
#for ($key, $value), $a, $b in $mr.page(20, $start)
$key =&gt; $value
#end for
* * * * *
STATISTICS METHODS AND FIELD VALUES
Certain methods below have an optional argument 'field'. If specified,
MondoReport will look up that field in each affected record and use its value
in the calculation. MondoReport uses Cheetah's NameMapper if available,
otherwise it uses a minimal NameMapper substitute that looks for an attribute
or dictionary key called "field". You'll get an exception if any record is a
type without attributes or keys, or if one or more records is missing that
attribute/key.
If 'field' is None, MondoReport will use the entire record in its
calculation. This makes sense mainly if the records are a numeric type.
All statistics methods filter out None values from their calculations, and
reduce the number of records accordingly. Most filter out non-numeric fields
(or records). Some raise NegativeError if a numeric field (or record) is
negative.
* * * * *
BatchRecord METHODS
The 'a' and 'b' objects of MondoReport.page() and MondoReport.all() provide
these methods.
.index()
The current subscript. For 'a', this is the true subscript into origList.
For 'b', this is relative to the current page, so the first record will be 0.
Hint: In Cheetah, use autocalling to skip the parentheses: '$b.index'.
.number()
The record's position starting from 1. This is always '.index() + 1'.
.Letter()
The letter ("A", "B", "C") corresponding to .number(). Undefined if .number()
> 26. The current implementation just adds the offset to 'a' and returns
whatever character it happens to be.
To make a less dumb implementation (e.g., "Z, AA, BB" or "Z, A1, B1"):
1) Subclass BatchRecord and override the .Letter method.
2) Subclass MondoReport and set the class variable .BatchRecordClass to your
new improved class.
.letter()
Same but lower case.
.Roman()
The Roman numeral corresponding to .number().
.roman()
Same but lower case.
.even()
True if .number() is even.
.odd()
True if .number() is odd.
.even_i()
True if .index() is even.
.odd_i()
True if .index() is odd.
.length()
For 'a', number of records in origList. For 'b', number of records on this
page.
.item()
The record itself. You don't need this in the normal case since it's the same
as 'r', but it's useful for previous/next batches.
.size()
The 'size' argument used when this BatchRecord was created.
'a.size() == b.size()'.
.first()
True if this is the first record.
.last()
True if this is the last record.
.firstValue(field=None)
True if there is no previous record, or if the previous field/record has a
different value. Used for to print section headers. For instance, if you
are printing addresses by country, this will be true at the first occurrance
of each country. Or for indexes, you can have a non-printing field showing
which letter of the alphablet this entry starts with, and then print a "B"
header before printing the first record starting with "B".
.lastValue(field=None)
True if this is the last record containing the current value in the
field/record.
.percentOfTotal(field=None, suffix="%", default="N/A", decimals=2)
Returns the percent that the current field/record is of all fields/records.
If 'suffix' is None, returns a number; otherwise it returns a string with
'suffix' suffixed. If the current value is non-numeric, returns 'default'
instead (without 'suffix'). 'decimals' tells the number of decimal places to
return; if 0, there will be no decimal point.
.prev()
Returns a PrevNextBatch instance for the previous page. If there is no
previous page, returns None. [Not implemented yet.]
.next()
Returns a PrevNextBatch instance for the next page. If there is no next page,
returns None. [Not implemented yet.]
.prevPages()
Returns a list of PrevNextPage instances for every previous page, or [] if no
previous pages. [Not implemented yet.]
.nextPages()
Returns a list of PrevNextPage instances for every next page, or [] if no next
pages. [Not implemented yet.]
.query(start=None, label=None, attribName="start", attribs=[])
[Not implemented yet.]
With no arguments, returns the HTML query string with start value removed (so
you can append a new start value in your hyperlink). The query string is taken
from the 'QUERY_STRING' environmental variable, or "" if missing. (This is
Webware compatible.)
With 'start' (an integer >= 0), returns the query string with an updated start
value, normally for the next or previous batch.
With 'label' (a string), returns a complete HTML hyperlink:
'<A HREF="?new_query_string">label</A>'. You'll get a TypeError if you specify
'label' but not 'start'.
With 'attribName' (a string), uses this attribute name rather than "start".
Useful if you have another CGI parameter "start" that's used for something
else.
With 'attribs' (a dictionary), adds these attributes to the hyperlink.
For instance, 'attribs={"target": "_blank"}'. Ignored unless 'label' is
specified too.
This method assumes the start parameter is a GET variable, not a POST variable.
.summary()
Returns a Summary instance. 'a.summary()' refers to all records in the
origList, so it's the same as MondoReport.summary(). 'b.summary()' refers only
to the records on the current page. [Not implemented yet.]
* * * * *
PrevNextPage INSTANCES
[Not implemented yet.]
PrevNextPage instances have the following methods:
.start()
The index (true index of origList) that that page starts at. You may also use
'.start().index()', '.start().number()', etc. Also
'.start().item(field=None)'. (Oh, so *that*'s what .item is for!)
.end()
The index (true index of origList) that that page ends at. You may also use
'.end().index()', '.end().number()', etc. Also
'.end().item(field=None)'.
.length()
Number of records on that page.
.query(label=None, attribName="start", attribs={}, before="", after="")
[Not implemented yet.]
Similar to 'a.query()' and 'b.query()', but automatically calculates the start
value for the appropriate page.
For fancy HTML formatting, 'before' is prepended to the returned text and
'after' is appended. (There was an argument 'else_' for if there is no such
batch, but it was removed because you can't even get to this method at all in
that case.)
* * * * * *
SUMMARY STATISTICS
These methods are supported by the Summary instances returned by
MondoReport.Summary():
.sum(field=None)
Sum of all numeric values in a field, or sum of all records.
.total(field=None)
Same.
.count(field=None)
Number of fields/records with non-None values.
.min(field=None)
Minimum value in that field/record. Ignores None values.
.max(field=None)
Maximum value in that field/record. Ignores None values.
.mean(field=None)
The mean (=average) of all numeric values in that field/record.
.average(field=None)
Same.
.median(field=None)
The median of all numeric values in that field/record. This is done by sorting
the values and taking the middle value.
.variance(field=None), .variance_n(field=None)
.standardDeviation(field=None), .standardDeviation_n(field=None)
[Not implemented yet.]
* * * * *
To run the regression tests (requires unittest.py, which is standard with
Python 2.2), run MondoReportTest.py from the command line. The regression test
double as usage examples.
# vim: shiftwidth=4 tabstop=4 expandtab textwidth=79

View File

@ -1,28 +0,0 @@
"""
Nothing, but in a friendly way. Good for filling in for objects you want to
hide. If $form.f1 is a RecursiveNull object, then
$form.f1.anything["you"].might("use") will resolve to the empty string.
This module was contributed by Ian Bicking.
"""
class RecursiveNull(object):
def __getattr__(self, attr):
return self
def __getitem__(self, item):
return self
def __call__(self, *args, **kwargs):
return self
def __str__(self):
return ''
def __repr__(self):
return ''
def __nonzero__(self):
return 0
def __eq__(self, x):
if x:
return False
return True
def __ne__(self, x):
return x and True or False

View File

@ -1,166 +0,0 @@
# $Id: SiteHierarchy.py,v 1.1 2001/10/11 03:25:54 tavis_rudd Exp $
"""Create menus and crumbs from a site hierarchy.
You define the site hierarchy as lists/tuples. Each location in the hierarchy
is a (url, description) tuple. Each list has the base URL/text in the 0
position, and all the children coming after it. Any child can be a list,
representing further depth to the hierarchy. See the end of the file for an
example hierarchy.
Use Hierarchy(contents, currentURL), where contents is this hierarchy, and
currentURL is the position you are currently in. The menubar and crumbs methods
give you the HTML output.
There are methods you can override to customize the HTML output.
"""
##################################################
## DEPENDENCIES
import string
try:
from cStringIO import StringIO
except ImportError:
from StringIO import StringIO
##################################################
## CLASSES
class Hierarchy:
def __init__(self, hierarchy, currentURL, prefix='', menuCSSClass=None,
crumbCSSClass=None):
"""
hierarchy is described above, currentURL should be somewhere in
the hierarchy. prefix will be added before all of the URLs (to
help mitigate the problems with absolute URLs), and if given,
cssClass will be used for both links *and* nonlinks.
"""
self._contents = hierarchy
self._currentURL = currentURL
if menuCSSClass:
self._menuCSSClass = ' class="%s"' % menuCSSClass
else:
self._menuCSSClass = ''
if crumbCSSClass:
self._crumbCSSClass = ' class="%s"' % crumbCSSClass
else:
self._crumbCSSClass = ''
self._prefix=prefix
## Main output methods
def menuList(self, menuCSSClass=None):
"""An indented menu list"""
if menuCSSClass:
self._menuCSSClass = ' class="%s"' % menuCSSClass
stream = StringIO()
for item in self._contents[1:]:
self._menubarRecurse(item, 0, stream)
return stream.getvalue()
def crumbs(self, crumbCSSClass=None):
"""The home>where>you>are crumbs"""
if crumbCSSClass:
self._crumbCSSClass = ' class="%s"' % crumbCSSClass
path = []
pos = self._contents
while True:
## This is not the fastest algorithm, I'm afraid.
## But it probably won't be for a huge hierarchy anyway.
foundAny = False
path.append(pos[0])
for item in pos[1:]:
if self._inContents(item):
if isinstance(item, tuple):
path.append(item)
break
else:
pos = item
foundAny = True
break
if not foundAny:
break
if len(path) == 1:
return self.emptyCrumb()
return string.join(map(lambda x, self=self: self.crumbLink(x[0], x[1]),
path), self.crumbSeperator()) + \
self.crumbTerminator()
## Methods to control the Aesthetics
# - override these methods for your own look
def menuLink(self, url, text, indent):
if url == self._currentURL or self._prefix + url == self._currentURL:
return '%s<B%s>%s</B> <BR>\n' % ('&nbsp;'*2*indent,
self._menuCSSClass, text)
else:
return '%s<A HREF="%s%s"%s>%s</A> <BR>\n' % \
('&nbsp;'*2*indent, self._prefix, url,
self._menuCSSClass, text)
def crumbLink(self, url, text):
if url == self._currentURL or self._prefix + url == self._currentURL:
return '<B%s>%s</B>' % (text, self._crumbCSSClass)
else:
return '<A HREF="%s%s"%s>%s</A>' % \
(self._prefix, url, self._crumbCSSClass, text)
def crumbSeperator(self):
return '&nbsp;&gt;&nbsp;'
def crumbTerminator(self):
return ''
def emptyCrumb(self):
"""When you are at the homepage"""
return ''
## internal methods
def _menubarRecurse(self, contents, indent, stream):
if isinstance(contents, tuple):
url, text = contents
rest = []
else:
url, text = contents[0]
rest = contents[1:]
stream.write(self.menuLink(url, text, indent))
if self._inContents(contents):
for item in rest:
self._menubarRecurse(item, indent+1, stream)
def _inContents(self, contents):
if isinstance(contents, tuple):
return self._currentURL == contents[0]
for item in contents:
if self._inContents(item):
return True
return False
##################################################
## from the command line
if __name__ == '__main__':
hierarchy = [('/', 'home'),
('/about', 'About Us'),
[('/services', 'Services'),
[('/services/products', 'Products'),
('/services/products/widget', 'The Widget'),
('/services/products/wedge', 'The Wedge'),
('/services/products/thimble', 'The Thimble'),
],
('/services/prices', 'Prices'),
],
('/contact', 'Contact Us'),
]
for url in ['/', '/services', '/services/products/widget', '/contact']:
print('<p>', '='*50)
print('<br> %s: <br>\n' % url)
n = Hierarchy(hierarchy, url, menuCSSClass='menu', crumbCSSClass='crumb',
prefix='/here')
print(n.menuList())
print('<p>', '-'*50)
print(n.crumbs())

View File

@ -1,8 +0,0 @@
"""This package contains classes, functions, objects and packages contributed
by Cheetah users. They are not used by Cheetah itself. There is no
guarantee that this directory will be included in Cheetah releases, that
these objects will remain here forever, or that they will remain
backward-compatible.
"""
# vim: shiftwidth=5 tabstop=5 expandtab

View File

@ -1,5 +0,0 @@
from turbocheetah import cheetahsupport
TurboCheetah = cheetahsupport.TurboCheetah
__all__ = ["TurboCheetah"]

View File

@ -1,110 +0,0 @@
"Template support for Cheetah"
import sys, os, imp
from Cheetah import Compiler
import pkg_resources
def _recompile_template(package, basename, tfile, classname):
tmpl = pkg_resources.resource_string(package, "%s.tmpl" % basename)
c = Compiler.Compiler(source=tmpl, mainClassName='GenTemplate')
code = str(c)
mod = imp.new_module(classname)
ns = dict()
exec(code, ns)
tempclass = ns.get("GenTemplate",
ns.get('DynamicallyCompiledCheetahTemplate'))
assert tempclass
tempclass.__name__ = basename
setattr(mod, basename, tempclass)
sys.modules[classname] = mod
return mod
class TurboCheetah:
extension = "tmpl"
def __init__(self, extra_vars_func=None, options=None):
if options is None:
options = dict()
self.get_extra_vars = extra_vars_func
self.options = options
self.compiledTemplates = {}
self.search_path = []
def load_template(self, template=None,
template_string=None, template_file=None,
loadingSite=False):
"""Searches for a template along the Python path.
Template files must end in ".tmpl" and be in legitimate packages.
"""
given = len([_f for _f in (template, template_string, template_file) if _f])
if given > 1:
raise TypeError(
"You may give only one of template, template_string, and "
"template_file")
if not given:
raise TypeError(
"You must give one of template, template_string, or "
"template_file")
if template:
return self.load_template_module(template)
elif template_string:
return self.load_template_string(template_string)
elif template_file:
return self.load_template_file(template_file)
def load_template_module(self, classname):
ct = self.compiledTemplates
divider = classname.rfind(".")
if divider > -1:
package = classname[0:divider]
basename = classname[divider+1:]
else:
raise ValueError("All templates must be in a package")
if not self.options.get("cheetah.precompiled", False):
tfile = pkg_resources.resource_filename(package,
"%s.%s" %
(basename,
self.extension))
if classname in ct:
mtime = os.stat(tfile).st_mtime
if ct[classname] != mtime:
ct[classname] = mtime
del sys.modules[classname]
mod = _recompile_template(package, basename,
tfile, classname)
else:
mod = __import__(classname, dict(), dict(), [basename])
else:
ct[classname] = os.stat(tfile).st_mtime
mod = _recompile_template(package, basename,
tfile, classname)
else:
mod = __import__(classname, dict(), dict(), [basename])
tempclass = getattr(mod, basename)
return tempclass
def load_template_string(self, content):
raise NotImplementedError
def load_template_file(self, filename):
raise NotImplementedError
def render(self, info, format="html", fragment=False, template=None,
template_string=None, template_file=None):
tclass = self.load_template(
template=template, template_string=template_string,
template_file=template_file)
if self.get_extra_vars:
extra = self.get_extra_vars()
else:
extra = {}
tempobj = tclass(searchList=[info, extra])
if fragment:
return tempobj.fragment()
else:
return tempobj.respond()

View File

@ -1,66 +0,0 @@
import os
from turbocheetah import TurboCheetah
here = os.path.dirname(__file__)
values = {
'v': 'VV',
'one': 1,
}
def test_normal():
plugin = TurboCheetah()
# Make sure a simple test works:
s = plugin.render(values, template='turbocheetah.tests.simple1')
assert s.strip() == 'This is a test: VV'
# Make sure one template can inherit from another:
s = plugin.render(values, template='turbocheetah.tests.import_inherit')
assert s.strip() == 'Inherited: import'
def test_path():
plugin = TurboCheetah()
plugin.search_path = [here]
# Make sure we pick up filenames (basic test):
s = plugin.render(values, template_file='simple1')
assert s.strip() == 'This is a test: VV'
# Make sure we pick up subdirectories:
s = plugin.render(values, template_file='sub/master')
assert s.strip() == 'sub1: 1'
def test_search():
plugin = TurboCheetah()
plugin.search_path = [os.path.join(here, 'sub'),
os.path.join(here, 'sub2'),
here]
# Pick up from third entry:
s = plugin.render(values, template_file='simple1')
assert s.strip() == 'This is a test: VV'
# Pick up from sub/master, non-ambiguous:
s = plugin.render(values, template_file='master')
assert s.strip() == 'sub1: 1'
# Pick up from sub/page, inherit from sub/template:
s = plugin.render(values, template_file='page')
assert s.strip() == 'SUB: sub content'
# Pick up from sub2/page_over, inherit from sub/template:
s = plugin.render(values, template_file='page_over')
assert s.strip() == 'SUB: override content'
# Pick up from sub/page_template_over, inherit from
# sub2/template_over:
s = plugin.render(values, template_file='page_template_over')
assert s.strip() == 'OVER: sub content'
# Change page, make sure that undoes overrides:
plugin.search_path = [os.path.join(here, 'sub'),
here]
s = plugin.render(values, template_file='page_over')
assert s.strip() == 'SUB: sub content'
def test_string():
# Make sure simple string evaluation works:
plugin = TurboCheetah()
s = plugin.render(values, template_string="""Hey $v""")
assert s == "Hey VV"
# Make sure a string can inherit from a file:
plugin.search_path = [here]
s = plugin.render(values, template_string="#extends inherit_from\ns value")
assert s.strip() == 'inherit: s value'

View File

@ -1,9 +0,0 @@
try:
from ds.sys.Unspecified import Unspecified
except ImportError:
class _Unspecified:
def __repr__(self):
return 'Unspecified'
def __str__(self):
return 'Unspecified'
Unspecified = _Unspecified()

View File

@ -1,123 +0,0 @@
"""
Indentation maker.
@@TR: this code is unsupported and largely undocumented ...
This version is based directly on code by Robert Kuzelj
<robert_kuzelj@yahoo.com> and uses his directive syntax. Some classes and
attributes have been renamed. Indentation is output via
$self._CHEETAH__indenter.indent() to prevent '_indenter' being looked up on the
searchList and another one being found. The directive syntax will
soon be changed somewhat.
"""
import re
import sys
def indentize(source):
return IndentProcessor().process(source)
class IndentProcessor(object):
"""Preprocess #indent tags."""
LINE_SEP = '\n'
ARGS = "args"
INDENT_DIR = re.compile(r'[ \t]*#indent[ \t]*(?P<args>.*)')
DIRECTIVE = re.compile(r"[ \t]*#")
WS = "ws"
WHITESPACES = re.compile(r"(?P<ws>[ \t]*)")
INC = "++"
DEC = "--"
SET = "="
CHAR = "char"
ON = "on"
OFF = "off"
PUSH = "push"
POP = "pop"
def process(self, _txt):
result = []
for line in _txt.splitlines():
match = self.INDENT_DIR.match(line)
if match:
#is indention directive
args = match.group(self.ARGS).strip()
if args == self.ON:
line = "#silent $self._CHEETAH__indenter.on()"
elif args == self.OFF:
line = "#silent $self._CHEETAH__indenter.off()"
elif args == self.INC:
line = "#silent $self._CHEETAH__indenter.inc()"
elif args == self.DEC:
line = "#silent $self._CHEETAH__indenter.dec()"
elif args.startswith(self.SET):
level = int(args[1:])
line = "#silent $self._CHEETAH__indenter.setLevel(%(level)d)" % {"level":level}
elif args.startswith('chars'):
self.indentChars = eval(args.split('=')[1])
line = "#silent $self._CHEETAH__indenter.setChars(%(level)d)" % {"level":level}
elif args.startswith(self.PUSH):
line = "#silent $self._CHEETAH__indenter.push()"
elif args.startswith(self.POP):
line = "#silent $self._CHEETAH__indenter.pop()"
else:
match = self.DIRECTIVE.match(line)
if not match:
#is not another directive
match = self.WHITESPACES.match(line)
if match:
size = len(match.group("ws").expandtabs(4))
line = ("${self._CHEETAH__indenter.indent(%(size)d)}" % {"size":size}) + line.lstrip()
else:
line = "${self._CHEETAH__indenter.indent(0)}" + line
result.append(line)
return self.LINE_SEP.join(result)
class Indenter(object):
"""
A class that keeps track of the current indentation level.
.indent() returns the appropriate amount of indentation.
"""
On = 1
Level = 0
Chars = ' '
LevelStack = []
def on(self):
self.On = 1
def off(self):
self.On = 0
def inc(self):
self.Level += 1
def dec(self):
"""decrement can only be applied to values greater zero
values below zero don't make any sense at all!"""
if self.Level > 0:
self.Level -= 1
def push(self):
self.LevelStack.append(self.Level)
def pop(self):
"""the levestack can not become -1. any attempt to do so
sets the level to 0!"""
if len(self.LevelStack) > 0:
self.Level = self.LevelStack.pop()
else:
self.Level = 0
def setLevel(self, _level):
"""the leve can't be less than zero. any attempt to do so
sets the level automatically to zero!"""
if _level < 0:
self.Level = 0
else:
self.Level = _level
def setChar(self, _chars):
self.Chars = _chars
def indent(self, _default=0):
if self.On:
return self.Chars * self.Level
return " " * _default

View File

@ -1,67 +0,0 @@
#!/usr/bin/env python
"""
Miscellaneous functions/objects used by Cheetah but also useful standalone.
"""
import os # Used in mkdirsWithPyInitFile.
import sys # Used in die.
##################################################
## MISCELLANEOUS FUNCTIONS
def die(reason):
sys.stderr.write(reason + '\n')
sys.exit(1)
def useOrRaise(thing, errmsg=''):
"""Raise 'thing' if it's a subclass of Exception. Otherwise return it.
Called by: Cheetah.Servlet.cgiImport()
"""
if isinstance(thing, type) and issubclass(thing, Exception):
raise thing(errmsg)
return thing
def checkKeywords(dic, legalKeywords, what='argument'):
"""Verify no illegal keyword arguments were passed to a function.
in : dic, dictionary (**kw in the calling routine).
legalKeywords, list of strings, the keywords that are allowed.
what, string, suffix for error message (see function source).
out: None.
exc: TypeError if 'dic' contains a key not in 'legalKeywords'.
called by: Cheetah.Template.__init__()
"""
# XXX legalKeywords could be a set when sets get added to Python.
for k in dic.keys(): # Can be dic.iterkeys() if Python >= 2.2.
if k not in legalKeywords:
raise TypeError("'%s' is not a valid %s" % (k, what))
def removeFromList(list_, *elements):
"""Save as list_.remove(each element) but don't raise an error if
element is missing. Modifies 'list_' in place! Returns None.
"""
for elm in elements:
try:
list_.remove(elm)
except ValueError:
pass
def mkdirsWithPyInitFiles(path):
"""Same as os.makedirs (mkdir 'path' and all missing parent directories)
but also puts a Python '__init__.py' file in every directory it
creates. Does nothing (without creating an '__init__.py' file) if the
directory already exists.
"""
dir, fil = os.path.split(path)
if dir and not os.path.exists(dir):
mkdirsWithPyInitFiles(dir)
if not os.path.exists(path):
os.mkdir(path)
init = os.path.join(path, "__init__.py")
f = open(init, 'w') # Open and close to produce empty file.
f.close()
# vim: shiftwidth=4 tabstop=4 expandtab

View File

@ -1,102 +0,0 @@
# $Id: WebInputMixin.py,v 1.10 2006/01/06 21:56:54 tavis_rudd Exp $
"""Provides helpers for Template.webInput(), a method for importing web
transaction variables in bulk. See the docstring of webInput for full details.
Meta-Data
================================================================================
Author: Mike Orr <iron@mso.oz.net>
License: This software is released for unlimited distribution under the
terms of the MIT license. See the LICENSE file.
Version: $Revision: 1.10 $
Start Date: 2002/03/17
Last Revision Date: $Date: 2006/01/06 21:56:54 $
"""
__author__ = "Mike Orr <iron@mso.oz.net>"
__revision__ = "$Revision: 1.10 $"[11:-2]
from Cheetah.Utils.Misc import useOrRaise
class NonNumericInputError(ValueError): pass
##################################################
## PRIVATE FUNCTIONS AND CLASSES
class _Converter:
"""A container object for info about type converters.
.name, string, name of this converter (for error messages).
.func, function, factory function.
.default, value to use or raise if the real value is missing.
.error, value to use or raise if .func() raises an exception.
"""
def __init__(self, name, func, default, error):
self.name = name
self.func = func
self.default = default
self.error = error
def _lookup(name, func, multi, converters):
"""Look up a Webware field/cookie/value/session value. Return
'(realName, value)' where 'realName' is like 'name' but with any
conversion suffix strips off. Applies numeric conversion and
single vs multi values according to the comments in the source.
"""
# Step 1 -- split off the conversion suffix from 'name'; e.g. "height:int".
# If there's no colon, the suffix is "". 'longName' is the name with the
# suffix, 'shortName' is without.
# XXX This implementation assumes "height:" means "height".
colon = name.find(':')
if colon != -1:
longName = name
shortName, ext = name[:colon], name[colon+1:]
else:
longName = shortName = name
ext = ''
# Step 2 -- look up the values by calling 'func'.
if longName != shortName:
values = func(longName, None) or func(shortName, None)
else:
values = func(shortName, None)
# 'values' is a list of strings, a string or None.
# Step 3 -- Coerce 'values' to a list of zero, one or more strings.
if values is None:
values = []
elif isinstance(values, str):
values = [values]
# Step 4 -- Find a _Converter object or raise TypeError.
try:
converter = converters[ext]
except KeyError:
fmt = "'%s' is not a valid converter name in '%s'"
tup = (ext, longName)
raise TypeError(fmt % tup)
# Step 5 -- if there's a converter func, run it on each element.
# If the converter raises an exception, use or raise 'converter.error'.
if converter.func is not None:
tmp = values[:]
values = []
for elm in tmp:
try:
elm = converter.func(elm)
except (TypeError, ValueError):
tup = converter.name, elm
errmsg = "%s '%s' contains invalid characters" % tup
elm = useOrRaise(converter.error, errmsg)
values.append(elm)
# 'values' is now a list of strings, ints or floats.
# Step 6 -- If we're supposed to return a multi value, return the list
# as is. If we're supposed to return a single value and the list is
# empty, return or raise 'converter.default'. Otherwise, return the
# first element in the list and ignore any additional values.
if multi:
return shortName, values
if len(values) == 0:
return shortName, useOrRaise(converter.default)
return shortName, values[0]
# vim: sw=4 ts=4 expandtab

View File

@ -1 +0,0 @@
#

View File

@ -1,14 +0,0 @@
"""This is a copy of the htmlDecode function in Webware.
@@TR: It implemented more efficiently.
"""
from Cheetah.Utils.htmlEncode import htmlCodesReversed
def htmlDecode(s, codes=htmlCodesReversed):
""" Returns the ASCII decoded version of the given HTML string. This does
NOT remove normal HTML tags like <p>. It is the inverse of htmlEncode()."""
for code in codes:
s = s.replace(code[1], code[0])
return s

View File

@ -1,21 +0,0 @@
"""This is a copy of the htmlEncode function in Webware.
@@TR: It implemented more efficiently.
"""
htmlCodes = [
['&', '&amp;'],
['<', '&lt;'],
['>', '&gt;'],
['"', '&quot;'],
]
htmlCodesReversed = htmlCodes[:]
htmlCodesReversed.reverse()
def htmlEncode(s, codes=htmlCodes):
""" Returns the HTML encoded version of the given string. This is useful to
display a plain ASCII text string on a web page."""
for code in codes:
s = s.replace(code[0], code[1])
return s

View File

@ -1,304 +0,0 @@
## statprof.py
## Copyright (C) 2004,2005 Andy Wingo <wingo at pobox dot com>
## Copyright (C) 2001 Rob Browning <rlb at defaultvalue dot org>
## This library is free software; you can redistribute it and/or
## modify it under the terms of the GNU Lesser General Public
## License as published by the Free Software Foundation; either
## version 2.1 of the License, or (at your option) any later version.
##
## This library is distributed in the hope that it will be useful,
## but WITHOUT ANY WARRANTY; without even the implied warranty of
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
## Lesser General Public License for more details.
##
## You should have received a copy of the GNU Lesser General Public
## License along with this program; if not, contact:
##
## Free Software Foundation Voice: +1-617-542-5942
## 59 Temple Place - Suite 330 Fax: +1-617-542-2652
## Boston, MA 02111-1307, USA gnu@gnu.org
"""
statprof is intended to be a fairly simple statistical profiler for
python. It was ported directly from a statistical profiler for guile,
also named statprof, available from guile-lib [0].
[0] http://wingolog.org/software/guile-lib/statprof/
To start profiling, call statprof.start():
>>> start()
Then run whatever it is that you want to profile, for example:
>>> import test.pystone; test.pystone.pystones()
Then stop the profiling and print out the results:
>>> stop()
>>> display()
% cumulative self
time seconds seconds name
26.72 1.40 0.37 pystone.py:79:Proc0
13.79 0.56 0.19 pystone.py:133:Proc1
13.79 0.19 0.19 pystone.py:208:Proc8
10.34 0.16 0.14 pystone.py:229:Func2
6.90 0.10 0.10 pystone.py:45:__init__
4.31 0.16 0.06 pystone.py:53:copy
...
All of the numerical data with the exception of the calls column is
statistically approximate. In the following column descriptions, and
in all of statprof, "time" refers to execution time (both user and
system), not wall clock time.
% time
The percent of the time spent inside the procedure itself (not
counting children).
cumulative seconds
The total number of seconds spent in the procedure, including
children.
self seconds
The total number of seconds spent in the procedure itself (not
counting children).
name
The name of the procedure.
By default statprof keeps the data collected from previous runs. If you
want to clear the collected data, call reset():
>>> reset()
reset() can also be used to change the sampling frequency. For example,
to tell statprof to sample 50 times a second:
>>> reset(50)
This means that statprof will sample the call stack after every 1/50 of
a second of user + system time spent running on behalf of the python
process. When your process is idle (for example, blocking in a read(),
as is the case at the listener), the clock does not advance. For this
reason statprof is not currently not suitable for profiling io-bound
operations.
The profiler uses the hash of the code object itself to identify the
procedures, so it won't confuse different procedures with the same name.
They will show up as two different rows in the output.
Right now the profiler is quite simplistic. I cannot provide
call-graphs or other higher level information. What you see in the
table is pretty much all there is. Patches are welcome :-)
Threading
---------
Because signals only get delivered to the main thread in Python,
statprof only profiles the main thread. However because the time
reporting function uses per-process timers, the results can be
significantly off if other threads' work patterns are not similar to the
main thread's work patterns.
Implementation notes
--------------------
The profiler works by setting the unix profiling signal ITIMER_PROF to
go off after the interval you define in the call to reset(). When the
signal fires, a sampling routine is run which looks at the current
procedure that's executing, and then crawls up the stack, and for each
frame encountered, increments that frame's code object's sample count.
Note that if a procedure is encountered multiple times on a given stack,
it is only counted once. After the sampling is complete, the profiler
resets profiling timer to fire again after the appropriate interval.
Meanwhile, the profiler keeps track, via os.times(), how much CPU time
(system and user -- which is also what ITIMER_PROF tracks), has elapsed
while code has been executing within a start()/stop() block.
The profiler also tries to avoid counting or timing its own code as
much as possible.
"""
try:
import itimer
except ImportError:
raise ImportError('''statprof requires the itimer python extension.
To install it, enter the following commands from a terminal:
wget http://www.cute.fi/~torppa/py-itimer/py-itimer.tar.gz
tar zxvf py-itimer.tar.gz
cd py-itimer
sudo python setup.py install
''')
import signal
import os
__all__ = ['start', 'stop', 'reset', 'display']
###########################################################################
## Utils
def clock():
times = os.times()
return times[0] + times[1]
###########################################################################
## Collection data structures
class ProfileState(object):
def __init__(self, frequency=None):
self.reset(frequency)
def reset(self, frequency=None):
# total so far
self.accumulated_time = 0.0
# start_time when timer is active
self.last_start_time = None
# total count of sampler calls
self.sample_count = 0
# a float
if frequency:
self.sample_interval = 1.0/frequency
elif not hasattr(self, 'sample_interval'):
# default to 100 Hz
self.sample_interval = 1.0/100.0
else:
# leave the frequency as it was
pass
self.remaining_prof_time = None
# for user start/stop nesting
self.profile_level = 0
# whether to catch apply-frame
self.count_calls = False
# gc time between start() and stop()
self.gc_time_taken = 0
def accumulate_time(self, stop_time):
self.accumulated_time += stop_time - self.last_start_time
state = ProfileState()
## call_data := { code object: CallData }
call_data = {}
class CallData(object):
def __init__(self, code):
self.name = code.co_name
self.filename = code.co_filename
self.lineno = code.co_firstlineno
self.call_count = 0
self.cum_sample_count = 0
self.self_sample_count = 0
call_data[code] = self
def get_call_data(code):
return call_data.get(code, None) or CallData(code)
###########################################################################
## SIGPROF handler
def sample_stack_procs(frame):
state.sample_count += 1
get_call_data(frame.f_code).self_sample_count += 1
code_seen = {}
while frame:
code_seen[frame.f_code] = True
frame = frame.f_back
for code in code_seen.iterkeys():
get_call_data(code).cum_sample_count += 1
def profile_signal_handler(signum, frame):
if state.profile_level > 0:
state.accumulate_time(clock())
sample_stack_procs(frame)
itimer.setitimer(itimer.ITIMER_PROF,
state.sample_interval, 0.0)
state.last_start_time = clock()
###########################################################################
## Profiling API
def is_active():
return state.profile_level > 0
def start():
state.profile_level += 1
if state.profile_level == 1:
state.last_start_time = clock()
rpt = state.remaining_prof_time
state.remaining_prof_time = None
signal.signal(signal.SIGPROF, profile_signal_handler)
itimer.setitimer(itimer.ITIMER_PROF,
rpt or state.sample_interval, 0.0)
state.gc_time_taken = 0 # dunno
def stop():
state.profile_level -= 1
if state.profile_level == 0:
state.accumulate_time(clock())
state.last_start_time = None
rpt = itimer.setitimer(itimer.ITIMER_PROF, 0.0, 0.0)
signal.signal(signal.SIGPROF, signal.SIG_IGN)
state.remaining_prof_time = rpt[0]
state.gc_time_taken = 0 # dunno
def reset(frequency=None):
assert state.profile_level == 0, "Can't reset() while statprof is running"
call_data.clear()
state.reset(frequency)
###########################################################################
## Reporting API
class CallStats(object):
def __init__(self, call_data):
self_samples = call_data.self_sample_count
cum_samples = call_data.cum_sample_count
nsamples = state.sample_count
secs_per_sample = state.accumulated_time / nsamples
basename = os.path.basename(call_data.filename)
self.name = '%s:%d:%s' % (basename, call_data.lineno, call_data.name)
self.pcnt_time_in_proc = self_samples / nsamples * 100
self.cum_secs_in_proc = cum_samples * secs_per_sample
self.self_secs_in_proc = self_samples * secs_per_sample
self.num_calls = None
self.self_secs_per_call = None
self.cum_secs_per_call = None
def display(self):
print('%6.2f %9.2f %9.2f %s' % (self.pcnt_time_in_proc,
self.cum_secs_in_proc,
self.self_secs_in_proc,
self.name))
def display():
if state.sample_count == 0:
print('No samples recorded.')
return
l = [CallStats(x) for x in call_data.itervalues()]
l = [(x.self_secs_in_proc, x.cum_secs_in_proc, x) for x in l]
l.sort(reverse=True)
l = [x[2] for x in l]
print('%5.5s %10.10s %7.7s %-8.8s' % ('% ', 'cumulative', 'self', ''))
print('%5.5s %9.9s %8.8s %-8.8s' % ("time", "seconds", "seconds", "name"))
for x in l:
x.display()
print('---')
print('Sample count: %d' % state.sample_count)
print('Total time: %f seconds' % state.accumulated_time)

View File

@ -1,58 +0,0 @@
Version = '2.4.3'
VersionTuple = (2, 4, 3, 'development', 0)
MinCompatibleVersion = '2.0rc6'
MinCompatibleVersionTuple = (2, 0, 0, 'candidate', 6)
####
def convertVersionStringToTuple(s):
versionNum = [0, 0, 0]
releaseType = 'final'
releaseTypeSubNum = 0
if s.find('a')!=-1:
num, releaseTypeSubNum = s.split('a')
releaseType = 'alpha'
elif s.find('b')!=-1:
num, releaseTypeSubNum = s.split('b')
releaseType = 'beta'
elif s.find('rc')!=-1:
num, releaseTypeSubNum = s.split('rc')
releaseType = 'candidate'
else:
num = s
num = num.split('.')
for i in range(len(num)):
versionNum[i] = int(num[i])
if len(versionNum)<3:
versionNum += [0]
releaseTypeSubNum = int(releaseTypeSubNum)
return tuple(versionNum+[releaseType, releaseTypeSubNum])
if __name__ == '__main__':
c = convertVersionStringToTuple
print(c('2.0a1'))
print(c('2.0b1'))
print(c('2.0rc1'))
print(c('2.0'))
print(c('2.0.2'))
assert c('0.9.19b1') < c('0.9.19')
assert c('0.9b1') < c('0.9.19')
assert c('2.0a2') > c('2.0a1')
assert c('2.0b1') > c('2.0a2')
assert c('2.0b2') > c('2.0b1')
assert c('2.0b2') == c('2.0b2')
assert c('2.0rc1') > c('2.0b1')
assert c('2.0rc2') > c('2.0rc1')
assert c('2.0rc2') > c('2.0b1')
assert c('2.0') > c('2.0a1')
assert c('2.0') > c('2.0b1')
assert c('2.0') > c('2.0rc1')
assert c('2.0.1') > c('2.0')
assert c('2.0rc1') > c('2.0b1')

View File

@ -1,20 +0,0 @@
'''
Cheetah is an open source template engine and code generation tool.
It can be used standalone or combined with other tools and frameworks. Web
development is its principle use, but Cheetah is very flexible and is also being
used to generate C++ game code, Java, sql, form emails and even Python code.
Homepage
http://www.cheetahtemplate.org/
Documentation
http://cheetahtemplate.org/learn.html
Mailing list
cheetahtemplate-discuss@lists.sourceforge.net
Subscribe at
http://lists.sourceforge.net/lists/listinfo/cheetahtemplate-discuss
'''
from Version import *

View File

@ -1,494 +0,0 @@
/* ***************************************************************************
This is the C language version of NameMapper.py. See the comments and
DocStrings in NameMapper for details on the purpose and interface of this
module.
===============================================================================
$Id: _namemapper.c,v 1.34 2007/12/10 18:25:20 tavis_rudd Exp $
Authors: Tavis Rudd <tavis@damnsimple.com>
Version: $Revision: 1.34 $
Start Date: 2001/08/07
Last Revision Date: $Date: 2007/12/10 18:25:20 $
*/
/* *************************************************************************** */
#include <Python.h>
#include <string.h>
#include <stdlib.h>
#include "cheetah.h"
#ifdef __cplusplus
extern "C" {
#endif
static PyObject *NotFound; /* locally-raised exception */
static PyObject *TooManyPeriods; /* locally-raised exception */
static PyObject* pprintMod_pformat; /* used for exception formatting */
/* *************************************************************************** */
/* First the c versions of the functions */
/* *************************************************************************** */
static void setNotFoundException(char *key, PyObject *namespace)
{
PyObject *exceptionStr = NULL;
exceptionStr = PyUnicode_FromFormat("cannot find \'%s\'", key);
PyErr_SetObject(NotFound, exceptionStr);
Py_XDECREF(exceptionStr);
}
static int wrapInternalNotFoundException(char *fullName, PyObject *namespace)
{
PyObject *excType, *excValue, *excTraceback, *isAlreadyWrapped = NULL;
PyObject *newExcValue = NULL;
if (!ALLOW_WRAPPING_OF_NOTFOUND_EXCEPTIONS) {
return 0;
}
if (!PyErr_Occurred()) {
return 0;
}
if (PyErr_GivenExceptionMatches(PyErr_Occurred(), NotFound)) {
PyErr_Fetch(&excType, &excValue, &excTraceback);
isAlreadyWrapped = PyObject_CallMethod(excValue, "find", "s", "while searching");
if (isAlreadyWrapped != NULL) {
if (PyLong_AsLong(isAlreadyWrapped) == -1) {
newExcValue = PyUnicode_FromFormat("%U while searching for \'%s\'",
excValue, fullName);
}
Py_DECREF(isAlreadyWrapped);
}
else {
newExcValue = excValue;
}
PyErr_Restore(excType, newExcValue, excTraceback);
return -1;
}
return 0;
}
static int isInstanceOrClass(PyObject *nextVal) {
#ifndef IS_PYTHON3
/* old style classes or instances */
if((PyInstance_Check(nextVal)) || (PyClass_Check(nextVal))) {
return 1;
}
#endif
if (!PyObject_HasAttrString(nextVal, "__class__")) {
return 0;
}
/* new style classes or instances */
if (PyType_Check(nextVal) || PyObject_HasAttrString(nextVal, "mro")) {
return 1;
}
if (strncmp(nextVal->ob_type->tp_name, "function", 9) == 0)
return 0;
/* method, func, or builtin func */
if (PyObject_HasAttrString(nextVal, "im_func")
|| PyObject_HasAttrString(nextVal, "func_code")
|| PyObject_HasAttrString(nextVal, "__self__")) {
return 0;
}
/* instance */
if ((!PyObject_HasAttrString(nextVal, "mro")) &&
PyObject_HasAttrString(nextVal, "__init__")) {
return 1;
}
return 0;
}
static int getNameChunks(char *nameChunks[], char *name, char *nameCopy)
{
char c;
char *currChunk;
int currChunkNum = 0;
currChunk = nameCopy;
while ('\0' != (c = *nameCopy)){
if ('.' == c) {
if (currChunkNum >= (MAXCHUNKS-2)) { /* avoid overflowing nameChunks[] */
PyErr_SetString(TooManyPeriods, name);
return 0;
}
*nameCopy ='\0';
nameChunks[currChunkNum++] = currChunk;
nameCopy++;
currChunk = nameCopy;
} else
nameCopy++;
}
if (nameCopy > currChunk) {
nameChunks[currChunkNum++] = currChunk;
}
return currChunkNum;
}
static int PyNamemapper_hasKey(PyObject *obj, char *key)
{
if (PyMapping_Check(obj) && PyMapping_HasKeyString(obj, key)) {
return TRUE;
} else if (PyObject_HasAttrString(obj, key)) {
return TRUE;
}
return FALSE;
}
static PyObject *PyNamemapper_valueForKey(PyObject *obj, char *key)
{
PyObject *theValue = NULL;
if (PyMapping_Check(obj) && PyMapping_HasKeyString(obj, key)) {
theValue = PyMapping_GetItemString(obj, key);
} else if (PyObject_HasAttrString(obj, key)) {
theValue = PyObject_GetAttrString(obj, key);
} else {
setNotFoundException(key, obj);
}
return theValue;
}
static PyObject *PyNamemapper_valueForName(PyObject *obj, char *nameChunks[], int numChunks, int executeCallables)
{
int i;
char *currentKey;
PyObject *currentVal = NULL;
PyObject *nextVal = NULL;
currentVal = obj;
for (i=0; i < numChunks;i++) {
currentKey = nameChunks[i];
if (PyErr_CheckSignals()) { /* not sure if I really need to do this here, but what the hell */
if (i>0) {
Py_DECREF(currentVal);
}
return NULL;
}
if (PyMapping_Check(currentVal) && PyMapping_HasKeyString(currentVal, currentKey)) {
nextVal = PyMapping_GetItemString(currentVal, currentKey);
}
else {
PyObject *exc;
nextVal = PyObject_GetAttrString(currentVal, currentKey);
exc = PyErr_Occurred();
if (exc != NULL) {
// if exception == AttributeError, report our own exception
if (PyErr_ExceptionMatches(PyExc_AttributeError)) {
setNotFoundException(currentKey, currentVal);
}
// any exceptions results in failure
if (i > 0) {
Py_DECREF(currentVal);
}
return NULL;
}
}
if (i > 0) {
Py_DECREF(currentVal);
}
if (executeCallables && PyCallable_Check(nextVal) &&
(isInstanceOrClass(nextVal) == 0) ) {
if (!(currentVal = PyObject_CallObject(nextVal, NULL))) {
Py_DECREF(nextVal);
return NULL;
}
Py_DECREF(nextVal);
} else {
currentVal = nextVal;
}
}
return currentVal;
}
/* *************************************************************************** */
/* Now the wrapper functions to export into the Python module */
/* *************************************************************************** */
static PyObject *namemapper_valueForKey(PyObject *self, PyObject *args)
{
PyObject *obj;
char *key;
if (!PyArg_ParseTuple(args, "Os", &obj, &key)) {
return NULL;
}
return PyNamemapper_valueForKey(obj, key);
}
static PyObject *namemapper_valueForName(PYARGS)
{
PyObject *obj;
char *name;
int executeCallables = 0;
char *nameCopy = NULL;
char *tmpPntr1 = NULL;
char *tmpPntr2 = NULL;
char *nameChunks[MAXCHUNKS];
int numChunks;
PyObject *theValue;
static char *kwlist[] = {"obj", "name", "executeCallables", NULL};
if (!PyArg_ParseTupleAndKeywords(args, kwargs, "Os|i", kwlist, &obj, &name, &executeCallables)) {
return NULL;
}
createNameCopyAndChunks();
theValue = PyNamemapper_valueForName(obj, nameChunks, numChunks, executeCallables);
free(nameCopy);
if (wrapInternalNotFoundException(name, obj)) {
theValue = NULL;
}
return theValue;
}
static PyObject *namemapper_valueFromSearchList(PYARGS)
{
PyObject *searchList;
char *name;
int executeCallables = 0;
char *nameCopy = NULL;
char *tmpPntr1 = NULL;
char *tmpPntr2 = NULL;
char *nameChunks[MAXCHUNKS];
int numChunks;
PyObject *nameSpace = NULL;
PyObject *theValue = NULL;
PyObject *iterator = NULL;
static char *kwlist[] = {"searchList", "name", "executeCallables", NULL};
if (!PyArg_ParseTupleAndKeywords(args, kwargs, "Os|i", kwlist, &searchList, &name, &executeCallables)) {
return NULL;
}
createNameCopyAndChunks();
iterator = PyObject_GetIter(searchList);
if (iterator == NULL) {
PyErr_SetString(PyExc_TypeError,"This searchList is not iterable!");
goto done;
}
while ((nameSpace = PyIter_Next(iterator))) {
checkForNameInNameSpaceAndReturnIfFound(TRUE);
Py_DECREF(nameSpace);
if(PyErr_CheckSignals()) {
theValue = NULL;
goto done;
}
}
if (PyErr_Occurred()) {
theValue = NULL;
goto done;
}
setNotFoundException(nameChunks[0], searchList);
done:
Py_XDECREF(iterator);
free(nameCopy);
return theValue;
}
static PyObject *namemapper_valueFromFrameOrSearchList(PyObject *self, PyObject *args, PyObject *keywds)
{
/* python function args */
char *name;
int executeCallables = 0;
PyObject *searchList = NULL;
/* locals */
char *nameCopy = NULL;
char *tmpPntr1 = NULL;
char *tmpPntr2 = NULL;
char *nameChunks[MAXCHUNKS];
int numChunks;
PyObject *nameSpace = NULL;
PyObject *theValue = NULL;
PyObject *excString = NULL;
PyObject *iterator = NULL;
static char *kwlist[] = {"searchList", "name", "executeCallables", NULL};
if (!PyArg_ParseTupleAndKeywords(args, keywds, "Os|i", kwlist, &searchList, &name,
&executeCallables)) {
return NULL;
}
createNameCopyAndChunks();
nameSpace = PyEval_GetLocals();
checkForNameInNameSpaceAndReturnIfFound(FALSE);
iterator = PyObject_GetIter(searchList);
if (iterator == NULL) {
PyErr_SetString(PyExc_TypeError,"This searchList is not iterable!");
goto done;
}
while ( (nameSpace = PyIter_Next(iterator)) ) {
checkForNameInNameSpaceAndReturnIfFound(TRUE);
Py_DECREF(nameSpace);
if(PyErr_CheckSignals()) {
theValue = NULL;
goto done;
}
}
if (PyErr_Occurred()) {
theValue = NULL;
goto done;
}
nameSpace = PyEval_GetGlobals();
checkForNameInNameSpaceAndReturnIfFound(FALSE);
nameSpace = PyEval_GetBuiltins();
checkForNameInNameSpaceAndReturnIfFound(FALSE);
excString = Py_BuildValue("s", "[locals()]+searchList+[globals(), __builtins__]");
setNotFoundException(nameChunks[0], excString);
Py_DECREF(excString);
done:
Py_XDECREF(iterator);
free(nameCopy);
return theValue;
}
static PyObject *namemapper_valueFromFrame(PyObject *self, PyObject *args, PyObject *keywds)
{
/* python function args */
char *name;
int executeCallables = 0;
/* locals */
char *tmpPntr1 = NULL;
char *tmpPntr2 = NULL;
char *nameCopy = NULL;
char *nameChunks[MAXCHUNKS];
int numChunks;
PyObject *nameSpace = NULL;
PyObject *theValue = NULL;
PyObject *excString = NULL;
static char *kwlist[] = {"name", "executeCallables", NULL};
if (!PyArg_ParseTupleAndKeywords(args, keywds, "s|i", kwlist, &name, &executeCallables)) {
return NULL;
}
createNameCopyAndChunks();
nameSpace = PyEval_GetLocals();
checkForNameInNameSpaceAndReturnIfFound(FALSE);
nameSpace = PyEval_GetGlobals();
checkForNameInNameSpaceAndReturnIfFound(FALSE);
nameSpace = PyEval_GetBuiltins();
checkForNameInNameSpaceAndReturnIfFound(FALSE);
excString = Py_BuildValue("s", "[locals(), globals(), __builtins__]");
setNotFoundException(nameChunks[0], excString);
Py_DECREF(excString);
done:
free(nameCopy);
return theValue;
}
/* *************************************************************************** */
/* Method registration table: name-string -> function-pointer */
static struct PyMethodDef namemapper_methods[] = {
{"valueForKey", namemapper_valueForKey, 1},
{"valueForName", (PyCFunction)namemapper_valueForName, METH_VARARGS|METH_KEYWORDS},
{"valueFromSearchList", (PyCFunction)namemapper_valueFromSearchList, METH_VARARGS|METH_KEYWORDS},
{"valueFromFrame", (PyCFunction)namemapper_valueFromFrame, METH_VARARGS|METH_KEYWORDS},
{"valueFromFrameOrSearchList", (PyCFunction)namemapper_valueFromFrameOrSearchList, METH_VARARGS|METH_KEYWORDS},
{NULL, NULL}
};
/* *************************************************************************** */
/* Initialization function (import-time) */
#ifdef IS_PYTHON3
static struct PyModuleDef namemappermodule = {
PyModuleDef_HEAD_INIT,
"_namemapper",
NULL, /* docstring */
-1,
namemapper_methods,
NULL,
NULL,
NULL,
NULL};
PyMODINIT_FUNC PyInit__namemapper(void)
{
PyObject *m = PyModule_Create(&namemappermodule);
#else
DL_EXPORT(void) init_namemapper(void)
{
PyObject *m = Py_InitModule3("_namemapper", namemapper_methods, NULL);
#endif
PyObject *d, *pprintMod;
/* add symbolic constants to the module */
d = PyModule_GetDict(m);
NotFound = PyErr_NewException("NameMapper.NotFound",PyExc_LookupError,NULL);
TooManyPeriods = PyErr_NewException("NameMapper.TooManyPeriodsInName",NULL,NULL);
PyDict_SetItemString(d, "NotFound", NotFound);
PyDict_SetItemString(d, "TooManyPeriodsInName", TooManyPeriods);
pprintMod = PyImport_ImportModule("pprint");
if (!pprintMod) {
#ifdef IS_PYTHON3
return NULL;
#else
return;
#endif
}
pprintMod_pformat = PyObject_GetAttrString(pprintMod, "pformat");
Py_DECREF(pprintMod);
/* check for errors */
if (PyErr_Occurred()) {
Py_FatalError("Can't initialize module _namemapper");
}
#ifdef IS_PYTHON3
return m;
#endif
}
#ifdef __cplusplus
}
#endif

View File

@ -1,78 +0,0 @@
/*
* Copyright 2009, R. Tyler Ballance <tyler@monkeypox.org>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. Neither the name of R. Tyler Ballance nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
* IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef _CHEETAH_H_
#define _CHEETAH_H_
#include <Python.h>
#if PY_MAJOR_VERSION >= 3
#define IS_PYTHON3
#endif
#define TRUE 1
#define FALSE 0
#define PYARGS PyObject *self, PyObject *args, PyObject *kwargs
/*
* _namemapper.c specific definitions
*/
#define MAXCHUNKS 15 /* max num of nameChunks for the arrays */
#define ALLOW_WRAPPING_OF_NOTFOUND_EXCEPTIONS 1
#define createNameCopyAndChunks() {\
nameCopy = malloc(strlen(name) + 1);\
tmpPntr1 = name; \
tmpPntr2 = nameCopy;\
while ((*tmpPntr2++ = *tmpPntr1++)); \
numChunks = getNameChunks(nameChunks, name, nameCopy); \
if (PyErr_Occurred()) { /* there might have been TooManyPeriods */\
free(nameCopy);\
return NULL;\
}\
}
#define checkForNameInNameSpaceAndReturnIfFound(namespace_decref) { \
if ( PyNamemapper_hasKey(nameSpace, nameChunks[0]) ) {\
theValue = PyNamemapper_valueForName(nameSpace, nameChunks, numChunks, executeCallables);\
if (namespace_decref) {\
Py_DECREF(nameSpace);\
}\
if (wrapInternalNotFoundException(name, nameSpace)) {\
theValue = NULL;\
}\
goto done;\
}\
}
#endif

View File

@ -1,20 +0,0 @@
import os.path
import string
letters = None
try:
letters = string.ascii_letters
except AttributeError:
letters = string.letters
l = ['_'] * 256
for c in string.digits + letters:
l[ord(c)] = c
_pathNameTransChars = ''.join(l)
del l, c
def convertTmplPathToModuleName(tmplPath,
_pathNameTransChars=_pathNameTransChars,
splitdrive=os.path.splitdrive,
):
return splitdrive(tmplPath)[1].translate(_pathNameTransChars)

View File

@ -6,7 +6,7 @@ __docformat__ = 'restructuredtext en'
import os, re
from lxml import etree
from Cheetah.Template import Template
from templite import Templite
from calibre.customize.conversion import OutputFormatPlugin
from calibre import CurrentDir
@ -29,7 +29,8 @@ class HTMLOutput(OutputFormatPlugin):
output_path = re.sub(r'\.html', '', output_path)+'_files'
with open(output_file, 'wb') as f:
root = oeb_book.html_toc()
link_prefix=os.path.basename(output_path)+'/'
root = oeb_book.html_toc(link_prefix=link_prefix)
html_txt = etree.tostring(root, pretty_print=True, encoding='utf-8', xml_declaration=False)
f.write(html_txt)
@ -70,13 +71,13 @@ class HTMLOutput(OutputFormatPlugin):
else:
prevLink = None
vars = {
'ebookContent': ebook_content,
'prevLink': prevLink,
'nextLink': nextLink
}
template_file = os.path.dirname(__file__)+'/outputtemplates/default.tmpl'
t = Template(file=template_file, searchList=[ vars ]) # compilerSettings={'useStackFrames': False}
templite = Templite(open(template_file).read())
t = templite.render(ebookContent=ebook_content, prevLink=prevLink, nextLink=nextLink)
with open(path, 'wb') as f:
f.write(str(t))
f.write(t)
item.unload_data_from_memory(memory=path)

View File

@ -5,17 +5,17 @@
<title>Unbenanntes Dokument</title>
</head>
<body>
$ebookContent
#if $prevLink
<a href="$prevLink">previous page</a>
#end if
${ebookContent}$
#if $nextLink
<a href="$nextLink">next page</a>
#end if
${if prevLink:}$
<a href="${prevLink}$">previous page</a>
${:endif}$
${if nextLink:}$
<a href="${nextLink}$">next page</a>
${:endif}$
</body>
</html>

View File

@ -1644,19 +1644,20 @@ class TOC(object):
node.to_ncx(point)
return parent
def to_xhtml(self, parent=None):
def to_xhtml(self, parent=None, link_prefix=''):
if parent is None:
parent = etree.Element(XHTML('ul'))
elif len(self.nodes):
parent = element(parent, (XHTML('ul')))
for node in self.nodes:
point = element(parent, XHTML('li'))
link = element(point, XHTML('a'), href=urlunquote(node.href))
href = link_prefix+urlunquote(node.href)
link = element(point, XHTML('a'), href=href)
title = node.title
if title:
title = re.sub(r'\s+', ' ', title)
link.text=title
node.to_xhtml(point)
node.to_xhtml(point, link_prefix=link_prefix)
return parent
def rationalize_play_orders(self):
@ -1978,7 +1979,7 @@ class OEBBook(object):
results[PAGE_MAP_MIME] = (href, self.pages.to_page_map())
return results
def html_toc(self):
def html_toc(self, link_prefix=''):
lang = unicode(self.metadata.language[0])
html = etree.Element(XHTML('html'),
attrib={XML('lang'): lang},
@ -1986,5 +1987,5 @@ class OEBBook(object):
head = etree.SubElement(html, XHTML('head'))
title = etree.SubElement(head, XHTML('title'))
body = etree.SubElement(html, XHTML('body'))
body.append(self.toc.to_xhtml())
body.append(self.toc.to_xhtml(link_prefix=link_prefix))
return html

87
src/templite/__init__.py Normal file
View File

@ -0,0 +1,87 @@
#!/usr/bin/env python
#
# Templite+
# A light-weight, fully functional, general purpose templating engine
#
# Copyright (c) 2009 joonis new media
# Author: Thimo Kraemer <thimo.kraemer@joonis.de>
#
# Based on Templite - Tomer Filiba
# http://code.activestate.com/recipes/496702/
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
# MA 02110-1301, USA.
#
import sys, re
class Templite(object):
auto_emit = re.compile('(^[\'\"])|(^[a-zA-Z0-9_\[\]\'\"]+$)')
def __init__(self, template, start='${', end='}$'):
if len(start) != 2 or len(end) != 2:
raise ValueError('each delimiter must be two characters long')
delimiter = re.compile('%s(.*?)%s' % (re.escape(start), re.escape(end)), re.DOTALL)
offset = 0
tokens = []
for i, part in enumerate(delimiter.split(template)):
part = part.replace('\\'.join(list(start)), start)
part = part.replace('\\'.join(list(end)), end)
if i % 2 == 0:
if not part: continue
part = part.replace('\\', '\\\\').replace('"', '\\"')
part = '\t' * offset + 'emit("""%s""")' % part
else:
part = part.rstrip()
if not part: continue
if part.lstrip().startswith(':'):
if not offset:
raise SyntaxError('no block statement to terminate: ${%s}$' % part)
offset -= 1
part = part.lstrip()[1:]
if not part.endswith(':'): continue
elif self.auto_emit.match(part.lstrip()):
part = 'emit(%s)' % part.lstrip()
lines = part.splitlines()
margin = min(len(l) - len(l.lstrip()) for l in lines if l.strip())
part = '\n'.join('\t' * offset + l[margin:] for l in lines)
if part.endswith(':'):
offset += 1
tokens.append(part)
if offset:
raise SyntaxError('%i block statement(s) not terminated' % offset)
self.__code = compile('\n'.join(tokens), '<templite %r>' % template[:20], 'exec')
def render(self, __namespace=None, **kw):
"""
renders the template according to the given namespace.
__namespace - a dictionary serving as a namespace for evaluation
**kw - keyword arguments which are added to the namespace
"""
namespace = {}
if __namespace: namespace.update(__namespace)
if kw: namespace.update(kw)
namespace['emit'] = self.write
__stdout = sys.stdout
sys.stdout = self
self.__output = []
eval(self.__code, namespace)
sys.stdout = __stdout
return ''.join(self.__output)
def write(self, *args):
for a in args:
self.__output.append(str(a))