[Zope-Checkins] SVN: Zope/branches/2.11/ backport fix for LP #360761 (r105349:105351) to Zope 2.11
Andreas Zeidler
az at zitc.de
Thu Oct 29 05:46:00 EDT 2009
Log message for revision 105353:
backport fix for LP #360761 (r105349:105351) to Zope 2.11
Changed:
U Zope/branches/2.11/doc/CHANGES.txt
U Zope/branches/2.11/lib/python/Acquisition/_Acquisition.c
U Zope/branches/2.11/lib/python/Acquisition/tests.py
-=-
Modified: Zope/branches/2.11/doc/CHANGES.txt
===================================================================
--- Zope/branches/2.11/doc/CHANGES.txt 2009-10-29 09:40:08 UTC (rev 105352)
+++ Zope/branches/2.11/doc/CHANGES.txt 2009-10-29 09:45:59 UTC (rev 105353)
@@ -8,6 +8,9 @@
Bugs Fixed
+ - LP #360761 (backported from Acquisition trunk): fix iteration proxy
+ to pass `self` acquisition-wrapped into `__iter__` and `__getitem__`.
+
- LP #414757 (backported from Zope trunk): don't emit a IEndRequestEvent
when clearing a cloned request.
Modified: Zope/branches/2.11/lib/python/Acquisition/_Acquisition.c
===================================================================
--- Zope/branches/2.11/lib/python/Acquisition/_Acquisition.c 2009-10-29 09:40:08 UTC (rev 105352)
+++ Zope/branches/2.11/lib/python/Acquisition/_Acquisition.c 2009-10-29 09:45:59 UTC (rev 105353)
@@ -819,10 +819,35 @@
return c;
}
+/* Support for iteration cannot rely on the internal implementation of
+ `PyObject_GetIter`, since the `self` passed into `__iter__` and
+ `__getitem__` should be acquisition-wrapped (also see LP 360761): The
+ wrapper obviously supports the iterator protocol so simply calling
+ `PyObject_GetIter(OBJECT(self))` results in an infinite recursion.
+ Instead the base object needs to be checked and the wrapper must only
+ be used when actually calling `__getitem__` or setting up a sequence
+ iterator. */
static PyObject *
Wrapper_iter(Wrapper *self)
{
- return PyObject_GetIter(self->obj);
+ PyObject *obj = self->obj;
+ PyObject *res;
+ if ((res=PyObject_GetAttr(OBJECT(self),py__iter__))) {
+ ASSIGN(res,PyObject_CallFunction(res,NULL,NULL));
+ if (res != NULL && !PyIter_Check(res)) {
+ PyErr_Format(PyExc_TypeError,
+ "iter() returned non-iterator "
+ "of type '%.100s'",
+ res->ob_type->tp_name);
+ Py_DECREF(res);
+ res = NULL;
+ }
+ } else if (PySequence_Check(obj)) {
+ ASSIGN(res,PySeqIter_New(OBJECT(self)));
+ } else {
+ res = PyErr_Format(PyExc_TypeError, "iteration over non-sequence");
+ }
+ return res;
}
static PySequenceMethods Wrapper_as_sequence = {
Modified: Zope/branches/2.11/lib/python/Acquisition/tests.py
===================================================================
--- Zope/branches/2.11/lib/python/Acquisition/tests.py 2009-10-29 09:40:08 UTC (rev 105352)
+++ Zope/branches/2.11/lib/python/Acquisition/tests.py 2009-10-29 09:45:59 UTC (rev 105353)
@@ -1719,8 +1719,9 @@
iterating...
[42]
- Finally let's check that https://bugs.launchpad.net/zope2/+bug/360761
- has been fixed:
+ Next let's check that the wrapper's __iter__ proxy falls back
+ to using the object's __getitem__ if it has no __iter__. See
+ https://bugs.launchpad.net/zope2/+bug/360761 .
>>> class C(Acquisition.Implicit):
... l=[1,2,3]
@@ -1739,6 +1740,59 @@
>>> list(c2)
[1, 2, 3]
+ The __iter__proxy should also pass the wrapped object as self to
+ the __iter__ of objects defining __iter__::
+
+ >>> class C(Acquisition.Implicit):
+ ... def __iter__(self):
+ ... print 'iterating...'
+ ... for i in range(5):
+ ... yield i, self.aq_parent.name
+ >>> c = C()
+ >>> i = Impl()
+ >>> i.c = c
+ >>> i.name = 'i'
+ >>> list(i.c)
+ iterating...
+ [(0, 'i'), (1, 'i'), (2, 'i'), (3, 'i'), (4, 'i')]
+
+ And it should pass the wrapped object as self to
+ the __getitem__ of objects without an __iter__::
+
+ >>> class C(Acquisition.Implicit):
+ ... def __getitem__(self, i):
+ ... return self.aq_parent.l[i]
+ >>> c = C()
+ >>> i = Impl()
+ >>> i.c = c
+ >>> i.l = range(5)
+ >>> list(i.c)
+ [0, 1, 2, 3, 4]
+
+ Finally let's make sure errors are still correctly raised after having
+ to use a modified version of `PyObject_GetIter` for iterator support::
+
+ >>> class C(Acquisition.Implicit):
+ ... pass
+ >>> c = C()
+ >>> i = Impl()
+ >>> i.c = c
+ >>> list(i.c)
+ Traceback (most recent call last):
+ ...
+ TypeError: iteration over non-sequence
+
+ >>> class C(Acquisition.Implicit):
+ ... def __iter__(self):
+ ... return [42]
+ >>> c = C()
+ >>> i = Impl()
+ >>> i.c = c
+ >>> list(i.c)
+ Traceback (most recent call last):
+ ...
+ TypeError: iter() returned non-iterator of type 'list'
+
"""
More information about the Zope-Checkins
mailing list