Format: text/x-python
⬇ Download File (15.2 KB)
   1  from jedi.inference.cache import inference_state_method_cache
   2  from jedi.inference.base_value import ValueSet, NO_VALUES, Value, \
   3      iterator_to_value_set, LazyValueWrapper, ValueWrapper
   4  from jedi.inference.compiled import builtin_from_name
   5  from jedi.inference.value.klass import ClassFilter
   6  from jedi.inference.value.klass import ClassMixin
   7  from jedi.inference.utils import to_list
   8  from jedi.inference.names import AbstractNameDefinition, ValueName
   9  from jedi.inference.context import ClassContext
  10  from jedi.inference.gradual.generics import TupleGenericManager
  11  
  12  
  13  class _BoundTypeVarName(AbstractNameDefinition):
  14      """
  15      This type var was bound to a certain type, e.g. int.
  16      """
  17      def __init__(self, type_var, value_set):
  18          self._type_var = type_var
  19          self.parent_context = type_var.parent_context
  20          self._value_set = value_set
  21  
  22      def infer(self):
  23          def iter_():
  24              for value in self._value_set:
  25                  # Replace any with the constraints if they are there.
  26                  from jedi.inference.gradual.typing import AnyClass
  27                  if isinstance(value, AnyClass):
  28                      yield from self._type_var.constraints
  29                  else:
  30                      yield value
  31          return ValueSet(iter_())
  32  
  33      def py__name__(self):
  34          return self._type_var.py__name__()
  35  
  36      def __repr__(self):
  37          return '<%s %s -> %s>' % (self.__class__.__name__, self.py__name__(), self._value_set)
  38  
  39  
  40  class _TypeVarFilter:
  41      """
  42      A filter for all given variables in a class.
  43  
  44          A = TypeVar('A')
  45          B = TypeVar('B')
  46          class Foo(Mapping[A, B]):
  47              ...
  48  
  49      In this example we would have two type vars given: A and B
  50      """
  51      def __init__(self, generics, type_vars):
  52          self._generics = generics
  53          self._type_vars = type_vars
  54  
  55      def get(self, name):
  56          for i, type_var in enumerate(self._type_vars):
  57              if type_var.py__name__() == name:
  58                  try:
  59                      return [_BoundTypeVarName(type_var, self._generics[i])]
  60                  except IndexError:
  61                      return [type_var.name]
  62          return []
  63  
  64      def values(self):
  65          # The values are not relevant. If it's not searched exactly, the type
  66          # vars are just global and should be looked up as that.
  67          return []
  68  
  69  
  70  class _AnnotatedClassContext(ClassContext):
  71      def get_filters(self, *args, **kwargs):
  72          filters = super().get_filters(
  73              *args, **kwargs
  74          )
  75          yield from filters
  76  
  77          # The type vars can only be looked up if it's a global search and
  78          # not a direct lookup on the class.
  79          yield self._value.get_type_var_filter()
  80  
  81  
  82  class DefineGenericBaseClass(LazyValueWrapper):
  83      def __init__(self, generics_manager):
  84          self._generics_manager = generics_manager
  85  
  86      def _create_instance_with_generics(self, generics_manager):
  87          raise NotImplementedError
  88  
  89      @inference_state_method_cache()
  90      def get_generics(self):
  91          return self._generics_manager.to_tuple()
  92  
  93      def define_generics(self, type_var_dict):
  94          from jedi.inference.gradual.type_var import TypeVar
  95          changed = False
  96          new_generics = []
  97          for generic_set in self.get_generics():
  98              values = NO_VALUES
  99              for generic in generic_set:
 100                  if isinstance(generic, (DefineGenericBaseClass, TypeVar)):
 101                      result = generic.define_generics(type_var_dict)
 102                      values |= result
 103                      if result != ValueSet({generic}):
 104                          changed = True
 105                  else:
 106                      values |= ValueSet([generic])
 107              new_generics.append(values)
 108  
 109          if not changed:
 110              # There might not be any type vars that change. In that case just
 111              # return itself, because it does not make sense to potentially lose
 112              # cached results.
 113              return ValueSet([self])
 114  
 115          return ValueSet([self._create_instance_with_generics(
 116              TupleGenericManager(tuple(new_generics))
 117          )])
 118  
 119      def is_same_class(self, other):
 120          if not isinstance(other, DefineGenericBaseClass):
 121              return False
 122  
 123          if self.tree_node != other.tree_node:
 124              # TODO not sure if this is nice.
 125              return False
 126          given_params1 = self.get_generics()
 127          given_params2 = other.get_generics()
 128  
 129          if len(given_params1) != len(given_params2):
 130              # If the amount of type vars doesn't match, the class doesn't
 131              # match.
 132              return False
 133  
 134          # Now compare generics
 135          return all(
 136              any(
 137                  # TODO why is this ordering the correct one?
 138                  cls2.is_same_class(cls1)
 139                  # TODO I'm still not sure gather_annotation_classes is a good
 140                  # idea. They are essentially here to avoid comparing Tuple <=>
 141                  # tuple and instead compare tuple <=> tuple, but at the moment
 142                  # the whole `is_same_class` and `is_sub_class` matching is just
 143                  # not in the best shape.
 144                  for cls1 in class_set1.gather_annotation_classes()
 145                  for cls2 in class_set2.gather_annotation_classes()
 146              ) for class_set1, class_set2 in zip(given_params1, given_params2)
 147          )
 148  
 149      def get_signatures(self):
 150          return []
 151  
 152      def __repr__(self):
 153          return '<%s: %s%s>' % (
 154              self.__class__.__name__,
 155              self._wrapped_value,
 156              list(self.get_generics()),
 157          )
 158  
 159  
 160  class GenericClass(DefineGenericBaseClass, ClassMixin):
 161      """
 162      A class that is defined with generics, might be something simple like:
 163  
 164          class Foo(Generic[T]): ...
 165          my_foo_int_cls = Foo[int]
 166      """
 167      def __init__(self, class_value, generics_manager):
 168          super().__init__(generics_manager)
 169          self._class_value = class_value
 170  
 171      def _get_wrapped_value(self):
 172          return self._class_value
 173  
 174      def get_type_hint(self, add_class_info=True):
 175          n = self.py__name__()
 176          # Not sure if this is the best way to do this, but all of these types
 177          # are a bit special in that they have type aliases and other ways to
 178          # become lower case. It's probably better to make them upper case,
 179          # because that's what you can use in annotations.
 180          n = dict(list="List", dict="Dict", set="Set", tuple="Tuple").get(n, n)
 181          s = n + self._generics_manager.get_type_hint()
 182          if add_class_info:
 183              return 'Type[%s]' % s
 184          return s
 185  
 186      def get_type_var_filter(self):
 187          return _TypeVarFilter(self.get_generics(), self.list_type_vars())
 188  
 189      def py__call__(self, arguments):
 190          instance, = super().py__call__(arguments)
 191          return ValueSet([_GenericInstanceWrapper(instance)])
 192  
 193      def _as_context(self):
 194          return _AnnotatedClassContext(self)
 195  
 196      @to_list
 197      def py__bases__(self):
 198          for base in self._wrapped_value.py__bases__():
 199              yield _LazyGenericBaseClass(self, base, self._generics_manager)
 200  
 201      def _create_instance_with_generics(self, generics_manager):
 202          return GenericClass(self._class_value, generics_manager)
 203  
 204      def is_sub_class_of(self, class_value):
 205          if super().is_sub_class_of(class_value):
 206              return True
 207          return self._class_value.is_sub_class_of(class_value)
 208  
 209      def with_generics(self, generics_tuple):
 210          return self._class_value.with_generics(generics_tuple)
 211  
 212      def infer_type_vars(self, value_set):
 213          # Circular
 214          from jedi.inference.gradual.annotation import merge_pairwise_generics, merge_type_var_dicts
 215  
 216          annotation_name = self.py__name__()
 217          type_var_dict = {}
 218          if annotation_name == 'Iterable':
 219              annotation_generics = self.get_generics()
 220              if annotation_generics:
 221                  return annotation_generics[0].infer_type_vars(
 222                      value_set.merge_types_of_iterate(),
 223                  )
 224          else:
 225              # Note: we need to handle the MRO _in order_, so we need to extract
 226              # the elements from the set first, then handle them, even if we put
 227              # them back in a set afterwards.
 228              for py_class in value_set:
 229                  if py_class.is_instance() and not py_class.is_compiled():
 230                      py_class = py_class.get_annotated_class_object()
 231                  else:
 232                      continue
 233  
 234                  if py_class.api_type != 'class':
 235                      # Functions & modules don't have an MRO and we're not
 236                      # expecting a Callable (those are handled separately within
 237                      # TypingClassValueWithIndex).
 238                      continue
 239  
 240                  for parent_class in py_class.py__mro__():
 241                      class_name = parent_class.py__name__()
 242                      if annotation_name == class_name:
 243                          merge_type_var_dicts(
 244                              type_var_dict,
 245                              merge_pairwise_generics(self, parent_class),
 246                          )
 247                          break
 248  
 249          return type_var_dict
 250  
 251  
 252  class _LazyGenericBaseClass:
 253      def __init__(self, class_value, lazy_base_class, generics_manager):
 254          self._class_value = class_value
 255          self._lazy_base_class = lazy_base_class
 256          self._generics_manager = generics_manager
 257  
 258      @iterator_to_value_set
 259      def infer(self):
 260          for base in self._lazy_base_class.infer():
 261              if isinstance(base, GenericClass):
 262                  # Here we have to recalculate the given types.
 263                  yield GenericClass.create_cached(
 264                      base.inference_state,
 265                      base._wrapped_value,
 266                      TupleGenericManager(tuple(self._remap_type_vars(base))),
 267                  )
 268              else:
 269                  if base.is_class_mixin():
 270                      # This case basically allows classes like `class Foo(List)`
 271                      # to be used like `Foo[int]`. The generics are not
 272                      # necessary and can be used later.
 273                      yield GenericClass.create_cached(
 274                          base.inference_state,
 275                          base,
 276                          self._generics_manager,
 277                      )
 278                  else:
 279                      yield base
 280  
 281      def _remap_type_vars(self, base):
 282          from jedi.inference.gradual.type_var import TypeVar
 283          filter = self._class_value.get_type_var_filter()
 284          for type_var_set in base.get_generics():
 285              new = NO_VALUES
 286              for type_var in type_var_set:
 287                  if isinstance(type_var, TypeVar):
 288                      names = filter.get(type_var.py__name__())
 289                      new |= ValueSet.from_sets(
 290                          name.infer() for name in names
 291                      )
 292                  else:
 293                      # Mostly will be type vars, except if in some cases
 294                      # a concrete type will already be there. In that
 295                      # case just add it to the value set.
 296                      new |= ValueSet([type_var])
 297              yield new
 298  
 299      def __repr__(self):
 300          return '<%s: %s>' % (self.__class__.__name__, self._lazy_base_class)
 301  
 302  
 303  class _GenericInstanceWrapper(ValueWrapper):
 304      def py__stop_iteration_returns(self):
 305          for cls in self._wrapped_value.class_value.py__mro__():
 306              if cls.py__name__() == 'Generator':
 307                  generics = cls.get_generics()
 308                  try:
 309                      return generics[2].execute_annotation()
 310                  except IndexError:
 311                      pass
 312              elif cls.py__name__() == 'Iterator':
 313                  return ValueSet([builtin_from_name(self.inference_state, 'None')])
 314          return self._wrapped_value.py__stop_iteration_returns()
 315  
 316      def get_type_hint(self, add_class_info=True):
 317          return self._wrapped_value.class_value.get_type_hint(add_class_info=False)
 318  
 319  
 320  class _PseudoTreeNameClass(Value):
 321      """
 322      In typeshed, some classes are defined like this:
 323  
 324          Tuple: _SpecialForm = ...
 325  
 326      Now this is not a real class, therefore we have to do some workarounds like
 327      this class. Essentially this class makes it possible to goto that `Tuple`
 328      name, without affecting anything else negatively.
 329      """
 330      api_type = 'class'
 331  
 332      def __init__(self, parent_context, tree_name):
 333          super().__init__(
 334              parent_context.inference_state,
 335              parent_context
 336          )
 337          self._tree_name = tree_name
 338  
 339      @property
 340      def tree_node(self):
 341          return self._tree_name
 342  
 343      def get_filters(self, *args, **kwargs):
 344          # TODO this is obviously wrong. Is it though?
 345          class EmptyFilter(ClassFilter):
 346              def __init__(self):
 347                  pass
 348  
 349              def get(self, name, **kwargs):
 350                  return []
 351  
 352              def values(self, **kwargs):
 353                  return []
 354  
 355          yield EmptyFilter()
 356  
 357      def py__class__(self):
 358          # This might not be 100% correct, but it is good enough. The details of
 359          # the typing library are not really an issue for Jedi.
 360          return builtin_from_name(self.inference_state, 'type')
 361  
 362      @property
 363      def name(self):
 364          return ValueName(self, self._tree_name)
 365  
 366      def get_qualified_names(self):
 367          return (self._tree_name.value,)
 368  
 369      def __repr__(self):
 370          return '%s(%s)' % (self.__class__.__name__, self._tree_name.value)
 371  
 372  
 373  class BaseTypingValue(LazyValueWrapper):
 374      def __init__(self, parent_context, tree_name):
 375          self.inference_state = parent_context.inference_state
 376          self.parent_context = parent_context
 377          self._tree_name = tree_name
 378  
 379      @property
 380      def name(self):
 381          return ValueName(self, self._tree_name)
 382  
 383      def _get_wrapped_value(self):
 384          return _PseudoTreeNameClass(self.parent_context, self._tree_name)
 385  
 386      def get_signatures(self):
 387          return self._wrapped_value.get_signatures()
 388  
 389      def __repr__(self):
 390          return '%s(%s)' % (self.__class__.__name__, self._tree_name.value)
 391  
 392  
 393  class BaseTypingClassWithGenerics(DefineGenericBaseClass):
 394      def __init__(self, parent_context, tree_name, generics_manager):
 395          super().__init__(generics_manager)
 396          self.inference_state = parent_context.inference_state
 397          self.parent_context = parent_context
 398          self._tree_name = tree_name
 399  
 400      def _get_wrapped_value(self):
 401          return _PseudoTreeNameClass(self.parent_context, self._tree_name)
 402  
 403      def __repr__(self):
 404          return '%s(%s%s)' % (self.__class__.__name__, self._tree_name.value,
 405                               self._generics_manager)
 406  
 407  
 408  class BaseTypingInstance(LazyValueWrapper):
 409      def __init__(self, parent_context, class_value, tree_name, generics_manager):
 410          self.inference_state = class_value.inference_state
 411          self.parent_context = parent_context
 412          self._class_value = class_value
 413          self._tree_name = tree_name
 414          self._generics_manager = generics_manager
 415  
 416      def py__class__(self):
 417          return self._class_value
 418  
 419      def get_annotated_class_object(self):
 420          return self._class_value
 421  
 422      def get_qualified_names(self):
 423          return (self.py__name__(),)
 424  
 425      @property
 426      def name(self):
 427          return ValueName(self, self._tree_name)
 428  
 429      def _get_wrapped_value(self):
 430          object_, = builtin_from_name(self.inference_state, 'object').execute_annotation()
 431          return object_
 432  
 433      def __repr__(self):
 434          return '<%s: %s>' % (self.__class__.__name__, self._generics_manager)
 435