Commit 41e3df36 authored by Shane Hathaway's avatar Shane Hathaway

The old "AttributeError: __call__" bug resurfaced with Python 2.1, but this

change is intended to solve it once and for all: there's now a "safe_callable"
function, implemented both in cDocumentTemplate and pDocumentTemplate, that
can more reliably check for callability.

A surprising side effect is that DTML is about 15% faster with this change
(according to a rudimentary test).
parent 47bd3976
...@@ -82,8 +82,8 @@ ...@@ -82,8 +82,8 @@
# attributions are listed in the accompanying credits file. # attributions are listed in the accompanying credits file.
# #
############################################################################## ##############################################################################
'''$Id: DT_Util.py,v 1.80 2001/06/21 17:45:12 shane Exp $''' '''$Id: DT_Util.py,v 1.81 2001/06/21 19:08:59 shane Exp $'''
__version__='$Revision: 1.80 $'[11:-2] __version__='$Revision: 1.81 $'[11:-2]
import re, os import re, os
from html_quote import html_quote # for import by other modules, dont remove! from html_quote import html_quote # for import by other modules, dont remove!
...@@ -112,8 +112,10 @@ def int_param(params,md,name,default=0, st=type('')): ...@@ -112,8 +112,10 @@ def int_param(params,md,name,default=0, st=type('')):
try: try:
import ExtensionClass import ExtensionClass
from cDocumentTemplate import InstanceDict, TemplateDict, render_blocks from cDocumentTemplate import InstanceDict, TemplateDict, \
except: from pDocumentTemplate import InstanceDict, TemplateDict, render_blocks render_blocks, safe_callable
except: from pDocumentTemplate import InstanceDict, TemplateDict, \
render_blocks, safe_callable
functype = type(int_param) functype = type(int_param)
...@@ -185,15 +187,11 @@ def render(self, v): ...@@ -185,15 +187,11 @@ def render(self, v):
v = v.__render_with_namespace__(self) v = v.__render_with_namespace__(self)
else: else:
vbase = getattr(v, 'aq_base', v) vbase = getattr(v, 'aq_base', v)
if callable(vbase): if safe_callable(vbase):
try: if getattr(vbase, 'isDocTemp', 0):
if getattr(vbase, 'isDocTemp', 0): v = v(None, self)
v = v(None, self) else:
else: v = v()
v = v()
except AttributeError, n:
if n != '__call__':
raise
return v return v
d['render']=render d['render']=render
......
...@@ -84,7 +84,7 @@ ...@@ -84,7 +84,7 @@
****************************************************************************/ ****************************************************************************/
static char cDocumentTemplate_module_documentation[] = static char cDocumentTemplate_module_documentation[] =
"" ""
"\n$Id: cDocumentTemplate.c,v 1.38 2001/06/21 17:45:12 shane Exp $" "\n$Id: cDocumentTemplate.c,v 1.39 2001/06/21 19:08:59 shane Exp $"
; ;
#include "ExtensionClass.h" #include "ExtensionClass.h"
...@@ -94,6 +94,7 @@ static PyObject *py___call__, *py___roles__, *py_AUTHENTICATED_USER; ...@@ -94,6 +94,7 @@ static PyObject *py___call__, *py___roles__, *py_AUTHENTICATED_USER;
static PyObject *py_hasRole, *py__proxy_roles, *py_Unauthorized; static PyObject *py_hasRole, *py__proxy_roles, *py_Unauthorized;
static PyObject *py_Unauthorized_fmt, *py_guarded_getattr; static PyObject *py_Unauthorized_fmt, *py_guarded_getattr;
static PyObject *py__push, *py__pop, *py_aq_base, *py_renderNS; static PyObject *py__push, *py__pop, *py_aq_base, *py_renderNS;
static PyObject *py___class__;
/* ----------------------------------------------------- */ /* ----------------------------------------------------- */
...@@ -322,7 +323,35 @@ MM__init__(MM *self, PyObject *args) ...@@ -322,7 +323,35 @@ MM__init__(MM *self, PyObject *args)
return Py_None; return Py_None;
} }
static int
safe_PyCallable_Check(PyObject *x)
{
PyObject *klass;
if (x == NULL)
return 0;
klass = PyObject_GetAttr(x, py___class__);
if (klass) {
PyObject *call = PyObject_GetAttr(x, py___call__);
if (call) {
Py_DECREF(klass);
Py_DECREF(call);
return 1;
}
else {
PyErr_Clear();
Py_DECREF(klass);
if (PyClass_Check(x) || PyExtensionClass_Check(x))
return 1;
else
return 0;
}
}
else {
PyErr_Clear();
return PyCallable_Check(x);
}
}
static int static int
dtObjectIsCallable(PyObject *ob) { dtObjectIsCallable(PyObject *ob) {
...@@ -332,9 +361,9 @@ dtObjectIsCallable(PyObject *ob) { ...@@ -332,9 +361,9 @@ dtObjectIsCallable(PyObject *ob) {
/* Ensure that an object is really callable by unwrapping it */ /* Ensure that an object is really callable by unwrapping it */
UNLESS(base=PyObject_GetAttr(ob, py_aq_base)) { UNLESS(base=PyObject_GetAttr(ob, py_aq_base)) {
PyErr_Clear(); PyErr_Clear();
return PyCallable_Check(ob); return safe_PyCallable_Check(ob);
} }
result=PyCallable_Check(base); result=safe_PyCallable_Check(base);
Py_DECREF(base); Py_DECREF(base);
return result; return result;
} }
...@@ -369,7 +398,7 @@ static PyObject * ...@@ -369,7 +398,7 @@ static PyObject *
MM_cget(MM *self, PyObject *key, int call) MM_cget(MM *self, PyObject *key, int call)
{ {
long i; long i;
PyObject *e, *t, *rr, *tb; PyObject *e, *rr, *tb;
UNLESS(-1 != (i=PyList_Size(self->data))) return NULL; UNLESS(-1 != (i=PyList_Size(self->data))) return NULL;
while (--i >= 0) while (--i >= 0)
...@@ -401,27 +430,10 @@ MM_cget(MM *self, PyObject *key, int call) ...@@ -401,27 +430,10 @@ MM_cget(MM *self, PyObject *key, int call)
rr=PyObject_CallObject(e,NULL); rr=PyObject_CallObject(e,NULL);
if (rr) ASSIGN(e,rr); if (rr) ASSIGN(e,rr);
else else {
{ Py_DECREF(e);
PyErr_Fetch(&t, &rr, &tb); return NULL;
if (t!=PyExc_AttributeError || }
PyObject_Compare(rr,py___call__) != 0)
{
PyErr_Restore(t,rr,tb);
Py_DECREF(e);
return NULL;
}
/*
Added by Brian on 08/30/99. We need to be sure
to DECREF the exception in the event of an
AttributeError to avoid leaking.
*/
else {
Py_XDECREF(t);
Py_XDECREF(rr);
Py_XDECREF(tb);
}
}
} }
return e; return e;
} }
...@@ -865,9 +877,26 @@ err: ...@@ -865,9 +877,26 @@ err:
return NULL; return NULL;
} }
static PyObject *
safe_callable(PyObject *self, PyObject *args)
{
PyObject *ob;
int res;
UNLESS(PyArg_ParseTuple(args,"O", &ob)) return NULL;
res = safe_PyCallable_Check(ob);
if (res)
return PyInt_FromLong(1);
else
return PyInt_FromLong(0);
}
static struct PyMethodDef Module_Level__methods[] = { static struct PyMethodDef Module_Level__methods[] = {
{"render_blocks", (PyCFunction)render_blocks, METH_VARARGS, {"render_blocks", (PyCFunction)render_blocks, METH_VARARGS,
""}, ""},
{"safe_callable", (PyCFunction)safe_callable, METH_VARARGS,
"callable() with a workaround for a problem with ExtensionClasses\n"
"and __call__()."},
{NULL, (PyCFunction)NULL, 0, NULL} /* sentinel */ {NULL, (PyCFunction)NULL, 0, NULL} /* sentinel */
}; };
...@@ -875,7 +904,7 @@ void ...@@ -875,7 +904,7 @@ void
initcDocumentTemplate(void) initcDocumentTemplate(void)
{ {
PyObject *m, *d; PyObject *m, *d;
char *rev="$Revision: 1.38 $"; char *rev="$Revision: 1.39 $";
DictInstanceType.ob_type=&PyType_Type; DictInstanceType.ob_type=&PyType_Type;
...@@ -894,6 +923,7 @@ initcDocumentTemplate(void) ...@@ -894,6 +923,7 @@ initcDocumentTemplate(void)
UNLESS(py_Unauthorized=PyString_FromString("Unauthorized")) return; UNLESS(py_Unauthorized=PyString_FromString("Unauthorized")) return;
UNLESS(py_Unauthorized_fmt=PyString_FromString( UNLESS(py_Unauthorized_fmt=PyString_FromString(
"You are not authorized to access <em>%s</em>.")) return; "You are not authorized to access <em>%s</em>.")) return;
UNLESS(py___class__=PyString_FromString("__class__")) return;
UNLESS(py_AUTHENTICATED_USER=PyString_FromString("AUTHENTICATED_USER")) UNLESS(py_AUTHENTICATED_USER=PyString_FromString("AUTHENTICATED_USER"))
return; return;
......
...@@ -85,35 +85,37 @@ ...@@ -85,35 +85,37 @@
__doc__='''Python implementations of document template some features __doc__='''Python implementations of document template some features
$Id: pDocumentTemplate.py,v 1.29 2001/06/21 17:45:12 shane Exp $''' $Id: pDocumentTemplate.py,v 1.30 2001/06/21 19:08:59 shane Exp $'''
__version__='$Revision: 1.29 $'[11:-2] __version__='$Revision: 1.30 $'[11:-2]
import string, sys, types import string, sys, types
from string import join from string import join
StringType=type('') ClassTypes = [types.ClassType]
TupleType=type(())
isFunctionType={}
for name in ['BuiltinFunctionType', 'BuiltinMethodType', 'ClassType',
'FunctionType', 'LambdaType', 'MethodType', 'UnboundMethodType']:
try: isFunctionType[getattr(types,name)]=1
except: pass
try: # Add function and method types from Extension Classes try:
import ExtensionClass from ExtensionClass import Base
isFunctionType[ExtensionClass.PythonMethodType]=1 except ImportError:
isFunctionType[ExtensionClass.ExtensionMethodType]=1 pass
except: pass else:
class c(Base): pass
ClassTypes.append(c.__class__)
isFunctionType=isFunctionType.has_key
isSimpleType={} def safe_callable(ob):
for n in dir(types): # Works with ExtensionClasses and Acquisition.
if (n[-4:]=='Type' and n != 'InstanceType' and if hasattr(ob, '__class__'):
not isFunctionType(getattr(types, n))): if hasattr(ob, '__call__'):
isSimpleType[getattr(types, n)]=1 return 1
else:
return type(ob) in ClassTypes
else:
return callable(ob)
StringType=type('')
TupleType=type(())
isSimpleType=isSimpleType.has_key
class InstanceDict: class InstanceDict:
...@@ -204,25 +206,18 @@ class TemplateDict: ...@@ -204,25 +206,18 @@ class TemplateDict:
try: self.keys=m.keys try: self.keys=m.keys
except: pass except: pass
def __getitem__(self,key,call=1, def __getitem__(self,key,call=1):
simple=isSimpleType,
isFunctionType=isFunctionType,
):
v = self.dicts[key] v = self.dicts[key]
if call: if call:
if hasattr(v, '__render_with_namespace__'): if hasattr(v, '__render_with_namespace__'):
return v.__render_with_namespace__(self) return v.__render_with_namespace__(self)
vbase = getattr(v, 'aq_base', v) vbase = getattr(v, 'aq_base', v)
if callable(vbase): if safe_callable(vbase):
try: if getattr(vbase, 'isDocTemp', 0):
if getattr(vbase, 'isDocTemp', 0): v = v(None, self)
v = v(None, self) else:
else: v = v()
v = v()
except AttributeError, n:
if n != '__call__':
raise
return v return v
def has_key(self,key): def has_key(self,key):
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment