2 One of the really important features of |jedi| is to have an option to
3 understand code like this::
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.
16 - search for function calls named ``foo``
17 - execute these calls and check the input.
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
34 MAX_PARAM_SEARCHES
= 20
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.
45 inf
.dynamic_params_depth
+= 1
47 return func(function_value
, param_index
)
49 inf
.dynamic_params_depth
-= 1
54 @debug.increase_indent
56 def dynamic_param_lookup(function_value
, param_index
):
58 A dynamic search for param values. If you try to complete a type:
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
69 if not function_value
.inference_state
.do_dynamic_params_search
:
72 funcdef
= function_value
.tree_node
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.
82 if funcdef
.type == 'lambdef':
83 string_name
= _get_lambda_name(funcdef
)
84 if string_name
is None:
87 string_name
= funcdef
.name
.value
88 debug
.dbg('Dynamic param search in %s.', string_name
, color
='MAGENTA')
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
98 debug
.dbg('Dynamic param result finished', color
='MAGENTA')
102 @inference_state_method_cache(default
=None)
104 def _search_function_arguments(module_context
, funcdef
, string_name
):
106 Returns a list of param names.
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
115 found_arguments
= False
117 inference_state
= module_context
.inference_state
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.
126 module_contexts
= [module_context
]
128 for for_mod_context
in module_contexts
:
129 for name
, trailer
in _get_potential_nodes(for_mod_context
, string_name
):
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
:
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
144 # If there are results after processing a module, we're probably
145 # good to process. This is a speed optimization.
150 def _get_lambda_name(node
):
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':
162 def _get_potential_nodes(module_value
, func_string_name
):
164 names
= module_value
.tree_node
.get_used_names()[func_string_name
]
169 bracket
= name
.get_next_leaf()
170 trailer
= bracket
.parent
171 if trailer
.type == 'trailer' and bracket
== '(':
175 def _check_name_for_execution(inference_state
, context
, compare_node
, name
, trailer
):
176 from jedi
.inference
.value
.function
import BaseFunctionExecutionContext
178 def create_args(value
):
179 arglist
= trailer
.children
[1]
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(
187 value
.parent_context
,
191 return InstanceArguments(created_instance
, args
)
193 if value
.is_bound_method():
194 args
= InstanceArguments(value
.instance
, args
)
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:
209 values
= param_names
[0].infer()
210 if [v
.tree_node
for v
in values
] == [compare_node
]:
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(