Example #1
0
    def get_filters(self, search_global=False):
        origin_scope = self._get_origin_scope()
        if search_global:
            position = self._position

            # For functions and classes the defaults don't belong to the
            # function and get evaluated in the context before the function. So
            # make sure to exclude the function/class name.
            if origin_scope is not None:
                ancestor = search_ancestor(origin_scope, 'funcdef', 'classdef', 'lambdef')
                lambdef = None
                if ancestor == 'lambdef':
                    # For lambdas it's even more complicated since parts will
                    # be evaluated later.
                    lambdef = ancestor
                    ancestor = search_ancestor(origin_scope, 'funcdef', 'classdef')
                if ancestor is not None:
                    colon = ancestor.children[-2]
                    if position < colon.start_pos:
                        if lambdef is None or position < lambdef.children[-2].start_pos:
                            position = ancestor.start_pos

            return get_global_filters(self._evaluator, self._context, position, origin_scope)
        else:
            return self._context.get_filters(search_global, self._position, origin_scope=origin_scope)
Example #2
0
    def get_filters(self, search_global=False):
        origin_scope = self._get_origin_scope()
        if search_global:
            position = self._position

            # For functions and classes the defaults don't belong to the
            # function and get evaluated in the context before the function. So
            # make sure to exclude the function/class name.
            if origin_scope is not None:
                ancestor = search_ancestor(origin_scope, 'funcdef', 'classdef',
                                           'lambdef')
                lambdef = None
                if ancestor == 'lambdef':
                    # For lambdas it's even more complicated since parts will
                    # be evaluated later.
                    lambdef = ancestor
                    ancestor = search_ancestor(origin_scope, 'funcdef',
                                               'classdef')
                if ancestor is not None:
                    colon = ancestor.children[-2]
                    if position is not None and position < colon.start_pos:
                        if lambdef is None or position < lambdef.children[
                                -2].start_pos:
                            position = ancestor.start_pos

            return get_global_filters(self._evaluator, self._context, position,
                                      origin_scope)
        else:
            return self._get_context_filters(origin_scope)
Example #3
0
 def _convert_names(self, names):
     for name in names:
         param = search_ancestor(name, 'param')
         if param:
             yield self.param_name(self.context, name)
         else:
             yield TreeNameDefinition(self.context, name)
def _iter_nodes_for_param(param_name):
    from parso.python.tree import search_ancestor
    from jedi.inference.arguments import TreeArguments

    execution_context = param_name.parent_context
    # Walk up the parso tree to get the FunctionNode we want. We use the parso
    # tree rather than going via the execution context so that we're agnostic of
    # the specific scope we're evaluating within (i.e: module or function,
    # etc.).
    function_node = tree.search_ancestor(param_name.tree_name, 'funcdef',
                                         'lambdef')
    module_node = function_node.get_root_node()
    start = function_node.children[-1].start_pos
    end = function_node.children[-1].end_pos
    for name in module_node.get_used_names().get(param_name.string_name):
        if start <= name.start_pos < end:
            # Is used in the function
            argument = name.parent
            if argument.type == 'argument' \
                    and argument.children[0] == '*' * param_name.star_count:
                trailer = search_ancestor(argument, 'trailer')
                if trailer is not None:  # Make sure we're in a function
                    context = execution_context.create_context(trailer)
                    if _goes_to_param_name(param_name, context, name):
                        values = _to_callables(context, trailer)

                        args = TreeArguments.create_cached(
                            execution_context.inference_state,
                            context=context,
                            argument_node=trailer.children[1],
                            trailer=trailer,
                        )
                        for c in values:
                            yield c, args
