Format: text/x-python
⬇ Download File (5.2 KB)
   1  """
   2  Searching for names with given scope and name. This is very central in Jedi and
   3  Python. The name resolution is quite complicated with descripter,
   4  ``__getattribute__``, ``__getattr__``, ``global``, etc.
   5  
   6  If you want to understand name resolution, please read the first few chapters
   7  in http://blog.ionelmc.ro/2015/02/09/understanding-python-metaclasses/.
   8  
   9  Flow checks
  10  +++++++++++
  11  
  12  Flow checks are not really mature. There's only a check for ``isinstance``.  It
  13  would check whether a flow has the form of ``if isinstance(a, type_or_tuple)``.
  14  Unfortunately every other thing is being ignored (e.g. a == '' would be easy to
  15  check for -> a is a string). There's big potential in these checks.
  16  """
  17  
  18  from parso.tree import search_ancestor
  19  from parso.python.tree import Name
  20  
  21  from jedi import settings
  22  from jedi.inference.arguments import TreeArguments
  23  from jedi.inference.value import iterable
  24  from jedi.inference.base_value import NO_VALUES
  25  from jedi.parser_utils import is_scope
  26  
  27  
  28  def filter_name(filters, name_or_str):
  29      """
  30      Searches names that are defined in a scope (the different
  31      ``filters``), until a name fits.
  32      """
  33      string_name = name_or_str.value if isinstance(name_or_str, Name) else name_or_str
  34      names = []
  35      for filter in filters:
  36          names = filter.get(string_name)
  37          if names:
  38              break
  39  
  40      return list(_remove_del_stmt(names))
  41  
  42  
  43  def _remove_del_stmt(names):
  44      # Catch del statements and remove them from results.
  45      for name in names:
  46          if name.tree_name is not None:
  47              definition = name.tree_name.get_definition()
  48              if definition is not None and definition.type == 'del_stmt':
  49                  continue
  50          yield name
  51  
  52  
  53  def check_flow_information(value, flow, search_name, pos):
  54      """ Try to find out the type of a variable just with the information that
  55      is given by the flows: e.g. It is also responsible for assert checks.::
  56  
  57          if isinstance(k, str):
  58              k.  # <- completion here
  59  
  60      ensures that `k` is a string.
  61      """
  62      if not settings.dynamic_flow_information:
  63          return None
  64  
  65      result = None
  66      if is_scope(flow):
  67          # Check for asserts.
  68          module_node = flow.get_root_node()
  69          try:
  70              names = module_node.get_used_names()[search_name.value]
  71          except KeyError:
  72              return None
  73          names = reversed([
  74              n for n in names
  75              if flow.start_pos <= n.start_pos < (pos or flow.end_pos)
  76          ])
  77  
  78          for name in names:
  79              ass = search_ancestor(name, 'assert_stmt')
  80              if ass is not None:
  81                  result = _check_isinstance_type(value, ass.assertion, search_name)
  82                  if result is not None:
  83                      return result
  84  
  85      if flow.type in ('if_stmt', 'while_stmt'):
  86          potential_ifs = [c for c in flow.children[1::4] if c != ':']
  87          for if_test in reversed(potential_ifs):
  88              if search_name.start_pos > if_test.end_pos:
  89                  return _check_isinstance_type(value, if_test, search_name)
  90      return result
  91  
  92  
  93  def _get_isinstance_trailer_arglist(node):
  94      if node.type in ('power', 'atom_expr') and len(node.children) == 2:
  95          # This might be removed if we analyze and, etc
  96          first, trailer = node.children
  97          if first.type == 'name' and first.value == 'isinstance' \
  98                  and trailer.type == 'trailer' and trailer.children[0] == '(':
  99              return trailer
 100      return None
 101  
 102  
 103  def _check_isinstance_type(value, node, search_name):
 104      lazy_cls = None
 105      trailer = _get_isinstance_trailer_arglist(node)
 106      if trailer is not None and len(trailer.children) == 3:
 107          arglist = trailer.children[1]
 108          args = TreeArguments(value.inference_state, value, arglist, trailer)
 109          param_list = list(args.unpack())
 110          # Disallow keyword arguments
 111          if len(param_list) == 2 and len(arglist.children) == 3:
 112              (key1, _), (key2, lazy_value_cls) = param_list
 113              if key1 is None and key2 is None:
 114                  call = _get_call_string(search_name)
 115                  is_instance_call = _get_call_string(arglist.children[0])
 116                  # Do a simple get_code comparison of the strings . They should
 117                  # just have the same code, and everything will be all right.
 118                  # There are ways that this is not correct, if some stuff is
 119                  # redefined in between. However here we don't care, because
 120                  # it's a heuristic that works pretty well.
 121                  if call == is_instance_call:
 122                      lazy_cls = lazy_value_cls
 123      if lazy_cls is None:
 124          return None
 125  
 126      value_set = NO_VALUES
 127      for cls_or_tup in lazy_cls.infer():
 128          if isinstance(cls_or_tup, iterable.Sequence) and cls_or_tup.array_type == 'tuple':
 129              for lazy_value in cls_or_tup.py__iter__():
 130                  value_set |= lazy_value.infer().execute_with_values()
 131          else:
 132              value_set |= cls_or_tup.execute_with_values()
 133      return value_set
 134  
 135  
 136  def _get_call_string(node):
 137      if node.parent.type == 'atom_expr':
 138          return _get_call_string(node.parent)
 139  
 140      code = ''
 141      leaf = node.get_first_leaf()
 142      end = node.get_last_leaf().end_pos
 143      while leaf.start_pos < end:
 144          code += leaf.value
 145          leaf = leaf.get_next_leaf()
 146      return code
 147