mirror of
https://github.com/davidhalter/jedi.git
synced 2025-12-08 14:54:47 +08:00
Fix recursion issues.
Completely refactored the recursion checker and changed some settings. Fixes #861.
This commit is contained in:
@@ -13,19 +13,17 @@ Settings
|
|||||||
~~~~~~~~~~
|
~~~~~~~~~~
|
||||||
|
|
||||||
Recursion settings are important if you don't want extremly
|
Recursion settings are important if you don't want extremly
|
||||||
recursive python code to go absolutely crazy. First of there is a
|
recursive python code to go absolutely crazy.
|
||||||
global limit :data:`max_executions`. This limit is important, to set
|
|
||||||
a maximum amount of time, the completion may use.
|
|
||||||
|
|
||||||
The default values are based on experiments while completing the |jedi| library
|
The default values are based on experiments while completing the |jedi| library
|
||||||
itself (inception!). But I don't think there's any other Python library that
|
itself (inception!). But I don't think there's any other Python library that
|
||||||
uses recursion in a similarly extreme way. These settings make the completion
|
uses recursion in a similarly extreme way. Completion should also be fast and
|
||||||
definitely worse in some cases. But a completion should also be fast.
|
therefore the quality might not always be maximal.
|
||||||
|
|
||||||
.. autodata:: max_until_execution_unique
|
.. autodata:: recursion_limit
|
||||||
.. autodata:: max_function_recursion_level
|
.. autodata:: total_function_execution_limit
|
||||||
.. autodata:: max_executions_without_builtins
|
.. autodata:: per_function_execution_limit
|
||||||
.. autodata:: max_executions
|
.. autodata:: per_function_recursion_limit
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from contextlib import contextmanager
|
from contextlib import contextmanager
|
||||||
@@ -33,34 +31,23 @@ from contextlib import contextmanager
|
|||||||
from jedi import debug
|
from jedi import debug
|
||||||
|
|
||||||
|
|
||||||
max_until_execution_unique = 50
|
recursion_limit = 15
|
||||||
"""
|
"""
|
||||||
This limit is probably the most important one, because if this limit is
|
Like ``sys.getrecursionlimit()``, just for |jedi|.
|
||||||
exceeded, functions can only be one time executed. So new functions will be
|
|
||||||
executed, complex recursions with the same functions again and again, are
|
|
||||||
ignored.
|
|
||||||
"""
|
"""
|
||||||
|
total_function_execution_limit = 200
|
||||||
max_function_recursion_level = 5
|
|
||||||
"""
|
"""
|
||||||
`max_function_recursion_level` is more about whether the recursions are
|
This is a hard limit of how many non-builtin functions can be executed.
|
||||||
stopped in deepth or in width. The ratio beetween this and
|
|
||||||
`max_until_execution_unique` is important here. It stops a recursion (after
|
|
||||||
the number of function calls in the recursion), if it was already used
|
|
||||||
earlier.
|
|
||||||
"""
|
"""
|
||||||
|
per_function_execution_limit = 6
|
||||||
max_executions_without_builtins = 200
|
|
||||||
"""
|
"""
|
||||||
.. todo:: Document this.
|
The maximal amount of times a specific function may be executed.
|
||||||
"""
|
"""
|
||||||
|
per_function_recursion_limit = 2
|
||||||
max_executions = 250
|
|
||||||
"""
|
"""
|
||||||
A maximum amount of time, the completion may use.
|
A function may not be executed more than this number of times recursively.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
||||||
class RecursionDetector(object):
|
class RecursionDetector(object):
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.pushed_nodes = []
|
self.pushed_nodes = []
|
||||||
@@ -106,25 +93,23 @@ class ExecutionRecursionDetector(object):
|
|||||||
Catches recursions of executions.
|
Catches recursions of executions.
|
||||||
"""
|
"""
|
||||||
def __init__(self, evaluator):
|
def __init__(self, evaluator):
|
||||||
self.recursion_level = 0
|
|
||||||
self.parent_execution_funcs = []
|
|
||||||
self.execution_funcs = set()
|
|
||||||
self.execution_count = 0
|
|
||||||
self._evaluator = evaluator
|
self._evaluator = evaluator
|
||||||
|
|
||||||
|
self._recursion_level = 0
|
||||||
|
self._parent_execution_funcs = []
|
||||||
|
self._funcdef_execution_counts = {}
|
||||||
|
self._execution_count = 0
|
||||||
|
|
||||||
def pop_execution(self):
|
def pop_execution(self):
|
||||||
self.parent_execution_funcs.pop()
|
self._parent_execution_funcs.pop()
|
||||||
self.recursion_level -= 1
|
self._recursion_level -= 1
|
||||||
|
|
||||||
def push_execution(self, execution):
|
def push_execution(self, execution):
|
||||||
self.recursion_level += 1
|
funcdef = execution.tree_node
|
||||||
self.execution_count += 1
|
|
||||||
self.execution_funcs.add(execution.tree_node)
|
|
||||||
self.parent_execution_funcs.append(execution.tree_node)
|
|
||||||
|
|
||||||
if self.execution_count > max_executions:
|
# These two will be undone in pop_execution.
|
||||||
debug.warning('Too many executions %s' % execution)
|
self._recursion_level += 1
|
||||||
return True
|
self._parent_execution_funcs.append(funcdef)
|
||||||
|
|
||||||
module = execution.get_root_context()
|
module = execution.get_root_context()
|
||||||
if module == self._evaluator.BUILTINS:
|
if module == self._evaluator.BUILTINS:
|
||||||
@@ -133,12 +118,17 @@ class ExecutionRecursionDetector(object):
|
|||||||
# they usually just help a lot with getting good results.
|
# they usually just help a lot with getting good results.
|
||||||
return False
|
return False
|
||||||
|
|
||||||
if execution.tree_node in self.parent_execution_funcs:
|
if self._recursion_level > recursion_limit:
|
||||||
if self.recursion_level > max_function_recursion_level:
|
|
||||||
return True
|
|
||||||
if execution.tree_node in self.execution_funcs and \
|
|
||||||
len(self.execution_funcs) > max_until_execution_unique:
|
|
||||||
return True
|
return True
|
||||||
if self.execution_count > max_executions_without_builtins:
|
|
||||||
|
if self._execution_count >= total_function_execution_limit:
|
||||||
|
return True
|
||||||
|
self._execution_count += 1
|
||||||
|
|
||||||
|
if self._funcdef_execution_counts.setdefault(funcdef, 0) >= per_function_execution_limit:
|
||||||
|
return True
|
||||||
|
self._funcdef_execution_counts[funcdef] += 1
|
||||||
|
|
||||||
|
if self._parent_execution_funcs.count(funcdef) > per_function_recursion_limit:
|
||||||
return True
|
return True
|
||||||
return False
|
return False
|
||||||
|
|||||||
@@ -247,5 +247,3 @@ qsplit = shlex.split("foo, ferwerwerw werw werw e")
|
|||||||
for part in qsplit:
|
for part in qsplit:
|
||||||
#? str() None
|
#? str() None
|
||||||
part
|
part
|
||||||
#? str() None
|
|
||||||
part
|
|
||||||
|
|||||||
Reference in New Issue
Block a user