Example #5
0
 def _convert_names(self, names):
     for name in names:
         param = search_ancestor(name, 'param')
         if param:
             yield self.param_name(self.context, name)
         else:
             yield TreeNameDefinition(self.context, name)
    def parent(self):
        """
        Returns the parent scope of this identifier.

        :rtype: Name
        """
        if not self._name.is_value_name:
            return None

        if self.type in ('function', 'class',
                         'param') and self._name.tree_name is not None:
            # Since the parent_context doesn't really match what the user
            # thinks of that the parent is here, we do these cases separately.
            # The reason for this is the following:
            # - class: Nested classes parent_context is always the
            #   parent_context of the most outer one.
            # - function: Functions in classes have the module as
            #   parent_context.
            # - param: The parent_context of a param is not its function but
            #   e.g. the outer class or module.
            cls_or_func_node = self._name.tree_name.get_definition()
            parent = search_ancestor(cls_or_func_node, 'funcdef', 'classdef',
                                     'file_input')
            context = self._get_module_context().create_value(
                parent).as_context()
        else:
            context = self._name.parent_context

        if context is None:
            return None
        while context.name is None:
            # Happens for comprehension contexts
            context = context.parent_context

        return Name(self._inference_state, context.name)
Example #7
0
def infer_import(context, tree_name, is_goto=False):
    module_context = context.get_root_context()
    import_node = search_ancestor(tree_name, 'import_name', 'import_from')
    import_path = import_node.get_path_for_name(tree_name)
    from_import_name = None
    evaluator = context.evaluator
    try:
        from_names = import_node.get_from_names()
    except AttributeError:
        # Is an import_name
        pass
    else:
        if len(from_names) + 1 == len(import_path):
            # We have to fetch the from_names part first and then check
            # if from_names exists in the modules.
            from_import_name = import_path[-1]
            import_path = from_names

    importer = Importer(evaluator, tuple(import_path),
                        module_context, import_node.level)

    types = importer.follow()

    #if import_node.is_nested() and not self.nested_resolve:
    #    scopes = [NestedImportModule(module, import_node)]

    if not types:
        return NO_CONTEXTS

    if from_import_name is not None:
        types = unite(
            t.py__getattribute__(
                from_import_name,
                name_context=context,
                is_goto=is_goto,
                analysis_errors=False
            )
            for t in types
        )
        if not is_goto:
            types = ContextSet(types)

        if not types:
            path = import_path + [from_import_name]
            importer = Importer(evaluator, tuple(path),
                                module_context, import_node.level)
            types = importer.follow()
            # goto only accepts `Name`
            if is_goto:
                types = set(s.name for s in types)
    else:
        # goto only accepts `Name`
        if is_goto:
            types = set(s.name for s in types)

    debug.dbg('after import: %s', types)
    return types
Example #8
0
 def create_name(self, tree_name):
     definition = tree_name.get_definition()
     if definition and definition.type == 'param' and definition.name == tree_name:
         funcdef = search_ancestor(definition, 'funcdef', 'lambdef')
         func = self.create_value(funcdef)
         return AnonymousParamName(func, tree_name)
     else:
         context = self.create_context(tree_name)
         return TreeNameDefinition(context, tree_name)
Example #9
0
def infer_import(context, tree_name, is_goto=False):
    module_context = context.get_root_context()
    import_node = search_ancestor(tree_name, 'import_name', 'import_from')
    import_path = import_node.get_path_for_name(tree_name)
    from_import_name = None
    evaluator = context.evaluator
    try:
        from_names = import_node.get_from_names()
    except AttributeError:
        # Is an import_name
        pass
    else:
        if len(from_names) + 1 == len(import_path):
            # We have to fetch the from_names part first and then check
            # if from_names exists in the modules.
            from_import_name = import_path[-1]
            import_path = from_names

    importer = Importer(evaluator, tuple(import_path),
                        module_context, import_node.level)

    types = importer.follow()

    #if import_node.is_nested() and not self.nested_resolve:
    #    scopes = [NestedImportModule(module, import_node)]

    if not types:
        return NO_CONTEXTS

    if from_import_name is not None:
        types = unite(
            t.py__getattribute__(
                from_import_name,
                name_context=context,
                is_goto=is_goto,
                analysis_errors=False
            )
            for t in types
        )
        if not is_goto:
            types = ContextSet.from_set(types)

        if not types:
            path = import_path + [from_import_name]
            importer = Importer(evaluator, tuple(path),
                                module_context, import_node.level)
            types = importer.follow()
            # goto only accepts `Name`
            if is_goto:
                types = set(s.name for s in types)
    else:
        # goto only accepts `Name`
        if is_goto:
            types = set(s.name for s in types)

    debug.dbg('after import: %s', types)
    return types
