Commit 4f3a07f7 authored by Jason Madden's avatar Jason Madden

Make subclasses of ProxyBase delegate __module__ to the wrapped object. Also...

Make subclasses of ProxyBase delegate __module__ to the wrapped object. Also fix differences between the C and Python implementations in handling __module__, whether or not a subclass is involved.
parent 476893f7
......@@ -4,7 +4,8 @@ Changes
4.1.6 (unreleased)
------------------
- TBD
- Made subclasses of ProxyBase properly delegate ``__module__`` to the
wrapped object.
4.1.5 (2015-05-19)
------------------
......
......@@ -131,9 +131,11 @@ class AbstractPyProxyBase(object):
if name == '_wrapped':
return _get_wrapped(self)
if name == '__class__':
# __class__ is special cased in the C implementation
return _get_wrapped(self).__class__
if name in ('__class__', '__module__'):
# __class__ and __module__ are special cased in the C
# implementation, because we will always find them on the
# type of this object if we are being subclassed
return getattr(_get_wrapped(self), name)
if name in ('__reduce__', '__reduce_ex__'):
# These things we specifically override and no one
......
......@@ -274,7 +274,9 @@ wrap_getattro(PyObject *self, PyObject *name)
maybe_special_name = name_as_string[0] == '_' && name_as_string[1] == '_';
if (!(maybe_special_name && strcmp(name_as_string, "__class__") == 0)) {
if (!(maybe_special_name
&& (strcmp(name_as_string, "__class__") == 0
|| strcmp(name_as_string, "__module__") == 0))) {
descriptor = WrapperType_Lookup(self->ob_type, name);
......
......@@ -729,13 +729,95 @@ class PyProxyBaseTestCase(unittest.TestCase):
from zope.proxy import PyProxyBase
self.assertNotEqual(self._getTargetClass, PyProxyBase)
class ProxyBaseTestCase(PyProxyBaseTestCase):
def _getTargetClass(self):
from zope.proxy import ProxyBase
return ProxyBase
class Test_py__module(unittest.TestCase):
# Historically, proxying __module__ has been troublesome,
# especially when subclasses of the proxy class are involved;
# there was also a discrepancy between the C and Python implementations
# in that the C implementation only failed Test_subclass__module:test__module__in_instance,
# whereas the Python version failed every test.
# See https://github.com/zopefoundation/zopetoolkit/pull/2#issuecomment-106075153
# and https://github.com/zopefoundation/zope.proxy/pull/8
def _getTargetClass(self):
from zope.proxy import PyProxyBase
return PyProxyBase
def _makeProxy(self, obj):
from zope.proxy import PyProxyBase
return self._getTargetClass()(obj)
def _check_module(self, obj, expected):
self.assertEqual(expected, obj.__module__)
self.assertEqual(expected, self._makeProxy(obj).__module__)
def test__module__in_instance(self):
# We can find __module__ in an instance dict
class Module(object):
def __init__(self):
self.__module__ = 'module'
self._check_module(Module(), 'module')
def test__module__in_class_instance(self):
# We can find module in an instance of a class
class Module(object):
pass
self._check_module(Module(), __name__)
def test__module__in_class(self):
# We can find module in a class itself
class Module(object):
pass
self._check_module(Module, __name__)
def test__module_in_eq_transitive(self):
# An object that uses __module__ in its implementation
# of __eq__ is transitively equal to a proxy of itself.
# Seen with zope.interface.interface.Interface
class Module(object):
def __init__(self):
self.__module__ = __name__
def __eq__(self, other):
return self.__module__ == other.__module__
module = Module()
# Sanity checks
self.assertEqual(module, module)
self.assertEqual(module.__module__, __name__)
# transitive equal
self.assertEqual(module, self._makeProxy(module))
self.assertEqual(self._makeProxy(module), module)
class Test__module(Test_py__module):
def _getTargetClass(self):
from zope.proxy import ProxyBase
return ProxyBase
class Test_py_subclass__module(Test_py__module):
def _getTargetClass(self):
class ProxySubclass(super(Test_py_subclass__module,self)._getTargetClass()):
pass
return ProxySubclass
class Test_subclass__module(Test__module):
def _getTargetClass(self):
class ProxySubclass(super(Test_subclass__module,self)._getTargetClass()):
pass
return ProxySubclass
class Test_py_getProxiedObject(unittest.TestCase):
def _callFUT(self, *args):
......@@ -1270,4 +1352,8 @@ def test_suite():
unittest.makeSuite(Test_removeAllProxies),
unittest.makeSuite(Test_ProxyIterator),
unittest.makeSuite(Test_nonOverridable),
unittest.makeSuite(Test_py__module),
unittest.makeSuite(Test__module),
unittest.makeSuite(Test_py_subclass__module),
unittest.makeSuite(Test_subclass__module),
))
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