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