[Zope-Checkins] SVN: Zope/branches/philikon-aq-and-__parent__/lib/python/Acquisition/ Step 2: Make aq_acquire aware of __parent__ pointers, even if the object

Philipp von Weitershausen philikon at philikon.de
Mon Nov 20 19:28:45 EST 2006


Log message for revision 71221:
  Step 2: Make aq_acquire aware of __parent__ pointers, even if the object
  isn't acquisition wrapped.
  

Changed:
  _U  Zope/branches/philikon-aq-and-__parent__/lib/python/Acquisition/
  U   Zope/branches/philikon-aq-and-__parent__/lib/python/Acquisition/_Acquisition.c
  U   Zope/branches/philikon-aq-and-__parent__/lib/python/Acquisition/tests.py

-=-

Property changes on: Zope/branches/philikon-aq-and-__parent__/lib/python/Acquisition
___________________________________________________________________
Name: svk:merge
   - bfa16f6a-5b7b-4684-983a-15b8a78f95a3:/local/Acquisition:3236
   + bfa16f6a-5b7b-4684-983a-15b8a78f95a3:/local/Acquisition:3243

Modified: Zope/branches/philikon-aq-and-__parent__/lib/python/Acquisition/_Acquisition.c
===================================================================
--- Zope/branches/philikon-aq-and-__parent__/lib/python/Acquisition/_Acquisition.c	2006-11-21 00:28:33 UTC (rev 71220)
+++ Zope/branches/philikon-aq-and-__parent__/lib/python/Acquisition/_Acquisition.c	2006-11-21 00:28:44 UTC (rev 71221)
@@ -38,7 +38,8 @@
   *py__long__, *py__float__, *py__oct__, *py__hex__,
   *py__getitem__, *py__setitem__, *py__delitem__,
   *py__getslice__, *py__setslice__, *py__delslice__,  *py__contains__,
-  *py__len__, *py__of__, *py__call__, *py__repr__, *py__str__, *py__cmp__;
+  *py__len__, *py__of__, *py__call__, *py__repr__, *py__str__, *py__cmp__,
+  *py__parent__;
 
 static PyObject *Acquired=0;
 
@@ -82,7 +83,8 @@
   INIT_PY_NAME(__repr__);
   INIT_PY_NAME(__str__);
   INIT_PY_NAME(__cmp__);
-  
+  INIT_PY_NAME(__parent__);
+
 #undef INIT_PY_NAME
 }
 
@@ -488,6 +490,7 @@
           Py_XDECREF(r); Py_XDECREF(v); Py_XDECREF(tb);
           r=NULL;
         }
+      /* normal attribute lookup */
       else if ((r=PyObject_GetAttr(self->obj,oname)))
         {
           if (r==Acquired)
@@ -522,7 +525,8 @@
       PyErr_Clear();
     }
 
-  if (sco && (*name != '_' || explicit)) 
+  /* lookup has failed, acquire it from parent */
+  if (sco && (*name != '_' || explicit))
     return Wrapper_acquire(self, oname, filter, extra, orig, explicit, 
                            containment);
 
