[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