Format: text/x-python
⬇ Download File (17.0 KB)
   1  from parso.python import tree
   2  
   3  from jedi import debug
   4  from jedi.inference.cache import inference_state_method_cache, CachedMetaClass
   5  from jedi.inference import compiled
   6  from jedi.inference import recursion
   7  from jedi.inference import docstrings
   8  from jedi.inference import flow_analysis
   9  from jedi.inference.signature import TreeSignature
  10  from jedi.inference.filters import ParserTreeFilter, FunctionExecutionFilter, \
  11      AnonymousFunctionExecutionFilter
  12  from jedi.inference.names import ValueName, AbstractNameDefinition, \
  13      AnonymousParamName, ParamName, NameWrapper
  14  from jedi.inference.base_value import ContextualizedNode, NO_VALUES, \
  15      ValueSet, TreeValue, ValueWrapper
  16  from jedi.inference.lazy_value import LazyKnownValues, LazyKnownValue, \
  17      LazyTreeValue
  18  from jedi.inference.context import ValueContext, TreeContextMixin
  19  from jedi.inference.value import iterable
  20  from jedi import parser_utils
  21  from jedi.inference.parser_cache import get_yield_exprs
  22  from jedi.inference.helpers import values_from_qualified_names
  23  from jedi.inference.gradual.generics import TupleGenericManager
  24  
  25  
  26  class LambdaName(AbstractNameDefinition):
  27      string_name = '<lambda>'
  28      api_type = 'function'
  29  
  30      def __init__(self, lambda_value):
  31          self._lambda_value = lambda_value
  32          self.parent_context = lambda_value.parent_context
  33  
  34      @property
  35      def start_pos(self):
  36          return self._lambda_value.tree_node.start_pos
  37  
  38      def infer(self):
  39          return ValueSet([self._lambda_value])
  40  
  41  
  42  class FunctionAndClassBase(TreeValue):
  43      def get_qualified_names(self):
  44          if self.parent_context.is_class():
  45              n = self.parent_context.get_qualified_names()
  46              if n is None:
  47                  # This means that the parent class lives within a function.
  48                  return None
  49              return n + (self.py__name__(),)
  50          elif self.parent_context.is_module():
  51              return (self.py__name__(),)
  52          else:
  53              return None
  54  
  55  
  56  class FunctionMixin:
  57      api_type = 'function'
  58  
  59      def get_filters(self, origin_scope=None):
  60          cls = self.py__class__()
  61          for instance in cls.execute_with_values():
  62              yield from instance.get_filters(origin_scope=origin_scope)
  63  
  64      def py__get__(self, instance, class_value):
  65          from jedi.inference.value.instance import BoundMethod
  66          if instance is None:
  67              # Calling the Foo.bar results in the original bar function.
  68              return ValueSet([self])
  69          return ValueSet([BoundMethod(instance, class_value.as_context(), self)])
  70  
  71      def get_param_names(self):
  72          return [AnonymousParamName(self, param.name)
  73                  for param in self.tree_node.get_params()]
  74  
  75      @property
  76      def name(self):
  77          if self.tree_node.type == 'lambdef':
  78              return LambdaName(self)
  79          return ValueName(self, self.tree_node.name)
  80  
  81      def is_function(self):
  82          return True
  83  
  84      def py__name__(self):
  85          return self.name.string_name
  86  
  87      def get_type_hint(self, add_class_info=True):
  88          return_annotation = self.tree_node.annotation
  89          if return_annotation is None:
  90              def param_name_to_str(n):
  91                  s = n.string_name
  92                  annotation = n.infer().get_type_hint()
  93                  if annotation is not None:
  94                      s += ': ' + annotation
  95                  if n.default_node is not None:
  96                      s += '=' + n.default_node.get_code(include_prefix=False)
  97                  return s
  98  
  99              function_execution = self.as_context()
 100              result = function_execution.infer()
 101              return_hint = result.get_type_hint()
 102              body = self.py__name__() + '(%s)' % ', '.join([
 103                  param_name_to_str(n)
 104                  for n in function_execution.get_param_names()
 105              ])
 106              if return_hint is None:
 107                  return body
 108          else:
 109              return_hint = return_annotation.get_code(include_prefix=False)
 110              body = self.py__name__() + self.tree_node.children[2].get_code(include_prefix=False)
 111  
 112          return body + ' -> ' + return_hint
 113  
 114      def py__call__(self, arguments):
 115          function_execution = self.as_context(arguments)
 116          return function_execution.infer()
 117  
 118      def _as_context(self, arguments=None):
 119          if arguments is None:
 120              return AnonymousFunctionExecution(self)
 121          return FunctionExecutionContext(self, arguments)
 122  
 123      def get_signatures(self):
 124          return [TreeSignature(f) for f in self.get_signature_functions()]
 125  
 126  
 127  class FunctionValue(FunctionMixin, FunctionAndClassBase, metaclass=CachedMetaClass):
 128      @classmethod
 129      def from_context(cls, context, tree_node):
 130          def create(tree_node):
 131              if context.is_class():
 132                  return MethodValue(
 133                      context.inference_state,
 134                      context,
 135                      parent_context=parent_context,
 136                      tree_node=tree_node
 137                  )
 138              else:
 139                  return cls(
 140                      context.inference_state,
 141                      parent_context=parent_context,
 142                      tree_node=tree_node
 143                  )
 144  
 145          overloaded_funcs = list(_find_overload_functions(context, tree_node))
 146  
 147          parent_context = context
 148          while parent_context.is_class() or parent_context.is_instance():
 149              parent_context = parent_context.parent_context
 150  
 151          function = create(tree_node)
 152  
 153          if overloaded_funcs:
 154              return OverloadedFunctionValue(
 155                  function,
 156                  # Get them into the correct order: lower line first.
 157                  list(reversed([create(f) for f in overloaded_funcs]))
 158              )
 159          return function
 160  
 161      def py__class__(self):
 162          c, = values_from_qualified_names(self.inference_state, 'types', 'FunctionType')
 163          return c
 164  
 165      def get_default_param_context(self):
 166          return self.parent_context
 167  
 168      def get_signature_functions(self):
 169          return [self]
 170  
 171  
 172  class FunctionNameInClass(NameWrapper):
 173      def __init__(self, class_context, name):
 174          super().__init__(name)
 175          self._class_context = class_context
 176  
 177      def get_defining_qualified_value(self):
 178          return self._class_context.get_value()  # Might be None.
 179  
 180  
 181  class MethodValue(FunctionValue):
 182      def __init__(self, inference_state, class_context, *args, **kwargs):
 183          super().__init__(inference_state, *args, **kwargs)
 184          self.class_context = class_context
 185  
 186      def get_default_param_context(self):
 187          return self.class_context
 188  
 189      def get_qualified_names(self):
 190          # Need to implement this, because the parent value of a method
 191          # value is not the class value but the module.
 192          names = self.class_context.get_qualified_names()
 193          if names is None:
 194              return None
 195          return names + (self.py__name__(),)
 196  
 197      @property
 198      def name(self):
 199          return FunctionNameInClass(self.class_context, super().name)
 200  
 201  
 202  class BaseFunctionExecutionContext(ValueContext, TreeContextMixin):
 203      def infer_annotations(self):
 204          raise NotImplementedError
 205  
 206      @inference_state_method_cache(default=NO_VALUES)
 207      @recursion.execution_recursion_decorator()
 208      def get_return_values(self, check_yields=False):
 209          funcdef = self.tree_node
 210          if funcdef.type == 'lambdef':
 211              return self.infer_node(funcdef.children[-1])
 212  
 213          if check_yields:
 214              value_set = NO_VALUES
 215              returns = get_yield_exprs(self.inference_state, funcdef)
 216          else:
 217              value_set = self.infer_annotations()
 218              if value_set:
 219                  # If there are annotations, prefer them over anything else.
 220                  # This will make it faster.
 221                  return value_set
 222              value_set |= docstrings.infer_return_types(self._value)
 223              returns = funcdef.iter_return_stmts()
 224  
 225          for r in returns:
 226              if check_yields:
 227                  value_set |= ValueSet.from_sets(
 228                      lazy_value.infer()
 229                      for lazy_value in self._get_yield_lazy_value(r)
 230                  )
 231              else:
 232                  check = flow_analysis.reachability_check(self, funcdef, r)
 233                  if check is flow_analysis.UNREACHABLE:
 234                      debug.dbg('Return unreachable: %s', r)
 235                  else:
 236                      try:
 237                          children = r.children
 238                      except AttributeError:
 239                          ctx = compiled.builtin_from_name(self.inference_state, 'None')
 240                          value_set |= ValueSet([ctx])
 241                      else:
 242                          value_set |= self.infer_node(children[1])
 243                  if check is flow_analysis.REACHABLE:
 244                      debug.dbg('Return reachable: %s', r)
 245                      break
 246          return value_set
 247  
 248      def _get_yield_lazy_value(self, yield_expr):
 249          if yield_expr.type == 'keyword':
 250              # `yield` just yields None.
 251              ctx = compiled.builtin_from_name(self.inference_state, 'None')
 252              yield LazyKnownValue(ctx)
 253              return
 254  
 255          node = yield_expr.children[1]
 256          if node.type == 'yield_arg':  # It must be a yield from.
 257              cn = ContextualizedNode(self, node.children[1])
 258              yield from cn.infer().iterate(cn)
 259          else:
 260              yield LazyTreeValue(self, node)
 261  
 262      @recursion.execution_recursion_decorator(default=iter([]))
 263      def get_yield_lazy_values(self, is_async=False):
 264          # TODO: if is_async, wrap yield statements in Awaitable/async_generator_asend
 265          for_parents = [(y, tree.search_ancestor(y, 'for_stmt', 'funcdef',
 266                                                  'while_stmt', 'if_stmt'))
 267                         for y in get_yield_exprs(self.inference_state, self.tree_node)]
 268  
 269          # Calculate if the yields are placed within the same for loop.
 270          yields_order = []
 271          last_for_stmt = None
 272          for yield_, for_stmt in for_parents:
 273              # For really simple for loops we can predict the order. Otherwise
 274              # we just ignore it.
 275              parent = for_stmt.parent
 276              if parent.type == 'suite':
 277                  parent = parent.parent
 278              if for_stmt.type == 'for_stmt' and parent == self.tree_node \
 279                      and parser_utils.for_stmt_defines_one_name(for_stmt):  # Simplicity for now.
 280                  if for_stmt == last_for_stmt:
 281                      yields_order[-1][1].append(yield_)
 282                  else:
 283                      yields_order.append((for_stmt, [yield_]))
 284              elif for_stmt == self.tree_node:
 285                  yields_order.append((None, [yield_]))
 286              else:
 287                  types = self.get_return_values(check_yields=True)
 288                  if types:
 289                      yield LazyKnownValues(types, min=0, max=float('inf'))
 290                  return
 291              last_for_stmt = for_stmt
 292  
 293          for for_stmt, yields in yields_order:
 294              if for_stmt is None:
 295                  # No for_stmt, just normal yields.
 296                  for yield_ in yields:
 297                      yield from self._get_yield_lazy_value(yield_)
 298              else:
 299                  input_node = for_stmt.get_testlist()
 300                  cn = ContextualizedNode(self, input_node)
 301                  ordered = cn.infer().iterate(cn)
 302                  ordered = list(ordered)
 303                  for lazy_value in ordered:
 304                      dct = {str(for_stmt.children[1].value): lazy_value.infer()}
 305                      with self.predefine_names(for_stmt, dct):
 306                          for yield_in_same_for_stmt in yields:
 307                              yield from self._get_yield_lazy_value(yield_in_same_for_stmt)
 308  
 309      def merge_yield_values(self, is_async=False):
 310          return ValueSet.from_sets(
 311              lazy_value.infer()
 312              for lazy_value in self.get_yield_lazy_values()
 313          )
 314  
 315      def is_generator(self):
 316          return bool(get_yield_exprs(self.inference_state, self.tree_node))
 317  
 318      def infer(self):
 319          """
 320          Created to be used by inheritance.
 321          """
 322          inference_state = self.inference_state
 323          is_coroutine = self.tree_node.parent.type in ('async_stmt', 'async_funcdef')
 324          from jedi.inference.gradual.base import GenericClass
 325  
 326          if is_coroutine:
 327              if self.is_generator():
 328                  async_generator_classes = inference_state.typing_module \
 329                      .py__getattribute__('AsyncGenerator')
 330  
 331                  yield_values = self.merge_yield_values(is_async=True)
 332                  # The contravariant doesn't seem to be defined.
 333                  generics = (yield_values.py__class__(), NO_VALUES)
 334                  return ValueSet(
 335                      GenericClass(c, TupleGenericManager(generics))
 336                      for c in async_generator_classes
 337                  ).execute_annotation()
 338              else:
 339                  async_classes = inference_state.typing_module.py__getattribute__('Coroutine')
 340                  return_values = self.get_return_values()
 341                  # Only the first generic is relevant.
 342                  generics = (return_values.py__class__(), NO_VALUES, NO_VALUES)
 343                  return ValueSet(
 344                      GenericClass(c, TupleGenericManager(generics)) for c in async_classes
 345                  ).execute_annotation()
 346          else:
 347              # If there are annotations, prefer them over anything else.
 348              if self.is_generator() and not self.infer_annotations():
 349                  return ValueSet([iterable.Generator(inference_state, self)])
 350              else:
 351                  return self.get_return_values()
 352  
 353  
 354  class FunctionExecutionContext(BaseFunctionExecutionContext):
 355      def __init__(self, function_value, arguments):
 356          super().__init__(function_value)
 357          self._arguments = arguments
 358  
 359      def get_filters(self, until_position=None, origin_scope=None):
 360          yield FunctionExecutionFilter(
 361              self, self._value,
 362              until_position=until_position,
 363              origin_scope=origin_scope,
 364              arguments=self._arguments
 365          )
 366  
 367      def infer_annotations(self):
 368          from jedi.inference.gradual.annotation import infer_return_types
 369          return infer_return_types(self._value, self._arguments)
 370  
 371      def get_param_names(self):
 372          return [
 373              ParamName(self._value, param.name, self._arguments)
 374              for param in self._value.tree_node.get_params()
 375          ]
 376  
 377  
 378  class AnonymousFunctionExecution(BaseFunctionExecutionContext):
 379      def infer_annotations(self):
 380          # I don't think inferring anonymous executions is a big thing.
 381          # Anonymous contexts are mostly there for the user to work in. ~ dave
 382          return NO_VALUES
 383  
 384      def get_filters(self, until_position=None, origin_scope=None):
 385          yield AnonymousFunctionExecutionFilter(
 386              self, self._value,
 387              until_position=until_position,
 388              origin_scope=origin_scope,
 389          )
 390  
 391      def get_param_names(self):
 392          return self._value.get_param_names()
 393  
 394  
 395  class OverloadedFunctionValue(FunctionMixin, ValueWrapper):
 396      def __init__(self, function, overloaded_functions):
 397          super().__init__(function)
 398          self._overloaded_functions = overloaded_functions
 399  
 400      def py__call__(self, arguments):
 401          debug.dbg("Execute overloaded function %s", self._wrapped_value, color='BLUE')
 402          function_executions = []
 403          for signature in self.get_signatures():
 404              function_execution = signature.value.as_context(arguments)
 405              function_executions.append(function_execution)
 406              if signature.matches_signature(arguments):
 407                  return function_execution.infer()
 408  
 409          if self.inference_state.is_analysis:
 410              # In this case we want precision.
 411              return NO_VALUES
 412          return ValueSet.from_sets(fe.infer() for fe in function_executions)
 413  
 414      def get_signature_functions(self):
 415          return self._overloaded_functions
 416  
 417      def get_type_hint(self, add_class_info=True):
 418          return 'Union[%s]' % ', '.join(f.get_type_hint() for f in self._overloaded_functions)
 419  
 420  
 421  def _find_overload_functions(context, tree_node):
 422      def _is_overload_decorated(funcdef):
 423          if funcdef.parent.type == 'decorated':
 424              decorators = funcdef.parent.children[0]
 425              if decorators.type == 'decorator':
 426                  decorators = [decorators]
 427              else:
 428                  decorators = decorators.children
 429              for decorator in decorators:
 430                  dotted_name = decorator.children[1]
 431                  if dotted_name.type == 'name' and dotted_name.value == 'overload':
 432                      # TODO check with values if it's the right overload
 433                      return True
 434          return False
 435  
 436      if tree_node.type == 'lambdef':
 437          return
 438  
 439      if _is_overload_decorated(tree_node):
 440          yield tree_node
 441  
 442      while True:
 443          filter = ParserTreeFilter(
 444              context,
 445              until_position=tree_node.start_pos
 446          )
 447          names = filter.get(tree_node.name.value)
 448          assert isinstance(names, list)
 449          if not names:
 450              break
 451  
 452          found = False
 453          for name in names:
 454              funcdef = name.tree_name.parent
 455              if funcdef.type == 'funcdef' and _is_overload_decorated(funcdef):
 456                  tree_node = funcdef
 457                  found = True
 458                  yield funcdef
 459  
 460          if not found:
 461              break
 462