]> crepu.dev Git - config.git/blame - djavu-asus/emacs/elpy/rpc-venv/lib/python3.11/site-packages/jedi/inference/analysis.py
Reorganización de directorios
[config.git] / djavu-asus / emacs / elpy / rpc-venv / lib / python3.11 / site-packages / jedi / inference / analysis.py
CommitLineData
53e6db90
DC
1"""
2Module for statical analysis.
3"""
4from parso.python import tree
5
6from jedi import debug
7from jedi.inference.helpers import is_string
8
9
10CODES = {
11 'attribute-error': (1, AttributeError, 'Potential AttributeError.'),
12 'name-error': (2, NameError, 'Potential NameError.'),
13 'import-error': (3, ImportError, 'Potential ImportError.'),
14 'type-error-too-many-arguments': (4, TypeError, None),
15 'type-error-too-few-arguments': (5, TypeError, None),
16 'type-error-keyword-argument': (6, TypeError, None),
17 'type-error-multiple-values': (7, TypeError, None),
18 'type-error-star-star': (8, TypeError, None),
19 'type-error-star': (9, TypeError, None),
20 'type-error-operation': (10, TypeError, None),
21 'type-error-not-iterable': (11, TypeError, None),
22 'type-error-isinstance': (12, TypeError, None),
23 'type-error-not-subscriptable': (13, TypeError, None),
24 'value-error-too-many-values': (14, ValueError, None),
25 'value-error-too-few-values': (15, ValueError, None),
26}
27
28
29class Error:
30 def __init__(self, name, module_path, start_pos, message=None):
31 self.path = module_path
32 self._start_pos = start_pos
33 self.name = name
34 if message is None:
35 message = CODES[self.name][2]
36 self.message = message
37
38 @property
39 def line(self):
40 return self._start_pos[0]
41
42 @property
43 def column(self):
44 return self._start_pos[1]
45
46 @property
47 def code(self):
48 # The class name start
49 first = self.__class__.__name__[0]
50 return first + str(CODES[self.name][0])
51
52 def __str__(self):
53 return '%s:%s:%s: %s %s' % (self.path, self.line, self.column,
54 self.code, self.message)
55
56 def __eq__(self, other):
57 return (self.path == other.path and self.name == other.name
58 and self._start_pos == other._start_pos)
59
60 def __ne__(self, other):
61 return not self.__eq__(other)
62
63 def __hash__(self):
64 return hash((self.path, self._start_pos, self.name))
65
66 def __repr__(self):
67 return '<%s %s: %s@%s,%s>' % (self.__class__.__name__,
68 self.name, self.path,
69 self._start_pos[0], self._start_pos[1])
70
71
72class Warning(Error):
73 pass
74
75
76def add(node_context, error_name, node, message=None, typ=Error, payload=None):
77 exception = CODES[error_name][1]
78 if _check_for_exception_catch(node_context, node, exception, payload):
79 return
80
81 # TODO this path is probably not right
82 module_context = node_context.get_root_context()
83 module_path = module_context.py__file__()
84 issue_instance = typ(error_name, module_path, node.start_pos, message)
85 debug.warning(str(issue_instance), format=False)
86 node_context.inference_state.analysis.append(issue_instance)
87 return issue_instance
88
89
90def _check_for_setattr(instance):
91 """
92 Check if there's any setattr method inside an instance. If so, return True.
93 """
94 module = instance.get_root_context()
95 node = module.tree_node
96 if node is None:
97 # If it's a compiled module or doesn't have a tree_node
98 return False
99
100 try:
101 stmt_names = node.get_used_names()['setattr']
102 except KeyError:
103 return False
104
105 return any(node.start_pos < n.start_pos < node.end_pos
106 # Check if it's a function called setattr.
107 and not (n.parent.type == 'funcdef' and n.parent.name == n)
108 for n in stmt_names)
109
110
111def add_attribute_error(name_context, lookup_value, name):
112 message = ('AttributeError: %s has no attribute %s.' % (lookup_value, name))
113 # Check for __getattr__/__getattribute__ existance and issue a warning
114 # instead of an error, if that happens.
115 typ = Error
116 if lookup_value.is_instance() and not lookup_value.is_compiled():
117 # TODO maybe make a warning for __getattr__/__getattribute__
118
119 if _check_for_setattr(lookup_value):
120 typ = Warning
121
122 payload = lookup_value, name
123 add(name_context, 'attribute-error', name, message, typ, payload)
124
125
126def _check_for_exception_catch(node_context, jedi_name, exception, payload=None):
127 """
128 Checks if a jedi object (e.g. `Statement`) sits inside a try/catch and
129 doesn't count as an error (if equal to `exception`).
130 Also checks `hasattr` for AttributeErrors and uses the `payload` to compare
131 it.
132 Returns True if the exception was catched.
133 """
134 def check_match(cls, exception):
135 if not cls.is_class():
136 return False
137
138 for python_cls in exception.mro():
139 if cls.py__name__() == python_cls.__name__ \
140 and cls.parent_context.is_builtins_module():
141 return True
142 return False
143
144 def check_try_for_except(obj, exception):
145 # Only nodes in try
146 iterator = iter(obj.children)
147 for branch_type in iterator:
148 next(iterator) # The colon
149 suite = next(iterator)
150 if branch_type == 'try' \
151 and not (branch_type.start_pos < jedi_name.start_pos <= suite.end_pos):
152 return False
153
154 for node in obj.get_except_clause_tests():
155 if node is None:
156 return True # An exception block that catches everything.
157 else:
158 except_classes = node_context.infer_node(node)
159 for cls in except_classes:
160 from jedi.inference.value import iterable
161 if isinstance(cls, iterable.Sequence) and \
162 cls.array_type == 'tuple':
163 # multiple exceptions
164 for lazy_value in cls.py__iter__():
165 for typ in lazy_value.infer():
166 if check_match(typ, exception):
167 return True
168 else:
169 if check_match(cls, exception):
170 return True
171
172 def check_hasattr(node, suite):
173 try:
174 assert suite.start_pos <= jedi_name.start_pos < suite.end_pos
175 assert node.type in ('power', 'atom_expr')
176 base = node.children[0]
177 assert base.type == 'name' and base.value == 'hasattr'
178 trailer = node.children[1]
179 assert trailer.type == 'trailer'
180 arglist = trailer.children[1]
181 assert arglist.type == 'arglist'
182 from jedi.inference.arguments import TreeArguments
183 args = TreeArguments(node_context.inference_state, node_context, arglist)
184 unpacked_args = list(args.unpack())
185 # Arguments should be very simple
186 assert len(unpacked_args) == 2
187
188 # Check name
189 key, lazy_value = unpacked_args[1]
190 names = list(lazy_value.infer())
191 assert len(names) == 1 and is_string(names[0])
192 assert names[0].get_safe_value() == payload[1].value
193
194 # Check objects
195 key, lazy_value = unpacked_args[0]
196 objects = lazy_value.infer()
197 return payload[0] in objects
198 except AssertionError:
199 return False
200
201 obj = jedi_name
202 while obj is not None and not isinstance(obj, (tree.Function, tree.Class)):
203 if isinstance(obj, tree.Flow):
204 # try/except catch check
205 if obj.type == 'try_stmt' and check_try_for_except(obj, exception):
206 return True
207 # hasattr check
208 if exception == AttributeError and obj.type in ('if_stmt', 'while_stmt'):
209 if check_hasattr(obj.children[1], obj.children[3]):
210 return True
211 obj = obj.parent
212
213 return False