From ca2f3ddadc97f871bc3d69eb023afb4be361e629 Mon Sep 17 00:00:00 2001 From: HoraDomu Date: Tue, 21 Apr 2026 17:17:05 -0500 Subject: [PATCH] Fix comment before binary op causing unnecessary line split (#3713) A standalone comment before an arithmetic/comparator expression was inside brackets and caused delimiter_split to use the operator as a split point. It was breaking the expression across lines. The fix was when the first leaf is a standalone comment and the delimter priority is below LOGIC_PRIORITY, raise CannotSplit to defer to standalone_comment_split. This peels off the comment first, and then lets the expression stay on one line if it fits. --- src/black/linegen.py | 16 ++++++++ tests/data/cases/comment_before_binary_op.py | 42 ++++++++++++++++++++ tests/data/cases/function_trailing_comma.py | 4 +- 3 files changed, 59 insertions(+), 3 deletions(-) create mode 100644 tests/data/cases/comment_before_binary_op.py diff --git a/src/black/linegen.py b/src/black/linegen.py index 72f7366489d..1abf0c76e6d 100644 --- a/src/black/linegen.py +++ b/src/black/linegen.py @@ -13,6 +13,7 @@ from black.brackets import ( COMMA_PRIORITY, DOT_PRIORITY, + LOGIC_PRIORITY, STRING_PRIORITY, get_leaves_inside_matching_brackets, max_delimiter_priority_in_atom, @@ -1384,6 +1385,21 @@ def delimiter_split( ): raise CannotSplit("Splitting a single attribute from its owner looks wrong") + # When a standalone comment is the first token on the line and precedes an + # arithmetic/comparator expression, let standalone_comment_split handle it so + # the expression can stay on one line if it fits (issue #3713). Only applies + # when the comment leads the line (not trailing) and the delimiter has lower + # priority than logical operators to avoid changing `or`/`and` behavior. + if ( + line.leaves + and line.leaves[0].type == STANDALONE_COMMENT + and delimiter_priority < LOGIC_PRIORITY + ): + raise CannotSplit( + "Standalone comment leads line before arithmetic delimiter; deferring to" + " standalone_comment_split" + ) + current_line = Line( mode=line.mode, depth=line.depth, inside_brackets=line.inside_brackets ) diff --git a/tests/data/cases/comment_before_binary_op.py b/tests/data/cases/comment_before_binary_op.py new file mode 100644 index 00000000000..b61b1888618 --- /dev/null +++ b/tests/data/cases/comment_before_binary_op.py @@ -0,0 +1,42 @@ +my_func( + arg1=1, + arg2=[ + func_call( + # Comment. + [MyClass(arg1, arg2)] * 10000 + ), + ], +) + +result = func_call( + # Comment. + value * 10000 +) + +result = func_call( + # Comment. + value + other_value +) + + +# output + +my_func( + arg1=1, + arg2=[ + func_call( + # Comment. + [MyClass(arg1, arg2)] * 10000 + ), + ], +) + +result = func_call( + # Comment. + value * 10000 +) + +result = func_call( + # Comment. + value + other_value +) diff --git a/tests/data/cases/function_trailing_comma.py b/tests/data/cases/function_trailing_comma.py index 63cf3999c2e..c4b0f3f4fb8 100644 --- a/tests/data/cases/function_trailing_comma.py +++ b/tests/data/cases/function_trailing_comma.py @@ -252,9 +252,7 @@ def foo() -> ( def foo() -> ( # comment inside parenthesised new union return type - int - | str - | bytes + int | str | bytes ): ...