| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434 |
- from jedi.inference.cache import inference_state_method_cache
- from jedi.inference.base_value import ValueSet, NO_VALUES, Value, \
- iterator_to_value_set, LazyValueWrapper, ValueWrapper
- from jedi.inference.compiled import builtin_from_name
- from jedi.inference.value.klass import ClassFilter
- from jedi.inference.value.klass import ClassMixin
- from jedi.inference.utils import to_list
- from jedi.inference.names import AbstractNameDefinition, ValueName
- from jedi.inference.context import ClassContext
- from jedi.inference.gradual.generics import TupleGenericManager
- class _BoundTypeVarName(AbstractNameDefinition):
- """
- This type var was bound to a certain type, e.g. int.
- """
- def __init__(self, type_var, value_set):
- self._type_var = type_var
- self.parent_context = type_var.parent_context
- self._value_set = value_set
- def infer(self):
- def iter_():
- for value in self._value_set:
- # Replace any with the constraints if they are there.
- from jedi.inference.gradual.typing import AnyClass
- if isinstance(value, AnyClass):
- yield from self._type_var.constraints
- else:
- yield value
- return ValueSet(iter_())
- def py__name__(self):
- return self._type_var.py__name__()
- def __repr__(self):
- return '<%s %s -> %s>' % (self.__class__.__name__, self.py__name__(), self._value_set)
- class _TypeVarFilter:
- """
- A filter for all given variables in a class.
- A = TypeVar('A')
- B = TypeVar('B')
- class Foo(Mapping[A, B]):
- ...
- In this example we would have two type vars given: A and B
- """
- def __init__(self, generics, type_vars):
- self._generics = generics
- self._type_vars = type_vars
- def get(self, name):
- for i, type_var in enumerate(self._type_vars):
- if type_var.py__name__() == name:
- try:
- return [_BoundTypeVarName(type_var, self._generics[i])]
- except IndexError:
- return [type_var.name]
- return []
- def values(self):
- # The values are not relevant. If it's not searched exactly, the type
- # vars are just global and should be looked up as that.
- return []
- class _AnnotatedClassContext(ClassContext):
- def get_filters(self, *args, **kwargs):
- filters = super().get_filters(
- *args, **kwargs
- )
- yield from filters
- # The type vars can only be looked up if it's a global search and
- # not a direct lookup on the class.
- yield self._value.get_type_var_filter()
- class DefineGenericBaseClass(LazyValueWrapper):
- def __init__(self, generics_manager):
- self._generics_manager = generics_manager
- def _create_instance_with_generics(self, generics_manager):
- raise NotImplementedError
- @inference_state_method_cache()
- def get_generics(self):
- return self._generics_manager.to_tuple()
- def define_generics(self, type_var_dict):
- from jedi.inference.gradual.type_var import TypeVar
- changed = False
- new_generics = []
- for generic_set in self.get_generics():
- values = NO_VALUES
- for generic in generic_set:
- if isinstance(generic, (DefineGenericBaseClass, TypeVar)):
- result = generic.define_generics(type_var_dict)
- values |= result
- if result != ValueSet({generic}):
- changed = True
- else:
- values |= ValueSet([generic])
- new_generics.append(values)
- if not changed:
- # There might not be any type vars that change. In that case just
- # return itself, because it does not make sense to potentially lose
- # cached results.
- return ValueSet([self])
- return ValueSet([self._create_instance_with_generics(
- TupleGenericManager(tuple(new_generics))
- )])
- def is_same_class(self, other):
- if not isinstance(other, DefineGenericBaseClass):
- return False
- if self.tree_node != other.tree_node:
- # TODO not sure if this is nice.
- return False
- given_params1 = self.get_generics()
- given_params2 = other.get_generics()
- if len(given_params1) != len(given_params2):
- # If the amount of type vars doesn't match, the class doesn't
- # match.
- return False
- # Now compare generics
- return all(
- any(
- # TODO why is this ordering the correct one?
- cls2.is_same_class(cls1)
- # TODO I'm still not sure gather_annotation_classes is a good
- # idea. They are essentially here to avoid comparing Tuple <=>
- # tuple and instead compare tuple <=> tuple, but at the moment
- # the whole `is_same_class` and `is_sub_class` matching is just
- # not in the best shape.
- for cls1 in class_set1.gather_annotation_classes()
- for cls2 in class_set2.gather_annotation_classes()
- ) for class_set1, class_set2 in zip(given_params1, given_params2)
- )
- def get_signatures(self):
- return []
- def __repr__(self):
- return '<%s: %s%s>' % (
- self.__class__.__name__,
- self._wrapped_value,
- list(self.get_generics()),
- )
- class GenericClass(DefineGenericBaseClass, ClassMixin):
- """
- A class that is defined with generics, might be something simple like:
- class Foo(Generic[T]): ...
- my_foo_int_cls = Foo[int]
- """
- def __init__(self, class_value, generics_manager):
- super().__init__(generics_manager)
- self._class_value = class_value
- def _get_wrapped_value(self):
- return self._class_value
- def get_type_hint(self, add_class_info=True):
- n = self.py__name__()
- # Not sure if this is the best way to do this, but all of these types
- # are a bit special in that they have type aliases and other ways to
- # become lower case. It's probably better to make them upper case,
- # because that's what you can use in annotations.
- n = dict(list="List", dict="Dict", set="Set", tuple="Tuple").get(n, n)
- s = n + self._generics_manager.get_type_hint()
- if add_class_info:
- return 'Type[%s]' % s
- return s
- def get_type_var_filter(self):
- return _TypeVarFilter(self.get_generics(), self.list_type_vars())
- def py__call__(self, arguments):
- instance, = super().py__call__(arguments)
- return ValueSet([_GenericInstanceWrapper(instance)])
- def _as_context(self):
- return _AnnotatedClassContext(self)
- @to_list
- def py__bases__(self):
- for base in self._wrapped_value.py__bases__():
- yield _LazyGenericBaseClass(self, base, self._generics_manager)
- def _create_instance_with_generics(self, generics_manager):
- return GenericClass(self._class_value, generics_manager)
- def is_sub_class_of(self, class_value):
- if super().is_sub_class_of(class_value):
- return True
- return self._class_value.is_sub_class_of(class_value)
- def with_generics(self, generics_tuple):
- return self._class_value.with_generics(generics_tuple)
- def infer_type_vars(self, value_set):
- # Circular
- from jedi.inference.gradual.annotation import merge_pairwise_generics, merge_type_var_dicts
- annotation_name = self.py__name__()
- type_var_dict = {}
- if annotation_name == 'Iterable':
- annotation_generics = self.get_generics()
- if annotation_generics:
- return annotation_generics[0].infer_type_vars(
- value_set.merge_types_of_iterate(),
- )
- else:
- # Note: we need to handle the MRO _in order_, so we need to extract
- # the elements from the set first, then handle them, even if we put
- # them back in a set afterwards.
- for py_class in value_set:
- if py_class.is_instance() and not py_class.is_compiled():
- py_class = py_class.get_annotated_class_object()
- else:
- continue
- if py_class.api_type != 'class':
- # Functions & modules don't have an MRO and we're not
- # expecting a Callable (those are handled separately within
- # TypingClassValueWithIndex).
- continue
- for parent_class in py_class.py__mro__():
- class_name = parent_class.py__name__()
- if annotation_name == class_name:
- merge_type_var_dicts(
- type_var_dict,
- merge_pairwise_generics(self, parent_class),
- )
- break
- return type_var_dict
- class _LazyGenericBaseClass:
- def __init__(self, class_value, lazy_base_class, generics_manager):
- self._class_value = class_value
- self._lazy_base_class = lazy_base_class
- self._generics_manager = generics_manager
- @iterator_to_value_set
- def infer(self):
- for base in self._lazy_base_class.infer():
- if isinstance(base, GenericClass):
- # Here we have to recalculate the given types.
- yield GenericClass.create_cached(
- base.inference_state,
- base._wrapped_value,
- TupleGenericManager(tuple(self._remap_type_vars(base))),
- )
- else:
- if base.is_class_mixin():
- # This case basically allows classes like `class Foo(List)`
- # to be used like `Foo[int]`. The generics are not
- # necessary and can be used later.
- yield GenericClass.create_cached(
- base.inference_state,
- base,
- self._generics_manager,
- )
- else:
- yield base
- def _remap_type_vars(self, base):
- from jedi.inference.gradual.type_var import TypeVar
- filter = self._class_value.get_type_var_filter()
- for type_var_set in base.get_generics():
- new = NO_VALUES
- for type_var in type_var_set:
- if isinstance(type_var, TypeVar):
- names = filter.get(type_var.py__name__())
- new |= ValueSet.from_sets(
- name.infer() for name in names
- )
- else:
- # Mostly will be type vars, except if in some cases
- # a concrete type will already be there. In that
- # case just add it to the value set.
- new |= ValueSet([type_var])
- yield new
- def __repr__(self):
- return '<%s: %s>' % (self.__class__.__name__, self._lazy_base_class)
- class _GenericInstanceWrapper(ValueWrapper):
- def py__stop_iteration_returns(self):
- for cls in self._wrapped_value.class_value.py__mro__():
- if cls.py__name__() == 'Generator':
- generics = cls.get_generics()
- try:
- return generics[2].execute_annotation()
- except IndexError:
- pass
- elif cls.py__name__() == 'Iterator':
- return ValueSet([builtin_from_name(self.inference_state, 'None')])
- return self._wrapped_value.py__stop_iteration_returns()
- def get_type_hint(self, add_class_info=True):
- return self._wrapped_value.class_value.get_type_hint(add_class_info=False)
- class _PseudoTreeNameClass(Value):
- """
- In typeshed, some classes are defined like this:
- Tuple: _SpecialForm = ...
- Now this is not a real class, therefore we have to do some workarounds like
- this class. Essentially this class makes it possible to goto that `Tuple`
- name, without affecting anything else negatively.
- """
- api_type = 'class'
- def __init__(self, parent_context, tree_name):
- super().__init__(
- parent_context.inference_state,
- parent_context
- )
- self._tree_name = tree_name
- @property
- def tree_node(self):
- return self._tree_name
- def get_filters(self, *args, **kwargs):
- # TODO this is obviously wrong. Is it though?
- class EmptyFilter(ClassFilter):
- def __init__(self):
- pass
- def get(self, name, **kwargs):
- return []
- def values(self, **kwargs):
- return []
- yield EmptyFilter()
- def py__class__(self):
- # This might not be 100% correct, but it is good enough. The details of
- # the typing library are not really an issue for Jedi.
- return builtin_from_name(self.inference_state, 'type')
- @property
- def name(self):
- return ValueName(self, self._tree_name)
- def get_qualified_names(self):
- return (self._tree_name.value,)
- def __repr__(self):
- return '%s(%s)' % (self.__class__.__name__, self._tree_name.value)
- class BaseTypingValue(LazyValueWrapper):
- def __init__(self, parent_context, tree_name):
- self.inference_state = parent_context.inference_state
- self.parent_context = parent_context
- self._tree_name = tree_name
- @property
- def name(self):
- return ValueName(self, self._tree_name)
- def _get_wrapped_value(self):
- return _PseudoTreeNameClass(self.parent_context, self._tree_name)
- def get_signatures(self):
- return self._wrapped_value.get_signatures()
- def __repr__(self):
- return '%s(%s)' % (self.__class__.__name__, self._tree_name.value)
- class BaseTypingClassWithGenerics(DefineGenericBaseClass):
- def __init__(self, parent_context, tree_name, generics_manager):
- super().__init__(generics_manager)
- self.inference_state = parent_context.inference_state
- self.parent_context = parent_context
- self._tree_name = tree_name
- def _get_wrapped_value(self):
- return _PseudoTreeNameClass(self.parent_context, self._tree_name)
- def __repr__(self):
- return '%s(%s%s)' % (self.__class__.__name__, self._tree_name.value,
- self._generics_manager)
- class BaseTypingInstance(LazyValueWrapper):
- def __init__(self, parent_context, class_value, tree_name, generics_manager):
- self.inference_state = class_value.inference_state
- self.parent_context = parent_context
- self._class_value = class_value
- self._tree_name = tree_name
- self._generics_manager = generics_manager
- def py__class__(self):
- return self._class_value
- def get_annotated_class_object(self):
- return self._class_value
- def get_qualified_names(self):
- return (self.py__name__(),)
- @property
- def name(self):
- return ValueName(self, self._tree_name)
- def _get_wrapped_value(self):
- object_, = builtin_from_name(self.inference_state, 'object').execute_annotation()
- return object_
- def __repr__(self):
- return '<%s: %s>' % (self.__class__.__name__, self._generics_manager)
|