From 8213d183fb04282f51c70113f40a906b5c113a58 Mon Sep 17 00:00:00 2001 From: Dave Halter Date: Sun, 8 Dec 2019 21:56:30 +0100 Subject: [PATCH] Start using generic managers, for #1413 --- jedi/inference/gradual/annotation.py | 14 ++++--- jedi/inference/gradual/base.py | 32 +++++---------- jedi/inference/gradual/generics.py | 61 ++++++++++++++++++---------- jedi/inference/utils.py | 6 +++ jedi/inference/value/function.py | 5 ++- jedi/inference/value/iterable.py | 6 ++- jedi/inference/value/klass.py | 13 +++--- 7 files changed, 81 insertions(+), 56 deletions(-) diff --git a/jedi/inference/gradual/annotation.py b/jedi/inference/gradual/annotation.py index 1e19ff57..e0df2a32 100644 --- a/jedi/inference/gradual/annotation.py +++ b/jedi/inference/gradual/annotation.py @@ -12,8 +12,8 @@ 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.base import LazyGenericClass, \ - DefineGenericBase, GenericClass +from jedi.inference.gradual.base import DefineGenericBase, GenericClass +from jedi.inference.gradual.generics import TupleGenericManager from jedi.inference.gradual.typing import TypingClassValueWithIndex from jedi.inference.gradual.type_var import TypeVar from jedi.inference.helpers import is_string @@ -118,13 +118,17 @@ def infer_param(function_value, param, ignore_stars=False): tuple_ = builtin_from_name(inference_state, 'tuple') return ValueSet([GenericClass( tuple_, - generics=(values,), + TupleGenericManager((values,)), ) for c in values]) elif param.star_count == 2: dct = builtin_from_name(inference_state, 'dict') + generics = ( + ValueSet([builtin_from_name(inference_state, 'str')]), + values + ) return ValueSet([GenericClass( dct, - generics=(ValueSet([builtin_from_name(inference_state, 'str')]), values), + TupleGenericManager(generics), ) for c in values]) pass return values @@ -325,7 +329,7 @@ def _infer_type_vars(annotation_value, value_set, is_class_value=False): value_set.execute_annotation(), ) ) - elif isinstance(annotation_value, LazyGenericClass): + elif isinstance(annotation_value, GenericClass): name = annotation_value.py__name__() if name == 'Iterable': given = annotation_value.get_generics() diff --git a/jedi/inference/gradual/base.py b/jedi/inference/gradual/base.py index eb481643..4b9a3e03 100644 --- a/jedi/inference/gradual/base.py +++ b/jedi/inference/gradual/base.py @@ -7,7 +7,7 @@ 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 iter_over_arguments +from jedi.inference.gradual.generics import TupleGenericManager class BaseTypingValue(Value): @@ -149,7 +149,7 @@ class DefineGenericBase(LazyValueWrapper): return ValueSet([self._get_fixed_generics_cls()( self._wrapped_value, - generics=tuple(new_generics) + TupleGenericManager(tuple(new_generics)) )]) def is_same_class(self, other): @@ -197,7 +197,7 @@ class _AbstractAnnotatedClass(ClassMixin, DefineGenericBase): def py__call__(self, arguments): instance, = super(_AbstractAnnotatedClass, self).py__call__(arguments) - return ValueSet([InstanceWrapper(instance)]) + return ValueSet([_InstanceWrapper(instance)]) def _as_context(self): return AnnotatedClassContext(self) @@ -205,30 +205,20 @@ class _AbstractAnnotatedClass(ClassMixin, DefineGenericBase): @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, context_of_index): - super(LazyGenericClass, self).__init__(class_value) - self._index_value = index_value - self._context_of_index = context_of_index - - @inference_state_method_cache() - def get_generics(self): - return list(iter_over_arguments(self._index_value, self._context_of_index)) + yield _LazyAnnotatedBaseClass(self, base) class GenericClass(_AbstractAnnotatedClass): - def __init__(self, class_value, generics): + def __init__(self, class_value, generics_manager): super(GenericClass, self).__init__(class_value) - self._generics = generics + self._generics_manager = generics_manager + @inference_state_method_cache() def get_generics(self): - return self._generics + return self._generics_manager.to_tuple() -class LazyAnnotatedBaseClass(object): +class _LazyAnnotatedBaseClass(object): def __init__(self, class_value, lazy_base_class): self._class_value = class_value self._lazy_base_class = lazy_base_class @@ -241,7 +231,7 @@ class LazyAnnotatedBaseClass(object): yield GenericClass.create_cached( base.inference_state, base._wrapped_value, - tuple(self._remap_type_vars(base)), + TupleGenericManager(tuple(self._remap_type_vars(base))), ) else: yield base @@ -265,7 +255,7 @@ class LazyAnnotatedBaseClass(object): yield new -class InstanceWrapper(ValueWrapper): +class _InstanceWrapper(ValueWrapper): def py__stop_iteration_returns(self): for cls in self._wrapped_value.class_value.py__mro__(): if cls.py__name__() == 'Generator': diff --git a/jedi/inference/gradual/generics.py b/jedi/inference/gradual/generics.py index 2d71bc86..45485af8 100644 --- a/jedi/inference/gradual/generics.py +++ b/jedi/inference/gradual/generics.py @@ -1,5 +1,10 @@ +""" +This module is about generics, like the `int` in `List[int]`. It's not about +the Generic class. +""" + from jedi.cache import memoize_method -from jedi.inference.utils import to_list +from jedi.inference.utils import to_tuple from jedi.inference.base_value import ValueSet from jedi.inference.value.iterable import SequenceLiteralValue from jedi.inference.helpers import is_string @@ -36,39 +41,51 @@ class LazyGenericManager(object): @memoize_method def __getitem__(self, index): - return self._list()[index]() + return self._tuple()[index]() def __len__(self): - return len(self._list()) + return len(self._tuple()) @memoize_method - @to_list - def _list(self): + @to_tuple + def _tuple(self): + def lambda_scoping_in_for_loop_sucks(lazy_value): + return lambda: ValueSet(_resolve_forward_references( + self._context_of_index, + lazy_value.infer() + )) + if isinstance(self._index_value, SequenceLiteralValue): for lazy_value in self._index_value.py__iter__(contextualized_node=None): - yield lambda: _resolve_forward_references( - self._context_of_index, - lazy_value.infer() - ) + yield lambda_scoping_in_for_loop_sucks(lazy_value) else: - yield lambda: ValueSet([ - _resolve_forward_references(self._context_of_index, self._index_value) - ]) + yield lambda: ValueSet(_resolve_forward_references( + self._context_of_index, + ValueSet([self._index_value]) + )) - def __iter__(self): - return iter(self._iterate()) + @to_tuple + def to_tuple(self): + for callable_ in self._tuple(): + yield callable_() + + #def __iter__(self): + # return iter(self._iterate()) -class ListGenericManager(object): - def __init__(self, lst): - self._lst = lst +class TupleGenericManager(object): + def __init__(self, tup): + self._tuple = tup def __getitem__(self, index): - return self._lst[index] + return self._tuple[index] def __len__(self): - return len(self._lst) + return len(self._tuple) - def __iter__(self): - for value_set in self._lst: - yield lambda: value_set + #def __iter__(self): + # for value_set in self._tuple: + # yield lambda: value_set + + def to_tuple(self): + return self._tuple diff --git a/jedi/inference/utils.py b/jedi/inference/utils.py index 3254d8e1..a1efc047 100644 --- a/jedi/inference/utils.py +++ b/jedi/inference/utils.py @@ -21,6 +21,12 @@ def to_list(func): return wrapper +def to_tuple(func): + def wrapper(*args, **kwargs): + return tuple(func(*args, **kwargs)) + return wrapper + + def unite(iterable): """Turns a two dimensional array into a one dimensional.""" return set(typ for types in iterable for typ in types) diff --git a/jedi/inference/value/function.py b/jedi/inference/value/function.py index d9045c66..90610165 100644 --- a/jedi/inference/value/function.py +++ b/jedi/inference/value/function.py @@ -21,6 +21,7 @@ from jedi.inference.value import iterable from jedi import parser_utils from jedi.inference.parser_cache import get_yield_exprs from jedi.inference.helpers import values_from_qualified_names +from jedi.inference.gradual.generics import TupleGenericManager class LambdaName(AbstractNameDefinition): @@ -297,7 +298,7 @@ class BaseFunctionExecutionContext(ValueContext, TreeContextMixin): generics = (yield_values.py__class__(), NO_VALUES) return ValueSet( # In Python 3.6 AsyncGenerator is still a class. - GenericClass(c, generics) + GenericClass(c, TupleGenericManager(generics)) for c in async_generator_classes ).execute_annotation() else: @@ -308,7 +309,7 @@ class BaseFunctionExecutionContext(ValueContext, TreeContextMixin): # Only the first generic is relevant. generics = (return_values.py__class__(), NO_VALUES, NO_VALUES) return ValueSet( - GenericClass(c, generics) for c in async_classes + GenericClass(c, TupleGenericManager(generics)) for c in async_classes ).execute_annotation() else: if is_generator: diff --git a/jedi/inference/value/iterable.py b/jedi/inference/value/iterable.py index d161aae0..7ced0f45 100644 --- a/jedi/inference/value/iterable.py +++ b/jedi/inference/value/iterable.py @@ -195,8 +195,12 @@ class Sequence(LazyAttributeOverwrite, IterableMixin): def _get_wrapped_value(self): from jedi.inference.gradual.base import GenericClass + from jedi.inference.gradual.generics import TupleGenericManager klass = compiled.builtin_from_name(self.inference_state, self.array_type) - c, = GenericClass(klass, self._get_generics()).execute_annotation() + c, = GenericClass( + klass, + TupleGenericManager(self._get_generics()) + ).execute_annotation() return c def py__bool__(self): diff --git a/jedi/inference/value/klass.py b/jedi/inference/value/klass.py index 3a312261..7ade6340 100644 --- a/jedi/inference/value/klass.py +++ b/jedi/inference/value/klass.py @@ -50,6 +50,7 @@ from jedi.inference.base_value import ValueSet, iterator_to_value_set, \ NO_VALUES from jedi.inference.context import ClassContext from jedi.inference.value.function import FunctionAndClassBase +from jedi.inference.gradual.generics import LazyGenericManager, TupleGenericManager from jedi.plugins import plugin_manager @@ -266,14 +267,16 @@ class ClassValue(use_metaclass(CachedMetaClass, ClassMixin, FunctionAndClassBase )] def py__getitem__(self, index_value_set, contextualized_node): - from jedi.inference.gradual.base import LazyGenericClass + from jedi.inference.gradual.base import GenericClass if not index_value_set: return ValueSet([self]) return ValueSet( - LazyGenericClass( + GenericClass( self, - index_value, - context_of_index=contextualized_node.context, + LazyGenericManager( + context_of_index=contextualized_node.context, + index_value=index_value, + ) ) for index_value in index_value_set ) @@ -297,7 +300,7 @@ class ClassValue(use_metaclass(CachedMetaClass, ClassMixin, FunctionAndClassBase if type_var_dict: return ValueSet([GenericClass( self, - generics=tuple(remap_type_vars()) + TupleGenericManager(tuple(remap_type_vars())) )]) return ValueSet({self})