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