diff options
author | Jason Madden <jamadden@gmail.com> | 2015-05-28 17:26:28 -0500 |
---|---|---|
committer | Jason Madden <jamadden@gmail.com> | 2015-05-28 17:26:28 -0500 |
commit | 0018bc9c478421537b2c7364e63c98ac55155363 (patch) | |
tree | 82ddbe9aa07f5772771fb51b197f27a1f43a0e3b | |
parent | 4f3a07f7734fd514463af093049f645feb4ba90e (diff) | |
download | zope-proxy-0018bc9c478421537b2c7364e63c98ac55155363.tar.gz |
Fix looking up interfaces implemented/providedBy proxies around builtin types (in pure-Python.).
-rw-r--r-- | CHANGES.rst | 6 | ||||
-rw-r--r-- | src/zope/proxy/__init__.py | 33 | ||||
-rw-r--r-- | src/zope/proxy/tests/test_proxy.py | 49 |
3 files changed, 85 insertions, 3 deletions
diff --git a/CHANGES.rst b/CHANGES.rst index 86bede0..53c6fad 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -5,7 +5,11 @@ Changes ------------------ - Made subclasses of ProxyBase properly delegate ``__module__`` to the - wrapped object. + wrapped object. This fixes some ``zope.interface`` lookups under + PyPy. +- Made the pure-Python implementation of ProxyBase properly report the + ``zope.interface`` interfaces implemented by builtin types like + ``list``. This fixes some ``zope.interface`` lookups under PyPy. 4.1.5 (2015-05-19) ------------------ diff --git a/src/zope/proxy/__init__.py b/src/zope/proxy/__init__.py index 4a38e59..fd3e531 100644 --- a/src/zope/proxy/__init__.py +++ b/src/zope/proxy/__init__.py @@ -21,7 +21,6 @@ import sys from zope.interface import moduleProvides from zope.proxy.interfaces import IProxyIntrospection - moduleProvides(IProxyIntrospection) __all__ = tuple(IProxyIntrospection) @@ -56,6 +55,35 @@ def _get_wrapped(self): """ return super(AbstractPyProxyBase, self).__getattribute__('_wrapped') +class _EmptyInterfaceDescriptor(object): + """A descriptor for the attributes used on the class by the + Python implementation of `zope.interface`. + + When wrapping builtin types, these descriptors prevent the objects + we find in the AbstractPyProxyBase from being used. + """ + + def __get__(self, inst, klass): + raise AttributeError() + + def __set__(self, inst, value): + raise TypeError() + + def __delete__(self, inst): + pass + + def __iter__(self): + return self + + def __next__(self): + raise StopIteration() + next = __next__ + +class _ProxyMetaclass(type): + # The metaclass is applied after the class definition + # for Py2/Py3 compatibility. + __implemented__ = _EmptyInterfaceDescriptor() + class AbstractPyProxyBase(object): """ A reference implementation that cannot be instantiated. Most users @@ -424,6 +452,9 @@ class AbstractPyProxyBase(object): self._wrapped = pow(self._wrapped, other, modulus) return self +AbstractPyProxyBase = _ProxyMetaclass(str('AbstractPyProxyBase'), (), + dict(AbstractPyProxyBase.__dict__)) + class PyProxyBase(AbstractPyProxyBase): """Reference implementation. """ diff --git a/src/zope/proxy/tests/test_proxy.py b/src/zope/proxy/tests/test_proxy.py index 59b0a04..dd11b54 100644 --- a/src/zope/proxy/tests/test_proxy.py +++ b/src/zope/proxy/tests/test_proxy.py @@ -706,6 +706,53 @@ class PyProxyBaseTestCase(unittest.TestCase): self.assertRaises(AttributeError, setattr, proxy, 'attr', 42) self.assertEqual(proxy.attr, "constant value") + def _check_wrapping_builtin_returns_correct_provided_by(self, proxy_class, builtin_type): + # We get the __implemented__ (fallback) of the type, not our own + from zope.interface import Interface + from zope.interface import classImplements + from zope.interface import classImplementsOnly + from zope.interface import implementedBy + from zope.interface import providedBy + from zope.interface import implementedBy + + # Set up the builtin interface + class IFoo(Interface): + pass + impl_before = list(implementedBy(builtin_type)) + classImplements(builtin_type, IFoo) + + builtin = builtin_type() + self.assertTrue(IFoo in list(providedBy(builtin))) + + try: + proxy_instance = proxy_class(builtin) + provided_instance = providedBy(proxy_instance) + proxy_type = proxy_class(builtin_type) + provided_type = implementedBy(proxy_type) + # The asserts must be before we remove the interface + # because there's a single object that gets mutated + self.assertTrue(IFoo in list(provided_instance)) + self.assertTrue(IFoo in list(provided_type)) + finally: + classImplementsOnly(builtin_type, *impl_before) + + def test_wrapping_builtin_type_returns_correct_provided_by(self): + self._check_wrapping_builtin_returns_correct_provided_by(self._getTargetClass(), list) + + def _check_wrapping_builtin_with_subclass_returns_correct_provided_by(self, builtin_type): + class Proxy(self._getTargetClass()): + pass + + self._check_wrapping_builtin_returns_correct_provided_by(Proxy, builtin_type) + # Our new class did not gain an __implemented__ attribute, unless we're + # the pure-python version + if hasattr(Proxy, '__implemented__'): + from zope.proxy import PyProxyBase + self.assertTrue(self._getTargetClass() is PyProxyBase) + + def test_wrapping_builtin_with_subclass_returns_correct_provided_by(self): + self._check_wrapping_builtin_with_subclass_returns_correct_provided_by(list) + def test_method_in_proxy_subclass(self): class Proxy(self._getTargetClass()): def __getitem__(self, k): @@ -727,7 +774,7 @@ class PyProxyBaseTestCase(unittest.TestCase): self.assertEqual(14, int(proxy)) except TypeError: from zope.proxy import PyProxyBase - self.assertNotEqual(self._getTargetClass, PyProxyBase) + self.assertNotEqual(self._getTargetClass(), PyProxyBase) class ProxyBaseTestCase(PyProxyBaseTestCase): |