]>
Commit | Line | Data |
---|---|---|
53e6db90 DC |
1 | """ |
2 | Values are the "values" that Python would return. However Values are at the | |
3 | same time also the "values" that a user is currently sitting in. | |
4 | ||
5 | A ValueSet is typically used to specify the return of a function or any other | |
6 | static analysis operation. In jedi there are always multiple returns and not | |
7 | just one. | |
8 | """ | |
9 | from functools import reduce | |
10 | from operator import add | |
11 | from itertools import zip_longest | |
12 | ||
13 | from parso.python.tree import Name | |
14 | ||
15 | from jedi import debug | |
16 | from jedi.parser_utils import clean_scope_docstring | |
17 | from jedi.inference.helpers import SimpleGetItemNotFound | |
18 | from jedi.inference.utils import safe_property | |
19 | from jedi.inference.cache import inference_state_as_method_param_cache | |
20 | from jedi.cache import memoize_method | |
21 | ||
22 | sentinel = object() | |
23 | ||
24 | ||
25 | class HasNoContext(Exception): | |
26 | pass | |
27 | ||
28 | ||
29 | class HelperValueMixin: | |
30 | def get_root_context(self): | |
31 | value = self | |
32 | if value.parent_context is None: | |
33 | return value.as_context() | |
34 | ||
35 | while True: | |
36 | if value.parent_context is None: | |
37 | return value | |
38 | value = value.parent_context | |
39 | ||
40 | def execute(self, arguments): | |
41 | return self.inference_state.execute(self, arguments=arguments) | |
42 | ||
43 | def execute_with_values(self, *value_list): | |
44 | from jedi.inference.arguments import ValuesArguments | |
45 | arguments = ValuesArguments([ValueSet([value]) for value in value_list]) | |
46 | return self.inference_state.execute(self, arguments) | |
47 | ||
48 | def execute_annotation(self): | |
49 | return self.execute_with_values() | |
50 | ||
51 | def gather_annotation_classes(self): | |
52 | return ValueSet([self]) | |
53 | ||
54 | def merge_types_of_iterate(self, contextualized_node=None, is_async=False): | |
55 | return ValueSet.from_sets( | |
56 | lazy_value.infer() | |
57 | for lazy_value in self.iterate(contextualized_node, is_async) | |
58 | ) | |
59 | ||
60 | def _get_value_filters(self, name_or_str): | |
61 | origin_scope = name_or_str if isinstance(name_or_str, Name) else None | |
62 | yield from self.get_filters(origin_scope=origin_scope) | |
63 | # This covers the case where a stub files are incomplete. | |
64 | if self.is_stub(): | |
65 | from jedi.inference.gradual.conversion import convert_values | |
66 | for c in convert_values(ValueSet({self})): | |
67 | yield from c.get_filters() | |
68 | ||
69 | def goto(self, name_or_str, name_context=None, analysis_errors=True): | |
70 | from jedi.inference import finder | |
71 | filters = self._get_value_filters(name_or_str) | |
72 | names = finder.filter_name(filters, name_or_str) | |
73 | debug.dbg('context.goto %s in (%s): %s', name_or_str, self, names) | |
74 | return names | |
75 | ||
76 | def py__getattribute__(self, name_or_str, name_context=None, position=None, | |
77 | analysis_errors=True): | |
78 | """ | |
79 | :param position: Position of the last statement -> tuple of line, column | |
80 | """ | |
81 | if name_context is None: | |
82 | name_context = self | |
83 | names = self.goto(name_or_str, name_context, analysis_errors) | |
84 | values = ValueSet.from_sets(name.infer() for name in names) | |
85 | if not values: | |
86 | n = name_or_str.value if isinstance(name_or_str, Name) else name_or_str | |
87 | values = self.py__getattribute__alternatives(n) | |
88 | ||
89 | if not names and not values and analysis_errors: | |
90 | if isinstance(name_or_str, Name): | |
91 | from jedi.inference import analysis | |
92 | analysis.add_attribute_error( | |
93 | name_context, self, name_or_str) | |
94 | debug.dbg('context.names_to_types: %s -> %s', names, values) | |
95 | return values | |
96 | ||
97 | def py__await__(self): | |
98 | await_value_set = self.py__getattribute__("__await__") | |
99 | if not await_value_set: | |
100 | debug.warning('Tried to run __await__ on value %s', self) | |
101 | return await_value_set.execute_with_values() | |
102 | ||
103 | def py__name__(self): | |
104 | return self.name.string_name | |
105 | ||
106 | def iterate(self, contextualized_node=None, is_async=False): | |
107 | debug.dbg('iterate %s', self) | |
108 | if is_async: | |
109 | from jedi.inference.lazy_value import LazyKnownValues | |
110 | # TODO if no __aiter__ values are there, error should be: | |
111 | # TypeError: 'async for' requires an object with __aiter__ method, got int | |
112 | return iter([ | |
113 | LazyKnownValues( | |
114 | self.py__getattribute__('__aiter__').execute_with_values() | |
115 | .py__getattribute__('__anext__').execute_with_values() | |
116 | .py__getattribute__('__await__').execute_with_values() | |
117 | .py__stop_iteration_returns() | |
118 | ) # noqa: E124 | |
119 | ]) | |
120 | return self.py__iter__(contextualized_node) | |
121 | ||
122 | def is_sub_class_of(self, class_value): | |
123 | with debug.increase_indent_cm('subclass matching of %s <=> %s' % (self, class_value), | |
124 | color='BLUE'): | |
125 | for cls in self.py__mro__(): | |
126 | if cls.is_same_class(class_value): | |
127 | debug.dbg('matched subclass True', color='BLUE') | |
128 | return True | |
129 | debug.dbg('matched subclass False', color='BLUE') | |
130 | return False | |
131 | ||
132 | def is_same_class(self, class2): | |
133 | # Class matching should prefer comparisons that are not this function. | |
134 | if type(class2).is_same_class != HelperValueMixin.is_same_class: | |
135 | return class2.is_same_class(self) | |
136 | return self == class2 | |
137 | ||
138 | @memoize_method | |
139 | def as_context(self, *args, **kwargs): | |
140 | return self._as_context(*args, **kwargs) | |
141 | ||
142 | ||
143 | class Value(HelperValueMixin): | |
144 | """ | |
145 | To be implemented by subclasses. | |
146 | """ | |
147 | tree_node = None | |
148 | # Possible values: None, tuple, list, dict and set. Here to deal with these | |
149 | # very important containers. | |
150 | array_type = None | |
151 | api_type = 'not_defined_please_report_bug' | |
152 | ||
153 | def __init__(self, inference_state, parent_context=None): | |
154 | self.inference_state = inference_state | |
155 | self.parent_context = parent_context | |
156 | ||
157 | def py__getitem__(self, index_value_set, contextualized_node): | |
158 | from jedi.inference import analysis | |
159 | # TODO this value is probably not right. | |
160 | analysis.add( | |
161 | contextualized_node.context, | |
162 | 'type-error-not-subscriptable', | |
163 | contextualized_node.node, | |
164 | message="TypeError: '%s' object is not subscriptable" % self | |
165 | ) | |
166 | return NO_VALUES | |
167 | ||
168 | def py__simple_getitem__(self, index): | |
169 | raise SimpleGetItemNotFound | |
170 | ||
171 | def py__iter__(self, contextualized_node=None): | |
172 | if contextualized_node is not None: | |
173 | from jedi.inference import analysis | |
174 | analysis.add( | |
175 | contextualized_node.context, | |
176 | 'type-error-not-iterable', | |
177 | contextualized_node.node, | |
178 | message="TypeError: '%s' object is not iterable" % self) | |
179 | return iter([]) | |
180 | ||
181 | def py__next__(self, contextualized_node=None): | |
182 | return self.py__iter__(contextualized_node) | |
183 | ||
184 | def get_signatures(self): | |
185 | return [] | |
186 | ||
187 | def is_class(self): | |
188 | return False | |
189 | ||
190 | def is_class_mixin(self): | |
191 | return False | |
192 | ||
193 | def is_instance(self): | |
194 | return False | |
195 | ||
196 | def is_function(self): | |
197 | return False | |
198 | ||
199 | def is_module(self): | |
200 | return False | |
201 | ||
202 | def is_namespace(self): | |
203 | return False | |
204 | ||
205 | def is_compiled(self): | |
206 | return False | |
207 | ||
208 | def is_bound_method(self): | |
209 | return False | |
210 | ||
211 | def is_builtins_module(self): | |
212 | return False | |
213 | ||
214 | def py__bool__(self): | |
215 | """ | |
216 | Since Wrapper is a super class for classes, functions and modules, | |
217 | the return value will always be true. | |
218 | """ | |
219 | return True | |
220 | ||
221 | def py__doc__(self): | |
222 | try: | |
223 | self.tree_node.get_doc_node | |
224 | except AttributeError: | |
225 | return '' | |
226 | else: | |
227 | return clean_scope_docstring(self.tree_node) | |
228 | ||
229 | def get_safe_value(self, default=sentinel): | |
230 | if default is sentinel: | |
231 | raise ValueError("There exists no safe value for value %s" % self) | |
232 | return default | |
233 | ||
234 | def execute_operation(self, other, operator): | |
235 | debug.warning("%s not possible between %s and %s", operator, self, other) | |
236 | return NO_VALUES | |
237 | ||
238 | def py__call__(self, arguments): | |
239 | debug.warning("no execution possible %s", self) | |
240 | return NO_VALUES | |
241 | ||
242 | def py__stop_iteration_returns(self): | |
243 | debug.warning("Not possible to return the stop iterations of %s", self) | |
244 | return NO_VALUES | |
245 | ||
246 | def py__getattribute__alternatives(self, name_or_str): | |
247 | """ | |
248 | For now a way to add values in cases like __getattr__. | |
249 | """ | |
250 | return NO_VALUES | |
251 | ||
252 | def py__get__(self, instance, class_value): | |
253 | debug.warning("No __get__ defined on %s", self) | |
254 | return ValueSet([self]) | |
255 | ||
256 | def py__get__on_class(self, calling_instance, instance, class_value): | |
257 | return NotImplemented | |
258 | ||
259 | def get_qualified_names(self): | |
260 | # Returns Optional[Tuple[str, ...]] | |
261 | return None | |
262 | ||
263 | def is_stub(self): | |
264 | # The root value knows if it's a stub or not. | |
265 | return self.parent_context.is_stub() | |
266 | ||
267 | def _as_context(self): | |
268 | raise HasNoContext | |
269 | ||
270 | @property | |
271 | def name(self): | |
272 | raise NotImplementedError | |
273 | ||
274 | def get_type_hint(self, add_class_info=True): | |
275 | return None | |
276 | ||
277 | def infer_type_vars(self, value_set): | |
278 | """ | |
279 | When the current instance represents a type annotation, this method | |
280 | tries to find information about undefined type vars and returns a dict | |
281 | from type var name to value set. | |
282 | ||
283 | This is for example important to understand what `iter([1])` returns. | |
284 | According to typeshed, `iter` returns an `Iterator[_T]`: | |
285 | ||
286 | def iter(iterable: Iterable[_T]) -> Iterator[_T]: ... | |
287 | ||
288 | This functions would generate `int` for `_T` in this case, because it | |
289 | unpacks the `Iterable`. | |
290 | ||
291 | Parameters | |
292 | ---------- | |
293 | ||
294 | `self`: represents the annotation of the current parameter to infer the | |
295 | value for. In the above example, this would initially be the | |
296 | `Iterable[_T]` of the `iterable` parameter and then, when recursing, | |
297 | just the `_T` generic parameter. | |
298 | ||
299 | `value_set`: represents the actual argument passed to the parameter | |
300 | we're inferred for, or (for recursive calls) their types. In the | |
301 | above example this would first be the representation of the list | |
302 | `[1]` and then, when recursing, just of `1`. | |
303 | """ | |
304 | return {} | |
305 | ||
306 | ||
307 | def iterate_values(values, contextualized_node=None, is_async=False): | |
308 | """ | |
309 | Calls `iterate`, on all values but ignores the ordering and just returns | |
310 | all values that the iterate functions yield. | |
311 | """ | |
312 | return ValueSet.from_sets( | |
313 | lazy_value.infer() | |
314 | for lazy_value in values.iterate(contextualized_node, is_async=is_async) | |
315 | ) | |
316 | ||
317 | ||
318 | class _ValueWrapperBase(HelperValueMixin): | |
319 | @safe_property | |
320 | def name(self): | |
321 | from jedi.inference.names import ValueName | |
322 | wrapped_name = self._wrapped_value.name | |
323 | if wrapped_name.tree_name is not None: | |
324 | return ValueName(self, wrapped_name.tree_name) | |
325 | else: | |
326 | from jedi.inference.compiled import CompiledValueName | |
327 | return CompiledValueName(self, wrapped_name.string_name) | |
328 | ||
329 | @classmethod | |
330 | @inference_state_as_method_param_cache() | |
331 | def create_cached(cls, inference_state, *args, **kwargs): | |
332 | return cls(*args, **kwargs) | |
333 | ||
334 | def __getattr__(self, name): | |
335 | assert name != '_wrapped_value', 'Problem with _get_wrapped_value' | |
336 | return getattr(self._wrapped_value, name) | |
337 | ||
338 | ||
339 | class LazyValueWrapper(_ValueWrapperBase): | |
340 | @safe_property | |
341 | @memoize_method | |
342 | def _wrapped_value(self): | |
343 | with debug.increase_indent_cm('Resolve lazy value wrapper'): | |
344 | return self._get_wrapped_value() | |
345 | ||
346 | def __repr__(self): | |
347 | return '<%s>' % (self.__class__.__name__) | |
348 | ||
349 | def _get_wrapped_value(self): | |
350 | raise NotImplementedError | |
351 | ||
352 | ||
353 | class ValueWrapper(_ValueWrapperBase): | |
354 | def __init__(self, wrapped_value): | |
355 | self._wrapped_value = wrapped_value | |
356 | ||
357 | def __repr__(self): | |
358 | return '%s(%s)' % (self.__class__.__name__, self._wrapped_value) | |
359 | ||
360 | ||
361 | class TreeValue(Value): | |
362 | def __init__(self, inference_state, parent_context, tree_node): | |
363 | super().__init__(inference_state, parent_context) | |
364 | self.tree_node = tree_node | |
365 | ||
366 | def __repr__(self): | |
367 | return '<%s: %s>' % (self.__class__.__name__, self.tree_node) | |
368 | ||
369 | ||
370 | class ContextualizedNode: | |
371 | def __init__(self, context, node): | |
372 | self.context = context | |
373 | self.node = node | |
374 | ||
375 | def get_root_context(self): | |
376 | return self.context.get_root_context() | |
377 | ||
378 | def infer(self): | |
379 | return self.context.infer_node(self.node) | |
380 | ||
381 | def __repr__(self): | |
382 | return '<%s: %s in %s>' % (self.__class__.__name__, self.node, self.context) | |
383 | ||
384 | ||
385 | def _getitem(value, index_values, contextualized_node): | |
386 | # The actual getitem call. | |
387 | result = NO_VALUES | |
388 | unused_values = set() | |
389 | for index_value in index_values: | |
390 | index = index_value.get_safe_value(default=None) | |
391 | if type(index) in (float, int, str, slice, bytes): | |
392 | try: | |
393 | result |= value.py__simple_getitem__(index) | |
394 | continue | |
395 | except SimpleGetItemNotFound: | |
396 | pass | |
397 | ||
398 | unused_values.add(index_value) | |
399 | ||
400 | # The index was somehow not good enough or simply a wrong type. | |
401 | # Therefore we now iterate through all the values and just take | |
402 | # all results. | |
403 | if unused_values or not index_values: | |
404 | result |= value.py__getitem__( | |
405 | ValueSet(unused_values), | |
406 | contextualized_node | |
407 | ) | |
408 | debug.dbg('py__getitem__ result: %s', result) | |
409 | return result | |
410 | ||
411 | ||
412 | class ValueSet: | |
413 | def __init__(self, iterable): | |
414 | self._set = frozenset(iterable) | |
415 | for value in iterable: | |
416 | assert not isinstance(value, ValueSet) | |
417 | ||
418 | @classmethod | |
419 | def _from_frozen_set(cls, frozenset_): | |
420 | self = cls.__new__(cls) | |
421 | self._set = frozenset_ | |
422 | return self | |
423 | ||
424 | @classmethod | |
425 | def from_sets(cls, sets): | |
426 | """ | |
427 | Used to work with an iterable of set. | |
428 | """ | |
429 | aggregated = set() | |
430 | for set_ in sets: | |
431 | if isinstance(set_, ValueSet): | |
432 | aggregated |= set_._set | |
433 | else: | |
434 | aggregated |= frozenset(set_) | |
435 | return cls._from_frozen_set(frozenset(aggregated)) | |
436 | ||
437 | def __or__(self, other): | |
438 | return self._from_frozen_set(self._set | other._set) | |
439 | ||
440 | def __and__(self, other): | |
441 | return self._from_frozen_set(self._set & other._set) | |
442 | ||
443 | def __iter__(self): | |
444 | return iter(self._set) | |
445 | ||
446 | def __bool__(self): | |
447 | return bool(self._set) | |
448 | ||
449 | def __len__(self): | |
450 | return len(self._set) | |
451 | ||
452 | def __repr__(self): | |
453 | return 'S{%s}' % (', '.join(str(s) for s in self._set)) | |
454 | ||
455 | def filter(self, filter_func): | |
456 | return self.__class__(filter(filter_func, self._set)) | |
457 | ||
458 | def __getattr__(self, name): | |
459 | def mapper(*args, **kwargs): | |
460 | return self.from_sets( | |
461 | getattr(value, name)(*args, **kwargs) | |
462 | for value in self._set | |
463 | ) | |
464 | return mapper | |
465 | ||
466 | def __eq__(self, other): | |
467 | return self._set == other._set | |
468 | ||
469 | def __ne__(self, other): | |
470 | return not self.__eq__(other) | |
471 | ||
472 | def __hash__(self): | |
473 | return hash(self._set) | |
474 | ||
475 | def py__class__(self): | |
476 | return ValueSet(c.py__class__() for c in self._set) | |
477 | ||
478 | def iterate(self, contextualized_node=None, is_async=False): | |
479 | from jedi.inference.lazy_value import get_merged_lazy_value | |
480 | type_iters = [c.iterate(contextualized_node, is_async=is_async) for c in self._set] | |
481 | for lazy_values in zip_longest(*type_iters): | |
482 | yield get_merged_lazy_value( | |
483 | [l for l in lazy_values if l is not None] | |
484 | ) | |
485 | ||
486 | def execute(self, arguments): | |
487 | return ValueSet.from_sets(c.inference_state.execute(c, arguments) for c in self._set) | |
488 | ||
489 | def execute_with_values(self, *args, **kwargs): | |
490 | return ValueSet.from_sets(c.execute_with_values(*args, **kwargs) for c in self._set) | |
491 | ||
492 | def goto(self, *args, **kwargs): | |
493 | return reduce(add, [c.goto(*args, **kwargs) for c in self._set], []) | |
494 | ||
495 | def py__getattribute__(self, *args, **kwargs): | |
496 | return ValueSet.from_sets(c.py__getattribute__(*args, **kwargs) for c in self._set) | |
497 | ||
498 | def get_item(self, *args, **kwargs): | |
499 | return ValueSet.from_sets(_getitem(c, *args, **kwargs) for c in self._set) | |
500 | ||
501 | def try_merge(self, function_name): | |
502 | value_set = self.__class__([]) | |
503 | for c in self._set: | |
504 | try: | |
505 | method = getattr(c, function_name) | |
506 | except AttributeError: | |
507 | pass | |
508 | else: | |
509 | value_set |= method() | |
510 | return value_set | |
511 | ||
512 | def gather_annotation_classes(self): | |
513 | return ValueSet.from_sets([c.gather_annotation_classes() for c in self._set]) | |
514 | ||
515 | def get_signatures(self): | |
516 | return [sig for c in self._set for sig in c.get_signatures()] | |
517 | ||
518 | def get_type_hint(self, add_class_info=True): | |
519 | t = [v.get_type_hint(add_class_info=add_class_info) for v in self._set] | |
520 | type_hints = sorted(filter(None, t)) | |
521 | if len(type_hints) == 1: | |
522 | return type_hints[0] | |
523 | ||
524 | optional = 'None' in type_hints | |
525 | if optional: | |
526 | type_hints.remove('None') | |
527 | ||
528 | if len(type_hints) == 0: | |
529 | return None | |
530 | elif len(type_hints) == 1: | |
531 | s = type_hints[0] | |
532 | else: | |
533 | s = 'Union[%s]' % ', '.join(type_hints) | |
534 | if optional: | |
535 | s = 'Optional[%s]' % s | |
536 | return s | |
537 | ||
538 | def infer_type_vars(self, value_set): | |
539 | # Circular | |
540 | from jedi.inference.gradual.annotation import merge_type_var_dicts | |
541 | ||
542 | type_var_dict = {} | |
543 | for value in self._set: | |
544 | merge_type_var_dicts( | |
545 | type_var_dict, | |
546 | value.infer_type_vars(value_set), | |
547 | ) | |
548 | return type_var_dict | |
549 | ||
550 | ||
551 | NO_VALUES = ValueSet([]) | |
552 | ||
553 | ||
554 | def iterator_to_value_set(func): | |
555 | def wrapper(*args, **kwargs): | |
556 | return ValueSet(func(*args, **kwargs)) | |
557 | ||
558 | return wrapper |