Example #10
0
 def infer(self):
     param_node = search_ancestor(self.tree_name, 'param')
     # TODO I think this should not belong here. It's not even really true,
     #      because classmethod and other descriptors can change it.
     if param_node.position_index == 0:
         # This is a speed optimization, to return the self param (because
         # it's known). This only affects anonymous instances.
         return ContextSet(self.parent_context.instance)
     else:
         return self.get_param().infer()
Example #11
0
 def infer(self):
     param_node = search_ancestor(self.tree_name, 'param')
     # TODO I think this should not belong here. It's not even really true,
     #      because classmethod and other descriptors can change it.
     if param_node.position_index == 0:
         # This is a speed optimization, to return the self param (because
         # it's known). This only affects anonymous instances.
         return ContextSet(self.parent_context.instance)
     else:
         return self.get_param().infer()
Example #12
0
def _get_global_filters_for_name(context, name_or_none, position):
    # For functions and classes the defaults don't belong to the
    # function and get inferred in the value before the function. So
    # make sure to exclude the function/class name.
    if name_or_none is not None:
        ancestor = search_ancestor(name_or_none, 'funcdef', 'classdef', 'lambdef')
        lambdef = None
        if ancestor == 'lambdef':
            # For lambdas it's even more complicated since parts will
            # be inferred later.
            lambdef = ancestor
            ancestor = search_ancestor(name_or_none, 'funcdef', 'classdef')
        if ancestor is not None:
            colon = ancestor.children[-2]
            if position is not None and position < colon.start_pos:
                if lambdef is None or position < lambdef.children[-2].start_pos:
                    position = ancestor.start_pos

    return get_global_filters(context, position, name_or_none)
 def _convert_names(self, names):
     for name in names:
         param = search_ancestor(name, 'param')
         # Here we don't need to check if the param is a default/annotation,
         # because those are not definitions and never make it to this
         # point.
         if param:
             yield self._convert_param(param, name)
         else:
             yield TreeNameDefinition(self.parent_context, name)
Example #14
0
 def infer(self):
     stmt = search_ancestor(self.tree_name, 'expr_stmt')
     if stmt is not None:
         if stmt.children[1].type == "annassign":
             from jedi.inference.gradual.annotation import infer_annotation
             values = infer_annotation(
                 self.parent_context,
                 stmt.children[1].children[1]).execute_annotation()
             if values:
                 return values
     return super().infer()
Example #15
0
def _is_a_pytest_param_and_inherited(param_name):
    """
    Pytest params are either in a `test_*` function or have a pytest fixture
    with the decorator @pytest.fixture.

    This is a heuristic and will work in most cases.
    """
    funcdef = search_ancestor(param_name.tree_name, 'funcdef')
    if funcdef is None:  # A lambda
        return False, False
    decorators = funcdef.get_decorators()
    return _is_pytest_func(funcdef.name.value, decorators), \
        funcdef.name.value == param_name.string_name
Example #16
0
 def create_instance_context(self, class_context, node):
     new = node
     while True:
         func_node = new
         new = search_ancestor(new, 'funcdef', 'classdef')
         if class_context.tree_node is new:
             func = FunctionValue.from_context(class_context, func_node)
             bound_method = BoundMethod(self, class_context, func)
             if func_node.name.value == '__init__':
                 context = bound_method.as_context(self._arguments)
             else:
                 context = bound_method.as_context()
             break
     return context.create_context(node)
Example #17
0
    def get_kind(self):
        tree_param = search_ancestor(self.tree_name, 'param')
        if tree_param.star_count == 1:  # *args
            return Parameter.VAR_POSITIONAL
        if tree_param.star_count == 2:  # **kwargs
            return Parameter.VAR_KEYWORD

        parent = tree_param.parent
        for p in parent.children:
            if p.type == 'param':
                if p.star_count:
                    return Parameter.KEYWORD_ONLY
                if p == tree_param:
                    break
        return Parameter.POSITIONAL_OR_KEYWORD
