diff --git a/jedi/evaluate/__init__.py b/jedi/evaluate/__init__.py index 9e75ea8e..160306ec 100644 --- a/jedi/evaluate/__init__.py +++ b/jedi/evaluate/__init__.py @@ -119,6 +119,7 @@ class Evaluator(object): def __init__(self): self.memoize_cache = {} # for memoize decorators self.recursion_detector = recursion.RecursionDetector() + self.execution_recursion_detector = recursion.ExecutionRecursionDetector() def get_names_of_scope(self, scope, position=None, star_search=True, include_builtin=True): diff --git a/jedi/evaluate/recursion.py b/jedi/evaluate/recursion.py index 566fd23c..a0bde9f2 100644 --- a/jedi/evaluate/recursion.py +++ b/jedi/evaluate/recursion.py @@ -96,14 +96,29 @@ class _RecursionNode(object): and not self.is_ignored and not other.is_ignored -class ExecutionRecursionDecorator(object): +def execution_recursion_decorator(func): + def run(execution, evaluate_generator=False): + detector = execution._evaluator.execution_recursion_detector + if detector.push_execution(execution, evaluate_generator): + result = [] + else: + result = func(execution, evaluate_generator) + detector.pop_execution() + return result + + return run + + +class ExecutionRecursionDetector(object): """ Catches recursions of executions. It is designed like a Singelton. Only one instance should exist. """ - def __init__(self, func): - self.func = func - self.reset() + def __init__(self): + self.recursion_level = 0 + self.parent_execution_funcs = [] + self.execution_funcs = set() + self.execution_count = 0 def __call__(self, execution, evaluate_generator=False): debug.dbg('Execution recursions: %s' % execution, self.recursion_level, @@ -112,16 +127,14 @@ class ExecutionRecursionDecorator(object): result = [] else: result = self.func(execution, evaluate_generator) - self.cleanup() + self.pop_execution() return result - @classmethod - def cleanup(cls): + def pop_execution(cls): cls.parent_execution_funcs.pop() cls.recursion_level -= 1 - @classmethod - def check_recursion(cls, execution, evaluate_generator): + def push_execution(cls, execution, evaluate_generator): in_par_execution_funcs = execution.base in cls.parent_execution_funcs in_execution_funcs = execution.base in cls.execution_funcs cls.recursion_level += 1 @@ -147,10 +160,3 @@ class ExecutionRecursionDecorator(object): if cls.execution_count > settings.max_executions_without_builtins: return True return False - - @classmethod - def reset(cls): - cls.recursion_level = 0 - cls.parent_execution_funcs = [] - cls.execution_funcs = set() - cls.execution_count = 0 diff --git a/jedi/evaluate/representation.py b/jedi/evaluate/representation.py index 1ff0f4f9..9015df66 100644 --- a/jedi/evaluate/representation.py +++ b/jedi/evaluate/representation.py @@ -429,7 +429,7 @@ class Execution(Executable): return base @memoize_default(default=()) - @recursion.ExecutionRecursionDecorator + @recursion.execution_recursion_decorator def get_return_types(self, evaluate_generator=False): """ Get the return types of a function. """ base = self._decorated diff --git a/jedi/parser/fast.py b/jedi/parser/fast.py index 45c97cfb..a2a23370 100644 --- a/jedi/parser/fast.py +++ b/jedi/parser/fast.py @@ -226,7 +226,6 @@ class FastParser(use_metaclass(CachedFastParser)): self.user_position = user_position self.reset_caches() - try: self._parse(code) except: @@ -372,8 +371,8 @@ class FastParser(use_metaclass(CachedFastParser)): else: self.current_node = self.current_node.add_node(node) - if self.current_node.parent and (isinstance(p.user_scope, - pr.SubModule) or p.user_scope is None) \ + if self.current_node.parent and (isinstance(p.user_scope, pr.SubModule) + or p.user_scope is None) \ and self.user_position \ and p.start_pos <= self.user_position < p.end_pos: p.user_scope = self.current_node.parent.content_scope @@ -407,9 +406,9 @@ class FastParser(use_metaclass(CachedFastParser)): raise ValueError() except ValueError: p = Parser(parser_code, self.module_path, - self.user_position, offset=(line_offset, 0), - is_fast_parser=True, top_module=self.module, - no_docstr=no_docstr) + self.user_position, offset=(line_offset, 0), + is_fast_parser=True, top_module=self.module, + no_docstr=no_docstr) p.module.parent = self.module else: if nodes[index] != self.current_node: