]> crepu.dev Git - config.git/blob - djavu-asus/elpy/rpc-venv/lib/python3.11/site-packages/jedi/inference/dynamic_params.py
Archivo de configuraciĆ³n de la terminal
[config.git] / djavu-asus / elpy / rpc-venv / lib / python3.11 / site-packages / jedi / inference / dynamic_params.py
1 """
2 One of the really important features of |jedi| is to have an option to
3 understand code like this::
4
5 def foo(bar):
6 bar. # completion here
7 foo(1)
8
9 There's no doubt wheter bar is an ``int`` or not, but if there's also a call
10 like ``foo('str')``, what would happen? Well, we'll just show both. Because
11 that's what a human would expect.
12
13 It works as follows:
14
15 - |Jedi| sees a param
16 - search for function calls named ``foo``
17 - execute these calls and check the input.
18 """
19
20 from jedi import settings
21 from jedi import debug
22 from jedi.parser_utils import get_parent_scope
23 from jedi.inference.cache import inference_state_method_cache
24 from jedi.inference.arguments import TreeArguments
25 from jedi.inference.param import get_executed_param_names
26 from jedi.inference.helpers import is_stdlib_path
27 from jedi.inference.utils import to_list
28 from jedi.inference.value import instance
29 from jedi.inference.base_value import ValueSet, NO_VALUES
30 from jedi.inference.references import get_module_contexts_containing_name
31 from jedi.inference import recursion
32
33
34 MAX_PARAM_SEARCHES = 20
35
36
37 def _avoid_recursions(func):
38 def wrapper(function_value, param_index):
39 inf = function_value.inference_state
40 with recursion.execution_allowed(inf, function_value.tree_node) as allowed:
41 # We need to catch recursions that may occur, because an
42 # anonymous functions can create an anonymous parameter that is
43 # more or less self referencing.
44 if allowed:
45 inf.dynamic_params_depth += 1
46 try:
47 return func(function_value, param_index)
48 finally:
49 inf.dynamic_params_depth -= 1
50 return NO_VALUES
51 return wrapper
52
53
54 @debug.increase_indent
55 @_avoid_recursions
56 def dynamic_param_lookup(function_value, param_index):
57 """
58 A dynamic search for param values. If you try to complete a type:
59
60 >>> def func(foo):
61 ... foo
62 >>> func(1)
63 >>> func("")
64
65 It is not known what the type ``foo`` without analysing the whole code. You
66 have to look for all calls to ``func`` to find out what ``foo`` possibly
67 is.
68 """
69 if not function_value.inference_state.do_dynamic_params_search:
70 return NO_VALUES
71
72 funcdef = function_value.tree_node
73
74 path = function_value.get_root_context().py__file__()
75 if path is not None and is_stdlib_path(path):
76 # We don't want to search for references in the stdlib. Usually people
77 # don't work with it (except if you are a core maintainer, sorry).
78 # This makes everything slower. Just disable it and run the tests,
79 # you will see the slowdown, especially in 3.6.
80 return NO_VALUES
81
82 if funcdef.type == 'lambdef':
83 string_name = _get_lambda_name(funcdef)
84 if string_name is None:
85 return NO_VALUES
86 else:
87 string_name = funcdef.name.value
88 debug.dbg('Dynamic param search in %s.', string_name, color='MAGENTA')
89
90 module_context = function_value.get_root_context()
91 arguments_list = _search_function_arguments(module_context, funcdef, string_name)
92 values = ValueSet.from_sets(
93 get_executed_param_names(
94 function_value, arguments
95 )[param_index].infer()
96 for arguments in arguments_list
97 )
98 debug.dbg('Dynamic param result finished', color='MAGENTA')
99 return values
100
101
102 @inference_state_method_cache(default=None)
103 @to_list
104 def _search_function_arguments(module_context, funcdef, string_name):
105 """
106 Returns a list of param names.
107 """
108 compare_node = funcdef
109 if string_name == '__init__':
110 cls = get_parent_scope(funcdef)
111 if cls.type == 'classdef':
112 string_name = cls.name.value
113 compare_node = cls
114
115 found_arguments = False
116 i = 0
117 inference_state = module_context.inference_state
118
119 if settings.dynamic_params_for_other_modules:
120 module_contexts = get_module_contexts_containing_name(
121 inference_state, [module_context], string_name,
122 # Limit the amounts of files to be opened massively.
123 limit_reduction=5,
124 )
125 else:
126 module_contexts = [module_context]
127
128 for for_mod_context in module_contexts:
129 for name, trailer in _get_potential_nodes(for_mod_context, string_name):
130 i += 1
131
132 # This is a simple way to stop Jedi's dynamic param recursion
133 # from going wild: The deeper Jedi's in the recursion, the less
134 # code should be inferred.
135 if i * inference_state.dynamic_params_depth > MAX_PARAM_SEARCHES:
136 return
137
138 random_context = for_mod_context.create_context(name)
139 for arguments in _check_name_for_execution(
140 inference_state, random_context, compare_node, name, trailer):
141 found_arguments = True
142 yield arguments
143
144 # If there are results after processing a module, we're probably
145 # good to process. This is a speed optimization.
146 if found_arguments:
147 return
148
149
150 def _get_lambda_name(node):
151 stmt = node.parent
152 if stmt.type == 'expr_stmt':
153 first_operator = next(stmt.yield_operators(), None)
154 if first_operator == '=':
155 first = stmt.children[0]
156 if first.type == 'name':
157 return first.value
158
159 return None
160
161
162 def _get_potential_nodes(module_value, func_string_name):
163 try:
164 names = module_value.tree_node.get_used_names()[func_string_name]
165 except KeyError:
166 return
167
168 for name in names:
169 bracket = name.get_next_leaf()
170 trailer = bracket.parent
171 if trailer.type == 'trailer' and bracket == '(':
172 yield name, trailer
173
174
175 def _check_name_for_execution(inference_state, context, compare_node, name, trailer):
176 from jedi.inference.value.function import BaseFunctionExecutionContext
177
178 def create_args(value):
179 arglist = trailer.children[1]
180 if arglist == ')':
181 arglist = None
182 args = TreeArguments(inference_state, context, arglist, trailer)
183 from jedi.inference.value.instance import InstanceArguments
184 if value.tree_node.type == 'classdef':
185 created_instance = instance.TreeInstance(
186 inference_state,
187 value.parent_context,
188 value,
189 args
190 )
191 return InstanceArguments(created_instance, args)
192 else:
193 if value.is_bound_method():
194 args = InstanceArguments(value.instance, args)
195 return args
196
197 for value in inference_state.infer(context, name):
198 value_node = value.tree_node
199 if compare_node == value_node:
200 yield create_args(value)
201 elif isinstance(value.parent_context, BaseFunctionExecutionContext) \
202 and compare_node.type == 'funcdef':
203 # Here we're trying to find decorators by checking the first
204 # parameter. It's not very generic though. Should find a better
205 # solution that also applies to nested decorators.
206 param_names = value.parent_context.get_param_names()
207 if len(param_names) != 1:
208 continue
209 values = param_names[0].infer()
210 if [v.tree_node for v in values] == [compare_node]:
211 # Found a decorator.
212 module_context = context.get_root_context()
213 execution_context = value.as_context(create_args(value))
214 potential_nodes = _get_potential_nodes(module_context, param_names[0].string_name)
215 for name, trailer in potential_nodes:
216 if value_node.start_pos < name.start_pos < value_node.end_pos:
217 random_context = execution_context.create_context(name)
218 yield from _check_name_for_execution(
219 inference_state,
220 random_context,
221 compare_node,
222 name,
223 trailer
224 )