forked from VimPlug/jedi
Simplify recursion issues.
This commit is contained in:
@@ -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
@@ -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
@@ -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):
|
||||||
|
|||||||
Reference in New Issue
Block a user