diff --git a/jedi/evaluate/__init__.py b/jedi/evaluate/__init__.py index 758751c6..c087790c 100644 --- a/jedi/evaluate/__init__.py +++ b/jedi/evaluate/__init__.py @@ -89,6 +89,7 @@ class Evaluator(object): self.mixed_cache = {} # see `evaluate.compiled.mixed.create()` self.analysis = [] self.predefined_if_name_dict_dict = {} + self.dynamic_params_depth = 0 self.is_analysis = False if sys_path is None: diff --git a/jedi/evaluate/dynamic.py b/jedi/evaluate/dynamic.py index e5edcaae..fcf1e646 100644 --- a/jedi/evaluate/dynamic.py +++ b/jedi/evaluate/dynamic.py @@ -52,17 +52,24 @@ def search_params(evaluator, param): is. """ if not settings.dynamic_params: - return [] + return set() - func = param.get_parent_until(tree.Function) - debug.dbg('Dynamic param search for %s in %s.', param, str(func.name), color='MAGENTA') - # Compare the param names. - names = [n for n in search_function_call(evaluator, func) - if n.value == param.name.value] - # Evaluate the ExecutedParams to types. - result = set(chain.from_iterable(n.parent.eval(evaluator) for n in names)) - debug.dbg('Dynamic param result %s', result, color='MAGENTA') - return result + if evaluator.dynamic_params_depth == settings.max_dynamic_params_depth: + return set() + + evaluator.dynamic_params_depth += 1 + try: + func = param.get_parent_until(tree.Function) + debug.dbg('Dynamic param search for %s in %s.', param, str(func.name), color='MAGENTA') + # Compare the param names. + names = [n for n in search_function_call(evaluator, func) + if n.value == param.name.value] + # Evaluate the ExecutedParams to types. + result = set(chain.from_iterable(n.parent.eval(evaluator) for n in names)) + debug.dbg('Dynamic param result %s', result, color='MAGENTA') + return result + finally: + evaluator.dynamic_params_depth -= 1 @memoize_default([], evaluator_is_first_arg=True) diff --git a/jedi/evaluate/representation.py b/jedi/evaluate/representation.py index cd572ede..700a91cd 100644 --- a/jedi/evaluate/representation.py +++ b/jedi/evaluate/representation.py @@ -696,7 +696,7 @@ class FunctionExecution(Executed): else: yield self._evaluator.eval_element(element) - # TODO add execution_recursion_decorator?! + @recursion.execution_recursion_decorator def get_yield_types(self): yields = self.yields stopAt = tree.ForStmt, tree.WhileStmt, FunctionExecution, tree.IfStmt diff --git a/jedi/settings.py b/jedi/settings.py index fe6e856c..45360ac9 100644 --- a/jedi/settings.py +++ b/jedi/settings.py @@ -63,6 +63,7 @@ definitely worse in some cases. But a completion should also be fast. .. autodata:: max_function_recursion_level .. autodata:: max_executions_without_builtins .. autodata:: max_executions +.. autodata:: max_dynamic_params_depth .. autodata:: scale_call_signatures @@ -207,6 +208,12 @@ max_executions = 250 A maximum amount of time, the completion may use. """ +max_dynamic_params_depth = 3 +""" +A maximum amount of a recursive search function calls, when the type of a +parameter is needed. +""" + scale_call_signatures = 0.1 """ Because call_signatures is normally used on every single key hit, it has diff --git a/test/completion/stdlib.py b/test/completion/stdlib.py index 68f8f10d..88f3cfed 100644 --- a/test/completion/stdlib.py +++ b/test/completion/stdlib.py @@ -212,3 +212,12 @@ class B(object): cls = random.choice([A, B]) #? ['say', 'shout'] cls().s + +# ----------------- +# random +# ----------------- + +import zipfile +z = zipfile.ZipFile("foo") +#? ['upper'] +z.read('name').upper