diff options
author | Michael Foord <fuzzyman@voidspace.org.uk> | 2010-11-20 16:40:44 (GMT) |
---|---|---|
committer | Michael Foord <fuzzyman@voidspace.org.uk> | 2010-11-20 16:40:44 (GMT) |
commit | e516265bbc75e4bb7fc2ab6eaeb7ad3aef86a938 (patch) | |
tree | 0436a95d2a4ca7b01c238eb6df780798388321df | |
parent | 6bb9989ae38cd2610e661d6e8899ef58dd9562e3 (diff) | |
download | cpython-e516265bbc75e4bb7fc2ab6eaeb7ad3aef86a938.zip cpython-e516265bbc75e4bb7fc2ab6eaeb7ad3aef86a938.tar.gz cpython-e516265bbc75e4bb7fc2ab6eaeb7ad3aef86a938.tar.bz2 |
Issue 9732: fetch the method resolution order from the type metaclass directly in getattr_static
-rw-r--r-- | Doc/library/inspect.rst | 28 | ||||
-rw-r--r-- | Lib/inspect.py | 7 | ||||
-rw-r--r-- | Lib/test/test_inspect.py | 16 |
3 files changed, 29 insertions, 22 deletions
diff --git a/Doc/library/inspect.rst b/Doc/library/inspect.rst index 32e56e5..9a068da 100644 --- a/Doc/library/inspect.rst +++ b/Doc/library/inspect.rst @@ -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 diff --git a/Lib/inspect.py b/Lib/inspect.py index 97e99aa..2f05829 100644 --- a/Lib/inspect.py +++ b/Lib/inspect.py @@ -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: diff --git a/Lib/test/test_inspect.py b/Lib/test/test_inspect.py index e320c68..b3e131c 100644 --- a/Lib/test/test_inspect.py +++ b/Lib/test/test_inspect.py @@ -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, |