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