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 /Lib/inspect.py | |
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 'Lib/inspect.py')
-rw-r--r-- | Lib/inspect.py | 64 |
1 files changed, 64 insertions, 0 deletions
diff --git a/Lib/inspect.py b/Lib/inspect.py index 5f92787..57d8c72 100644 --- a/Lib/inspect.py +++ b/Lib/inspect.py @@ -1054,3 +1054,67 @@ def stack(context=1): def trace(context=1): """Return a list of records for the stack below the current exception.""" return getinnerframes(sys.exc_info()[2], context) + + +# ------------------------------------------------ static version of getattr + +_sentinel = object() + +def _check_instance(obj, attr): + instance_dict = {} + try: + instance_dict = object.__getattribute__(obj, "__dict__") + except AttributeError: + pass + return instance_dict.get(attr, _sentinel) + + +def _check_class(klass, attr): + for entry in getmro(klass): + try: + return entry.__dict__[attr] + except KeyError: + pass + return _sentinel + + +def getattr_static(obj, attr, default=_sentinel): + """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 descriptor objects + instead of instance members in some cases. See the + documentation for details. + """ + instance_result = _sentinel + if not isinstance(obj, type): + instance_result = _check_instance(obj, attr) + klass = obj.__class__ + else: + klass = obj + + klass_result = _check_class(klass, attr) + + if instance_result is not _sentinel and klass_result is not _sentinel: + if (_check_class(type(klass_result), '__get__') is not _sentinel and + _check_class(type(klass_result), '__set__') is not _sentinel): + return klass_result + + if instance_result is not _sentinel: + return instance_result + if klass_result is not _sentinel: + return klass_result + + if obj is klass: + # for types we check the metaclass too + for entry in getmro(type(klass)): + try: + return entry.__dict__[attr] + except KeyError: + pass + if default is not _sentinel: + return default + raise AttributeError(attr) |