diff options
author | Michael Foord <fuzzyman@voidspace.org.uk> | 2010-11-20 15:07:30 (GMT) |
---|---|---|
committer | Michael Foord <fuzzyman@voidspace.org.uk> | 2010-11-20 15:07:30 (GMT) |
commit | 95fc51dfda805c2c1aa7aacf9a100d90c8747ffc (patch) | |
tree | 9666c61139b78e7cc7fcb63eb21aded0f97a3fff /Doc/library/inspect.rst | |
parent | 89197fe93c4a3f3e983721b0325b7bb5613c7e9c (diff) | |
download | cpython-95fc51dfda805c2c1aa7aacf9a100d90c8747ffc.zip cpython-95fc51dfda805c2c1aa7aacf9a100d90c8747ffc.tar.gz cpython-95fc51dfda805c2c1aa7aacf9a100d90c8747ffc.tar.bz2 |
Issue 9732: addition of getattr_static to the inspect module
Diffstat (limited to 'Doc/library/inspect.rst')
-rw-r--r-- | Doc/library/inspect.rst | 67 |
1 files changed, 67 insertions, 0 deletions
diff --git a/Doc/library/inspect.rst b/Doc/library/inspect.rst index 1f5e72e..2f09348 100644 --- a/Doc/library/inspect.rst +++ b/Doc/library/inspect.rst @@ -563,3 +563,70 @@ line. entry in the list represents the caller; the last entry represents where the exception was raised. + +Fetching attributes statically +------------------------------ + +Both :func:`getattr` and :func:`hasattr` can trigger code execution when +fetching or checking for the existence of attributes. Descriptors, like +properties, will be invoked and :meth:`__getattr__` and :meth:`__getattribute__` +may be called. + +For cases where you want passive introspection, like documentation tools, this +can be inconvenient. `getattr_static` has the same signature as :func:`getattr` +but avoids executing code when it fetches attributes. + +.. function:: getattr_static(obj, attr, default=None) + + Retrieve attributes without triggering dynamic lookup via the + descriptor protocol, `__getattr__` or `__getattribute__`. + + Note: this function may not be able to retrieve all attributes + that getattr can fetch (like dynamically created attributes) + and may find attributes that getattr can't (like descriptors + 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__` +* objects that lie about their type by having `__class__` as a + descriptor (`getattr_static` traverses the :term:`MRO` of whatever type + `obj.__class__` returns instead of the real type) +* type objects that lie about their :term:`MRO` + +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. + +You can handle these with code like the following. Note that +for arbitrary getset descriptors invoking these may trigger +code execution:: + + # example code for resolving the builtin descriptor types + class _foo(object): + __slots__ = ['foo'] + + slot_descriptor = type(_foo.foo) + getset_descriptor = type(type(open(__file__)).name) + wrapper_descriptor = type(str.__dict__['__add__']) + descriptor_types = (slot_descriptor, getset_descriptor, wrapper_descriptor) + + result = getattr_static(some_object, 'foo') + if type(result) in descriptor_types: + try: + result = result.__get__() + except AttributeError: + # descriptors can raise AttributeError to + # indicate there is no underlying value + # in which case the descriptor itself will + # have to do + pass |