| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558 |
- """
- Values are the "values" that Python would return. However Values are at the
- same time also the "values" that a user is currently sitting in.
- A ValueSet is typically used to specify the return of a function or any other
- static analysis operation. In jedi there are always multiple returns and not
- just one.
- """
- from functools import reduce
- from operator import add
- from itertools import zip_longest
- from parso.python.tree import Name
- from jedi import debug
- from jedi.parser_utils import clean_scope_docstring
- from jedi.inference.helpers import SimpleGetItemNotFound
- from jedi.inference.utils import safe_property
- from jedi.inference.cache import inference_state_as_method_param_cache
- from jedi.cache import memoize_method
- sentinel = object()
- class HasNoContext(Exception):
- pass
- class HelperValueMixin:
- def get_root_context(self):
- value = self
- if value.parent_context is None:
- return value.as_context()
- while True:
- if value.parent_context is None:
- return value
- value = value.parent_context
- def execute(self, arguments):
- return self.inference_state.execute(self, arguments=arguments)
- def execute_with_values(self, *value_list):
- from jedi.inference.arguments import ValuesArguments
- arguments = ValuesArguments([ValueSet([value]) for value in value_list])
- return self.inference_state.execute(self, arguments)
- def execute_annotation(self):
- return self.execute_with_values()
- def gather_annotation_classes(self):
- return ValueSet([self])
- def merge_types_of_iterate(self, contextualized_node=None, is_async=False):
- return ValueSet.from_sets(
- lazy_value.infer()
- for lazy_value in self.iterate(contextualized_node, is_async)
- )
- def _get_value_filters(self, name_or_str):
- origin_scope = name_or_str if isinstance(name_or_str, Name) else None
- yield from self.get_filters(origin_scope=origin_scope)
- # This covers the case where a stub files are incomplete.
- if self.is_stub():
- from jedi.inference.gradual.conversion import convert_values
- for c in convert_values(ValueSet({self})):
- yield from c.get_filters()
- def goto(self, name_or_str, name_context=None, analysis_errors=True):
- from jedi.inference import finder
- filters = self._get_value_filters(name_or_str)
- names = finder.filter_name(filters, name_or_str)
- debug.dbg('context.goto %s in (%s): %s', name_or_str, self, names)
- return names
- def py__getattribute__(self, name_or_str, name_context=None, position=None,
- analysis_errors=True):
- """
- :param position: Position of the last statement -> tuple of line, column
- """
- if name_context is None:
- name_context = self
- names = self.goto(name_or_str, name_context, analysis_errors)
- values = ValueSet.from_sets(name.infer() for name in names)
- if not values:
- n = name_or_str.value if isinstance(name_or_str, Name) else name_or_str
- values = self.py__getattribute__alternatives(n)
- if not names and not values and analysis_errors:
- if isinstance(name_or_str, Name):
- from jedi.inference import analysis
- analysis.add_attribute_error(
- name_context, self, name_or_str)
- debug.dbg('context.names_to_types: %s -> %s', names, values)
- return values
- def py__await__(self):
- await_value_set = self.py__getattribute__("__await__")
- if not await_value_set:
- debug.warning('Tried to run __await__ on value %s', self)
- return await_value_set.execute_with_values()
- def py__name__(self):
- return self.name.string_name
- def iterate(self, contextualized_node=None, is_async=False):
- debug.dbg('iterate %s', self)
- if is_async:
- from jedi.inference.lazy_value import LazyKnownValues
- # TODO if no __aiter__ values are there, error should be:
- # TypeError: 'async for' requires an object with __aiter__ method, got int
- return iter([
- LazyKnownValues(
- self.py__getattribute__('__aiter__').execute_with_values()
- .py__getattribute__('__anext__').execute_with_values()
- .py__getattribute__('__await__').execute_with_values()
- .py__stop_iteration_returns()
- ) # noqa: E124
- ])
- return self.py__iter__(contextualized_node)
- def is_sub_class_of(self, class_value):
- with debug.increase_indent_cm('subclass matching of %s <=> %s' % (self, class_value),
- color='BLUE'):
- for cls in self.py__mro__():
- if cls.is_same_class(class_value):
- debug.dbg('matched subclass True', color='BLUE')
- return True
- debug.dbg('matched subclass False', color='BLUE')
- return False
- def is_same_class(self, class2):
- # Class matching should prefer comparisons that are not this function.
- if type(class2).is_same_class != HelperValueMixin.is_same_class:
- return class2.is_same_class(self)
- return self == class2
- @memoize_method
- def as_context(self, *args, **kwargs):
- return self._as_context(*args, **kwargs)
- class Value(HelperValueMixin):
- """
- To be implemented by subclasses.
- """
- tree_node = None
- # Possible values: None, tuple, list, dict and set. Here to deal with these
- # very important containers.
- array_type = None
- api_type = 'not_defined_please_report_bug'
- def __init__(self, inference_state, parent_context=None):
- self.inference_state = inference_state
- self.parent_context = parent_context
- def py__getitem__(self, index_value_set, contextualized_node):
- from jedi.inference import analysis
- # TODO this value is probably not right.
- analysis.add(
- contextualized_node.context,
- 'type-error-not-subscriptable',
- contextualized_node.node,
- message="TypeError: '%s' object is not subscriptable" % self
- )
- return NO_VALUES
- def py__simple_getitem__(self, index):
- raise SimpleGetItemNotFound
- def py__iter__(self, contextualized_node=None):
- if contextualized_node is not None:
- from jedi.inference import analysis
- analysis.add(
- contextualized_node.context,
- 'type-error-not-iterable',
- contextualized_node.node,
- message="TypeError: '%s' object is not iterable" % self)
- return iter([])
- def py__next__(self, contextualized_node=None):
- return self.py__iter__(contextualized_node)
- def get_signatures(self):
- return []
- def is_class(self):
- return False
- def is_class_mixin(self):
- return False
- def is_instance(self):
- return False
- def is_function(self):
- return False
- def is_module(self):
- return False
- def is_namespace(self):
- return False
- def is_compiled(self):
- return False
- def is_bound_method(self):
- return False
- def is_builtins_module(self):
- return False
- def py__bool__(self):
- """
- Since Wrapper is a super class for classes, functions and modules,
- the return value will always be true.
- """
- return True
- def py__doc__(self):
- try:
- self.tree_node.get_doc_node
- except AttributeError:
- return ''
- else:
- return clean_scope_docstring(self.tree_node)
- def get_safe_value(self, default=sentinel):
- if default is sentinel:
- raise ValueError("There exists no safe value for value %s" % self)
- return default
- def execute_operation(self, other, operator):
- debug.warning("%s not possible between %s and %s", operator, self, other)
- return NO_VALUES
- def py__call__(self, arguments):
- debug.warning("no execution possible %s", self)
- return NO_VALUES
- def py__stop_iteration_returns(self):
- debug.warning("Not possible to return the stop iterations of %s", self)
- return NO_VALUES
- def py__getattribute__alternatives(self, name_or_str):
- """
- For now a way to add values in cases like __getattr__.
- """
- return NO_VALUES
- def py__get__(self, instance, class_value):
- debug.warning("No __get__ defined on %s", self)
- return ValueSet([self])
- def py__get__on_class(self, calling_instance, instance, class_value):
- return NotImplemented
- def get_qualified_names(self):
- # Returns Optional[Tuple[str, ...]]
- return None
- def is_stub(self):
- # The root value knows if it's a stub or not.
- return self.parent_context.is_stub()
- def _as_context(self):
- raise HasNoContext
- @property
- def name(self):
- raise NotImplementedError
- def get_type_hint(self, add_class_info=True):
- return None
- def infer_type_vars(self, value_set):
- """
- When the current instance represents a type annotation, this method
- tries to find information about undefined type vars and returns a dict
- from type var name to value set.
- This is for example important to understand what `iter([1])` returns.
- According to typeshed, `iter` returns an `Iterator[_T]`:
- def iter(iterable: Iterable[_T]) -> Iterator[_T]: ...
- This functions would generate `int` for `_T` in this case, because it
- unpacks the `Iterable`.
- Parameters
- ----------
- `self`: represents the annotation of the current parameter to infer the
- value for. In the above example, this would initially be the
- `Iterable[_T]` of the `iterable` parameter and then, when recursing,
- just the `_T` generic parameter.
- `value_set`: represents the actual argument passed to the parameter
- we're inferred for, or (for recursive calls) their types. In the
- above example this would first be the representation of the list
- `[1]` and then, when recursing, just of `1`.
- """
- return {}
- def iterate_values(values, contextualized_node=None, is_async=False):
- """
- Calls `iterate`, on all values but ignores the ordering and just returns
- all values that the iterate functions yield.
- """
- return ValueSet.from_sets(
- lazy_value.infer()
- for lazy_value in values.iterate(contextualized_node, is_async=is_async)
- )
- class _ValueWrapperBase(HelperValueMixin):
- @safe_property
- def name(self):
- from jedi.inference.names import ValueName
- wrapped_name = self._wrapped_value.name
- if wrapped_name.tree_name is not None:
- return ValueName(self, wrapped_name.tree_name)
- else:
- from jedi.inference.compiled import CompiledValueName
- return CompiledValueName(self, wrapped_name.string_name)
- @classmethod
- @inference_state_as_method_param_cache()
- def create_cached(cls, inference_state, *args, **kwargs):
- return cls(*args, **kwargs)
- def __getattr__(self, name):
- assert name != '_wrapped_value', 'Problem with _get_wrapped_value'
- return getattr(self._wrapped_value, name)
- class LazyValueWrapper(_ValueWrapperBase):
- @safe_property
- @memoize_method
- def _wrapped_value(self):
- with debug.increase_indent_cm('Resolve lazy value wrapper'):
- return self._get_wrapped_value()
- def __repr__(self):
- return '<%s>' % (self.__class__.__name__)
- def _get_wrapped_value(self):
- raise NotImplementedError
- class ValueWrapper(_ValueWrapperBase):
- def __init__(self, wrapped_value):
- self._wrapped_value = wrapped_value
- def __repr__(self):
- return '%s(%s)' % (self.__class__.__name__, self._wrapped_value)
- class TreeValue(Value):
- def __init__(self, inference_state, parent_context, tree_node):
- super().__init__(inference_state, parent_context)
- self.tree_node = tree_node
- def __repr__(self):
- return '<%s: %s>' % (self.__class__.__name__, self.tree_node)
- class ContextualizedNode:
- def __init__(self, context, node):
- self.context = context
- self.node = node
- def get_root_context(self):
- return self.context.get_root_context()
- def infer(self):
- return self.context.infer_node(self.node)
- def __repr__(self):
- return '<%s: %s in %s>' % (self.__class__.__name__, self.node, self.context)
- def _getitem(value, index_values, contextualized_node):
- # The actual getitem call.
- result = NO_VALUES
- unused_values = set()
- for index_value in index_values:
- index = index_value.get_safe_value(default=None)
- if type(index) in (float, int, str, slice, bytes):
- try:
- result |= value.py__simple_getitem__(index)
- continue
- except SimpleGetItemNotFound:
- pass
- unused_values.add(index_value)
- # The index was somehow not good enough or simply a wrong type.
- # Therefore we now iterate through all the values and just take
- # all results.
- if unused_values or not index_values:
- result |= value.py__getitem__(
- ValueSet(unused_values),
- contextualized_node
- )
- debug.dbg('py__getitem__ result: %s', result)
- return result
- class ValueSet:
- def __init__(self, iterable):
- self._set = frozenset(iterable)
- for value in iterable:
- assert not isinstance(value, ValueSet)
- @classmethod
- def _from_frozen_set(cls, frozenset_):
- self = cls.__new__(cls)
- self._set = frozenset_
- return self
- @classmethod
- def from_sets(cls, sets):
- """
- Used to work with an iterable of set.
- """
- aggregated = set()
- for set_ in sets:
- if isinstance(set_, ValueSet):
- aggregated |= set_._set
- else:
- aggregated |= frozenset(set_)
- return cls._from_frozen_set(frozenset(aggregated))
- def __or__(self, other):
- return self._from_frozen_set(self._set | other._set)
- def __and__(self, other):
- return self._from_frozen_set(self._set & other._set)
- def __iter__(self):
- return iter(self._set)
- def __bool__(self):
- return bool(self._set)
- def __len__(self):
- return len(self._set)
- def __repr__(self):
- return 'S{%s}' % (', '.join(str(s) for s in self._set))
- def filter(self, filter_func):
- return self.__class__(filter(filter_func, self._set))
- def __getattr__(self, name):
- def mapper(*args, **kwargs):
- return self.from_sets(
- getattr(value, name)(*args, **kwargs)
- for value in self._set
- )
- return mapper
- def __eq__(self, other):
- return self._set == other._set
- def __ne__(self, other):
- return not self.__eq__(other)
- def __hash__(self):
- return hash(self._set)
- def py__class__(self):
- return ValueSet(c.py__class__() for c in self._set)
- def iterate(self, contextualized_node=None, is_async=False):
- from jedi.inference.lazy_value import get_merged_lazy_value
- type_iters = [c.iterate(contextualized_node, is_async=is_async) for c in self._set]
- for lazy_values in zip_longest(*type_iters):
- yield get_merged_lazy_value(
- [l for l in lazy_values if l is not None]
- )
- def execute(self, arguments):
- return ValueSet.from_sets(c.inference_state.execute(c, arguments) for c in self._set)
- def execute_with_values(self, *args, **kwargs):
- return ValueSet.from_sets(c.execute_with_values(*args, **kwargs) for c in self._set)
- def goto(self, *args, **kwargs):
- return reduce(add, [c.goto(*args, **kwargs) for c in self._set], [])
- def py__getattribute__(self, *args, **kwargs):
- return ValueSet.from_sets(c.py__getattribute__(*args, **kwargs) for c in self._set)
- def get_item(self, *args, **kwargs):
- return ValueSet.from_sets(_getitem(c, *args, **kwargs) for c in self._set)
- def try_merge(self, function_name):
- value_set = self.__class__([])
- for c in self._set:
- try:
- method = getattr(c, function_name)
- except AttributeError:
- pass
- else:
- value_set |= method()
- return value_set
- def gather_annotation_classes(self):
- return ValueSet.from_sets([c.gather_annotation_classes() for c in self._set])
- def get_signatures(self):
- return [sig for c in self._set for sig in c.get_signatures()]
- def get_type_hint(self, add_class_info=True):
- t = [v.get_type_hint(add_class_info=add_class_info) for v in self._set]
- type_hints = sorted(filter(None, t))
- if len(type_hints) == 1:
- return type_hints[0]
- optional = 'None' in type_hints
- if optional:
- type_hints.remove('None')
- if len(type_hints) == 0:
- return None
- elif len(type_hints) == 1:
- s = type_hints[0]
- else:
- s = 'Union[%s]' % ', '.join(type_hints)
- if optional:
- s = 'Optional[%s]' % s
- return s
- def infer_type_vars(self, value_set):
- # Circular
- from jedi.inference.gradual.annotation import merge_type_var_dicts
- type_var_dict = {}
- for value in self._set:
- merge_type_var_dicts(
- type_var_dict,
- value.infer_type_vars(value_set),
- )
- return type_var_dict
- NO_VALUES = ValueSet([])
- def iterator_to_value_set(func):
- def wrapper(*args, **kwargs):
- return ValueSet(func(*args, **kwargs))
- return wrapper
|