diff --git a/jedi/inference/gradual/annotation.py b/jedi/inference/gradual/annotation.py index 656f57b9..b58269c9 100644 --- a/jedi/inference/gradual/annotation.py +++ b/jedi/inference/gradual/annotation.py @@ -12,10 +12,10 @@ from parso import ParserSyntaxError, parse from jedi._compatibility import force_unicode, Parameter from jedi.inference.cache import inference_state_method_cache from jedi.inference.base_value import ValueSet, NO_VALUES -from jedi.inference.gradual.typing import TypeVar, LazyGenericClass, \ - AbstractAnnotatedClass -from jedi.inference.gradual.typing import GenericClass, \ - TypingClassValueWithIndex +from jedi.inference.gradual.base import LazyGenericClass, \ + AbstractAnnotatedClass, GenericClass +from jedi.inference.gradual.typing import TypingClassValueWithIndex +from jedi.inference.gradual.type_var import TypeVar from jedi.inference.helpers import is_string from jedi.inference.compiled import builtin_from_name from jedi.inference.param import get_executed_param_names diff --git a/jedi/inference/gradual/base.py b/jedi/inference/gradual/base.py index 78c21476..88363b6d 100644 --- a/jedi/inference/gradual/base.py +++ b/jedi/inference/gradual/base.py @@ -1,7 +1,37 @@ -from jedi.inference.base_value import Value -from jedi.inference.value.klass import ClassFilter -from jedi.inference.names import ValueName +from jedi.inference.cache import inference_state_method_cache +from jedi.inference.base_value import ValueSet, NO_VALUES, Value, \ + iterator_to_value_set, ValueWrapper from jedi.inference.compiled import builtin_from_name +from jedi.inference.value.klass import ClassFilter +from jedi.inference.value.iterable import SequenceLiteralValue +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.helpers import is_string + + +def iter_over_arguments(maybe_tuple_value, defining_context): + def iterate(): + if isinstance(maybe_tuple_value, SequenceLiteralValue): + for lazy_value in maybe_tuple_value.py__iter__(contextualized_node=None): + yield lazy_value.infer() + else: + yield ValueSet([maybe_tuple_value]) + + def resolve_forward_references(value_set): + for value in value_set: + if is_string(value): + from jedi.inference.gradual.annotation import _get_forward_reference_node + node = _get_forward_reference_node(defining_context, value.get_safe_value()) + if node is not None: + for c in defining_context.infer_node(node): + yield c + else: + yield value + + for value_set in iterate(): + yield ValueSet(resolve_forward_references(value_set)) class BaseTypingValue(Value): @@ -39,3 +69,224 @@ class BaseTypingValue(Value): def __repr__(self): return '%s(%s)' % (self.__class__.__name__, self._tree_name.value) + + +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 Any + if isinstance(value, Any): + for constraint in self._type_var.constraints: + yield constraint + 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(object): + """ + 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(AnnotatedClassContext, self).get_filters( + *args, **kwargs + ) + for f in filters: + yield f + + # 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 AbstractAnnotatedClass(ClassMixin, ValueWrapper): + def get_type_var_filter(self): + return TypeVarFilter(self.get_generics(), self.list_type_vars()) + + def is_same_class(self, other): + if not isinstance(other, AbstractAnnotatedClass): + 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) + for cls1 in class_set1 + for cls2 in class_set2 + ) for class_set1, class_set2 in zip(given_params1, given_params2) + ) + + def py__call__(self, arguments): + instance, = super(AbstractAnnotatedClass, self).py__call__(arguments) + return ValueSet([InstanceWrapper(instance)]) + + def get_generics(self): + raise NotImplementedError + + 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, (AbstractAnnotatedClass, 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([GenericClass( + self._wrapped_value, + generics=tuple(new_generics) + )]) + + def _as_context(self): + return AnnotatedClassContext(self) + + def __repr__(self): + return '<%s: %s%s>' % ( + self.__class__.__name__, + self._wrapped_value, + list(self.get_generics()), + ) + + @to_list + def py__bases__(self): + for base in self._wrapped_value.py__bases__(): + yield LazyAnnotatedBaseClass(self, base) + + +class LazyGenericClass(AbstractAnnotatedClass): + def __init__(self, class_value, index_value, value_of_index): + super(LazyGenericClass, self).__init__(class_value) + self._index_value = index_value + self._context_of_index = value_of_index + + @inference_state_method_cache() + def get_generics(self): + return list(iter_over_arguments(self._index_value, self._context_of_index)) + + +class GenericClass(AbstractAnnotatedClass): + def __init__(self, class_value, generics): + super(GenericClass, self).__init__(class_value) + self._generics = generics + + def get_generics(self): + return self._generics + + +class LazyAnnotatedBaseClass(object): + def __init__(self, class_value, lazy_base_class): + self._class_value = class_value + self._lazy_base_class = lazy_base_class + + @iterator_to_value_set + def infer(self): + for base in self._lazy_base_class.infer(): + if isinstance(base, AbstractAnnotatedClass): + # Here we have to recalculate the given types. + yield GenericClass.create_cached( + base.inference_state, + base._wrapped_value, + tuple(self._remap_type_vars(base)), + ) + 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 + + +class InstanceWrapper(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, u'None')]) + return self._wrapped_value.py__stop_iteration_returns() diff --git a/jedi/inference/gradual/typing.py b/jedi/inference/gradual/typing.py index 14660649..d79fe2e8 100644 --- a/jedi/inference/gradual/typing.py +++ b/jedi/inference/gradual/typing.py @@ -9,19 +9,16 @@ from jedi import debug from jedi.inference.cache import inference_state_method_cache from jedi.inference.compiled import builtin_from_name from jedi.inference.base_value import ValueSet, NO_VALUES, Value, \ - iterator_to_value_set, ValueWrapper, LazyValueWrapper + LazyValueWrapper from jedi.inference.lazy_value import LazyKnownValues from jedi.inference.value.iterable import SequenceLiteralValue from jedi.inference.arguments import repack_with_argument_clinic -from jedi.inference.utils import to_list from jedi.inference.filters import FilterWrapper -from jedi.inference.names import NameWrapper, AbstractTreeName, \ - AbstractNameDefinition, ValueName -from jedi.inference.helpers import is_string +from jedi.inference.names import NameWrapper, ValueName from jedi.inference.value.klass import ClassMixin -from jedi.inference.context import ClassContext from jedi.inference.gradual.base import BaseTypingValue -from jedi.inference.gradual.type_var import TypeVarClass, TypeVar +from jedi.inference.gradual.type_var import TypeVarClass +from jedi.inference.gradual.base import iter_over_arguments _PROXY_CLASS_TYPES = 'Tuple Generic Protocol Callable Type'.split() _TYPE_ALIAS_TYPES = { @@ -138,7 +135,7 @@ class TypingValueWithIndex(_WithIndexBase): def gather_annotation_classes(self): return ValueSet.from_sets( - _iter_over_arguments(self._index_value, self._context_of_index) + iter_over_arguments(self._index_value, self._context_of_index) ) @@ -176,36 +173,13 @@ class TypingClassValueWithIndex(_TypingClassMixin, TypingValueWithIndex): @inference_state_method_cache() def get_generics(self): - return list(_iter_over_arguments(self._index_value, self._context_of_index)) + return list(iter_over_arguments(self._index_value, self._context_of_index)) class TypingClassValue(_TypingClassMixin, TypingValue): index_class = TypingClassValueWithIndex -def _iter_over_arguments(maybe_tuple_value, defining_context): - def iterate(): - if isinstance(maybe_tuple_value, SequenceLiteralValue): - for lazy_value in maybe_tuple_value.py__iter__(contextualized_node=None): - yield lazy_value.infer() - else: - yield ValueSet([maybe_tuple_value]) - - def resolve_forward_references(value_set): - for value in value_set: - if is_string(value): - from jedi.inference.gradual.annotation import _get_forward_reference_node - node = _get_forward_reference_node(defining_context, value.get_safe_value()) - if node is not None: - for c in defining_context.infer_node(node): - yield c - else: - yield value - - for value_set in iterate(): - yield ValueSet(resolve_forward_references(value_set)) - - class TypeAlias(LazyValueWrapper): def __init__(self, parent_context, origin_tree_name, actual): self.inference_state = parent_context.inference_state @@ -242,7 +216,7 @@ class TypeAlias(LazyValueWrapper): class _GetItemMixin(object): def _get_getitem_values(self, index): - args = _iter_over_arguments(self._index_value, self._context_of_index) + args = iter_over_arguments(self._index_value, self._context_of_index) for i, values in enumerate(args): if i == index: return values @@ -296,7 +270,7 @@ class Tuple(LazyValueWrapper, _GetItemMixin): return self._get_getitem_values(0).execute_annotation() return ValueSet.from_sets( - _iter_over_arguments(self._index_value, self._context_of_index) + iter_over_arguments(self._index_value, self._context_of_index) ).execute_annotation() def _get_wrapped_value(self): @@ -356,221 +330,3 @@ class CastFunction(BaseTypingValue): @repack_with_argument_clinic('type, object, /') def py__call__(self, type_value_set, object_value_set): return type_value_set.execute_annotation() - - -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. - if isinstance(value, Any): - for constraint in self._type_var.constraints: - yield constraint - 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(object): - """ - 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(AnnotatedClassContext, self).get_filters( - *args, **kwargs - ) - for f in filters: - yield f - - # 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 AbstractAnnotatedClass(ClassMixin, ValueWrapper): - def get_type_var_filter(self): - return TypeVarFilter(self.get_generics(), self.list_type_vars()) - - def is_same_class(self, other): - if not isinstance(other, AbstractAnnotatedClass): - 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) - for cls1 in class_set1 - for cls2 in class_set2 - ) for class_set1, class_set2 in zip(given_params1, given_params2) - ) - - def py__call__(self, arguments): - instance, = super(AbstractAnnotatedClass, self).py__call__(arguments) - return ValueSet([InstanceWrapper(instance)]) - - def get_generics(self): - raise NotImplementedError - - def define_generics(self, type_var_dict): - changed = False - new_generics = [] - for generic_set in self.get_generics(): - values = NO_VALUES - for generic in generic_set: - if isinstance(generic, (AbstractAnnotatedClass, 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([GenericClass( - self._wrapped_value, - generics=tuple(new_generics) - )]) - - def _as_context(self): - return AnnotatedClassContext(self) - - def __repr__(self): - return '<%s: %s%s>' % ( - self.__class__.__name__, - self._wrapped_value, - list(self.get_generics()), - ) - - @to_list - def py__bases__(self): - for base in self._wrapped_value.py__bases__(): - yield LazyAnnotatedBaseClass(self, base) - - -class LazyGenericClass(AbstractAnnotatedClass): - def __init__(self, class_value, index_value, value_of_index): - super(LazyGenericClass, self).__init__(class_value) - self._index_value = index_value - self._context_of_index = value_of_index - - @inference_state_method_cache() - def get_generics(self): - return list(_iter_over_arguments(self._index_value, self._context_of_index)) - - -class GenericClass(AbstractAnnotatedClass): - def __init__(self, class_value, generics): - super(GenericClass, self).__init__(class_value) - self._generics = generics - - def get_generics(self): - return self._generics - - -class LazyAnnotatedBaseClass(object): - def __init__(self, class_value, lazy_base_class): - self._class_value = class_value - self._lazy_base_class = lazy_base_class - - @iterator_to_value_set - def infer(self): - for base in self._lazy_base_class.infer(): - if isinstance(base, AbstractAnnotatedClass): - # Here we have to recalculate the given types. - yield GenericClass.create_cached( - base.inference_state, - base._wrapped_value, - tuple(self._remap_type_vars(base)), - ) - else: - yield base - - def _remap_type_vars(self, base): - 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 - - -class InstanceWrapper(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, u'None')]) - return self._wrapped_value.py__stop_iteration_returns() diff --git a/jedi/inference/value/function.py b/jedi/inference/value/function.py index 10706b5f..d9045c66 100644 --- a/jedi/inference/value/function.py +++ b/jedi/inference/value/function.py @@ -283,7 +283,7 @@ class BaseFunctionExecutionContext(ValueContext, TreeContextMixin): inference_state = self.inference_state is_coroutine = self.tree_node.parent.type in ('async_stmt', 'async_funcdef') is_generator = bool(get_yield_exprs(inference_state, self.tree_node)) - from jedi.inference.gradual.typing import GenericClass + from jedi.inference.gradual.base import GenericClass if is_coroutine: if is_generator: diff --git a/jedi/inference/value/iterable.py b/jedi/inference/value/iterable.py index d5dbfaad..d161aae0 100644 --- a/jedi/inference/value/iterable.py +++ b/jedi/inference/value/iterable.py @@ -194,7 +194,7 @@ class Sequence(LazyAttributeOverwrite, IterableMixin): return (self.merge_types_of_iterate().py__class__(),) def _get_wrapped_value(self): - from jedi.inference.gradual.typing import GenericClass + from jedi.inference.gradual.base import GenericClass klass = compiled.builtin_from_name(self.inference_state, self.array_type) c, = GenericClass(klass, self._get_generics()).execute_annotation() return c diff --git a/jedi/inference/value/klass.py b/jedi/inference/value/klass.py index 7c1c3aab..a6fb7fdd 100644 --- a/jedi/inference/value/klass.py +++ b/jedi/inference/value/klass.py @@ -266,7 +266,7 @@ class ClassValue(use_metaclass(CachedMetaClass, ClassMixin, FunctionAndClassBase )] def py__getitem__(self, index_value_set, contextualized_node): - from jedi.inference.gradual.typing import LazyGenericClass + from jedi.inference.gradual.base import LazyGenericClass if not index_value_set: return ValueSet([self]) return ValueSet( @@ -279,7 +279,7 @@ class ClassValue(use_metaclass(CachedMetaClass, ClassMixin, FunctionAndClassBase ) def define_generics(self, type_var_dict): - from jedi.inference.gradual.typing import GenericClass + from jedi.inference.gradual.base import GenericClass def remap_type_vars(): """