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' + """ _______________________________________________ Zope-Checkins maillist - Zope-Checkins@zope.org https://mail.zope.org/mailman/listinfo/zope-checkins