]>
Commit | Line | Data |
---|---|---|
53e6db90 DC |
1 | from parso.python import tree |
2 | ||
3 | from jedi import debug | |
4 | from jedi.inference.cache import inference_state_method_cache, CachedMetaClass | |
5 | from jedi.inference import compiled | |
6 | from jedi.inference import recursion | |
7 | from jedi.inference import docstrings | |
8 | from jedi.inference import flow_analysis | |
9 | from jedi.inference.signature import TreeSignature | |
10 | from jedi.inference.filters import ParserTreeFilter, FunctionExecutionFilter, \ | |
11 | AnonymousFunctionExecutionFilter | |
12 | from jedi.inference.names import ValueName, AbstractNameDefinition, \ | |
13 | AnonymousParamName, ParamName, NameWrapper | |
14 | from jedi.inference.base_value import ContextualizedNode, NO_VALUES, \ | |
15 | ValueSet, TreeValue, ValueWrapper | |
16 | from jedi.inference.lazy_value import LazyKnownValues, LazyKnownValue, \ | |
17 | LazyTreeValue | |
18 | from jedi.inference.context import ValueContext, TreeContextMixin | |
19 | from jedi.inference.value import iterable | |
20 | from jedi import parser_utils | |
21 | from jedi.inference.parser_cache import get_yield_exprs | |
22 | from jedi.inference.helpers import values_from_qualified_names | |
23 | from jedi.inference.gradual.generics import TupleGenericManager | |
24 | ||
25 | ||
26 | class LambdaName(AbstractNameDefinition): | |
27 | string_name = '<lambda>' | |
28 | api_type = 'function' | |
29 | ||
30 | def __init__(self, lambda_value): | |
31 | self._lambda_value = lambda_value | |
32 | self.parent_context = lambda_value.parent_context | |
33 | ||
34 | @property | |
35 | def start_pos(self): | |
36 | return self._lambda_value.tree_node.start_pos | |
37 | ||
38 | def infer(self): | |
39 | return ValueSet([self._lambda_value]) | |
40 | ||
41 | ||
42 | class FunctionAndClassBase(TreeValue): | |
43 | def get_qualified_names(self): | |
44 | if self.parent_context.is_class(): | |
45 | n = self.parent_context.get_qualified_names() | |
46 | if n is None: | |
47 | # This means that the parent class lives within a function. | |
48 | return None | |
49 | return n + (self.py__name__(),) | |
50 | elif self.parent_context.is_module(): | |
51 | return (self.py__name__(),) | |
52 | else: | |
53 | return None | |
54 | ||
55 | ||
56 | class FunctionMixin: | |
57 | api_type = 'function' | |
58 | ||
59 | def get_filters(self, origin_scope=None): | |
60 | cls = self.py__class__() | |
61 | for instance in cls.execute_with_values(): | |
62 | yield from instance.get_filters(origin_scope=origin_scope) | |
63 | ||
64 | def py__get__(self, instance, class_value): | |
65 | from jedi.inference.value.instance import BoundMethod | |
66 | if instance is None: | |
67 | # Calling the Foo.bar results in the original bar function. | |
68 | return ValueSet([self]) | |
69 | return ValueSet([BoundMethod(instance, class_value.as_context(), self)]) | |
70 | ||
71 | def get_param_names(self): | |
72 | return [AnonymousParamName(self, param.name) | |
73 | for param in self.tree_node.get_params()] | |
74 | ||
75 | @property | |
76 | def name(self): | |
77 | if self.tree_node.type == 'lambdef': | |
78 | return LambdaName(self) | |
79 | return ValueName(self, self.tree_node.name) | |
80 | ||
81 | def is_function(self): | |
82 | return True | |
83 | ||
84 | def py__name__(self): | |
85 | return self.name.string_name | |
86 | ||
87 | def get_type_hint(self, add_class_info=True): | |
88 | return_annotation = self.tree_node.annotation | |
89 | if return_annotation is None: | |
90 | def param_name_to_str(n): | |
91 | s = n.string_name | |
92 | annotation = n.infer().get_type_hint() | |
93 | if annotation is not None: | |
94 | s += ': ' + annotation | |
95 | if n.default_node is not None: | |
96 | s += '=' + n.default_node.get_code(include_prefix=False) | |
97 | return s | |
98 | ||
99 | function_execution = self.as_context() | |
100 | result = function_execution.infer() | |
101 | return_hint = result.get_type_hint() | |
102 | body = self.py__name__() + '(%s)' % ', '.join([ | |
103 | param_name_to_str(n) | |
104 | for n in function_execution.get_param_names() | |
105 | ]) | |
106 | if return_hint is None: | |
107 | return body | |
108 | else: | |
109 | return_hint = return_annotation.get_code(include_prefix=False) | |
110 | body = self.py__name__() + self.tree_node.children[2].get_code(include_prefix=False) | |
111 | ||
112 | return body + ' -> ' + return_hint | |
113 | ||
114 | def py__call__(self, arguments): | |
115 | function_execution = self.as_context(arguments) | |
116 | return function_execution.infer() | |
117 | ||
118 | def _as_context(self, arguments=None): | |
119 | if arguments is None: | |
120 | return AnonymousFunctionExecution(self) | |
121 | return FunctionExecutionContext(self, arguments) | |
122 | ||
123 | def get_signatures(self): | |
124 | return [TreeSignature(f) for f in self.get_signature_functions()] | |
125 | ||
126 | ||
127 | class FunctionValue(FunctionMixin, FunctionAndClassBase, metaclass=CachedMetaClass): | |
128 | @classmethod | |
129 | def from_context(cls, context, tree_node): | |
130 | def create(tree_node): | |
131 | if context.is_class(): | |
132 | return MethodValue( | |
133 | context.inference_state, | |
134 | context, | |
135 | parent_context=parent_context, | |
136 | tree_node=tree_node | |
137 | ) | |
138 | else: | |
139 | return cls( | |
140 | context.inference_state, | |
141 | parent_context=parent_context, | |
142 | tree_node=tree_node | |
143 | ) | |
144 | ||
145 | overloaded_funcs = list(_find_overload_functions(context, tree_node)) | |
146 | ||
147 | parent_context = context | |
148 | while parent_context.is_class() or parent_context.is_instance(): | |
149 | parent_context = parent_context.parent_context | |
150 | ||
151 | function = create(tree_node) | |
152 | ||
153 | if overloaded_funcs: | |
154 | return OverloadedFunctionValue( | |
155 | function, | |
156 | # Get them into the correct order: lower line first. | |
157 | list(reversed([create(f) for f in overloaded_funcs])) | |
158 | ) | |
159 | return function | |
160 | ||
161 | def py__class__(self): | |
162 | c, = values_from_qualified_names(self.inference_state, 'types', 'FunctionType') | |
163 | return c | |
164 | ||
165 | def get_default_param_context(self): | |
166 | return self.parent_context | |
167 | ||
168 | def get_signature_functions(self): | |
169 | return [self] | |
170 | ||
171 | ||
172 | class FunctionNameInClass(NameWrapper): | |
173 | def __init__(self, class_context, name): | |
174 | super().__init__(name) | |
175 | self._class_context = class_context | |
176 | ||
177 | def get_defining_qualified_value(self): | |
178 | return self._class_context.get_value() # Might be None. | |
179 | ||
180 | ||
181 | class MethodValue(FunctionValue): | |
182 | def __init__(self, inference_state, class_context, *args, **kwargs): | |
183 | super().__init__(inference_state, *args, **kwargs) | |
184 | self.class_context = class_context | |
185 | ||
186 | def get_default_param_context(self): | |
187 | return self.class_context | |
188 | ||
189 | def get_qualified_names(self): | |
190 | # Need to implement this, because the parent value of a method | |
191 | # value is not the class value but the module. | |
192 | names = self.class_context.get_qualified_names() | |
193 | if names is None: | |
194 | return None | |
195 | return names + (self.py__name__(),) | |
196 | ||
197 | @property | |
198 | def name(self): | |
199 | return FunctionNameInClass(self.class_context, super().name) | |
200 | ||
201 | ||
202 | class BaseFunctionExecutionContext(ValueContext, TreeContextMixin): | |
203 | def infer_annotations(self): | |
204 | raise NotImplementedError | |
205 | ||
206 | @inference_state_method_cache(default=NO_VALUES) | |
207 | @recursion.execution_recursion_decorator() | |
208 | def get_return_values(self, check_yields=False): | |
209 | funcdef = self.tree_node | |
210 | if funcdef.type == 'lambdef': | |
211 | return self.infer_node(funcdef.children[-1]) | |
212 | ||
213 | if check_yields: | |
214 | value_set = NO_VALUES | |
215 | returns = get_yield_exprs(self.inference_state, funcdef) | |
216 | else: | |
217 | value_set = self.infer_annotations() | |
218 | if value_set: | |
219 | # If there are annotations, prefer them over anything else. | |
220 | # This will make it faster. | |
221 | return value_set | |
222 | value_set |= docstrings.infer_return_types(self._value) | |
223 | returns = funcdef.iter_return_stmts() | |
224 | ||
225 | for r in returns: | |
226 | if check_yields: | |
227 | value_set |= ValueSet.from_sets( | |
228 | lazy_value.infer() | |
229 | for lazy_value in self._get_yield_lazy_value(r) | |
230 | ) | |
231 | else: | |
232 | check = flow_analysis.reachability_check(self, funcdef, r) | |
233 | if check is flow_analysis.UNREACHABLE: | |
234 | debug.dbg('Return unreachable: %s', r) | |
235 | else: | |
236 | try: | |
237 | children = r.children | |
238 | except AttributeError: | |
239 | ctx = compiled.builtin_from_name(self.inference_state, 'None') | |
240 | value_set |= ValueSet([ctx]) | |
241 | else: | |
242 | value_set |= self.infer_node(children[1]) | |
243 | if check is flow_analysis.REACHABLE: | |
244 | debug.dbg('Return reachable: %s', r) | |
245 | break | |
246 | return value_set | |
247 | ||
248 | def _get_yield_lazy_value(self, yield_expr): | |
249 | if yield_expr.type == 'keyword': | |
250 | # `yield` just yields None. | |
251 | ctx = compiled.builtin_from_name(self.inference_state, 'None') | |
252 | yield LazyKnownValue(ctx) | |
253 | return | |
254 | ||
255 | node = yield_expr.children[1] | |
256 | if node.type == 'yield_arg': # It must be a yield from. | |
257 | cn = ContextualizedNode(self, node.children[1]) | |
258 | yield from cn.infer().iterate(cn) | |
259 | else: | |
260 | yield LazyTreeValue(self, node) | |
261 | ||
262 | @recursion.execution_recursion_decorator(default=iter([])) | |
263 | def get_yield_lazy_values(self, is_async=False): | |
264 | # TODO: if is_async, wrap yield statements in Awaitable/async_generator_asend | |
265 | for_parents = [(y, tree.search_ancestor(y, 'for_stmt', 'funcdef', | |
266 | 'while_stmt', 'if_stmt')) | |
267 | for y in get_yield_exprs(self.inference_state, self.tree_node)] | |
268 | ||
269 | # Calculate if the yields are placed within the same for loop. | |
270 | yields_order = [] | |
271 | last_for_stmt = None | |
272 | for yield_, for_stmt in for_parents: | |
273 | # For really simple for loops we can predict the order. Otherwise | |
274 | # we just ignore it. | |
275 | parent = for_stmt.parent | |
276 | if parent.type == 'suite': | |
277 | parent = parent.parent | |
278 | if for_stmt.type == 'for_stmt' and parent == self.tree_node \ | |
279 | and parser_utils.for_stmt_defines_one_name(for_stmt): # Simplicity for now. | |
280 | if for_stmt == last_for_stmt: | |
281 | yields_order[-1][1].append(yield_) | |
282 | else: | |
283 | yields_order.append((for_stmt, [yield_])) | |
284 | elif for_stmt == self.tree_node: | |
285 | yields_order.append((None, [yield_])) | |
286 | else: | |
287 | types = self.get_return_values(check_yields=True) | |
288 | if types: | |
289 | yield LazyKnownValues(types, min=0, max=float('inf')) | |
290 | return | |
291 | last_for_stmt = for_stmt | |
292 | ||
293 | for for_stmt, yields in yields_order: | |
294 | if for_stmt is None: | |
295 | # No for_stmt, just normal yields. | |
296 | for yield_ in yields: | |
297 | yield from self._get_yield_lazy_value(yield_) | |
298 | else: | |
299 | input_node = for_stmt.get_testlist() | |
300 | cn = ContextualizedNode(self, input_node) | |
301 | ordered = cn.infer().iterate(cn) | |
302 | ordered = list(ordered) | |
303 | for lazy_value in ordered: | |
304 | dct = {str(for_stmt.children[1].value): lazy_value.infer()} | |
305 | with self.predefine_names(for_stmt, dct): | |
306 | for yield_in_same_for_stmt in yields: | |
307 | yield from self._get_yield_lazy_value(yield_in_same_for_stmt) | |
308 | ||
309 | def merge_yield_values(self, is_async=False): | |
310 | return ValueSet.from_sets( | |
311 | lazy_value.infer() | |
312 | for lazy_value in self.get_yield_lazy_values() | |
313 | ) | |
314 | ||
315 | def is_generator(self): | |
316 | return bool(get_yield_exprs(self.inference_state, self.tree_node)) | |
317 | ||
318 | def infer(self): | |
319 | """ | |
320 | Created to be used by inheritance. | |
321 | """ | |
322 | inference_state = self.inference_state | |
323 | is_coroutine = self.tree_node.parent.type in ('async_stmt', 'async_funcdef') | |
324 | from jedi.inference.gradual.base import GenericClass | |
325 | ||
326 | if is_coroutine: | |
327 | if self.is_generator(): | |
328 | async_generator_classes = inference_state.typing_module \ | |
329 | .py__getattribute__('AsyncGenerator') | |
330 | ||
331 | yield_values = self.merge_yield_values(is_async=True) | |
332 | # The contravariant doesn't seem to be defined. | |
333 | generics = (yield_values.py__class__(), NO_VALUES) | |
334 | return ValueSet( | |
335 | GenericClass(c, TupleGenericManager(generics)) | |
336 | for c in async_generator_classes | |
337 | ).execute_annotation() | |
338 | else: | |
339 | async_classes = inference_state.typing_module.py__getattribute__('Coroutine') | |
340 | return_values = self.get_return_values() | |
341 | # Only the first generic is relevant. | |
342 | generics = (return_values.py__class__(), NO_VALUES, NO_VALUES) | |
343 | return ValueSet( | |
344 | GenericClass(c, TupleGenericManager(generics)) for c in async_classes | |
345 | ).execute_annotation() | |
346 | else: | |
347 | # If there are annotations, prefer them over anything else. | |
348 | if self.is_generator() and not self.infer_annotations(): | |
349 | return ValueSet([iterable.Generator(inference_state, self)]) | |
350 | else: | |
351 | return self.get_return_values() | |
352 | ||
353 | ||
354 | class FunctionExecutionContext(BaseFunctionExecutionContext): | |
355 | def __init__(self, function_value, arguments): | |
356 | super().__init__(function_value) | |
357 | self._arguments = arguments | |
358 | ||
359 | def get_filters(self, until_position=None, origin_scope=None): | |
360 | yield FunctionExecutionFilter( | |
361 | self, self._value, | |
362 | until_position=until_position, | |
363 | origin_scope=origin_scope, | |
364 | arguments=self._arguments | |
365 | ) | |
366 | ||
367 | def infer_annotations(self): | |
368 | from jedi.inference.gradual.annotation import infer_return_types | |
369 | return infer_return_types(self._value, self._arguments) | |
370 | ||
371 | def get_param_names(self): | |
372 | return [ | |
373 | ParamName(self._value, param.name, self._arguments) | |
374 | for param in self._value.tree_node.get_params() | |
375 | ] | |
376 | ||
377 | ||
378 | class AnonymousFunctionExecution(BaseFunctionExecutionContext): | |
379 | def infer_annotations(self): | |
380 | # I don't think inferring anonymous executions is a big thing. | |
381 | # Anonymous contexts are mostly there for the user to work in. ~ dave | |
382 | return NO_VALUES | |
383 | ||
384 | def get_filters(self, until_position=None, origin_scope=None): | |
385 | yield AnonymousFunctionExecutionFilter( | |
386 | self, self._value, | |
387 | until_position=until_position, | |
388 | origin_scope=origin_scope, | |
389 | ) | |
390 | ||
391 | def get_param_names(self): | |
392 | return self._value.get_param_names() | |
393 | ||
394 | ||
395 | class OverloadedFunctionValue(FunctionMixin, ValueWrapper): | |
396 | def __init__(self, function, overloaded_functions): | |
397 | super().__init__(function) | |
398 | self._overloaded_functions = overloaded_functions | |
399 | ||
400 | def py__call__(self, arguments): | |
401 | debug.dbg("Execute overloaded function %s", self._wrapped_value, color='BLUE') | |
402 | function_executions = [] | |
403 | for signature in self.get_signatures(): | |
404 | function_execution = signature.value.as_context(arguments) | |
405 | function_executions.append(function_execution) | |
406 | if signature.matches_signature(arguments): | |
407 | return function_execution.infer() | |
408 | ||
409 | if self.inference_state.is_analysis: | |
410 | # In this case we want precision. | |
411 | return NO_VALUES | |
412 | return ValueSet.from_sets(fe.infer() for fe in function_executions) | |
413 | ||
414 | def get_signature_functions(self): | |
415 | return self._overloaded_functions | |
416 | ||
417 | def get_type_hint(self, add_class_info=True): | |
418 | return 'Union[%s]' % ', '.join(f.get_type_hint() for f in self._overloaded_functions) | |
419 | ||
420 | ||
421 | def _find_overload_functions(context, tree_node): | |
422 | def _is_overload_decorated(funcdef): | |
423 | if funcdef.parent.type == 'decorated': | |
424 | decorators = funcdef.parent.children[0] | |
425 | if decorators.type == 'decorator': | |
426 | decorators = [decorators] | |
427 | else: | |
428 | decorators = decorators.children | |
429 | for decorator in decorators: | |
430 | dotted_name = decorator.children[1] | |
431 | if dotted_name.type == 'name' and dotted_name.value == 'overload': | |
432 | # TODO check with values if it's the right overload | |
433 | return True | |
434 | return False | |
435 | ||
436 | if tree_node.type == 'lambdef': | |
437 | return | |
438 | ||
439 | if _is_overload_decorated(tree_node): | |
440 | yield tree_node | |
441 | ||
442 | while True: | |
443 | filter = ParserTreeFilter( | |
444 | context, | |
445 | until_position=tree_node.start_pos | |
446 | ) | |
447 | names = filter.get(tree_node.name.value) | |
448 | assert isinstance(names, list) | |
449 | if not names: | |
450 | break | |
451 | ||
452 | found = False | |
453 | for name in names: | |
454 | funcdef = name.tree_name.parent | |
455 | if funcdef.type == 'funcdef' and _is_overload_decorated(funcdef): | |
456 | tree_node = funcdef | |
457 | found = True | |
458 | yield funcdef | |
459 | ||
460 | if not found: | |
461 | break |