diff --git a/Doc/library/inspect.rst b/Doc/library/inspect.rst index 2f0934840f0..32e56e5917a 100644 --- a/Doc/library/inspect.rst +++ b/Doc/library/inspect.rst @@ -598,11 +598,13 @@ any of these then you deserve to have everything break anyway): member deleted from the class, or a fake `__slots__` attribute attached to the instance, or any other monkeying with `__slots__` -* objects that lie about their type by having `__class__` as a - descriptor (`getattr_static` traverses the :term:`MRO` of whatever type - `obj.__class__` returns instead of the real type) * type objects that lie about their :term:`MRO` +.. note:: + + Classes that override :data:`~object.__mro__` as a property will have this + code executed by `getattr_static`. + Descriptors are not resolved (for example slot descriptors or getset descriptors on objects implemented in C). The descriptor is returned instead of the underlying attribute. diff --git a/Lib/inspect.py b/Lib/inspect.py index 57d8c724d60..97e99aa0cee 100644 --- a/Lib/inspect.py +++ b/Lib/inspect.py @@ -1092,7 +1092,7 @@ def getattr_static(obj, attr, default=_sentinel): instance_result = _sentinel if not isinstance(obj, type): instance_result = _check_instance(obj, attr) - klass = obj.__class__ + klass = type(obj) else: klass = obj diff --git a/Lib/test/test_inspect.py b/Lib/test/test_inspect.py index 88c57d31e1e..e320c68b2be 100644 --- a/Lib/test/test_inspect.py +++ b/Lib/test/test_inspect.py @@ -855,6 +855,18 @@ class TestGetattrStatic(unittest.TestCase): self.assertEqual(inspect.getattr_static(Thing, 'd'), meta.__dict__['d']) + def test_class_as_property(self): + class Base(object): + foo = 3 + + class Something(Base): + @property + def __class__(self): + return object + + self.assertEqual(inspect.getattr_static(Something(), 'foo'), 3) + self.assertEqual(inspect.getattr_static(Something, 'foo'), 3) + def test_main(): run_unittest( TestDecorators, TestRetrievingSourceCode, TestOneliners, TestBuggyCases,