def maybe_make_parens_invisible_in_atom( node: LN, parent: LN, remove_brackets_around_comma: bool = False, ) -> bool: """If it's safe, make the parens in the atom `node` invisible, recursively. Additionally, remove repeated, adjacent invisible parens from the atom `node` as they are redundant. Returns whether the node should itself be wrapped in invisible parentheses. """ if ( node.type != syms.atom or is_empty_tuple(node) or is_one_tuple(node) or (is_yield(node) and parent.type != syms.expr_stmt) or ( # This condition tries to prevent removing non-optional brackets # around a tuple, however, can be a bit overzealous so we provide # and option to skip this check for `for` and `with` statements. not remove_brackets_around_comma and max_delimiter_priority_in_atom(node) >= COMMA_PRIORITY ) ): return False if is_walrus_assignment(node): if parent.type in [ syms.annassign, syms.expr_stmt, syms.assert_stmt, syms.return_stmt, # these ones aren't useful to end users, but they do please fuzzers syms.for_stmt, syms.del_stmt, ]: return False first = node.children[0] last = node.children[-1] if is_lpar_token(first) and is_rpar_token(last): middle = node.children[1] # make parentheses invisible first.value = "" last.value = "" maybe_make_parens_invisible_in_atom( middle, parent=parent, remove_brackets_around_comma=remove_brackets_around_comma, ) if is_atom_with_invisible_parens(middle): # Strip the invisible parens from `middle` by replacing # it with the child in-between the invisible parens middle.replace(middle.children[1]) return False return True
def maybe_make_parens_invisible_in_atom( node: LN, parent: LN, preview: bool = False, ) -> bool: """If it's safe, make the parens in the atom `node` invisible, recursively. Additionally, remove repeated, adjacent invisible parens from the atom `node` as they are redundant. Returns whether the node should itself be wrapped in invisible parentheses. """ if (preview and parent.type == syms.for_stmt and isinstance(node.prev_sibling, Leaf) and node.prev_sibling.type == token.NAME and node.prev_sibling.value == "for"): for_stmt_check = False else: for_stmt_check = True if (node.type != syms.atom or is_empty_tuple(node) or is_one_tuple(node) or (is_yield(node) and parent.type != syms.expr_stmt) or (max_delimiter_priority_in_atom(node) >= COMMA_PRIORITY and for_stmt_check)): return False if is_walrus_assignment(node): if parent.type in [ syms.annassign, syms.expr_stmt, syms.assert_stmt, syms.return_stmt, # these ones aren't useful to end users, but they do please fuzzers syms.for_stmt, syms.del_stmt, ]: return False first = node.children[0] last = node.children[-1] if is_lpar_token(first) and is_rpar_token(last): middle = node.children[1] # make parentheses invisible first.value = "" last.value = "" maybe_make_parens_invisible_in_atom(middle, parent=parent, preview=preview) if is_atom_with_invisible_parens(middle): # Strip the invisible parens from `middle` by replacing # it with the child in-between the invisible parens middle.replace(middle.children[1]) return False return True
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 child.type == syms.atom: if maybe_make_parens_invisible_in_atom( child, parent=node, preview=preview, ): 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 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