Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit e516265

Browse files
author
Michael Foord
committed
Issue 9732: fetch the method resolution order from the type metaclass directly in getattr_static
1 parent 6bb9989 commit e516265

3 files changed

Lines changed: 29 additions & 22 deletions

File tree

Doc/library/inspect.rst

Lines changed: 8 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -587,26 +587,14 @@ but avoids executing code when it fetches attributes.
587587
that raise AttributeError). It can also return descriptors objects
588588
instead of instance members.
589589

590-
There are several cases that will break `getattr_static` or be handled
591-
incorrectly. These are pathological enough not to worry about (i.e. if you do
592-
any of these then you deserve to have everything break anyway):
593-
594-
* :data:`~object.__dict__` existing (e.g. as a property) but returning the
595-
wrong dictionary or even returning something other than a
596-
dictionary
597-
* classes created with :data:`~object.__slots__` that have the `__slots__`
598-
member deleted from the class, or a fake `__slots__` attribute
599-
attached to the instance, or any other monkeying with
600-
`__slots__`
601-
* type objects that lie about their :term:`MRO`
602-
603-
.. note::
604-
605-
Classes that override :data:`~object.__mro__` as a property will have this
606-
code executed by `getattr_static`.
607-
608-
Descriptors are not resolved (for example slot descriptors or
609-
getset descriptors on objects implemented in C). The descriptor
590+
The only known case that can cause `getattr_static` to trigger code execution,
591+
and cause it to return incorrect results (or even break), is where a class uses
592+
:data:`~object.__slots__` and provides a `__dict__` member using a property or
593+
descriptor. If you find other cases please report them so they can be fixed
594+
or documented.
595+
596+
`getattr_static` does not resolve descriptors, for example slot descriptors or
597+
getset descriptors on objects implemented in C. The descriptor object
610598
is returned instead of the underlying attribute.
611599

612600
You can handle these with code like the following. Note that

Lib/inspect.py

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1060,6 +1060,9 @@ def trace(context=1):
10601060

10611061
_sentinel = object()
10621062

1063+
def _static_getmro(klass):
1064+
return type.__dict__['__mro__'].__get__(klass)
1065+
10631066
def _check_instance(obj, attr):
10641067
instance_dict = {}
10651068
try:
@@ -1070,7 +1073,7 @@ def _check_instance(obj, attr):
10701073

10711074

10721075
def _check_class(klass, attr):
1073-
for entry in getmro(klass):
1076+
for entry in _static_getmro(klass):
10741077
try:
10751078
return entry.__dict__[attr]
10761079
except KeyError:
@@ -1110,7 +1113,7 @@ def getattr_static(obj, attr, default=_sentinel):
11101113

11111114
if obj is klass:
11121115
# for types we check the metaclass too
1113-
for entry in getmro(type(klass)):
1116+
for entry in _static_getmro(type(klass)):
11141117
try:
11151118
return entry.__dict__[attr]
11161119
except KeyError:

Lib/test/test_inspect.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -867,6 +867,22 @@ def __class__(self):
867867
self.assertEqual(inspect.getattr_static(Something(), 'foo'), 3)
868868
self.assertEqual(inspect.getattr_static(Something, 'foo'), 3)
869869

870+
def test_mro_as_property(self):
871+
class Meta(type):
872+
@property
873+
def __mro__(self):
874+
return (object,)
875+
876+
class Base(object):
877+
foo = 3
878+
879+
class Something(Base, metaclass=Meta):
880+
pass
881+
882+
self.assertEqual(inspect.getattr_static(Something(), 'foo'), 3)
883+
self.assertEqual(inspect.getattr_static(Something, 'foo'), 3)
884+
885+
870886
def test_main():
871887
run_unittest(
872888
TestDecorators, TestRetrievingSourceCode, TestOneliners, TestBuggyCases,

0 commit comments

Comments
 (0)