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
« 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"""
7import types
9from jedi import debug
11_sentinel = object()
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)
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
33def _is_type(obj):
34 try:
35 _static_getmro(obj)
36 except TypeError:
37 return False
38 return True
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
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
67def _safe_hasattr(obj, name):
68 return _check_class(type(obj), name) is not _sentinel
71def _safe_is_data_descriptor(obj):
72 return _safe_hasattr(obj, '__set__') or _safe_hasattr(obj, '__delete__')
75def getattr_static(obj, attr, default=_sentinel):
76 """Retrieve attributes without triggering dynamic lookup via the
77 descriptor protocol, __getattr__ or __getattribute__.
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.
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
98 klass_result = _check_class(klass, attr)
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
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__')
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)