Format: text/x-python
⬇ Download File (7.4 KB)
   1  from jedi import debug
   2  from jedi.inference.base_value import ValueSet, \
   3      NO_VALUES
   4  from jedi.inference.utils import to_list
   5  from jedi.inference.gradual.stub_value import StubModuleValue
   6  from jedi.inference.gradual.typeshed import try_to_load_stub_cached
   7  from jedi.inference.value.decorator import Decoratee
   8  
   9  
  10  def _stub_to_python_value_set(stub_value, ignore_compiled=False):
  11      stub_module_context = stub_value.get_root_context()
  12      if not stub_module_context.is_stub():
  13          return ValueSet([stub_value])
  14  
  15      decorates = None
  16      if isinstance(stub_value, Decoratee):
  17          decorates = stub_value._original_value
  18  
  19      was_instance = stub_value.is_instance()
  20      if was_instance:
  21          arguments = getattr(stub_value, '_arguments', None)
  22          stub_value = stub_value.py__class__()
  23  
  24      qualified_names = stub_value.get_qualified_names()
  25      if qualified_names is None:
  26          return NO_VALUES
  27  
  28      was_bound_method = stub_value.is_bound_method()
  29      if was_bound_method:
  30          # Infer the object first. We can infer the method later.
  31          method_name = qualified_names[-1]
  32          qualified_names = qualified_names[:-1]
  33          was_instance = True
  34          arguments = None
  35  
  36      values = _infer_from_stub(stub_module_context, qualified_names, ignore_compiled)
  37      if was_instance:
  38          values = ValueSet.from_sets(
  39              c.execute_with_values() if arguments is None else c.execute(arguments)
  40              for c in values
  41              if c.is_class()
  42          )
  43      if was_bound_method:
  44          # Now that the instance has been properly created, we can simply get
  45          # the method.
  46          values = values.py__getattribute__(method_name)
  47      if decorates is not None:
  48          values = ValueSet(Decoratee(v, decorates) for v in values)
  49      return values
  50  
  51  
  52  def _infer_from_stub(stub_module_context, qualified_names, ignore_compiled):
  53      from jedi.inference.compiled.mixed import MixedObject
  54      stub_module = stub_module_context.get_value()
  55      assert isinstance(stub_module, (StubModuleValue, MixedObject)), stub_module_context
  56      non_stubs = stub_module.non_stub_value_set
  57      if ignore_compiled:
  58          non_stubs = non_stubs.filter(lambda c: not c.is_compiled())
  59      for name in qualified_names:
  60          non_stubs = non_stubs.py__getattribute__(name)
  61      return non_stubs
  62  
  63  
  64  @to_list
  65  def _try_stub_to_python_names(names, prefer_stub_to_compiled=False):
  66      for name in names:
  67          module_context = name.get_root_context()
  68          if not module_context.is_stub():
  69              yield name
  70              continue
  71  
  72          if name.api_type == 'module':
  73              values = convert_values(name.infer(), ignore_compiled=prefer_stub_to_compiled)
  74              if values:
  75                  for v in values:
  76                      yield v.name
  77                  continue
  78          else:
  79              v = name.get_defining_qualified_value()
  80              if v is not None:
  81                  converted = _stub_to_python_value_set(v, ignore_compiled=prefer_stub_to_compiled)
  82                  if converted:
  83                      converted_names = converted.goto(name.get_public_name())
  84                      if converted_names:
  85                          for n in converted_names:
  86                              if n.get_root_context().is_stub():
  87                                  # If it's a stub again, it means we're going in
  88                                  # a circle. Probably some imports make it a
  89                                  # stub again.
  90                                  yield name
  91                              else:
  92                                  yield n
  93                          continue
  94          yield name
  95  
  96  
  97  def _load_stub_module(module):
  98      if module.is_stub():
  99          return module
 100      return try_to_load_stub_cached(
 101          module.inference_state,
 102          import_names=module.string_names,
 103          python_value_set=ValueSet([module]),
 104          parent_module_value=None,
 105          sys_path=module.inference_state.get_sys_path(),
 106      )
 107  
 108  
 109  @to_list
 110  def _python_to_stub_names(names, fallback_to_python=False):
 111      for name in names:
 112          module_context = name.get_root_context()
 113          if module_context.is_stub():
 114              yield name
 115              continue
 116  
 117          if name.api_type == 'module':
 118              found_name = False
 119              for n in name.goto():
 120                  if n.api_type == 'module':
 121                      values = convert_values(n.infer(), only_stubs=True)
 122                      for v in values:
 123                          yield v.name
 124                          found_name = True
 125                  else:
 126                      for x in _python_to_stub_names([n], fallback_to_python=fallback_to_python):
 127                          yield x
 128                          found_name = True
 129              if found_name:
 130                  continue
 131          else:
 132              v = name.get_defining_qualified_value()
 133              if v is not None:
 134                  converted = to_stub(v)
 135                  if converted:
 136                      converted_names = converted.goto(name.get_public_name())
 137                      if converted_names:
 138                          yield from converted_names
 139                          continue
 140          if fallback_to_python:
 141              # This is the part where if we haven't found anything, just return
 142              # the stub name.
 143              yield name
 144  
 145  
 146  def convert_names(names, only_stubs=False, prefer_stubs=False, prefer_stub_to_compiled=True):
 147      if only_stubs and prefer_stubs:
 148          raise ValueError("You cannot use both of only_stubs and prefer_stubs.")
 149  
 150      with debug.increase_indent_cm('convert names'):
 151          if only_stubs or prefer_stubs:
 152              return _python_to_stub_names(names, fallback_to_python=prefer_stubs)
 153          else:
 154              return _try_stub_to_python_names(
 155                  names, prefer_stub_to_compiled=prefer_stub_to_compiled)
 156  
 157  
 158  def convert_values(values, only_stubs=False, prefer_stubs=False, ignore_compiled=True):
 159      assert not (only_stubs and prefer_stubs)
 160      with debug.increase_indent_cm('convert values'):
 161          if only_stubs or prefer_stubs:
 162              return ValueSet.from_sets(
 163                  to_stub(value)
 164                  or (ValueSet({value}) if prefer_stubs else NO_VALUES)
 165                  for value in values
 166              )
 167          else:
 168              return ValueSet.from_sets(
 169                  _stub_to_python_value_set(stub_value, ignore_compiled=ignore_compiled)
 170                  or ValueSet({stub_value})
 171                  for stub_value in values
 172              )
 173  
 174  
 175  def to_stub(value):
 176      if value.is_stub():
 177          return ValueSet([value])
 178  
 179      was_instance = value.is_instance()
 180      if was_instance:
 181          value = value.py__class__()
 182  
 183      qualified_names = value.get_qualified_names()
 184      stub_module = _load_stub_module(value.get_root_context().get_value())
 185      if stub_module is None or qualified_names is None:
 186          return NO_VALUES
 187  
 188      was_bound_method = value.is_bound_method()
 189      if was_bound_method:
 190          # Infer the object first. We can infer the method later.
 191          method_name = qualified_names[-1]
 192          qualified_names = qualified_names[:-1]
 193          was_instance = True
 194  
 195      stub_values = ValueSet([stub_module])
 196      for name in qualified_names:
 197          stub_values = stub_values.py__getattribute__(name)
 198  
 199      if was_instance:
 200          stub_values = ValueSet.from_sets(
 201              c.execute_with_values()
 202              for c in stub_values
 203              if c.is_class()
 204          )
 205      if was_bound_method:
 206          # Now that the instance has been properly created, we can simply get
 207          # the method.
 208          stub_values = stub_values.py__getattribute__(method_name)
 209      return stub_values
 210