Example #18
0
    def get_qualified_names(self, include_module_names=False):
        import_node = search_ancestor(self.tree_name, 'import_name', 'import_from')
        # For import nodes we cannot just have names, because it's very unclear
        # how they would look like. For now we just ignore them in most cases.
        # In case of level == 1, it works always, because it's like a submodule
        # lookup.
        if import_node is not None and not (import_node.level == 1
                                            and self.get_root_context().is_package):
            # TODO improve the situation for when level is present.
            if include_module_names and not import_node.level:
                return tuple(n.value for n in import_node.get_path_for_name(self.tree_name))
            else:
                return None

        return super(AbstractTreeName, self).get_qualified_names(include_module_names)
Example #19
0
    def get_qualified_names(self, include_module_names=False):
        import_node = search_ancestor(self.tree_name, 'import_name',
                                      'import_from')
        if import_node is not None:
            return tuple(
                n.value for n in import_node.get_path_for_name(self.tree_name))

        parent_names = self.parent_context.get_qualified_names()
        if parent_names is None:
            return None
        parent_names += (self.tree_name.value, )
        if include_module_names:
            module_names = self.get_root_context().string_names
            if module_names is None:
                return None
            return module_names + parent_names
        return parent_names
def _prepare_infer_import(module_context, tree_name):
    import_node = search_ancestor(tree_name, 'import_name', 'import_from')
    import_path = import_node.get_path_for_name(tree_name)
    from_import_name = None
    try:
        from_names = import_node.get_from_names()
    except AttributeError:
        # Is an import_name
        pass
    else:
        if len(from_names) + 1 == len(import_path):
            # We have to fetch the from_names part first and then check
            # if from_names exists in the modules.
            from_import_name = import_path[-1]
            import_path = from_names

    importer = Importer(module_context.inference_state, tuple(import_path),
                        module_context, import_node.level)

    return from_import_name, tuple(import_path), import_node.level, importer.follow()
Example #21
0
    def _complete_params(self, leaf):
        stack_node = self.stack[-2]
        if stack_node.nonterminal == 'parameters':
            stack_node = self.stack[-3]
        if stack_node.nonterminal == 'funcdef':
            context = get_user_context(self._module_context, self._position)
            node = search_ancestor(leaf, 'error_node', 'funcdef')
            if node.type == 'error_node':
                n = node.children[0]
                if n.type == 'decorators':
                    decorators = n.children
                elif n.type == 'decorator':
                    decorators = [n]
                else:
                    decorators = []
            else:
                decorators = node.get_decorators()
            function_name = stack_node.nodes[1]

            return complete_param_names(context, function_name.value, decorators)
        return []
Example #22
0
def _check_flow_information(context, flow, search_name, pos):
    """ Try to find out the type of a variable just with the information that
    is given by the flows: e.g. It is also responsible for assert checks.::

        if isinstance(k, str):
            k.  # <- completion here

    ensures that `k` is a string.
    """
    if not settings.dynamic_flow_information:
        return None

    result = None
    if is_scope(flow):
        # Check for asserts.
        module_node = flow.get_root_node()
        try:
            names = module_node.get_used_names()[search_name.value]
        except KeyError:
            return None
        names = reversed([
            n for n in names
            if flow.start_pos <= n.start_pos < (pos or flow.end_pos)
        ])

        for name in names:
            ass = search_ancestor(name, 'assert_stmt')
            if ass is not None:
                result = _check_isinstance_type(context, ass.assertion,
                                                search_name)
                if result is not None:
                    return result

    if flow.type in ('if_stmt', 'while_stmt'):
        potential_ifs = [c for c in flow.children[1::4] if c != ':']
        for if_test in reversed(potential_ifs):
            if search_name.start_pos > if_test.end_pos:
                return _check_isinstance_type(context, if_test, search_name)
    return result
