1
0
forked from VimPlug/jedi

Fix recursion issues about dynamic param lookups and defaults work again

This commit is contained in:
Dave Halter
2019-09-03 23:59:31 +02:00
parent 75262d294f
commit c8564a68df
3 changed files with 62 additions and 79 deletions

View File

@@ -23,40 +23,38 @@ from jedi.parser_utils import get_parent_scope
from jedi.inference.cache import inference_state_method_cache from jedi.inference.cache import inference_state_method_cache
from jedi.inference import imports from jedi.inference import imports
from jedi.inference.arguments import TreeArguments 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.helpers import is_stdlib_path
from jedi.inference.utils import to_list from jedi.inference.utils import to_list
from jedi.inference.value import instance from jedi.inference.value import instance
from jedi.inference.base_value import ValueSet, NO_VALUES from jedi.inference.base_value import ValueSet, NO_VALUES
from jedi.inference import recursion from jedi.inference import recursion
from jedi.inference.names import ParamNameWrapper
MAX_PARAM_SEARCHES = 20 MAX_PARAM_SEARCHES = 20
class DynamicExecutedParamName(ParamNameWrapper): def _avoid_recursions(func):
""" def wrapper(function_value, param_index):
Simulates being a parameter while actually just being multiple params. inf = function_value.inference_state
""" with recursion.execution_allowed(inf, function_value.tree_node) as allowed:
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:
# We need to catch recursions that may occur, because an # We need to catch recursions that may occur, because an
# anonymous functions can create an anonymous parameter that is # anonymous functions can create an anonymous parameter that is
# more or less self referencing. # more or less self referencing.
if allowed: 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 NO_VALUES
return
return wrapper
@debug.increase_indent @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: 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 have to look for all calls to ``func`` to find out what ``foo`` possibly
is. is.
""" """
function_value.inference_state
funcdef = function_value.tree_node funcdef = function_value.tree_node
if not settings.dynamic_params: if not settings.dynamic_params:
return create_default_params(function_value, funcdef) return NO_VALUES
function_value.inference_state.dynamic_params_depth += 1 path = function_value.get_root_context().py__file__()
try: if path is not None and is_stdlib_path(path):
path = function_value.get_root_context().py__file__() # We don't want to search for usages in the stdlib. Usually people
if path is not None and is_stdlib_path(path): # don't work with it (except if you are a core maintainer, sorry).
# We don't want to search for usages in the stdlib. Usually people # This makes everything slower. Just disable it and run the tests,
# don't work with it (except if you are a core maintainer, sorry). # you will see the slowdown, especially in 3.6.
# This makes everything slower. Just disable it and run the tests, return NO_VALUES
# you will see the slowdown, especially in 3.6.
return create_default_params(function_value, funcdef)
if funcdef.type == 'lambdef': if funcdef.type == 'lambdef':
string_name = _get_lambda_name(funcdef) string_name = _get_lambda_name(funcdef)
if string_name is None: if string_name is None:
return create_default_params(function_value, funcdef) return NO_VALUES
else: else:
string_name = funcdef.name.value string_name = funcdef.name.value
debug.dbg('Dynamic param search in %s.', string_name, color='MAGENTA') debug.dbg('Dynamic param search in %s.', string_name, color='MAGENTA')
try: module_context = function_value.get_root_context()
module_context = function_value.get_root_context() arguments_list = _search_function_arguments(
arguments_list = _search_function_arguments( module_context,
module_context, funcdef,
funcdef, string_name=string_name,
string_name=string_name, )
) if arguments_list:
if arguments_list: return ValueSet.from_sets(
zipped_param_names = zip(*list( get_executed_param_names(
get_executed_param_names(function_value, arguments) function_value, arguments
for arguments in arguments_list )[param_index].infer()
)) for arguments in arguments_list
params = [DynamicExecutedParamName(executed_param_names) )
for executed_param_names in zipped_param_names] else:
else: return NO_VALUES
return create_default_params(function_value, funcdef) debug.dbg('Dynamic param result finished', color='MAGENTA')
finally:
debug.dbg('Dynamic param result finished', color='MAGENTA')
return params
finally:
function_value.inference_state.dynamic_params_depth -= 1
@inference_state_method_cache(default=None) @inference_state_method_cache(default=None)

View File

@@ -332,9 +332,23 @@ class SimpleParamName(X):
if values: if values:
return values return values
# TODO private access # TODO private access
from jedi.inference.dynamic_params import search_param_names from jedi.inference.dynamic_params import dynamic_param_lookup
param_names = search_param_names(self.function_value) param = self._get_param_node()
return param_names[self._get_param_node().position_index].infer() 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): class ParamName(X):

View File

@@ -219,24 +219,3 @@ def _error_argument_count(funcdef, actual_count):
before = 'from %s to ' % (len(params) - default_arguments) before = 'from %s to ' % (len(params) - default_arguments)
return ('TypeError: %s() takes %s%s arguments (%s given).' return ('TypeError: %s() takes %s%s arguments (%s given).'
% (funcdef.name, before, len(params), actual_count)) % (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()]