From 8fcb4685390b3b76b168a0633ed59f54e7b29bfd Mon Sep 17 00:00:00 2001 From: Dave Halter Date: Sun, 17 Sep 2017 02:02:52 +0200 Subject: [PATCH] Jedi was able to go crazy and loop endlessly in certain if/self assignment combinations. Here we limit type inferance per tree scope. I'm still not sure this is the way to go, but it looks okay for now and we can still go anther way in the future. Tests are there. Fixes #929. --- jedi/evaluate/__init__.py | 26 ++++++++++++++++++++++++++ test/completion/stdlib.py | 13 +++++++++++++ 2 files changed, 39 insertions(+) diff --git a/jedi/evaluate/__init__.py b/jedi/evaluate/__init__.py index e58825bb..9a073eb1 100644 --- a/jedi/evaluate/__init__.py +++ b/jedi/evaluate/__init__.py @@ -86,6 +86,29 @@ from jedi.evaluate.context import ContextualizedName, ContextualizedNode from jedi import parser_utils +def _limit_context_infers(func): + """ + This is for now the way how we limit type inferance going wild. There are + other ways to ensure recursion limits as well. This is mostly necessary + because of instance (self) access that can be quite tricky to limit. + + I'm still not sure this is the way to go, but it looks okay for now and we + can still go anther way in the future. Tests are there. ~ dave + """ + def wrapper(evaluator, context, *args, **kwargs): + n = context.tree_node + try: + evaluator.inferred_element_counts[n] += 1 + if evaluator.inferred_element_counts[n] > 300: + debug.warning('In context %s there were too many inferences.', n) + return set() + except KeyError: + evaluator.inferred_element_counts[n] = 1 + return func(evaluator, context, *args, **kwargs) + + return wrapper + + class Evaluator(object): def __init__(self, grammar, sys_path=None): self.grammar = grammar @@ -94,6 +117,7 @@ class Evaluator(object): # To memorize modules -> equals `sys.modules`. self.modules = {} # like `sys.modules`. self.compiled_cache = {} # see `evaluate.compiled.create()` + self.inferred_element_counts = {} self.mixed_cache = {} # see `evaluate.compiled.mixed._create()` self.analysis = [] self.dynamic_params_depth = 0 @@ -134,6 +158,7 @@ class Evaluator(object): return f.filter_name(filters) return f.find(filters, attribute_lookup=not search_global) + @_limit_context_infers def eval_statement(self, context, stmt, seek_name=None): with recursion.execution_allowed(self, stmt) as allowed: if allowed or context.get_root_context() == self.BUILTINS: @@ -273,6 +298,7 @@ class Evaluator(object): return self._eval_element_not_cached(context, element) @debug.increase_indent + @_limit_context_infers def _eval_element_not_cached(self, context, element): debug.dbg('eval_element %s@%s', element, element.start_pos) types = set() diff --git a/test/completion/stdlib.py b/test/completion/stdlib.py index 09eeb061..7fdf154f 100644 --- a/test/completion/stdlib.py +++ b/test/completion/stdlib.py @@ -236,3 +236,16 @@ import contextlib with contextlib.closing('asd') as string: #? str() string + +# ----------------- +# shlex +# ----------------- + +# Github issue #929 +import shlex +qsplit = shlex.split("foo, ferwerwerw werw werw e") +for part in qsplit: + #? str() None + part + #? str() None + part