Example #23
0
def _check_flow_information(context, flow, search_name, pos):
    """ Try to find out the type of a variable just with the information that
    is given by the flows: e.g. It is also responsible for assert checks.::

        if isinstance(k, str):
            k.  # <- completion here

    ensures that `k` is a string.
    """
    if not settings.dynamic_flow_information:
        return None

    result = None
    if is_scope(flow):
        # Check for asserts.
        module_node = flow.get_root_node()
        try:
            names = module_node.get_used_names()[search_name.value]
        except KeyError:
            return None
        names = reversed([
            n for n in names
            if flow.start_pos <= n.start_pos < (pos or flow.end_pos)
        ])

        for name in names:
            ass = search_ancestor(name, 'assert_stmt')
            if ass is not None:
                result = _check_isinstance_type(context, ass.assertion, search_name)
                if result is not None:
                    return result

    if flow.type in ('if_stmt', 'while_stmt'):
        potential_ifs = [c for c in flow.children[1::4] if c != ':']
        for if_test in reversed(potential_ifs):
            if search_name.start_pos > if_test.end_pos:
                return _check_isinstance_type(context, if_test, search_name)
    return result
Example #24
0
    def goto(self):
        context = self.parent_context
        name = self.tree_name
        definition = name.get_definition(import_name_always=True)
        if definition is not None:
            type_ = definition.type
            if type_ == 'expr_stmt':
                # Only take the parent, because if it's more complicated than just
                # a name it's something you can "goto" again.
                is_simple_name = name.parent.type not in ('power', 'trailer')
                if is_simple_name:
                    return [self]
            elif type_ in ('import_from', 'import_name'):
                from jedi.inference.imports import goto_import
                module_names = goto_import(context, name)
                return module_names
            else:
                return [self]
        else:
            from jedi.inference.imports import follow_error_node_imports_if_possible
            values = follow_error_node_imports_if_possible(context, name)
            if values is not None:
                return [value.name for value in values]

        par = name.parent
        node_type = par.type
        if node_type == 'argument' and par.children[1] == '=' and par.children[
                0] == name:
            # Named param goto.
            trailer = par.parent
            if trailer.type == 'arglist':
                trailer = trailer.parent
            if trailer.type != 'classdef':
                if trailer.type == 'decorator':
                    value_set = context.infer_node(trailer.children[1])
                else:
                    i = trailer.parent.children.index(trailer)
                    to_infer = trailer.parent.children[:i]
                    if to_infer[0] == 'await':
                        to_infer.pop(0)
                    value_set = context.infer_node(to_infer[0])
                    from jedi.inference.syntax_tree import infer_trailer
                    for trailer in to_infer[1:]:
                        value_set = infer_trailer(context, value_set, trailer)
                param_names = []
                for value in value_set:
                    for signature in value.get_signatures():
                        for param_name in signature.get_param_names():
                            if param_name.string_name == name.value:
                                param_names.append(param_name)
                return param_names
        elif node_type == 'dotted_name':  # Is a decorator.
            index = par.children.index(name)
            if index > 0:
                new_dotted = deep_ast_copy(par)
                new_dotted.children[index - 1:] = []
                values = context.infer_node(new_dotted)
                return unite(
                    value.goto(name, name_context=context) for value in values)

        if node_type == 'trailer' and par.children[0] == '.':
            values = infer_call_of_leaf(context, name, cut_own_trailer=True)
            return values.goto(name, name_context=context)
        else:
            stmt = search_ancestor(name, 'expr_stmt', 'lambdef') or name
            if stmt.type == 'lambdef':
                stmt = name
            return context.goto(name, position=stmt.start_pos)
Example #25
0
 def get_parent_function(self):
     """
     Returns the function/lambda of a parameter.
     """
     return search_ancestor(self, 'funcdef', 'lambdef')
Example #26
0
 def get_param(self):
     params = self.parent_context.get_params()
     param_node = search_ancestor(self.tree_name, 'param')
     return params[param_node.position_index]
Example #27
0
 def get_param(self):
     params = self.parent_context.get_params()
     param_node = search_ancestor(self.tree_name, 'param')
     return params[param_node.position_index]
