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.
This commit is contained in:
Dave Halter
2017-09-17 02:02:52 +02:00
parent 9dd2027299
commit 8fcb468539
2 changed files with 39 additions and 0 deletions

View File

@@ -86,6 +86,29 @@ from jedi.evaluate.context import ContextualizedName, ContextualizedNode
from jedi import parser_utils 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): class Evaluator(object):
def __init__(self, grammar, sys_path=None): def __init__(self, grammar, sys_path=None):
self.grammar = grammar self.grammar = grammar
@@ -94,6 +117,7 @@ class Evaluator(object):
# To memorize modules -> equals `sys.modules`. # To memorize modules -> equals `sys.modules`.
self.modules = {} # like `sys.modules`. self.modules = {} # like `sys.modules`.
self.compiled_cache = {} # see `evaluate.compiled.create()` self.compiled_cache = {} # see `evaluate.compiled.create()`
self.inferred_element_counts = {}
self.mixed_cache = {} # see `evaluate.compiled.mixed._create()` self.mixed_cache = {} # see `evaluate.compiled.mixed._create()`
self.analysis = [] self.analysis = []
self.dynamic_params_depth = 0 self.dynamic_params_depth = 0
@@ -134,6 +158,7 @@ class Evaluator(object):
return f.filter_name(filters) return f.filter_name(filters)
return f.find(filters, attribute_lookup=not search_global) return f.find(filters, attribute_lookup=not search_global)
@_limit_context_infers
def eval_statement(self, context, stmt, seek_name=None): def eval_statement(self, context, stmt, seek_name=None):
with recursion.execution_allowed(self, stmt) as allowed: with recursion.execution_allowed(self, stmt) as allowed:
if allowed or context.get_root_context() == self.BUILTINS: if allowed or context.get_root_context() == self.BUILTINS:
@@ -273,6 +298,7 @@ class Evaluator(object):
return self._eval_element_not_cached(context, element) return self._eval_element_not_cached(context, element)
@debug.increase_indent @debug.increase_indent
@_limit_context_infers
def _eval_element_not_cached(self, context, element): def _eval_element_not_cached(self, context, element):
debug.dbg('eval_element %s@%s', element, element.start_pos) debug.dbg('eval_element %s@%s', element, element.start_pos)
types = set() types = set()

View File

@@ -236,3 +236,16 @@ import contextlib
with contextlib.closing('asd') as string: with contextlib.closing('asd') as string:
#? str() #? str()
string 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