Format: text/x-python
⬇ Download File (22.8 KB)
   1  """
   2  Contains all classes and functions to deal with lists, dicts, generators and
   3  iterators in general.
   4  """
   5  from jedi.inference import compiled
   6  from jedi.inference import analysis
   7  from jedi.inference.lazy_value import LazyKnownValue, LazyKnownValues, \
   8      LazyTreeValue
   9  from jedi.inference.helpers import get_int_or_none, is_string, \
  10      reraise_getitem_errors, SimpleGetItemNotFound
  11  from jedi.inference.utils import safe_property, to_list
  12  from jedi.inference.cache import inference_state_method_cache
  13  from jedi.inference.filters import LazyAttributeOverwrite, publish_method
  14  from jedi.inference.base_value import ValueSet, Value, NO_VALUES, \
  15      ContextualizedNode, iterate_values, sentinel, \
  16      LazyValueWrapper
  17  from jedi.parser_utils import get_sync_comp_fors
  18  from jedi.inference.context import CompForContext
  19  from jedi.inference.value.dynamic_arrays import check_array_additions
  20  
  21  
  22  class IterableMixin:
  23      def py__next__(self, contextualized_node=None):
  24          return self.py__iter__(contextualized_node)
  25  
  26      def py__stop_iteration_returns(self):
  27          return ValueSet([compiled.builtin_from_name(self.inference_state, 'None')])
  28  
  29      # At the moment, safe values are simple values like "foo", 1 and not
  30      # lists/dicts. Therefore as a small speed optimization we can just do the
  31      # default instead of resolving the lazy wrapped values, that are just
  32      # doing this in the end as well.
  33      # This mostly speeds up patterns like `sys.version_info >= (3, 0)` in
  34      # typeshed.
  35      get_safe_value = Value.get_safe_value
  36  
  37  
  38  class GeneratorBase(LazyAttributeOverwrite, IterableMixin):
  39      array_type = None
  40  
  41      def _get_wrapped_value(self):
  42          instance, = self._get_cls().execute_annotation()
  43          return instance
  44  
  45      def _get_cls(self):
  46          generator, = self.inference_state.typing_module.py__getattribute__('Generator')
  47          return generator
  48  
  49      def py__bool__(self):
  50          return True
  51  
  52      @publish_method('__iter__')
  53      def _iter(self, arguments):
  54          return ValueSet([self])
  55  
  56      @publish_method('send')
  57      @publish_method('__next__')
  58      def _next(self, arguments):
  59          return ValueSet.from_sets(lazy_value.infer() for lazy_value in self.py__iter__())
  60  
  61      def py__stop_iteration_returns(self):
  62          return ValueSet([compiled.builtin_from_name(self.inference_state, 'None')])
  63  
  64      @property
  65      def name(self):
  66          return compiled.CompiledValueName(self, 'Generator')
  67  
  68      def get_annotated_class_object(self):
  69          from jedi.inference.gradual.generics import TupleGenericManager
  70          gen_values = self.merge_types_of_iterate().py__class__()
  71          gm = TupleGenericManager((gen_values, NO_VALUES, NO_VALUES))
  72          return self._get_cls().with_generics(gm)
  73  
  74  
  75  class Generator(GeneratorBase):
  76      """Handling of `yield` functions."""
  77      def __init__(self, inference_state, func_execution_context):
  78          super().__init__(inference_state)
  79          self._func_execution_context = func_execution_context
  80  
  81      def py__iter__(self, contextualized_node=None):
  82          iterators = self._func_execution_context.infer_annotations()
  83          if iterators:
  84              return iterators.iterate(contextualized_node)
  85          return self._func_execution_context.get_yield_lazy_values()
  86  
  87      def py__stop_iteration_returns(self):
  88          return self._func_execution_context.get_return_values()
  89  
  90      def __repr__(self):
  91          return "<%s of %s>" % (type(self).__name__, self._func_execution_context)
  92  
  93  
  94  def comprehension_from_atom(inference_state, value, atom):
  95      bracket = atom.children[0]
  96      test_list_comp = atom.children[1]
  97  
  98      if bracket == '{':
  99          if atom.children[1].children[1] == ':':
 100              sync_comp_for = test_list_comp.children[3]
 101              if sync_comp_for.type == 'comp_for':
 102                  sync_comp_for = sync_comp_for.children[1]
 103  
 104              return DictComprehension(
 105                  inference_state,
 106                  value,
 107                  sync_comp_for_node=sync_comp_for,
 108                  key_node=test_list_comp.children[0],
 109                  value_node=test_list_comp.children[2],
 110              )
 111          else:
 112              cls = SetComprehension
 113      elif bracket == '(':
 114          cls = GeneratorComprehension
 115      elif bracket == '[':
 116          cls = ListComprehension
 117  
 118      sync_comp_for = test_list_comp.children[1]
 119      if sync_comp_for.type == 'comp_for':
 120          sync_comp_for = sync_comp_for.children[1]
 121  
 122      return cls(
 123          inference_state,
 124          defining_context=value,
 125          sync_comp_for_node=sync_comp_for,
 126          entry_node=test_list_comp.children[0],
 127      )
 128  
 129  
 130  class ComprehensionMixin:
 131      @inference_state_method_cache()
 132      def _get_comp_for_context(self, parent_context, comp_for):
 133          return CompForContext(parent_context, comp_for)
 134  
 135      def _nested(self, comp_fors, parent_context=None):
 136          comp_for = comp_fors[0]
 137  
 138          is_async = comp_for.parent.type == 'comp_for'
 139  
 140          input_node = comp_for.children[3]
 141          parent_context = parent_context or self._defining_context
 142          input_types = parent_context.infer_node(input_node)
 143  
 144          cn = ContextualizedNode(parent_context, input_node)
 145          iterated = input_types.iterate(cn, is_async=is_async)
 146          exprlist = comp_for.children[1]
 147          for i, lazy_value in enumerate(iterated):
 148              types = lazy_value.infer()
 149              dct = unpack_tuple_to_dict(parent_context, types, exprlist)
 150              context = self._get_comp_for_context(
 151                  parent_context,
 152                  comp_for,
 153              )
 154              with context.predefine_names(comp_for, dct):
 155                  try:
 156                      yield from self._nested(comp_fors[1:], context)
 157                  except IndexError:
 158                      iterated = context.infer_node(self._entry_node)
 159                      if self.array_type == 'dict':
 160                          yield iterated, context.infer_node(self._value_node)
 161                      else:
 162                          yield iterated
 163  
 164      @inference_state_method_cache(default=[])
 165      @to_list
 166      def _iterate(self):
 167          comp_fors = tuple(get_sync_comp_fors(self._sync_comp_for_node))
 168          yield from self._nested(comp_fors)
 169  
 170      def py__iter__(self, contextualized_node=None):
 171          for set_ in self._iterate():
 172              yield LazyKnownValues(set_)
 173  
 174      def __repr__(self):
 175          return "<%s of %s>" % (type(self).__name__, self._sync_comp_for_node)
 176  
 177  
 178  class _DictMixin:
 179      def _get_generics(self):
 180          return tuple(c_set.py__class__() for c_set in self.get_mapping_item_values())
 181  
 182  
 183  class Sequence(LazyAttributeOverwrite, IterableMixin):
 184      api_type = 'instance'
 185  
 186      @property
 187      def name(self):
 188          return compiled.CompiledValueName(self, self.array_type)
 189  
 190      def _get_generics(self):
 191          return (self.merge_types_of_iterate().py__class__(),)
 192  
 193      @inference_state_method_cache(default=())
 194      def _cached_generics(self):
 195          return self._get_generics()
 196  
 197      def _get_wrapped_value(self):
 198          from jedi.inference.gradual.base import GenericClass
 199          from jedi.inference.gradual.generics import TupleGenericManager
 200          klass = compiled.builtin_from_name(self.inference_state, self.array_type)
 201          c, = GenericClass(
 202              klass,
 203              TupleGenericManager(self._cached_generics())
 204          ).execute_annotation()
 205          return c
 206  
 207      def py__bool__(self):
 208          return None  # We don't know the length, because of appends.
 209  
 210      @safe_property
 211      def parent(self):
 212          return self.inference_state.builtins_module
 213  
 214      def py__getitem__(self, index_value_set, contextualized_node):
 215          if self.array_type == 'dict':
 216              return self._dict_values()
 217          return iterate_values(ValueSet([self]))
 218  
 219  
 220  class _BaseComprehension(ComprehensionMixin):
 221      def __init__(self, inference_state, defining_context, sync_comp_for_node, entry_node):
 222          assert sync_comp_for_node.type == 'sync_comp_for'
 223          super().__init__(inference_state)
 224          self._defining_context = defining_context
 225          self._sync_comp_for_node = sync_comp_for_node
 226          self._entry_node = entry_node
 227  
 228  
 229  class ListComprehension(_BaseComprehension, Sequence):
 230      array_type = 'list'
 231  
 232      def py__simple_getitem__(self, index):
 233          if isinstance(index, slice):
 234              return ValueSet([self])
 235  
 236          all_types = list(self.py__iter__())
 237          with reraise_getitem_errors(IndexError, TypeError):
 238              lazy_value = all_types[index]
 239          return lazy_value.infer()
 240  
 241  
 242  class SetComprehension(_BaseComprehension, Sequence):
 243      array_type = 'set'
 244  
 245  
 246  class GeneratorComprehension(_BaseComprehension, GeneratorBase):
 247      pass
 248  
 249  
 250  class _DictKeyMixin:
 251      # TODO merge with _DictMixin?
 252      def get_mapping_item_values(self):
 253          return self._dict_keys(), self._dict_values()
 254  
 255      def get_key_values(self):
 256          # TODO merge with _dict_keys?
 257          return self._dict_keys()
 258  
 259  
 260  class DictComprehension(ComprehensionMixin, Sequence, _DictKeyMixin):
 261      array_type = 'dict'
 262  
 263      def __init__(self, inference_state, defining_context, sync_comp_for_node, key_node, value_node):
 264          assert sync_comp_for_node.type == 'sync_comp_for'
 265          super().__init__(inference_state)
 266          self._defining_context = defining_context
 267          self._sync_comp_for_node = sync_comp_for_node
 268          self._entry_node = key_node
 269          self._value_node = value_node
 270  
 271      def py__iter__(self, contextualized_node=None):
 272          for keys, values in self._iterate():
 273              yield LazyKnownValues(keys)
 274  
 275      def py__simple_getitem__(self, index):
 276          for keys, values in self._iterate():
 277              for k in keys:
 278                  # Be careful in the future if refactoring, index could be a
 279                  # slice object.
 280                  if k.get_safe_value(default=object()) == index:
 281                      return values
 282          raise SimpleGetItemNotFound()
 283  
 284      def _dict_keys(self):
 285          return ValueSet.from_sets(keys for keys, values in self._iterate())
 286  
 287      def _dict_values(self):
 288          return ValueSet.from_sets(values for keys, values in self._iterate())
 289  
 290      @publish_method('values')
 291      def _imitate_values(self, arguments):
 292          lazy_value = LazyKnownValues(self._dict_values())
 293          return ValueSet([FakeList(self.inference_state, [lazy_value])])
 294  
 295      @publish_method('items')
 296      def _imitate_items(self, arguments):
 297          lazy_values = [
 298              LazyKnownValue(
 299                  FakeTuple(
 300                      self.inference_state,
 301                      [LazyKnownValues(key),
 302                       LazyKnownValues(value)]
 303                  )
 304              )
 305              for key, value in self._iterate()
 306          ]
 307  
 308          return ValueSet([FakeList(self.inference_state, lazy_values)])
 309  
 310      def exact_key_items(self):
 311          # NOTE: A smarter thing can probably done here to achieve better
 312          # completions, but at least like this jedi doesn't crash
 313          return []
 314  
 315  
 316  class SequenceLiteralValue(Sequence):
 317      _TUPLE_LIKE = 'testlist_star_expr', 'testlist', 'subscriptlist'
 318      mapping = {'(': 'tuple',
 319                 '[': 'list',
 320                 '{': 'set'}
 321  
 322      def __init__(self, inference_state, defining_context, atom):
 323          super().__init__(inference_state)
 324          self.atom = atom
 325          self._defining_context = defining_context
 326  
 327          if self.atom.type in self._TUPLE_LIKE:
 328              self.array_type = 'tuple'
 329          else:
 330              self.array_type = SequenceLiteralValue.mapping[atom.children[0]]
 331              """The builtin name of the array (list, set, tuple or dict)."""
 332  
 333      def _get_generics(self):
 334          if self.array_type == 'tuple':
 335              return tuple(x.infer().py__class__() for x in self.py__iter__())
 336          return super()._get_generics()
 337  
 338      def py__simple_getitem__(self, index):
 339          """Here the index is an int/str. Raises IndexError/KeyError."""
 340          if isinstance(index, slice):
 341              return ValueSet([self])
 342          else:
 343              with reraise_getitem_errors(TypeError, KeyError, IndexError):
 344                  node = self.get_tree_entries()[index]
 345              if node == ':' or node.type == 'subscript':
 346                  return NO_VALUES
 347              return self._defining_context.infer_node(node)
 348  
 349      def py__iter__(self, contextualized_node=None):
 350          """
 351          While values returns the possible values for any array field, this
 352          function returns the value for a certain index.
 353          """
 354          for node in self.get_tree_entries():
 355              if node == ':' or node.type == 'subscript':
 356                  # TODO this should probably use at least part of the code
 357                  #      of infer_subscript_list.
 358                  yield LazyKnownValue(Slice(self._defining_context, None, None, None))
 359              else:
 360                  yield LazyTreeValue(self._defining_context, node)
 361          yield from check_array_additions(self._defining_context, self)
 362  
 363      def py__len__(self):
 364          # This function is not really used often. It's more of a try.
 365          return len(self.get_tree_entries())
 366  
 367      def get_tree_entries(self):
 368          c = self.atom.children
 369  
 370          if self.atom.type in self._TUPLE_LIKE:
 371              return c[::2]
 372  
 373          array_node = c[1]
 374          if array_node in (']', '}', ')'):
 375              return []  # Direct closing bracket, doesn't contain items.
 376  
 377          if array_node.type == 'testlist_comp':
 378              # filter out (for now) pep 448 single-star unpacking
 379              return [value for value in array_node.children[::2]
 380                      if value.type != "star_expr"]
 381          elif array_node.type == 'dictorsetmaker':
 382              kv = []
 383              iterator = iter(array_node.children)
 384              for key in iterator:
 385                  if key == "**":
 386                      # dict with pep 448 double-star unpacking
 387                      # for now ignoring the values imported by **
 388                      next(iterator)
 389                      next(iterator, None)  # Possible comma.
 390                  else:
 391                      op = next(iterator, None)
 392                      if op is None or op == ',':
 393                          if key.type == "star_expr":
 394                              # pep 448 single-star unpacking
 395                              # for now ignoring values imported by *
 396                              pass
 397                          else:
 398                              kv.append(key)  # A set.
 399                      else:
 400                          assert op == ':'  # A dict.
 401                          kv.append((key, next(iterator)))
 402                          next(iterator, None)  # Possible comma.
 403              return kv
 404          else:
 405              if array_node.type == "star_expr":
 406                  # pep 448 single-star unpacking
 407                  # for now ignoring values imported by *
 408                  return []
 409              else:
 410                  return [array_node]
 411  
 412      def __repr__(self):
 413          return "<%s of %s>" % (self.__class__.__name__, self.atom)
 414  
 415  
 416  class DictLiteralValue(_DictMixin, SequenceLiteralValue, _DictKeyMixin):
 417      array_type = 'dict'
 418  
 419      def __init__(self, inference_state, defining_context, atom):
 420          # Intentionally don't call the super class. This is definitely a sign
 421          # that the architecture is bad and we should refactor.
 422          Sequence.__init__(self, inference_state)
 423          self._defining_context = defining_context
 424          self.atom = atom
 425  
 426      def py__simple_getitem__(self, index):
 427          """Here the index is an int/str. Raises IndexError/KeyError."""
 428          compiled_value_index = compiled.create_simple_object(self.inference_state, index)
 429          for key, value in self.get_tree_entries():
 430              for k in self._defining_context.infer_node(key):
 431                  for key_v in k.execute_operation(compiled_value_index, '=='):
 432                      if key_v.get_safe_value():
 433                          return self._defining_context.infer_node(value)
 434          raise SimpleGetItemNotFound('No key found in dictionary %s.' % self)
 435  
 436      def py__iter__(self, contextualized_node=None):
 437          """
 438          While values returns the possible values for any array field, this
 439          function returns the value for a certain index.
 440          """
 441          # Get keys.
 442          types = NO_VALUES
 443          for k, _ in self.get_tree_entries():
 444              types |= self._defining_context.infer_node(k)
 445          # We don't know which dict index comes first, therefore always
 446          # yield all the types.
 447          for _ in types:
 448              yield LazyKnownValues(types)
 449  
 450      @publish_method('values')
 451      def _imitate_values(self, arguments):
 452          lazy_value = LazyKnownValues(self._dict_values())
 453          return ValueSet([FakeList(self.inference_state, [lazy_value])])
 454  
 455      @publish_method('items')
 456      def _imitate_items(self, arguments):
 457          lazy_values = [
 458              LazyKnownValue(FakeTuple(
 459                  self.inference_state,
 460                  (LazyTreeValue(self._defining_context, key_node),
 461                   LazyTreeValue(self._defining_context, value_node))
 462              )) for key_node, value_node in self.get_tree_entries()
 463          ]
 464  
 465          return ValueSet([FakeList(self.inference_state, lazy_values)])
 466  
 467      def exact_key_items(self):
 468          """
 469          Returns a generator of tuples like dict.items(), where the key is
 470          resolved (as a string) and the values are still lazy values.
 471          """
 472          for key_node, value in self.get_tree_entries():
 473              for key in self._defining_context.infer_node(key_node):
 474                  if is_string(key):
 475                      yield key.get_safe_value(), LazyTreeValue(self._defining_context, value)
 476  
 477      def _dict_values(self):
 478          return ValueSet.from_sets(
 479              self._defining_context.infer_node(v)
 480              for k, v in self.get_tree_entries()
 481          )
 482  
 483      def _dict_keys(self):
 484          return ValueSet.from_sets(
 485              self._defining_context.infer_node(k)
 486              for k, v in self.get_tree_entries()
 487          )
 488  
 489  
 490  class _FakeSequence(Sequence):
 491      def __init__(self, inference_state, lazy_value_list):
 492          """
 493          type should be one of "tuple", "list"
 494          """
 495          super().__init__(inference_state)
 496          self._lazy_value_list = lazy_value_list
 497  
 498      def py__simple_getitem__(self, index):
 499          if isinstance(index, slice):
 500              return ValueSet([self])
 501  
 502          with reraise_getitem_errors(IndexError, TypeError):
 503              lazy_value = self._lazy_value_list[index]
 504          return lazy_value.infer()
 505  
 506      def py__iter__(self, contextualized_node=None):
 507          return self._lazy_value_list
 508  
 509      def py__bool__(self):
 510          return bool(len(self._lazy_value_list))
 511  
 512      def __repr__(self):
 513          return "<%s of %s>" % (type(self).__name__, self._lazy_value_list)
 514  
 515  
 516  class FakeTuple(_FakeSequence):
 517      array_type = 'tuple'
 518  
 519  
 520  class FakeList(_FakeSequence):
 521      array_type = 'tuple'
 522  
 523  
 524  class FakeDict(_DictMixin, Sequence, _DictKeyMixin):
 525      array_type = 'dict'
 526  
 527      def __init__(self, inference_state, dct):
 528          super().__init__(inference_state)
 529          self._dct = dct
 530  
 531      def py__iter__(self, contextualized_node=None):
 532          for key in self._dct:
 533              yield LazyKnownValue(compiled.create_simple_object(self.inference_state, key))
 534  
 535      def py__simple_getitem__(self, index):
 536          with reraise_getitem_errors(KeyError, TypeError):
 537              lazy_value = self._dct[index]
 538          return lazy_value.infer()
 539  
 540      @publish_method('values')
 541      def _values(self, arguments):
 542          return ValueSet([FakeTuple(
 543              self.inference_state,
 544              [LazyKnownValues(self._dict_values())]
 545          )])
 546  
 547      def _dict_values(self):
 548          return ValueSet.from_sets(lazy_value.infer() for lazy_value in self._dct.values())
 549  
 550      def _dict_keys(self):
 551          return ValueSet.from_sets(lazy_value.infer() for lazy_value in self.py__iter__())
 552  
 553      def exact_key_items(self):
 554          return self._dct.items()
 555  
 556      def __repr__(self):
 557          return '<%s: %s>' % (self.__class__.__name__, self._dct)
 558  
 559  
 560  class MergedArray(Sequence):
 561      def __init__(self, inference_state, arrays):
 562          super().__init__(inference_state)
 563          self.array_type = arrays[-1].array_type
 564          self._arrays = arrays
 565  
 566      def py__iter__(self, contextualized_node=None):
 567          for array in self._arrays:
 568              yield from array.py__iter__()
 569  
 570      def py__simple_getitem__(self, index):
 571          return ValueSet.from_sets(lazy_value.infer() for lazy_value in self.py__iter__())
 572  
 573  
 574  def unpack_tuple_to_dict(context, types, exprlist):
 575      """
 576      Unpacking tuple assignments in for statements and expr_stmts.
 577      """
 578      if exprlist.type == 'name':
 579          return {exprlist.value: types}
 580      elif exprlist.type == 'atom' and exprlist.children[0] in ('(', '['):
 581          return unpack_tuple_to_dict(context, types, exprlist.children[1])
 582      elif exprlist.type in ('testlist', 'testlist_comp', 'exprlist',
 583                             'testlist_star_expr'):
 584          dct = {}
 585          parts = iter(exprlist.children[::2])
 586          n = 0
 587          for lazy_value in types.iterate(ContextualizedNode(context, exprlist)):
 588              n += 1
 589              try:
 590                  part = next(parts)
 591              except StopIteration:
 592                  analysis.add(context, 'value-error-too-many-values', part,
 593                               message="ValueError: too many values to unpack (expected %s)" % n)
 594              else:
 595                  dct.update(unpack_tuple_to_dict(context, lazy_value.infer(), part))
 596          has_parts = next(parts, None)
 597          if types and has_parts is not None:
 598              analysis.add(context, 'value-error-too-few-values', has_parts,
 599                           message="ValueError: need more than %s values to unpack" % n)
 600          return dct
 601      elif exprlist.type == 'power' or exprlist.type == 'atom_expr':
 602          # Something like ``arr[x], var = ...``.
 603          # This is something that is not yet supported, would also be difficult
 604          # to write into a dict.
 605          return {}
 606      elif exprlist.type == 'star_expr':  # `a, *b, c = x` type unpackings
 607          # Currently we're not supporting them.
 608          return {}
 609      raise NotImplementedError
 610  
 611  
 612  class Slice(LazyValueWrapper):
 613      def __init__(self, python_context, start, stop, step):
 614          self.inference_state = python_context.inference_state
 615          self._context = python_context
 616          # All of them are either a Precedence or None.
 617          self._start = start
 618          self._stop = stop
 619          self._step = step
 620  
 621      def _get_wrapped_value(self):
 622          value = compiled.builtin_from_name(self._context.inference_state, 'slice')
 623          slice_value, = value.execute_with_values()
 624          return slice_value
 625  
 626      def get_safe_value(self, default=sentinel):
 627          """
 628          Imitate CompiledValue.obj behavior and return a ``builtin.slice()``
 629          object.
 630          """
 631          def get(element):
 632              if element is None:
 633                  return None
 634  
 635              result = self._context.infer_node(element)
 636              if len(result) != 1:
 637                  # For simplicity, we want slices to be clear defined with just
 638                  # one type.  Otherwise we will return an empty slice object.
 639                  raise IndexError
 640  
 641              value, = result
 642              return get_int_or_none(value)
 643  
 644          try:
 645              return slice(get(self._start), get(self._stop), get(self._step))
 646          except IndexError:
 647              return slice(None, None, None)
 648