Example #28
0
    def _get_context_completions(self):
        """
        Analyzes the context that a completion is made in and decides what to
        return.

        Technically this works by generating a parser stack and analysing the
        current stack for possible grammar nodes.

        Possible enhancements:
        - global/nonlocal search global
        - yield from / raise from <- could be only exceptions/generators
        - In args: */**: no completion
        - In params (also lambda): no completion before =
        """

        grammar = self._evaluator.grammar

        try:
            self.stack = stack = helpers.get_stack_at_position(
                grammar, self._code_lines, self._module_node, self._position
            )
        except helpers.OnErrorLeaf as e:
            self.stack = stack = None
            if e.error_leaf.value == '.':
                # After ErrorLeaf's that are dots, we will not do any
                # completions since this probably just confuses the user.
                return []
            # If we don't have a context, just use global completion.

            return self._global_completions()

        allowed_transitions = \
            list(stack._allowed_transition_names_and_token_types())

        if 'if' in allowed_transitions:
            leaf = self._module_node.get_leaf_for_position(self._position, include_prefixes=True)
            previous_leaf = leaf.get_previous_leaf()

            indent = self._position[1]
            if not (leaf.start_pos <= self._position <= leaf.end_pos):
                indent = leaf.start_pos[1]

            if previous_leaf is not None:
                stmt = previous_leaf
                while True:
                    stmt = search_ancestor(
                        stmt, 'if_stmt', 'for_stmt', 'while_stmt', 'try_stmt',
                        'error_node',
                    )
                    if stmt is None:
                        break

                    type_ = stmt.type
                    if type_ == 'error_node':
                        first = stmt.children[0]
                        if isinstance(first, Leaf):
                            type_ = first.value + '_stmt'
                    # Compare indents
                    if stmt.start_pos[1] == indent:
                        if type_ == 'if_stmt':
                            allowed_transitions += ['elif', 'else']
                        elif type_ == 'try_stmt':
                            allowed_transitions += ['except', 'finally', 'else']
                        elif type_ == 'for_stmt':
                            allowed_transitions.append('else')

        completion_names = []
        current_line = self._code_lines[self._position[0] - 1][:self._position[1]]
        if not current_line or current_line[-1] in ' \t.;':
            completion_names += self._get_keyword_completion_names(allowed_transitions)

        if any(t in allowed_transitions for t in (PythonTokenTypes.NAME,
                                                  PythonTokenTypes.INDENT)):
            # This means that we actually have to do type inference.

            nonterminals = [stack_node.nonterminal for stack_node in stack]

            nodes = []
            for stack_node in stack:
                if stack_node.dfa.from_rule == 'small_stmt':
                    nodes = []
                else:
                    nodes += stack_node.nodes

            if nodes and nodes[-1] in ('as', 'def', 'class'):
                # No completions for ``with x as foo`` and ``import x as foo``.
                # Also true for defining names as a class or function.
                return list(self._get_class_context_completions(is_function=True))
            elif "import_stmt" in nonterminals:
                level, names = parse_dotted_names(nodes, "import_from" in nonterminals)

                only_modules = not ("import_from" in nonterminals and 'import' in nodes)
                completion_names += self._get_importer_names(
                    names,
                    level,
                    only_modules=only_modules,
                )
            elif nonterminals[-1] in ('trailer', 'dotted_name') and nodes[-1] == '.':
                dot = self._module_node.get_leaf_for_position(self._position)
                completion_names += self._trailer_completions(dot.get_previous_leaf())
            else:
                completion_names += self._global_completions()
                completion_names += self._get_class_context_completions(is_function=False)

            if 'trailer' in nonterminals:
                call_signatures = self._call_signatures_method()
                completion_names += get_call_signature_param_names(call_signatures)

        return completion_names
 def get_test_node_from_name(self, name):
     node = search_ancestor(name, "with_item")
     if node is None:
         raise ValueError(
             'The name is not actually part of a with statement.')
     return node.children[0]
