Skip to content

Commit

Permalink
Fix looking up interfaces implemented/providedBy proxies around built…
Browse files Browse the repository at this point in the history
…in types (in pure-Python.).
  • Loading branch information
jamadden committed May 28, 2015
1 parent 4f3a07f commit 0018bc9
Show file tree
Hide file tree
Showing 3 changed files with 85 additions and 3 deletions.
6 changes: 5 additions & 1 deletion CHANGES.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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)
------------------
Expand Down
33 changes: 32 additions & 1 deletion src/zope/proxy/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@
from zope.interface import moduleProvides
from zope.proxy.interfaces import IProxyIntrospection


moduleProvides(IProxyIntrospection)
__all__ = tuple(IProxyIntrospection)

Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -424,6 +452,9 @@ def __ipow__(self, other, modulus=None):
self._wrapped = pow(self._wrapped, other, modulus)
return self

AbstractPyProxyBase = _ProxyMetaclass(str('AbstractPyProxyBase'), (),
dict(AbstractPyProxyBase.__dict__))

class PyProxyBase(AbstractPyProxyBase):
"""Reference implementation.
"""
Expand Down
49 changes: 48 additions & 1 deletion src/zope/proxy/tests/test_proxy.py
Original file line number Diff line number Diff line change
Expand Up @@ -706,6 +706,53 @@ class Proxy(self._getTargetClass()):
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):
Expand All @@ -727,7 +774,7 @@ def test_string_to_int(self):
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):

Expand Down

0 comments on commit 0018bc9

Please sign in to comment.