summaryrefslogtreecommitdiffstats
path: root/Lib/test
diff options
context:
space:
mode:
authorEthan Furman <ethan@stoneleaf.us>2013-10-18 07:27:39 (GMT)
committerEthan Furman <ethan@stoneleaf.us>2013-10-18 07:27:39 (GMT)
commit63c141cacd0d655647430fe4b6a10c22d355aef2 (patch)
tree74d992cc1102dd4a538ed9d027e7b3aec0d045af /Lib/test
parent0e0cd46227bb3e95cc6395ef514bf13f0d30c668 (diff)
downloadcpython-63c141cacd0d655647430fe4b6a10c22d355aef2.zip
cpython-63c141cacd0d655647430fe4b6a10c22d355aef2.tar.gz
cpython-63c141cacd0d655647430fe4b6a10c22d355aef2.tar.bz2
Close #19030: inspect.getmembers and inspect.classify_class_attrs
Order of search is now: 1. Try getattr 2. If that throws an exception, check __dict__ directly 3. If still not found, walk the mro looking for the eldest class that has the attribute (e.g. things returned by __getattr__) 4. If none of that works (e.g. due to a buggy __dir__, __getattr__, etc. method or missing __slot__ attribute), ignore the attribute entirely.
Diffstat (limited to 'Lib/test')
-rw-r--r--Lib/test/test_inspect.py100
1 files changed, 89 insertions, 11 deletions
diff --git a/Lib/test/test_inspect.py b/Lib/test/test_inspect.py
index fb5fe17..fb6aa6a 100644
--- a/Lib/test/test_inspect.py
+++ b/Lib/test/test_inspect.py
@@ -126,7 +126,6 @@ class TestPredicates(IsTestBase):
def test_get_slot_members(self):
class C(object):
__slots__ = ("a", "b")
-
x = C()
x.a = 42
members = dict(inspect.getmembers(x))
@@ -469,13 +468,13 @@ class _BrokenDataDescriptor(object):
A broken data descriptor. See bug #1785.
"""
def __get__(*args):
- raise AssertionError("should not __get__ data descriptors")
+ raise AttributeError("broken data descriptor")
def __set__(*args):
raise RuntimeError
def __getattr__(*args):
- raise AssertionError("should not __getattr__ data descriptors")
+ raise AttributeError("broken data descriptor")
class _BrokenMethodDescriptor(object):
@@ -483,10 +482,10 @@ class _BrokenMethodDescriptor(object):
A broken method descriptor. See bug #1785.
"""
def __get__(*args):
- raise AssertionError("should not __get__ method descriptors")
+ raise AttributeError("broken method descriptor")
def __getattr__(*args):
- raise AssertionError("should not __getattr__ method descriptors")
+ raise AttributeError("broken method descriptor")
# Helper for testing classify_class_attrs.
@@ -656,13 +655,77 @@ class TestClassesAndFunctions(unittest.TestCase):
if isinstance(builtin, type):
inspect.classify_class_attrs(builtin)
- def test_classify_VirtualAttribute(self):
- class VA:
+ def test_classify_DynamicClassAttribute(self):
+ class Meta(type):
+ def __getattr__(self, name):
+ if name == 'ham':
+ return 'spam'
+ return super().__getattr__(name)
+ class VA(metaclass=Meta):
@types.DynamicClassAttribute
def ham(self):
return 'eggs'
- should_find = inspect.Attribute('ham', 'data', VA, VA.__dict__['ham'])
- self.assertIn(should_find, inspect.classify_class_attrs(VA))
+ should_find_dca = inspect.Attribute('ham', 'data', VA, VA.__dict__['ham'])
+ self.assertIn(should_find_dca, inspect.classify_class_attrs(VA))
+ should_find_ga = inspect.Attribute('ham', 'data', VA, 'spam')
+ self.assertIn(should_find_ga, inspect.classify_class_attrs(VA))
+
+ def test_classify_VirtualAttribute(self):
+ class Meta(type):
+ def __dir__(cls):
+ return ['__class__', '__module__', '__name__', 'BOOM']
+ def __getattr__(self, name):
+ if name =='BOOM':
+ return 42
+ return super().__getattr(name)
+ class Class(metaclass=Meta):
+ pass
+ should_find = inspect.Attribute('BOOM', 'data', Class, 42)
+ self.assertIn(should_find, inspect.classify_class_attrs(Class))
+
+ def test_classify_VirtualAttribute_multi_classes(self):
+ class Meta1(type):
+ def __dir__(cls):
+ return ['__class__', '__module__', '__name__', 'one']
+ def __getattr__(self, name):
+ if name =='one':
+ return 1
+ return super().__getattr__(name)
+ class Meta2(type):
+ def __dir__(cls):
+ return ['__class__', '__module__', '__name__', 'two']
+ def __getattr__(self, name):
+ if name =='two':
+ return 2
+ return super().__getattr__(name)
+ class Meta3(Meta1, Meta2):
+ def __dir__(cls):
+ return list(sorted(set(['__class__', '__module__', '__name__', 'three'] +
+ Meta1.__dir__(cls) + Meta2.__dir__(cls))))
+ def __getattr__(self, name):
+ if name =='three':
+ return 3
+ return super().__getattr__(name)
+ class Class1(metaclass=Meta1):
+ pass
+ class Class2(Class1, metaclass=Meta3):
+ pass
+
+ should_find1 = inspect.Attribute('one', 'data', Class1, 1)
+ should_find2 = inspect.Attribute('two', 'data', Class2, 2)
+ should_find3 = inspect.Attribute('three', 'data', Class2, 3)
+ cca = inspect.classify_class_attrs(Class2)
+ for sf in (should_find1, should_find2, should_find3):
+ self.assertIn(sf, cca)
+
+ def test_classify_class_attrs_with_buggy_dir(self):
+ class M(type):
+ def __dir__(cls):
+ return ['__class__', '__name__', 'missing']
+ class C(metaclass=M):
+ pass
+ attrs = [a[0] for a in inspect.classify_class_attrs(C)]
+ self.assertNotIn('missing', attrs)
def test_getmembers_descriptors(self):
class A(object):
@@ -708,11 +771,26 @@ class TestClassesAndFunctions(unittest.TestCase):
self.assertIn(('f', b.f), inspect.getmembers(b, inspect.ismethod))
def test_getmembers_VirtualAttribute(self):
- class A:
+ class M(type):
+ def __getattr__(cls, name):
+ if name == 'eggs':
+ return 'scrambled'
+ return super().__getattr__(name)
+ class A(metaclass=M):
@types.DynamicClassAttribute
def eggs(self):
return 'spam'
- self.assertIn(('eggs', A.__dict__['eggs']), inspect.getmembers(A))
+ self.assertIn(('eggs', 'scrambled'), inspect.getmembers(A))
+ self.assertIn(('eggs', 'spam'), inspect.getmembers(A()))
+
+ def test_getmembers_with_buggy_dir(self):
+ class M(type):
+ def __dir__(cls):
+ return ['__class__', '__name__', 'missing']
+ class C(metaclass=M):
+ pass
+ attrs = [a[0] for a in inspect.getmembers(C)]
+ self.assertNotIn('missing', attrs)
_global_ref = object()