mirror of
https://github.com/python/cpython.git
synced 2025-08-03 08:34:29 +00:00
Issue 9732: addition of getattr_static to the inspect module
This commit is contained in:
parent
89197fe93c
commit
95fc51dfda
6 changed files with 295 additions and 2 deletions
|
@ -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
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue