resync with trunk to get fix for https://bugs.launchpad.net/zope2/+bug/360761

parent af4b9549
Changelog
=========
2.12.4 (unreleased)
2.12.5 (unreleased)
-------------------
2.12.4 (2009-10-29)
-------------------
- Fix iteration proxying to pass `self` acquisition-wrapped into both
`__iter__` as well as `__getitem__` (this fixes
https://bugs.launchpad.net/zope2/+bug/360761).
- Add tests for the __getslice__ proxying, including open-ended slicing.
2.12.3 (2009-08-08)
......
......@@ -17,7 +17,7 @@ import os
from setuptools import setup, find_packages, Extension
setup(name='Acquisition',
version = '2.12.4dev',
version = '2.12.5dev',
url='http://pypi.python.org/pypi/Acquisition',
license='ZPL 2.1',
description="Acquisition is a mechanism that allows objects to obtain "
......
......@@ -1000,10 +1000,35 @@ Wrapper_contains(Wrapper *self, PyObject *v)
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 = {
......
......@@ -1813,8 +1813,9 @@ def test_proxying():
slicing...
True
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]
......@@ -1833,8 +1834,60 @@ def test_proxying():
>>> 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'
"""
class Location(object):
__parent__ = None
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment