diff -urN Zope-2.7.2-rc1.orig/lib/Components/ExtensionClass/src/Acquisition.c Zope-2.7.2-rc1/lib/Components/ExtensionClass/src/Acquisition.c
--- Zope-2.7.2-rc1.orig/lib/Components/ExtensionClass/src/Acquisition.c	2003-10-21 14:44:39.000000000 +0200
+++ Zope-2.7.2-rc1/lib/Components/ExtensionClass/src/Acquisition.c	2005-02-01 01:04:12.617262632 +0100
@@ -81,7 +81,7 @@
   INIT_PY_NAME(__repr__);
   INIT_PY_NAME(__str__);
   INIT_PY_NAME(__cmp__);
-  
+
 #undef INIT_PY_NAME
 }
 
@@ -150,13 +150,13 @@
   PyTuple_SET_ITEM(t,0,NULL);
   Py_DECREF(t);
 
-  if (r 
+  if (r
       && r->ob_refcnt==1
-      && isWrapper(r) 
+      && isWrapper(r)
       && WRAPPER(r)->container && isWrapper(WRAPPER(r)->container)
       )
     while (WRAPPER(r)->obj && isWrapper(WRAPPER(r)->obj)
-	   && (WRAPPER(WRAPPER(r)->obj)->container == 
+	   && (WRAPPER(WRAPPER(r)->obj)->container ==
 	       WRAPPER(WRAPPER(r)->container)->obj)
 	   )
       {
@@ -179,7 +179,7 @@
 newWrapper(PyObject *obj, PyObject *container, PyTypeObject *Wrappertype)
 {
   Wrapper *self;
-  
+
   if (freeWrappers)
     {
       self=freeWrappers;
@@ -211,7 +211,7 @@
 
 
 static void
-Wrapper_dealloc(Wrapper *self)     
+Wrapper_dealloc(Wrapper *self)
 {
   Py_XDECREF(self->obj);
   Py_XDECREF(self->container);
@@ -223,7 +223,7 @@
       freeWrappers=self;
       nWrappers++;
     }
-  else 
+  else
     {
       PyObject_DEL(self);
     }
@@ -271,7 +271,7 @@
       if (strcmp(name,"explicit")==0)
 	{
 	  if (self->ob_type != (PyTypeObject *)&XaqWrappertype)
-	    return newWrapper(self->obj, self->container, 
+	    return newWrapper(self->obj, self->container,
 			      (PyTypeObject *)&XaqWrappertype);
 	  Py_INCREF(self);
 	  return OBJECT(self);
@@ -291,7 +291,7 @@
 	      {
 		if (PyList_Append(r,OBJECT(self)) >= 0)
 		  {
-		    if (isWrapper(self) && self->container) 
+		    if (isWrapper(self) && self->container)
 		      {
 			self=WRAPPER(self->container);
 			continue;
@@ -316,7 +316,7 @@
 	  if (self->obj)
 	    {
 	      r=self->obj;
-	      while (isWrapper(r) && WRAPPER(r)->obj) 
+	      while (isWrapper(r) && WRAPPER(r)->obj)
 		{
 		  self=WRAPPER(r);
 		  r=WRAPPER(r)->obj;
@@ -336,7 +336,7 @@
 	  return PyString_FromString("Bob");
 	}
       break;
-      
+
     }
 
   return NULL;
@@ -382,7 +382,65 @@
 }
 
 static PyObject *
-Wrapper_acquire(Wrapper *self, PyObject *oname, 
+Wrapper_GetAttr(PyObject *self, PyObject *attr_name, PyObject *orig)
+{
+  /* This function retrieves an attribute from an object by PyObject_GetAttr.
+
+     The main difference between Wrapper_GetAttr and PyObject_GetAttr is that
+     Wrapper_GetAttr calls _aq_dynamic to generate an attribute dynamically, if
+     the attribute is not found.
+  */
+  PyObject *r, *v, *tb;
+  PyObject *d, *m;
+  PyObject *o;
+
+  if (isWrapper (self))
+    o = WRAPPER(self)->obj;
+  else
+    o = self;
+  
+  /* Try to get an attribute in the normal way first.  */
+  r = PyObject_GetAttr(o, attr_name);
+  if (r)
+    return r;
+
+  /* If an unexpected error happens, return immediately.  */
+  PyErr_Fetch(&r,&v,&tb);
+  if (r != PyExc_AttributeError)
+    {
+      PyErr_Restore(r,v,tb);
+      return NULL;
+    }
+
+  /* Try to get _aq_dynamic.  */
+  m = PyObject_GetAttrString(o, "_aq_dynamic");
+  if (! m) {
+    PyErr_Restore(r,v,tb);
+    return NULL;
+  }
+
+  /* Call _aq_dynamic in the context of the original acquisition wrapper.  */
+  if (PyECMethod_Check(m) && PyECMethod_Self(m)==o)
+    ASSIGN(m,PyECMethod_New(m,OBJECT(self)));
+  else if (has__of__(m)) ASSIGN(m,__of__(m,OBJECT(self)));
+  d = PyObject_CallFunction(m, "O", attr_name);
+  Py_DECREF(m);
+
+  /* In the case of None, assume that the attribute is not found.  */
+  if (d == Py_None) {
+    Py_DECREF(d);
+    PyErr_Restore(r,v,tb);
+    return NULL;
+  }
+
+  Py_XDECREF(r);
+  Py_XDECREF(v);
+  Py_XDECREF(tb);
+  return d;
+}
+
+static PyObject *
+Wrapper_acquire(Wrapper *self, PyObject *oname,
 		PyObject *filter, PyObject *extra, PyObject *orig,
 		int explicit, int containment);
 
@@ -409,7 +467,7 @@
     else PyErr_Clear();
   else if (*name=='_' && name[1]=='_' && strcmp(name+2,"reduce__")==0)
     {
-      PyErr_SetString(PyExc_TypeError, 
+      PyErr_SetString(PyExc_TypeError,
                       "Can't pickle objects in acquisition wrappers.");
       return NULL;
     }
@@ -429,12 +487,12 @@
 		return NULL;
 	  }
 	  if ((r=Wrapper_findattr(WRAPPER(self->obj),
-				 oname, filter, extra, orig, 1, 
+				 oname, filter, extra, orig, 1,
 
 				 /* Search object container if explicit,
 				    or object is implicit acquirer */
 				 explicit ||
-				 self->obj->ob_type == 
+				 self->obj->ob_type ==
 				 (PyTypeObject*)&Wrappertype,
 				  explicit, containment)))
 	    {
@@ -453,12 +511,12 @@
 	  Py_XDECREF(r); Py_XDECREF(v); Py_XDECREF(tb);
 	  r=NULL;
 	}
-      else if ((r=PyObject_GetAttr(self->obj,oname)))
+      else if ((r=Wrapper_GetAttr(OBJECT(self),oname,orig)))
 	{
 	  if (r==Acquired)
 	    {
 	      Py_DECREF(r);
-	      return Wrapper_acquire(self, oname, filter, extra, orig, 1, 
+	      return Wrapper_acquire(self, oname, filter, extra, orig, 1,
 				     containment);
 	    }
 
@@ -487,8 +545,8 @@
       PyErr_Clear();
     }
 
-  if (sco && (*name != '_' || explicit)) 
-    return Wrapper_acquire(self, oname, filter, extra, orig, explicit, 
+  if (sco && (*name != '_' || explicit))
+    return Wrapper_acquire(self, oname, filter, extra, orig, explicit,
 			   containment);
 
   PyErr_SetObject(PyExc_AttributeError,oname);
@@ -496,7 +554,7 @@
 }
 
 static PyObject *
-Wrapper_acquire(Wrapper *self, PyObject *oname, 
+Wrapper_acquire(Wrapper *self, PyObject *oname,
 		PyObject *filter, PyObject *extra, PyObject *orig,
 		int explicit, int containment)
 {
@@ -511,23 +569,23 @@
 	    {
 	      /* Try to optimize search by recognizing repeated obs in path */
 	      if (WRAPPER(self->obj)->container==
-		  WRAPPER(self->container)->container) 
+		  WRAPPER(self->container)->container)
 		sco=0;
 	      else if (WRAPPER(self->obj)->container==
-		      WRAPPER(self->container)->obj)  
+		      WRAPPER(self->container)->obj)
 		sob=0;
 	   }
 
 	  r=Wrapper_findattr((Wrapper*)self->container,
-			     oname, filter, extra, orig, sob, sco, explicit, 
+			     oname, filter, extra, orig, sob, sco, explicit,
 			     containment);
-	  
+
 	  if (r && has__of__(r)) ASSIGN(r,__of__(r,OBJECT(self)));
 	  return r;
 	}
       else
 	{
-	  if ((r=PyObject_GetAttr(self->container,oname))) {
+	  if ((r=Wrapper_GetAttr(self->container,oname,orig))) {
 	    if (r == Acquired) {
 	      Py_DECREF(r);
 	    }
@@ -536,9 +594,9 @@
 		switch(apply_filter(filter,self->container,oname,r,
 				    extra,orig))
 		  {
-		  case -1: 
+		  case -1:
 		    return NULL;
-		  case 1: 
+		  case 1:
 		    if (has__of__(r)) ASSIGN(r,__of__(r,OBJECT(self)));
 		    return r;
 		  }
@@ -555,7 +613,7 @@
 	  }
 	}
     }
-  
+
   PyErr_SetObject(PyExc_AttributeError, oname);
   return NULL;
 }
@@ -564,7 +622,7 @@
 Wrapper_getattro(Wrapper *self, PyObject *oname)
 {
   if (self->obj || self->container)
-    return Wrapper_findattr(self, oname, NULL, NULL, NULL, 1, 1, 0, 0);
+    return Wrapper_findattr(self, oname, NULL, NULL, OBJECT(self), 1, 1, 0, 0);
 
   /* Maybe we are getting initialized? */
   return Py_FindAttr(OBJECT(self),oname);
@@ -581,7 +639,7 @@
     return Py_FindAttr(OBJECT(self),oname);
 
   if (self->obj || self->container)
-    return Wrapper_findattr(self, oname, NULL, NULL, NULL, 1, 0, 0, 0);
+    return Wrapper_findattr(self, oname, NULL, NULL, OBJECT(self), 1, 0, 0, 0);
 
   /* Maybe we are getting initialized? */
   return Py_FindAttr(OBJECT(self),oname);
@@ -594,7 +652,7 @@
 
   /* Allow assignment to parent, to change context. */
   if (PyString_Check(oname)) name=PyString_AS_STRING(oname);
-  if (*name=='a' && name[1]=='q' && name[2]=='_' 
+  if (*name=='a' && name[1]=='q' && name[2]=='_'
       && strcmp(name+3,"parent")==0)
     {
       Py_XINCREF(v);
@@ -612,7 +670,7 @@
       else   return PyObject_DelAttr(self->obj, oname);
     }
 
-  PyErr_SetString(PyExc_AttributeError, 
+  PyErr_SetString(PyExc_AttributeError,
 		  "Attempt to set attribute on empty acquisition wrapper");
   return -1;
 }
@@ -648,12 +706,12 @@
 
   ASSIGN(m, PyObject_CallFunction(m, "O", w));
   UNLESS (m) return -1;
-  
+
   r=PyInt_AsLong(m);
 
   Py_DECREF(m);
 
-  return r;  
+  return r;
 }
 
 static PyObject *
@@ -914,7 +972,7 @@
   return CallMethodO(OBJECT(self),py__or__,Build("(O)", o),NULL);
 }
 
-static int 
+static int
 Wrapper_coerce(Wrapper **self, PyObject **o)
 {
   PyObject *m;
@@ -938,7 +996,7 @@
 
 err:
   Py_DECREF(m);
-  return -1;  
+  return -1;
 }
 
 static PyObject *
@@ -1049,7 +1107,7 @@
   if (filter==Py_None) filter=0;
 
   result=Wrapper_findattr(self,name,filter,extra,OBJECT(self),1,
-			  explicit || 
+			  explicit ||
 			  self->ob_type==(PyTypeObject*)&Wrappertype,
 			  explicit, containment);
   if (result == NULL && defalt != NULL) {
@@ -1122,10 +1180,10 @@
 static struct PyMethodDef Wrapper_methods[] = {
   {"__init__", (PyCFunction)Wrapper__init__, 0,
    "Initialize an Acquirer Wrapper"},
-  {"acquire", (PyCFunction)Wrapper_acquire_method, 
+  {"acquire", (PyCFunction)Wrapper_acquire_method,
    METH_VARARGS|METH_KEYWORDS,
    "Get an attribute, acquiring it if necessary"},
-  {"aq_acquire", (PyCFunction)Wrapper_acquire_method, 
+  {"aq_acquire", (PyCFunction)Wrapper_acquire_method,
    METH_VARARGS|METH_KEYWORDS,
    "Get an attribute, acquiring it if necessary"},
   {"aq_inContextOf", (PyCFunction)Wrapper_inContextOf, METH_VARARGS,
@@ -1228,15 +1286,15 @@
 }
 
 static struct PyMethodDef Acquirer_methods[] = {
-  {"__of__",(PyCFunction)acquire_of, METH_VARARGS, 
+  {"__of__",(PyCFunction)acquire_of, METH_VARARGS,
    "__of__(context) -- return the object in a context"},
-  
+
   {NULL,		NULL}		/* sentinel */
 };
 
 static struct PyMethodDef Xaq_methods[] = {
   {"__of__",(PyCFunction)xaq_of, METH_VARARGS,""},
-  
+
   {NULL,		NULL}		/* sentinel */
 };
 
@@ -1244,26 +1302,26 @@
 capi_aq_acquire(PyObject *self, PyObject *name, PyObject *filter,
 	PyObject *extra, int explicit, PyObject *defalt, int containment)
 {
-  
+
   PyObject *result;
 
   if (filter==Py_None) filter=0;
 
   /* We got a wrapped object, so business as usual */
-  if (isWrapper(self)) 
+  if (isWrapper(self))
     return Wrapper_findattr(
 	      WRAPPER(self), name, filter, extra, OBJECT(self),1,
-	      explicit || 
+	      explicit ||
 	      WRAPPER(self)->ob_type==(PyTypeObject*)&Wrappertype,
 	      explicit, containment);
-  
+
   /* Not wrapped and no filter, so just getattr */
   if (! filter) return PyObject_GetAttr(self, name);
 
   /* Crap, we've got to construct a wrapper so we can use Wrapper_findattr */
-  UNLESS (self=newWrapper(self, NULL, (PyTypeObject*)&Wrappertype)) 
+  UNLESS (self=newWrapper(self, NULL, (PyTypeObject*)&Wrappertype))
     return NULL;
-  
+
   result=Wrapper_findattr(WRAPPER(self), name, filter, extra, OBJECT(self),
 			   1, 1, explicit, containment);
 
@@ -1298,8 +1356,8 @@
 {
   PyObject *result = NULL;
   /* We got a wrapped object, so business as usual */
-  if (isWrapper(self)) 
-    result=Wrapper_findattr(WRAPPER(self), name, 0, 0, OBJECT(self), 1, 1, 1, 
+  if (isWrapper(self))
+    result=Wrapper_findattr(WRAPPER(self), name, 0, 0, OBJECT(self), 1, 1, 1,
 		       containment);
   else
     result=PyObject_GetAttr(self, name);
@@ -1310,7 +1368,7 @@
       result=defalt;
       Py_INCREF(result);
     }
-  
+
   return result;
 }
 
@@ -1320,14 +1378,14 @@
 {
   PyObject *self, *name, *defalt=0;
   int containment=0;
-  
-  UNLESS (PyArg_ParseTuple(args, "OO|Oi", 
+
+  UNLESS (PyArg_ParseTuple(args, "OO|Oi",
 			   &self, &name, &defalt, &containment
 			   )) return NULL;
   return capi_aq_get(self, name, defalt, containment);
 }
 
-static int 
+static int
 capi_aq_iswrapper(PyObject *self) {
 	return isWrapper(self);
 }
@@ -1336,12 +1394,12 @@
 capi_aq_base(PyObject *self)
 {
   PyObject *result;
-  if (! isWrapper(self)) 
+  if (! isWrapper(self))
     {
       Py_INCREF(self);
       return self;
     }
-  
+
   if (WRAPPER(self)->obj)
     {
       result=WRAPPER(self)->obj;
@@ -1388,12 +1446,12 @@
 capi_aq_self(PyObject *self)
 {
   PyObject *result;
-  if (! isWrapper(self)) 
+  if (! isWrapper(self))
     {
       Py_INCREF(self);
       return self;
     }
-  
+
   if (WRAPPER(self)->obj) result=WRAPPER(self)->obj;
   else result=Py_None;
 
@@ -1413,7 +1471,7 @@
 capi_aq_inner(PyObject *self)
 {
   PyObject *result;
-  if (! isWrapper(self)) 
+  if (! isWrapper(self))
     {
       Py_INCREF(self);
       return self;
@@ -1422,7 +1480,7 @@
   if (WRAPPER(self)->obj)
     {
       result=WRAPPER(self)->obj;
-      while (isWrapper(result) && WRAPPER(result)->obj) 
+      while (isWrapper(result) && WRAPPER(result)->obj)
 	{
 	  self=result;
 	  result=WRAPPER(result)->obj;
@@ -1463,7 +1521,7 @@
 	      if (PyList_Append(result,OBJECT(self)) < 0)
 		goto err;
 	    }
-	  if (WRAPPER(self)->container) 
+	  if (WRAPPER(self)->container)
 	    {
 	      self=WRAPPER(self)->container;
 	      continue;
@@ -1475,7 +1533,7 @@
 
       break;
     }
-  
+
   return result;
 err:
   Py_DECREF(result);
@@ -1495,7 +1553,7 @@
 }
 
 static struct PyMethodDef methods[] = {
-  {"aq_acquire", (PyCFunction)module_aq_acquire, METH_VARARGS|METH_KEYWORDS, 
+  {"aq_acquire", (PyCFunction)module_aq_acquire, METH_VARARGS|METH_KEYWORDS,
    "aq_acquire(ob, name [, filter, extra, explicit]) -- "
    "Get an attribute, acquiring it if necessary"
   },
@@ -1503,16 +1561,16 @@
    "aq_get(ob, name [, default]) -- "
    "Get an attribute, acquiring it if necessary."
   },
-  {"aq_base", (PyCFunction)module_aq_base, METH_VARARGS, 
+  {"aq_base", (PyCFunction)module_aq_base, METH_VARARGS,
    "aq_base(ob) -- Get the object unwrapped"},
-  {"aq_parent", (PyCFunction)module_aq_parent, METH_VARARGS, 
+  {"aq_parent", (PyCFunction)module_aq_parent, METH_VARARGS,
    "aq_parent(ob) -- Get the parent of an object"},
-  {"aq_self", (PyCFunction)module_aq_self, METH_VARARGS, 
+  {"aq_self", (PyCFunction)module_aq_self, METH_VARARGS,
    "aq_self(ob) -- Get the object with the outermost wrapper removed"},
-  {"aq_inner", (PyCFunction)module_aq_inner, METH_VARARGS, 
+  {"aq_inner", (PyCFunction)module_aq_inner, METH_VARARGS,
    "aq_inner(ob) -- "
    "Get the object with alll but the innermost wrapper removed"},
-  {"aq_chain", (PyCFunction)module_aq_chain, METH_VARARGS, 
+  {"aq_chain", (PyCFunction)module_aq_chain, METH_VARARGS,
    "aq_chain(ob [, containment]) -- "
    "Get a list of objects in the acquisition environment"},
   {NULL,	NULL}
diff -urN Zope-2.7.2-rc1.orig/lib/Components/ExtensionClass/test/test_dynamic_acquisition.py Zope-2.7.2-rc1/lib/Components/ExtensionClass/test/test_dynamic_acquisition.py
--- Zope-2.7.2-rc1.orig/lib/Components/ExtensionClass/test/test_dynamic_acquisition.py	1970-01-01 01:00:00.000000000 +0100
+++ Zope-2.7.2-rc1/lib/Components/ExtensionClass/test/test_dynamic_acquisition.py	2005-02-01 01:03:50.469629584 +0100
@@ -0,0 +1,94 @@
+##############################################################################
+#
+# Copyright (c) 1996-2002 Zope Corporation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.0 (ZPL).  A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE
+#
+##############################################################################
+from ExtensionClass import Base
+import Acquisition
+
+def log(message):
+    print message
+    return message
+    
+class B(Acquisition.Implicit):
+    color='red'
+    def _aq_dynamic(self, attr):
+        if attr == 'bonjour': return None
+        return lambda: log(self.__class__.__name__ + ':' + attr)
+
+class A(Acquisition.Implicit):
+    def hi(self):
+        print "%s()" % self.__class__.__name__, self.color
+    def _aq_dynamic(self, attr): return None
+
+
+b=B()
+b.a=A()
+b.a.hi()
+b.a.color='green'
+b.a.hi()
+
+b.a.salut()
+a=A()
+a.b=B()
+a.b.salut()
+
+try:
+  b.a.bonjour()
+except AttributeError: pass
+
+try:
+  a.b.bonjour()
+except AttributeError: pass
+
+try:
+    A().hi()
+    raise 'Program error', 'spam'
+except AttributeError: pass
+
+#
+#   New test for wrapper comparisons.
+#
+foo = b.a
+bar = b.a
+assert( foo == bar )
+c = A()
+b.c = c
+b.c.d = c
+assert( b.c.d == c )
+assert( b.c.d == b.c )
+assert( b.c == c )
+
+
+def checkContext(self, o):
+    # Python equivalent to aq_inContextOf
+    from Acquisition import aq_base, aq_parent, aq_inner
+    subob = self
+    o = aq_base(o)
+    while 1:
+        if aq_base(subob) is o: return 1
+        self = aq_inner(subob)
+        if self is None: break
+        subob = aq_parent(self)
+        if subob is None: break
+
+
+assert checkContext(b.c, b)
+assert not checkContext(b.c, b.a)
+
+assert b.a.aq_inContextOf(b)
+assert b.c.aq_inContextOf(b)
+assert b.c.d.aq_inContextOf(b)
+assert b.c.d.aq_inContextOf(c)
+assert b.c.d.aq_inContextOf(b.c)
+assert not b.c.aq_inContextOf(foo)
+assert not b.c.aq_inContextOf(b.a)
+assert not b.a.aq_inContextOf('somestring')
