1from abc import abstractmethod
2from inspect import Parameter
3from typing import Optional, Tuple
4
5from jedi.parser_utils import find_statement_documentation, clean_scope_docstring
6from jedi.inference.utils import unite
7from jedi.inference.base_value import ValueSet, NO_VALUES
8from jedi.inference.cache import inference_state_method_cache
9from jedi.inference import docstrings
10from jedi.cache import memoize_method
11from jedi.inference.helpers import deep_ast_copy, infer_call_of_leaf
12from jedi.plugins import plugin_manager
13
14
15def _merge_name_docs(names):
16 doc = ''
17 for name in names:
18 if doc:
19 # In case we have multiple values, just return all of them
20 # separated by a few dashes.
21 doc += '\n' + '-' * 30 + '\n'
22 doc += name.py__doc__()
23 return doc
24
25
26class AbstractNameDefinition:
27 start_pos: Optional[Tuple[int, int]] = None
28 string_name: str
29 parent_context = None
30 tree_name = None
31 is_value_name = True
32 """
33 Used for the Jedi API to know if it's a keyword or an actual name.
34 """
35
36 @abstractmethod
37 def infer(self):
38 raise NotImplementedError
39
40 @abstractmethod
41 def goto(self):
42 # Typically names are already definitions and therefore a goto on that
43 # name will always result on itself.
44 return {self}
45
46 def get_qualified_names(self, include_module_names=False):
47 qualified_names = self._get_qualified_names()
48 if qualified_names is None or not include_module_names:
49 return qualified_names
50
51 module_names = self.get_root_context().string_names
52 if module_names is None:
53 return None
54 return module_names + qualified_names
55
56 def _get_qualified_names(self):
57 # By default, a name has no qualified names.
58 return None
59
60 def get_root_context(self):
61 return self.parent_context.get_root_context()
62
63 def get_public_name(self):
64 return self.string_name
65
66 def __repr__(self):
67 if self.start_pos is None:
68 return '<%s: string_name=%s>' % (self.__class__.__name__, self.string_name)
69 return '<%s: string_name=%s start_pos=%s>' % (self.__class__.__name__,
70 self.string_name, self.start_pos)
71
72 def is_import(self):
73 return False
74
75 def py__doc__(self):
76 return ''
77
78 @property
79 def api_type(self):
80 return self.parent_context.api_type
81
82 def get_defining_qualified_value(self):
83 """
84 Returns either None or the value that is public and qualified. Won't
85 return a function, because a name in a function is never public.
86 """
87 return None
88
89
90class AbstractArbitraryName(AbstractNameDefinition):
91 """
92 When you e.g. want to complete dicts keys, you probably want to complete
93 string literals, which is not really a name, but for Jedi we use this
94 concept of Name for completions as well.
95 """
96 is_value_name = False
97
98 def __init__(self, inference_state, string):
99 self.inference_state = inference_state
100 self.string_name = string
101 self.parent_context = inference_state.builtins_module
102
103 def infer(self):
104 return NO_VALUES
105
106
107class AbstractTreeName(AbstractNameDefinition):
108 def __init__(self, parent_context, tree_name):
109 self.parent_context = parent_context
110 self.tree_name = tree_name
111
112 def get_qualified_names(self, include_module_names=False):
113 import_node = self.tree_name.search_ancestor('import_name', 'import_from')
114 # For import nodes we cannot just have names, because it's very unclear
115 # how they would look like. For now we just ignore them in most cases.
116 # In case of level == 1, it works always, because it's like a submodule
117 # lookup.
118 if import_node is not None and not (import_node.level == 1
119 and self.get_root_context().get_value().is_package()):
120 # TODO improve the situation for when level is present.
121 if include_module_names and not import_node.level:
122 return tuple(n.value for n in import_node.get_path_for_name(self.tree_name))
123 else:
124 return None
125
126 return super().get_qualified_names(include_module_names)
127
128 def _get_qualified_names(self):
129 parent_names = self.parent_context.get_qualified_names()
130 if parent_names is None:
131 return None
132 return parent_names + (self.tree_name.value,)
133
134 def get_defining_qualified_value(self):
135 if self.is_import():
136 raise NotImplementedError("Shouldn't really happen, please report")
137 elif self.parent_context:
138 return self.parent_context.get_value() # Might be None
139 return None
140
141 def goto(self):
142 context = self.parent_context
143 name = self.tree_name
144 definition = name.get_definition(import_name_always=True)
145 if definition is not None:
146 type_ = definition.type
147 if type_ == 'expr_stmt':
148 # Only take the parent, because if it's more complicated than just
149 # a name it's something you can "goto" again.
150 is_simple_name = name.parent.type not in ('power', 'trailer')
151 if is_simple_name:
152 return [self]
153 elif type_ in ('import_from', 'import_name'):
154 from jedi.inference.imports import goto_import
155 module_names = goto_import(context, name)
156 return module_names
157 else:
158 return [self]
159 else:
160 from jedi.inference.imports import follow_error_node_imports_if_possible
161 values = follow_error_node_imports_if_possible(context, name)
162 if values is not None:
163 return [value.name for value in values]
164
165 par = name.parent
166 node_type = par.type
167 if node_type == 'argument' and par.children[1] == '=' and par.children[0] == name:
168 # Named param goto.
169 trailer = par.parent
170 if trailer.type == 'arglist':
171 trailer = trailer.parent
172 if trailer.type != 'classdef':
173 if trailer.type == 'decorator':
174 value_set = context.infer_node(trailer.children[1])
175 else:
176 i = trailer.parent.children.index(trailer)
177 to_infer = trailer.parent.children[:i]
178 if to_infer[0] == 'await':
179 to_infer.pop(0)
180 value_set = context.infer_node(to_infer[0])
181 from jedi.inference.syntax_tree import infer_trailer
182 for trailer in to_infer[1:]:
183 value_set = infer_trailer(context, value_set, trailer)
184 param_names = []
185 for value in value_set:
186 for signature in value.get_signatures():
187 for param_name in signature.get_param_names():
188 if param_name.string_name == name.value:
189 param_names.append(param_name)
190 return param_names
191 elif node_type == 'dotted_name': # Is a decorator.
192 index = par.children.index(name)
193 if index > 0:
194 new_dotted = deep_ast_copy(par)
195 new_dotted.children[index - 1:] = []
196 values = context.infer_node(new_dotted)
197 return unite(
198 value.goto(name, name_context=context)
199 for value in values
200 )
201
202 if node_type == 'trailer' and par.children[0] == '.':
203 values = infer_call_of_leaf(context, name, cut_own_trailer=True)
204 return values.goto(name, name_context=context)
205 else:
206 stmt = name.search_ancestor('expr_stmt', 'lambdef') or name
207 if stmt.type == 'lambdef':
208 stmt = name
209 return context.goto(name, position=stmt.start_pos)
210
211 def is_import(self):
212 imp = self.tree_name.search_ancestor('import_from', 'import_name')
213 return imp is not None
214
215 @property
216 def string_name(self):
217 return self.tree_name.value
218
219 @property
220 def start_pos(self):
221 return self.tree_name.start_pos
222
223
224class ValueNameMixin:
225 def infer(self):
226 return ValueSet([self._value])
227
228 def py__doc__(self):
229 doc = self._value.py__doc__()
230 if not doc and self._value.is_stub():
231 from jedi.inference.gradual.conversion import convert_names
232 names = convert_names([self], prefer_stub_to_compiled=False)
233 if self not in names:
234 return _merge_name_docs(names)
235 return doc
236
237 def _get_qualified_names(self):
238 return self._value.get_qualified_names()
239
240 def get_root_context(self):
241 if self.parent_context is None: # A module
242 return self._value.as_context()
243 return super().get_root_context()
244
245 def get_defining_qualified_value(self):
246 context = self.parent_context
247 if context is not None and (context.is_module() or context.is_class()):
248 return self.parent_context.get_value() # Might be None
249 return None
250
251 @property
252 def api_type(self):
253 return self._value.api_type
254
255
256class ValueName(ValueNameMixin, AbstractTreeName):
257 def __init__(self, value, tree_name):
258 super().__init__(value.parent_context, tree_name)
259 self._value = value
260
261 def goto(self):
262 return ValueSet([self._value.name])
263
264
265class TreeNameDefinition(AbstractTreeName):
266 _API_TYPES = dict(
267 import_name='module',
268 import_from='module',
269 funcdef='function',
270 param='param',
271 classdef='class',
272 )
273
274 def infer(self):
275 # Refactor this, should probably be here.
276 from jedi.inference.syntax_tree import tree_name_to_values
277 return tree_name_to_values(
278 self.parent_context.inference_state,
279 self.parent_context,
280 self.tree_name
281 )
282
283 @property
284 def api_type(self):
285 definition = self.tree_name.get_definition(import_name_always=True)
286 if definition is None:
287 return 'statement'
288 return self._API_TYPES.get(definition.type, 'statement')
289
290 def assignment_indexes(self):
291 """
292 Returns an array of tuple(int, node) of the indexes that are used in
293 tuple assignments.
294
295 For example if the name is ``y`` in the following code::
296
297 x, (y, z) = 2, ''
298
299 would result in ``[(1, xyz_node), (0, yz_node)]``.
300
301 When searching for b in the case ``a, *b, c = [...]`` it will return::
302
303 [(slice(1, -1), abc_node)]
304 """
305 indexes = []
306 is_star_expr = False
307 node = self.tree_name.parent
308 compare = self.tree_name
309 while node is not None:
310 if node.type in ('testlist', 'testlist_comp', 'testlist_star_expr', 'exprlist'):
311 for i, child in enumerate(node.children):
312 if child == compare:
313 index = int(i / 2)
314 if is_star_expr:
315 from_end = int((len(node.children) - i) / 2)
316 index = slice(index, -from_end)
317 indexes.insert(0, (index, node))
318 break
319 else:
320 raise LookupError("Couldn't find the assignment.")
321 is_star_expr = False
322 elif node.type == 'star_expr':
323 is_star_expr = True
324 elif node.type in ('expr_stmt', 'sync_comp_for'):
325 break
326
327 compare = node
328 node = node.parent
329 return indexes
330
331 @property
332 def inference_state(self):
333 # Used by the cache function below
334 return self.parent_context.inference_state
335
336 @inference_state_method_cache(default='')
337 def py__doc__(self):
338 api_type = self.api_type
339 if api_type in ('function', 'class', 'property'):
340 if self.parent_context.get_root_context().is_stub():
341 from jedi.inference.gradual.conversion import convert_names
342 names = convert_names([self], prefer_stub_to_compiled=False)
343 if self not in names:
344 return _merge_name_docs(names)
345
346 # Make sure the names are not TreeNameDefinitions anymore.
347 return clean_scope_docstring(self.tree_name.get_definition())
348
349 if api_type == 'module':
350 names = self.goto()
351 if self not in names:
352 return _merge_name_docs(names)
353
354 if api_type == 'statement' and self.tree_name.is_definition():
355 return find_statement_documentation(self.tree_name.get_definition())
356 return ''
357
358
359class _ParamMixin:
360 def maybe_positional_argument(self, include_star=True):
361 options = [Parameter.POSITIONAL_ONLY, Parameter.POSITIONAL_OR_KEYWORD]
362 if include_star:
363 options.append(Parameter.VAR_POSITIONAL)
364 return self.get_kind() in options
365
366 def maybe_keyword_argument(self, include_stars=True):
367 options = [Parameter.KEYWORD_ONLY, Parameter.POSITIONAL_OR_KEYWORD]
368 if include_stars:
369 options.append(Parameter.VAR_KEYWORD)
370 return self.get_kind() in options
371
372 def _kind_string(self):
373 kind = self.get_kind()
374 if kind == Parameter.VAR_POSITIONAL: # *args
375 return '*'
376 if kind == Parameter.VAR_KEYWORD: # **kwargs
377 return '**'
378 return ''
379
380 def get_qualified_names(self, include_module_names=False):
381 return None
382
383
384class ParamNameInterface(_ParamMixin):
385 api_type = 'param'
386
387 def get_kind(self):
388 raise NotImplementedError
389
390 def to_string(self):
391 raise NotImplementedError
392
393 def get_executed_param_name(self):
394 """
395 For dealing with type inference and working around the graph, we
396 sometimes want to have the param name of the execution. This feels a
397 bit strange and we might have to refactor at some point.
398
399 For now however it exists to avoid infering params when we don't really
400 need them (e.g. when we can just instead use annotations.
401 """
402 return None
403
404 @property
405 def star_count(self):
406 kind = self.get_kind()
407 if kind == Parameter.VAR_POSITIONAL:
408 return 1
409 if kind == Parameter.VAR_KEYWORD:
410 return 2
411 return 0
412
413 def infer_default(self):
414 return NO_VALUES
415
416
417class BaseTreeParamName(ParamNameInterface, AbstractTreeName):
418 annotation_node = None
419 default_node = None
420
421 def to_string(self):
422 output = self._kind_string() + self.get_public_name()
423 annotation = self.annotation_node
424 default = self.default_node
425 if annotation is not None:
426 output += ': ' + annotation.get_code(include_prefix=False)
427 if default is not None:
428 output += '=' + default.get_code(include_prefix=False)
429 return output
430
431 def get_public_name(self):
432 name = self.string_name
433 if name.startswith('__'):
434 # Params starting with __ are an equivalent to positional only
435 # variables in typeshed.
436 name = name[2:]
437 return name
438
439 def goto(self, **kwargs):
440 return [self]
441
442
443class _ActualTreeParamName(BaseTreeParamName):
444 def __init__(self, function_value, tree_name):
445 super().__init__(
446 function_value.get_default_param_context(), tree_name)
447 self.function_value = function_value
448
449 def _get_param_node(self):
450 return self.tree_name.search_ancestor('param')
451
452 @property
453 def annotation_node(self):
454 return self._get_param_node().annotation
455
456 def infer_annotation(self, execute_annotation=True, ignore_stars=False):
457 from jedi.inference.gradual.annotation import infer_param
458 values = infer_param(
459 self.function_value, self._get_param_node(),
460 ignore_stars=ignore_stars)
461 if execute_annotation:
462 values = values.execute_annotation()
463 return values
464
465 def infer_default(self):
466 node = self.default_node
467 if node is None:
468 return NO_VALUES
469 return self.parent_context.infer_node(node)
470
471 @property
472 def default_node(self):
473 return self._get_param_node().default
474
475 def get_kind(self):
476 tree_param = self._get_param_node()
477 if tree_param.star_count == 1: # *args
478 return Parameter.VAR_POSITIONAL
479 if tree_param.star_count == 2: # **kwargs
480 return Parameter.VAR_KEYWORD
481
482 # Params starting with __ are an equivalent to positional only
483 # variables in typeshed.
484 if tree_param.name.value.startswith('__'):
485 return Parameter.POSITIONAL_ONLY
486
487 parent = tree_param.parent
488 param_appeared = False
489 for p in parent.children:
490 if param_appeared:
491 if p == '/':
492 return Parameter.POSITIONAL_ONLY
493 else:
494 if p == '*':
495 return Parameter.KEYWORD_ONLY
496 if p.type == 'param':
497 if p.star_count:
498 return Parameter.KEYWORD_ONLY
499 if p == tree_param:
500 param_appeared = True
501 return Parameter.POSITIONAL_OR_KEYWORD
502
503 def infer(self):
504 values = self.infer_annotation()
505 if values:
506 return values
507
508 doc_params = docstrings.infer_param(self.function_value, self._get_param_node())
509 return doc_params
510
511
512class AnonymousParamName(_ActualTreeParamName):
513 @plugin_manager.decorate(name='goto_anonymous_param')
514 def goto(self):
515 return super().goto()
516
517 @plugin_manager.decorate(name='infer_anonymous_param')
518 def infer(self):
519 values = super().infer()
520 if values:
521 return values
522 from jedi.inference.dynamic_params import dynamic_param_lookup
523 param = self._get_param_node()
524 values = dynamic_param_lookup(self.function_value, param.position_index)
525 if values:
526 return values
527
528 if param.star_count == 1:
529 from jedi.inference.value.iterable import FakeTuple
530 value = FakeTuple(self.function_value.inference_state, [])
531 elif param.star_count == 2:
532 from jedi.inference.value.iterable import FakeDict
533 value = FakeDict(self.function_value.inference_state, {})
534 elif param.default is None:
535 return NO_VALUES
536 else:
537 return self.function_value.parent_context.infer_node(param.default)
538 return ValueSet({value})
539
540
541class ParamName(_ActualTreeParamName):
542 def __init__(self, function_value, tree_name, arguments):
543 super().__init__(function_value, tree_name)
544 self.arguments = arguments
545
546 def infer(self):
547 values = super().infer()
548 if values:
549 return values
550
551 return self.get_executed_param_name().infer()
552
553 def get_executed_param_name(self):
554 from jedi.inference.param import get_executed_param_names
555 params_names = get_executed_param_names(self.function_value, self.arguments)
556 return params_names[self._get_param_node().position_index]
557
558
559class ParamNameWrapper(_ParamMixin):
560 def __init__(self, param_name):
561 self._wrapped_param_name = param_name
562
563 def __getattr__(self, name):
564 return getattr(self._wrapped_param_name, name)
565
566 def __repr__(self):
567 return '<%s: %s>' % (self.__class__.__name__, self._wrapped_param_name)
568
569
570class ImportName(AbstractNameDefinition):
571 start_pos = (1, 0)
572 _level = 0
573
574 def __init__(self, parent_context, string_name):
575 self._from_module_context = parent_context
576 self.string_name = string_name
577
578 def get_qualified_names(self, include_module_names=False):
579 if include_module_names:
580 if self._level:
581 assert self._level == 1, "Everything else is not supported for now"
582 module_names = self._from_module_context.string_names
583 if module_names is None:
584 return module_names
585 return module_names + (self.string_name,)
586 return (self.string_name,)
587 return ()
588
589 @property
590 def parent_context(self):
591 m = self._from_module_context
592 import_values = self.infer()
593 if not import_values:
594 return m
595 # It's almost always possible to find the import or to not find it. The
596 # importing returns only one value, pretty much always.
597 return next(iter(import_values)).as_context()
598
599 @memoize_method
600 def infer(self):
601 from jedi.inference.imports import Importer
602 m = self._from_module_context
603 return Importer(m.inference_state, [self.string_name], m, level=self._level).follow()
604
605 def goto(self):
606 return [m.name for m in self.infer()]
607
608 @property
609 def api_type(self):
610 return 'module'
611
612 def py__doc__(self):
613 return _merge_name_docs(self.goto())
614
615
616class SubModuleName(ImportName):
617 _level = 1
618
619
620class NameWrapper:
621 def __init__(self, wrapped_name):
622 self._wrapped_name = wrapped_name
623
624 def __getattr__(self, name):
625 return getattr(self._wrapped_name, name)
626
627 def __repr__(self):
628 return '%s(%s)' % (self.__class__.__name__, self._wrapped_name)
629
630
631class StubNameMixin:
632 def py__doc__(self):
633 from jedi.inference.gradual.conversion import convert_names
634 # Stubs are not complicated and we can just follow simple statements
635 # that have an equals in them, because they typically make something
636 # else public. See e.g. stubs for `requests`.
637 names = [self]
638 if self.api_type == 'statement' and '=' in self.tree_name.get_definition().children:
639 names = [v.name for v in self.infer()]
640
641 names = convert_names(names, prefer_stub_to_compiled=False)
642 if self in names:
643 return super().py__doc__()
644 else:
645 # We have signatures ourselves in stubs, so don't use signatures
646 # from the implementation.
647 return _merge_name_docs(names)
648
649
650# From here on down we make looking up the sys.version_info fast.
651class StubName(StubNameMixin, TreeNameDefinition):
652 def infer(self):
653 inferred = super().infer()
654 if self.string_name == 'version_info' and self.get_root_context().py__name__() == 'sys':
655 from jedi.inference.gradual.stub_value import VersionInfo
656 return ValueSet(VersionInfo(c) for c in inferred)
657 return inferred
658
659
660class ModuleName(ValueNameMixin, AbstractNameDefinition):
661 start_pos = 1, 0
662
663 def __init__(self, value, name):
664 self._value = value
665 self._name = name
666
667 @property
668 def string_name(self):
669 return self._name
670
671
672class StubModuleName(StubNameMixin, ModuleName):
673 pass