diff --git a/jedi/evaluate/context/function.py b/jedi/evaluate/context/function.py index 822f6651..f0bba090 100644 --- a/jedi/evaluate/context/function.py +++ b/jedi/evaluate/context/function.py @@ -104,12 +104,24 @@ class FunctionContext(use_metaclass(CachedMetaClass, AbstractFunction)): """ @classmethod def from_context(cls, context, tree_node): + def create(tree_node): + return cls(context.evaluator, parent_context=context, tree_node=tree_node) + from jedi.evaluate.context import AbstractInstanceContext + overloaded_funcs = list(_find_overload_functions(context, tree_node)) + while context.is_class() or isinstance(context, AbstractInstanceContext): context = context.parent_context - return cls(context.evaluator, parent_context=context, tree_node=tree_node) + function = create(tree_node) + + if len(overloaded_funcs) > 1: + return OverloadedFunctionContext( + function, + ContextSet.from_iterable(create(f) for f in overloaded_funcs) + ) + return function def get_function_execution(self, arguments=None): if arguments is None: @@ -255,3 +267,76 @@ class FunctionExecutionContext(TreeContext): @evaluator_method_cache() def get_executed_params(self): return self.var_args.get_executed_params(self) + + +class OverloadedFunctionContext(object): + def __init__(self, function, overloaded_functions): + self._function = function + self._overloaded_functions = overloaded_functions + + def py__call__(self, arguments): + return ContextSet.from_sets( + f.py__call__(arguments=arguments) + for f in self._overloaded_functions + if signature_matches(f, arguments) + ) + + def __getattr__(self, name): + return getattr(self._function, name) + + +def signature_matches(function_context, arguments): + unpacked_arguments = arguments.unpack() + for param_node in function_context.tree_node.get_params(): + key, argument = next(unpacked_arguments, (None, None)) + print(param_node) + if argument is None: + # This signature has an parameter more than arguments were given. + return False + if key is not None: + # TODO this is obviously wrong, we cannot just ignore keyword + # arguments, but it's easier for now. + return False + + if param_node.annotation is not None: + annotation_result = function_context.evaluator.eval_node( + function_context.parent_context, + param_node.annotation + ) + print(annotation_result) + + return True + + +def _find_overload_functions(context, tree_node): + def _is_overload_decorated(funcdef): + if funcdef.parent.type == 'decorated': + decorators = funcdef.parent.children[0] + if decorators.type == 'decorator': + decorators = [decorators] + else: + decorators = decorators.children + for decorator in decorators: + dotted_name = decorator.children[1] + if dotted_name.type == 'name' and dotted_name.value == 'overload': + # TODO check with contexts if it's the right overload + return True + return False + + if _is_overload_decorated(tree_node): + yield tree_node + + while True: + filter = ParserTreeFilter( + context.evaluator, + context, + until_position=tree_node.start_pos + ) + names = filter.get(tree_node.name.value) + + for name in names: + funcdef = name.tree_name.parent + if funcdef.type == 'funcdef' and _is_overload_decorated(funcdef): + yield funcdef + + break # By default break diff --git a/jedi/evaluate/context/typing.py b/jedi/evaluate/context/typing.py index 6ae9430b..3cf3635b 100644 --- a/jedi/evaluate/context/typing.py +++ b/jedi/evaluate/context/typing.py @@ -296,6 +296,7 @@ class TypeVar(Context): def execute_annotation(self): if self._bound_lazy_context is not None: return self._bound_lazy_context.infer().execute_annotation() + debug.warning('Tried to infer a TypeVar without a given type') return NO_CONTEXTS def __repr__(self): @@ -305,5 +306,5 @@ class TypeVar(Context): class OverloadFunction(_BaseTypingContext): @repack_with_argument_clinic('func, /') def py__call__(self, func_context_set): - debug.warning('overload used %s', func_context_set) + # Just pass arguments through. return func_context_set diff --git a/jedi/plugins/typeshed.py b/jedi/plugins/typeshed.py index 9185c704..2f17677c 100644 --- a/jedi/plugins/typeshed.py +++ b/jedi/plugins/typeshed.py @@ -9,7 +9,8 @@ from jedi.cache import memoize_method from jedi.evaluate.base_context import ContextSet, iterator_to_context_set from jedi.evaluate.filters import AbstractTreeName, ParserTreeFilter, \ TreeNameDefinition -from jedi.evaluate.context import ModuleContext, FunctionContext, ClassContext +from jedi.evaluate.context import ModuleContext, FunctionContext, \ + ClassContext, BoundMethod from jedi.evaluate.context.typing import TypingModuleFilterWrapper, \ TypingModuleName from jedi.evaluate.compiled import CompiledObject