From c8564a68df24f2a4208c8e2e3c277a6ed0a508f8 Mon Sep 17 00:00:00 2001 From: Dave Halter Date: Tue, 3 Sep 2019 23:59:31 +0200 Subject: [PATCH] Fix recursion issues about dynamic param lookups and defaults work again --- jedi/inference/dynamic_params.py | 100 ++++++++++++++----------------- jedi/inference/names.py | 20 ++++++- jedi/inference/param.py | 21 ------- 3 files changed, 62 insertions(+), 79 deletions(-) diff --git a/jedi/inference/dynamic_params.py b/jedi/inference/dynamic_params.py index 401e655d..060d5545 100644 --- a/jedi/inference/dynamic_params.py +++ b/jedi/inference/dynamic_params.py @@ -23,40 +23,38 @@ from jedi.parser_utils import get_parent_scope from jedi.inference.cache import inference_state_method_cache from jedi.inference import imports from jedi.inference.arguments import TreeArguments -from jedi.inference.param import create_default_params, get_executed_param_names +from jedi.inference.param import get_executed_param_names from jedi.inference.helpers import is_stdlib_path from jedi.inference.utils import to_list from jedi.inference.value import instance from jedi.inference.base_value import ValueSet, NO_VALUES from jedi.inference import recursion -from jedi.inference.names import ParamNameWrapper MAX_PARAM_SEARCHES = 20 -class DynamicExecutedParamName(ParamNameWrapper): - """ - Simulates being a parameter while actually just being multiple params. - """ - - def __init__(self, executed_param_names): - super(DynamicExecutedParamName, self).__init__(executed_param_names[0]) - self._executed_param_names = executed_param_names - - def infer(self): - inf = self.parent_context.inference_state - with recursion.execution_allowed(inf, self.tree_name) as allowed: +def _avoid_recursions(func): + def wrapper(function_value, param_index): + inf = function_value.inference_state + with recursion.execution_allowed(inf, function_value.tree_node) as allowed: # We need to catch recursions that may occur, because an # anonymous functions can create an anonymous parameter that is # more or less self referencing. if allowed: - return ValueSet.from_sets(p.infer() for p in self._executed_param_names) + inf.dynamic_params_depth += 1 + try: + return func(function_value, param_index) + finally: + inf.dynamic_params_depth -= 1 return NO_VALUES + return + return wrapper @debug.increase_indent -def search_param_names(function_value): +@_avoid_recursions +def dynamic_param_lookup(function_value, param_index): """ A dynamic search for param values. If you try to complete a type: @@ -69,51 +67,43 @@ def search_param_names(function_value): have to look for all calls to ``func`` to find out what ``foo`` possibly is. """ - function_value.inference_state funcdef = function_value.tree_node if not settings.dynamic_params: - return create_default_params(function_value, funcdef) + return NO_VALUES - function_value.inference_state.dynamic_params_depth += 1 - try: - path = function_value.get_root_context().py__file__() - if path is not None and is_stdlib_path(path): - # We don't want to search for usages in the stdlib. Usually people - # don't work with it (except if you are a core maintainer, sorry). - # This makes everything slower. Just disable it and run the tests, - # you will see the slowdown, especially in 3.6. - return create_default_params(function_value, funcdef) + path = function_value.get_root_context().py__file__() + if path is not None and is_stdlib_path(path): + # We don't want to search for usages in the stdlib. Usually people + # don't work with it (except if you are a core maintainer, sorry). + # This makes everything slower. Just disable it and run the tests, + # you will see the slowdown, especially in 3.6. + return NO_VALUES - if funcdef.type == 'lambdef': - string_name = _get_lambda_name(funcdef) - if string_name is None: - return create_default_params(function_value, funcdef) - else: - string_name = funcdef.name.value - debug.dbg('Dynamic param search in %s.', string_name, color='MAGENTA') + if funcdef.type == 'lambdef': + string_name = _get_lambda_name(funcdef) + if string_name is None: + return NO_VALUES + else: + string_name = funcdef.name.value + debug.dbg('Dynamic param search in %s.', string_name, color='MAGENTA') - try: - module_context = function_value.get_root_context() - arguments_list = _search_function_arguments( - module_context, - funcdef, - string_name=string_name, - ) - if arguments_list: - zipped_param_names = zip(*list( - get_executed_param_names(function_value, arguments) - for arguments in arguments_list - )) - params = [DynamicExecutedParamName(executed_param_names) - for executed_param_names in zipped_param_names] - else: - return create_default_params(function_value, funcdef) - finally: - debug.dbg('Dynamic param result finished', color='MAGENTA') - return params - finally: - function_value.inference_state.dynamic_params_depth -= 1 + module_context = function_value.get_root_context() + arguments_list = _search_function_arguments( + module_context, + funcdef, + string_name=string_name, + ) + if arguments_list: + return ValueSet.from_sets( + get_executed_param_names( + function_value, arguments + )[param_index].infer() + for arguments in arguments_list + ) + else: + return NO_VALUES + debug.dbg('Dynamic param result finished', color='MAGENTA') @inference_state_method_cache(default=None) diff --git a/jedi/inference/names.py b/jedi/inference/names.py index 6cb912a1..c11d11ab 100644 --- a/jedi/inference/names.py +++ b/jedi/inference/names.py @@ -332,9 +332,23 @@ class SimpleParamName(X): if values: return values # TODO private access - from jedi.inference.dynamic_params import search_param_names - param_names = search_param_names(self.function_value) - return param_names[self._get_param_node().position_index].infer() + from jedi.inference.dynamic_params import dynamic_param_lookup + param = self._get_param_node() + values = dynamic_param_lookup(self.function_value, param.position_index) + if values: + return values + + if param.star_count == 1: + from jedi.inference.value.iterable import FakeTuple + value = FakeTuple(self.function_value.inference_state, []) + elif param.star_count == 2: + from jedi.inference.value.iterable import FakeDict + value = FakeDict(self.function_value.inference_state, {}) + elif param.default is None: + return NO_VALUES + else: + return self.function_value.parent_context.infer_node(param.default) + return ValueSet({value}) class ParamName(X): diff --git a/jedi/inference/param.py b/jedi/inference/param.py index cfee4ac7..fb2bc479 100644 --- a/jedi/inference/param.py +++ b/jedi/inference/param.py @@ -219,24 +219,3 @@ def _error_argument_count(funcdef, actual_count): before = 'from %s to ' % (len(params) - default_arguments) return ('TypeError: %s() takes %s%s arguments (%s given).' % (funcdef.name, before, len(params), actual_count)) - - -def _create_default_param(function_value, arguments, param): - if param.star_count == 1: - result_arg = LazyKnownValue( - iterable.FakeTuple(function_value.inference_state, []) - ) - elif param.star_count == 2: - result_arg = LazyKnownValue( - iterable.FakeDict(function_value.inference_state, {}) - ) - elif param.default is None: - result_arg = LazyUnknownValue() - else: - result_arg = LazyTreeValue(function_value.parent_context, param.default) - return ExecutedParamName(function_value, arguments, param, result_arg) - - -def create_default_params(function_value, funcdef): - return [_create_default_param(function_value, None, p) - for p in funcdef.get_params()]