Commit e516265b authored by Michael Foord's avatar Michael Foord

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

parent 6bb9989a
...@@ -587,26 +587,14 @@ but avoids executing code when it fetches attributes. ...@@ -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
* :data:`~object.__dict__` existing (e.g. as a property) but returning the or documented.
wrong dictionary or even returning something other than a
dictionary `getattr_static` does not resolve descriptors, for example slot descriptors or
* classes created with :data:`~object.__slots__` that have the `__slots__` getset descriptors on objects implemented in C. The descriptor object
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
......
...@@ -1060,6 +1060,9 @@ def trace(context=1): ...@@ -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): ...@@ -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): ...@@ -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:
......
...@@ -867,6 +867,22 @@ class TestGetattrStatic(unittest.TestCase): ...@@ -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,
......
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