Commit 6d3caff5 authored by Jason Madden's avatar Jason Madden

Don't trust descriptors to return themselves. Fixes #1122.

parent 6ddcca21
......@@ -22,6 +22,8 @@ dnspython
idna
# Makes tests faster
psutil
# Used in a test
zope.interface
# For viewing README.rst (restview --long-description),
# CONTRIBUTING.rst, etc.
# https://github.com/mgedmin/restview
......
......@@ -102,6 +102,9 @@ cdef inline dict _local_get_dict(local self)
@cython.locals(entry=_localimpl_dict_entry)
cdef _local__copy_dict_from(local self, _localimpl impl, dict duplicate)
@cython.locals(mro=list, base=type, gets=set, dels=set, set_or_del=set,
type_self=type, type_attr=type,
sets=set)
cdef tuple _local_find_descriptors(local self)
@cython.locals(result=list, local_impl=_localimpl,
......
......@@ -531,9 +531,21 @@ def _local_find_descriptors(self):
dels = set()
set_or_del = set()
sets = set()
mro = list(type_self.mro())
for attr_name in dir(type_self):
attr = getattr(type_self, attr_name)
# Conventionally, descriptors when called on a class
# return themself, but not all do. Notable exceptions are
# in the zope.interface package, where things like __provides__
# return other class attributes. So we can't use getattr, and instead
# walk up the dicts
for base in mro:
if attr_name in base.__dict__:
attr = base.__dict__[attr_name]
break
else:
raise AttributeError(attr_name)
type_attr = type(attr)
if hasattr(type_attr, '__get__'):
gets.add(attr_name)
......
......@@ -68,7 +68,7 @@ class WithGetattr(local):
return 42
return super(WithGetattr, self).__getattr__(name)
class GeventLocalTestCase(greentest.TestCase):
class TestGeventLocal(greentest.TestCase):
# pylint:disable=attribute-defined-outside-init,blacklisted-name
def setUp(self):
......@@ -323,6 +323,36 @@ class GeventLocalTestCase(greentest.TestCase):
self.assertEqual(results,
[((MyLocal, id(x)), {'foo': 42})])
try:
from zope import interface
except ImportError:
interface = None
@greentest.skipIf(interface is None, "Needs zope.interface")
class TestLocalInterface(greentest.TestCase):
__timeout__ = None
@greentest.ignores_leakcheck
def test_provides(self):
# https://github.com/gevent/gevent/issues/1122
# pylint:disable=inherit-non-class
class IFoo(interface.Interface):
pass
@interface.implementer(IFoo)
class Base(object):
pass
class Derived(Base, local):
pass
d = Derived()
p = list(interface.providedBy(d))
self.assertEqual([IFoo], p)
@greentest.skipOnPurePython("Needs C extension")
class TestCExt(greentest.TestCase):
......
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