def convert_one_fmt_off_pair(node: Node) -> bool: """Convert content of a single `# fmt: off`/`# fmt: on` into a standalone comment. Returns True if a pair was converted. """ for leaf in node.leaves(): previous_consumed = 0 for comment in list_comments(leaf.prefix, is_endmarker=False): if comment.value not in FMT_PASS: previous_consumed = comment.consumed continue # We only want standalone comments. If there's no previous leaf or # the previous leaf is indentation, it's a standalone comment in # disguise. if comment.value in FMT_PASS and comment.type != STANDALONE_COMMENT: prev = preceding_leaf(leaf) if prev: if comment.value in FMT_OFF and prev.type not in WHITESPACE: continue if comment.value in FMT_SKIP and prev.type in WHITESPACE: continue ignored_nodes = list(generate_ignored_nodes(leaf, comment)) if not ignored_nodes: continue first = ignored_nodes[ 0] # Can be a container node with the `leaf`. parent = first.parent prefix = first.prefix first.prefix = prefix[comment.consumed:] hidden_value = "".join(str(n) for n in ignored_nodes) if comment.value in FMT_OFF: hidden_value = comment.value + "\n" + hidden_value if comment.value in FMT_SKIP: hidden_value += " " + comment.value if hidden_value.endswith("\n"): # That happens when one of the `ignored_nodes` ended with a NEWLINE # leaf (possibly followed by a DEDENT). hidden_value = hidden_value[:-1] first_idx: Optional[int] = None for ignored in ignored_nodes: index = ignored.remove() if first_idx is None: first_idx = index assert parent is not None, "INTERNAL ERROR: fmt: on/off handling (1)" assert first_idx is not None, "INTERNAL ERROR: fmt: on/off handling (2)" parent.insert_child( first_idx, Leaf( STANDALONE_COMMENT, hidden_value, prefix=prefix[:previous_consumed] + "\n" * comment.newlines, ), ) return True return False
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)