Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.11/site-packages/jedi/inference/compiled/getattr_static.py: 17%

Shortcuts on this page

r m x   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

71 statements  

1""" 

2A static version of getattr. 

3This is a backport of the Python 3 code with a little bit of additional 

4information returned to enable Jedi to make decisions. 

5""" 

6 

7import types 

8 

9from jedi import debug 

10 

11_sentinel = object() 

12 

13 

14def _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 

23def _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 

33def _is_type(obj): 

34 try: 

35 _static_getmro(obj) 

36 except TypeError: 

37 return False 

38 return True 

39 

40 

41def _shadowed_dict(klass): 

42 dict_attr = type.__dict__["__dict__"] # type: ignore[index] 

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 

56def _static_getmro(klass): 

57 mro = type.__dict__['__mro__'].__get__(klass) # type: ignore[index] 

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 

67def _safe_hasattr(obj, name): 

68 return _check_class(type(obj), name) is not _sentinel 

69 

70 

71def _safe_is_data_descriptor(obj): 

72 return _safe_hasattr(obj, '__set__') or _safe_hasattr(obj, '__delete__') 

73 

74 

75def 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 # In Python 3.15+, __dict__ is a GetSetDescriptorType instead of being _sentinel 

94 if (dict_attr is _sentinel 

95 or type(dict_attr) is types.MemberDescriptorType 

96 or type(dict_attr) is types.GetSetDescriptorType): 

97 instance_result = _check_instance(obj, attr) 

98 else: 

99 klass = obj 

100 

101 klass_result = _check_class(klass, attr) 

102 

103 if instance_result is not _sentinel and klass_result is not _sentinel: 

104 if _safe_hasattr(klass_result, '__get__') \ 

105 and _safe_is_data_descriptor(klass_result): 

106 # A get/set descriptor has priority over everything. 

107 return klass_result, True 

108 

109 if instance_result is not _sentinel: 

110 return instance_result, False 

111 if klass_result is not _sentinel: 

112 return klass_result, _safe_hasattr(klass_result, '__get__') 

113 

114 if obj is klass: 

115 # for types we check the metaclass too 

116 for entry in _static_getmro(type(klass)): 

117 if _shadowed_dict(type(entry)) is _sentinel: 

118 try: 

119 return entry.__dict__[attr], False 

120 except KeyError: 

121 pass 

122 if default is not _sentinel: 

123 return default, False 

124 raise AttributeError(attr)