]> granicus.if.org Git - python/commitdiff
Issue 9732: fetch the method resolution order from the type metaclass directly in...
authorMichael Foord <fuzzyman@voidspace.org.uk>
Sat, 20 Nov 2010 16:40:44 +0000 (16:40 +0000)
committerMichael Foord <fuzzyman@voidspace.org.uk>
Sat, 20 Nov 2010 16:40:44 +0000 (16:40 +0000)
Doc/library/inspect.rst
Lib/inspect.py
Lib/test/test_inspect.py

index 32e56e5917a61458ad7b5c70a6e9529644765076..9a068dab312352c182964a2c3c025ffdee8b3818 100644 (file)
@@ -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
index 97e99aa0cee6e861f31958d213edaf6734046684..2f05829b3c93e8fdc9335a5ca250cd5799112d1d 100644 (file)
@@ -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:
index e320c68b2be6995d8c02baa020b0cd32335ed13e..b3e131ca102e651f8ec4d9a771057f7b4d01b314 100644 (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)
 
+    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,