Example #30
0
    def _complete_python(self, leaf):
        """
        Analyzes the current context of a completion and decides what to
        return.

        Technically this works by generating a parser stack and analysing the
        current stack for possible grammar nodes.

        Possible enhancements:
        - global/nonlocal search global
        - yield from / raise from <- could be only exceptions/generators
        - In args: */**: no completion
        - In params (also lambda): no completion before =
        """

        grammar = self._inference_state.grammar
        self.stack = stack = None
        self._position = (self._original_position[0],
                          self._original_position[1] - len(self._like_name))
        cached_name = None

        try:
            self.stack = stack = helpers.get_stack_at_position(
                grammar, self._code_lines, leaf, self._position)
        except helpers.OnErrorLeaf as e:
            value = e.error_leaf.value
            if value == '.':
                # After ErrorLeaf's that are dots, we will not do any
                # completions since this probably just confuses the user.
                return cached_name, []

            # If we don't have a value, just use global completion.
            return cached_name, self._complete_global_scope()

        allowed_transitions = \
            list(stack._allowed_transition_names_and_token_types())

        if 'if' in allowed_transitions:
            leaf = self._module_node.get_leaf_for_position(
                self._position, include_prefixes=True)
            previous_leaf = leaf.get_previous_leaf()

            indent = self._position[1]
            if not (leaf.start_pos <= self._position <= leaf.end_pos):
                indent = leaf.start_pos[1]

            if previous_leaf is not None:
                stmt = previous_leaf
                while True:
                    stmt = search_ancestor(
                        stmt,
                        'if_stmt',
                        'for_stmt',
                        'while_stmt',
                        'try_stmt',
                        'error_node',
                    )
                    if stmt is None:
                        break

                    type_ = stmt.type
                    if type_ == 'error_node':
                        first = stmt.children[0]
                        if isinstance(first, Leaf):
                            type_ = first.value + '_stmt'
                    # Compare indents
                    if stmt.start_pos[1] == indent:
                        if type_ == 'if_stmt':
                            allowed_transitions += ['elif', 'else']
                        elif type_ == 'try_stmt':
                            allowed_transitions += [
                                'except', 'finally', 'else'
                            ]
                        elif type_ == 'for_stmt':
                            allowed_transitions.append('else')

        completion_names = []
        current_line = self._code_lines[self._position[0] -
                                        1][:self._position[1]]

        kwargs_only = False
        if any(t in allowed_transitions
               for t in (PythonTokenTypes.NAME, PythonTokenTypes.INDENT)):
            # This means that we actually have to do type inference.

            nonterminals = [stack_node.nonterminal for stack_node in stack]

            nodes = _gather_nodes(stack)
            if nodes and nodes[-1] in ('as', 'def', 'class'):
                # No completions for ``with x as foo`` and ``import x as foo``.
                # Also true for defining names as a class or function.
                return cached_name, list(
                    self._complete_inherited(is_function=True))
            elif "import_stmt" in nonterminals:
                level, names = parse_dotted_names(
                    nodes, "import_from" in nonterminals)

                only_modules = not ("import_from" in nonterminals
                                    and 'import' in nodes)
                completion_names += self._get_importer_names(
                    names,
                    level,
                    only_modules=only_modules,
                )
            elif nonterminals[-1] in ('trailer',
                                      'dotted_name') and nodes[-1] == '.':
                dot = self._module_node.get_leaf_for_position(self._position)
                cached_name, n = self._complete_trailer(
                    dot.get_previous_leaf())
                completion_names += n
            elif self._is_parameter_completion():
                completion_names += self._complete_params(leaf)
            else:
                # Apparently this looks like it's good enough to filter most cases
                # so that signature completions don't randomly appear.
                # To understand why this works, three things are important:
                # 1. trailer with a `,` in it is either a subscript or an arglist.
                # 2. If there's no `,`, it's at the start and only signatures start
                #    with `(`. Other trailers could start with `.` or `[`.
                # 3. Decorators are very primitive and have an optional `(` with
                #    optional arglist in them.
                if nodes[-1] in ['(', ','] \
                        and nonterminals[-1] in ('trailer', 'arglist', 'decorator'):
                    signatures = self._signatures_callback(*self._position)
                    if signatures:
                        call_details = signatures[0]._call_details
                        used_kwargs = list(
                            call_details.iter_used_keyword_arguments())
                        positional_count = call_details.count_positional_arguments(
                        )

                        completion_names += _get_signature_param_names(
                            signatures,
                            positional_count,
                            used_kwargs,
                        )

                        kwargs_only = _must_be_kwarg(signatures,
                                                     positional_count,
                                                     used_kwargs)

                if not kwargs_only:
                    completion_names += self._complete_global_scope()
                    completion_names += self._complete_inherited(
                        is_function=False)

        if not kwargs_only:
            completion_names += self._complete_keywords(
                allowed_transitions,
                only_values=not (not current_line or current_line[-1]
                                 in ' \t.;' and current_line[-3:] != '...'))

        return cached_name, completion_names
