Commit b6f423d2 authored by Stephan Richter's avatar Stephan Richter

- Fixed ``PyProxyBase.__iter__()`` to return the result of

  ``PyProxyBase._wrapped.__iter__`` if available, otherwise fall back to
  Python internals. The previous implementation always created a generator.

- Fixed ``PyProxyBase.__setattr__()`` to allow setting of properties on the
  proxy itself. This is needed to properly allow proxy extensions as was
  evidenced int he ``zope.security.decorator`` module.
parent 5a72fd85
......@@ -5,7 +5,13 @@ CHANGES
4.1.2 (unreleased)
------------------
- TBD
- Fixed ``PyProxyBase.__iter__()`` to return the result of
``PyProxyBase._wrapped.__iter__`` if available, otherwise fall back to
Python internals. The previous implementation always created a generator.
- Fixed ``PyProxyBase.__setattr__()`` to allow setting of properties on the
proxy itself. This is needed to properly allow proxy extensions as was
evidenced int he ``zope.security.decorator`` module.
4.1.1 (2012-12-31)
------------------
......@@ -19,7 +25,7 @@ CHANGES
- Replaced use of ``PyCObject`` APIs with equivalent ``PyCapsule`` APIs,
except under Python 2.6.
N.B. This change is an ABI incompatibility under Python 2.7:
extensions built under Python 2.7 against 4.0.x versions of
``zope.proxy`` must be rebuilt.
......@@ -37,7 +43,7 @@ CHANGES
N.B.: the C extension is *not* built under PyPy.
- Added a pure-Python reference / fallback implementations of
``zope.proxy.ProxyBase`` and the proxy module API functions.
``zope.proxy.ProxyBase`` and the proxy module API functions.
N.B.: the pure-Python proxy implements all regular features of
``ProxyBase``; however, it does not exclude access to the wrapped object
......
......@@ -115,7 +115,12 @@ class PyProxyBase(object):
def __setattr__(self, name, value):
if name == '_wrapped':
return super(PyProxyBase, self).__setattr__(name, value)
setattr(self._wrapped, name, value)
try:
mine = super(PyProxyBase, self).__getattribute__(name)
except AttributeError:
return setattr(self._wrapped, name, value)
else:
return object.__setattr__(self, name, value)
def __delattr__(self, name):
if name == '_wrapped':
......@@ -150,8 +155,8 @@ class PyProxyBase(object):
del self._wrapped[key]
def __iter__(self):
for item in self._wrapped:
yield item
# This handles a custom __iter__ and generator support at the same time.
return iter(self._wrapped)
def next(self):
# Called when we wrap an iterator itself.
......
......@@ -215,6 +215,20 @@ class PyProxyBaseTestCase(unittest.TestCase):
w.foo = 1
self.assertEqual(o.foo, 1)
def test___setattr__sets_proxy_property(self):
class Proxy(self._getTargetClass()):
bar = property(
lambda s: s.__dict__.get('_bar'),
lambda s, v: s.__dict__.__setitem__('_bar', v)
)
class Foo(object):
pass
o = Foo()
w = Proxy(o)
w.bar = 43
self.assertEqual(w.bar, 43)
self.assertRaises(AttributeError, getattr, o, 'bar')
def test___delattr___wrapped(self):
class Foo(object):
pass
......@@ -343,6 +357,19 @@ class PyProxyBaseTestCase(unittest.TestCase):
t = tuple(self._makeOne(iter(a)))
self.assertEqual(t, (1, 2, 3))
def test___iter___returns_self_if_defined(self):
# Return the wrapped object itself, if it is an iterator.
class MyIter(object):
def __iter__(self):
return self
def __next__(self):
return 42
next = __next__
myIter = MyIter()
p = self._makeOne(myIter)
self.assertEqual(iter(p), p)
self.assertTrue(isinstance(iter(p), MyIter))
def test___iter___next_when_returned_by_iterable(self):
# Wrap an iterator within the iteration protocol, expecting it
# still to work. PyObject_GetIter() will not be called on the
......
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