Skip to content

Commit

Permalink
The old "AttributeError: __call__" bug resurfaced with Python 2.1, bu…
Browse files Browse the repository at this point in the history
…t 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).
  • Loading branch information
hathawsh committed Jun 21, 2001
1 parent 7292659 commit c0a3894
Show file tree
Hide file tree
Showing 3 changed files with 97 additions and 74 deletions.
24 changes: 11 additions & 13 deletions DT_Util.py
Original file line number Diff line number Diff line change
Expand Up @@ -82,8 +82,8 @@
# attributions are listed in the accompanying credits file.
#
##############################################################################
'''$Id: DT_Util.py,v 1.80 2001/06/21 17:45:12 shane Exp $'''
__version__='$Revision: 1.80 $'[11:-2]
'''$Id: DT_Util.py,v 1.81 2001/06/21 19:08:59 shane Exp $'''
__version__='$Revision: 1.81 $'[11:-2]

import re, os
from html_quote import html_quote # for import by other modules, dont remove!
Expand Down Expand Up @@ -112,8 +112,10 @@ def int_param(params,md,name,default=0, st=type('')):

try:
import ExtensionClass
from cDocumentTemplate import InstanceDict, TemplateDict, render_blocks
except: from pDocumentTemplate import InstanceDict, TemplateDict, render_blocks
from cDocumentTemplate import InstanceDict, TemplateDict, \
render_blocks, safe_callable
except: from pDocumentTemplate import InstanceDict, TemplateDict, \
render_blocks, safe_callable


functype = type(int_param)
Expand Down Expand Up @@ -185,15 +187,11 @@ def render(self, v):
v = v.__render_with_namespace__(self)
else:
vbase = getattr(v, 'aq_base', v)
if callable(vbase):
try:
if getattr(vbase, 'isDocTemp', 0):
v = v(None, self)
else:
v = v()
except AttributeError, n:
if n != '__call__':
raise
if safe_callable(vbase):
if getattr(vbase, 'isDocTemp', 0):
v = v(None, self)
else:
v = v()
return v

d['render']=render
Expand Down
84 changes: 57 additions & 27 deletions cDocumentTemplate.c
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@
****************************************************************************/
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"
Expand All @@ -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_Unauthorized_fmt, *py_guarded_getattr;
static PyObject *py__push, *py__pop, *py_aq_base, *py_renderNS;
static PyObject *py___class__;

/* ----------------------------------------------------- */

Expand Down Expand Up @@ -322,7 +323,35 @@ MM__init__(MM *self, PyObject *args)
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
dtObjectIsCallable(PyObject *ob) {
Expand All @@ -332,9 +361,9 @@ dtObjectIsCallable(PyObject *ob) {
/* Ensure that an object is really callable by unwrapping it */
UNLESS(base=PyObject_GetAttr(ob, py_aq_base)) {
PyErr_Clear();
return PyCallable_Check(ob);
return safe_PyCallable_Check(ob);
}
result=PyCallable_Check(base);
result=safe_PyCallable_Check(base);
Py_DECREF(base);
return result;
}
Expand Down Expand Up @@ -369,7 +398,7 @@ static PyObject *
MM_cget(MM *self, PyObject *key, int call)
{
long i;
PyObject *e, *t, *rr, *tb;
PyObject *e, *rr, *tb;

UNLESS(-1 != (i=PyList_Size(self->data))) return NULL;
while (--i >= 0)
Expand Down Expand Up @@ -401,27 +430,10 @@ MM_cget(MM *self, PyObject *key, int call)

rr=PyObject_CallObject(e,NULL);
if (rr) ASSIGN(e,rr);
else
{
PyErr_Fetch(&t, &rr, &tb);
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);
}
}
else {
Py_DECREF(e);
return NULL;
}
}
return e;
}
Expand Down Expand Up @@ -865,17 +877,34 @@ render_blocks(PyObject *self, PyObject *args)
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[] = {
{"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 */
};

void
initcDocumentTemplate(void)
{
PyObject *m, *d;
char *rev="$Revision: 1.38 $";
char *rev="$Revision: 1.39 $";

DictInstanceType.ob_type=&PyType_Type;

Expand All @@ -894,6 +923,7 @@ initcDocumentTemplate(void)
UNLESS(py_Unauthorized=PyString_FromString("Unauthorized")) return;
UNLESS(py_Unauthorized_fmt=PyString_FromString(
"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"))
return;
Expand Down
63 changes: 29 additions & 34 deletions pDocumentTemplate.py
Original file line number Diff line number Diff line change
Expand Up @@ -85,35 +85,37 @@
__doc__='''Python implementations of document template some features
$Id: pDocumentTemplate.py,v 1.29 2001/06/21 17:45:12 shane Exp $'''
__version__='$Revision: 1.29 $'[11:-2]
$Id: pDocumentTemplate.py,v 1.30 2001/06/21 19:08:59 shane Exp $'''
__version__='$Revision: 1.30 $'[11:-2]

import string, sys, types
from string import join

StringType=type('')
TupleType=type(())
isFunctionType={}
for name in ['BuiltinFunctionType', 'BuiltinMethodType', 'ClassType',
'FunctionType', 'LambdaType', 'MethodType', 'UnboundMethodType']:
try: isFunctionType[getattr(types,name)]=1
except: pass
ClassTypes = [types.ClassType]

try: # Add function and method types from Extension Classes
import ExtensionClass
isFunctionType[ExtensionClass.PythonMethodType]=1
isFunctionType[ExtensionClass.ExtensionMethodType]=1
except: pass
try:
from ExtensionClass import Base
except ImportError:
pass
else:
class c(Base): pass
ClassTypes.append(c.__class__)

isFunctionType=isFunctionType.has_key

isSimpleType={}
for n in dir(types):
if (n[-4:]=='Type' and n != 'InstanceType' and
not isFunctionType(getattr(types, n))):
isSimpleType[getattr(types, n)]=1
def safe_callable(ob):
# Works with ExtensionClasses and Acquisition.
if hasattr(ob, '__class__'):
if hasattr(ob, '__call__'):
return 1
else:
return type(ob) in ClassTypes
else:
return callable(ob)


StringType=type('')
TupleType=type(())

isSimpleType=isSimpleType.has_key

class InstanceDict:

Expand Down Expand Up @@ -204,25 +206,18 @@ def __init__(self):
try: self.keys=m.keys
except: pass

def __getitem__(self,key,call=1,
simple=isSimpleType,
isFunctionType=isFunctionType,
):
def __getitem__(self,key,call=1):

v = self.dicts[key]
if call:
if hasattr(v, '__render_with_namespace__'):
return v.__render_with_namespace__(self)
vbase = getattr(v, 'aq_base', v)
if callable(vbase):
try:
if getattr(vbase, 'isDocTemp', 0):
v = v(None, self)
else:
v = v()
except AttributeError, n:
if n != '__call__':
raise
if safe_callable(vbase):
if getattr(vbase, 'isDocTemp', 0):
v = v(None, self)
else:
v = v()
return v

def has_key(self,key):
Expand Down

0 comments on commit c0a3894

Please sign in to comment.