diff --git a/jedi/inference/gradual/annotation.py b/jedi/inference/gradual/annotation.py index e0df2a32..4624f560 100644 --- a/jedi/inference/gradual/annotation.py +++ b/jedi/inference/gradual/annotation.py @@ -277,6 +277,39 @@ def infer_type_vars_for_execution(function, arguments, annotation_dict): return annotation_variable_results +def infer_return_for_callable(arguments, param_values, result_values): + result = NO_VALUES + for pv in param_values: + if pv.array_type == 'list': + type_var_dict = infer_type_vars_for_callable(arguments, pv.py__iter__()) + + result |= ValueSet.from_sets( + v.define_generics(type_var_dict) + if isinstance(v, (DefineGenericBase, TypeVar)) else ValueSet({v}) + for v in result_values + ).execute_annotation() + return result + + +def infer_type_vars_for_callable(arguments, lazy_params): + """ + Infers type vars for the Calllable class: + + def x() -> Callable[[Callable[..., _T]], _T]: ... + """ + annotation_variable_results = {} + for (_, lazy_value), lazy_callable_param in zip(arguments.unpack(), lazy_params): + callable_param_values = lazy_callable_param.infer() + # Infer unknown type var + actual_value_set = lazy_value.infer() + for v in callable_param_values: + _merge_type_var_dicts( + annotation_variable_results, + _infer_type_vars(v, actual_value_set), + ) + return annotation_variable_results + + def _merge_type_var_dicts(base_dict, new_dict): for type_var_name, values in new_dict.items(): if values: @@ -419,16 +452,21 @@ def find_unknown_type_vars(context, node): for subscript_node in _unpack_subscriptlist(trailer.children[1]): check_node(subscript_node) else: - type_var_set = context.infer_node(node) - for type_var in type_var_set: - if isinstance(type_var, TypeVar) and type_var not in found: - found.append(type_var) + found[:] = _filter_type_vars(context.infer_node(node), found) found = [] # We're not using a set, because the order matters. check_node(node) return found +def _filter_type_vars(value_set, found=()): + new_found = list(found) + for type_var in value_set: + if isinstance(type_var, TypeVar) and type_var not in found: + new_found.append(type_var) + return new_found + + def _unpack_subscriptlist(subscriptlist): if subscriptlist.type == 'subscriptlist': for subscript in subscriptlist.children[::2]: diff --git a/jedi/inference/gradual/base.py b/jedi/inference/gradual/base.py index 2b4c024e..cad9a740 100644 --- a/jedi/inference/gradual/base.py +++ b/jedi/inference/gradual/base.py @@ -297,6 +297,9 @@ class BaseTypingValue(LazyValueWrapper): def _get_wrapped_value(self): return _PseudoTreeNameClass(self.parent_context, self._tree_name) + def __repr__(self): + return '%s(%s)' % (self.__class__.__name__, self._tree_name.value) + class BaseTypingValueWithGenerics(DefineGenericBase): def __init__(self, parent_context, tree_name, generics_manager): @@ -307,3 +310,7 @@ class BaseTypingValueWithGenerics(DefineGenericBase): 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) diff --git a/jedi/inference/gradual/generics.py b/jedi/inference/gradual/generics.py index 5d1bcd14..f0060e4f 100644 --- a/jedi/inference/gradual/generics.py +++ b/jedi/inference/gradual/generics.py @@ -74,6 +74,9 @@ class LazyGenericManager(_AbstractGenericManager): return True return False + def __repr__(self): + return '[%s]' % (', '.join(repr(x) for x in self.to_tuple())) + class TupleGenericManager(_AbstractGenericManager): def __init__(self, tup): @@ -90,3 +93,6 @@ class TupleGenericManager(_AbstractGenericManager): def is_homogenous_tuple(self): return False + + def __repr__(self): + return '[%s]' % (', '.join(repr(x) for x in self.to_tuple())) diff --git a/jedi/inference/gradual/typing.py b/jedi/inference/gradual/typing.py index d6c26838..08a91c9f 100644 --- a/jedi/inference/gradual/typing.py +++ b/jedi/inference/gradual/typing.py @@ -216,8 +216,19 @@ class TypeAlias(LazyValueWrapper): class Callable(BaseTypingValueWithGenerics): def py__call__(self, arguments): + """ + def x() -> Callable[[Callable[..., _T]], _T]: ... + """ # The 0th index are the arguments. - return self._generics_manager.get_index_and_execute(1) + try: + param_values = self._generics_manager[0] + result_values = self._generics_manager[1] + except IndexError: + debug.warning('Callable[...] defined without two arguments') + return NO_VALUES + else: + from jedi.inference.gradual.annotation import infer_return_for_callable + return infer_return_for_callable(arguments, param_values, result_values) class Tuple(LazyValueWrapper): diff --git a/test/completion/pep0484_typing.py b/test/completion/pep0484_typing.py index ba8299ee..32d00932 100644 --- a/test/completion/pep0484_typing.py +++ b/test/completion/pep0484_typing.py @@ -420,6 +420,19 @@ xxx([0])[1] #? xxx([0])[2] +def call_pls() -> typing.Callable[[TYPE_VARX], TYPE_VARX]: ... +#? int() +call_pls()(1) + +def call2_pls() -> typing.Callable[[str, typing.Callable[[int], TYPE_VARX]], TYPE_VARX]: ... +#? float() +call2_pls('')(1, lambda x: 3.0) + +def call3_pls() -> typing.Callable[[typing.Callable[[int], TYPE_VARX]], typing.List[TYPE_VARX]]: ... +def the_callable() -> float: ... +#? float() +call3_pls()(the_callable)[0] + # ------------------------- # TYPE_CHECKING # ------------------------- diff --git a/test/completion/stdlib.py b/test/completion/stdlib.py index 0d004571..ba617fea 100644 --- a/test/completion/stdlib.py +++ b/test/completion/stdlib.py @@ -329,3 +329,24 @@ X.attr_y.value X().name #? float() X().attr_x.attr_y.value + + +# ----------------- +# functools Python 3.8 +# ----------------- + +# python >= 3.8 + +@functools.lru_cache +def x() -> int: ... +@functools.lru_cache() +def y() -> float: ... +@functools.lru_cache(8) +def z() -> str: ... + +#? int() +x() +#? float() +y() +#? str() +z()