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