1
0
forked from VimPlug/jedi

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
+1 -1
View File
@@ -108,7 +108,7 @@ class Evaluator(object):
self.BUILTINS = compiled.get_special_object(self, 'BUILTINS') self.BUILTINS = compiled.get_special_object(self, 'BUILTINS')
def reset_recursion_limitations(self): def reset_recursion_limitations(self):
self.recursion_detector = recursion.RecursionDetector(self) self.recursion_detector = recursion.RecursionDetector()
self.execution_recursion_detector = recursion.ExecutionRecursionDetector(self) self.execution_recursion_detector = recursion.ExecutionRecursionDetector(self)
def find_types(self, context, name_or_str, name_context, position=None, def find_types(self, context, name_or_str, name_context, position=None,
+19 -22
View File
@@ -20,21 +20,22 @@ It is important to note that:
1. Array modfications work only in the current module. 1. Array modfications work only in the current module.
2. Jedi only checks Array additions; ``list.pop``, etc are ignored. 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 debug
from jedi import settings 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._compatibility import unicode, zip_longest, is_py3
from jedi.parser import tree from jedi.parser import tree
from jedi.evaluate import compiled from jedi.evaluate import compiled
from jedi.evaluate import helpers from jedi.evaluate import helpers
from jedi.evaluate.cache import memoize_default
from jedi.evaluate import analysis from jedi.evaluate import analysis
from jedi.evaluate import pep0484 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 context
from jedi.evaluate import precedence 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): class AbstractSequence(context.Context):
@@ -774,25 +775,21 @@ def _check_array_additions(context, sequence):
continue continue
random_context = context.create_context(name) random_context = context.create_context(name)
if context.evaluator.recursion_detector.push_stmt(power):
# Check for recursion. Possible by using 'extend' in with recursion.execution_allowed(context.evaluator, power) as allowed:
# combination with function calls. if allowed:
continue found = helpers.evaluate_call_of_leaf(
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(
random_context, random_context,
execution_trailer.children[1], name,
add_name cut_own_trailer=True
) )
finally: if sequence in found:
context.evaluator.recursion_detector.pop_stmt() # The arrays match. Now add the results
added_types |= find_additions(
random_context,
execution_trailer.children[1],
add_name
)
# reset settings # reset settings
settings.dynamic_params_for_other_modules = temp_param_add settings.dynamic_params_for_other_modules = temp_param_add
+17 -59
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 thread-safe. Why? ``execution_recursion_decorator`` uses class variables to
count the function calls. count the function calls.
""" """
from contextlib import contextmanager
from jedi import debug from jedi import debug
from jedi import settings from jedi import settings
class RecursionDetector(object): 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 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. at the same place, in the same module may not be executed two times.
""" """
def __init__(self, evaluator): pushed_nodes = evaluator.recursion_detector.pushed_nodes
self.top = None
self.current = None
self._evaluator = evaluator
def push_stmt(self, stmt): if node in pushed_nodes:
self.current = _RecursionNode(self._evaluator, stmt, self.current) debug.warning('catched stmt recursion: %s against %s @%s', node,
check = self._check_recursion() node.start_pos)
if check: yield False
debug.warning('catched stmt recursion: %s against %s @%s', stmt, else:
check.stmt, stmt.start_pos) pushed_nodes.append(node)
self.pop_stmt() yield True
return True pushed_nodes.pop()
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
def execution_recursion_decorator(func): def execution_recursion_decorator(func):