[Zope] [ANN] Zope 2.2 patch for cDocumentTemplate/Acquisition bug

Dieter Maurer dieter@handshake.de
Sun, 5 Nov 2000 19:08:04 +0100 (CET)


Appended is a patch for the cDocumentTemplate/Acquisition bug.

Usually, this bug manifests itself by an "AttributeError: __call__"
for a folder object. The usual context is:

    DTML Document:

	 .....
	 <dtml-with folder>  <!-- here is the Attribute Error -->
	   ....
	 </dtml-with>
	 .....


The patch modifies "C" code. This means you need a Zope source
distribution to apply it and then you must regenerate Zope.


Almost surely, the patch is not yet complete:

  The code in "cDocumentTemplate.c" looks like (Python emulating
  "C" code):

      if callable(o):
        if hasattr('isDocTemp') and o.isDocTemp:
	  o= o(None,md)
	else: o= o()

  The remaining problem is that 'isDocTemp' might be acquired
  and does not belong to "o.__call__". In this case, "o"
  might be called with the wrong number of parameters.

  I would expect, that the problem disappears, when the "isDocTemp"
  test is performed with "o.aq_base" rather than "o". But
  I am not sure.
  Maybe, an acquisition expert can look into this remaining
  issue.
  
Dieter

------------------------------------------------------------------------
--- lib/Components/ExtensionClass/:Acquisition.c	Sat Oct  7 20:40:53 2000
+++ lib/Components/ExtensionClass/Acquisition.c	Sat Nov  4 21:10:04 2000
@@ -1391,6 +1391,31 @@
   return r;
 }
 
+
+/* DM: adding "AqCallable_Check" */
+static int
+aqCallable(PyObject *o) {
+  if (isWrapper(o)) {
+    PyObject *call;
+
+    UNLESS(call=PyObject_GetAttr(o,py__call__)) {
+      PyErr_Clear();
+      return 0;
+    }
+    Py_DECREF(call); return 1;
+  } else
+    return PyCallable_Check(o);
+}
+
+static PyObject *
+AqCallable_Check(PyObject *ignored, PyObject *args)
+{
+  PyObject *o;
+
+  UNLESS (PyArg_ParseTuple(args, "O", &o)) return NULL;
+  return PyInt_FromLong((long) aqCallable(o));
+}
+
 static struct PyMethodDef methods[] = {
   {"aq_acquire", (PyCFunction)module_aq_acquire, METH_VARARGS|METH_KEYWORDS, 
    "aq_acquire(ob, name [, filter, extra, explicit]) -- "
@@ -1412,6 +1437,10 @@
   {"aq_chain", (PyCFunction)module_aq_chain, METH_VARARGS, 
    "aq_chain(ob [, containment]) -- "
    "Get a list of objects in the acquisition environment"},
+  /* DM */
+  {"callable", (PyCFunction)AqCallable_Check, METH_VARARGS,
+   "callable(ob) -- "
+   "return true iff the object is callable"},
   {NULL,	NULL}
 };
 
--- lib/python/DocumentTemplate/:cDocumentTemplate.c	Sat Jul 29 11:24:34 2000
+++ lib/python/DocumentTemplate/cDocumentTemplate.c	Sat Nov  4 21:11:11 2000
@@ -95,14 +95,35 @@
 static PyObject *py_Unauthorized_fmt, *py_validate;
 static PyObject *py__push, *py__pop, *py_aq_base;
 
-/* ----------------------------------------------------- */
-
 static void PyVar_Assign(PyObject **v, PyObject *e) { Py_XDECREF(*v); *v=e;}
 #define ASSIGN(V,E) PyVar_Assign(&(V),(E))
 #define UNLESS(E) if (!(E))
 #define UNLESS_ASSIGN(V,E) ASSIGN(V,E); UNLESS(V)
 #define OBJECT(O)(((PyObject*)O))
 
+/* ----------------------------------------------------- */
+/* DM: callable */
+static int (*callable)(PyObject *o);
+
+static PyObject *aq_callable;
+
+static int aq_callable_check(PyObject *o) {
+  PyObject *result= PyObject_CallFunction(aq_callable,"O",o);
+  int	   callable= PyInt_AsLong(result);
+  Py_DECREF(result);
+  return callable;
+}
+
+static void init_callable() {
+  PyObject *aq= PyImport_ImportModule("Acquisition");
+  if(aq &&
+     (aq_callable= PyObject_GetAttrString(aq,"callable")))
+    callable= aq_callable_check;
+  else callable= PyCallable_Check;
+}
+
+/* ----------------------------------------------------- */
+
 typedef struct {
   PyObject_HEAD
   PyObject *inst;
@@ -370,7 +391,7 @@
 	{
 	  dt=0;
 
-	  if (PyCallable_Check(e))
+	  if ((*callable)(e))
 	    {
 	      /* Decide whether we have a document template */
 	      if (rr=PyObject_GetAttr(e,py_isDocTemp))
@@ -917,6 +938,9 @@
 
   PyDict_SetItemString(d, "__version__",
 		       PyString_FromStringAndSize(rev+11,strlen(rev+11)-2));
+
+  /* DM */
+  init_callable();
 
   if (PyErr_Occurred())
     Py_FatalError("can't initialize module cDocumentTemplate");