Kaydet (Commit) e516265b authored tarafından Michael Foord's avatar Michael Foord

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

üst 6bb9989a
......@@ -587,26 +587,14 @@ but avoids executing code when it fetches attributes.
that raise AttributeError). It can also return descriptors objects
instead of instance members.
There are several cases that will break `getattr_static` or be handled
incorrectly. These are pathological enough not to worry about (i.e. if you do
any of these then you deserve to have everything break anyway):
* :data:`~object.__dict__` existing (e.g. as a property) but returning the
wrong dictionary or even returning something other than a
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
The only known case that can cause `getattr_static` to trigger code execution,
and cause it to return incorrect results (or even break), is where a class uses
: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.
`getattr_static` does not resolve descriptors, for example slot descriptors or
getset descriptors on objects implemented in C. The descriptor object
is returned instead of the underlying attribute.
You can handle these with code like the following. Note that
......
......@@ -1060,6 +1060,9 @@ def trace(context=1):
_sentinel = object()
def _static_getmro(klass):
return type.__dict__['__mro__'].__get__(klass)
def _check_instance(obj, attr):
instance_dict = {}
try:
......@@ -1070,7 +1073,7 @@ def _check_instance(obj, attr):
def _check_class(klass, attr):
for entry in getmro(klass):
for entry in _static_getmro(klass):
try:
return entry.__dict__[attr]
except KeyError:
......@@ -1110,7 +1113,7 @@ def getattr_static(obj, attr, default=_sentinel):
if obj is klass:
# for types we check the metaclass too
for entry in getmro(type(klass)):
for entry in _static_getmro(type(klass)):
try:
return entry.__dict__[attr]
except KeyError:
......
......@@ -867,6 +867,22 @@ class TestGetattrStatic(unittest.TestCase):
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():
run_unittest(
TestDecorators, TestRetrievingSourceCode, TestOneliners, TestBuggyCases,
......
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