]> crepu.dev Git - config.git/blame - djavu-asus/emacs/elpy/rpc-venv/lib/python3.11/site-packages/jedi/inference/arguments.py
Reorganización de directorios
[config.git] / djavu-asus / emacs / elpy / rpc-venv / lib / python3.11 / site-packages / jedi / inference / arguments.py
CommitLineData
53e6db90
DC
1import re
2from itertools import zip_longest
3
4from parso.python import tree
5
6from jedi import debug
7from jedi.inference.utils import PushBackIterator
8from jedi.inference import analysis
9from jedi.inference.lazy_value import LazyKnownValue, LazyKnownValues, \
10 LazyTreeValue, get_merged_lazy_value
11from jedi.inference.names import ParamName, TreeNameDefinition, AnonymousParamName
12from jedi.inference.base_value import NO_VALUES, ValueSet, ContextualizedNode
13from jedi.inference.value import iterable
14from jedi.inference.cache import inference_state_as_method_param_cache
15
16
17def try_iter_content(types, depth=0):
18 """Helper method for static analysis."""
19 if depth > 10:
20 # It's possible that a loop has references on itself (especially with
21 # CompiledValue). Therefore don't loop infinitely.
22 return
23
24 for typ in types:
25 try:
26 f = typ.py__iter__
27 except AttributeError:
28 pass
29 else:
30 for lazy_value in f():
31 try_iter_content(lazy_value.infer(), depth + 1)
32
33
34class ParamIssue(Exception):
35 pass
36
37
38def repack_with_argument_clinic(clinic_string):
39 """
40 Transforms a function or method with arguments to the signature that is
41 given as an argument clinic notation.
42
43 Argument clinic is part of CPython and used for all the functions that are
44 implemented in C (Python 3.7):
45
46 str.split.__text_signature__
47 # Results in: '($self, /, sep=None, maxsplit=-1)'
48 """
49 def decorator(func):
50 def wrapper(value, arguments):
51 try:
52 args = tuple(iterate_argument_clinic(
53 value.inference_state,
54 arguments,
55 clinic_string,
56 ))
57 except ParamIssue:
58 return NO_VALUES
59 else:
60 return func(value, *args)
61
62 return wrapper
63 return decorator
64
65
66def 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))
69
70 iterator = PushBackIterator(arguments.unpack())
71 for i, (name, optional, allow_kwargs, stars) in enumerate(clinic_args):
72 if stars == 1:
73 lazy_values = []
74 for key, argument in iterator:
75 if key is not None:
76 iterator.push_back((key, argument))
77 break
78
79 lazy_values.append(argument)
80 yield ValueSet([iterable.FakeTuple(inference_state, lazy_values)])
81 lazy_values
82 continue
83 elif stars == 2:
84 raise NotImplementedError()
85 key, argument = next(iterator, (None, None))
86 if key is not None:
87 debug.warning('Keyword arguments in argument clinic are currently not supported.')
88 raise ParamIssue
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)
92 raise ParamIssue
93
94 value_set = NO_VALUES if argument is None else argument.infer()
95
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)
101 raise ParamIssue
102 yield value_set
103
104
105def _parse_argument_clinic(string):
106 allow_kwargs = False
107 optional = False
108 while 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
116 allow_kwargs = True
117 continue
118 optional = optional or bool(match.group(1))
119 word = match.group(2)
120 stars = word.count('*')
121 word = word[stars:]
122 yield (word, optional, allow_kwargs, stars)
123 if stars:
124 allow_kwargs = True
125
126
127class _AbstractArgumentsMixin:
128 def unpack(self, funcdef=None):
129 raise NotImplementedError
130
131 def get_calling_nodes(self):
132 return []
133
134
135class AbstractArguments(_AbstractArgumentsMixin):
136 context = None
137 argument_node = None
138 trailer = None
139
140
141def unpack_arglist(arglist):
142 if arglist is None:
143 return
144
145 if arglist.type != 'arglist' and not (
146 arglist.type == 'argument' and arglist.children[0] in ('*', '**')):
147 yield 0, arglist
148 return
149
150 iterator = iter(arglist.children)
151 for child in iterator:
152 if child == ',':
153 continue
154 elif child in ('*', '**'):
155 c = next(iterator, None)
156 assert c is not 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]
162 else:
163 yield 0, child
164
165
166class TreeArguments(AbstractArguments):
167 def __init__(self, inference_state, context, argument_node, trailer=None):
168 """
169 :param argument_node: May be an argument_node or a list of nodes.
170 """
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.
175
176 @classmethod
177 @inference_state_as_method_param_cache()
178 def create_cached(cls, *args, **kwargs):
179 return cls(*args, **kwargs)
180
181 def unpack(self, funcdef=None):
182 named_args = []
183 for star_count, el in unpack_arglist(self.argument_node):
184 if star_count == 1:
185 arrays = self.context.infer_node(el)
186 iterators = [_iterate_star_args(self.context, a, el, funcdef)
187 for a in arrays]
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]
191 )
192 elif star_count == 2:
193 arrays = self.context.infer_node(el)
194 for dct in arrays:
195 yield from _star_star_dict(self.context, dct, el, funcdef)
196 else:
197 if el.type == 'argument':
198 c = el.children
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],
211 )
212 yield None, LazyKnownValue(comp)
213 else:
214 yield None, LazyTreeValue(self.context, el)
215
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
219
220 def _as_tree_tuple_objects(self):
221 for star_count, argument in unpack_arglist(self.argument_node):
222 default = None
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
227
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):
232 continue
233
234 yield TreeNameDefinition(self.context, name)
235
236 def __repr__(self):
237 return '<%s: %s>' % (self.__class__.__name__, self.argument_node)
238
239 def get_calling_nodes(self):
240 old_arguments_list = []
241 arguments = self
242
243 while arguments not in old_arguments_list:
244 if not isinstance(arguments, TreeArguments):
245 break
246
247 old_arguments_list.append(arguments)
248 for calling_name in reversed(list(arguments.iter_calling_names_with_star())):
249 names = calling_name.goto()
250 if len(names) != 1:
251 break
252 if isinstance(names[0], AnonymousParamName):
253 # Dynamic parameters should not have calling nodes, because
254 # they are dynamic and extremely random.
255 return []
256 if not isinstance(names[0], ParamName):
257 break
258 executed_param_name = names[0].get_executed_param_name()
259 arguments = executed_param_name.arguments
260 break
261
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)]
266 return []
267
268
269class ValuesArguments(AbstractArguments):
270 def __init__(self, values_list):
271 self._values_list = values_list
272
273 def unpack(self, funcdef=None):
274 for values in self._values_list:
275 yield None, LazyKnownValues(values)
276
277 def __repr__(self):
278 return '<%s: %s>' % (self.__class__.__name__, self._values_list)
279
280
281class TreeArgumentsWrapper(_AbstractArgumentsMixin):
282 def __init__(self, arguments):
283 self._wrapped_arguments = arguments
284
285 @property
286 def context(self):
287 return self._wrapped_arguments.context
288
289 @property
290 def argument_node(self):
291 return self._wrapped_arguments.argument_node
292
293 @property
294 def trailer(self):
295 return self._wrapped_arguments.trailer
296
297 def unpack(self, func=None):
298 raise NotImplementedError
299
300 def get_calling_nodes(self):
301 return self._wrapped_arguments.get_calling_nodes()
302
303 def __repr__(self):
304 return '<%s: %s>' % (self.__class__.__name__, self._wrapped_arguments)
305
306
307def _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)
314 try:
315 iter_ = array.py__iter__
316 except AttributeError:
317 pass
318 else:
319 yield from iter_()
320
321
322def _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.
327 return {}
328 elif isinstance(array, iterable.Sequence) and array.array_type == 'dict':
329 return array.exact_key_items()
330 else:
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)
335 return {}