Skip to content
This repository has been archived by the owner on Oct 11, 2022. It is now read-only.

Commit

Permalink
Browse files Browse the repository at this point in the history
Merge branch 'next'
Conflicts:
	cheetah/Template.py
	cheetah/Tests/Performance.py
	cheetah/Version.py
  • Loading branch information
R. Tyler Ballance committed Oct 15, 2009
2 parents 32fffac + 8237cf4 commit da10b2f
Show file tree
Hide file tree
Showing 7 changed files with 125 additions and 83 deletions.
10 changes: 10 additions & 0 deletions CHANGES
@@ -1,4 +1,14 @@

2.4.0 (October 15th, 2009)
- Fix a major performance regression in Template.__init__()
- More graceful handling of unicode when calling .respond() to render a template
- Minor code updates

2.3.0 (October 15th, 2009) (loosely equivalent to 2.4.0)
- Fix a major performance regression in Template.__init__()
- More graceful handling of unicode when calling .respond() to render a template
- Minor code updates

2.2.2 (September 10th, 2009)
- Prevent _namemapper.c from segfaulting when PyImport_ImportModule fails for some reason (Bogdano Arendartchuk <debogdano@gmail.com>)
- Removal of the contrib/markdown module (in favor of a setuptools dependency)
Expand Down
37 changes: 25 additions & 12 deletions cheetah/DummyTransaction.py
Expand Up @@ -8,6 +8,7 @@
specific DummyTransaction or DummyResponse behavior
'''

import logging
import types

class DummyResponseFailure(Exception):
Expand All @@ -24,7 +25,25 @@ def __init__(self):

def flush(self):
pass


def safeConvert(self, chunk):
# Exceptionally gross, but the safest way
# I've found to ensure I get a legit unicode object
if not chunk:
return u''
if isinstance(chunk, unicode):
return chunk
try:
return chunk.decode('utf-8', 'strict')
except UnicodeDecodeError:
try:
return chunk.decode('latin-1', 'strict')
except UnicodeDecodeError:
return chunk.decode('ascii', 'ignore')
except AttributeError:
return unicode(chunk, errors='ignore')
return chunk

def write(self, value):
self._outputChunks.append(value)

Expand All @@ -34,18 +53,12 @@ def writeln(self, txt):

def getvalue(self, outputChunks=None):
chunks = outputChunks or self._outputChunks
try:
return ''.join(chunks)
try:
return u''.join(chunks)
except UnicodeDecodeError, ex:
nonunicode = [c for c in chunks if not isinstance(c, unicode)]
raise DummyResponseFailure('''Looks like you're trying to mix encoded strings with Unicode strings
(most likely utf-8 encoded ones)
This can happen if you're using the `EncodeUnicode` filter, or if you're manually
encoding strings as utf-8 before passing them in on the searchList (possible offenders:
%s)
(%s)''' % (nonunicode, ex))

logging.debug('Trying to work around a UnicodeDecodeError in getvalue()')
logging.debug('...perhaps you could fix "%s" while you\'re debugging')
return ''.join((self.safeConvert(c) for c in chunks))

def writelines(self, *lines):
## not used
Expand Down
106 changes: 47 additions & 59 deletions cheetah/Template.py
Expand Up @@ -95,6 +95,7 @@ def hashDict(d):
hashedList.append((k,v))
return hash(tuple(hashedList))


################################################################################
## MODULE GLOBALS AND CONSTANTS

Expand Down Expand Up @@ -579,45 +580,38 @@ def __str__(self): return self.respond()
preprocessors=[ dict(tokens='@ %', searchList=[...]) ] )
"""
##################################################
## normalize and validate args
N = types.NoneType; S = types.StringType; U = types.UnicodeType
D = types.DictType; F = types.FileType
C = types.ClassType; M = types.ModuleType
I = types.IntType; B = types.BooleanType
errmsg = "arg '%s' must be %s"

t = type(source)
if not (t is N or t is S or t is U):
if not isinstance(source, (types.NoneType, basestring)):
raise TypeError(errmsg % ('source', 'string or None'))
t = type(file)
if not (t is N or t is S or t is U or t is F):

if not isinstance(file, (types.NoneType, basestring, types.FileType)):
raise TypeError(errmsg %
('file', 'string, file-like object, or None'))

if baseclass is Unspecified:
baseclass = klass._CHEETAH_defaultBaseclassForTemplates
if isinstance(baseclass, Template):
baseclass = baseclass.__class__
t = type(baseclass)
if not (t is N or t is S or t is C or t is type):

if not isinstance(baseclass, (types.NoneType, basestring, types.ClassType, types.TypeType)):
raise TypeError(errmsg % ('baseclass', 'string, class or None'))

if cacheCompilationResults is Unspecified:
cacheCompilationResults = klass._CHEETAH_cacheCompilationResults
t = type(cacheCompilationResults)
if not (t is I or t is B):

if not isinstance(cacheCompilationResults, (int, bool)):
raise TypeError(errmsg % ('cacheCompilationResults', 'boolean'))

if useCache is Unspecified:
useCache = klass._CHEETAH_useCompilationCache
t = type(useCache)
if not (t is I or t is B):

if not isinstance(useCache, (int, bool)):
raise TypeError(errmsg % ('useCache', 'boolean'))

if compilerSettings is Unspecified:
compilerSettings = klass._getCompilerSettings(source, file) or {}
if type(compilerSettings) is not D:
if not isinstance(compilerSettings, dict):
raise TypeError(errmsg % ('compilerSettings', 'dictionary'))

if compilerClass is Unspecified:
Expand All @@ -627,48 +621,47 @@ def __str__(self): return self.respond()

if keepRefToGeneratedCode is Unspecified:
keepRefToGeneratedCode = klass._CHEETAH_keepRefToGeneratedCode
t = type(keepRefToGeneratedCode)
if not (t is I or t is B):

if not isinstance(keepRefToGeneratedCode, (int, bool)):
raise TypeError(errmsg % ('keepReftoGeneratedCode', 'boolean'))

t = type(moduleName)
if not (t is N or t is S):
if not isinstance(moduleName, (types.NoneType, basestring)):
raise TypeError(errmsg % ('moduleName', 'string or None'))
__orig_file__ = None
if not moduleName:
if file and type(file) in StringTypes:
if file and isinstance(file, basestring):
moduleName = convertTmplPathToModuleName(file)
__orig_file__ = file
else:
moduleName = klass._CHEETAH_defaultModuleNameForTemplates

if className is Unspecified:
className = klass._CHEETAH_defaultClassNameForTemplates
t = type(className)
if not (t is N or t is S):

if not isinstance(className, (types.NoneType, basestring)):
raise TypeError(errmsg % ('className', 'string or None'))
className = className or moduleName

if mainMethodName is Unspecified:
mainMethodName = klass._CHEETAH_defaultMainMethodNameForTemplates
t = type(mainMethodName)
if not (t is N or t is S):

if not isinstance(mainMethodName, (types.NoneType, basestring)):
raise TypeError(errmsg % ('mainMethodName', 'string or None'))

if moduleGlobals is Unspecified:
moduleGlobals = klass._CHEETAH_defaultModuleGlobalsForTemplates

if cacheModuleFilesForTracebacks is Unspecified:
cacheModuleFilesForTracebacks = klass._CHEETAH_cacheModuleFilesForTracebacks
t = type(cacheModuleFilesForTracebacks)
if not (t is I or t is B):

if not isinstance(cacheModuleFilesForTracebacks, (int, bool)):
raise TypeError(errmsg %
('cacheModuleFilesForTracebacks', 'boolean'))

if cacheDirForModuleFiles is Unspecified:
cacheDirForModuleFiles = klass._CHEETAH_cacheDirForModuleFiles
t = type(cacheDirForModuleFiles)
if not (t is N or t is S):

if not isinstance(cacheDirForModuleFiles, (types.NoneType, basestring)):
raise TypeError(errmsg %
('cacheDirForModuleFiles', 'string or None'))

Expand All @@ -683,9 +676,9 @@ def __str__(self): return self.respond()
baseclassValue = None
baseclassName = None
if baseclass:
if type(baseclass) in StringTypes:
if isinstance(baseclass, basestring):
baseclassName = baseclass
elif type(baseclass) in (ClassType, type):
elif isinstance(baseclass, (types.ClassType, types.TypeType)):
# @@TR: should soft-code this
baseclassName = 'CHEETAH_dynamicallyAssignedBaseClass_'+baseclass.__name__
baseclassValue = baseclass
Expand Down Expand Up @@ -1142,46 +1135,41 @@ def __init__(self, source=None,
Do NOT mess with the args _globalSetVars or _preBuiltSearchList!
"""

##################################################
## Verify argument keywords and types
S = types.StringType; U = types.UnicodeType
L = types.ListType; T = types.TupleType
D = types.DictType; F = types.FileType
C = types.ClassType; M = types.ModuleType
N = types.NoneType
"""
errmsg = "arg '%s' must be %s"
errmsgextra = errmsg + "\n%s"

t = type(source)
if not (t is N or t is S or t is U):
if not isinstance(source, (types.NoneType, basestring)):
raise TypeError(errmsg % ('source', 'string or None'))
t = type(file)
if not (t is N or t is S or t is U or t is F):

if not isinstance(source, (types.NoneType, basestring, types.FileType)):
raise TypeError(errmsg %
('file', 'string, file open for reading, or None'))
t = type(filter)
if not (t is S or (t is C and issubclass(filter, Filters.Filter)) or
t is type):

if not isinstance(filter, (basestring, types.TypeType)) and not \
(isinstance(filter, types.ClassType) and issubclass(filter, Filters.Filter)):
raise TypeError(errmsgextra %
('filter', 'string or class',
'(if class, must be subclass of Cheetah.Filters.Filter)'))
t = type(filtersLib)
if not (t is S or t is M):
if not isinstance(filtersLib, (basestring, types.ModuleType)):
raise TypeError(errmsgextra %
('filtersLib', 'string or module',
'(if module, must contain subclasses of Cheetah.Filters.Filter)'))
t = type(errorCatcher)
if not (t is N or t is S or
(t is C and issubclass(errorCatcher, ErrorCatchers.ErrorCatcher)) or
t is type):
raise TypeError(errmsgextra %

if not errorCatcher is None:
err = True
if isinstance(errorCatcher, (basestring, types.TypeType)):
err = False
if isinstance(errorCatcher, types.ClassType) and \
issubclass(errorCatcher, ErrorCatchers.ErrorCatcher):
err = False
if err:
raise TypeError(errmsgextra %
('errorCatcher', 'string, class or None',
'(if class, must be subclass of Cheetah.ErrorCatchers.ErrorCatcher)'))
if compilerSettings is not Unspecified:
if type(compilerSettings) is not D:
if not isinstance(compilerSettings, types.DictType):
raise TypeError(errmsg %
('compilerSettings', 'dictionary'))

Expand Down Expand Up @@ -1216,7 +1204,7 @@ def __init__(self, source=None,
if searchList:
for namespace in searchList:
if isinstance(namespace, dict):
intersection = Reserved_SearchList & set(namespace.keys())
intersection = self.Reserved_SearchList & set(namespace.keys())
warn = False
if intersection:
warn = True
Expand Down Expand Up @@ -1525,7 +1513,7 @@ def _compile(self, source=None, file=None, compilerSettings=Unspecified,
self._fileMtime = None
self._fileDirName = None
self._fileBaseName = None
if file and type(file) in StringTypes:
if file and isinstance(file, basestring):
file = self.serverSidePath(file)
self._fileMtime = os.path.getmtime(file)
self._fileDirName, self._fileBaseName = os.path.split(file)
Expand Down Expand Up @@ -1831,7 +1819,7 @@ def webInput(self, names, namesMulti=(), default='', src='f',
return dic

T = Template # Short and sweet for debugging at the >>> prompt.
Reserved_SearchList = set(dir(Template))
Template.Reserved_SearchList = set(dir(Template))

def genParserErrorFromPythonException(source, file, generatedPyCode, exception):

Expand Down
15 changes: 13 additions & 2 deletions cheetah/Tests/CheetahWrapper.py
Expand Up @@ -12,7 +12,6 @@
Show the output of each subcommand. (Normally suppressed.)
'''
import os
import popen2
import re # Used by listTests.
import shutil
import sys
Expand All @@ -22,6 +21,18 @@
from optparse import OptionParser
from Cheetah.CheetahWrapper import CheetahWrapper # Used by NoBackup.

try:
from subprocess import Popen, PIPE, STDOUT
class Popen4(Popen):
def __init__(self, cmd, bufsize=-1):
super(Popen4, self).__init__(cmd, bufsize=bufsize,
shell=True, close_fds=True,
stdin=PIPE, stdout=PIPE, stderr=STDOUT)
self.tochild = self.stdin
self.fromchild = self.stdout
self.childerr = self.stderr
except ImportError:
from popen2 import Popen4

DELETE = True # True to clean up after ourselves, False for debugging.
OUTPUT = False # Normally False, True for debugging.
Expand Down Expand Up @@ -152,7 +163,7 @@ def assertWin32Subprocess(self, cmd):
return rc, output

def assertPosixSubprocess(self, cmd):
process = popen2.Popen4(cmd)
process = Popen4(cmd)
process.tochild.close()
output = process.fromchild.read()
status = process.wait()
Expand Down

0 comments on commit da10b2f

Please sign in to comment.