]> crepu.dev Git - config.git/blob - djavu-asus/elpy/rpc-venv/lib/python3.11/site-packages/jedi/inference/recursion.py
Actualizado el Readme
[config.git] / djavu-asus / elpy / rpc-venv / lib / python3.11 / site-packages / jedi / inference / recursion.py
1 """
2 Recursions are the recipe of |jedi| to conquer Python code. However, someone
3 must stop recursions going mad. Some settings are here to make |jedi| stop at
4 the right time. You can read more about them :ref:`here <settings-recursion>`.
5
6 Next to the internal ``jedi.inference.cache`` this module also makes |jedi| not
7 thread-safe, because ``execution_recursion_decorator`` uses class variables to
8 count the function calls.
9
10 .. _settings-recursion:
11
12 Settings
13 ~~~~~~~~~~
14
15 Recursion settings are important if you don't want extremely
16 recursive python code to go absolutely crazy.
17
18 The default values are based on experiments while completing the |jedi| library
19 itself (inception!). But I don't think there's any other Python library that
20 uses recursion in a similarly extreme way. Completion should also be fast and
21 therefore the quality might not always be maximal.
22
23 .. autodata:: recursion_limit
24 .. autodata:: total_function_execution_limit
25 .. autodata:: per_function_execution_limit
26 .. autodata:: per_function_recursion_limit
27 """
28
29 from contextlib import contextmanager
30
31 from jedi import debug
32 from jedi.inference.base_value import NO_VALUES
33
34
35 recursion_limit = 15
36 """
37 Like :func:`sys.getrecursionlimit()`, just for |jedi|.
38 """
39 total_function_execution_limit = 200
40 """
41 This is a hard limit of how many non-builtin functions can be executed.
42 """
43 per_function_execution_limit = 6
44 """
45 The maximal amount of times a specific function may be executed.
46 """
47 per_function_recursion_limit = 2
48 """
49 A function may not be executed more than this number of times recursively.
50 """
51
52
53 class RecursionDetector:
54 def __init__(self):
55 self.pushed_nodes = []
56
57
58 @contextmanager
59 def execution_allowed(inference_state, node):
60 """
61 A decorator to detect recursions in statements. In a recursion a statement
62 at the same place, in the same module may not be executed two times.
63 """
64 pushed_nodes = inference_state.recursion_detector.pushed_nodes
65
66 if node in pushed_nodes:
67 debug.warning('catched stmt recursion: %s @%s', node,
68 getattr(node, 'start_pos', None))
69 yield False
70 else:
71 try:
72 pushed_nodes.append(node)
73 yield True
74 finally:
75 pushed_nodes.pop()
76
77
78 def execution_recursion_decorator(default=NO_VALUES):
79 def decorator(func):
80 def wrapper(self, **kwargs):
81 detector = self.inference_state.execution_recursion_detector
82 limit_reached = detector.push_execution(self)
83 try:
84 if limit_reached:
85 result = default
86 else:
87 result = func(self, **kwargs)
88 finally:
89 detector.pop_execution()
90 return result
91 return wrapper
92 return decorator
93
94
95 class ExecutionRecursionDetector:
96 """
97 Catches recursions of executions.
98 """
99 def __init__(self, inference_state):
100 self._inference_state = inference_state
101
102 self._recursion_level = 0
103 self._parent_execution_funcs = []
104 self._funcdef_execution_counts = {}
105 self._execution_count = 0
106
107 def pop_execution(self):
108 self._parent_execution_funcs.pop()
109 self._recursion_level -= 1
110
111 def push_execution(self, execution):
112 funcdef = execution.tree_node
113
114 # These two will be undone in pop_execution.
115 self._recursion_level += 1
116 self._parent_execution_funcs.append(funcdef)
117
118 module_context = execution.get_root_context()
119
120 if module_context.is_builtins_module():
121 # We have control over builtins so we know they are not recursing
122 # like crazy. Therefore we just let them execute always, because
123 # they usually just help a lot with getting good results.
124 return False
125
126 if self._recursion_level > recursion_limit:
127 debug.warning('Recursion limit (%s) reached', recursion_limit)
128 return True
129
130 if self._execution_count >= total_function_execution_limit:
131 debug.warning('Function execution limit (%s) reached', total_function_execution_limit)
132 return True
133 self._execution_count += 1
134
135 if self._funcdef_execution_counts.setdefault(funcdef, 0) >= per_function_execution_limit:
136 if module_context.py__name__() == 'typing':
137 return False
138 debug.warning(
139 'Per function execution limit (%s) reached: %s',
140 per_function_execution_limit,
141 funcdef
142 )
143 return True
144 self._funcdef_execution_counts[funcdef] += 1
145
146 if self._parent_execution_funcs.count(funcdef) > per_function_recursion_limit:
147 debug.warning(
148 'Per function recursion limit (%s) reached: %s',
149 per_function_recursion_limit,
150 funcdef
151 )
152 return True
153 return False