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

70 statements  

« prev     ^ index     » next       coverage.py v7.4.4, created at 2024-04-20 06:09 +0000

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__"] 

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) 

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 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)