diff options
author | R. Tyler Ballance <tyler@monkeypox.org> | 2009-12-19 15:27:36 -0800 |
---|---|---|
committer | R. Tyler Ballance <tyler@monkeypox.org> | 2009-12-19 15:27:36 -0800 |
commit | a753724d68880488a27141d1e4f174880d39e154 (patch) | |
tree | 26cdf49d72e1b967e9e072a9f7a291e971f86468 /cheetah | |
parent | 7b1c2ad9f4e5830b5c88d95715014bb4044c090c (diff) | |
parent | ffa377c79b12f6b3674cba2267f186d37078d0d7 (diff) | |
download | python-cheetah-a753724d68880488a27141d1e4f174880d39e154.tar.gz |
Merge branch 'next'
Diffstat (limited to 'cheetah')
44 files changed, 947 insertions, 2092 deletions
diff --git a/cheetah/CacheRegion.py b/cheetah/CacheRegion.py index dd0d099..2586b72 100644 --- a/cheetah/CacheRegion.py +++ b/cheetah/CacheRegion.py @@ -128,7 +128,7 @@ class CacheRegion(object): """ cacheItemID = md5(str(cacheItemID)).hexdigest() - if not self._cacheItems.has_key(cacheItemID): + if cacheItemID not in self._cacheItems: cacheItem = self._cacheItemClass( cacheItemID=cacheItemID, cacheStore=self._wrappedCacheDataStore) self._cacheItems[cacheItemID] = cacheItem diff --git a/cheetah/CacheStore.py b/cheetah/CacheStore.py index 9c41656..8f57f33 100644 --- a/cheetah/CacheStore.py +++ b/cheetah/CacheStore.py @@ -46,12 +46,12 @@ class MemoryCacheStore(AbstractCacheStore): self._data[key] = (val, time) def add(self, key, val, time=0): - if self._data.has_key(key): + 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 self._data.has_key(key): + if key in self._data: raise Error('a value for key %r is already in the cache'%key) self._data[key] = (val, time) diff --git a/cheetah/CheetahWrapper.py b/cheetah/CheetahWrapper.py index a7fef80..6203e0b 100644 --- a/cheetah/CheetahWrapper.py +++ b/cheetah/CheetahWrapper.py @@ -161,7 +161,8 @@ class CheetahWrapper(object): 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='Verbosely print informational messages to stdout') + 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') @@ -194,14 +195,14 @@ Files are %s""", args, pprint.pformat(vars(opts)), files) if opts.print_settings: - print - print '>> Available Cheetah compiler 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 + print('\t%s (default: "%s")\t%s' % l) sys.exit(0) #cleanup trailing path separators @@ -222,7 +223,6 @@ Files are %s""", args, pprint.pformat(vars(opts)), files) unpickled = pickle.load(f) f.close() self.searchList.insert(0, unpickled) - opts.verbose = not opts.stdout ################################################## ## COMMAND METHODS @@ -266,7 +266,7 @@ you do have write permission to and re-run the tests.""") runner.run(unittest.TestSuite(Test.suites)) def version(self): - print Version + print(Version) # If you add a command, also add it to the 'meths' variable in main(). @@ -387,12 +387,11 @@ you do have write permission to and re-run the tests.""") isError = False dstSources = {} for b in bundles: - if dstSources.has_key(b.dst): + if b.dst in dstSources: dstSources[b.dst].append(b.src) else: dstSources[b.dst] = [b.src] - keys = dstSources.keys() - keys.sort() + keys = sorted(dstSources.keys()) for dst in keys: sources = dstSources[dst] if len(sources) > 1: @@ -537,7 +536,7 @@ you do have write permission to and re-run the tests.""") return kws if self.opts.compilerSettingsString: try: - exec 'settings = getkws(%s)'%self.opts.compilerSettingsString + 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" diff --git a/cheetah/Compiler.py b/cheetah/Compiler.py index 39c7f51..8946710 100644 --- a/cheetah/Compiler.py +++ b/cheetah/Compiler.py @@ -25,8 +25,8 @@ from Cheetah.Utils.Indenter import indentize # an undocumented preprocessor from Cheetah import ErrorCatchers from Cheetah import NameMapper from Cheetah.Parser import Parser, ParseError, specialVarRE, \ - STATIC_CACHE, REFRESH_CACHE, SET_LOCAL, SET_GLOBAL,SET_MODULE, \ - unicodeDirectiveRE, encodingDirectiveRE,escapedNewlineRE + STATIC_CACHE, REFRESH_CACHE, SET_LOCAL, SET_GLOBAL, SET_MODULE, \ + unicodeDirectiveRE, encodingDirectiveRE, escapedNewlineRE from Cheetah.NameMapper import NotFound, valueForName, valueFromSearchList, valueFromFrameOrSearchList VFFSL=valueFromFrameOrSearchList @@ -56,8 +56,8 @@ _DEFAULT_COMPILER_SETTINGS = [ ('commentOffset', 1, ''), ('outputRowColComments', True, ''), ('includeBlockMarkers', False, 'Wrap #block\'s in a comment in the template\'s output'), - ('blockMarkerStart', ('\n<!-- START BLOCK: ',' -->\n'), ''), - ('blockMarkerEnd', ('\n<!-- END BLOCK: ',' -->\n'), ''), + ('blockMarkerStart', ('\n<!-- START BLOCK: ', ' -->\n'), ''), + ('blockMarkerEnd', ('\n<!-- END BLOCK: ', ' -->\n'), ''), ('defDocStrMsg', 'Autogenerated by Cheetah: The Python-Powered Template Engine', ''), ('setup__str__method', False, ''), ('mainMethodName', 'respond', ''), @@ -379,13 +379,13 @@ class MethodCompiler(GenUtils): ind = self._indent*2 docStr = (ind + '"""\n' + ind + - ('\n' + ind).join([ln.replace('"""',"'''") for ln in self._docStringLines]) + + ('\n' + ind).join([ln.replace('"""', "'''") for ln in self._docStringLines]) + '\n' + ind + '"""\n') return docStr ## methods for adding code def addMethDocString(self, line): - self._docStringLines.append(line.replace('%','%%')) + self._docStringLines.append(line.replace('%', '%%')) def addChunk(self, chunk): self.commitStrConst() @@ -418,7 +418,7 @@ class MethodCompiler(GenUtils): self.addChunk("if _v is not None: write(str(_v))") else: if self.setting('useFilters'): - self.addChunk("write(_filter(%s%s))"%(chunk,filterArgs)) + self.addChunk("write(_filter(%s%s))"%(chunk, filterArgs)) else: self.addChunk("write(str(%s))"%chunk) @@ -428,48 +428,35 @@ class MethodCompiler(GenUtils): else: self._pendingStrConstChunks = [strConst] - def _unescapeCheetahVars(self, theString): - """Unescape any escaped Cheetah \$vars in the string. - """ - - token = self.setting('cheetahVarStartToken') - return theString.replace('\\' + token, token) - - def _unescapeDirectives(self, theString): - """Unescape any escaped Cheetah \$vars in the string. - """ - - token = self.setting('directiveStartToken') - return theString.replace('\\' + token, token) - def commitStrConst(self): """Add the code for outputting the pending strConst without chopping off any whitespace from it. """ - if self._pendingStrConstChunks: - strConst = self._unescapeCheetahVars(''.join(self._pendingStrConstChunks)) - strConst = self._unescapeDirectives(strConst) - self._pendingStrConstChunks = [] - if not strConst: - return - else: - reprstr = repr(strConst).replace('\\012','\n') - i = 0 - out = [] - if reprstr.startswith('u'): - i = 1 - out = ['u'] - body = escapedNewlineRE.sub('\n', reprstr[i+1:-1]) - - if reprstr[i]=="'": - out.append("'''") - out.append(body) - out.append("'''") - else: - out.append('"""') - out.append(body) - out.append('"""') - self.addWriteChunk(''.join(out)) + if not self._pendingStrConstChunks: + return + + strConst = ''.join(self._pendingStrConstChunks) + self._pendingStrConstChunks = [] + if not strConst: + return + + reprstr = repr(strConst) + i = 0 + out = [] + if reprstr.startswith('u'): + i = 1 + out = ['u'] + body = escapedNewlineRE.sub('\\1\n', reprstr[i+1:-1]) + + if reprstr[i]=="'": + out.append("'''") + out.append(body) + out.append("'''") + else: + out.append('"""') + out.append(body) + out.append('"""') + self.addWriteChunk(''.join(out)) def handleWSBeforeDirective(self): """Truncate the pending strCont to the beginning of the current line. @@ -545,7 +532,7 @@ class MethodCompiler(GenUtils): splitPos2 = LVALUE.find('[') if splitPos1 > 0 and splitPos2==-1: splitPos = splitPos1 - elif splitPos1 > 0 and splitPos1 < max(splitPos2,0): + elif splitPos1 > 0 and splitPos1 < max(splitPos2, 0): splitPos = splitPos1 else: splitPos = splitPos2 @@ -579,7 +566,7 @@ class MethodCompiler(GenUtils): def addRepeat(self, expr, lineCol=None): #the _repeatCount stuff here allows nesting of #repeat directives self._repeatCount = getattr(self, "_repeatCount", -1) + 1 - self.addFor('for __i%s in range(%s)' % (self._repeatCount,expr), lineCol=lineCol) + self.addFor('for __i%s in range(%s)' % (self._repeatCount, expr), lineCol=lineCol) def addIndentingDirective(self, expr, lineCol=None): if expr and not expr[-1] == ':': @@ -623,7 +610,7 @@ class MethodCompiler(GenUtils): self.dedent() def addElse(self, expr, dedent=True, lineCol=None): - expr = re.sub(r'else[ \f\t]+if','elif', expr) + expr = re.sub(r'else[ \f\t]+if', 'elif', expr) self.addReIndentingDirective(expr, dedent=dedent, lineCol=lineCol) def addElif(self, expr, dedent=True, lineCol=None): @@ -660,7 +647,7 @@ class MethodCompiler(GenUtils): def addYield(self, expr): assert not self._hasReturnStatement self._isGenerator = True - if expr.replace('yield','').strip(): + if expr.replace('yield', '').strip(): self.addChunk(expr) else: self.addChunk('if _dummyTrans:') @@ -727,9 +714,9 @@ class MethodCompiler(GenUtils): # @@TR: we should add some runtime logging to this ID = self.nextCacheID() - interval = cacheInfo.get('interval',None) - test = cacheInfo.get('test',None) - customID = cacheInfo.get('id',None) + interval = cacheInfo.get('interval', None) + test = cacheInfo.get('test', None) + customID = cacheInfo.get('id', None) if customID: ID = customID varyBy = cacheInfo.get('varyBy', repr(ID)) @@ -896,7 +883,7 @@ class MethodCompiler(GenUtils): captureDetails.assignTo = assignTo captureDetails.lineCol = lineCol - self._captureRegionsStack.append((ID,captureDetails)) # attrib of current methodCompiler + self._captureRegionsStack.append((ID, captureDetails)) # attrib of current methodCompiler self.addChunk('## START CAPTURE REGION: '+ID +' '+assignTo +' at line %s, col %s'%lineCol + ' in the source.') @@ -982,13 +969,13 @@ class AutoMethodCompiler(MethodCompiler): def _setupState(self): MethodCompiler._setupState(self) - self._argStringList = [ ("self",None) ] + self._argStringList = [ ("self", None) ] self._streamingEnabled = True self._isClassMethod = None self._isStaticMethod = None def _useKWsDictArgForPassingTrans(self): - alreadyHasTransArg = [argname for argname,defval in self._argStringList + alreadyHasTransArg = [argname for argname, defval in self._argStringList if argname=='trans'] return (self.methodName()!='respond' and not alreadyHasTransArg @@ -1015,12 +1002,12 @@ class AutoMethodCompiler(MethodCompiler): if self._streamingEnabled: kwargsName = None positionalArgsListName = None - for argname,defval in self._argStringList: + for argname, defval in self._argStringList: if argname.strip().startswith('**'): - kwargsName = argname.strip().replace('**','') + kwargsName = argname.strip().replace('**', '') break elif argname.strip().startswith('*'): - positionalArgsListName = argname.strip().replace('*','') + positionalArgsListName = argname.strip().replace('*', '') if not kwargsName and self._useKWsDictArgForPassingTrans(): kwargsName = 'KWS' @@ -1100,7 +1087,7 @@ class AutoMethodCompiler(MethodCompiler): self.addChunk('return _dummyTrans and trans.response().getvalue() or ""') def addMethArg(self, name, defVal=None): - self._argStringList.append( (name,defVal) ) + self._argStringList.append( (name, defVal) ) def methodSignature(self): argStringChunks = [] @@ -1136,7 +1123,7 @@ if not self._CHEETAH__instanceInitialized: for k,v in KWs.items(): if k in allowedKWs: cheetahKWArgs[k] = v self._initCheetahInstance(**cheetahKWArgs) -""".replace('\n','\n'+' '*8) +""".replace('\n', '\n'+' '*8) class ClassCompiler(GenUtils): methodCompilerClass = AutoMethodCompiler @@ -1173,14 +1160,14 @@ class ClassCompiler(GenUtils): from the methods of this class!!! or you will be assigning to attributes of this object instead.""" - if self.__dict__.has_key(name): + if name in self.__dict__: return self.__dict__[name] elif hasattr(self.__class__, name): return getattr(self.__class__, name) elif self._activeMethodsList and hasattr(self._activeMethodsList[-1], name): return getattr(self._activeMethodsList[-1], name) else: - raise AttributeError, name + raise AttributeError(name) def _setupState(self): self._classDef = None @@ -1333,9 +1320,9 @@ class ClassCompiler(GenUtils): self._decoratorsForNextMethod.append(decoratorExpr) def addClassDocString(self, line): - self._classDocStringLines.append( line.replace('%','%%')) + self._classDocStringLines.append( line.replace('%', '%%')) - def addChunkToInit(self,chunk): + def addChunkToInit(self, chunk): self._initMethChunks.append(chunk) def addAttribute(self, attribExpr): @@ -1364,7 +1351,7 @@ class ClassCompiler(GenUtils): 'super(%(className)s, self).%(methodName)s(%(argString)s)'%locals()) def addErrorCatcherCall(self, codeChunk, rawCode='', lineCol=''): - if self._placeholderToErrorCatcherMap.has_key(rawCode): + if rawCode in self._placeholderToErrorCatcherMap: methodName = self._placeholderToErrorCatcherMap[rawCode] if not self.setting('outputRowColComments'): self._methodsIndex[methodName].addMethDocString( @@ -1601,14 +1588,14 @@ class ModuleCompiler(SettingsManager, GenUtils): from the methods of this class!!! or you will be assigning to attributes of this object instead. """ - if self.__dict__.has_key(name): + if name in self.__dict__: return self.__dict__[name] elif hasattr(self.__class__, name): return getattr(self.__class__, name) elif self._activeClassesList and hasattr(self._activeClassesList[-1], name): return getattr(self._activeClassesList[-1], name) else: - raise AttributeError, name + raise AttributeError(name) def _initializeSettings(self): self.updateSettings(copy.deepcopy(DEFAULT_COMPILER_SETTINGS)) @@ -1848,7 +1835,7 @@ class ModuleCompiler(SettingsManager, GenUtils): self._getActiveClassCompiler().addAttribute(attribName + ' =' + expr) def addComment(self, comm): - if re.match(r'#+$',comm): # skip bar comments + if re.match(r'#+$', comm): # skip bar comments return specialVarMatch = specialVarRE.match(comm) @@ -1935,14 +1922,14 @@ if not hasattr(%(mainClassName)s, '_initCheetahAttributes'): templateAPIClass._addCheetahPlumbingCodeToClass(%(mainClassName)s) %(footer)s -""" % {'header':self.moduleHeader(), - 'docstring':self.moduleDocstring(), - 'specialVars':self.specialVars(), - 'imports':self.importStatements(), - 'constants':self.moduleConstants(), - 'classes':self.classDefs(), - 'footer':self.moduleFooter(), - 'mainClassName':self._mainClassName, +""" % {'header': self.moduleHeader(), + 'docstring': self.moduleDocstring(), + 'specialVars': self.specialVars(), + 'imports': self.importStatements(), + 'constants': self.moduleConstants(), + 'classes': self.classDefs(), + 'footer': self.moduleFooter(), + 'mainClassName': self._mainClassName, } self._moduleDef = moduleDef @@ -1976,8 +1963,7 @@ if not hasattr(%(mainClassName)s, '_initCheetahAttributes'): def specialVars(self): chunks = [] theVars = self._specialVars - keys = theVars.keys() - keys.sort() + keys = sorted(theVars.keys()) for key in keys: chunks.append(key + ' = ' + repr(theVars[key]) ) return '\n'.join(chunks) diff --git a/cheetah/DirectiveAnalyzer.py b/cheetah/DirectiveAnalyzer.py new file mode 100644 index 0000000..a9f9387 --- /dev/null +++ b/cheetah/DirectiveAnalyzer.py @@ -0,0 +1,98 @@ +#!/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() + diff --git a/cheetah/DummyTransaction.py b/cheetah/DummyTransaction.py index f84ade4..72f8662 100644 --- a/cheetah/DummyTransaction.py +++ b/cheetah/DummyTransaction.py @@ -92,7 +92,7 @@ class TransformerResponse(DummyResponse): output = super(TransformerResponse, self).getvalue(**kwargs) if self._filter: _filter = self._filter - if isinstance(_filter, types.TypeType): + if isinstance(_filter, type): _filter = _filter() return _filter.filter(output) return output diff --git a/cheetah/FileUtils.py b/cheetah/FileUtils.py index 97e77f8..c4e65f3 100644 --- a/cheetah/FileUtils.py +++ b/cheetah/FileUtils.py @@ -1,30 +1,14 @@ -# $Id: FileUtils.py,v 1.12 2005/11/02 22:26:07 tavis_rudd Exp $ -"""File utitilies for Python: - -Meta-Data -================================================================================ -Author: Tavis Rudd <tavis@damnsimple.com> -License: This software is released for unlimited distribution under the - terms of the MIT license. See the LICENSE file. -Version: $Revision: 1.12 $ -Start Date: 2001/09/26 -Last Revision Date: $Date: 2005/11/02 22:26:07 $ -""" -__author__ = "Tavis Rudd <tavis@damnsimple.com>" -__revision__ = "$Revision: 1.12 $"[11:-2] - from glob import glob import os from os import listdir import os.path import re -from types import StringType from tempfile import mktemp def _escapeRegexChars(txt, escapeRE=re.compile(r'([\$\^\*\+\.\?\{\}\[\]\(\)\|\\])')): - return escapeRE.sub(r'\\\1' , txt) + return escapeRE.sub(r'\\\1', txt) def findFiles(*args, **kw): """Recursively find all the files matching a glob pattern. @@ -70,7 +54,7 @@ class FileFinder: def __init__(self, rootPath, globPatterns=('*',), - ignoreBasenames=('CVS','.svn'), + ignoreBasenames=('CVS', '.svn'), ignoreDirs=(), ): @@ -220,7 +204,7 @@ class _GenSubberFunc: return "def subber(m):\n\treturn ''.join([%s])\n" % (self.codeBody()) def subberFunc(self): - exec self.code() + exec(self.code()) return subber @@ -238,11 +222,11 @@ class FindAndReplace: recordResults=True): - if type(patternOrRE) == StringType: + if isinstance(patternOrRE, basestring): self._regex = re.compile(patternOrRE) else: self._regex = patternOrRE - if type(replacement) == StringType: + if isinstance(replacement, basestring): self._subber = _GenSubberFunc(replacement).subberFunc() else: self._subber = replacement @@ -279,7 +263,7 @@ class FindAndReplace: self._currFile = file found = False - if locals().has_key('orig'): + if 'orig' in locals(): del orig if self._usePgrep: if os.popen('pgrep "' + pattern + '" ' + file ).read(): @@ -289,23 +273,23 @@ class FindAndReplace: if regex.search(orig): found = True if found: - if not locals().has_key('orig'): + 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 not self._results.has_key(self._currFile): + 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(), + res['matches'].append({'contents': match.group(), + 'start': match.start(), + 'end': match.end(), } ) return self._subber(match) @@ -337,10 +321,10 @@ class SourceFileStats: commentLines += fileStats['commentLines'] totalLines += fileStats['totalLines'] - stats = {'codeLines':codeLines, - 'blankLines':blankLines, - 'commentLines':commentLines, - 'totalLines':totalLines, + stats = {'codeLines': codeLines, + 'blankLines': blankLines, + 'commentLines': commentLines, + 'totalLines': totalLines, } return stats @@ -364,10 +348,10 @@ class SourceFileStats: else: codeLines += 1 - stats = {'codeLines':codeLines, - 'blankLines':blankLines, - 'commentLines':commentLines, - 'totalLines':totalLines, + stats = {'codeLines': codeLines, + 'blankLines': blankLines, + 'commentLines': commentLines, + 'totalLines': totalLines, } return stats diff --git a/cheetah/Filters.py b/cheetah/Filters.py index d452439..47858b1 100644 --- a/cheetah/Filters.py +++ b/cheetah/Filters.py @@ -65,9 +65,9 @@ class Markdown(EncodeUnicode): 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' + 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) @@ -97,8 +97,8 @@ class CodeHighlighter(EncodeUnicode): 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' + 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 @@ -121,7 +121,7 @@ class MaxLen(Filter): """Replace None with '' and cut off at maxlen.""" output = super(MaxLen, self).filter(val, **kw) - if kw.has_key('maxlen') and len(output) > kw['maxlen']: + if 'maxlen' in kw and len(output) > kw['maxlen']: return output[:kw['maxlen']] return output @@ -135,7 +135,7 @@ class WebSafe(Filter): s = s.replace("<", "<") s = s.replace(">", ">") # Process the additional transformations if any. - if kw.has_key('also'): + if 'also' in kw: also = kw['also'] entities = webSafeEntities # Global variable. for k in also: @@ -166,7 +166,7 @@ class Strip(Filter): s = super(Strip, self).filter(val, **kw) result = [] start = 0 # The current line will be s[start:end]. - while 1: # Loop through each line. + while True: # Loop through each line. end = s.find('\n', start) # Find next newline. if end == -1: # If no more newlines. break @@ -196,15 +196,15 @@ class StripSqueeze(Filter): def test(): s1 = "abc <=> &" s2 = " asdf \n\t 1 2 3\n" - print "WebSafe INPUT:", `s1` - print " WebSafe:", `WebSafe().filter(s1)` + print("WebSafe INPUT:", repr(s1)) + print(" WebSafe:", repr(WebSafe().filter(s1))) - print - print " Strip INPUT:", `s2` - print " Strip:", `Strip().filter(s2)` - print "StripSqueeze:", `StripSqueeze().filter(s2)` + print() + print(" Strip INPUT:", repr(s2)) + print(" Strip:", repr(Strip().filter(s2))) + print("StripSqueeze:", repr(StripSqueeze().filter(s2))) - print "Unicode:", `EncodeUnicode().filter(u'aoeu12345\u1234')` + print("Unicode:", repr(EncodeUnicode().filter(u'aoeu12345\u1234'))) if __name__ == "__main__": test() diff --git a/cheetah/ImportHooks.py b/cheetah/ImportHooks.py index 261fb01..a29dfed 100755 --- a/cheetah/ImportHooks.py +++ b/cheetah/ImportHooks.py @@ -114,7 +114,7 @@ def install(templateFileExtensions=('.tmpl',)): if not _installed: CheetahDirOwner.templateFileExtensions = templateFileExtensions import __builtin__ - if type(__builtin__.__import__) == types.BuiltinFunctionType: + if isinstance(__builtin__.__import__, types.BuiltinFunctionType): global __oldimport__ __oldimport__ = __builtin__.__import__ ImportManager._globalOwnerTypes.insert(0, CheetahDirOwner) @@ -129,7 +129,7 @@ def uninstall(): global _installed if not _installed: import __builtin__ - if type(__builtin__.__import__) == types.MethodType: + if isinstance(__builtin__.__import__, types.MethodType): __builtin__.__import__ = __oldimport__ global _manager del _manager diff --git a/cheetah/ImportManager.py b/cheetah/ImportManager.py index 743360e..a043cce 100755 --- a/cheetah/ImportManager.py +++ b/cheetah/ImportManager.py @@ -1,6 +1,5 @@ -# $Id: ImportManager.py,v 1.6 2007/04/03 01:56:24 tavis_rudd Exp $ - -"""Provides an emulator/replacement for Python's standard import system. +""" +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 @@ -18,37 +17,14 @@ This is a hacked/documented version of Gordon McMillan's iu.py. I have: - reorganized the code layout to enhance readability -Meta-Data -================================================================================ -Author: Tavis Rudd <tavis@damnsimple.com> based on Gordon McMillan's iu.py -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/03/30 -Last Revision Date: $Date: 2007/04/03 01:56:24 $ """ -__author__ = "Tavis Rudd <tavis@damnsimple.com>" -__revision__ = "$Revision: 1.6 $"[11:-2] - -################################################## -## DEPENDENCIES import sys import imp import marshal -################################################## -## CONSTANTS & GLOBALS - -try: - True,False -except NameError: - True, False = (1==1),(1==0) - _installed = False -STRINGTYPE = type('') - # _globalOwnerTypes is defined at the bottom of this file _os_stat = _os_path_join = _os_getcwd = _os_path_dirname = None @@ -85,7 +61,7 @@ def _os_bootstrap(): a = a + ':' return a + b else: - raise ImportError, 'no os specific module found' + raise ImportError('no os specific module found') if join is None: def join(a, b, sep=sep): @@ -184,7 +160,7 @@ class DirOwner(Owner): if path == '': path = _os_getcwd() if not pathIsDir(path): - raise ValueError, "%s is not a directory" % path + raise ValueError("%s is not a directory" % path) Owner.__init__(self, path) def getmod(self, nm, @@ -217,14 +193,14 @@ class DirOwner(Owner): break if py is None and pyc is None: return None - while 1: + 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 + print("Invalid syntax in %s" % py[0]) + print(e.args) raise elif pyc: stuff = open(pyc[0], 'rb').read() @@ -260,7 +236,7 @@ class BuiltinImportDirector(ImportDirector): def getmod(self, nm, isbuiltin=imp.is_builtin): if isbuiltin(nm): - mod = imp.load_module(nm, None, nm, ('','',imp.C_BUILTIN)) + mod = imp.load_module(nm, None, nm, ('', '', imp.C_BUILTIN)) return mod return None @@ -273,7 +249,7 @@ class FrozenImportDirector(ImportDirector): def getmod(self, nm, isFrozen=imp.is_frozen, loadMod=imp.load_module): if isFrozen(nm): - mod = loadMod(nm, None, nm, ('','',imp.PY_FROZEN)) + 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 @@ -345,7 +321,7 @@ class PathImportDirector(ImportDirector): def getmod(self, nm): mod = None for thing in self.path: - if type(thing) is STRINGTYPE: + if isinstance(thing, basestring): owner = self._shadowPath.get(thing, -1) if owner == -1: owner = self._shadowPath[thing] = self._makeOwner(thing) @@ -420,11 +396,11 @@ class ImportManager: importernm = globals.get('__name__', '') if importernm: if hasattr(_sys_modules_get(importernm), '__path__'): - contexts.insert(0,importernm) + contexts.insert(0, importernm) else: pkgnm = packageName(importernm) if pkgnm: - contexts.insert(0,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] @@ -462,7 +438,7 @@ class ImportManager: #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 + 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: @@ -487,7 +463,7 @@ class ImportManager: if self.threaded: self._release() if not mod: - raise ImportError, "%s not found in %s" % (nm, ctx) + raise ImportError("%s not found in %s" % (nm, ctx)) #print "importHook done with %s %s %s (case 3)" % (name, globals['__name__'], fromlist) return bottommod @@ -519,7 +495,7 @@ class ImportManager: if hasattr(mod, '__co__'): co = mod.__co__ del mod.__co__ - exec co in mod.__dict__ + exec(co, mod.__dict__) if fqname == 'thread' and not self.threaded: ## print "thread detected!" self.setThreaded() diff --git a/cheetah/NameMapper.py b/cheetah/NameMapper.py index 3a6322e..12257db 100644 --- a/cheetah/NameMapper.py +++ b/cheetah/NameMapper.py @@ -138,7 +138,7 @@ Version: $Revision: 1.32 $ Start Date: 2001/04/03 Last Revision Date: $Date: 2007/12/10 19:20:09 $ """ -from __future__ import generators + __author__ = "Tavis Rudd <tavis@damnsimple.com>," +\ "\nChuck Esterbrook <echuck@mindspring.com>" __revision__ = "$Revision: 1.32 $"[11:-2] @@ -211,7 +211,7 @@ def _isInstanceOrClass(obj): def hasKey(obj, key): """Determine if 'obj' has 'key' """ - if hasattr(obj,'has_key') and obj.has_key(key): + if hasattr(obj, 'has_key') and key in obj: return True elif hasattr(obj, key): return True @@ -219,7 +219,7 @@ def hasKey(obj, key): return False def valueForKey(obj, key): - if hasattr(obj, 'has_key') and obj.has_key(key): + if hasattr(obj, 'has_key') and key in obj: return obj[key] elif hasattr(obj, key): return getattr(obj, key) @@ -230,7 +230,7 @@ def _valueForName(obj, name, executeCallables=False): nameChunks=name.split('.') for i in range(len(nameChunks)): key = nameChunks[i] - if hasattr(obj, 'has_key') and obj.has_key(key): + if hasattr(obj, 'has_key') and key in obj: nextObj = obj[key] else: try: @@ -238,7 +238,7 @@ def _valueForName(obj, name, executeCallables=False): except AttributeError: _raiseNotFoundException(key, obj) - if executeCallables and callable(nextObj) and not _isInstanceOrClass(nextObj): + if executeCallables and hasattr(nextObj, '__call__') and not _isInstanceOrClass(nextObj): obj = nextObj() else: obj = nextObj @@ -364,13 +364,13 @@ def example(): } 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) + 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() diff --git a/cheetah/Parser.py b/cheetah/Parser.py index 7436e9c..8810ac8 100644 --- a/cheetah/Parser.py +++ b/cheetah/Parser.py @@ -1,21 +1,12 @@ -# $Id: Parser.py,v 1.137 2008/03/10 05:25:13 tavis_rudd Exp $ -"""Parser classes for Cheetah's Compiler +""" +Parser classes for Cheetah's Compiler Classes: ParseError( Exception ) _LowLevelParser( Cheetah.SourceReader.SourceReader ), basically a lexer _HighLevelParser( _LowLevelParser ) Parser === _HighLevelParser (an alias) - -Meta-Data -================================================================================ -Author: Tavis Rudd <tavis@damnsimple.com> -Version: $Revision: 1.137 $ -Start Date: 2001/08/01 -Last Revision Date: $Date: 2008/03/10 05:25:13 $ """ -__author__ = "Tavis Rudd <tavis@damnsimple.com>" -__revision__ = "$Revision: 1.137 $"[11:-2] import os import sys @@ -46,13 +37,13 @@ def escapeRegexChars(txt, """Return a txt with all special regular expressions chars escaped.""" - return escapeRE.sub(r'\\\1' , txt) + return escapeRE.sub(r'\\\1', txt) def group(*choices): return '(' + '|'.join(choices) + ')' def nongroup(*choices): return '(?:' + '|'.join(choices) + ')' def namedGroup(name, *choices): return '(P:<' + name +'>' + '|'.join(choices) + ')' -def any(*choices): return apply(group, choices) + '*' -def maybe(*choices): return apply(group, choices) + '?' +def any(*choices): return group(*choices) + '*' +def maybe(*choices): return group(*choices) + '?' ################################################## ## CONSTANTS & GLOBALS ## @@ -76,22 +67,22 @@ namechars = identchars + "0123456789" #operators powerOp = '**' unaryArithOps = ('+', '-', '~') -binaryArithOps = ('+', '-', '/', '//','%') -shiftOps = ('>>','<<') -bitwiseOps = ('&','|','^') +binaryArithOps = ('+', '-', '/', '//', '%') +shiftOps = ('>>', '<<') +bitwiseOps = ('&', '|', '^') assignOp = '=' -augAssignOps = ('+=','-=','/=','*=', '**=','^=','%=', - '>>=','<<=','&=','|=', ) +augAssignOps = ('+=', '-=', '/=', '*=', '**=', '^=', '%=', + '>>=', '<<=', '&=', '|=', ) assignmentOps = (assignOp,) + augAssignOps -compOps = ('<','>','==','!=','<=','>=', '<>', 'is', 'in',) -booleanOps = ('and','or','not') +compOps = ('<', '>', '==', '!=', '<=', '>=', '<>', 'is', 'in',) +booleanOps = ('and', 'or', 'not') operators = (powerOp,) + unaryArithOps + binaryArithOps \ + shiftOps + bitwiseOps + assignmentOps \ + compOps + booleanOps -delimeters = ('(',')','{','}','[',']', - ',','.',':',';','=','`') + augAssignOps +delimeters = ('(', ')', '{', '}', '[', ']', + ',', '.', ':', ';', '=', '`') + augAssignOps keywords = ('and', 'del', 'for', 'is', 'raise', @@ -140,7 +131,7 @@ for start, end in tripleQuotedStringPairs.items(): WS = r'[ \f\t]*' EOL = r'\r\n|\n|\r' EOLZ = EOL + r'|\Z' -escCharLookBehind = nongroup(r'(?<=\A)',r'(?<!\\)') +escCharLookBehind = nongroup(r'(?<=\A)', r'(?<!\\)') nameCharLookAhead = r'(?=[A-Za-z_])' identRE=re.compile(r'[a-zA-Z_][a-zA-Z_0-9]*') EOLre=re.compile(r'(?:\r\n|\r|\n)') @@ -153,12 +144,12 @@ unicodeDirectiveRE = re.compile( encodingDirectiveRE = re.compile( r'(?:^|\r\n|\r|\n)\s*#\s{0,5}encoding[:\s]*([-\w.]*)\s*(?:\r\n|\r|\n)', re.MULTILINE) -escapedNewlineRE = re.compile(r'(?<!\\)\\n') +escapedNewlineRE = re.compile(r'(?<!\\)((\\\\)*)\\(n|012)') directiveNamesAndParsers = { # importing and inheritance - 'import':None, - 'from':None, + 'import': None, + 'from': None, 'extends': 'eatExtends', 'implements': 'eatImplements', 'super': 'eatSuper', @@ -171,7 +162,7 @@ directiveNamesAndParsers = { 'filter': 'eatFilter', 'echo': None, 'silent': None, - 'transform' : 'eatTransform', + 'transform': 'eatTransform', 'call': 'eatCall', 'arg': 'eatCallArg', @@ -235,7 +226,7 @@ endDirectiveNamesAndHandlers = { 'call': None, # has short-form 'capture': None, # has short-form 'filter': None, - 'errorCatcher':None, + 'errorCatcher': None, 'while': None, # has short-form 'for': None, # has short-form 'if': None, # has short-form @@ -279,16 +270,16 @@ class ParseError(ValueError): ## get the surrounding lines lines = stream.splitlines() prevLines = [] # (rowNum, content) - for i in range(1,4): + for i in range(1, 4): if row-1-i <=0: break - prevLines.append( (row-i,lines[row-1-i]) ) + prevLines.append( (row-i, lines[row-1-i]) ) nextLines = [] # (rowNum, content) - for i in range(1,4): + for i in range(1, 4): if not row-1+i < len(lines): break - nextLines.append( (row+i,lines[row-1+i]) ) + nextLines.append( (row+i, lines[row-1+i]) ) nextLines.reverse() ## print the main message @@ -311,11 +302,14 @@ class ParseError(ValueError): return report -class ForbiddenSyntax(ParseError): pass -class ForbiddenExpression(ForbiddenSyntax): pass -class ForbiddenDirective(ForbiddenSyntax): pass +class ForbiddenSyntax(ParseError): + pass +class ForbiddenExpression(ForbiddenSyntax): + pass +class ForbiddenDirective(ForbiddenSyntax): + pass -class CheetahVariable: +class CheetahVariable(object): def __init__(self, nameChunks, useNameMapper=True, cacheToken=None, rawSource=None): self.nameChunks = nameChunks @@ -323,36 +317,33 @@ class CheetahVariable: self.cacheToken = cacheToken self.rawSource = rawSource -class Placeholder(CheetahVariable): pass +class Placeholder(CheetahVariable): + pass -class ArgList: +class ArgList(object): """Used by _LowLevelParser.getArgList()""" def __init__(self): - self.argNames = [] - self.defVals = [] - self.i = 0 + self.arguments = [] + self.defaults = [] + self.count = 0 - def addArgName(self, name): - self.argNames.append( name ) - self.defVals.append( None ) + def add_argument(self, name): + self.arguments.append(name) + self.defaults.append(None) def next(self): - self.i += 1 + self.count += 1 - def addToDefVal(self, token): - i = self.i - if self.defVals[i] == None: - self.defVals[i] = '' - self.defVals[i] += token + def add_default(self, token): + count = self.count + if self.defaults[count] is None: + self.defaults[count] = '' + self.defaults[count] += token def merge(self): - defVals = self.defVals - for i in range(len(defVals)): - if type(defVals[i]) == StringType: - defVals[i] = defVals[i].strip() - - return map(None, [i.strip() for i in self.argNames], defVals) + defaults = (isinstance(d, basestring) and d.strip() or None for d in self.defaults) + return list(map(None, (a.strip() for a in self.arguments), defaults)) def __str__(self): return str(self.merge()) @@ -519,6 +510,19 @@ class _LowLevelParser(SourceReader): endTokenEsc = escapeRegexChars(endToken) self.PSPEndTokenRE = cachedRegex(escCharLookBehind + endTokenEsc) + def _unescapeCheetahVars(self, theString): + """Unescape any escaped Cheetah \$vars in the string. + """ + + token = self.setting('cheetahVarStartToken') + return theString.replace('\\' + token, token) + + def _unescapeDirectives(self, theString): + """Unescape any escaped Cheetah directives in the string. + """ + + token = self.setting('directiveStartToken') + return theString.replace('\\' + token, token) def isLineClearToStartToken(self, pos=None): return self.isLineClearToPos(pos) @@ -942,7 +946,7 @@ class _LowLevelParser(SourceReader): argStringBits = ['('] addBit = argStringBits.append - while 1: + while True: if self.atEnd(): open = enclosures[-1][0] close = closurePairsRev[open] @@ -987,7 +991,7 @@ class _LowLevelParser(SourceReader): else: beforeTokenPos = self.pos() token = self.getPyToken() - if token in ('{','(','['): + if token in ('{', '(', '['): self.rev() token = self.getExpression(enclosed=True) token = self.transformToken(token, beforeTokenPos) @@ -1027,7 +1031,7 @@ class _LowLevelParser(SourceReader): useNameMapper_orig = self.setting('useNameMapper') self.setSetting('useNameMapper', useNameMapper) - while 1: + while True: if self.atEnd(): raise ParseError( self, msg="EOF was reached before a matching ')'"+ @@ -1043,7 +1047,7 @@ class _LowLevelParser(SourceReader): break elif c in " \t\f\r\n": if onDefVal: - argList.addToDefVal(c) + argList.add_default(c) self.advance() elif c == '=': onDefVal = True @@ -1055,7 +1059,7 @@ class _LowLevelParser(SourceReader): elif self.startswith(self.cheetahVarStartToken) and not onDefVal: self.advance(len(self.cheetahVarStartToken)) elif self.matchIdentifier() and not onDefVal: - argList.addArgName( self.getIdentifier() ) + argList.add_argument( self.getIdentifier() ) elif onDefVal: if self.matchCheetahVarInExpressionStartToken(): token = self.getCheetahVar() @@ -1065,11 +1069,11 @@ class _LowLevelParser(SourceReader): else: beforeTokenPos = self.pos() token = self.getPyToken() - if token in ('{','(','['): + if token in ('{', '(', '['): self.rev() token = self.getExpression(enclosed=True) token = self.transformToken(token, beforeTokenPos) - argList.addToDefVal(token) + argList.add_default(token) elif c == '*' and not onDefVal: varName = self.getc() if self.peek() == '*': @@ -1077,7 +1081,7 @@ class _LowLevelParser(SourceReader): if not self.matchIdentifier(): raise ParseError(self) varName += self.getIdentifier() - argList.addArgName(varName) + argList.add_argument(varName) else: raise ParseError(self) @@ -1106,7 +1110,7 @@ class _LowLevelParser(SourceReader): srcLen = len(self) exprBits = [] - while 1: + while True: if self.atEnd(): if enclosures: open = enclosures[-1][0] @@ -1224,7 +1228,6 @@ class _LowLevelParser(SourceReader): startPosIdx = 3 else: startPosIdx = 1 - #print 'CHEETAH STRING', nextToken, theStr, startPosIdx self.setPos(beforeTokenPos+startPosIdx+1) outputExprs = [] strConst = '' @@ -1240,8 +1243,6 @@ class _LowLevelParser(SourceReader): self.setPos(endPos) if strConst: outputExprs.append(repr(strConst)) - #if not self.atEnd() and self.matches('.join('): - # print 'DEBUG***' token = "''.join(["+','.join(outputExprs)+"])" return token @@ -1318,8 +1319,8 @@ class _LowLevelParser(SourceReader): expr = expr[:-1] rawPlaceholder=self[startPos: self.pos()] - expr = self._applyExpressionFilters(expr,'placeholder', - rawExpr=rawPlaceholder,startPos=startPos) + expr = self._applyExpressionFilters(expr, 'placeholder', + rawExpr=rawPlaceholder, startPos=startPos) for callback in self.setting('postparsePlaceholderHooks'): callback(parser=self) @@ -1336,7 +1337,7 @@ class _HighLevelParser(_LowLevelParser): Cheetah.Compiler.Compiler. """ def __init__(self, src, filename=None, breakPoint=None, compiler=None): - _LowLevelParser.__init__(self, src, filename=filename, breakPoint=breakPoint) + super(_HighLevelParser, self).__init__(src, filename=filename, breakPoint=breakPoint) self.setSettingsManager(compiler) self._compiler = compiler self.setupState() @@ -1357,16 +1358,16 @@ class _HighLevelParser(_LowLevelParser): self._macroDetails.clear() def configureParser(self): - _LowLevelParser.configureParser(self) + super(_HighLevelParser, self).configureParser() self._initDirectives() def _initDirectives(self): def normalizeParserVal(val): - if isinstance(val, (str,unicode)): + if isinstance(val, (str, unicode)): handler = getattr(self, val) elif type(val) in (ClassType, TypeType): handler = val(self) - elif callable(val): + elif hasattr(val, '__call__'): handler = val elif val is None: handler = val @@ -1377,11 +1378,11 @@ class _HighLevelParser(_LowLevelParser): normalizeHandlerVal = normalizeParserVal _directiveNamesAndParsers = directiveNamesAndParsers.copy() - customNamesAndParsers = self.setting('directiveNamesAndParsers',{}) + customNamesAndParsers = self.setting('directiveNamesAndParsers', {}) _directiveNamesAndParsers.update(customNamesAndParsers) _endDirectiveNamesAndHandlers = endDirectiveNamesAndHandlers.copy() - customNamesAndHandlers = self.setting('endDirectiveNamesAndHandlers',{}) + customNamesAndHandlers = self.setting('endDirectiveNamesAndHandlers', {}) _endDirectiveNamesAndHandlers.update(customNamesAndHandlers) self._directiveNamesAndParsers = {} @@ -1396,21 +1397,21 @@ class _HighLevelParser(_LowLevelParser): continue self._endDirectiveNamesAndHandlers[name] = normalizeHandlerVal(val) - self._closeableDirectives = ['def','block','closure','defmacro', + self._closeableDirectives = ['def', 'block', 'closure', 'defmacro', 'call', 'capture', 'cache', 'filter', - 'if','unless', - 'for','while','repeat', + 'if', 'unless', + 'for', 'while', 'repeat', 'try', ] - for directiveName in self.setting('closeableDirectives',[]): + for directiveName in self.setting('closeableDirectives', []): self._closeableDirectives.append(directiveName) - macroDirectives = self.setting('macroDirectives',{}) + macroDirectives = self.setting('macroDirectives', {}) macroDirectives['i18n'] = I18n @@ -1509,6 +1510,8 @@ class _HighLevelParser(_LowLevelParser): else: self.advance() strConst = self.readTo(self.pos(), start=startPos) + strConst = self._unescapeCheetahVars(strConst) + strConst = self._unescapeDirectives(strConst) self._compiler.addStrConst(strConst) return match @@ -1527,7 +1530,7 @@ class _HighLevelParser(_LowLevelParser): self.getMultiLineCommentStartToken() endPos = startPos = self.pos() level = 1 - while 1: + while True: endPos = self.pos() if self.atEnd(): break @@ -1594,8 +1597,8 @@ class _HighLevelParser(_LowLevelParser): del assert raise silent echo import from'''.split() - _directiveHandlerNames = {'import':'addImportStatement', - 'from':'addImportStatement', } + _directiveHandlerNames = {'import': 'addImportStatement', + 'from': 'addImportStatement', } def eatDirective(self): directiveName = self.matchDirective() self._filterDisabledDirectives(directiveName) @@ -1854,13 +1857,11 @@ class _HighLevelParser(_LowLevelParser): try: self._compiler.setCompilerSetting(settingName, valueExpr) except: - out = sys.stderr - print >> out, 'An error occurred while processing the following #compiler directive.' - print >> out, '-'*80 - print >> out, self[startPos:endPos] - print >> out, '-'*80 - print >> out, 'Please check the syntax of these settings.' - print >> out, 'A full Python exception traceback follows.' + sys.stderr.write('An error occurred while processing the following #compiler directive.\n') + sys.stderr.write('----------------------------------------------------------------------\n') + sys.stderr.write('%s\n' % self[startPos:endPos]) + sys.stderr.write('----------------------------------------------------------------------\n') + sys.stderr.write('Please check the syntax of these settings.\n\n') raise @@ -1890,13 +1891,11 @@ class _HighLevelParser(_LowLevelParser): try: self._compiler.setCompilerSettings(keywords=keywords, settingsStr=settingsStr) except: - out = sys.stderr - print >> out, 'An error occurred while processing the following compiler settings.' - print >> out, '-'*80 - print >> out, settingsStr.strip() - print >> out, '-'*80 - print >> out, 'Please check the syntax of these settings.' - print >> out, 'A full Python exception traceback follows.' + sys.stderr.write('An error occurred while processing the following compiler settings.\n') + sys.stderr.write('----------------------------------------------------------------------\n') + sys.stderr.write('%s\n' % settingsStr.strip()) + sys.stderr.write('----------------------------------------------------------------------\n') + sys.stderr.write('Please check the syntax of these settings.\n\n') raise def eatAttr(self): @@ -1946,8 +1945,8 @@ class _HighLevelParser(_LowLevelParser): startPos = self.pos() methodName, rawSignature = self._eatDefOrBlock('block') self._compiler._blockMetaData[methodName] = { - 'raw':rawSignature, - 'lineCol':self.getRowCol(startPos), + 'raw': rawSignature, + 'lineCol': self.getRowCol(startPos), } def eatClosure(self): @@ -1956,7 +1955,7 @@ class _HighLevelParser(_LowLevelParser): def _eatDefOrBlock(self, directiveName): # filtered - assert directiveName in ('def','block','closure') + assert directiveName in ('def', 'block', 'closure') isLineClearToStartToken = self.isLineClearToStartToken() endOfFirstLinePos = self.findEOL() startPos = self.pos() @@ -2249,15 +2248,15 @@ class _HighLevelParser(_LowLevelParser): else: argsList=[] - assert not self._directiveNamesAndParsers.has_key(macroName) - argsList.insert(0, ('src',None)) - argsList.append(('parser','None')) - argsList.append(('macros','None')) - argsList.append(('compilerSettings','None')) - argsList.append(('isShortForm','None')) - argsList.append(('EOLCharsInShortForm','None')) - argsList.append(('startPos','None')) - argsList.append(('endPos','None')) + assert macroName not in self._directiveNamesAndParsers + argsList.insert(0, ('src', None)) + argsList.append(('parser', 'None')) + argsList.append(('macros', 'None')) + argsList.append(('compilerSettings', 'None')) + argsList.append(('isShortForm', 'None')) + argsList.append(('EOLCharsInShortForm', 'None')) + argsList.append(('startPos', 'None')) + argsList.append(('endPos', 'None')) if self.matchColonForSingleLineShortFormDirective(): self.advance() # skip over : @@ -2273,8 +2272,8 @@ class _HighLevelParser(_LowLevelParser): #print argsList normalizedMacroSrc = ''.join( - ['%def callMacro('+','.join([defv and '%s=%s'%(n,defv) or n - for n,defv in argsList]) + ['%def callMacro('+','.join([defv and '%s=%s'%(n, defv) or n + for n, defv in argsList]) +')\n', macroSrc, '%end def']) @@ -2285,9 +2284,9 @@ class _HighLevelParser(_LowLevelParser): compilerSettings = self.setting('compilerSettingsForDefMacro', default={}) searchListForMacros = self.setting('searchListForDefMacro', default=[]) searchListForMacros = list(searchListForMacros) # copy to avoid mutation bugs - searchListForMacros.append({'macros':self._macros, - 'parser':self, - 'compilerSettings':self.settings(), + searchListForMacros.append({'macros': self._macros, + 'parser': self, + 'compilerSettings': self.settings(), }) templateAPIClass._updateSettingsWithPreprocessTokens( @@ -2347,12 +2346,12 @@ class _HighLevelParser(_LowLevelParser): else: def getArgs(*pargs, **kws): return pargs, kws - exec 'positionalArgs, kwArgs = getArgs(%(args)s)'%locals() + exec('positionalArgs, kwArgs = getArgs(%(args)s)'%locals()) - assert not kwArgs.has_key('src') + assert 'src' not in kwArgs kwArgs['src'] = srcBlock - if type(macro)==new.instancemethod: + if isinstance(macro, new.instancemethod): co = macro.im_func.func_code elif (hasattr(macro, '__call__') and hasattr(macro.__call__, 'im_func')): diff --git a/cheetah/Servlet.py b/cheetah/Servlet.py index f19e508..e122aca 100644 --- a/cheetah/Servlet.py +++ b/cheetah/Servlet.py @@ -103,7 +103,7 @@ definition.""") if self._CHEETAH__isControlledByWebKit: return super(Servlet, self).serverSidePath(path) elif path: - return normpath(abspath(path.replace("\\",'/'))) + return normpath(abspath(path.replace("\\", '/'))) elif hasattr(self, '_filePath') and self._filePath: return normpath(abspath(self._filePath)) else: diff --git a/cheetah/SettingsManager.py b/cheetah/SettingsManager.py index dfb396b..94f7723 100644 --- a/cheetah/SettingsManager.py +++ b/cheetah/SettingsManager.py @@ -34,10 +34,8 @@ def mergeNestedDictionaries(dict1, dict2, copy=False, deepcopy=False): elif deepcopy: dict1 = copyModule.deepcopy(dict1) - for key,val in dict2.items(): - if dict1.has_key(key) and type(val) == types.DictType and \ - type(dict1[key]) == types.DictType: - + 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 @@ -96,7 +94,7 @@ class _SettingsCollector(object): """ S = {} attrs = vars(mod) - for k, v in attrs.items(): + for k, v in attrs.iteritems(): if (ignoreUnderscored and k.startswith('_')): continue else: @@ -106,11 +104,11 @@ class _SettingsCollector(object): def readSettingsFromPySrcStr(self, theString): """Return a dictionary of the settings in a Python src string.""" - globalsDict = {'True':(1==1), - 'False':(0==1), + globalsDict = {'True': (1==1), + 'False': (0==1), } newSettings = {'self':self} - exec (theString+os.linesep) in globalsDict, newSettings + exec((theString+os.linesep), globalsDict, newSettings) del newSettings['self'] module = new.module('temp_settings_module') module.__dict__.update(newSettings) @@ -156,7 +154,7 @@ class _SettingsCollector(object): newSettings[s] = {} for o in p.options(s): if o != '__name__': - newSettings[s][o] = p.get(s,o) + newSettings[s][o] = p.get(s, o) ## loop through new settings -> deal with global settings, numbers, ## booleans and None ++ also deal with 'importSettings' commands @@ -165,7 +163,7 @@ class _SettingsCollector(object): for key, val in subDict.items(): if convert: if val.lower().startswith('python:'): - subDict[key] = eval(val[7:],{},{}) + subDict[key] = eval(val[7:], {}, {}) if val.lower() == 'none': subDict[key] = None if val.lower() == 'true': @@ -267,7 +265,7 @@ class SettingsManager(_SettingsCollector): newSettings = self.readSettingsFromPySrcStr(theString) self.updateSettings(newSettings, - merge=newSettings.get('mergeSettings',merge) ) + merge=newSettings.get('mergeSettings', merge) ) def updateSettingsFromConfigFileObj(self, inFile, convert=True, merge=True): @@ -278,7 +276,7 @@ class SettingsManager(_SettingsCollector): newSettings = self.readSettingsFromConfigFileObj(inFile, convert=convert) self.updateSettings(newSettings, - merge=newSettings.get('mergeSettings',merge)) + merge=newSettings.get('mergeSettings', merge)) def updateSettingsFromConfigStr(self, configStr, convert=True, merge=True): """See the docstring for .updateSettingsFromConfigFile() @@ -288,5 +286,5 @@ class SettingsManager(_SettingsCollector): inFile = StringIO(configStr) newSettings = self.readSettingsFromConfigFileObj(inFile, convert=convert) self.updateSettings(newSettings, - merge=newSettings.get('mergeSettings',merge)) + merge=newSettings.get('mergeSettings', merge)) diff --git a/cheetah/SourceReader.py b/cheetah/SourceReader.py index 0dc0e60..7a08837 100644 --- a/cheetah/SourceReader.py +++ b/cheetah/SourceReader.py @@ -1,18 +1,5 @@ -# $Id: SourceReader.py,v 1.15 2007/04/03 01:57:42 tavis_rudd Exp $ """SourceReader class for Cheetah's Parser and CodeGenerator - -Meta-Data -================================================================================ -Author: Tavis Rudd <tavis@damnsimple.com> -License: This software is released for unlimited distribution under the - terms of the MIT license. See the LICENSE file. -Version: $Revision: 1.15 $ -Start Date: 2001/09/19 -Last Revision Date: $Date: 2007/04/03 01:57:42 $ """ -__author__ = "Tavis Rudd <tavis@damnsimple.com>" -__revision__ = "$Revision: 1.15 $"[11:-2] - import re import sys @@ -23,7 +10,7 @@ ENCODINGsearch = re.compile("coding[=:]\s*([-\w.]+)").search class Error(Exception): pass -class SourceReader: +class SourceReader(object): def __init__(self, src, filename=None, breakPoint=None, encoding=None): ## @@TR 2005-01-17: the following comes from a patch Terrel Shumway @@ -160,7 +147,7 @@ class SourceReader: self._posTobookmarkMap[self._pos] = name def hasBookmark(self, name): - return self._bookmarks.has_key(name) + return name in self._bookmarks def gotoBookmark(self, name): if not self.hasBookmark(name): @@ -247,7 +234,7 @@ class SourceReader: if pos == None: pos = self._pos src = self.src() - return max(src.rfind('\n',0,pos)+1, src.rfind('\r',0,pos)+1, 0) + 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: diff --git a/cheetah/Template.py b/cheetah/Template.py index ec92208..0eba725 100644 --- a/cheetah/Template.py +++ b/cheetah/Template.py @@ -25,7 +25,7 @@ from types import StringType, ClassType try: from types import StringTypes except ImportError: - StringTypes = (types.StringType,types.UnicodeType) + StringTypes = (types.StringType, types.UnicodeType) try: from threading import Lock @@ -84,15 +84,14 @@ def hashList(l): return hash(tuple(hashedList)) def hashDict(d): - items = d.items() - items.sort() + items = sorted(d.items()) hashedList = [] for k, v in items: if isinstance(v, dict): v = hashDict(v) elif isinstance(v, list): v = hashList(v) - hashedList.append((k,v)) + hashedList.append((k, v)) return hash(tuple(hashedList)) @@ -106,7 +105,7 @@ def _genUniqueModuleName(baseModuleName): finalName = baseModuleName else: finalName = ('cheetah_%s_%s_%s'%(baseModuleName, - str(time.time()).replace('.','_'), + str(time.time()).replace('.', '_'), str(randrange(10000, 99999)))) return finalName @@ -276,8 +275,8 @@ class Template(Servlet): '_getTemplateAPIClassForIncludeDirectiveCompilation', ) _CHEETAH_requiredCheetahClassMethods = ('subclass',) - _CHEETAH_requiredCheetahClassAttributes = ('cacheRegionClass','cacheStore', - 'cacheStoreIdPrefix','cacheStoreClass') + _CHEETAH_requiredCheetahClassAttributes = ('cacheRegionClass', 'cacheStore', + 'cacheStoreIdPrefix', 'cacheStoreClass') ## the following are used by .compile(). Most are documented in its docstring. _CHEETAH_cacheModuleFilesForTracebacks = False @@ -293,6 +292,7 @@ class Template(Servlet): _CHEETAH_defaultMainMethodName = None _CHEETAH_compilerSettings = None _CHEETAH_compilerClass = Compiler + _CHEETAH_compilerInstance = None _CHEETAH_cacheCompilationResults = True _CHEETAH_useCompilationCache = True _CHEETAH_keepRefToGeneratedCode = True @@ -314,14 +314,15 @@ class Template(Servlet): _CHEETAH_cacheStore = None _CHEETAH_cacheStoreIdPrefix = None + @classmethod def _getCompilerClass(klass, source=None, file=None): return klass._CHEETAH_compilerClass - _getCompilerClass = classmethod(_getCompilerClass) + @classmethod def _getCompilerSettings(klass, source=None, file=None): return klass._CHEETAH_compilerSettings - _getCompilerSettings = classmethod(_getCompilerSettings) + @classmethod def compile(klass, source=None, file=None, returnAClass=True, @@ -640,7 +641,7 @@ class Template(Servlet): if not isinstance(className, (types.NoneType, basestring)): raise TypeError(errmsg % ('className', 'string or None')) - className = className or moduleName + className = re.sub(r'^_+','', className or moduleName) if mainMethodName is Unspecified: mainMethodName = klass._CHEETAH_defaultMainMethodNameForTemplates @@ -723,6 +724,7 @@ class Template(Servlet): #@@TR: should add some logging to this pass outputEncoding = 'ascii' + compiler = None if useCache and cacheHash and cacheHash in klass._CHEETAH_compileCache: cacheItem = klass._CHEETAH_compileCache[cacheHash] generatedModuleCode = cacheItem.code @@ -780,7 +782,7 @@ class Template(Servlet): ## try: co = compile(generatedModuleCode, __file__, 'exec') - exec co in mod.__dict__ + exec(co, mod.__dict__) except SyntaxError, e: try: parseError = genParserErrorFromPythonException( @@ -818,9 +820,13 @@ class Template(Servlet): if keepRefToGeneratedCode or cacheCompilationResults: templateClass._CHEETAH_generatedModuleCode = generatedModuleCode + # If we have a compiler object, let's set it to the compiler class + # to help the directive analyzer code + if compiler: + templateClass._CHEETAH_compilerInstance = compiler return templateClass - compile = classmethod(compile) + @classmethod def subclass(klass, *args, **kws): """Takes the same args as the .compile() classmethod and returns a template that is a subclass of the template this method is called from. @@ -834,8 +840,8 @@ class Template(Servlet): else: templateAPIClass = Template return templateAPIClass.compile(*args, **kws) - subclass = classmethod(subclass) + @classmethod def _preprocessSource(klass, source, file, preprocessors): """Iterates through the .compile() classmethod's preprocessors argument and pipes the source code through each each preprocessor. @@ -849,8 +855,8 @@ class Template(Servlet): preprocessor = klass._normalizePreprocessorArg(preprocessor) source, file = preprocessor.preprocess(source, file) return source, file - _preprocessSource = classmethod(_preprocessSource) + @classmethod def _normalizePreprocessorArg(klass, arg): """Used to convert the items in the .compile() classmethod's preprocessors argument into real source preprocessors. This permits the @@ -859,7 +865,7 @@ class Template(Servlet): if hasattr(arg, 'preprocess'): return arg - elif callable(arg): + elif hasattr(arg, '__call__'): class WrapperPreprocessor: def preprocess(self, source, file): return arg(source, file) @@ -880,8 +886,8 @@ class Template(Servlet): settings = klass._normalizePreprocessorSettings(settings) return klass._CHEETAH_defaultPreprocessorClass(settings) - _normalizePreprocessorArg = classmethod(_normalizePreprocessorArg) + @classmethod def _normalizePreprocessorSettings(klass, settings): settings.keepRefToGeneratedCode = True @@ -902,7 +908,7 @@ class Template(Servlet): (settings.placeholderToken, settings.directiveToken) = normalizeTokens(settings.tokens) - if (not getattr(settings,'compilerSettings', None) + if (not getattr(settings, 'compilerSettings', None) and not getattr(settings, 'placeholderToken', None) ): raise TypeError( @@ -936,8 +942,8 @@ class Template(Servlet): directiveToken=settings.directiveToken ) return settings - _normalizePreprocessorSettings = classmethod(_normalizePreprocessorSettings) + @classmethod def _updateSettingsWithPreprocessTokens( klass, compilerSettings, placeholderToken, directiveToken): @@ -958,8 +964,8 @@ class Template(Servlet): '*'+directiveToken) if 'EOLSlurpToken' not in compilerSettings: compilerSettings['EOLSlurpToken'] = directiveToken - _updateSettingsWithPreprocessTokens = classmethod(_updateSettingsWithPreprocessTokens) + @classmethod def _addCheetahPlumbingCodeToClass(klass, concreteTemplateClass): """If concreteTemplateClass is not a subclass of Cheetah.Template, add the required cheetah methods and attributes to it. @@ -991,7 +997,7 @@ class Template(Servlet): or concreteTemplateClass.__str__ is object.__str__): mainMethNameAttr = '_mainCheetahMethod_for_'+concreteTemplateClass.__name__ - mainMethName = getattr(concreteTemplateClass,mainMethNameAttr, None) + mainMethName = getattr(concreteTemplateClass, mainMethNameAttr, None) if mainMethName: def __str__(self): rc = getattr(self, mainMethName)() @@ -1013,7 +1019,7 @@ class Template(Servlet): def __str__(self): rc = None if hasattr(self, mainMethNameAttr): - rc = getattr(self,mainMethNameAttr)() + rc = getattr(self, mainMethNameAttr)() elif hasattr(self, 'respond'): rc = self.respond() else: @@ -1023,7 +1029,7 @@ class Template(Servlet): return rc def __unicode__(self): if hasattr(self, mainMethNameAttr): - return getattr(self,mainMethNameAttr)() + return getattr(self, mainMethNameAttr)() elif hasattr(self, 'respond'): return self.respond() else: @@ -1033,10 +1039,7 @@ class Template(Servlet): __unicode__ = new.instancemethod(__unicode__, None, concreteTemplateClass) setattr(concreteTemplateClass, '__str__', __str__) setattr(concreteTemplateClass, '__unicode__', __unicode__) - - _addCheetahPlumbingCodeToClass = classmethod(_addCheetahPlumbingCodeToClass) - ## end classmethods ## def __init__(self, source=None, @@ -1358,7 +1361,7 @@ class Template(Servlet): """ try: - return valueFromSearchList(self.searchList(), varName.replace('$',''), autoCall) + return valueFromSearchList(self.searchList(), varName.replace('$', ''), autoCall) except NotFound: if default is not Unspecified: return default @@ -1369,7 +1372,7 @@ class Template(Servlet): """Test if a variable name exists in the searchList. """ try: - valueFromSearchList(self.searchList(), varName.replace('$',''), autoCall) + valueFromSearchList(self.searchList(), varName.replace('$', ''), autoCall) return True except NotFound: return False @@ -1418,7 +1421,7 @@ class Template(Servlet): various protocols, as PHP allows with its 'URL fopen wrapper' """ - fp = open(path,'r') + fp = open(path, 'r') output = fp.read() fp.close() return output @@ -1502,7 +1505,7 @@ class Template(Servlet): if errorCatcher: if isinstance(errorCatcher, basestring): errorCatcherClass = getattr(ErrorCatchers, errorCatcher) - elif type(errorCatcher) == ClassType: + elif isinstance(errorCatcher, ClassType): errorCatcherClass = errorCatcher self._CHEETAH__errorCatcher = ec = errorCatcherClass(self) @@ -1559,7 +1562,7 @@ class Template(Servlet): """Called at runtime to handle #include directives. """ _includeID = srcArg - if not self._CHEETAH__cheetahIncludes.has_key(_includeID): + if _includeID not in self._CHEETAH__cheetahIncludes: if not raw: if includeFrom == 'file': source = None @@ -1577,7 +1580,7 @@ class Template(Servlet): # Template class to be used for compilation so compilerSettings # can be changed. compiler = self._getTemplateAPIClassForIncludeDirectiveCompilation(source, file) - nestedTemplateClass = compiler.compile(source=source,file=file) + nestedTemplateClass = compiler.compile(source=source, file=file) nestedTemplate = nestedTemplateClass(_preBuiltSearchList=self.searchList(), _globalSetVars=self._CHEETAH__globalSetVars) # Set the inner template filters to the initial filter of the @@ -1819,8 +1822,8 @@ class Template(Servlet): raise TypeError("arg 'src' invalid") sources = source + 's' converters = { - '' : _Converter('string', None, default, default ), - 'int' : _Converter('int', int, defaultInt, badInt ), + '': _Converter('string', None, default, default ), + 'int': _Converter('int', int, defaultInt, badInt ), 'float': _Converter('float', float, defaultFloat, badFloat), } #pprint.pprint(locals()); return {} dic = {} # Destination. @@ -1837,7 +1840,7 @@ class Template(Servlet): # 'dic = super(ThisClass, self).webInput(names, namesMulti, ...)' # and then the code below. if debug: - print "<PRE>\n" + pprint.pformat(dic) + "\n</PRE>\n\n" + print("<PRE>\n" + pprint.pformat(dic) + "\n</PRE>\n\n") self.searchList().insert(0, dic) return dic @@ -1862,16 +1865,16 @@ def genParserErrorFromPythonException(source, file, generatedPyCode, exception): lines = generatedPyCode.splitlines() prevLines = [] # (i, content) - for i in range(1,4): + for i in range(1, 4): if pyLineno-i <=0: break - prevLines.append( (pyLineno+1-i,lines[pyLineno-i]) ) + prevLines.append( (pyLineno+1-i, lines[pyLineno-i]) ) nextLines = [] # (i, content) - for i in range(1,4): + for i in range(1, 4): if not pyLineno+i < len(lines): break - nextLines.append( (pyLineno+i,lines[pyLineno+i]) ) + nextLines.append( (pyLineno+i, lines[pyLineno+i]) ) nextLines.reverse() report = 'Line|Python Code\n' report += '----|-------------------------------------------------------------\n' @@ -1918,7 +1921,7 @@ def genParserErrorFromPythonException(source, file, generatedPyCode, exception): message = '\n'.join(message) reader = SourceReader(source, filename=filename) - return ParseError(reader, message, lineno=lineno,col=col) + return ParseError(reader, message, lineno=lineno, col=col) # vim: shiftwidth=4 tabstop=4 expandtab diff --git a/cheetah/TemplateCmdLineIface.py b/cheetah/TemplateCmdLineIface.py index 16a90cf..9787577 100644 --- a/cheetah/TemplateCmdLineIface.py +++ b/cheetah/TemplateCmdLineIface.py @@ -41,7 +41,7 @@ class CmdLineIface: """The main program controller.""" self._processCmdLineArgs() - print self._template + print(self._template) def _processCmdLineArgs(self): try: @@ -53,13 +53,13 @@ class CmdLineIface: except getopt.GetoptError, v: # print help information and exit: - print v - print self.usage() + print(v) + print(self.usage()) sys.exit(2) for o, a in self._opts: - if o in ('-h','--help'): - print self.usage() + if o in ('-h', '--help'): + print(self.usage()) sys.exit() if o == '--env': self._template.searchList().insert(0, os.environ) @@ -100,8 +100,8 @@ 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, +""" % {'scriptName': self._scriptName, + 'Version': Version, } # vim: shiftwidth=4 tabstop=4 expandtab diff --git a/cheetah/Templates/SkeletonPage.py b/cheetah/Templates/SkeletonPage.py index 04bf4fc..928ae2b 100644 --- a/cheetah/Templates/SkeletonPage.py +++ b/cheetah/Templates/SkeletonPage.py @@ -62,7 +62,7 @@ class SkeletonPage(_SkeletonPage): if not self._CHEETAH__instanceInitialized: cheetahKWArgs = {} allowedKWs = 'searchList namespaces filter filtersLib errorCatcher'.split() - for k,v in KWs.items(): + for k, v in KWs.items(): if k in allowedKWs: cheetahKWArgs[k] = v self._initCheetahInstance(**cheetahKWArgs) @@ -73,7 +73,7 @@ class SkeletonPage(_SkeletonPage): ## CHEETAH: generated from #block writeHeadTag at line 22, col 1. trans = KWS.get("trans") - if (not trans and not self._CHEETAH__isBuffering and not callable(self.transaction)): + 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() @@ -87,16 +87,16 @@ class SkeletonPage(_SkeletonPage): ## START - generated method body write('<head>\n<title>') - _v = VFFSL(SL,"title",True) # '$title' on line 24, col 8 + _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 + _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 + _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 + _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') @@ -112,7 +112,7 @@ class SkeletonPage(_SkeletonPage): ## CHEETAH: generated from #block writeBody at line 36, col 1. trans = KWS.get("trans") - if (not trans and not self._CHEETAH__isBuffering and not callable(self.transaction)): + 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() @@ -138,7 +138,7 @@ class SkeletonPage(_SkeletonPage): ## CHEETAH: main method generated for this template - if (not trans and not self._CHEETAH__isBuffering and not callable(self.transaction)): + 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() @@ -172,29 +172,29 @@ class SkeletonPage(_SkeletonPage): _orig_transheader = trans trans = _cacheCollector_header = DummyTransaction() write = _cacheCollector_header.response().write - _v = VFFSL(SL,"docType",True) # '$docType' on line 7, col 1 + _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 + _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 + _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 + _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 + _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 + _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(''' @@ -215,7 +215,7 @@ However, please do not copy any content or images without permission. ## END CACHE REGION: header write('\n') - _v = VFFSL(SL,"bodyTag",True) # '$bodyTag' on line 34, col 1 + _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) diff --git a/cheetah/Templates/_SkeletonPage.py b/cheetah/Templates/_SkeletonPage.py index fe01ebf..13f9db3 100644 --- a/cheetah/Templates/_SkeletonPage.py +++ b/cheetah/Templates/_SkeletonPage.py @@ -46,8 +46,8 @@ class _SkeletonPage(Template): def __init__(self, *args, **KWs): Template.__init__(self, *args, **KWs) - self._metaTags = {'HTTP-EQUIV':{'keywords':'Cheetah', - 'Content-Type':'text/html; charset=iso-8859-1', + self._metaTags = {'HTTP-EQUIV':{'keywords': 'Cheetah', + 'Content-Type': 'text/html; charset=iso-8859-1', }, 'NAME':{'generator':'Cheetah: The Python-Powered Template Engine'} } @@ -85,10 +85,10 @@ class _SkeletonPage(Template): stylesheetTagsTxt += '<style type="text/css"><!--\n' for identifier in self._stylesheetsOrder: - if not self._stylesheets.has_key(identifier): + if identifier not in self._stylesheets: warning = '# the identifier ' + identifier + \ 'was in stylesheetsOrder, but not in stylesheets' - print warning + print(warning) stylesheetTagsTxt += warning continue @@ -115,18 +115,18 @@ class _SkeletonPage(Template): SRC filename rather than a code string.""" javascriptTagsTxt = [] - for key, details in self._javascriptTags.items(): - if type(details) not in (types.ListType, types.TupleType): - details = ['',details] + 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.items(): - if type(details) not in (types.ListType, types.TupleType): - details = ['',details] + 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="', @@ -177,16 +177,16 @@ class _SkeletonPage(Template): return ''.join(['<img src="', src, '" height="', str(height), '" alt="', alt, '" border="', str(border), '" />']) else: - return ''.join(['<img src="', src, '" alt="', alt, '" border="', str(border),'" />']) + 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())) + 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())) + 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)) @@ -195,19 +195,19 @@ class _SkeletonPage(Template): """returns a string containing an HTML <tag> """ tagTxt = ['<', tagName.lower()] for name, val in attributes.items(): - tagTxt += [' ', name.lower(), '="', str(val),'"'] + 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 metaTags.has_key('HTTP-EQUIV'): + 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 metaTags.has_key('NAME'): + if 'NAME' in metaTags: for name, contents in metaTags['NAME'].items(): metaTagsTxt += ['<meta name="', str(name), '" content="', str(contents), '" />\n'] diff --git a/cheetah/Tests/Analyzer.py b/cheetah/Tests/Analyzer.py new file mode 100644 index 0000000..59f6c1c --- /dev/null +++ b/cheetah/Tests/Analyzer.py @@ -0,0 +1,29 @@ +#!/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() + diff --git a/cheetah/Tests/CheetahWrapper.py b/cheetah/Tests/CheetahWrapper.py index e152e68..1097aa0 100644 --- a/cheetah/Tests/CheetahWrapper.py +++ b/cheetah/Tests/CheetahWrapper.py @@ -12,6 +12,8 @@ Besides unittest usage, recognizes the following command-line options: Show the output of each subcommand. (Normally suppressed.) ''' import os +import os.path +import pdb import re # Used by listTests. import shutil import sys @@ -24,10 +26,13 @@ from Cheetah.CheetahWrapper import CheetahWrapper # Used by NoBackup. try: from subprocess import Popen, PIPE, STDOUT class Popen4(Popen): - def __init__(self, cmd, bufsize=-1): - super(Popen4, self).__init__(cmd, bufsize=bufsize, - shell=True, close_fds=True, - stdin=PIPE, stdout=PIPE, stderr=STDOUT) + 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 @@ -52,7 +57,7 @@ class CFBase(unittest.TestCase): def inform(self, message): if self.verbose: - print message + print(message) def setUp(self): """Create the top-level directories, subdirectories and .tmpl @@ -152,6 +157,17 @@ Found %(result)r""" msg = "backup file exists in spite of --nobackup: %s" % path self.failIf(exists, msg) + def locate_command(self, cmd): + paths = os.getenv('PATH') + if not paths: + return cmd + parts = cmd.split(' ') + paths = paths.split(':') + for p in paths: + p = p + os.path.sep + parts[0] + if os.path.isfile(p): + return ' '.join([p] + parts[1:]) + return ' '.join(parts) def assertWin32Subprocess(self, cmd): _in, _out = os.popen4(cmd) @@ -163,7 +179,8 @@ Found %(result)r""" return rc, output def assertPosixSubprocess(self, cmd): - process = Popen4(cmd) + cmd = self.locate_command(cmd) + process = Popen4(cmd, env=os.environ) process.tochild.close() output = process.fromchild.read() status = process.wait() @@ -514,13 +531,13 @@ def listTests(cheetahWrapperFile): """ rx = re.compile( R'self\.go\("(.*?)"\)' ) f = open(cheetahWrapperFile) - while 1: + while True: lin = f.readline() if not lin: break m = rx.search(lin) if m: - print m.group(1) + print(m.group(1)) f.close() def main(): diff --git a/cheetah/Tests/Filters.py b/cheetah/Tests/Filters.py index bf35440..65b3d93 100644 --- a/cheetah/Tests/Filters.py +++ b/cheetah/Tests/Filters.py @@ -1,12 +1,11 @@ #!/usr/bin/env python import sys +import unittest import Cheetah.Template import Cheetah.Filters -import unittest_local_copy as unittest - majorVer, minorVer = sys.version_info[0], sys.version_info[1] versionTuple = (majorVer, minorVer) @@ -29,9 +28,12 @@ Header 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 + print('>>> NOTE: Support for the Markdown filter will be broken for you. Markdown says: %s' % ex) return raise diff --git a/cheetah/Tests/Misc.py b/cheetah/Tests/Misc.py new file mode 100644 index 0000000..9ea66f0 --- /dev/null +++ b/cheetah/Tests/Misc.py @@ -0,0 +1,20 @@ +#!/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() + diff --git a/cheetah/Tests/NameMapper.py b/cheetah/Tests/NameMapper.py index 8efd6ac..fe6b658 100644 --- a/cheetah/Tests/NameMapper.py +++ b/cheetah/Tests/NameMapper.py @@ -1,6 +1,6 @@ #!/usr/bin/env python -from __future__ import generators + import sys import types import os @@ -34,7 +34,7 @@ class DummyClass: x = 'A string' try: - for i in [1,2,3,4]: + for i in [1, 2, 3, 4]: if x == 2: pass @@ -53,52 +53,52 @@ def funcThatRaises(): testNamespace = { - 'aStr':'blarg', - 'anInt':1, - 'aFloat':1.5, - 'aDict': {'one':'item1', - 'two':'item2', - 'nestedDict':{'one':'nestedItem1', - 'two':'nestedItem2', - 'funcThatRaises':funcThatRaises, + 'aStr': 'blarg', + 'anInt': 1, + 'aFloat': 1.5, + 'aDict': {'one': 'item1', + 'two': 'item2', + 'nestedDict': {'one': 'nestedItem1', + 'two': 'nestedItem2', + 'funcThatRaises': funcThatRaises, 'aClass': DummyClass, }, - 'nestedFunc':dummyFunc, + 'nestedFunc': dummyFunc, }, 'aClass': DummyClass, 'aFunc': dummyFunc, 'anObj': DummyClass(), 'aMeth': DummyClass().meth1, - 'none' : None, - 'emptyString':'', - 'funcThatRaises':funcThatRaises, + 'none': None, + 'emptyString': '', + 'funcThatRaises': funcThatRaises, } -autoCallResults = {'aFunc':'Scooby', - 'aMeth':'doo', +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', +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 + exec('%s = testNamespace[k]'%k) ################################################## ## TEST BASE CLASSES class NameMapperTest(unittest.TestCase): - failureException = (NotFound,AssertionError) + failureException = (NotFound, AssertionError) _testNamespace = testNamespace _results = results @@ -117,7 +117,7 @@ class NameMapperTest(unittest.TestCase): def check(self, name): got = self.get(name) - if autoCallResults.has_key(name): + if name in autoCallResults: expected = autoCallResults[name] else: expected = self._results[name] @@ -317,7 +317,7 @@ class VFN(NameMapperTest): def test(self=self): self.get('anObj.methX') - self.assertRaises(NotFound,test) + self.assertRaises(NotFound, test) def test44(self): """NotFound test in a loop""" @@ -325,7 +325,7 @@ class VFN(NameMapperTest): self.get('anObj.methX') for i in range(10): - self.assertRaises(NotFound,test) + self.assertRaises(NotFound, test) def test45(self): """Other exception from meth test""" @@ -340,7 +340,7 @@ class VFN(NameMapperTest): self.get('anObj.meth2') for i in range(10): - self.assertRaises(ValueError,test) + self.assertRaises(ValueError, test) def test47(self): """None in dict lookup""" @@ -373,7 +373,7 @@ class VFN(NameMapperTest): self.get('funcThatRaises') for i in range(10): - self.assertRaises(ValueError,test) + self.assertRaises(ValueError, test) def test53(self): @@ -389,7 +389,7 @@ class VFN(NameMapperTest): self.get('aDict.nestedDict.funcThatRaises') for i in range(10): - self.assertRaises(ValueError,test) + self.assertRaises(ValueError, test) def test55(self): """aDict.nestedDict.aClass in dict lookup""" @@ -428,10 +428,10 @@ class VFS(VFN): if lng == 1: return [self.namespace()] elif lng == 2: - return [self.namespace(),{'dummy':1234}] + return [self.namespace(), {'dummy':1234}] elif lng == 3: # a tuple for kicks - return ({'dummy':1234}, self.namespace(),{'dummy':1234}) + return ({'dummy':1234}, self.namespace(), {'dummy':1234}) elif lng == 4: # a generator for more kicks return self.searchListGenerator() @@ -439,7 +439,7 @@ class VFS(VFN): def searchListGenerator(self): class Test: pass - for i in [Test(),{'dummy':1234}, self.namespace(),{'dummy':1234}]: + for i in [Test(), {'dummy':1234}, self.namespace(), {'dummy':1234}]: yield i def get(self, name, autocall=True): diff --git a/cheetah/Tests/Parser.py b/cheetah/Tests/Parser.py new file mode 100644 index 0000000..050b613 --- /dev/null +++ b/cheetah/Tests/Parser.py @@ -0,0 +1,49 @@ +#!/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() + diff --git a/cheetah/Tests/Performance.py b/cheetah/Tests/Performance.py index 8101e85..d76cc00 100644 --- a/cheetah/Tests/Performance.py +++ b/cheetah/Tests/Performance.py @@ -57,8 +57,8 @@ def perftest(max_num_pystones, current_pystone=None): pystone_total_time = total_time / pystone_rate global DEBUG if DEBUG: - print 'The test "%s" took: %s pystones' % (function.func_name, - pystone_total_time) + 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, ' @@ -78,7 +78,7 @@ class DynamicTemplatePerformanceTest(unittest.TestCase): #pass #end def ''' - for i in xrange(self.loops): + for i in range(self.loops): klass = Cheetah.Template.Template.compile(template) assert klass test_BasicDynamic = perftest(1200)(test_BasicDynamic) @@ -91,15 +91,15 @@ class PerformanceTest(unittest.TestCase): def runTest(self): self.prof = hotshot.Profile('%s.prof' % self.__class__.__name__) self.prof.start() - for i in xrange(self.iterations): + 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) + 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') @@ -130,7 +130,7 @@ class BunchOfWriteCalls(PerformanceTest): template = ''' #import sys #import os - #for i in xrange(1000) + #for i in range(1000) $i #end for ''' @@ -210,7 +210,7 @@ class LongCompileTest(PerformanceTest): <body> $header() - #for $i in $xrange(10) + #for $i in $range(10) This is just some stupid page! <br/> #end for diff --git a/cheetah/Tests/Regressions.py b/cheetah/Tests/Regressions.py index 4d50348..67a736a 100644 --- a/cheetah/Tests/Regressions.py +++ b/cheetah/Tests/Regressions.py @@ -29,7 +29,7 @@ class GetAttrTest(unittest.TestCase): def test_ValidException(self): o = CustomGetAttrClass() try: - print o.attr + print(o.attr) except GetAttrException, e: # expected return diff --git a/cheetah/Tests/SyntaxAndOutput.py b/cheetah/Tests/SyntaxAndOutput.py index 72721bc..92c129b 100644 --- a/cheetah/Tests/SyntaxAndOutput.py +++ b/cheetah/Tests/SyntaxAndOutput.py @@ -67,37 +67,37 @@ def dummyFunc(arg="Scooby"): return arg defaultTestNameSpace = { - 'aStr':'blarg', - 'anInt':1, - 'aFloat':1.5, - 'aList': ['item0','item1','item2'], - 'aDict': {'one':'item1', - 'two':'item2', - 'nestedDict':{1:'nestedItem1', + 'aStr': 'blarg', + 'anInt': 1, + 'aFloat': 1.5, + 'aList': ['item0', 'item1', 'item2'], + 'aDict': {'one': 'item1', + 'two': 'item2', + 'nestedDict': {1:'nestedItem1', 'two':'nestedItem2' }, - 'nestedFunc':dummyFunc, + 'nestedFunc': dummyFunc, }, 'aFunc': dummyFunc, 'anObj': DummyClass(), 'aMeth': DummyClass().meth1, 'aStrToBeIncluded': "$aStr $anInt", - 'none' : None, - 'emptyString':'', - 'numOne':1, - 'numTwo':2, - 'zero':0, + 'none': None, + 'emptyString': '', + 'numOne': 1, + 'numTwo': 2, + 'zero': 0, 'tenDigits': 1234567890, 'webSafeTest': 'abc <=> &', 'strip1': ' \t strippable whitespace \t\t \n', 'strip2': ' \t strippable whitespace \t\t ', 'strip3': ' \t strippable whitespace \t\t\n1 2 3\n', - 'blockToBeParsed':"""$numOne $numTwo""", - 'includeBlock2':"""$numOne $numTwo $aSetVar""", + 'blockToBeParsed': """$numOne $numTwo""", + 'includeBlock2': """$numOne $numTwo $aSetVar""", - 'includeFileName':'parseTest.txt', - 'listOfLambdas':[lambda x: x, lambda x: x, lambda x: x,], + 'includeFileName': 'parseTest.txt', + 'listOfLambdas': [lambda x: x, lambda x: x, lambda x: x,], 'list': [ {'index': 0, 'numOne': 1, 'numTwo': 2}, {'index': 1, 'numOne': 1, 'numTwo': 2}, @@ -105,7 +105,7 @@ defaultTestNameSpace = { 'nameList': [('john', 'doe'), ('jane', 'smith')], 'letterList': ['a', 'b', 'c'], '_': lambda x: 'Translated: ' + x, - 'unicodeData':u'aoeu12345\u1234', + 'unicodeData': u'aoeu12345\u1234', } @@ -162,7 +162,8 @@ Template output mismatch: **extraKwArgs ) moduleCode = templateClass._CHEETAH_generatedModuleCode - self.template = templateObj = templateClass(searchList=self.searchList()) + searchList = self.searchList() or self._searchList + self.template = templateObj = templateClass(searchList=searchList) else: self.template = templateObj = Template( input, @@ -171,7 +172,7 @@ Template output mismatch: ) moduleCode = templateObj._CHEETAH_generatedModuleCode if self.DEBUGLEV >= 1: - print moduleCode + print(moduleCode) try: output = templateObj.respond() # rather than __str__, because of unicode assert output==expectedOutput, self._outputMismatchReport(output, expectedOutput) @@ -185,9 +186,9 @@ Template output mismatch: if self._debugEOLReplacement and self._EOLreplacement: EOLrepl = self._EOLreplacement marker = '*EOL*' - return self.report % {'template': self._input.replace(EOLrepl,marker), - 'expected': expectedOutput.replace(EOLrepl,marker), - 'actual': output.replace(EOLrepl,marker), + return self.report % {'template': self._input.replace(EOLrepl, marker), + 'expected': expectedOutput.replace(EOLrepl, marker), + 'actual': output.replace(EOLrepl, marker), 'end': '(end)'} else: return self.report % {'template': self._input, @@ -237,7 +238,7 @@ class Backslashes(OutputTest): convertEOLs = False def setUp(self): - fp = open('backslashes.txt','w') + fp = open('backslashes.txt', 'w') fp.write(r'\ #LogFormat "%h %l %u %t \"%r\" %>s %b"' + '\n\n\n\n\n\n\n') fp.flush() fp.close @@ -620,7 +621,7 @@ class Placeholders(OutputTest): tmpl = tmpl.subclass('#for name in $names: $*1*(name) ') assert str(tmpl({'names':names}))=='You '*len(names) - if versionTuple > (2,2): + if versionTuple > (2, 2): tmpl = tmpl.subclass('#for name in $names: $*1*(name) ') assert str(tmpl(names=names))=='You '*len(names) @@ -1359,6 +1360,12 @@ class RawDirective(OutputTest): self.verify("#raw: $aFunc().\n$anInt", "$aFunc().\n1") + def test6(self): + """ Escape characters in a #raw block """ + self.verify( """#raw: This escape should be preserved: \\$unexpanded So should this one: \\#blah The string "\\012" should not disappear.""", + r"""This escape should be preserved: \$unexpanded So should this one: \#blah The string "\012" should not disappear.""") + + class BreakpointDirective(OutputTest): def test1(self): """#breakpoint part way through source code""" @@ -1477,7 +1484,7 @@ class YieldDirective(OutputTest): ) - for src in (src1,src2,src3): + for src in (src1, src2, src3): klass = Template.compile(src, keepRefToGeneratedCode=True) #print klass._CHEETAH_generatedModuleCode iter = klass().respond() @@ -1487,7 +1494,7 @@ class YieldDirective(OutputTest): # @@TR: need to expand this to cover error conditions etc. -if versionTuple < (2,3): +if versionTuple < (2, 3): del YieldDirective class ForDirective(OutputTest): @@ -1589,7 +1596,7 @@ class ForDirective(OutputTest): self.verify("#for $i in range(5): \n$i\n#end for", "0\n1\n2\n3\n4\n") -if versionTuple < (2,3): +if versionTuple < (2, 3): del ForDirective.test12 class RepeatDirective(OutputTest): @@ -1826,7 +1833,7 @@ class DecoratorDirective(OutputTest): "$testMeth", "1234\n") -if versionTuple < (2,4): +if versionTuple < (2, 4): del DecoratorDirective class BlockDirective(OutputTest): @@ -1941,7 +1948,7 @@ inner class IncludeDirective(OutputTest): def setUp(self): - fp = open('parseTest.txt','w') + fp = open('parseTest.txt', 'w') fp.write("$numOne $numTwo") fp.flush() fp.close @@ -2338,6 +2345,8 @@ class UnlessDirective(OutputTest): self.verify("#unless 0: 1234\n"*2, "1234\n"*2) class PSP(OutputTest): + def searchList(self): + return None def test1(self): """simple <%= [int] %>""" @@ -2376,6 +2385,19 @@ class PSP(OutputTest): self.verify("""<% for i in range(5): i=i*2$%><%=i%>-<%end%>""", "0-2-4-6-8-") + def test10(self): + """ Using getVar and write within a PSP """ + self._searchList = [{'me' : 1}] + template = '''This is my template +<% +me = self.getVar('me') +if isinstance(me, int): + write('Bork') +else: + write('Nork') +%>''' + self.verify(template, 'This is my template\nBork') + class WhileDirective(OutputTest): def test1(self): @@ -3192,26 +3214,26 @@ public class X ################################################## ## CREATE CONVERTED EOL VERSIONS OF THE TEST CASES -if OutputTest._useNewStyleCompilation and versionTuple >= (2,3): +if OutputTest._useNewStyleCompilation and versionTuple >= (2, 3): extraCompileKwArgsForDiffBaseclass = {'baseclass':dict} else: extraCompileKwArgsForDiffBaseclass = {'baseclass':object} def install_eols(): - klasses = [v for v in globals().values() if isinstance(v, (types.ClassType, types.TypeType)) and issubclass(v, unittest.TestCase)] + klasses = [v for v in globals().values() if isinstance(v, type) and issubclass(v, unittest.TestCase)] for klass in klasses: name = klass.__name__ - if hasattr(klass,'convertEOLs') and klass.convertEOLs: + if hasattr(klass, 'convertEOLs') and klass.convertEOLs: win32Src = r"class %(name)s_Win32EOL(%(name)s): _EOLreplacement = '\r\n'"%locals() macSrc = r"class %(name)s_MacEOL(%(name)s): _EOLreplacement = '\r'"%locals() - exec win32Src in globals() - exec macSrc in globals() + exec(win32Src, globals()) + exec(macSrc, globals()) - if versionTuple >= (2,3): + if versionTuple >= (2, 3): src = r"class %(name)s_DiffBaseClass(%(name)s): "%locals() src += " _extraCompileKwArgs = extraCompileKwArgsForDiffBaseclass" - exec src in globals() + exec(src, globals()) del name del klass diff --git a/cheetah/Tests/Template.py b/cheetah/Tests/Template.py index 144ae6f..331c0f9 100644 --- a/cheetah/Tests/Template.py +++ b/cheetah/Tests/Template.py @@ -44,7 +44,7 @@ class ClassMethods_compile(TemplateTest): assert str(t)=='1234' def test_moduleFileCaching(self): - if versionTuple < (2,3): + if versionTuple < (2, 3): return tmpDir = tempfile.mkdtemp() try: @@ -220,9 +220,9 @@ class Preprocessors(TemplateTest): class TemplateSubclass(Template): pass - compilerSettings = {'cheetahVarStartToken':'@', - 'directiveStartToken':'%', - 'commentStartToken':'%%', + compilerSettings = {'cheetahVarStartToken': '@', + 'directiveStartToken': '%', + 'commentStartToken': '%%', } for arg in ['@ %', @@ -293,7 +293,7 @@ class TryExceptImportTest(TemplateTest): 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' + print('This version of Python doesn\'t support decorators, skipping tests') return template = ''' #@classmethod @@ -311,7 +311,7 @@ class ClassMethodSupport(TemplateTest): 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' + print('This version of Python doesn\'t support decorators, skipping tests') return template = ''' #@staticmethod diff --git a/cheetah/Tests/Test.py b/cheetah/Tests/Test.py index 13b0171..8e78a8e 100755 --- a/cheetah/Tests/Test.py +++ b/cheetah/Tests/Test.py @@ -13,14 +13,17 @@ TODO import sys import unittest -import SyntaxAndOutput -import NameMapper -import Filters -import Template -import Cheps -import Regressions -import Unicode -import CheetahWrapper +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() @@ -32,6 +35,9 @@ suites = [ #unittest.findTestCases(Cheps), unittest.findTestCases(Regressions), unittest.findTestCases(Unicode), + unittest.findTestCases(Misc), + unittest.findTestCases(Parser), + unittest.findTestCases(Analyzer), ] if not sys.platform.startswith('java'): diff --git a/cheetah/Tests/Unicode.py b/cheetah/Tests/Unicode.py index 12c00ac..c881d86 100644 --- a/cheetah/Tests/Unicode.py +++ b/cheetah/Tests/Unicode.py @@ -21,7 +21,7 @@ class CommandLineTest(unittest.TestCase): fd.close() wrap = CheetahWrapper.CheetahWrapper() - wrap.main(['cheetah', 'compile', '--nobackup', sourcefile]) + 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) @@ -194,5 +194,44 @@ class Unicode_in_SearchList_Test(CommandLineTest): 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() diff --git a/cheetah/Tests/VerifyType.py b/cheetah/Tests/VerifyType.py deleted file mode 100644 index a581d70..0000000 --- a/cheetah/Tests/VerifyType.py +++ /dev/null @@ -1,158 +0,0 @@ -#!/usr/bin/env python -import unittest - -from Cheetah.Utils import VerifyType -from Cheetah import _verifytype - -class VerifyType_Test(unittest.TestCase): - def test_Verified(self): - arg = 'foo' - legalTypes = [str, unicode] - try: - rc = VerifyType.VerifyType(arg, 'arg', legalTypes, 'string') - assert rc - except TypeError: - self.fail('Should not have raised a TypeError here') - - try: - rc = _verifytype.verifyType(arg, 'arg', legalTypes, 'string') - assert rc - except TypeError: - self.fail('Should not have raised a TypeError here') - - def test_Unverified(self): - arg = 'foo' - legalTypes = [list, dict] - self.failUnlessRaises(TypeError, VerifyType.VerifyType, arg, - 'arg', legalTypes, 'list or dict') - self.failUnlessRaises(TypeError, _verifytype.verifyType, arg, - 'arg', legalTypes, 'list or dict') - - def test_IncorrectNumberOfArgs(self): - arg = 'foo' - legalTypes = [str, unicode] - - self.failUnlessRaises(TypeError, VerifyType.VerifyType) - self.failUnlessRaises(TypeError, _verifytype.verifyType) - - self.failUnlessRaises(TypeError, VerifyType.VerifyType, arg) - self.failUnlessRaises(TypeError, _verifytype.verifyType, arg) - - self.failUnlessRaises(TypeError, VerifyType.VerifyType, arg, - 'arg') - self.failUnlessRaises(TypeError, _verifytype.verifyType, arg, - 'arg') - - self.failUnlessRaises(TypeError, VerifyType.VerifyType, arg, - 'arg', legalTypes) - self.failUnlessRaises(TypeError, _verifytype.verifyType, arg, - 'arg', legalTypes) - - self.failUnlessRaises(TypeError, VerifyType.VerifyType, arg, - 'arg', legalTypes, 'string', 'errmsgExtra', 'one more') - self.failUnlessRaises(TypeError, _verifytype.verifyType, arg, - 'arg', legalTypes, 'string', 'errmsgExtra', 'one more') - - def test_LegalTypesNotIterable(self): - arg = 'foo' - legalTypes = 1 - - self.failUnlessRaises(TypeError, VerifyType.VerifyType, arg, - 'arg', legalTypes, 'string') - self.failUnlessRaises(TypeError, _verifytype.verifyType, arg, - 'arg', legalTypes, 'string') - -class FakeClass(dict): - pass - -class VerifyTypeClass_Test(unittest.TestCase): - def test_VerifiedClass(self): - arg = FakeClass - legalTypes = [type] - try: - rc = VerifyType.VerifyTypeClass(arg, 'arg', legalTypes, '', dict) - assert rc - except TypeError: - self.fail('Should not have raised a TypeError here') - - try: - rc = _verifytype.verifyTypeClass(arg, 'arg', legalTypes, 'foo', dict) - assert rc - except TypeError: - self.fail('Should not have raised a TypeError here') - - def test_UnverifiedClass(self): - arg = FakeClass - legalTypes = [type] - self.failUnlessRaises(TypeError, VerifyType.VerifyTypeClass, arg, - legalTypes, 'subclass of list', list) - self.failUnlessRaises(TypeError, _verifytype.verifyTypeClass, arg, - legalTypes, 'subclass of list', list) - - def test_Verified(self): - arg = 'foo' - legalTypes = [str, unicode] - try: - rc = VerifyType.VerifyTypeClass(arg, 'arg', legalTypes, 'string', int) - assert rc - except TypeError: - self.fail('Should not have raised a TypeError here') - - try: - rc = _verifytype.verifyTypeClass(arg, 'arg', legalTypes, 'string', int) - assert rc - except TypeError: - self.fail('Should not have raised a TypeError here') - - def test_Unverified(self): - arg = 'foo' - legalTypes = [list, dict] - self.failUnlessRaises(TypeError, VerifyType.VerifyTypeClass, arg, - 'arg', legalTypes, 'list or dict', int) - self.failUnlessRaises(TypeError, _verifytype.verifyTypeClass, arg, - 'arg', legalTypes, 'list or dict', int) - - def test_IncorrectNumberOfArgs(self): - arg = 'foo' - legalTypes = [str, unicode] - - self.failUnlessRaises(TypeError, VerifyType.VerifyTypeClass) - self.failUnlessRaises(TypeError, _verifytype.verifyTypeClass) - - self.failUnlessRaises(TypeError, VerifyType.VerifyTypeClass, arg) - self.failUnlessRaises(TypeError, _verifytype.verifyTypeClass, arg) - - self.failUnlessRaises(TypeError, VerifyType.VerifyTypeClass, arg, - 'arg') - self.failUnlessRaises(TypeError, _verifytype.verifyTypeClass, arg, - 'arg') - - self.failUnlessRaises(TypeError, VerifyType.VerifyTypeClass, arg, - 'arg', legalTypes) - self.failUnlessRaises(TypeError, _verifytype.verifyTypeClass, arg, - 'arg', legalTypes) - - self.failUnlessRaises(TypeError, VerifyType.VerifyTypeClass, arg, - 'arg', legalTypes, 'string') - self.failUnlessRaises(TypeError, _verifytype.verifyTypeClass, arg, - 'arg', legalTypes, 'string') - - self.failUnlessRaises(TypeError, VerifyType.VerifyTypeClass, arg, - 'arg', legalTypes, 'string', int, 'errmsgExtra', 'one more') - self.failUnlessRaises(TypeError, _verifytype.verifyTypeClass, arg, - 'arg', legalTypes, 'string', int, 'errmsgExtra', 'one more') - - def test_LegalTypesNotIterable(self): - arg = 'foo' - legalTypes = 1 - - self.failUnlessRaises(TypeError, VerifyType.VerifyTypeClass, arg, - 'arg', legalTypes, 'string', int) - self.failUnlessRaises(TypeError, _verifytype.verifyTypeClass, arg, - 'arg', legalTypes, 'string', int) - - - - -if __name__ == '__main__': - unittest.main() diff --git a/cheetah/Tests/unittest_local_copy.py b/cheetah/Tests/unittest_local_copy.py deleted file mode 100755 index a5f5499..0000000 --- a/cheetah/Tests/unittest_local_copy.py +++ /dev/null @@ -1,978 +0,0 @@ -#!/usr/bin/env python -""" This is a hacked version of PyUnit that extends its reporting capabilities -with optional meta data on the test cases. It also makes it possible to -separate the standard and error output streams in TextTestRunner. - -It's a hack rather than a set of subclasses because a) Steve had used double -underscore private attributes for some things I needed access to, and b) the -changes affected so many classes that it was easier just to hack it. - -The changes are in the following places: -TestCase: - - minor refactoring of __init__ and __call__ internals - - added some attributes and methods for storing and retrieving meta data - -_TextTestResult - - refactored the stream handling - - incorporated all the output code from TextTestRunner - - made the output of FAIL and ERROR information more flexible and - incorporated the new meta data from TestCase - - added a flag called 'explain' to __init__ that controls whether the new ' - explanation' meta data from TestCase is printed along with tracebacks - -TextTestRunner - - delegated all output to _TextTestResult - - added 'err' and 'explain' to the __init__ signature to match the changes - in _TextTestResult - -TestProgram - - added -e and --explain as flags on the command line - --- Tavis Rudd <tavis@redonions.net> (Sept 28th, 2001) - -- _TestTextResult.printErrorList(): print blank line after each traceback - --- Mike Orr <mso@oz.net> (Nov 11, 2002) - -TestCase methods copied from unittest in Python 2.3: - - .assertAlmostEqual(first, second, places=7, msg=None): to N decimal places. - - .failIfAlmostEqual(first, second, places=7, msg=None) - --- Mike Orr (Jan 5, 2004) - - -Below is the original docstring for unittest. ---------------------------------------------------------------------------- -Python unit testing framework, based on Erich Gamma's JUnit and Kent Beck's -Smalltalk testing framework. - -This module contains the core framework classes that form the basis of -specific test cases and suites (TestCase, TestSuite etc.), and also a -text-based utility class for running the tests and reporting the results -(TextTestRunner). - -Simple usage: - - import unittest - - class IntegerArithmenticTestCase(unittest.TestCase): - def testAdd(self): ## test method names begin 'test*' - self.assertEquals((1 + 2), 3) - self.assertEquals(0 + 1, 1) - def testMultiply(self); - self.assertEquals((0 * 10), 0) - self.assertEquals((5 * 8), 40) - - if __name__ == '__main__': - unittest.main() - -Further information is available in the bundled documentation, and from - - http://pyunit.sourceforge.net/ - -Copyright (c) 1999, 2000, 2001 Steve Purcell -This module is free software, and you may redistribute it and/or modify -it under the same terms as Python itself, so long as this copyright message -and disclaimer are retained in their original form. - -IN NO EVENT SHALL THE AUTHOR BE LIABLE TO ANY PARTY FOR DIRECT, INDIRECT, -SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OF -THIS CODE, EVEN IF THE AUTHOR HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH -DAMAGE. - -THE AUTHOR SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A -PARTICULAR PURPOSE. THE CODE PROVIDED HEREUNDER IS ON AN "AS IS" BASIS, -AND THERE IS NO OBLIGATION WHATSOEVER TO PROVIDE MAINTENANCE, -SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. -""" - -__author__ = "Steve Purcell" -__email__ = "stephen_purcell at yahoo dot com" -__revision__ = "$Revision: 1.11 $"[11:-2] - - -################################################## -## DEPENDENCIES ## - -import os -import re -import string -import sys -import time -import traceback -import types -import pprint - -################################################## -## CONSTANTS & GLOBALS - -try: - True,False -except NameError: - True, False = (1==1),(1==0) - -############################################################################## -# Test framework core -############################################################################## - - -class TestResult: - """Holder for test result information. - - Test results are automatically managed by the TestCase and TestSuite - classes, and do not need to be explicitly manipulated by writers of tests. - - Each instance holds the total number of tests run, and collections of - failures and errors that occurred among those test runs. The collections - contain tuples of (testcase, exceptioninfo), where exceptioninfo is a - tuple of values as returned by sys.exc_info(). - """ - def __init__(self): - self.failures = [] - self.errors = [] - self.testsRun = 0 - self.shouldStop = 0 - - def startTest(self, test): - "Called when the given test is about to be run" - self.testsRun = self.testsRun + 1 - - def stopTest(self, test): - "Called when the given test has been run" - pass - - def addError(self, test, err): - "Called when an error has occurred" - self.errors.append((test, err)) - - def addFailure(self, test, err): - "Called when a failure has occurred" - self.failures.append((test, err)) - - def addSuccess(self, test): - "Called when a test has completed successfully" - pass - - def wasSuccessful(self): - "Tells whether or not this result was a success" - return len(self.failures) == len(self.errors) == 0 - - def stop(self): - "Indicates that the tests should be aborted" - self.shouldStop = 1 - - def __repr__(self): - return "<%s run=%i errors=%i failures=%i>" % \ - (self.__class__, self.testsRun, len(self.errors), - len(self.failures)) - -class TestCase: - """A class whose instances are single test cases. - - By default, the test code itself should be placed in a method named - 'runTest'. - - If the fixture may be used for many test cases, create as - many test methods as are needed. When instantiating such a TestCase - subclass, specify in the constructor arguments the name of the test method - that the instance is to execute. - - Test authors should subclass TestCase for their own tests. Construction - and deconstruction of the test's environment ('fixture') can be - implemented by overriding the 'setUp' and 'tearDown' methods respectively. - - If it is necessary to override the __init__ method, the base class - __init__ method must always be called. It is important that subclasses - should not change the signature of their __init__ method, since instances - of the classes are instantiated automatically by parts of the framework - in order to be run. - """ - - # This attribute determines which exception will be raised when - # the instance's assertion methods fail; test methods raising this - # exception will be deemed to have 'failed' rather than 'errored' - - failureException = AssertionError - - # the name of the fixture. Used for displaying meta data about the test - name = None - - def __init__(self, methodName='runTest'): - """Create an instance of the class that will use the named test - method when executed. Raises a ValueError if the instance does - not have a method with the specified name. - """ - self._testMethodName = methodName - self._setupTestMethod() - self._setupMetaData() - - def _setupTestMethod(self): - try: - self._testMethod = getattr(self, self._testMethodName) - except AttributeError: - raise ValueError, "no such test method in %s: %s" % \ - (self.__class__, self._testMethodName) - - ## meta data methods - - def _setupMetaData(self): - """Setup the default meta data for the test case: - - - id: self.__class__.__name__ + testMethodName OR self.name + testMethodName - - description: 1st line of Class docstring + 1st line of method docstring - - explanation: rest of Class docstring + rest of method docstring - - """ - - - testDoc = self._testMethod.__doc__ or '\n' - testDocLines = testDoc.splitlines() - - testDescription = testDocLines[0].strip() - if len(testDocLines) > 1: - testExplanation = '\n'.join( - [ln.strip() for ln in testDocLines[1:]] - ).strip() - else: - testExplanation = '' - - fixtureDoc = self.__doc__ or '\n' - fixtureDocLines = fixtureDoc.splitlines() - fixtureDescription = fixtureDocLines[0].strip() - if len(fixtureDocLines) > 1: - fixtureExplanation = '\n'.join( - [ln.strip() for ln in fixtureDocLines[1:]] - ).strip() - else: - fixtureExplanation = '' - - if not self.name: - self.name = self.__class__ - self._id = "%s.%s" % (self.name, self._testMethodName) - - if not fixtureDescription: - self._description = testDescription - else: - self._description = fixtureDescription + ', ' + testDescription - - if not fixtureExplanation: - self._explanation = testExplanation - else: - self._explanation = ['Fixture Explanation:', - '--------------------', - fixtureExplanation, - '', - 'Test Explanation:', - '-----------------', - testExplanation - ] - self._explanation = '\n'.join(self._explanation) - - def id(self): - return self._id - - def setId(self, id): - self._id = id - - def describe(self): - """Returns a one-line description of the test, or None if no - description has been provided. - - The default implementation of this method returns the first line of - the specified test method's docstring. - """ - return self._description - - shortDescription = describe - - def setDescription(self, descr): - self._description = descr - - def explain(self): - return self._explanation - - def setExplanation(self, expln): - self._explanation = expln - - ## core methods - - def setUp(self): - "Hook method for setting up the test fixture before exercising it." - pass - - def run(self, result=None): - return self(result) - - def tearDown(self): - "Hook method for deconstructing the test fixture after testing it." - pass - - def debug(self): - """Run the test without collecting errors in a TestResult""" - self.setUp() - self._testMethod() - self.tearDown() - - ## internal methods - - def defaultTestResult(self): - return TestResult() - - def __call__(self, result=None): - if result is None: - result = self.defaultTestResult() - - result.startTest(self) - try: - try: - self.setUp() - except: - result.addError(self, self.__exc_info()) - return - - ok = 0 - try: - self._testMethod() - ok = 1 - except self.failureException, e: - result.addFailure(self, self.__exc_info()) - except: - result.addError(self, self.__exc_info()) - try: - self.tearDown() - except: - result.addError(self, self.__exc_info()) - ok = 0 - if ok: - result.addSuccess(self) - finally: - result.stopTest(self) - - return result - - def countTestCases(self): - return 1 - - def __str__(self): - return "%s (%s)" % (self._testMethodName, self.__class__) - - def __repr__(self): - return "<%s testMethod=%s>" % \ - (self.__class__, self._testMethodName) - - def __exc_info(self): - """Return a version of sys.exc_info() with the traceback frame - minimised; usually the top level of the traceback frame is not - needed. - """ - exctype, excvalue, tb = sys.exc_info() - if sys.platform[:4] == 'java': ## tracebacks look different in Jython - return (exctype, excvalue, tb) - newtb = tb.tb_next - if newtb is None: - return (exctype, excvalue, tb) - return (exctype, excvalue, newtb) - - ## methods for use by the test cases - - def fail(self, msg=None): - """Fail immediately, with the given message.""" - raise self.failureException, msg - - def failIf(self, expr, msg=None): - "Fail the test if the expression is true." - if expr: raise self.failureException, msg - - def failUnless(self, expr, msg=None): - """Fail the test unless the expression is true.""" - if not expr: raise self.failureException, msg - - def failUnlessRaises(self, excClass, callableObj, *args, **kwargs): - """Fail unless an exception of class excClass is thrown - by callableObj when invoked with arguments args and keyword - arguments kwargs. If a different type of exception is - thrown, it will not be caught, and the test case will be - deemed to have suffered an error, exactly as for an - unexpected exception. - """ - try: - apply(callableObj, args, kwargs) - except excClass: - return - else: - if hasattr(excClass,'__name__'): excName = excClass.__name__ - else: excName = str(excClass) - raise self.failureException, excName - - def failUnlessEqual(self, first, second, msg=None): - """Fail if the two objects are unequal as determined by the '!=' - operator. - """ - if first != second: - raise self.failureException, (msg or '%s != %s' % (first, second)) - - def failIfEqual(self, first, second, msg=None): - """Fail if the two objects are equal as determined by the '==' - operator. - """ - if first == second: - raise self.failureException, (msg or '%s == %s' % (first, second)) - - def failUnlessAlmostEqual(self, first, second, places=7, msg=None): - """Fail if the two objects are unequal as determined by their - difference rounded to the given number of decimal places - (default 7) and comparing to zero. - - Note that decimal places (from zero) is usually not the same - as significant digits (measured from the most signficant digit). - """ - if round(second-first, places) != 0: - raise self.failureException, \ - (msg or '%s != %s within %s places' % (`first`, `second`, `places` )) - - def failIfAlmostEqual(self, first, second, places=7, msg=None): - """Fail if the two objects are equal as determined by their - difference rounded to the given number of decimal places - (default 7) and comparing to zero. - - Note that decimal places (from zero) is usually not the same - as significant digits (measured from the most signficant digit). - """ - if round(second-first, places) == 0: - raise self.failureException, \ - (msg or '%s == %s within %s places' % (`first`, `second`, `places`)) - - ## aliases - - assertEqual = assertEquals = failUnlessEqual - - assertNotEqual = assertNotEquals = failIfEqual - - assertAlmostEqual = assertAlmostEquals = failUnlessAlmostEqual - - assertNotAlmostEqual = assertNotAlmostEquals = failIfAlmostEqual - - assertRaises = failUnlessRaises - - assert_ = failUnless - - -class FunctionTestCase(TestCase): - """A test case that wraps a test function. - - This is useful for slipping pre-existing test functions into the - PyUnit framework. Optionally, set-up and tidy-up functions can be - supplied. As with TestCase, the tidy-up ('tearDown') function will - always be called if the set-up ('setUp') function ran successfully. - """ - - def __init__(self, testFunc, setUp=None, tearDown=None, - description=None): - TestCase.__init__(self) - self.__setUpFunc = setUp - self.__tearDownFunc = tearDown - self.__testFunc = testFunc - self.__description = description - - def setUp(self): - if self.__setUpFunc is not None: - self.__setUpFunc() - - def tearDown(self): - if self.__tearDownFunc is not None: - self.__tearDownFunc() - - def runTest(self): - self.__testFunc() - - def id(self): - return self.__testFunc.__name__ - - def __str__(self): - return "%s (%s)" % (self.__class__, self.__testFunc.__name__) - - def __repr__(self): - return "<%s testFunc=%s>" % (self.__class__, self.__testFunc) - - - def describe(self): - if self.__description is not None: return self.__description - doc = self.__testFunc.__doc__ - return doc and string.strip(string.split(doc, "\n")[0]) or None - - ## aliases - shortDescription = describe - -class TestSuite: - """A test suite is a composite test consisting of a number of TestCases. - - For use, create an instance of TestSuite, then add test case instances. - When all tests have been added, the suite can be passed to a test - runner, such as TextTestRunner. It will run the individual test cases - in the order in which they were added, aggregating the results. When - subclassing, do not forget to call the base class constructor. - """ - def __init__(self, tests=(), suiteName=None): - self._tests = [] - self._testMap = {} - self.suiteName = suiteName - self.addTests(tests) - - def __repr__(self): - return "<%s tests=%s>" % (self.__class__, pprint.pformat(self._tests)) - - __str__ = __repr__ - - def countTestCases(self): - cases = 0 - for test in self._tests: - cases = cases + test.countTestCases() - return cases - - def addTest(self, test): - self._tests.append(test) - if isinstance(test, TestSuite) and test.suiteName: - name = test.suiteName - elif isinstance(test, TestCase): - #print test, test._testMethodName - name = test._testMethodName - else: - name = test.__class__.__name__ - self._testMap[name] = test - - def addTests(self, tests): - for test in tests: - self.addTest(test) - - def getTestForName(self, name): - return self._testMap[name] - - def run(self, result): - return self(result) - - def __call__(self, result): - for test in self._tests: - if result.shouldStop: - break - test(result) - return result - - def debug(self): - """Run the tests without collecting errors in a TestResult""" - for test in self._tests: test.debug() - - -############################################################################## -# Text UI -############################################################################## - -class StreamWrapper: - def __init__(self, out=sys.stdout, err=sys.stderr): - self._streamOut = out - self._streamErr = err - - def write(self, txt): - self._streamOut.write(txt) - self._streamOut.flush() - - def writeln(self, *lines): - for line in lines: - self.write(line + '\n') - if not lines: - self.write('\n') - - def writeErr(self, txt): - self._streamErr.write(txt) - - def writelnErr(self, *lines): - for line in lines: - self.writeErr(line + '\n') - if not lines: - self.writeErr('\n') - - -class _TextTestResult(TestResult, StreamWrapper): - _separatorWidth = 70 - _sep1 = '=' - _sep2 = '-' - _errorSep1 = '*' - _errorSep2 = '-' - _errorSep3 = '' - - def __init__(self, - stream=sys.stdout, - errStream=sys.stderr, - verbosity=1, - explain=False): - - TestResult.__init__(self) - StreamWrapper.__init__(self, out=stream, err=errStream) - - self._verbosity = verbosity - self._showAll = verbosity > 1 - self._dots = (verbosity == 1) - self._explain = explain - - ## startup and shutdown methods - - def beginTests(self): - self._startTime = time.time() - - def endTests(self): - self._stopTime = time.time() - self._timeTaken = float(self._stopTime - self._startTime) - - def stop(self): - self.shouldStop = 1 - - ## methods called for each test - - def startTest(self, test): - TestResult.startTest(self, test) - if self._showAll: - self.write("%s (%s)" %( test.id(), test.describe() ) ) - self.write(" ... ") - - def addSuccess(self, test): - TestResult.addSuccess(self, test) - if self._showAll: - self.writeln("ok") - elif self._dots: - self.write('.') - - def addError(self, test, err): - TestResult.addError(self, test, err) - if self._showAll: - self.writeln("ERROR") - elif self._dots: - self.write('E') - if err[0] is KeyboardInterrupt: - self.stop() - - def addFailure(self, test, err): - TestResult.addFailure(self, test, err) - if self._showAll: - self.writeln("FAIL") - elif self._dots: - self.write('F') - - ## display methods - - def summarize(self): - self.printErrors() - self.writeSep2() - run = self.testsRun - self.writeln("Ran %d test%s in %.3fs" % - (run, run == 1 and "" or "s", self._timeTaken)) - self.writeln() - if not self.wasSuccessful(): - self.writeErr("FAILED (") - failed, errored = map(len, (self.failures, self.errors)) - if failed: - self.writeErr("failures=%d" % failed) - if errored: - if failed: self.writeErr(", ") - self.writeErr("errors=%d" % errored) - self.writelnErr(")") - else: - self.writelnErr("OK") - - def writeSep1(self): - self.writeln(self._sep1 * self._separatorWidth) - - def writeSep2(self): - self.writeln(self._sep2 * self._separatorWidth) - - def writeErrSep1(self): - self.writeln(self._errorSep1 * self._separatorWidth) - - def writeErrSep2(self): - self.writeln(self._errorSep2 * self._separatorWidth) - - def printErrors(self): - if self._dots or self._showAll: - self.writeln() - self.printErrorList('ERROR', self.errors) - self.printErrorList('FAIL', self.failures) - - def printErrorList(self, flavour, errors): - for test, err in errors: - self.writeErrSep1() - self.writelnErr("%s %s (%s)" % (flavour, test.id(), test.describe() )) - if self._explain: - expln = test.explain() - if expln: - self.writeErrSep2() - self.writeErr( expln ) - self.writelnErr() - - self.writeErrSep2() - for line in apply(traceback.format_exception, err): - for l in line.split("\n")[:-1]: - self.writelnErr(l) - self.writelnErr("") - -class TextTestRunner: - def __init__(self, - stream=sys.stdout, - errStream=sys.stderr, - verbosity=1, - explain=False): - - self._out = stream - self._err = errStream - self._verbosity = verbosity - self._explain = explain - - ## main methods - - def run(self, test): - result = self._makeResult() - result.beginTests() - test( result ) - result.endTests() - result.summarize() - - return result - - ## internal methods - - def _makeResult(self): - return _TextTestResult(stream=self._out, - errStream=self._err, - verbosity=self._verbosity, - explain=self._explain, - ) - -############################################################################## -# Locating and loading tests -############################################################################## - -class TestLoader: - """This class is responsible for loading tests according to various - criteria and returning them wrapped in a Test - """ - testMethodPrefix = 'test' - sortTestMethodsUsing = cmp - suiteClass = TestSuite - - def loadTestsFromTestCase(self, testCaseClass): - """Return a suite of all tests cases contained in testCaseClass""" - return self.suiteClass(tests=map(testCaseClass, - self.getTestCaseNames(testCaseClass)), - suiteName=testCaseClass.__name__) - - def loadTestsFromModule(self, module): - """Return a suite of all tests cases contained in the given module""" - tests = [] - for name in dir(module): - obj = getattr(module, name) - if type(obj) == types.ClassType and issubclass(obj, TestCase): - tests.append(self.loadTestsFromTestCase(obj)) - return self.suiteClass(tests) - - def loadTestsFromName(self, name, module=None): - """Return a suite of all tests cases given a string specifier. - - The name may resolve either to a module, a test case class, a - test method within a test case class, or a callable object which - returns a TestCase or TestSuite instance. - - The method optionally resolves the names relative to a given module. - """ - parts = string.split(name, '.') - if module is None: - if not parts: - raise ValueError, "incomplete test name: %s" % name - else: - parts_copy = parts[:] - while parts_copy: - try: - module = __import__(string.join(parts_copy,'.')) - break - except ImportError: - del parts_copy[-1] - if not parts_copy: raise - parts = parts[1:] - obj = module - for part in parts: - if isinstance(obj, TestSuite): - obj = obj.getTestForName(part) - else: - obj = getattr(obj, part) - - if type(obj) == types.ModuleType: - return self.loadTestsFromModule(obj) - elif type(obj) == types.ClassType and issubclass(obj, TestCase): - return self.loadTestsFromTestCase(obj) - elif type(obj) == types.UnboundMethodType: - return obj.im_class(obj.__name__) - elif isinstance(obj, TestSuite): - return obj - elif isinstance(obj, TestCase): - return obj - elif callable(obj): - test = obj() - if not isinstance(test, TestCase) and \ - not isinstance(test, TestSuite): - raise ValueError, \ - "calling %s returned %s, not a test" %(obj,test) - return test - else: - raise ValueError, "don't know how to make test from: %s" % obj - - def loadTestsFromNames(self, names, module=None): - """Return a suite of all tests cases found using the given sequence - of string specifiers. See 'loadTestsFromName()'. - """ - suites = [] - for name in names: - suites.append(self.loadTestsFromName(name, module)) - return self.suiteClass(suites) - - def getTestCaseNames(self, testCaseClass): - """Return a sorted sequence of method names found within testCaseClass. - """ - testFnNames = [fn for fn in dir(testCaseClass) if fn.startswith(self.testMethodPrefix)] - if hasattr(testCaseClass, 'runTest'): - testFnNames.append('runTest') - for baseclass in testCaseClass.__bases__: - for testFnName in self.getTestCaseNames(baseclass): - if testFnName not in testFnNames: # handle overridden methods - testFnNames.append(testFnName) - if self.sortTestMethodsUsing: - testFnNames.sort(self.sortTestMethodsUsing) - return testFnNames - - - -defaultTestLoader = TestLoader() - - -############################################################################## -# Patches for old functions: these functions should be considered obsolete -############################################################################## - -def _makeLoader(prefix, sortUsing, suiteClass=None): - loader = TestLoader() - loader.sortTestMethodsUsing = sortUsing - loader.testMethodPrefix = prefix - if suiteClass: loader.suiteClass = suiteClass - return loader - -def getTestCaseNames(testCaseClass, prefix, sortUsing=cmp): - return _makeLoader(prefix, sortUsing).getTestCaseNames(testCaseClass) - -def makeSuite(testCaseClass, prefix='test', sortUsing=cmp, suiteClass=TestSuite): - return _makeLoader(prefix, sortUsing, suiteClass).loadTestsFromTestCase(testCaseClass) - -def findTestCases(module, prefix='test', sortUsing=cmp, suiteClass=TestSuite): - return _makeLoader(prefix, sortUsing, suiteClass).loadTestsFromModule(module) - -############################################################################## -# Facilities for running tests from the command line -############################################################################## - -class TestProgram: - """A command-line program that runs a set of tests; this is primarily - for making test modules conveniently executable. - """ - USAGE = """\ -Usage: %(progName)s [options] [test] [...] - -Options: - -h, --help Show this message - -v, --verbose Verbose output - -q, --quiet Minimal output - -e, --expain Output extra test details if there is a failure or error - -Examples: - %(progName)s - run default set of tests - %(progName)s MyTestSuite - run suite 'MyTestSuite' - %(progName)s MyTestSuite.MyTestCase - run suite 'MyTestSuite' - %(progName)s MyTestCase.testSomething - run MyTestCase.testSomething - %(progName)s MyTestCase - run all 'test*' test methods - in MyTestCase -""" - def __init__(self, module='__main__', defaultTest=None, - argv=None, testRunner=None, testLoader=defaultTestLoader, - testSuite=None): - if type(module) == type(''): - self.module = __import__(module) - for part in string.split(module,'.')[1:]: - self.module = getattr(self.module, part) - else: - self.module = module - if argv is None: - argv = sys.argv - self.test = testSuite - self.verbosity = 1 - self.explain = 0 - self.defaultTest = defaultTest - self.testRunner = testRunner - self.testLoader = testLoader - self.progName = os.path.basename(argv[0]) - self.parseArgs(argv) - self.runTests() - - def usageExit(self, msg=None): - if msg: print msg - print self.USAGE % self.__dict__ - sys.exit(2) - - def parseArgs(self, argv): - import getopt - try: - options, args = getopt.getopt(argv[1:], 'hHvqer', - ['help','verbose','quiet','explain', 'raise']) - for opt, value in options: - if opt in ('-h','-H','--help'): - self.usageExit() - if opt in ('-q','--quiet'): - self.verbosity = 0 - if opt in ('-v','--verbose'): - self.verbosity = 2 - if opt in ('-e','--explain'): - self.explain = True - if len(args) == 0 and self.defaultTest is None and self.test is None: - self.test = self.testLoader.loadTestsFromModule(self.module) - return - if len(args) > 0: - self.testNames = args - else: - self.testNames = (self.defaultTest,) - self.createTests() - except getopt.error, msg: - self.usageExit(msg) - - def createTests(self): - if self.test == None: - self.test = self.testLoader.loadTestsFromNames(self.testNames, - self.module) - - def runTests(self): - if self.testRunner is None: - self.testRunner = TextTestRunner(verbosity=self.verbosity, - explain=self.explain) - result = self.testRunner.run(self.test) - self._cleanupAfterRunningTests() - sys.exit(not result.wasSuccessful()) - - def _cleanupAfterRunningTests(self): - """A hook method that is called immediately prior to calling - sys.exit(not result.wasSuccessful()) in self.runTests(). - """ - pass - -main = TestProgram - - -############################################################################## -# Executing this module from the command line -############################################################################## - -if __name__ == "__main__": - main(module=None) - -# vim: shiftwidth=4 tabstop=4 expandtab diff --git a/cheetah/Tests/xmlrunner.py b/cheetah/Tests/xmlrunner.py index dc49c56..36b5d8d 100644 --- a/cheetah/Tests/xmlrunner.py +++ b/cheetah/Tests/xmlrunner.py @@ -313,7 +313,7 @@ class XMLTestRunnerTest(unittest.TestCase): """ class TestTest(unittest.TestCase): def test_foo(self): - print "Test" + 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 @@ -329,7 +329,7 @@ class XMLTestRunnerTest(unittest.TestCase): """ class TestTest(unittest.TestCase): def test_foo(self): - print >>sys.stderr, "Test" + 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> diff --git a/cheetah/Tools/CGITemplate.py b/cheetah/Tools/CGITemplate.py index c94ddc4..1349b5b 100644 --- a/cheetah/Tools/CGITemplate.py +++ b/cheetah/Tools/CGITemplate.py @@ -68,7 +68,7 @@ class CGITemplate(Template): def isCgi(self): """Is this a CGI script? """ - env = os.environ.has_key('REQUEST_METHOD') + env = 'REQUEST_METHOD' in os.environ wk = self._CHEETAH__isControlledByWebKit return env and not wk diff --git a/cheetah/Tools/MondoReport.py b/cheetah/Tools/MondoReport.py index d0fada2..de4a8b5 100644 --- a/cheetah/Tools/MondoReport.py +++ b/cheetah/Tools/MondoReport.py @@ -12,7 +12,14 @@ next.query. How about Report: .page(), .all(), .summary()? Or PageBreaker. """ -import operator, types +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: @@ -22,18 +29,13 @@ except ImportError: else: return obj[name] # Raises KeyError. -########## CONSTANTS ############################## - -True, False = (1==1), (1==0) -numericTypes = types.IntType, types.LongType, types.FloatType - ########## PUBLIC GENERIC FUNCTIONS ############################## class NegativeError(ValueError): pass def isNumeric(v): - return type(v) in numericTypes + return isinstance(v, (int, float)) def isNonNegative(v): ret = isNumeric(v) @@ -91,8 +93,7 @@ def mean(lis): return total / lis_len def median(lis): - lis = lis[:] - lis.sort() + lis = sorted(lis[:]) return lis[int(len(lis)/2)] @@ -169,7 +170,7 @@ class ValuesGetterMixin: else: ret = self._origList if criteria: - ret = filter(criteria, ret) + ret = list(filter(criteria, ret)) return ret @@ -271,7 +272,7 @@ class RecordStats(IndexFormats, ValuesGetterMixin): - def _prevNextHelper(self, start,end,size,orphan,sequence): + def _prevNextHelper(self, start, end, size, orphan, sequence): """Copied from Zope's DT_InSV.py's "opt" function. """ if size < 1: @@ -304,7 +305,7 @@ class RecordStats(IndexFormats, ValuesGetterMixin): try: sequence[end+orphan-1] except: end=len(sequence) # if l - end < orphan: end=l - return start,end,size + return start, end, size diff --git a/cheetah/Tools/SiteHierarchy.py b/cheetah/Tools/SiteHierarchy.py index 06da56f..dece01e 100644 --- a/cheetah/Tools/SiteHierarchy.py +++ b/cheetah/Tools/SiteHierarchy.py @@ -12,171 +12,155 @@ 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. - -Meta-Data -================================================================================ -Author: Ian Bicking <ianb@colorstudy.com> -Version: $Revision: 1.1 $ -Start Date: 2001/07/23 -Last Revision Date: $Date: 2001/10/11 03:25:54 $ """ -__author__ = "Ian Bicking <ianb@colorstudy.com>" -__version__ = "$Revision: 1.1 $"[11:-2] ################################################## ## DEPENDENCIES import string try: - from cStringIO import StringIO + from cStringIO import StringIO except ImportError: - from StringIO import StringIO - - -################################################## -## GLOBALS & CONSTANTS - -True, False = (1==1), (0==1) + 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 1: - ## 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 type(item) is type(()): - 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' % (' '*2*indent, - self._menuCSSClass, text) - else: - return '%s<A HREF="%s%s"%s>%s</A> <BR>\n' % \ - (' '*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 ' > ' - - def crumbTerminator(self): - return '' - - def emptyCrumb(self): - """When you are at the homepage""" - return '' - - ## internal methods - - def _menubarRecurse(self, contents, indent, stream): - if type(contents) is type(()): - 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 type(contents) is type(()): - return self._currentURL == contents[0] - for item in contents: - if self._inContents(item): - return True - return False - + 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' % (' '*2*indent, + self._menuCSSClass, text) + else: + return '%s<A HREF="%s%s"%s>%s</A> <BR>\n' % \ + (' '*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 ' > ' + + 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() + 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()) diff --git a/cheetah/Tools/turbocheetah/cheetahsupport.py b/cheetah/Tools/turbocheetah/cheetahsupport.py index 682206f..1a70286 100644 --- a/cheetah/Tools/turbocheetah/cheetahsupport.py +++ b/cheetah/Tools/turbocheetah/cheetahsupport.py @@ -11,7 +11,7 @@ def _recompile_template(package, basename, tfile, classname): code = str(c) mod = imp.new_module(classname) ns = dict() - exec code in ns + exec(code, ns) tempclass = ns.get("GenTemplate", ns.get('DynamicallyCompiledCheetahTemplate')) assert tempclass @@ -38,7 +38,7 @@ class TurboCheetah: Template files must end in ".tmpl" and be in legitimate packages. """ - given = len(filter(None, (template, template_string, template_file))) + 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 " @@ -63,14 +63,14 @@ class TurboCheetah: package = classname[0:divider] basename = classname[divider+1:] else: - raise ValueError, "All templates must be in a package" + 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 ct.has_key(classname): + if classname in ct: mtime = os.stat(tfile).st_mtime if ct[classname] != mtime: ct[classname] = mtime diff --git a/cheetah/Utils/Misc.py b/cheetah/Utils/Misc.py index 6ff5bb2..81949d1 100644 --- a/cheetah/Utils/Misc.py +++ b/cheetah/Utils/Misc.py @@ -1,20 +1,8 @@ -# $Id: Misc.py,v 1.8 2005/11/02 22:26:08 tavis_rudd Exp $ -"""Miscellaneous functions/objects used by Cheetah but also useful standalone. - -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.8 $ -Start Date: 2001/11/07 -Last Revision Date: $Date: 2005/11/02 22:26:08 $ +#!/usr/bin/env python +""" + Miscellaneous functions/objects used by Cheetah but also useful standalone. """ -__author__ = "Mike Orr <iron@mso.oz.net>" -__revision__ = "$Revision: 1.8 $"[11:-2] - import os # Used in mkdirsWithPyInitFile. -import types # Used in useOrRaise. import sys # Used in die. ################################################## @@ -29,7 +17,7 @@ def useOrRaise(thing, errmsg=''): Called by: Cheetah.Servlet.cgiImport() """ - if type(thing) == types.ClassType and issubclass(thing, Exception): + if isinstance(thing, type) and issubclass(thing, Exception): raise thing(errmsg) return thing @@ -76,6 +64,4 @@ def mkdirsWithPyInitFiles(path): f = open(init, 'w') # Open and close to produce empty file. f.close() - - # vim: shiftwidth=4 tabstop=4 expandtab diff --git a/cheetah/Utils/VerifyType.py b/cheetah/Utils/VerifyType.py deleted file mode 100644 index 11a435d..0000000 --- a/cheetah/Utils/VerifyType.py +++ /dev/null @@ -1,83 +0,0 @@ -# $Id: VerifyType.py,v 1.4 2005/11/02 22:26:08 tavis_rudd Exp $ -"""Functions to verify an argument's type - -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.4 $ -Start Date: 2001/11/07 -Last Revision Date: $Date: 2005/11/02 22:26:08 $ -""" -__author__ = "Mike Orr <iron@mso.oz.net>" -__revision__ = "$Revision: 1.4 $"[11:-2] - -################################################## -## DEPENDENCIES - -import types # Used in VerifyTypeClass. - -################################################## -## PRIVATE FUNCTIONS - -def _errmsg(argname, ltd, errmsgExtra=''): - """Construct an error message. - - argname, string, the argument name. - ltd, string, description of the legal types. - errmsgExtra, string, text to append to error mssage. - Returns: string, the error message. - """ - if errmsgExtra: - errmsgExtra = '\n' + errmsgExtra - return "arg '%s' must be %s%s" % (argname, ltd, errmsgExtra) - - -################################################## -## TYPE VERIFICATION FUNCTIONS - -def VerifyType(arg, argname, legalTypes, ltd, errmsgExtra=''): - """Verify the type of an argument. - - arg, any, the argument. - argname, string, name of the argument. - legalTypes, list of type objects, the allowed types. - ltd, string, description of legal types (for error message). - errmsgExtra, string, text to append to error message. - Returns: None. - Exceptions: TypeError if 'arg' is the wrong type. - """ - if type(arg) not in legalTypes: - m = _errmsg(argname, ltd, errmsgExtra) - raise TypeError(m) - return True - - -def VerifyTypeClass(arg, argname, legalTypes, ltd, klass, errmsgExtra=''): - """Same, but if it's a class, verify it's a subclass of the right class. - - arg, any, the argument. - argname, string, name of the argument. - legalTypes, list of type objects, the allowed types. - ltd, string, description of legal types (for error message). - klass, class, the parent class. - errmsgExtra, string, text to append to the error message. - Returns: None. - Exceptions: TypeError if 'arg' is the wrong type. - """ - VerifyType(arg, argname, legalTypes, ltd, errmsgExtra) - # If no exception, the arg is a legal type. - if type(arg) == types.ClassType and not issubclass(arg, klass): - # Must test for "is class type" to avoid TypeError from issubclass(). - m = _errmsg(argname, ltd, errmsgExtra) - raise TypeError(m) - return True - -# @@MO: Commented until we determine whether it's useful. -#def VerifyClass(arg, argname, klass, ltd): -# """Same, but allow *only* a subclass of the right class. -# """ -# VerifyTypeClass(arg, argname, [types.ClassType], ltd, klass) - -# vim: shiftwidth=4 tabstop=4 expandtab diff --git a/cheetah/Utils/memcache.py b/cheetah/Utils/memcache.py index ee9678d..f10324d 100644 --- a/cheetah/Utils/memcache.py +++ b/cheetah/Utils/memcache.py @@ -45,7 +45,6 @@ More detailed documentation is available in the L{Client} class. import sys import socket import time -import types try: import cPickle as pickle except ImportError: @@ -129,7 +128,7 @@ class Client: serverData = {} data.append(( name, serverData )) readline = s.readline - while 1: + while True: line = readline() if not line or line.strip() == 'END': break stats = line.split(' ', 2) @@ -149,7 +148,7 @@ class Client: sys.stderr.write("MemCached: %s\n" % str) def _statlog(self, func): - if not self.stats.has_key(func): + if func not in self.stats: self.stats[func] = 1 else: self.stats[func] += 1 @@ -168,7 +167,7 @@ class Client: self.buckets.append(server) def _get_server(self, key): - if type(key) == types.TupleType: + if isinstance(key, tuple): serverhash = key[0] key = key[1] else: @@ -177,7 +176,6 @@ class Client: for i in range(Client._SERVER_RETRIES): server = self.buckets[serverhash % len(self.buckets)] if server.connect(): - #print "(using server %s)" % server, return server, key serverhash = hash(str(serverhash) + str(i)) return None, None @@ -302,7 +300,7 @@ class Client: self._statlog(cmd) flags = 0 - if isinstance(val, types.StringTypes): + if isinstance(val, str): pass elif isinstance(val, int): flags |= Client._FLAG_INTEGER @@ -344,7 +342,7 @@ class Client: value = self._recv_value(server, flags, rlen) server.expect("END") except (_Error, socket.error), msg: - if type(msg) is types.TupleType: + if isinstance(msg, tuple): msg = msg[1] server.mark_dead(msg) return None @@ -378,7 +376,7 @@ class Client: server, key = self._get_server(key) if not server: continue - if not server_keys.has_key(server): + if server not in server_keys: server_keys[server] = [] server_keys[server].append(key) @@ -452,7 +450,7 @@ class _Host: _DEAD_RETRY = 30 # number of seconds before retrying a dead server. def __init__(self, host, debugfunc=None): - if isinstance(host, types.TupleType): + if isinstance(host, tuple): host = host[0] self.weight = host[1] else: @@ -517,7 +515,7 @@ class _Host: def readline(self): buffers = '' recv = self.socket.recv - while 1: + while True: data = recv(1) if not data: self.mark_dead('Connection closed while reading from %s' @@ -555,27 +553,26 @@ def _doctest(): return doctest.testmod(memcache, globs=globs) if __name__ == "__main__": - print "Testing docstrings..." + print("Testing docstrings...") _doctest() - print "Running tests:" - print + print("Running tests:") #servers = ["127.0.0.1:11211", "127.0.0.1:11212"] servers = ["127.0.0.1:11211"] mc = Client(servers, debug=1) def to_s(val): - if not isinstance(val, types.StringTypes): + if not isinstance(val, str): return "%s (%s)" % (val, type(val)) return "%s" % val def test_setget(key, val): - print "Testing set/get {'%s': %s} ..." % (to_s(key), to_s(val)), + print("Testing set/get {'%s': %s} ..." % (to_s(key), to_s(val))) mc.set(key, val) newval = mc.get(key) if newval == val: - print "OK" + print("OK") return 1 else: - print "FAIL" + print("FAIL") return 0 class FooStruct: @@ -591,34 +588,33 @@ if __name__ == "__main__": test_setget("a_string", "some random string") test_setget("an_integer", 42) if test_setget("long", long(1<<30)): - print "Testing delete ...", + print("Testing delete ...") if mc.delete("long"): - print "OK" + print("OK") else: - print "FAIL" - print "Testing get_multi ...", - print mc.get_multi(["a_string", "an_integer"]) + print("FAIL") + print("Testing get_multi ...") + print(mc.get_multi(["a_string", "an_integer"])) - print "Testing get(unknown value) ...", - print to_s(mc.get("unknown_value")) + print("Testing get(unknown value) ...") + print(to_s(mc.get("unknown_value"))) f = FooStruct() test_setget("foostruct", f) - print "Testing incr ...", + print("Testing incr ...") x = mc.incr("an_integer", 1) if x == 43: - print "OK" + print("OK") else: - print "FAIL" + print("FAIL") - print "Testing decr ...", + print("Testing decr ...") x = mc.decr("an_integer", 1) if x == 42: - print "OK" + print("OK") else: - print "FAIL" - + print("FAIL") # vim: ts=4 sw=4 et : diff --git a/cheetah/Utils/statprof.py b/cheetah/Utils/statprof.py index 55638eb..0431628 100644 --- a/cheetah/Utils/statprof.py +++ b/cheetah/Utils/statprof.py @@ -120,7 +120,7 @@ much as possible. """ -from __future__ import division + try: import itimer @@ -277,15 +277,15 @@ class CallStats(object): self.cum_secs_per_call = None def display(self): - print '%6.2f %9.2f %9.2f %s' % (self.pcnt_time_in_proc, + 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) + self.name)) def display(): if state.sample_count == 0: - print 'No samples recorded.' + print('No samples recorded.') return l = [CallStats(x) for x in call_data.itervalues()] @@ -293,12 +293,12 @@ def display(): 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") + 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 + print('---') + print('Sample count: %d' % state.sample_count) + print('Total time: %f seconds' % state.accumulated_time) diff --git a/cheetah/Version.py b/cheetah/Version.py index 7fdf82f..69afbb0 100644 --- a/cheetah/Version.py +++ b/cheetah/Version.py @@ -2,11 +2,11 @@ Version = '2.4.0' VersionTuple = (2, 4, 0, 'final', 0) MinCompatibleVersion = '2.0rc6' -MinCompatibleVersionTuple = (2,0,0,'candidate',6) +MinCompatibleVersionTuple = (2, 0, 0, 'candidate', 6) #### def convertVersionStringToTuple(s): - versionNum = [0,0,0] + versionNum = [0, 0, 0] releaseType = 'final' releaseTypeSubNum = 0 if s.find('a')!=-1: @@ -27,16 +27,16 @@ def convertVersionStringToTuple(s): versionNum += [0] releaseTypeSubNum = int(releaseTypeSubNum) - return tuple(versionNum+[releaseType,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') + 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') diff --git a/cheetah/c/_verifytype.c b/cheetah/c/_verifytype.c deleted file mode 100644 index 6dadf22..0000000 --- a/cheetah/c/_verifytype.c +++ /dev/null @@ -1,107 +0,0 @@ -/* - * C-version of the src/Utils/VerifyType.py module. - * - * (c) 2009, R. Tyler Ballance <tyler@slide.com> - */ -#include <Python.h> -#if __STDC_VERSION__ >= 199901L -#include <stdbool.h> -#else -typedef enum { false, true } bool; -#endif - -#include "Cheetah.h" - -#ifdef __cplusplus -extern "C" { -#endif - -static PyObject *_errorMessage(char *arg, char *legalTypes, char *extra) -{ - return PyString_FromFormat("Argument '%s' must be %s\n", arg, legalTypes); -} - -static PyObject *py_verifytype(PyObject *self, PyObject *args, PyObject *kwargs) -{ - PyObject *argument, *legalTypes; - char *arg_string, *types_string, *extra; - PyObject *iterator, *item; - bool rc = false; - char *kwlist[] = {"argument", "argument_name", "legalType", - "types_string", "errmsgExtra", NULL}; - - if (!PyArg_ParseTupleAndKeywords(args, kwargs, "OsOs|s", kwlist, &argument, - &arg_string, &legalTypes, &types_string, &extra)) - return NULL; - - iterator = PyObject_GetIter(legalTypes); - if (iterator == NULL) { - return NULL; - } - - while (item = PyIter_Next(iterator)) { - if ((PyObject *)argument->ob_type == item) { - rc = true; - Py_DECREF(item); - break; - } - Py_DECREF(item); - } - Py_DECREF(iterator); - - if (rc) - Py_RETURN_TRUE; - - PyErr_SetObject(PyExc_TypeError, _errorMessage(arg_string, - types_string, extra)); - return NULL; -} - -static PyObject *py_verifytypeclass(PyObject *self, PyObject *args, PyObject *kwargs) -{ - PyObject *argument, *legalTypes, *klass; - PyObject *verifyTypeArgs, *v; - char *arg_string, *types_string, *extra; - bool rc = false; - - char *kwlist[] = {"argument", "argument_name", "legalTypes", - "types_string", "klass", "errmsgExtra", NULL}; - - if (!PyArg_ParseTupleAndKeywords(args, kwargs, "OsOsO|s", kwlist, &argument, - &arg_string, &legalTypes, &types_string, &klass, &extra)) - return NULL; - - verifyTypeArgs = Py_BuildValue("OsOs", argument, arg_string, legalTypes, - types_string); - v = py_verifytype(self, verifyTypeArgs, NULL); - - if (v == NULL) - return NULL; - Py_DECREF(v); - - if (PyClass_Check(argument) && (!PyClass_IsSubclass(argument, klass)) ) { - PyErr_SetObject(PyExc_TypeError, _errorMessage(arg_string, - types_string, extra)); - return NULL; - } - Py_RETURN_TRUE; -} - -static const char _verifytypedoc[] = "\ -\n\ -"; -static struct PyMethodDef _verifytype_methods[] = { - {"verifyType", (PyCFunction)py_verifytype, METH_VARARGS | METH_KEYWORDS, NULL}, - {"verifyTypeClass", (PyCFunction)py_verifytypeclass, METH_VARARGS | METH_KEYWORDS, NULL}, - {NULL} -}; - -PyMODINIT_FUNC init_verifytype() -{ - PyObject *module = Py_InitModule3("_verifytype", _verifytype_methods, - _verifytypedoc); -} - -#ifdef __cplusplus -} -#endif |