Issue 9732: fetch the method resolution order from the type metaclass directly in getattr_static

This commit is contained in:
Michael Foord 2010-11-20 16:40:44 +00:00
parent 6bb9989ae3
commit e516265bbc
3 changed files with 28 additions and 21 deletions

View file

@ -587,26 +587,14 @@ but avoids executing code when it fetches attributes.
that raise AttributeError). It can also return descriptors objects that raise AttributeError). It can also return descriptors objects
instead of instance members. instead of instance members.
There are several cases that will break `getattr_static` or be handled The only known case that can cause `getattr_static` to trigger code execution,
incorrectly. These are pathological enough not to worry about (i.e. if you do and cause it to return incorrect results (or even break), is where a class uses
any of these then you deserve to have everything break anyway): :data:`~object.__slots__` and provides a `__dict__` member using a property or
descriptor. If you find other cases please report them so they can be fixed
or documented.
* :data:`~object.__dict__` existing (e.g. as a property) but returning the `getattr_static` does not resolve descriptors, for example slot descriptors or
wrong dictionary or even returning something other than a getset descriptors on objects implemented in C. The descriptor object
dictionary
* classes created with :data:`~object.__slots__` that have the `__slots__`
member deleted from the class, or a fake `__slots__` attribute
attached to the instance, or any other monkeying with
`__slots__`
* 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. is returned instead of the underlying attribute.
You can handle these with code like the following. Note that You can handle these with code like the following. Note that

View file

@ -1060,6 +1060,9 @@ def trace(context=1):
_sentinel = object() _sentinel = object()
def _static_getmro(klass):
return type.__dict__['__mro__'].__get__(klass)
def _check_instance(obj, attr): def _check_instance(obj, attr):
instance_dict = {} instance_dict = {}
try: try:
@ -1070,7 +1073,7 @@ def _check_instance(obj, attr):
def _check_class(klass, attr): def _check_class(klass, attr):
for entry in getmro(klass): for entry in _static_getmro(klass):
try: try:
return entry.__dict__[attr] return entry.__dict__[attr]
except KeyError: except KeyError:
@ -1110,7 +1113,7 @@ def getattr_static(obj, attr, default=_sentinel):
if obj is klass: if obj is klass:
# for types we check the metaclass too # for types we check the metaclass too
for entry in getmro(type(klass)): for entry in _static_getmro(type(klass)):
try: try:
return entry.__dict__[attr] return entry.__dict__[attr]
except KeyError: except KeyError:

View file

@ -867,6 +867,22 @@ class TestGetattrStatic(unittest.TestCase):
self.assertEqual(inspect.getattr_static(Something(), 'foo'), 3) self.assertEqual(inspect.getattr_static(Something(), 'foo'), 3)
self.assertEqual(inspect.getattr_static(Something, 'foo'), 3) self.assertEqual(inspect.getattr_static(Something, 'foo'), 3)
def test_mro_as_property(self):
class Meta(type):
@property
def __mro__(self):
return (object,)
class Base(object):
foo = 3
class Something(Base, metaclass=Meta):
pass
self.assertEqual(inspect.getattr_static(Something(), 'foo'), 3)
self.assertEqual(inspect.getattr_static(Something, 'foo'), 3)
def test_main(): def test_main():
run_unittest( run_unittest(
TestDecorators, TestRetrievingSourceCode, TestOneliners, TestBuggyCases, TestDecorators, TestRetrievingSourceCode, TestOneliners, TestBuggyCases,