You cannot select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
	
	
		
			435 lines
		
	
	
		
			15 KiB
		
	
	
	
		
			Python
		
	
			
		
		
	
	
			435 lines
		
	
	
		
			15 KiB
		
	
	
	
		
			Python
		
	
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)
 |