Format: text/x-python
⬇ Download File (5.8 KB)
   1  import copy
   2  import sys
   3  import re
   4  import os
   5  from itertools import chain
   6  from contextlib import contextmanager
   7  
   8  from parso.python import tree
   9  
  10  
  11  def is_stdlib_path(path):
  12      # Python standard library paths look like this:
  13      # /usr/lib/python3.9/...
  14      # TODO The implementation below is probably incorrect and not complete.
  15      parts = path.parts
  16      if 'dist-packages' in parts or 'site-packages' in parts:
  17          return False
  18  
  19      base_path = os.path.join(sys.prefix, 'lib', 'python')
  20      return bool(re.match(re.escape(base_path) + r'\d.\d', str(path)))
  21  
  22  
  23  def deep_ast_copy(obj):
  24      """
  25      Much, much faster than copy.deepcopy, but just for parser tree nodes.
  26      """
  27      # If it's already in the cache, just return it.
  28      new_obj = copy.copy(obj)
  29  
  30      # Copy children
  31      new_children = []
  32      for child in obj.children:
  33          if isinstance(child, tree.Leaf):
  34              new_child = copy.copy(child)
  35              new_child.parent = new_obj
  36          else:
  37              new_child = deep_ast_copy(child)
  38              new_child.parent = new_obj
  39          new_children.append(new_child)
  40      new_obj.children = new_children
  41  
  42      return new_obj
  43  
  44  
  45  def infer_call_of_leaf(context, leaf, cut_own_trailer=False):
  46      """
  47      Creates a "call" node that consist of all ``trailer`` and ``power``
  48      objects.  E.g. if you call it with ``append``::
  49  
  50          list([]).append(3) or None
  51  
  52      You would get a node with the content ``list([]).append`` back.
  53  
  54      This generates a copy of the original ast node.
  55  
  56      If you're using the leaf, e.g. the bracket `)` it will return ``list([])``.
  57  
  58      We use this function for two purposes. Given an expression ``bar.foo``,
  59      we may want to
  60        - infer the type of ``foo`` to offer completions after foo
  61        - infer the type of ``bar`` to be able to jump to the definition of foo
  62      The option ``cut_own_trailer`` must be set to true for the second purpose.
  63      """
  64      trailer = leaf.parent
  65      if trailer.type == 'fstring':
  66          from jedi.inference import compiled
  67          return compiled.get_string_value_set(context.inference_state)
  68  
  69      # The leaf may not be the last or first child, because there exist three
  70      # different trailers: `( x )`, `[ x ]` and `.x`. In the first two examples
  71      # we should not match anything more than x.
  72      if trailer.type != 'trailer' or leaf not in (trailer.children[0], trailer.children[-1]):
  73          if leaf == ':':
  74              # Basically happens with foo[:] when the cursor is on the colon
  75              from jedi.inference.base_value import NO_VALUES
  76              return NO_VALUES
  77          if trailer.type == 'atom':
  78              return context.infer_node(trailer)
  79          return context.infer_node(leaf)
  80  
  81      power = trailer.parent
  82      index = power.children.index(trailer)
  83      if cut_own_trailer:
  84          cut = index
  85      else:
  86          cut = index + 1
  87  
  88      if power.type == 'error_node':
  89          start = index
  90          while True:
  91              start -= 1
  92              base = power.children[start]
  93              if base.type != 'trailer':
  94                  break
  95          trailers = power.children[start + 1:cut]
  96      else:
  97          base = power.children[0]
  98          trailers = power.children[1:cut]
  99  
 100      if base == 'await':
 101          base = trailers[0]
 102          trailers = trailers[1:]
 103  
 104      values = context.infer_node(base)
 105      from jedi.inference.syntax_tree import infer_trailer
 106      for trailer in trailers:
 107          values = infer_trailer(context, values, trailer)
 108      return values
 109  
 110  
 111  def get_names_of_node(node):
 112      try:
 113          children = node.children
 114      except AttributeError:
 115          if node.type == 'name':
 116              return [node]
 117          else:
 118              return []
 119      else:
 120          return list(chain.from_iterable(get_names_of_node(c) for c in children))
 121  
 122  
 123  def is_string(value):
 124      return value.is_compiled() and isinstance(value.get_safe_value(default=None), str)
 125  
 126  
 127  def is_literal(value):
 128      return is_number(value) or is_string(value)
 129  
 130  
 131  def _get_safe_value_or_none(value, accept):
 132      value = value.get_safe_value(default=None)
 133      if isinstance(value, accept):
 134          return value
 135  
 136  
 137  def get_int_or_none(value):
 138      return _get_safe_value_or_none(value, int)
 139  
 140  
 141  def get_str_or_none(value):
 142      return _get_safe_value_or_none(value, str)
 143  
 144  
 145  def is_number(value):
 146      return _get_safe_value_or_none(value, (int, float)) is not None
 147  
 148  
 149  class SimpleGetItemNotFound(Exception):
 150      pass
 151  
 152  
 153  @contextmanager
 154  def reraise_getitem_errors(*exception_classes):
 155      try:
 156          yield
 157      except exception_classes as e:
 158          raise SimpleGetItemNotFound(e)
 159  
 160  
 161  def parse_dotted_names(nodes, is_import_from, until_node=None):
 162      level = 0
 163      names = []
 164      for node in nodes[1:]:
 165          if node in ('.', '...'):
 166              if not names:
 167                  level += len(node.value)
 168          elif node.type == 'dotted_name':
 169              for n in node.children[::2]:
 170                  names.append(n)
 171                  if n is until_node:
 172                      break
 173              else:
 174                  continue
 175              break
 176          elif node.type == 'name':
 177              names.append(node)
 178              if node is until_node:
 179                  break
 180          elif node == ',':
 181              if not is_import_from:
 182                  names = []
 183          else:
 184              # Here if the keyword `import` comes along it stops checking
 185              # for names.
 186              break
 187      return level, names
 188  
 189  
 190  def values_from_qualified_names(inference_state, *names):
 191      return inference_state.import_module(names[:-1]).py__getattribute__(names[-1])
 192  
 193  
 194  def is_big_annoying_library(context):
 195      string_names = context.get_root_context().string_names
 196      if string_names is None:
 197          return False
 198  
 199      # Especially pandas and tensorflow are huge complicated Python libraries
 200      # that get even slower than they already are when Jedi tries to undrstand
 201      # dynamic features like decorators, ifs and other stuff.
 202      return string_names[0] in ('pandas', 'numpy', 'tensorflow', 'matplotlib')
 203