@@ -535,11 +539,13 @@
                 PyObject *filter, PyObject *extra, PyObject *orig,
                 int explicit, int containment)
 {
-  PyObject *r;
+  PyObject *r, *v, *tb, *__parent__;
   int sob=1, sco=1;
 
   if (self->container)
     {
+      /* if the container has an acquisition wrapper itself, we'll use
+         Wrapper_findattr to progress further */
       if (isWrapper(self->container))
         {
           if (self->obj && isWrapper(self->obj))
@@ -560,8 +566,36 @@
           if (r && has__of__(r)) ASSIGN(r,__of__(r,OBJECT(self)));
           return r;
         }
+      /* if the container has a __parent__ pointer, we create an
+         acquisition wrapper for it accordingly.  Then we can proceed
+         with Wrapper_findattr, just as if the container had an
+         acquisition wrapper in the first place (see above) */
+      else if ((__parent__ = PyObject_GetAttr(self->container, py__parent__)))
+        {
+          ASSIGN(self->container, newWrapper(self->container, __parent__,
+                                             (PyTypeObject*)&Wrappertype));
+          r=Wrapper_findattr((Wrapper*)self->container,
+                             oname, filter, extra, orig, sob, sco, explicit, 
+                             containment);
+          return r;
+        }
+      /* the container is the end of the acquisition chain; if we
+         can't look up the attribute here, we can't look it up at
+         all. */
       else
         {
+          /* we need to clean up the AttributeError from the previous
+             getattr (because it has clearly failed) */
+          /* perhaps it's overkill to only catch  AttributeErrors */
+          PyErr_Fetch(&r,&v,&tb);
+          if (r && (r != PyExc_AttributeError))
+            {
+              PyErr_Restore(r,v,tb);
+              return NULL;
+            }
+          Py_XDECREF(r); Py_XDECREF(v); Py_XDECREF(tb);
+          r=NULL;
+
           if ((r=PyObject_GetAttr(self->container,oname))) {
             if (r == Acquired) {
               Py_DECREF(r);
@@ -1343,8 +1377,7 @@
 capi_aq_acquire(PyObject *self, PyObject *name, PyObject *filter,
         PyObject *extra, int explicit, PyObject *defalt, int containment)
 {
-  
-  PyObject *result;
+  PyObject *result, *__parent__;
 
   if (filter==Py_None) filter=0;
 
@@ -1356,7 +1389,17 @@
               WRAPPER(self)->ob_type==(PyTypeObject*)&Wrappertype,
               explicit, containment);
   
-  /* Not wrapped and no filter, so just getattr */
+  /* Not wrapped; check if we have a __parent__ pointer.  If that's
+     the case, we create a wrapper and pretend it's business as
+     usual */
+  if ((__parent__ = PyObject_GetAttr(self, py__parent__))) {
+    self = newWrapper(self, __parent__, (PyTypeObject*)&Wrappertype);
+    return Wrapper_findattr(
+              WRAPPER(self), name, filter, extra, OBJECT(self), 1, 1,
+              explicit, containment);
+  }
+
+  /* no filter, and no __parent__, so just getattr */
   if (! filter) return PyObject_GetAttr(self, name);
 
   /* Crap, we've got to construct a wrapper so we can use Wrapper_findattr */

Modified: Zope/branches/philikon-aq-and-__parent__/lib/python/Acquisition/tests.py
===================================================================
--- Zope/branches/philikon-aq-and-__parent__/lib/python/Acquisition/tests.py	2006-11-21 00:28:33 UTC (rev 71220)
+++ Zope/branches/philikon-aq-and-__parent__/lib/python/Acquisition/tests.py	2006-11-21 00:28:44 UTC (rev 71221)
@@ -1663,7 +1663,129 @@
 
     """
 
+class Location(object):
+    __parent__ = None
 
+class ECLocation(ExtensionClass.Base):
+    __parent__ = None
+
+def test___parent__no_wrappers():
+    """
+    Acquisition also works with objects that aren't wrappers, as long
+    as they have __parent__ pointers.  Let's take a hierarchy like
+    z --isParent--> y --isParent--> x:
+
+      >>> x = Location()
+      >>> y = Location()
+      >>> z = Location()
+      >>> x.__parent__ = y
+      >>> y.__parent__ = z
+
+    and some attributes that we want to acquire:
+
+      >>> x.hello = 'world'
+      >>> y.foo = 42
+      >>> z.foo = 43  # this should not be found
+      >>> z.bar = 3.145
+
+    ``aq_acquire`` works we know it from implicit/acquisition wrappers:
+
+      >>> Acquisition.aq_acquire(x, 'hello')
+      'world'
+      >>> Acquisition.aq_acquire(x, 'foo')
+      42
+      >>> Acquisition.aq_acquire(x, 'bar')
+      3.145
+
+    TODO aq_parent, aq_chain
+    """
+
+def test_implicit_wrapper_as___parent__():
+    """
+    Let's do the same test again, only now not all objects are of the
+    same kind and link to each other via __parent__ pointers.  The
+    root is a stupid ExtensionClass object:
+
+      >>> class Root(ExtensionClass.Base):
+      ...     bar = 3.145
+      >>> z = Root()
+
+    The intermediate parent is an object that supports implicit
+    acquisition.  We bind it to the root via the __of__ protocol:
+
+      >>> class ImplWrapper(Acquisition.Implicit):
+      ...     foo = 42
+      >>> y = ImplWrapper().__of__(z)
+
+    The child object is again a simple object with a simple __parent__
+    pointer:
+
+      >>> x = Location()
+      >>> x.hello = 'world'
+      >>> x.__parent__ = y
+
+    ``aq_acquire`` works as expected from implicit/acquisition
+    wrappers:
+
+      >>> Acquisition.aq_acquire(x, 'hello')
+      'world'
+      >>> Acquisition.aq_acquire(x, 'foo')
+      42
+      >>> Acquisition.aq_acquire(x, 'bar')
+      3.145
+
+    Note that also the (implicit) acquisition wrapper has a __parent__
+    pointer, which is automatically computed from the acquisition
+    container (it's identical to aq_parent):
+
+      >>> y.__parent__ is z
+      True
+
+    TODO aq_parent, aq_chain
+    """
+
+def test_explicit_wrapper_as___parent__():
+    """
+    Let's do this test yet another time, with an explicit wrapper:
+
+      >>> class Root(ExtensionClass.Base):
+      ...     bar = 3.145
+      >>> z = Root()
+
+    The intermediate parent is an object that supports implicit
+    acquisition.  We bind it to the root via the __of__ protocol:
+
+      >>> class ExplWrapper(Acquisition.Explicit):
+      ...     foo = 42
+      >>> y = ExplWrapper().__of__(z)
+
+    The child object is again a simple object with a simple __parent__
+    pointer:
+
+      >>> x = Location()
+      >>> x.hello = 'world'
+      >>> x.__parent__ = y
+
+    ``aq_acquire`` works as expected from implicit/acquisition
+    wrappers:
+
+      >>> Acquisition.aq_acquire(x, 'hello')
+      'world'
+      >>> Acquisition.aq_acquire(x, 'foo')
+      42
+      >>> Acquisition.aq_acquire(x, 'bar')
+      3.145
+
+    Note that also the (implicit) acquisition wrapper has a __parent__
+    pointer, which is automatically computed from the acquisition
+    container (it's identical to aq_parent):
+
+      >>> y.__parent__ is z
+      True
+
+    TODO aq_parent, aq_chain
+    """
+
 import unittest
 from zope.testing.doctest import DocTestSuite
 



More information about the Zope-Checkins mailing list