2 from itertools
import zip_longest
4 from parso
.python
import tree
7 from jedi
.inference
.utils
import PushBackIterator
8 from jedi
.inference
import analysis
9 from jedi
.inference
.lazy_value
import LazyKnownValue
, LazyKnownValues
, \
10 LazyTreeValue
, get_merged_lazy_value
11 from jedi
.inference
.names
import ParamName
, TreeNameDefinition
, AnonymousParamName
12 from jedi
.inference
.base_value
import NO_VALUES
, ValueSet
, ContextualizedNode
13 from jedi
.inference
.value
import iterable
14 from jedi
.inference
.cache
import inference_state_as_method_param_cache
17 def try_iter_content(types
, depth
=0):
18 """Helper method for static analysis."""
20 # It's possible that a loop has references on itself (especially with
21 # CompiledValue). Therefore don't loop infinitely.
27 except AttributeError:
30 for lazy_value
in f():
31 try_iter_content(lazy_value
.infer(), depth
+ 1)
34 class ParamIssue(Exception):
38 def repack_with_argument_clinic(clinic_string
):
40 Transforms a function or method with arguments to the signature that is
41 given as an argument clinic notation.
43 Argument clinic is part of CPython and used for all the functions that are
44 implemented in C (Python 3.7):
46 str.split.__text_signature__
47 # Results in: '($self, /, sep=None, maxsplit=-1)'
50 def wrapper(value
, arguments
):
52 args
= tuple(iterate_argument_clinic(
53 value
.inference_state
,
60 return func(value
, *args
)
66 def iterate_argument_clinic(inference_state
, arguments
, clinic_string
):
67 """Uses a list with argument clinic information (see PEP 436)."""
68 clinic_args
= list(_parse_argument_clinic(clinic_string
))
70 iterator
= PushBackIterator(arguments
.unpack())
71 for i
, (name
, optional
, allow_kwargs
, stars
) in enumerate(clinic_args
):
74 for key
, argument
in iterator
:
76 iterator
.push_back((key
, argument
))
79 lazy_values
.append(argument
)
80 yield ValueSet([iterable
.FakeTuple(inference_state
, lazy_values
)])
84 raise NotImplementedError()
85 key
, argument
= next(iterator
, (None, None))
87 debug
.warning('Keyword arguments in argument clinic are currently not supported.')
89 if argument
is None and not optional
:
90 debug
.warning('TypeError: %s expected at least %s arguments, got %s',
91 name
, len(clinic_args
), i
)
94 value_set
= NO_VALUES
if argument
is None else argument
.infer()
96 if not value_set
and not optional
:
97 # For the stdlib we always want values. If we don't get them,
98 # that's ok, maybe something is too hard to resolve, however,
99 # we will not proceed with the type inference of that function.
100 debug
.warning('argument_clinic "%s" not resolvable.', name
)
105 def _parse_argument_clinic(string
):
109 # Optional arguments have to begin with a bracket. And should always be
110 # at the end of the arguments. This is therefore not a proper argument
111 # clinic implementation. `range()` for exmple allows an optional start
112 # value at the beginning.
113 match
= re
.match(r
'(?:(?:(\[),? ?|, ?|)(\**\w+)|, ?/)\]*', string
)
114 string
= string
[len(match
.group(0)):]
115 if not match
.group(2): # A slash -> allow named arguments
118 optional
= optional
or bool(match
.group(1))
119 word
= match
.group(2)
120 stars
= word
.count('*')
122 yield (word
, optional
, allow_kwargs
, stars
)
127 class _AbstractArgumentsMixin
:
128 def unpack(self
, funcdef
=None):
129 raise NotImplementedError
131 def get_calling_nodes(self
):
135 class AbstractArguments(_AbstractArgumentsMixin
):
141 def unpack_arglist(arglist
):
145 if arglist
.type != 'arglist' and not (
146 arglist
.type == 'argument' and arglist
.children
[0] in ('*', '**')):
150 iterator
= iter(arglist
.children
)
151 for child
in iterator
:
154 elif child
in ('*', '**'):
155 c
= next(iterator
, None)
157 yield len(child
.value
), c
158 elif child
.type == 'argument' and \
159 child
.children
[0] in ('*', '**'):
160 assert len(child
.children
) == 2
161 yield len(child
.children
[0].value
), child
.children
[1]
166 class TreeArguments(AbstractArguments
):
167 def __init__(self
, inference_state
, context
, argument_node
, trailer
=None):
169 :param argument_node: May be an argument_node or a list of nodes.
171 self
.argument_node
= argument_node
172 self
.context
= context
173 self
._inference
_state
= inference_state
174 self
.trailer
= trailer
# Can be None, e.g. in a class definition.
177 @inference_state_as_method_param_cache()
178 def create_cached(cls
, *args
, **kwargs
):
179 return cls(*args
, **kwargs
)
181 def unpack(self
, funcdef
=None):
183 for star_count
, el
in unpack_arglist(self
.argument_node
):
185 arrays
= self
.context
.infer_node(el
)
186 iterators
= [_iterate_star_args(self
.context
, a
, el
, funcdef
)
188 for values
in list(zip_longest(*iterators
)):
189 yield None, get_merged_lazy_value(
190 [v
for v
in values
if v
is not None]
192 elif star_count
== 2:
193 arrays
= self
.context
.infer_node(el
)
195 yield from _star_star_dict(self
.context
, dct
, el
, funcdef
)
197 if el
.type == 'argument':
199 if len(c
) == 3: # Keyword argument.
200 named_args
.append((c
[0].value
, LazyTreeValue(self
.context
, c
[2]),))
201 else: # Generator comprehension.
202 # Include the brackets with the parent.
203 sync_comp_for
= el
.children
[1]
204 if sync_comp_for
.type == 'comp_for':
205 sync_comp_for
= sync_comp_for
.children
[1]
206 comp
= iterable
.GeneratorComprehension(
207 self
._inference
_state
,
208 defining_context
=self
.context
,
209 sync_comp_for_node
=sync_comp_for
,
210 entry_node
=el
.children
[0],
212 yield None, LazyKnownValue(comp
)
214 yield None, LazyTreeValue(self
.context
, el
)
216 # Reordering arguments is necessary, because star args sometimes appear
217 # after named argument, but in the actual order it's prepended.
218 yield from named_args
220 def _as_tree_tuple_objects(self
):
221 for star_count
, argument
in unpack_arglist(self
.argument_node
):
223 if argument
.type == 'argument':
224 if len(argument
.children
) == 3: # Keyword argument.
225 argument
, default
= argument
.children
[::2]
226 yield argument
, default
, star_count
228 def iter_calling_names_with_star(self
):
229 for name
, default
, star_count
in self
._as
_tree
_tuple
_objects
():
230 # TODO this function is a bit strange. probably refactor?
231 if not star_count
or not isinstance(name
, tree
.Name
):
234 yield TreeNameDefinition(self
.context
, name
)
237 return '<%s: %s>' % (self
.__class
__.__name
__, self
.argument_node
)
239 def get_calling_nodes(self
):
240 old_arguments_list
= []
243 while arguments
not in old_arguments_list
:
244 if not isinstance(arguments
, TreeArguments
):
247 old_arguments_list
.append(arguments
)
248 for calling_name
in reversed(list(arguments
.iter_calling_names_with_star())):
249 names
= calling_name
.goto()
252 if isinstance(names
[0], AnonymousParamName
):
253 # Dynamic parameters should not have calling nodes, because
254 # they are dynamic and extremely random.
256 if not isinstance(names
[0], ParamName
):
258 executed_param_name
= names
[0].get_executed_param_name()
259 arguments
= executed_param_name
.arguments
262 if arguments
.argument_node
is not None:
263 return [ContextualizedNode(arguments
.context
, arguments
.argument_node
)]
264 if arguments
.trailer
is not None:
265 return [ContextualizedNode(arguments
.context
, arguments
.trailer
)]
269 class ValuesArguments(AbstractArguments
):
270 def __init__(self
, values_list
):
271 self
._values
_list
= values_list
273 def unpack(self
, funcdef
=None):
274 for values
in self
._values
_list
:
275 yield None, LazyKnownValues(values
)
278 return '<%s: %s>' % (self
.__class
__.__name
__, self
._values
_list
)
281 class TreeArgumentsWrapper(_AbstractArgumentsMixin
):
282 def __init__(self
, arguments
):
283 self
._wrapped
_arguments
= arguments
287 return self
._wrapped
_arguments
.context
290 def argument_node(self
):
291 return self
._wrapped
_arguments
.argument_node
295 return self
._wrapped
_arguments
.trailer
297 def unpack(self
, func
=None):
298 raise NotImplementedError
300 def get_calling_nodes(self
):
301 return self
._wrapped
_arguments
.get_calling_nodes()
304 return '<%s: %s>' % (self
.__class
__.__name
__, self
._wrapped
_arguments
)
307 def _iterate_star_args(context
, array
, input_node
, funcdef
=None):
308 if not array
.py__getattribute__('__iter__'):
309 if funcdef
is not None:
310 # TODO this funcdef should not be needed.
311 m
= "TypeError: %s() argument after * must be a sequence, not %s" \
312 % (funcdef
.name
.value
, array
)
313 analysis
.add(context
, 'type-error-star', input_node
, message
=m
)
315 iter_
= array
.py__iter__
316 except AttributeError:
322 def _star_star_dict(context
, array
, input_node
, funcdef
):
323 from jedi
.inference
.value
.instance
import CompiledInstance
324 if isinstance(array
, CompiledInstance
) and array
.name
.string_name
== 'dict':
325 # For now ignore this case. In the future add proper iterators and just
326 # make one call without crazy isinstance checks.
328 elif isinstance(array
, iterable
.Sequence
) and array
.array_type
== 'dict':
329 return array
.exact_key_items()
331 if funcdef
is not None:
332 m
= "TypeError: %s argument after ** must be a mapping, not %s" \
333 % (funcdef
.name
.value
, array
)
334 analysis
.add(context
, 'type-error-star-star', input_node
, message
=m
)