1from abc import abstractmethod
2from inspect import Parameter
3from typing import Optional, Tuple
4
5from parso.tree import search_ancestor
6
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
15
16
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
26
27
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    """
37
38    @abstractmethod
39    def infer(self):
40        raise NotImplementedError
41
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}
47
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
52
53        module_names = self.get_root_context().string_names
54        if module_names is None:
55            return None
56        return module_names + qualified_names
57
58    def _get_qualified_names(self):
59        # By default, a name has no qualified names.
60        return None
61
62    def get_root_context(self):
63        return self.parent_context.get_root_context()
64
65    def get_public_name(self):
66        return self.string_name
67
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)
73
74    def is_import(self):
75        return False
76
77    def py__doc__(self):
78        return ''
79
80    @property
81    def api_type(self):
82        return self.parent_context.api_type
83
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
90
91
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
99
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
104
105    def infer(self):
106        return NO_VALUES
107
108
109class AbstractTreeName(AbstractNameDefinition):
110    def __init__(self, parent_context, tree_name):
111        self.parent_context = parent_context
112        self.tree_name = tree_name
113
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
127
128        return super().get_qualified_names(include_module_names)
129
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,)
135
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
142
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]
166
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                )
203
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)
214
215    def is_import(self):
216        imp = search_ancestor(self.tree_name, 'import_from', 'import_name')
217        return imp is not None
218
219    @property
220    def string_name(self):
221        return self.tree_name.value
222
223    @property
224    def start_pos(self):
225        return self.tree_name.start_pos
226
227
228class ValueNameMixin:
229    def infer(self):
230        return ValueSet([self._value])
231
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
240
241    def _get_qualified_names(self):
242        return self._value.get_qualified_names()
243
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()
248
249    def get_defining_qualified_value(self):
250        context = self.parent_context
251        if context.is_module() or context.is_class():
252            return self.parent_context.get_value()  # Might be None
253        return None
254
255    @property
256    def api_type(self):
257        return self._value.api_type
258
259
260class ValueName(ValueNameMixin, AbstractTreeName):
261    def __init__(self, value, tree_name):
262        super().__init__(value.parent_context, tree_name)
263        self._value = value
264
265    def goto(self):
266        return ValueSet([self._value.name])
267
268
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    )
277
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        )
286
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')
293
294    def assignment_indexes(self):
295        """
296        Returns an array of tuple(int, node) of the indexes that are used in
297        tuple assignments.
298
299        For example if the name is ``y`` in the following code::
300
301            x, (y, z) = 2, ''
302
303        would result in ``[(1, xyz_node), (0, yz_node)]``.
304
305        When searching for b in the case ``a, *b, c = [...]`` it will return::
306
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
330
331            compare = node
332            node = node.parent
333        return indexes
334
335    @property
336    def inference_state(self):
337        # Used by the cache function below
338        return self.parent_context.inference_state
339
340    @inference_state_method_cache(default='')
341    def py__doc__(self):
342        api_type = self.api_type
343        if api_type in ('function', 'class'):
344            # Make sure the names are not TreeNameDefinitions anymore.
345            return clean_scope_docstring(self.tree_name.get_definition())
346
347        if api_type == 'module':
348            names = self.goto()
349            if self not in names:
350                return _merge_name_docs(names)
351
352        if api_type == 'statement' and self.tree_name.is_definition():
353            return find_statement_documentation(self.tree_name.get_definition())
354        return ''
355
356
357class _ParamMixin:
358    def maybe_positional_argument(self, include_star=True):
359        options = [Parameter.POSITIONAL_ONLY, Parameter.POSITIONAL_OR_KEYWORD]
360        if include_star:
361            options.append(Parameter.VAR_POSITIONAL)
362        return self.get_kind() in options
363
364    def maybe_keyword_argument(self, include_stars=True):
365        options = [Parameter.KEYWORD_ONLY, Parameter.POSITIONAL_OR_KEYWORD]
366        if include_stars:
367            options.append(Parameter.VAR_KEYWORD)
368        return self.get_kind() in options
369
370    def _kind_string(self):
371        kind = self.get_kind()
372        if kind == Parameter.VAR_POSITIONAL:  # *args
373            return '*'
374        if kind == Parameter.VAR_KEYWORD:  # **kwargs
375            return '**'
376        return ''
377
378    def get_qualified_names(self, include_module_names=False):
379        return None
380
381
382class ParamNameInterface(_ParamMixin):
383    api_type = 'param'
384
385    def get_kind(self):
386        raise NotImplementedError
387
388    def to_string(self):
389        raise NotImplementedError
390
391    def get_executed_param_name(self):
392        """
393        For dealing with type inference and working around the graph, we
394        sometimes want to have the param name of the execution. This feels a
395        bit strange and we might have to refactor at some point.
396
397        For now however it exists to avoid infering params when we don't really
398        need them (e.g. when we can just instead use annotations.
399        """
400        return None
401
402    @property
403    def star_count(self):
404        kind = self.get_kind()
405        if kind == Parameter.VAR_POSITIONAL:
406            return 1
407        if kind == Parameter.VAR_KEYWORD:
408            return 2
409        return 0
410
411
412class BaseTreeParamName(ParamNameInterface, AbstractTreeName):
413    annotation_node = None
414    default_node = None
415
416    def to_string(self):
417        output = self._kind_string() + self.get_public_name()
418        annotation = self.annotation_node
419        default = self.default_node
420        if annotation is not None:
421            output += ': ' + annotation.get_code(include_prefix=False)
422        if default is not None:
423            output += '=' + default.get_code(include_prefix=False)
424        return output
425
426    def get_public_name(self):
427        name = self.string_name
428        if name.startswith('__'):
429            # Params starting with __ are an equivalent to positional only
430            # variables in typeshed.
431            name = name[2:]
432        return name
433
434    def goto(self, **kwargs):
435        return [self]
436
437
438class _ActualTreeParamName(BaseTreeParamName):
439    def __init__(self, function_value, tree_name):
440        super().__init__(
441            function_value.get_default_param_context(), tree_name)
442        self.function_value = function_value
443
444    def _get_param_node(self):
445        return search_ancestor(self.tree_name, 'param')
446
447    @property
448    def annotation_node(self):
449        return self._get_param_node().annotation
450
451    def infer_annotation(self, execute_annotation=True, ignore_stars=False):
452        from jedi.inference.gradual.annotation import infer_param
453        values = infer_param(
454            self.function_value, self._get_param_node(),
455            ignore_stars=ignore_stars)
456        if execute_annotation:
457            values = values.execute_annotation()
458        return values
459
460    def infer_default(self):
461        node = self.default_node
462        if node is None:
463            return NO_VALUES
464        return self.parent_context.infer_node(node)
465
466    @property
467    def default_node(self):
468        return self._get_param_node().default
469
470    def get_kind(self):
471        tree_param = self._get_param_node()
472        if tree_param.star_count == 1:  # *args
473            return Parameter.VAR_POSITIONAL
474        if tree_param.star_count == 2:  # **kwargs
475            return Parameter.VAR_KEYWORD
476
477        # Params starting with __ are an equivalent to positional only
478        # variables in typeshed.
479        if tree_param.name.value.startswith('__'):
480            return Parameter.POSITIONAL_ONLY
481
482        parent = tree_param.parent
483        param_appeared = False
484        for p in parent.children:
485            if param_appeared:
486                if p == '/':
487                    return Parameter.POSITIONAL_ONLY
488            else:
489                if p == '*':
490                    return Parameter.KEYWORD_ONLY
491                if p.type == 'param':
492                    if p.star_count:
493                        return Parameter.KEYWORD_ONLY
494                    if p == tree_param:
495                        param_appeared = True
496        return Parameter.POSITIONAL_OR_KEYWORD
497
498    def infer(self):
499        values = self.infer_annotation()
500        if values:
501            return values
502
503        doc_params = docstrings.infer_param(self.function_value, self._get_param_node())
504        return doc_params
505
506
507class AnonymousParamName(_ActualTreeParamName):
508    @plugin_manager.decorate(name='goto_anonymous_param')
509    def goto(self):
510        return super().goto()
511
512    @plugin_manager.decorate(name='infer_anonymous_param')
513    def infer(self):
514        values = super().infer()
515        if values:
516            return values
517        from jedi.inference.dynamic_params import dynamic_param_lookup
518        param = self._get_param_node()
519        values = dynamic_param_lookup(self.function_value, param.position_index)
520        if values:
521            return values
522
523        if param.star_count == 1:
524            from jedi.inference.value.iterable import FakeTuple
525            value = FakeTuple(self.function_value.inference_state, [])
526        elif param.star_count == 2:
527            from jedi.inference.value.iterable import FakeDict
528            value = FakeDict(self.function_value.inference_state, {})
529        elif param.default is None:
530            return NO_VALUES
531        else:
532            return self.function_value.parent_context.infer_node(param.default)
533        return ValueSet({value})
534
535
536class ParamName(_ActualTreeParamName):
537    def __init__(self, function_value, tree_name, arguments):
538        super().__init__(function_value, tree_name)
539        self.arguments = arguments
540
541    def infer(self):
542        values = super().infer()
543        if values:
544            return values
545
546        return self.get_executed_param_name().infer()
547
548    def get_executed_param_name(self):
549        from jedi.inference.param import get_executed_param_names
550        params_names = get_executed_param_names(self.function_value, self.arguments)
551        return params_names[self._get_param_node().position_index]
552
553
554class ParamNameWrapper(_ParamMixin):
555    def __init__(self, param_name):
556        self._wrapped_param_name = param_name
557
558    def __getattr__(self, name):
559        return getattr(self._wrapped_param_name, name)
560
561    def __repr__(self):
562        return '<%s: %s>' % (self.__class__.__name__, self._wrapped_param_name)
563
564
565class ImportName(AbstractNameDefinition):
566    start_pos = (1, 0)
567    _level = 0
568
569    def __init__(self, parent_context, string_name):
570        self._from_module_context = parent_context
571        self.string_name = string_name
572
573    def get_qualified_names(self, include_module_names=False):
574        if include_module_names:
575            if self._level:
576                assert self._level == 1, "Everything else is not supported for now"
577                module_names = self._from_module_context.string_names
578                if module_names is None:
579                    return module_names
580                return module_names + (self.string_name,)
581            return (self.string_name,)
582        return ()
583
584    @property
585    def parent_context(self):
586        m = self._from_module_context
587        import_values = self.infer()
588        if not import_values:
589            return m
590        # It's almost always possible to find the import or to not find it. The
591        # importing returns only one value, pretty much always.
592        return next(iter(import_values)).as_context()
593
594    @memoize_method
595    def infer(self):
596        from jedi.inference.imports import Importer
597        m = self._from_module_context
598        return Importer(m.inference_state, [self.string_name], m, level=self._level).follow()
599
600    def goto(self):
601        return [m.name for m in self.infer()]
602
603    @property
604    def api_type(self):
605        return 'module'
606
607    def py__doc__(self):
608        return _merge_name_docs(self.goto())
609
610
611class SubModuleName(ImportName):
612    _level = 1
613
614
615class NameWrapper:
616    def __init__(self, wrapped_name):
617        self._wrapped_name = wrapped_name
618
619    def __getattr__(self, name):
620        return getattr(self._wrapped_name, name)
621
622    def __repr__(self):
623        return '%s(%s)' % (self.__class__.__name__, self._wrapped_name)
624
625
626class StubNameMixin:
627    def py__doc__(self):
628        from jedi.inference.gradual.conversion import convert_names
629        # Stubs are not complicated and we can just follow simple statements
630        # that have an equals in them, because they typically make something
631        # else public. See e.g. stubs for `requests`.
632        names = [self]
633        if self.api_type == 'statement' and '=' in self.tree_name.get_definition().children:
634            names = [v.name for v in self.infer()]
635
636        names = convert_names(names, prefer_stub_to_compiled=False)
637        if self in names:
638            return super().py__doc__()
639        else:
640            # We have signatures ourselves in stubs, so don't use signatures
641            # from the implementation.
642            return _merge_name_docs(names)
643
644
645# From here on down we make looking up the sys.version_info fast.
646class StubName(StubNameMixin, TreeNameDefinition):
647    def infer(self):
648        inferred = super().infer()
649        if self.string_name == 'version_info' and self.get_root_context().py__name__() == 'sys':
650            from jedi.inference.gradual.stub_value import VersionInfo
651            return ValueSet(VersionInfo(c) for c in inferred)
652        return inferred
653
654
655class ModuleName(ValueNameMixin, AbstractNameDefinition):
656    start_pos = 1, 0
657
658    def __init__(self, value, name):
659        self._value = value
660        self._name = name
661
662    @property
663    def string_name(self):
664        return self._name
665
666
667class StubModuleName(StubNameMixin, ModuleName):
668    pass
669