Simplify recursion issues.

This commit is contained in:
Dave Halter
2016-12-18 17:24:20 +01:00
parent 0daf3e4e9f
commit b7ae8a746c
3 changed files with 37 additions and 82 deletions

View File

@@ -108,7 +108,7 @@ class Evaluator(object):
self.BUILTINS = compiled.get_special_object(self, 'BUILTINS')
def reset_recursion_limitations(self):
self.recursion_detector = recursion.RecursionDetector(self)
self.recursion_detector = recursion.RecursionDetector()
self.execution_recursion_detector = recursion.ExecutionRecursionDetector(self)
def find_types(self, context, name_or_str, name_context, position=None,

View File

@@ -20,21 +20,22 @@ It is important to note that:
1. Array modfications work only in the current module.
2. Jedi only checks Array additions; ``list.pop``, etc are ignored.
"""
from jedi.common import unite, safe_property
from jedi import debug
from jedi import settings
from jedi import common
from jedi.common import unite, safe_property
from jedi._compatibility import unicode, zip_longest, is_py3
from jedi.parser import tree
from jedi.evaluate import compiled
from jedi.evaluate import helpers
from jedi.evaluate.cache import memoize_default
from jedi.evaluate import analysis
from jedi.evaluate import pep0484
from jedi import common
from jedi.evaluate.filters import DictFilter, AbstractNameDefinition, \
ParserTreeFilter
from jedi.evaluate import context
from jedi.evaluate import precedence
from jedi.evaluate import recursion
from jedi.evaluate.cache import memoize_default
from jedi.evaluate.filters import DictFilter, AbstractNameDefinition, \
ParserTreeFilter
class AbstractSequence(context.Context):
@@ -774,25 +775,21 @@ def _check_array_additions(context, sequence):
continue
random_context = context.create_context(name)
if context.evaluator.recursion_detector.push_stmt(power):
# Check for recursion. Possible by using 'extend' in
# combination with function calls.
continue
try:
found = helpers.evaluate_call_of_leaf(
random_context,
name,
cut_own_trailer=True
)
if sequence in found:
# The arrays match. Now add the results
added_types |= find_additions(
with recursion.execution_allowed(context.evaluator, power) as allowed:
if allowed:
found = helpers.evaluate_call_of_leaf(
random_context,
execution_trailer.children[1],
add_name
name,
cut_own_trailer=True
)
finally:
context.evaluator.recursion_detector.pop_stmt()
if sequence in found:
# The arrays match. Now add the results
added_types |= find_additions(
random_context,
execution_trailer.children[1],
add_name
)
# reset settings
settings.dynamic_params_for_other_modules = temp_param_add

View File

@@ -7,75 +7,33 @@ Next to :mod:`jedi.evaluate.cache` this module also makes |jedi| not
thread-safe. Why? ``execution_recursion_decorator`` uses class variables to
count the function calls.
"""
from contextlib import contextmanager
from jedi import debug
from jedi import settings
class RecursionDetector(object):
def __init__(self):
self.pushed_nodes = []
@contextmanager
def execution_allowed(evaluator, node):
"""
A decorator to detect recursions in statements. In a recursion a statement
at the same place, in the same module may not be executed two times.
"""
def __init__(self, evaluator):
self.top = None
self.current = None
self._evaluator = evaluator
pushed_nodes = evaluator.recursion_detector.pushed_nodes
def push_stmt(self, stmt):
self.current = _RecursionNode(self._evaluator, stmt, self.current)
check = self._check_recursion()
if check:
debug.warning('catched stmt recursion: %s against %s @%s', stmt,
check.stmt, stmt.start_pos)
self.pop_stmt()
return True
return False
def pop_stmt(self):
if self.current is not None:
# I don't know how current can be None, but sometimes it happens
# with Python3.
self.current = self.current.parent
def _check_recursion(self):
test = self.current
while True:
test = test.parent
if self.current == test:
return test
if not test:
return False
def node_statements(self):
result = []
n = self.current
while n:
result.insert(0, n.stmt)
n = n.parent
return result
class _RecursionNode(object):
""" A node of the RecursionDecorator. """
def __init__(self, evaluator, stmt, parent):
self._evaluator = evaluator
self.script = stmt.get_parent_until()
self.position = stmt.start_pos
self.parent = parent
self.stmt = stmt
# Don't check param instances, they are not causing recursions
# The same's true for the builtins, because the builtins are really
# simple.
self.is_ignored = self.script == self._evaluator.BUILTINS
def __eq__(self, other):
if not other:
return None
return self.script == other.script \
and self.position == other.position \
and not self.is_ignored and not other.is_ignored
if node in pushed_nodes:
debug.warning('catched stmt recursion: %s against %s @%s', node,
node.start_pos)
yield False
else:
pushed_nodes.append(node)
yield True
pushed_nodes.pop()
def execution_recursion_decorator(func):