[Zope-Checkins] CVS: Zope2 - DT_Util.py:1.81 cDocumentTemplate.c:1.39 pDocumentTemplate.py:1.30

shane@digicool.com shane@digicool.com
Thu, 21 Jun 2001 15:08:59 -0400 (EDT)


Update of /cvs-repository/Zope2/lib/python/DocumentTemplate
In directory korak.digicool.com:/tmp/cvs-serv29498

Modified Files:
	DT_Util.py cDocumentTemplate.c pDocumentTemplate.py 
Log Message:
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).



--- Updated File DT_Util.py in package Zope2 --
--- DT_Util.py	2001/06/21 17:45:12	1.80
+++ DT_Util.py	2001/06/21 19:08:59	1.81
@@ -112,8 +112,10 @@
 
 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)
@@ -185,15 +187,11 @@
         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

--- Updated File cDocumentTemplate.c in package Zope2 --
--- cDocumentTemplate.c	2001/06/21 17:45:12	1.38
+++ cDocumentTemplate.c	2001/06/21 19:08:59	1.39
@@ -94,6 +94,7 @@
 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__;
 
 /* ----------------------------------------------------- */
 
@@ -322,7 +323,35 @@
   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) {
@@ -332,9 +361,9 @@
   /* 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;
 }
@@ -369,7 +398,7 @@
 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)
@@ -401,27 +430,10 @@
 
               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;
 	}
@@ -865,9 +877,26 @@
   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 */
 };
 
@@ -894,6 +923,7 @@
   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;

--- Updated File pDocumentTemplate.py in package Zope2 --
--- pDocumentTemplate.py	2001/06/21 17:45:12	1.29
+++ pDocumentTemplate.py	2001/06/21 19:08:59	1.30
@@ -91,29 +91,31 @@
 import string, sys, types
 from string import join
 
+ClassTypes = [types.ClassType]
+
+try:
+    from ExtensionClass import Base
+except ImportError:
+    pass
+else:
+    class c(Base): pass
+    ClassTypes.append(c.__class__)
+
+
+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(())
-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
-    import ExtensionClass
-    isFunctionType[ExtensionClass.PythonMethodType]=1
-    isFunctionType[ExtensionClass.ExtensionMethodType]=1
-except: pass
-
-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
 
-isSimpleType=isSimpleType.has_key
 
 class InstanceDict:
 
@@ -204,25 +206,18 @@
         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):