def visit_simple_stmt(self, node: Node) -> Iterator[Line]:
        """Visit a statement without nested statements."""
        prev_type: Optional[int] = None
        for child in node.children:
            if (prev_type is None or prev_type == token.SEMI) and is_arith_like(child):
                wrap_in_parentheses(node, child, visible=False)
            prev_type = child.type

        is_suite_like = node.parent and node.parent.type in STATEMENT
        if is_suite_like:
            if self.mode.is_pyi and is_stub_body(node):
                yield from self.visit_default(node)
            else:
                yield from self.line(+1)
                yield from self.visit_default(node)
                yield from self.line(-1)

        else:
            if (
                not self.mode.is_pyi
                or not node.parent
                or not is_stub_suite(node.parent)
            ):
                yield from self.line()
            yield from self.visit_default(node)
Beispiel #2
0
def remove_await_parens(node: Node) -> None:
    if node.children[0].type == token.AWAIT and len(node.children) > 1:
        if (
            node.children[1].type == syms.atom
            and node.children[1].children[0].type == token.LPAR
        ):
            if maybe_make_parens_invisible_in_atom(
                node.children[1],
                parent=node,
                remove_brackets_around_comma=True,
            ):
                wrap_in_parentheses(node, node.children[1], visible=False)

            # Since await is an expression we shouldn't remove
            # brackets in cases where this would change
            # the AST due to operator precedence.
            # Therefore we only aim to remove brackets around
            # power nodes that aren't also await expressions themselves.
            # https://peps.python.org/pep-0492/#updated-operator-precedence-table
            # N.B. We've still removed any redundant nested brackets though :)
            opening_bracket = cast(Leaf, node.children[1].children[0])
            closing_bracket = cast(Leaf, node.children[1].children[-1])
            bracket_contents = cast(Node, node.children[1].children[1])
            if bracket_contents.type != syms.power:
                ensure_visible(opening_bracket)
                ensure_visible(closing_bracket)
            elif (
                bracket_contents.type == syms.power
                and bracket_contents.children[0].type == token.AWAIT
            ):
                ensure_visible(opening_bracket)
                ensure_visible(closing_bracket)
                # If we are in a nested await then recurse down.
                remove_await_parens(bracket_contents)
Beispiel #3
0
    def visit_funcdef(self, node: Node) -> Iterator[Line]:
        """Visit function definition."""
        if Preview.annotation_parens not in self.mode:
            yield from self.visit_stmt(node, keywords={"def"}, parens=set())
        else:
            yield from self.line()

            # Remove redundant brackets around return type annotation.
            is_return_annotation = False
            for child in node.children:
                if child.type == token.RARROW:
                    is_return_annotation = True
                elif is_return_annotation:
                    if child.type == syms.atom and child.children[0].type == token.LPAR:
                        if maybe_make_parens_invisible_in_atom(
                            child,
                            parent=node,
                            remove_brackets_around_comma=False,
                        ):
                            wrap_in_parentheses(node, child, visible=False)
                    else:
                        wrap_in_parentheses(node, child, visible=False)
                    is_return_annotation = False

            for child in node.children:
                yield from self.visit(child)
Beispiel #4
0
def normalize_invisible_parens(node: Node, parens_after: Set[str]) -> None:
    """Make existing optional parentheses invisible or create new ones.

    `parens_after` is a set of string leaf values immediately after which parens
    should be put.

    Standardizes on visible parentheses for single-element tuples, and keeps
    existing visible parentheses for other tuples and generator expressions.
    """
    for pc in list_comments(node.prefix, is_endmarker=False):
        if pc.value in FMT_OFF:
            # This `node` has a prefix with `# fmt: off`, don't mess with parens.
            return
    check_lpar = False
    for index, child in enumerate(list(node.children)):
        # Fixes a bug where invisible parens are not properly stripped from
        # assignment statements that contain type annotations.
        if isinstance(child, Node) and child.type == syms.annassign:
            normalize_invisible_parens(child, parens_after=parens_after)

        # Add parentheses around long tuple unpacking in assignments.
        if (index == 0 and isinstance(child, Node)
                and child.type == syms.testlist_star_expr):
            check_lpar = True

        if check_lpar:
            if child.type == syms.atom:
                if maybe_make_parens_invisible_in_atom(child, parent=node):
                    wrap_in_parentheses(node, child, visible=False)
            elif is_one_tuple(child):
                # wrap_in_parentheses(node, child, visible=True)
                pass
            elif node.type == syms.import_from:
                # "import from" nodes store parentheses directly as part of
                # the statement
                if child.type == token.LPAR:
                    # make parentheses invisible
                    child.value = ""  # type: ignore
                    node.children[-1].value = ""  # type: ignore
                elif child.type != token.STAR:
                    # insert invisible parentheses
                    node.insert_child(index, Leaf(token.LPAR, ""))
                    node.append_child(Leaf(token.RPAR, ""))
                break

            elif not (isinstance(child, Leaf) and is_multiline_string(child)):
                wrap_in_parentheses(node, child, visible=False)

        check_lpar = isinstance(child, Leaf) and child.value in parens_after
Beispiel #5
0
    def visit_simple_stmt(self, node: Node) -> Iterator[Line]:
        """Visit a statement without nested statements."""
        if first_child_is_arith(node):
            wrap_in_parentheses(node, node.children[0], visible=False)
        is_suite_like = node.parent and node.parent.type in STATEMENT
        if is_suite_like:
            if self.mode.is_pyi and is_stub_body(node):
                yield from self.visit_default(node)
            else:
                yield from self.line(+1)
                yield from self.visit_default(node)
                yield from self.line(-1)

        else:
            if (not self.mode.is_pyi or not node.parent
                    or not is_stub_suite(node.parent)):
                yield from self.line()
            yield from self.visit_default(node)