Example #31
0
 def get_parent_function(self):
     """
     Returns the function/lambda of a parameter.
     """
     return search_ancestor(self, 'funcdef', 'lambdef')
Example #32
0
    def _get_context_completions(self):
        """
        Analyzes the context that a completion is made in and decides what to
        return.

        Technically this works by generating a parser stack and analysing the
        current stack for possible grammar nodes.

        Possible enhancements:
        - global/nonlocal search global
        - yield from / raise from <- could be only exceptions/generators
        - In args: */**: no completion
        - In params (also lambda): no completion before =
        """

        grammar = self._evaluator.grammar

        try:
            self.stack = helpers.get_stack_at_position(
                grammar, self._code_lines, self._module_node, self._position
            )
        except helpers.OnErrorLeaf as e:
            self.stack = None
            if e.error_leaf.value == '.':
                # After ErrorLeaf's that are dots, we will not do any
                # completions since this probably just confuses the user.
                return []
            # If we don't have a context, just use global completion.

            return self._global_completions()

        allowed_keywords, allowed_tokens = \
            helpers.get_possible_completion_types(grammar._pgen_grammar, self.stack)

        if 'if' in allowed_keywords:
            leaf = self._module_node.get_leaf_for_position(self._position, include_prefixes=True)
            previous_leaf = leaf.get_previous_leaf()

            indent = self._position[1]
            if not (leaf.start_pos <= self._position <= leaf.end_pos):
                indent = leaf.start_pos[1]

            if previous_leaf is not None:
                stmt = previous_leaf
                while True:
                    stmt = search_ancestor(
                        stmt, 'if_stmt', 'for_stmt', 'while_stmt', 'try_stmt',
                        'error_node',
                    )
                    if stmt is None:
                        break

                    type_ = stmt.type
                    if type_ == 'error_node':
                        first = stmt.children[0]
                        if isinstance(first, Leaf):
                            type_ = first.value + '_stmt'
                    # Compare indents
                    if stmt.start_pos[1] == indent:
                        if type_ == 'if_stmt':
                            allowed_keywords += ['elif', 'else']
                        elif type_ == 'try_stmt':
                            allowed_keywords += ['except', 'finally', 'else']
                        elif type_ == 'for_stmt':
                            allowed_keywords.append('else')

        completion_names = list(self._get_keyword_completion_names(allowed_keywords))

        if token.NAME in allowed_tokens or token.INDENT in allowed_tokens:
            # This means that we actually have to do type inference.

            symbol_names = list(self.stack.get_node_names(grammar._pgen_grammar))

            nodes = list(self.stack.get_nodes())

            if nodes and nodes[-1] in ('as', 'def', 'class'):
                # No completions for ``with x as foo`` and ``import x as foo``.
                # Also true for defining names as a class or function.
                return list(self._get_class_context_completions(is_function=True))
            elif "import_stmt" in symbol_names:
                level, names = self._parse_dotted_names(nodes, "import_from" in symbol_names)

                only_modules = not ("import_from" in symbol_names and 'import' in nodes)
                completion_names += self._get_importer_names(
                    names,
                    level,
                    only_modules=only_modules,
                )
            elif symbol_names[-1] in ('trailer', 'dotted_name') and nodes[-1] == '.':
                dot = self._module_node.get_leaf_for_position(self._position)
                completion_names += self._trailer_completions(dot.get_previous_leaf())
            else:
                completion_names += self._global_completions()
                completion_names += self._get_class_context_completions(is_function=False)

            if 'trailer' in symbol_names:
                call_signatures = self._call_signatures_method()
                completion_names += get_call_signature_param_names(call_signatures)

        return completion_names
Example #33
0
 def _get_param_node(self):
     return search_ancestor(self.tree_name, 'param')
Example #34
0
 def get_param(self):
     params, _ = self.parent_context.get_executed_params_and_issues()
     param_node = search_ancestor(self.tree_name, 'param')
     return params[param_node.position_index]
Example #35
0
 def is_import(self):
     imp = search_ancestor(self.tree_name, 'import_from', 'import_name')
     return imp is not None