diff --git a/CHANGES.md b/CHANGES.md index 303d6849bcb..40c8f07e81f 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -30,6 +30,8 @@ - Standardize type comments to form `# type: ` (#4645) - Fix `fix_fmt_skip_in_one_liners` preview feature to respect `# fmt: skip` for compound statements with semicolon-separated bodies (#4800) +- Fix `fix_fmt_skip_in_one_liners` crashing on `with` statements and annotated + parameters (#4822) ### Configuration diff --git a/src/black/comments.py b/src/black/comments.py index 9a89c85e779..52477c3f65f 100644 --- a/src/black/comments.py +++ b/src/black/comments.py @@ -587,6 +587,7 @@ def _generate_ignored_nodes_from_fmt_skip( comments = list_comments(leaf.prefix, is_endmarker=False, mode=mode) if not comments or comment.value != comments[0].value: return + if prev_sibling is not None: leaf.prefix = leaf.prefix[comment.consumed :] @@ -632,10 +633,22 @@ def _generate_ignored_nodes_from_fmt_skip( current_node.prefix = "" break + # Special case for with expressions + # Without this, we can stuck inside the asexpr_test's children's children + parent = current_node.parent + if ( + parent + and parent.type == syms.asexpr_test + and parent.parent + and parent.parent.type == syms.with_stmt + ): + current_node = parent + ignored_nodes.insert(0, current_node) - if current_node.prev_sibling is None and current_node.parent is not None: - current_node = current_node.parent + if current_node.prev_sibling is None and parent is not None: + current_node = parent + # Special handling for compound statements with semicolon-separated bodies if Preview.fix_fmt_skip_in_one_liners in mode and isinstance(parent, Node): body_node = _find_compound_statement_context(parent) diff --git a/src/black/linegen.py b/src/black/linegen.py index c2fd5f6858e..b9cd8a27675 100644 --- a/src/black/linegen.py +++ b/src/black/linegen.py @@ -440,8 +440,7 @@ def foo(a: int, b: float = 7): ... def foo(a: (int), b: (float) = 7): ... """ - assert len(node.children) == 3 - if maybe_make_parens_invisible_in_atom( + if len(node.children) == 3 and maybe_make_parens_invisible_in_atom( node.children[2], parent=node, mode=self.mode, features=self.features ): wrap_in_parentheses(node, node.children[2], visible=False) diff --git a/tests/data/cases/fmtskip10.py b/tests/data/cases/fmtskip10.py index ed7ffacd07c..98bb843c63f 100644 --- a/tests/data/cases/fmtskip10.py +++ b/tests/data/cases/fmtskip10.py @@ -5,6 +5,7 @@ def foo(): return "mock" # fmt: skip if True: print("this"); print("that") # fmt: skip while True: print("loop"); break # fmt: skip for x in [1, 2]: print(x); print("done") # fmt: skip +def f(x: int): return x # fmt: skip j = 1 # fmt: skip while j < 10: j += 1 # fmt: skip diff --git a/tests/data/cases/fmtskip12.py b/tests/data/cases/fmtskip12.py new file mode 100644 index 00000000000..3af6b4443a1 --- /dev/null +++ b/tests/data/cases/fmtskip12.py @@ -0,0 +1,21 @@ +# flags: --preview + +with open("file.txt") as f: content = f.read() # fmt: skip + +# Ideally, only the last line would be ignored +# But ignoring only part of the asexpr_test causes a parse error +# Same with ignoring the asexpr_test without also ignoring the entire with_stmt +with open ( + "file.txt" , +) as f: content = f.read() # fmt: skip + +# output + +with open("file.txt") as f: content = f.read() # fmt: skip + +# Ideally, only the last line would be ignored +# But ignoring only part of the asexpr_test causes a parse error +# Same with ignoring the asexpr_test without also ignoring the entire with_stmt +with open ( + "file.txt" , +) as f: content = f.read() # fmt: skip