1 """ 2 A static version of getattr. 3 This is a backport of the Python 3 code with a little bit of additional 4 information returned to enable Jedi to make decisions. 5 """ 6 7 import types 8 9 from jedi import debug 10 11 _sentinel = object() 12 13 14 def _check_instance(obj, attr): 15 instance_dict = {} 16 try: 17 instance_dict = object.__getattribute__(obj, "__dict__") 18 except AttributeError: 19 pass 20 return dict.get(instance_dict, attr, _sentinel) 21 22 23 def _check_class(klass, attr): 24 for entry in _static_getmro(klass): 25 if _shadowed_dict(type(entry)) is _sentinel: 26 try: 27 return entry.__dict__[attr] 28 except KeyError: 29 pass 30 return _sentinel 31 32 33 def _is_type(obj): 34 try: 35 _static_getmro(obj) 36 except TypeError: 37 return False 38 return True 39 40 41 def _shadowed_dict(klass): 42 dict_attr = type.__dict__["__dict__"] 43 for entry in _static_getmro(klass): 44 try: 45 class_dict = dict_attr.__get__(entry)["__dict__"] 46 except KeyError: 47 pass 48 else: 49 if not (type(class_dict) is types.GetSetDescriptorType 50 and class_dict.__name__ == "__dict__" 51 and class_dict.__objclass__ is entry): 52 return class_dict 53 return _sentinel 54 55 56 def _static_getmro(klass): 57 mro = type.__dict__['__mro__'].__get__(klass) 58 if not isinstance(mro, (tuple, list)): 59 # There are unfortunately no tests for this, I was not able to 60 # reproduce this in pure Python. However should still solve the issue 61 # raised in GH #1517. 62 debug.warning('mro of %s returned %s, should be a tuple' % (klass, mro)) 63 return () 64 return mro 65 66 67 def _safe_hasattr(obj, name): 68 return _check_class(type(obj), name) is not _sentinel 69 70 71 def _safe_is_data_descriptor(obj): 72 return _safe_hasattr(obj, '__set__') or _safe_hasattr(obj, '__delete__') 73 74 75 def getattr_static(obj, attr, default=_sentinel): 76 """Retrieve attributes without triggering dynamic lookup via the 77 descriptor protocol, __getattr__ or __getattribute__. 78 79 Note: this function may not be able to retrieve all attributes 80 that getattr can fetch (like dynamically created attributes) 81 and may find attributes that getattr can't (like descriptors 82 that raise AttributeError). It can also return descriptor objects 83 instead of instance members in some cases. See the 84 documentation for details. 85 86 Returns a tuple `(attr, is_get_descriptor)`. is_get_descripter means that 87 the attribute is a descriptor that has a `__get__` attribute. 88 """ 89 instance_result = _sentinel 90 if not _is_type(obj): 91 klass = type(obj) 92 dict_attr = _shadowed_dict(klass) 93 if (dict_attr is _sentinel or type(dict_attr) is types.MemberDescriptorType): 94 instance_result = _check_instance(obj, attr) 95 else: 96 klass = obj 97 98 klass_result = _check_class(klass, attr) 99 100 if instance_result is not _sentinel and klass_result is not _sentinel: 101 if _safe_hasattr(klass_result, '__get__') \ 102 and _safe_is_data_descriptor(klass_result): 103 # A get/set descriptor has priority over everything. 104 return klass_result, True 105 106 if instance_result is not _sentinel: 107 return instance_result, False 108 if klass_result is not _sentinel: 109 return klass_result, _safe_hasattr(klass_result, '__get__') 110 111 if obj is klass: 112 # for types we check the metaclass too 113 for entry in _static_getmro(type(klass)): 114 if _shadowed_dict(type(entry)) is _sentinel: 115 try: 116 return entry.__dict__[attr], False 117 except KeyError: 118 pass 119 if default is not _sentinel: 120 return default, False 121 raise AttributeError(attr) 122