Format: text/x-python
⬇ Download File (17.8 KB)
   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
 559