diff options
-rw-r--r-- | SetupConfig.py | 28 | ||||
-rw-r--r-- | bin/.gitattributes | 1 | ||||
-rw-r--r-- | bin/cheetah.bat | 1 | ||||
-rw-r--r-- | cheetah/Filters.py | 29 | ||||
-rw-r--r-- | cheetah/Template.py | 99 | ||||
-rwxr-xr-x | cheetah/Tests/Test.py | 4 | ||||
-rw-r--r-- | cheetah/Utils/Indenter.py | 35 | ||||
-rw-r--r-- | cheetah/Utils/VerifyType.py | 2 | ||||
-rw-r--r-- | cheetah/c/Cheetah.h | 47 | ||||
-rw-r--r-- | cheetah/c/_filters.c | 93 | ||||
-rw-r--r-- | cheetah/c/_namemapper.c (renamed from cheetah/_namemapper.c) | 0 | ||||
-rw-r--r-- | cheetah/c/_template.c | 52 | ||||
-rw-r--r-- | cheetah/c/_verifytype.c | 107 | ||||
-rw-r--r-- | src/Tests/Performance.py | 220 | ||||
-rw-r--r-- | src/Tests/VerifyType.py | 157 |
15 files changed, 771 insertions, 104 deletions
diff --git a/SetupConfig.py b/SetupConfig.py index d974418..bcf5ca3 100644 --- a/SetupConfig.py +++ b/SetupConfig.py @@ -33,24 +33,30 @@ package_dir = {'Cheetah':'cheetah'} import os import os.path +import sys from distutils.core import Extension -## we only assume the presence of a c compiler on Posix systems, NT people will -# have to enable this manually. -if os.name == 'posix': - ext_modules=[Extension("Cheetah._namemapper", [os.path.join("cheetah" ,"_namemapper.c")] - ) - ] -else: - ext_modules=[] - +ext_modules=[ + Extension("Cheetah._namemapper", + [os.path.join('cheetah', 'c', '_namemapper.c')]), + Extension("Cheetah._verifytype", + [os.path.join('cheetah', 'c', '_verifytype.c')]), + Extension("Cheetah._filters", + [os.path.join('cheetah', 'c', '_filters.c')]), + Extension('Cheetah._template', + [os.path.join('cheetah', 'c', '_template.c')]), + ] ## Data Files and Scripts scripts = ['bin/cheetah-compile', 'bin/cheetah', ] -data_files = ['recursive: cheetah *.tmpl *.txt LICENSE README TODO CHANGES', - ] + +if sys.platform == "win32": + scripts.append('bin/cheetah.bat') + +data_files = ['recursive: src *.tmpl *.txt LICENSE README TODO CHANGES',] + if not os.getenv('CHEETAH_INSTALL_WITHOUT_SETUPTOOLS'): try: from setuptools import setup diff --git a/bin/.gitattributes b/bin/.gitattributes new file mode 100644 index 0000000..5bebad2 --- /dev/null +++ b/bin/.gitattributes @@ -0,0 +1 @@ +cheetah.bat -crlf diff --git a/bin/cheetah.bat b/bin/cheetah.bat new file mode 100644 index 0000000..3f6cf2a --- /dev/null +++ b/bin/cheetah.bat @@ -0,0 +1 @@ +@"%~dp0..\python" "%~dpn0" %*
diff --git a/cheetah/Filters.py b/cheetah/Filters.py index 09ffa4e..dd65f28 100644 --- a/cheetah/Filters.py +++ b/cheetah/Filters.py @@ -22,30 +22,26 @@ class Filter(object): """ self.template = template - def filter(self, val, - #encoding='utf8', - encoding=None, - str=str, - **kw): - """Pass Unicode strings through unmolested, unless an encoding is specified. - """ + def filter(self, val, encoding=None, str=str, **kw): + ''' + Pass Unicode strings through unmolested, unless an encoding is specified. + ''' + if val is None: + return u'' if isinstance(val, unicode): if encoding: - filtered = val.encode(encoding) + return val.encode(encoding) else: - filtered = val - elif val is None: - filtered = '' + return val else: try: - filtered = str(val) + return str(val) except UnicodeEncodeError: - filtered = unicode(val) - return filtered + return unicode(val) + return u'' RawOrEncodedUnicode = Filter - class EncodeUnicode(Filter): def filter(self, val, encoding='utf8', @@ -231,6 +227,7 @@ def test(): print "Unicode:", `EncodeUnicode().filter(u'aoeu12345\u1234')` -if __name__ == "__main__": test() +if __name__ == "__main__": + test() # vim: shiftwidth=4 tabstop=4 expandtab diff --git a/cheetah/Template.py b/cheetah/Template.py index 4bf6799..eeeeb95 100644 --- a/cheetah/Template.py +++ b/cheetah/Template.py @@ -25,11 +25,6 @@ try: from types import StringTypes except ImportError: StringTypes = (types.StringType,types.UnicodeType) -try: - from types import BooleanType - boolTypeAvailable = True -except ImportError: - boolTypeAvailable = False try: from threading import Lock @@ -56,7 +51,14 @@ from Cheetah.Compiler import Compiler, DEFAULT_COMPILER_SETTINGS from Cheetah import ErrorCatchers # for placeholder tags from Cheetah import Filters # the output filters from Cheetah.convertTmplPathToModuleName import convertTmplPathToModuleName -from Cheetah.Utils import VerifyType # Used in Template.__init__ + +try: + from Cheetah._verifytype import * +except ImportError: + from Cheetah.Utils import VerifyType + verifyType = VerifyType.VerifyType + verifyTypeClass = VerifyType.VerifyTypeClass + from Cheetah.Utils.Misc import checkKeywords # Used in Template.__init__ from Cheetah.Utils.Indenter import Indenter # Used in Template.__init__ and for # placeholders @@ -117,11 +119,14 @@ def _genUniqueModuleName(baseModuleName): # This is only relavent to templates used as CGI scripts. _formUsedByWebInput = None -# used in Template.compile() -def valOrDefault(val, default): - if val is not Unspecified: - return val - return default +try: + from Cheetah._template import valOrDefault +except ImportError: + # used in Template.compile() + def valOrDefault(val, default): + if val is not Unspecified: + return val + return default def updateLinecache(filename, src): import linecache @@ -592,46 +597,43 @@ class Template(Servlet): ################################################## ## normalize and validate args try: - vt = VerifyType.VerifyType - vtc = VerifyType.VerifyTypeClass + vt = verifyType + vtc = verifyTypeClass N = types.NoneType; S = types.StringType; U = types.UnicodeType D = types.DictType; F = types.FileType C = types.ClassType; M = types.ModuleType - I = types.IntType + I = types.IntType; B = types.BooleanType - if boolTypeAvailable: - B = types.BooleanType - - vt(source, 'source', [N,S,U], 'string or None') - vt(file, 'file',[N,S,U,F], 'string, file-like object, or None') + IB = (I, B) + NS = (N, S) + + vt(source, 'source', (N,S,U), 'string or None') + vt(file, 'file',(N,S,U,F), 'string, file-like object, or None') baseclass = valOrDefault(baseclass, klass._CHEETAH_defaultBaseclassForTemplates) if isinstance(baseclass, Template): baseclass = baseclass.__class__ - vt(baseclass, 'baseclass', [N,S,C,type], 'string, class or None') + vt(baseclass, 'baseclass', (N,S,C,type), 'string, class or None') cacheCompilationResults = valOrDefault( cacheCompilationResults, klass._CHEETAH_cacheCompilationResults) - if boolTypeAvailable: - vt(cacheCompilationResults, 'cacheCompilationResults', [I,B], 'boolean') + vt(cacheCompilationResults, 'cacheCompilationResults', IB, 'boolean') useCache = valOrDefault(useCache, klass._CHEETAH_useCompilationCache) - if boolTypeAvailable: - vt(cacheCompilationResults, 'cacheCompilationResults', [I,B], 'boolean') + vt(useCache, 'useCache', IB, 'boolean') compilerSettings = valOrDefault( compilerSettings, klass._getCompilerSettings(source, file) or {}) - vt(compilerSettings, 'compilerSettings', [D], 'dictionary') + vt(compilerSettings, 'compilerSettings', (D,), 'dictionary') compilerClass = valOrDefault(compilerClass, klass._getCompilerClass(source, file)) preprocessors = valOrDefault(preprocessors, klass._CHEETAH_preprocessors) keepRefToGeneratedCode = valOrDefault( keepRefToGeneratedCode, klass._CHEETAH_keepRefToGeneratedCode) - if boolTypeAvailable: - vt(cacheCompilationResults, 'cacheCompilationResults', [I,B], 'boolean') - - vt(moduleName, 'moduleName', [N,S], 'string or None') + vt(keepRefToGeneratedCode, 'keepRefToGeneratedCode', IB, 'boolean') + + vt(moduleName, 'moduleName', NS, 'string or None') __orig_file__ = None if not moduleName: if file and type(file) in StringTypes: @@ -639,27 +641,26 @@ class Template(Servlet): __orig_file__ = file else: moduleName = klass._CHEETAH_defaultModuleNameForTemplates - + className = valOrDefault( className, klass._CHEETAH_defaultClassNameForTemplates) - vt(className, 'className', [N,S], 'string or None') + vt(className, 'className', NS, 'string or None') className = className or moduleName mainMethodName = valOrDefault( mainMethodName, klass._CHEETAH_defaultMainMethodNameForTemplates) - vt(mainMethodName, 'mainMethodName', [N,S], 'string or None') + vt(mainMethodName, 'mainMethodName', NS, 'string or None') moduleGlobals = valOrDefault( moduleGlobals, klass._CHEETAH_defaultModuleGlobalsForTemplates) cacheModuleFilesForTracebacks = valOrDefault( cacheModuleFilesForTracebacks, klass._CHEETAH_cacheModuleFilesForTracebacks) - if boolTypeAvailable: - vt(cacheModuleFilesForTracebacks, 'cacheModuleFilesForTracebacks', [I,B], 'boolean') - + vt(cacheModuleFilesForTracebacks, 'cacheModuleFilesForTracebacks', IB, 'boolean') + cacheDirForModuleFiles = valOrDefault( cacheDirForModuleFiles, klass._CHEETAH_cacheDirForModuleFiles) - vt(cacheDirForModuleFiles, 'cacheDirForModuleFiles', [N,S], 'string or None') + vt(cacheDirForModuleFiles, 'cacheDirForModuleFiles', NS, 'string or None') except TypeError, reason: raise TypeError(reason) @@ -1144,26 +1145,24 @@ class Template(Servlet): D = types.DictType; F = types.FileType C = types.ClassType; M = types.ModuleType N = types.NoneType - vt = VerifyType.VerifyType - vtc = VerifyType.VerifyTypeClass + vt = verifyType + vtc = verifyTypeClass try: - vt(source, 'source', [N,S,U], 'string or None') - vt(file, 'file', [N,S,U,F], 'string, file open for reading, or None') - vtc(filter, 'filter', [S,C,type], 'string or class', + vt(source, 'source', (N,S,U), 'string or None') + vt(file, 'file', (N,S,U,F), 'string, file open for reading, or None') + vtc(filter, 'filter', (S,C,type), 'string or class', Filters.Filter, '(if class, must be subclass of Cheetah.Filters.Filter)') - vt(filtersLib, 'filtersLib', [S,M], 'string or module', + vt(filtersLib, 'filtersLib', (S,M), 'string or module', '(if module, must contain subclasses of Cheetah.Filters.Filter)') - vtc(errorCatcher, 'errorCatcher', [N,S,C,type], 'string, class or None', + vtc(errorCatcher, 'errorCatcher', (N,S,C,type), 'string, class or None', ErrorCatchers.ErrorCatcher, '(if class, must be subclass of Cheetah.ErrorCatchers.ErrorCatcher)') if compilerSettings is not Unspecified: - vt(compilerSettings, 'compilerSettings', [D], 'dictionary') + vt(compilerSettings, 'compilerSettings', (D,), 'dictionary') - except TypeError, reason: - # Re-raise the exception here so that the traceback will end in - # this function rather than in some utility function. - raise TypeError(reason) + except TypeError: + raise if source is not None and file is not None: raise TypeError("you must supply either a source string or the" + @@ -1463,7 +1462,7 @@ class Template(Servlet): # @@TR: consider allowing simple callables as the filter argument self._CHEETAH__filtersLib = filtersLib self._CHEETAH__filters = {} - if type(filter) in StringTypes: + if isinstance(filter, basestring): filterName = filter klass = getattr(self._CHEETAH__filtersLib, filterName) else: @@ -1474,7 +1473,7 @@ class Template(Servlet): self._CHEETAH__errorCatchers = {} if errorCatcher: - if type(errorCatcher) in StringTypes: + if isinstance(errorCatcher, basestring): errorCatcherClass = getattr(ErrorCatchers, errorCatcher) elif type(errorCatcher) == ClassType: errorCatcherClass = errorCatcher diff --git a/cheetah/Tests/Test.py b/cheetah/Tests/Test.py index e168fc9..fe97ff4 100755 --- a/cheetah/Tests/Test.py +++ b/cheetah/Tests/Test.py @@ -41,7 +41,3 @@ if __name__ == '__main__': results = runner.run(unittest.TestSuite(suites)) - - - - diff --git a/cheetah/Utils/Indenter.py b/cheetah/Utils/Indenter.py index 91f50ed..52c142d 100644 --- a/cheetah/Utils/Indenter.py +++ b/cheetah/Utils/Indenter.py @@ -1,5 +1,5 @@ -# $Id: Indenter.py,v 1.7 2006/01/08 01:09:30 tavis_rudd Exp $ -"""Indentation maker. +""" +Indentation maker. @@TR: this code is unsupported and largely undocumented ... This version is based directly on code by Robert Kuzelj @@ -8,18 +8,7 @@ attributes have been renamed. Indentation is output via $self._CHEETAH__indenter.indent() to prevent '_indenter' being looked up on the searchList and another one being found. The directive syntax will soon be changed somewhat. - -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.7 $ -Start Date: 2001/11/07 -Last Revision Date: $Date: 2006/01/08 01:09:30 $ """ -__author__ = "Mike Orr <iron@mso.oz.net>" -__revision__ = "$Revision: 1.7 $"[11:-2] import re import sys @@ -27,7 +16,7 @@ import sys def indentize(source): return IndentProcessor().process(source) -class IndentProcessor: +class IndentProcessor(object): """Preprocess #indent tags.""" LINE_SEP = '\n' ARGS = "args" @@ -88,15 +77,16 @@ class IndentProcessor: return self.LINE_SEP.join(result) -class Indenter: - """A class that keeps track of the current indentation level. +class Indenter(object): + """ + A class that keeps track of the current indentation level. .indent() returns the appropriate amount of indentation. """ - def __init__(self): - self.On = 1 - self.Level = 0 - self.Chars = " "*4 - self.LevelStack = [] + On = 1 + Level = 0 + Chars = ' ' + LevelStack = [] + def on(self): self.On = 1 def off(self): @@ -129,6 +119,5 @@ class Indenter: def indent(self, _default=0): if self.On: return self.Chars * self.Level - else: - return " " * _default + return " " * _default diff --git a/cheetah/Utils/VerifyType.py b/cheetah/Utils/VerifyType.py index 520148b..11a435d 100644 --- a/cheetah/Utils/VerifyType.py +++ b/cheetah/Utils/VerifyType.py @@ -51,6 +51,7 @@ def VerifyType(arg, argname, legalTypes, ltd, errmsgExtra=''): if type(arg) not in legalTypes: m = _errmsg(argname, ltd, errmsgExtra) raise TypeError(m) + return True def VerifyTypeClass(arg, argname, legalTypes, ltd, klass, errmsgExtra=''): @@ -71,6 +72,7 @@ def VerifyTypeClass(arg, argname, legalTypes, ltd, klass, errmsgExtra=''): # 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): diff --git a/cheetah/c/Cheetah.h b/cheetah/c/Cheetah.h new file mode 100644 index 0000000..d149c15 --- /dev/null +++ b/cheetah/c/Cheetah.h @@ -0,0 +1,47 @@ +/* + * (c) 2009, R. Tyler Ballance <tyler@slide.com> + */ + +#ifndef _CHEETAH_H_ +#define _CHEETAH_H_ + +#include <Python.h> + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * Python 2.3 compatibility + */ +#ifndef Py_RETURN_TRUE +#define Py_RETURN_TRUE Py_INCREF(Py_True);\ + return Py_True +#endif +#ifndef Py_RETURN_FALSE +#define Py_RETURN_FALSE Py_INCREF(Py_False);\ + return Py_False +#endif +#ifndef Py_RETURN_NONE +#define Py_RETURN_NONE Py_INCREF(Py_None);\ + return Py_None +#endif + + +/* + * Filter Module + */ +typedef struct { + PyObject_HEAD + /* type specific fields */ +} PyFilter; + +/* + * End Filter Module + */ + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/cheetah/c/_filters.c b/cheetah/c/_filters.c new file mode 100644 index 0000000..c81b3bd --- /dev/null +++ b/cheetah/c/_filters.c @@ -0,0 +1,93 @@ +/* + * C-version of the src/Filters.py module + * + * (c) 2009, R. Tyler Ballance <tyler@slide.com> + */ +#include <Python.h> + +#include "Cheetah.h" + +#if __STDC_VERSION__ >= 199901L +#include <stdbool.h> +#else +typedef enum { false, true } bool; +#endif + +#ifdef __cplusplus +extern "C" { +#endif + + +static PyObject *py_filter(PyObject *self, PyObject *args, PyObject *kwargs) +{ + Py_RETURN_FALSE; +} + +static const char _filtersdoc[] = "\ +\n\ +"; +static struct PyMethodDef py_filtermethods[] = { + {"filter", (PyCFunction)(py_filter), METH_VARARGS | METH_KEYWORDS, + PyDoc_STR("Filter stuff")}, + {NULL}, +}; +static PyTypeObject PyFilterType = { + PyObject_HEAD_INIT(NULL) + 0, /*ob_size*/ + "_filters.Filter", /*tp_name*/ + sizeof(PyFilter), /*tp_basicsize*/ + 0, /*tp_itemsize*/ + 0, /*tp_dealloc*/ + 0, /*tp_print*/ + 0, /*tp_getattr*/ + 0, /*tp_setattr*/ + 0, /*tp_compare*/ + 0, /*tp_repr*/ + 0, /*tp_as_number*/ + 0, /*tp_as_sequence*/ + 0, /*tp_as_mapping*/ + 0, /*tp_hash */ + 0, /*tp_call*/ + 0, /*tp_str*/ + 0, /*tp_getattro*/ + 0, /*tp_setattro*/ + 0, /*tp_as_buffer*/ + Py_TPFLAGS_DEFAULT, /*tp_flags*/ + "Filter object", /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + py_filtermethods, /* tp_methods */ +#if 0 + py_filtermembers, /* tp_members */ + 0, /* tp_getset */ + 0, /* tp_base */ + 0, /* tp_dict */ + 0, /* tp_descr_get */ + 0, /* tp_descr_set */ + 0, /* tp_dictoffset */ + (initproc)Noddy_init, /* tp_init */ + 0, /* tp_alloc */ + NULL, /* tp_new */ +#endif +}; + +PyMODINIT_FUNC init_filters() +{ + PyObject *module = Py_InitModule3("_filters", py_filtermethods, _filtersdoc); + + PyFilterType.tp_new = PyType_GenericNew; + if (PyType_Ready(&PyFilterType) < 0) + return; + + Py_INCREF(&PyFilterType); + + PyModule_AddObject(module, "Filter", (PyObject *)(&PyFilterType)); +} + +#ifdef __cplusplus +} +#endif diff --git a/cheetah/_namemapper.c b/cheetah/c/_namemapper.c index d07769e..d07769e 100644 --- a/cheetah/_namemapper.c +++ b/cheetah/c/_namemapper.c diff --git a/cheetah/c/_template.c b/cheetah/c/_template.c new file mode 100644 index 0000000..2345336 --- /dev/null +++ b/cheetah/c/_template.c @@ -0,0 +1,52 @@ +/* + * Implementing a few of the functions needed for Template.py in C + * + * (c) 2009, R. Tyler Ballance <tyler@slide.com> + */ +#include <Python.h> + +#ifdef __cplusplus +extern "C" { +#endif + +static PyObject *unspecifiedModule = NULL; +static PyObject *unspecified = NULL; + +static PyObject *py_valordefault(PyObject *self, PyObject *args, PyObject *kwargs) +{ + PyObject *value, *def; + + if (!PyArg_ParseTuple(args, "OO", &value, &def)) + return NULL; + + if (value == unspecified) { + Py_XINCREF(def); + return def; + } + Py_XINCREF(value); + return value; +} + +static const char _template_doc[] = "\ +\n\ +"; +static struct PyMethodDef _template_methods[] = { + {"valOrDefault", (PyCFunction)py_valordefault, METH_VARARGS, NULL}, + {NULL} +}; + +PyMODINIT_FUNC init_template() +{ + PyObject *module = Py_InitModule3("_template", _template_methods, + _template_doc); + unspecifiedModule = PyImport_ImportModule("Cheetah.Unspecified"); + if ( (PyErr_Occurred()) || (!unspecifiedModule) ) + return; + unspecified = PyObject_GetAttrString(unspecifiedModule, "Unspecified"); + if (PyErr_Occurred()) + return; +} + +#ifdef __cplusplus +} +#endif diff --git a/cheetah/c/_verifytype.c b/cheetah/c/_verifytype.c new file mode 100644 index 0000000..6dadf22 --- /dev/null +++ b/cheetah/c/_verifytype.c @@ -0,0 +1,107 @@ +/* + * 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 diff --git a/src/Tests/Performance.py b/src/Tests/Performance.py new file mode 100644 index 0000000..db721f9 --- /dev/null +++ b/src/Tests/Performance.py @@ -0,0 +1,220 @@ +#!/usr/bin/env python + +import Cheetah.NameMapper +import Cheetah.Template +from Cheetah.Utils import statprof + +import os +import sys +import unittest + +from test import pystone +import time + +# This can be turned on with the `--debug` flag when running the test +# and will cause the tests to all just dump out how long they took +# insteasd of asserting on duration +DEBUG = False + +# TOLERANCE in Pystones +kPS = 1000 +TOLERANCE = 0.5*kPS + +class DurationError(AssertionError): + pass + +_pystone_calibration_mark = None +def _pystone_calibration(): + global _pystone_calibration_mark + if not _pystone_calibration_mark: + _pystone_calibration_mark = pystone.pystones(loops=pystone.LOOPS) + return _pystone_calibration_mark + +def perftest(max_num_pystones, current_pystone=None): + ''' + Performance test decorator based off the 'timedtest' + decorator found in this Active State recipe: + http://code.activestate.com/recipes/440700/ + ''' + if not isinstance(max_num_pystones, float): + max_num_pystones = float(max_num_pystones) + + if not current_pystone: + current_pystone = _pystone_calibration() + + def _test(function): + def wrapper(*args, **kw): + start_time = time.time() + try: + return function(*args, **kw) + finally: + total_time = time.time() - start_time + if total_time == 0: + pystone_total_time = 0 + else: + pystone_rate = current_pystone[0] / current_pystone[1] + pystone_total_time = total_time / pystone_rate + global DEBUG + if DEBUG: + print 'The test "%s" took: %s pystones' % (function.func_name, + pystone_total_time) + else: + if pystone_total_time > (max_num_pystones + TOLERANCE): + raise DurationError((('Test too long (%.2f Ps, ' + 'need at most %.2f Ps)') + % (pystone_total_time, + max_num_pystones))) + return wrapper + return _test + + +class DynamicTemplatePerformanceTest(unittest.TestCase): + loops = 10 + @perftest(1200) + def test_BasicDynamic(self): + template = ''' + #def foo(arg1, arg2) + #pass + #end def + ''' + for i in xrange(self.loops): + klass = Cheetah.Template.Template.compile(template) + assert klass + +class PerformanceTest(unittest.TestCase): + iterations = 1000000 + display = False + def setUp(self): + super(PerformanceTest, self).setUp() + statprof.start() + + def runTest(self): + for i in xrange(self.iterations): + if hasattr(self, 'performanceSample'): + self.display = True + self.performanceSample() + + def tearDown(self): + super(PerformanceTest, self).tearDown() + statprof.stop() + if self.display: + print '>>> %s (%d iterations) ' % (self.__class__.__name__, + self.iterations) + statprof.display() + +class DynamicMethodCompilationTest(PerformanceTest): + def performanceSample(self): + template = ''' + #import sys + #import os + #def testMethod() + #set foo = [1, 2, 3, 4] + #return $foo[0] + #end def + ''' + template = Cheetah.Template.Template.compile(template, + keepRefToGeneratedCode=False) + template = template() + value = template.testMethod() + +class DynamicSimpleCompilationTest(PerformanceTest): + def performanceSample(self): + template = ''' + #import sys + #import os + #set foo = [1,2,3,4] + + Well hello there! This is basic. + + Here's an array too: $foo + ''' + template = Cheetah.Template.Template.compile(template, + keepRefToGeneratedCode=False) + template = template() + template = unicode(template) + + +class FilterTest(PerformanceTest): + template = None + def setUp(self): + super(FilterTest, self).setUp() + template = ''' + #import sys + #import os + #set foo = [1, 2, 3, 4] + + $foo, $foo, $foo + ''' + template = Cheetah.Template.Template.compile(template, + keepRefToGeneratedCode=False) + self.template = template() + + def performanceSample(self): + value = unicode(self.template) + + +class LongCompileTest(PerformanceTest): + ''' Test the compilation on a sufficiently large template ''' + def compile(self, template): + return Cheetah.Template.Template.compile(template, keepRefToGeneratedCode=False) + + def performanceSample(self): + template = ''' + #import sys + #import Cheetah.Template + + #extends Cheetah.Template.Template + + #def header() + <center><h2>This is my header</h2></center> + #end def + + #def footer() + #return "Huzzah" + #end def + + #def scripts() + #pass + #end def + + #def respond() + <html> + <head> + <title>${title}</title> + + $scripts() + </head> + <body> + $header() + + #for $i in $xrange(10) + This is just some stupid page! + <br/> + #end for + + <br/> + $footer() + </body> + </html> + #end def + + ''' + return self.compile(template) + +class LongCompile_CompilerSettingsTest(LongCompileTest): + def compile(self, template): + return Cheetah.Template.Template.compile(template, keepRefToGeneratedCode=False, + compilerSettings={'useStackFrames' : True, 'useAutocalling' : True}) + +class LongCompileAndRun(LongCompileTest): + def performanceSample(self): + template = super(LongCompileAndRun, self).performanceSample() + template = template(searchList=[{'title' : 'foo'}]) + template = template.respond() + + +if __name__ == '__main__': + if '--debug' in sys.argv: + DEBUG = True + sys.argv = [arg for arg in sys.argv if not arg == '--debug'] + unittest.main() diff --git a/src/Tests/VerifyType.py b/src/Tests/VerifyType.py new file mode 100644 index 0000000..7cab587 --- /dev/null +++ b/src/Tests/VerifyType.py @@ -0,0 +1,157 @@ +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() |