Beispiel #6
0
    def visit_power(self, node: Node) -> Iterator[Line]:
        for idx, leaf in enumerate(node.children[:-1]):
            next_leaf = node.children[idx + 1]

            if not isinstance(leaf, Leaf):
                continue

            value = leaf.value.lower()
            if (leaf.type == token.NUMBER and next_leaf.type == syms.trailer
                    # Ensure that we are in an attribute trailer
                    and next_leaf.children[0].type == token.DOT
                    # It shouldn't wrap hexadecimal, binary and octal literals
                    and not value.startswith(("0x", "0b", "0o"))
                    # It shouldn't wrap complex literals
                    and "j" not in value):
                wrap_in_parentheses(node, leaf)

        yield from self.visit_default(node)
Beispiel #7
0
def remove_with_parens(node: Node, parent: Node) -> None:
    """Recursively hide optional parens in `with` statements."""
    # Removing all unnecessary parentheses in with statements in one pass is a tad
    # complex as different variations of bracketed statements result in pretty
    # different parse trees:
    #
    # with (open("file")) as f:                       # this is an asexpr_test
    #     ...
    #
    # with (open("file") as f):                       # this is an atom containing an
    #     ...                                         # asexpr_test
    #
    # with (open("file")) as f, (open("file")) as f:  # this is asexpr_test, COMMA,
    #     ...                                         # asexpr_test
    #
    # with (open("file") as f, open("file") as f):    # an atom containing a
    #     ...                                         # testlist_gexp which then
    #                                                 # contains multiple asexpr_test(s)
    if node.type == syms.atom:
        if maybe_make_parens_invisible_in_atom(
            node,
            parent=parent,
            remove_brackets_around_comma=True,
        ):
            wrap_in_parentheses(parent, node, visible=False)
        if isinstance(node.children[1], Node):
            remove_with_parens(node.children[1], node)
    elif node.type == syms.testlist_gexp:
        for child in node.children:
            if isinstance(child, Node):
                remove_with_parens(child, node)
    elif node.type == syms.asexpr_test and not any(
        leaf.type == token.COLONEQUAL for leaf in node.leaves()
    ):
        if maybe_make_parens_invisible_in_atom(
            node.children[0],
            parent=node,
            remove_brackets_around_comma=True,
        ):
            wrap_in_parentheses(node, node.children[0], visible=False)
Beispiel #8
0
def normalize_invisible_parens(
    node: Node, parens_after: Set[str], *, preview: bool
) -> None:
    """Make existing optional parentheses invisible or create new ones.

    `parens_after` is a set of string leaf values immediately after which parens
    should be put.

    Standardizes on visible parentheses for single-element tuples, and keeps
    existing visible parentheses for other tuples and generator expressions.
    """
    for pc in list_comments(node.prefix, is_endmarker=False, preview=preview):
        if pc.value in FMT_OFF:
            # This `node` has a prefix with `# fmt: off`, don't mess with parens.
            return
    check_lpar = False
    for index, child in enumerate(list(node.children)):
        # Fixes a bug where invisible parens are not properly stripped from
        # assignment statements that contain type annotations.
        if isinstance(child, Node) and child.type == syms.annassign:
            normalize_invisible_parens(
                child, parens_after=parens_after, preview=preview
            )

        # Add parentheses around long tuple unpacking in assignments.
        if (
            index == 0
            and isinstance(child, Node)
            and child.type == syms.testlist_star_expr
        ):
            check_lpar = True

        if check_lpar:
            if (
                preview
                and child.type == syms.atom
                and node.type == syms.for_stmt
                and isinstance(child.prev_sibling, Leaf)
                and child.prev_sibling.type == token.NAME
                and child.prev_sibling.value == "for"
            ):
                if maybe_make_parens_invisible_in_atom(
                    child,
                    parent=node,
                    remove_brackets_around_comma=True,
                ):
                    wrap_in_parentheses(node, child, visible=False)
            elif preview and isinstance(child, Node) and node.type == syms.with_stmt:
                remove_with_parens(child, node)
            elif child.type == syms.atom:
                if maybe_make_parens_invisible_in_atom(
                    child,
                    parent=node,
                ):
                    wrap_in_parentheses(node, child, visible=False)
            elif is_one_tuple(child):
                wrap_in_parentheses(node, child, visible=True)
            elif node.type == syms.import_from:
                # "import from" nodes store parentheses directly as part of
                # the statement
                if is_lpar_token(child):
                    assert is_rpar_token(node.children[-1])
                    # make parentheses invisible
                    child.value = ""
                    node.children[-1].value = ""
                elif child.type != token.STAR:
                    # insert invisible parentheses
                    node.insert_child(index, Leaf(token.LPAR, ""))
                    node.append_child(Leaf(token.RPAR, ""))
                break
            elif (
                index == 1
                and child.type == token.STAR
                and node.type == syms.except_clause
            ):
                # In except* (PEP 654), the star is actually part of
                # of the keyword. So we need to skip the insertion of
                # invisible parentheses to work more precisely.
                continue

            elif not (isinstance(child, Leaf) and is_multiline_string(child)):
                wrap_in_parentheses(node, child, visible=False)

        comma_check = child.type == token.COMMA if preview else False

        check_lpar = isinstance(child, Leaf) and (
            child.value in parens_after or comma_check
        )