summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJason Madden <jamadden@gmail.com>2015-05-28 17:26:28 -0500
committerJason Madden <jamadden@gmail.com>2015-05-28 17:26:28 -0500
commit0018bc9c478421537b2c7364e63c98ac55155363 (patch)
tree82ddbe9aa07f5772771fb51b197f27a1f43a0e3b
parent4f3a07f7734fd514463af093049f645feb4ba90e (diff)
downloadzope-proxy-0018bc9c478421537b2c7364e63c98ac55155363.tar.gz
Fix looking up interfaces implemented/providedBy proxies around builtin types (in pure-Python.).
-rw-r--r--CHANGES.rst6
-rw-r--r--src/zope/proxy/__init__.py33
-rw-r--r--src/zope/proxy/tests/test_proxy.py49
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):