diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/allow_empty_first_line.py b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/allow_empty_first_line.py new file mode 100644 index 0000000000000..44b54ba9d7aa5 --- /dev/null +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/allow_empty_first_line.py @@ -0,0 +1,82 @@ +def foo(): + """ + Docstring + """ + + # Here we go + if x: + + # This is also now fine + a = 123 + + else: + # But not necessary + a = 123 + + if y: + + while True: + + """ + Long comment here + """ + a = 123 + + if z: + + for _ in range(100): + a = 123 + else: + + try: + + # this should be ok + a = 123 + except: + + """also this""" + a = 123 + + +def bar(): + + if x: + a = 123 + + +def baz(): + + # OK + if x: + a = 123 + +def quux(): + + new_line = here + + +class Cls: + + def method(self): + + pass + + +async def async_fn(): + + """Docstring.""" + + +@decorated +async def async_fn(): + + """Docstring.""" + + +def top_level( + a: int, + b: str, +) -> Whatever[Generic, Something]: + + def nested(x: int) -> int: + pass diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/allow_empty_first_line.py.expect b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/allow_empty_first_line.py.expect new file mode 100644 index 0000000000000..3e5bdea0f93e5 --- /dev/null +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/allow_empty_first_line.py.expect @@ -0,0 +1,81 @@ +def foo(): + """ + Docstring + """ + + # Here we go + if x: + + # This is also now fine + a = 123 + + else: + # But not necessary + a = 123 + + if y: + + while True: + + """ + Long comment here + """ + a = 123 + + if z: + + for _ in range(100): + a = 123 + else: + + try: + + # this should be ok + a = 123 + except: + + """also this""" + a = 123 + + +def bar(): + + if x: + a = 123 + + +def baz(): + + # OK + if x: + a = 123 + + +def quux(): + + new_line = here + + +class Cls: + + def method(self): + + pass + + +async def async_fn(): + """Docstring.""" + + +@decorated +async def async_fn(): + """Docstring.""" + + +def top_level( + a: int, + b: str, +) -> Whatever[Generic, Something]: + + def nested(x: int) -> int: + pass diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/async_stmts.py b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/async_stmts.py new file mode 100644 index 0000000000000..88767abb88cc6 --- /dev/null +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/async_stmts.py @@ -0,0 +1,11 @@ +async def func() -> (int): + return 0 + + +@decorated +async def func() -> (int): + return 0 + + +async for (item) in async_iter: + pass diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/async_stmts.py.expect b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/async_stmts.py.expect new file mode 100644 index 0000000000000..13423345b6386 --- /dev/null +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/async_stmts.py.expect @@ -0,0 +1,11 @@ +async def func() -> int: + return 0 + + +@decorated +async def func() -> int: + return 0 + + +async for item in async_iter: + pass diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/comments5.py b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/comments5.py index bda40619f62ce..4270d3a09a2f0 100644 --- a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/comments5.py +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/comments5.py @@ -45,8 +45,7 @@ def wat(): @deco2(with_args=True) # leading 3 @deco3 -def decorated1(): - ... +def decorated1(): ... # leading 1 @@ -54,8 +53,7 @@ def decorated1(): # leading 2 @deco2(with_args=True) # leading function comment -def decorated1(): - ... +def decorated1(): ... # Note: this is fixed in @@ -65,8 +63,7 @@ def decorated1(): # This comment should be split from `some_instruction` by two lines but isn't. -def g(): - ... +def g(): ... if __name__ == "__main__": diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/comments5.py.expect b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/comments5.py.expect index bda40619f62ce..4270d3a09a2f0 100644 --- a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/comments5.py.expect +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/comments5.py.expect @@ -45,8 +45,7 @@ def wat(): @deco2(with_args=True) # leading 3 @deco3 -def decorated1(): - ... +def decorated1(): ... # leading 1 @@ -54,8 +53,7 @@ def decorated1(): # leading 2 @deco2(with_args=True) # leading function comment -def decorated1(): - ... +def decorated1(): ... # Note: this is fixed in @@ -65,8 +63,7 @@ some_instruction # This comment should be split from `some_instruction` by two lines but isn't. -def g(): - ... +def g(): ... if __name__ == "__main__": diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/comments_in_double_parens.py b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/comments_in_double_parens.py new file mode 100644 index 0000000000000..df6103b067d4d --- /dev/null +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/comments_in_double_parens.py @@ -0,0 +1,56 @@ +if ( + True + # sdf +): + print("hw") + +if (( + True + # sdf +)): + print("hw") + +if (( + # type: ignore + True +)): + print("hw") + +if (( + True + # type: ignore +)): + print("hw") + +if ( + # a long comment about + # the condition below + (a or b) +): + pass + +def return_true(): + return ( + ( + True # this comment gets removed accidentally + ) + ) + +def return_true(): + return (True) # this comment gets removed accidentally + + +if ( + # huh comment + (True) +): + ... + +if ( + # huh + ( + # comment + True + ) +): + ... diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/comments_in_double_parens.py.expect b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/comments_in_double_parens.py.expect new file mode 100644 index 0000000000000..61ef33cbe9b92 --- /dev/null +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/comments_in_double_parens.py.expect @@ -0,0 +1,53 @@ +if ( + True + # sdf +): + print("hw") + +if ( + True + # sdf +): + print("hw") + +if ( + # type: ignore + True +): + print("hw") + +if ( + True + # type: ignore +): + print("hw") + +if ( + # a long comment about + # the condition below + a + or b +): + pass + + +def return_true(): + return True # this comment gets removed accidentally + + +def return_true(): + return True # this comment gets removed accidentally + + +if ( + # huh comment + True +): + ... + +if ( + # huh + # comment + True +): + ... diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/conditional_expression.py.expect b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/conditional_expression.py.expect index 0d80042ca145b..f0b2141d9a159 100644 --- a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/conditional_expression.py.expect +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/conditional_expression.py.expect @@ -104,7 +104,9 @@ def foo(wait: bool = True): time.sleep(1) if wait else None -a = "".join(( - "", # comment - "" if True else "", -)) +a = "".join( + ( + "", # comment + "" if True else "", + ) +) diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/context_managers_38.options.json b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/context_managers_38.options.json new file mode 100644 index 0000000000000..56ae457e6016c --- /dev/null +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/context_managers_38.options.json @@ -0,0 +1 @@ +{"target_version": "py38"} \ No newline at end of file diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/context_managers_38.py b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/context_managers_38.py new file mode 100644 index 0000000000000..811be47176279 --- /dev/null +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/context_managers_38.py @@ -0,0 +1,31 @@ +with \ + make_context_manager1() as cm1, \ + make_context_manager2() as cm2, \ + make_context_manager3() as cm3, \ + make_context_manager4() as cm4 \ +: + pass + + +with \ + make_context_manager1() as cm1, \ + make_context_manager2(), \ + make_context_manager3() as cm3, \ + make_context_manager4() \ +: + pass + + +with \ + new_new_new1() as cm1, \ + new_new_new2() \ +: + pass + + +with mock.patch.object( + self.my_runner, "first_method", autospec=True +) as mock_run_adb, mock.patch.object( + self.my_runner, "second_method", autospec=True, return_value="foo" +): + pass diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/context_managers_38.py.expect b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/context_managers_38.py.expect new file mode 100644 index 0000000000000..5c0cdde0d29a5 --- /dev/null +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/context_managers_38.py.expect @@ -0,0 +1,18 @@ +with make_context_manager1() as cm1, make_context_manager2() as cm2, make_context_manager3() as cm3, make_context_manager4() as cm4: + pass + + +with make_context_manager1() as cm1, make_context_manager2(), make_context_manager3() as cm3, make_context_manager4(): + pass + + +with new_new_new1() as cm1, new_new_new2(): + pass + + +with mock.patch.object( + self.my_runner, "first_method", autospec=True +) as mock_run_adb, mock.patch.object( + self.my_runner, "second_method", autospec=True, return_value="foo" +): + pass diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/context_managers_39.options.json b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/context_managers_39.options.json new file mode 100644 index 0000000000000..cd3adca74c976 --- /dev/null +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/context_managers_39.options.json @@ -0,0 +1 @@ +{"target_version": "py39"} \ No newline at end of file diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/context_managers_39.py b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/context_managers_39.py new file mode 100644 index 0000000000000..f0ae0053630a2 --- /dev/null +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/context_managers_39.py @@ -0,0 +1,84 @@ +with \ + make_context_manager1() as cm1, \ + make_context_manager2() as cm2, \ + make_context_manager3() as cm3, \ + make_context_manager4() as cm4 \ +: + pass + + +# Leading comment +with \ + make_context_manager1() as cm1, \ + make_context_manager2(), \ + make_context_manager3() as cm3, \ + make_context_manager4() \ +: + pass + + +with \ + new_new_new1() as cm1, \ + new_new_new2() \ +: + pass + + +with ( + new_new_new1() as cm1, + new_new_new2() +): + pass + + +# Leading comment. +with ( + # First comment. + new_new_new1() as cm1, + # Second comment. + new_new_new2() + # Last comment. +): + pass + + +with \ + this_is_a_very_long_call(looong_arg1=looong_value1, looong_arg2=looong_value2) as cm1, \ + this_is_a_very_long_call(looong_arg1=looong_value1, looong_arg2=looong_value2, looong_arg3=looong_value3, looong_arg4=looong_value4) as cm2 \ +: + pass + + +with mock.patch.object( + self.my_runner, "first_method", autospec=True +) as mock_run_adb, mock.patch.object( + self.my_runner, "second_method", autospec=True, return_value="foo" +): + pass + + +with xxxxxxxx.some_kind_of_method( + some_argument=[ + "first", + "second", + "third", + ] +).another_method() as cmd: + pass + + +async def func(): + async with \ + make_context_manager1() as cm1, \ + make_context_manager2() as cm2, \ + make_context_manager3() as cm3, \ + make_context_manager4() as cm4 \ + : + pass + + async with some_function( + argument1, argument2, argument3="some_value" + ) as some_cm, some_other_function( + argument1, argument2, argument3="some_value" + ): + pass diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/context_managers_39.py.expect b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/context_managers_39.py.expect new file mode 100644 index 0000000000000..1b8e86593390b --- /dev/null +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/context_managers_39.py.expect @@ -0,0 +1,85 @@ +with ( + make_context_manager1() as cm1, + make_context_manager2() as cm2, + make_context_manager3() as cm3, + make_context_manager4() as cm4, +): + pass + + +# Leading comment +with ( + make_context_manager1() as cm1, + make_context_manager2(), + make_context_manager3() as cm3, + make_context_manager4(), +): + pass + + +with new_new_new1() as cm1, new_new_new2(): + pass + + +with new_new_new1() as cm1, new_new_new2(): + pass + + +# Leading comment. +with ( + # First comment. + new_new_new1() as cm1, + # Second comment. + new_new_new2(), + # Last comment. +): + pass + + +with ( + this_is_a_very_long_call( + looong_arg1=looong_value1, looong_arg2=looong_value2 + ) as cm1, + this_is_a_very_long_call( + looong_arg1=looong_value1, + looong_arg2=looong_value2, + looong_arg3=looong_value3, + looong_arg4=looong_value4, + ) as cm2, +): + pass + + +with ( + mock.patch.object(self.my_runner, "first_method", autospec=True) as mock_run_adb, + mock.patch.object( + self.my_runner, "second_method", autospec=True, return_value="foo" + ), +): + pass + + +with xxxxxxxx.some_kind_of_method( + some_argument=[ + "first", + "second", + "third", + ] +).another_method() as cmd: + pass + + +async def func(): + async with ( + make_context_manager1() as cm1, + make_context_manager2() as cm2, + make_context_manager3() as cm3, + make_context_manager4() as cm4, + ): + pass + + async with ( + some_function(argument1, argument2, argument3="some_value") as some_cm, + some_other_function(argument1, argument2, argument3="some_value"), + ): + pass diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/context_managers_autodetect_310.options.json b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/context_managers_autodetect_310.options.json new file mode 100644 index 0000000000000..60044b9854d9b --- /dev/null +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/context_managers_autodetect_310.options.json @@ -0,0 +1 @@ +{"target_version": "py310"} \ No newline at end of file diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/context_managers_autodetect_310.py b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/context_managers_autodetect_310.py new file mode 100644 index 0000000000000..493ad2110867c --- /dev/null +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/context_managers_autodetect_310.py @@ -0,0 +1,15 @@ +# This file uses pattern matching introduced in Python 3.10. + + +match http_code: + case 404: + print("Not found") + + +with \ + make_context_manager1() as cm1, \ + make_context_manager2() as cm2, \ + make_context_manager3() as cm3, \ + make_context_manager4() as cm4 \ +: + pass diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/context_managers_autodetect_310.py.expect b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/context_managers_autodetect_310.py.expect new file mode 100644 index 0000000000000..4c35978000410 --- /dev/null +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/context_managers_autodetect_310.py.expect @@ -0,0 +1,15 @@ +# This file uses pattern matching introduced in Python 3.10. + + +match http_code: + case 404: + print("Not found") + + +with ( + make_context_manager1() as cm1, + make_context_manager2() as cm2, + make_context_manager3() as cm3, + make_context_manager4() as cm4, +): + pass diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/context_managers_autodetect_311.options.json b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/context_managers_autodetect_311.options.json new file mode 100644 index 0000000000000..c723d00e423e8 --- /dev/null +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/context_managers_autodetect_311.options.json @@ -0,0 +1 @@ +{"target_version": "py311"} \ No newline at end of file diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/context_managers_autodetect_311.py b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/context_managers_autodetect_311.py new file mode 100644 index 0000000000000..fb3fb65a31129 --- /dev/null +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/context_managers_autodetect_311.py @@ -0,0 +1,16 @@ +# This file uses except* clause in Python 3.11. + + +try: + some_call() +except* Error as e: + pass + + +with \ + make_context_manager1() as cm1, \ + make_context_manager2() as cm2, \ + make_context_manager3() as cm3, \ + make_context_manager4() as cm4 \ +: + pass diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/context_managers_autodetect_311.py.expect b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/context_managers_autodetect_311.py.expect new file mode 100644 index 0000000000000..b5ca9c0b1473e --- /dev/null +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/context_managers_autodetect_311.py.expect @@ -0,0 +1,16 @@ +# This file uses except* clause in Python 3.11. + + +try: + some_call() +except* Error as e: + pass + + +with ( + make_context_manager1() as cm1, + make_context_manager2() as cm2, + make_context_manager3() as cm3, + make_context_manager4() as cm4, +): + pass diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/context_managers_autodetect_38.py b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/context_managers_autodetect_38.py new file mode 100644 index 0000000000000..f48fbe78e3709 --- /dev/null +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/context_managers_autodetect_38.py @@ -0,0 +1,24 @@ +# This file doesn't use any Python 3.9+ only grammars. + + +# Make sure parens around a single context manager don't get autodetected as +# Python 3.9+. +with (a): + pass + + +with \ + make_context_manager1() as cm1, \ + make_context_manager2() as cm2, \ + make_context_manager3() as cm3, \ + make_context_manager4() as cm4 \ +: + pass + + +with mock.patch.object( + self.my_runner, "first_method", autospec=True +) as mock_run_adb, mock.patch.object( + self.my_runner, "second_method", autospec=True, return_value="foo" +): + pass diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/context_managers_autodetect_38.py.expect b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/context_managers_autodetect_38.py.expect new file mode 100644 index 0000000000000..d43eb9f0917ca --- /dev/null +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/context_managers_autodetect_38.py.expect @@ -0,0 +1,19 @@ +# This file doesn't use any Python 3.9+ only grammars. + + +# Make sure parens around a single context manager don't get autodetected as +# Python 3.9+. +with a: + pass + + +with make_context_manager1() as cm1, make_context_manager2() as cm2, make_context_manager3() as cm3, make_context_manager4() as cm4: + pass + + +with mock.patch.object( + self.my_runner, "first_method", autospec=True +) as mock_run_adb, mock.patch.object( + self.my_runner, "second_method", autospec=True, return_value="foo" +): + pass diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/context_managers_autodetect_39.options.json b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/context_managers_autodetect_39.options.json new file mode 100644 index 0000000000000..cd3adca74c976 --- /dev/null +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/context_managers_autodetect_39.options.json @@ -0,0 +1 @@ +{"target_version": "py39"} \ No newline at end of file diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/context_managers_autodetect_39.py b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/context_managers_autodetect_39.py new file mode 100644 index 0000000000000..c69d3a72894b7 --- /dev/null +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/context_managers_autodetect_39.py @@ -0,0 +1,17 @@ +# This file uses parenthesized context managers introduced in Python 3.9. + + +with \ + make_context_manager1() as cm1, \ + make_context_manager2() as cm2, \ + make_context_manager3() as cm3, \ + make_context_manager4() as cm4 \ +: + pass + + +with ( + new_new_new1() as cm1, + new_new_new2() +): + pass diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/context_managers_autodetect_39.py.expect b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/context_managers_autodetect_39.py.expect new file mode 100644 index 0000000000000..ff6ce999175b2 --- /dev/null +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/context_managers_autodetect_39.py.expect @@ -0,0 +1,14 @@ +# This file uses parenthesized context managers introduced in Python 3.9. + + +with ( + make_context_manager1() as cm1, + make_context_manager2() as cm2, + make_context_manager3() as cm3, + make_context_manager4() as cm4, +): + pass + + +with new_new_new1() as cm1, new_new_new2(): + pass diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/conditional_expression.options.json b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/docstring_newline_preview.options.json similarity index 100% rename from crates/ruff_python_formatter/resources/test/fixtures/black/cases/conditional_expression.options.json rename to crates/ruff_python_formatter/resources/test/fixtures/black/cases/docstring_newline_preview.options.json diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/docstring_newline_preview.py b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/docstring_newline_preview.py new file mode 100644 index 0000000000000..75b8db4817538 --- /dev/null +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/docstring_newline_preview.py @@ -0,0 +1,3 @@ +""" +87 characters ............................................................................ +""" diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/docstring_newline_preview.py.expect b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/docstring_newline_preview.py.expect new file mode 100644 index 0000000000000..75b8db4817538 --- /dev/null +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/docstring_newline_preview.py.expect @@ -0,0 +1,3 @@ +""" +87 characters ............................................................................ +""" diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/dummy_implementations.py b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/dummy_implementations.py new file mode 100644 index 0000000000000..739359ee25bbc --- /dev/null +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/dummy_implementations.py @@ -0,0 +1,69 @@ +from typing import NoReturn, Protocol, Union, overload + +class Empty: + ... + +def dummy(a): ... +async def other(b): ... + + +@overload +def a(arg: int) -> int: ... +@overload +def a(arg: str) -> str: ... +@overload +def a(arg: object) -> NoReturn: ... +def a(arg: Union[int, str, object]) -> Union[int, str]: + if not isinstance(arg, (int, str)): + raise TypeError + return arg + +class Proto(Protocol): + def foo(self, a: int) -> int: + ... + + def bar(self, b: str) -> str: ... + def baz(self, c: bytes) -> str: + ... + + +def dummy_two(): + ... +@dummy +def dummy_three(): + ... + +def dummy_four(): + ... + +@overload +def b(arg: int) -> int: ... + +@overload +def b(arg: str) -> str: ... +@overload +def b(arg: object) -> NoReturn: ... + +def b(arg: Union[int, str, object]) -> Union[int, str]: + if not isinstance(arg, (int, str)): + raise TypeError + return arg + +def has_comment(): + ... # still a dummy + +if some_condition: + ... + +if already_dummy: ... + +class AsyncCls: + async def async_method(self): + ... + +async def async_function(self): + ... + +@decorated +async def async_function(self): + ... diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/dummy_implementations.py.expect b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/dummy_implementations.py.expect new file mode 100644 index 0000000000000..369e442152116 --- /dev/null +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/dummy_implementations.py.expect @@ -0,0 +1,72 @@ +from typing import NoReturn, Protocol, Union, overload + + +class Empty: ... + + +def dummy(a): ... +async def other(b): ... + + +@overload +def a(arg: int) -> int: ... +@overload +def a(arg: str) -> str: ... +@overload +def a(arg: object) -> NoReturn: ... +def a(arg: Union[int, str, object]) -> Union[int, str]: + if not isinstance(arg, (int, str)): + raise TypeError + return arg + + +class Proto(Protocol): + def foo(self, a: int) -> int: ... + + def bar(self, b: str) -> str: ... + def baz(self, c: bytes) -> str: ... + + +def dummy_two(): ... +@dummy +def dummy_three(): ... + + +def dummy_four(): ... + + +@overload +def b(arg: int) -> int: ... + + +@overload +def b(arg: str) -> str: ... +@overload +def b(arg: object) -> NoReturn: ... + + +def b(arg: Union[int, str, object]) -> Union[int, str]: + if not isinstance(arg, (int, str)): + raise TypeError + return arg + + +def has_comment(): ... # still a dummy + + +if some_condition: + ... + +if already_dummy: + ... + + +class AsyncCls: + async def async_method(self): ... + + +async def async_function(self): ... + + +@decorated +async def async_function(self): ... diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/empty_lines.py.expect b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/empty_lines.py.expect index 346226178e24e..1a9a51f04f764 100644 --- a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/empty_lines.py.expect +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/empty_lines.py.expect @@ -22,6 +22,7 @@ def f(): if not prev: prevp = preceding_leaf(p) if not prevp or prevp.type in OPENING_BRACKETS: + return NO if prevp.type == token.EQUAL: diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/fmtonoff.py.expect b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/fmtonoff.py.expect index 2534e5cc1d322..710acc5bfb7bf 100644 --- a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/fmtonoff.py.expect +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/fmtonoff.py.expect @@ -53,12 +53,8 @@ def spaces_types( g: int = 1 if False else 2, h: str = "", i: str = r"", -): - ... - - -def spaces2(result=_core.Value(None)): - ... +): ... +def spaces2(result=_core.Value(None)): ... something = { diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/fmtonoff5.py.expect b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/fmtonoff5.py.expect index 69b5430b8b427..c33313354a747 100644 --- a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/fmtonoff5.py.expect +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/fmtonoff5.py.expect @@ -72,8 +72,7 @@ class Named(t.Protocol): class Factory(t.Protocol): - def this_will_be_formatted(self, **kwargs) -> Named: - ... + def this_will_be_formatted(self, **kwargs) -> Named: ... # fmt: on diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/module_docstring_1.options.json b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/fmtskip9.options.json similarity index 100% rename from crates/ruff_python_formatter/resources/test/fixtures/black/cases/module_docstring_1.options.json rename to crates/ruff_python_formatter/resources/test/fixtures/black/cases/fmtskip9.options.json diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/fmtskip9.py b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/fmtskip9.py new file mode 100644 index 0000000000000..0fcd86f9766c2 --- /dev/null +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/fmtskip9.py @@ -0,0 +1,2 @@ +print () # fmt: skip +print () # fmt:skip diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/fmtskip9.py.expect b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/fmtskip9.py.expect new file mode 100644 index 0000000000000..0fcd86f9766c2 --- /dev/null +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/fmtskip9.py.expect @@ -0,0 +1,2 @@ +print () # fmt: skip +print () # fmt:skip diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/form_feeds.py b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/form_feeds.py new file mode 100644 index 0000000000000..42ba8fe243b29 --- /dev/null +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/form_feeds.py @@ -0,0 +1,116 @@ +# Warning! This file contains form feeds (ASCII 0x0C, often represented by \f or ^L). +# These may be invisible in your editor: ensure you can see them before making changes here. + +# There's one at the start that'll get stripped + +# Comment and statement processing is different enough that we'll test variations of both +# contexts here + +# + + +# + + +# + + + +# + + + +# + + + +# + + +# + + + +# + +# + +# + + \ +# +pass + +pass + + +pass + + +pass + + + +pass + + + +pass + + + +pass + + +pass + + + +pass + +pass + +pass + + +# form feed after a dedent +def foo(): + pass + +pass + + +# form feeds are prohibited inside blocks, or on a line with nonwhitespace + def bar( a = 1 ,b : bool = False ) : + + + pass + + +class Baz: + + def __init__(self): + pass + + + def something(self): + pass + + + +# +pass +pass # +a = 1 + # + pass + a = 1 + +a = [ + +] + +# as internal whitespace of a comment is allowed but why +"form feed literal in a string is okay " + +# form feeds at the very end get removed. diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/form_feeds.py.expect b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/form_feeds.py.expect new file mode 100644 index 0000000000000..4ce3d2832bf84 --- /dev/null +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/form_feeds.py.expect @@ -0,0 +1,103 @@ +# Warning! This file contains form feeds (ASCII 0x0C, often represented by \f or ^L). +# These may be invisible in your editor: ensure you can see them before making changes here. + +# There's one at the start that'll get stripped + +# Comment and statement processing is different enough that we'll test variations of both +# contexts here + +# + + +# + + +# + + +# + + +# + + +# + + +# + + +# + +# + +# + +# +pass + +pass + + +pass + + +pass + + +pass + + +pass + + +pass + + +pass + + +pass + +pass + +pass + + +# form feed after a dedent +def foo(): + pass + + +pass + + +# form feeds are prohibited inside blocks, or on a line with nonwhitespace +def bar(a=1, b: bool = False): + + pass + + +class Baz: + + def __init__(self): + pass + + def something(self): + pass + + +# +pass +pass # +a = 1 +# +pass +a = 1 + +a = [] + +# as internal whitespace of a comment is allowed but why +"form feed literal in a string is okay " + +# form feeds at the very end get removed. diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/function.py.expect b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/function.py.expect index 1c26d01cb22c8..9a24470273c26 100644 --- a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/function.py.expect +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/function.py.expect @@ -59,10 +59,7 @@ def spaces_types( g: int = 1 if False else 2, h: str = "", i: str = r"", -): - ... - - +): ... def spaces2(result=_core.Value(None)): assert fut is self._read_fut, (fut, self._read_fut) diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/module_docstring_3.options.json b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/is_simple_lookup_for_doublestar_expression.options.json similarity index 100% rename from crates/ruff_python_formatter/resources/test/fixtures/black/cases/module_docstring_3.options.json rename to crates/ruff_python_formatter/resources/test/fixtures/black/cases/is_simple_lookup_for_doublestar_expression.options.json diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/is_simple_lookup_for_doublestar_expression.py b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/is_simple_lookup_for_doublestar_expression.py new file mode 100644 index 0000000000000..48e1c35ac846a --- /dev/null +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/is_simple_lookup_for_doublestar_expression.py @@ -0,0 +1,5 @@ +m2 = None if not isinstance(dist, Normal) else m** 2 + s * 2 +m3 = None if not isinstance(dist, Normal) else m ** 2 + s * 2 +m4 = None if not isinstance(dist, Normal) else m**2 + s * 2 +m5 = obj.method(another_obj.method()).attribute **2 +m6 = None if ... else m**2 + s**2 diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/is_simple_lookup_for_doublestar_expression.py.expect b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/is_simple_lookup_for_doublestar_expression.py.expect new file mode 100644 index 0000000000000..c117e7132adba --- /dev/null +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/is_simple_lookup_for_doublestar_expression.py.expect @@ -0,0 +1,5 @@ +m2 = None if not isinstance(dist, Normal) else m**2 + s * 2 +m3 = None if not isinstance(dist, Normal) else m**2 + s * 2 +m4 = None if not isinstance(dist, Normal) else m**2 + s * 2 +m5 = obj.method(another_obj.method()).attribute ** 2 +m6 = None if ... else m**2 + s**2 diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/keep_newline_after_match.options.json b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/keep_newline_after_match.options.json new file mode 100644 index 0000000000000..60044b9854d9b --- /dev/null +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/keep_newline_after_match.options.json @@ -0,0 +1 @@ +{"target_version": "py310"} \ No newline at end of file diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/keep_newline_after_match.py b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/keep_newline_after_match.py new file mode 100644 index 0000000000000..629b645fce34f --- /dev/null +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/keep_newline_after_match.py @@ -0,0 +1,19 @@ +def http_status(status): + + match status: + + case 400: + + return "Bad request" + + case 401: + + return "Unauthorized" + + case 403: + + return "Forbidden" + + case 404: + + return "Not found" diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/keep_newline_after_match.py.expect b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/keep_newline_after_match.py.expect new file mode 100644 index 0000000000000..629b645fce34f --- /dev/null +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/keep_newline_after_match.py.expect @@ -0,0 +1,19 @@ +def http_status(status): + + match status: + + case 400: + + return "Bad request" + + case 401: + + return "Unauthorized" + + case 403: + + return "Forbidden" + + case 404: + + return "Not found" diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/long_strings_flag_disabled.py b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/long_strings_flag_disabled.py index db3954e3abd7e..d81c331cab256 100644 --- a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/long_strings_flag_disabled.py +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/long_strings_flag_disabled.py @@ -43,8 +43,10 @@ % ( "formatted", "string", - ): "This is a really really really long string that has to go inside of a dictionary. It is %s bad (#%d)." - % ("soooo", 2), + ): ( + "This is a really really really long string that has to go inside of a dictionary. It is %s bad (#%d)." + % ("soooo", 2) + ), } func_with_keywords( @@ -254,10 +256,12 @@ + CONCATENATED + "using the '+' operator." ) -annotated_variable: Final = "This is a large string that has a type annotation attached to it. A type annotation should NOT stop a long string from being wrapped." -annotated_variable: Literal[ - "fakse_literal" -] = "This is a large string that has a type annotation attached to it. A type annotation should NOT stop a long string from being wrapped." +annotated_variable: Final = ( + "This is a large string that has a type annotation attached to it. A type annotation should NOT stop a long string from being wrapped." +) +annotated_variable: Literal["fakse_literal"] = ( + "This is a large string that has a type annotation attached to it. A type annotation should NOT stop a long string from being wrapped." +) backslashes = "This is a really long string with \"embedded\" double quotes and 'single' quotes that also handles checking for an even number of backslashes \\" backslashes = "This is a really long string with \"embedded\" double quotes and 'single' quotes that also handles checking for an even number of backslashes \\\\" diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/long_strings_flag_disabled.py.expect b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/long_strings_flag_disabled.py.expect index db3954e3abd7e..d81c331cab256 100644 --- a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/long_strings_flag_disabled.py.expect +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/long_strings_flag_disabled.py.expect @@ -43,8 +43,10 @@ D4 = { % ( "formatted", "string", - ): "This is a really really really long string that has to go inside of a dictionary. It is %s bad (#%d)." - % ("soooo", 2), + ): ( + "This is a really really really long string that has to go inside of a dictionary. It is %s bad (#%d)." + % ("soooo", 2) + ), } func_with_keywords( @@ -254,10 +256,12 @@ annotated_variable: Final = ( + CONCATENATED + "using the '+' operator." ) -annotated_variable: Final = "This is a large string that has a type annotation attached to it. A type annotation should NOT stop a long string from being wrapped." -annotated_variable: Literal[ - "fakse_literal" -] = "This is a large string that has a type annotation attached to it. A type annotation should NOT stop a long string from being wrapped." +annotated_variable: Final = ( + "This is a large string that has a type annotation attached to it. A type annotation should NOT stop a long string from being wrapped." +) +annotated_variable: Literal["fakse_literal"] = ( + "This is a large string that has a type annotation attached to it. A type annotation should NOT stop a long string from being wrapped." +) backslashes = "This is a really long string with \"embedded\" double quotes and 'single' quotes that also handles checking for an even number of backslashes \\" backslashes = "This is a really long string with \"embedded\" double quotes and 'single' quotes that also handles checking for an even number of backslashes \\\\" diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/module_docstring_2.py b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/module_docstring_2.py index 724e28cd4ebac..0e29b14bf4711 100644 --- a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/module_docstring_2.py +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/module_docstring_2.py @@ -1,6 +1,6 @@ """I am a very helpful module docstring. -With trailing spaces: +With trailing spaces (only removed with unify_docstring_detection on): Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/module_docstring_2.py.expect b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/module_docstring_2.py.expect index 9b270c521b34b..7c31ef75cfaa7 100644 --- a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/module_docstring_2.py.expect +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/module_docstring_2.py.expect @@ -1,6 +1,6 @@ """I am a very helpful module docstring. -With trailing spaces: +With trailing spaces (only removed with unify_docstring_detection on): Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/module_docstring_followed_by_class.options.json b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/module_docstring_followed_by_class.options.json deleted file mode 100644 index ce7c52b163497..0000000000000 --- a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/module_docstring_followed_by_class.options.json +++ /dev/null @@ -1 +0,0 @@ -{"preview": "enabled"} \ No newline at end of file diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/module_docstring_followed_by_function.options.json b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/module_docstring_followed_by_function.options.json deleted file mode 100644 index ce7c52b163497..0000000000000 --- a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/module_docstring_followed_by_function.options.json +++ /dev/null @@ -1 +0,0 @@ -{"preview": "enabled"} \ No newline at end of file diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/nested_stub.options.json b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/nested_stub.options.json deleted file mode 100644 index b85c7adb4c6ff..0000000000000 --- a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/nested_stub.options.json +++ /dev/null @@ -1 +0,0 @@ -{"preview": "enabled", "source_type": "Stub"} \ No newline at end of file diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/no_blank_line_before_docstring.py b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/no_blank_line_before_docstring.py new file mode 100644 index 0000000000000..56d361087d206 --- /dev/null +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/no_blank_line_before_docstring.py @@ -0,0 +1,33 @@ +def line_before_docstring(): + + """Please move me up""" + + +class LineBeforeDocstring: + + """Please move me up""" + + +class EvenIfThereIsAMethodAfter: + + """I'm the docstring""" + def method(self): + pass + + +class TwoLinesBeforeDocstring: + + + """I want to be treated the same as if I were closer""" + + +class MultilineDocstringsAsWell: + + """I'm so far + + and on so many lines... + """ + +class SingleQuotedDocstring: + + "I'm a docstring but I don't even get triple quotes." diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/no_blank_line_before_docstring.py.expect b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/no_blank_line_before_docstring.py.expect new file mode 100644 index 0000000000000..3e1118fba6f43 --- /dev/null +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/no_blank_line_before_docstring.py.expect @@ -0,0 +1,29 @@ +def line_before_docstring(): + """Please move me up""" + + +class LineBeforeDocstring: + """Please move me up""" + + +class EvenIfThereIsAMethodAfter: + """I'm the docstring""" + + def method(self): + pass + + +class TwoLinesBeforeDocstring: + """I want to be treated the same as if I were closer""" + + +class MultilineDocstringsAsWell: + """I'm so far + + and on so many lines... + """ + + +class SingleQuotedDocstring: + + "I'm a docstring but I don't even get triple quotes." diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/pattern_matching_long.options.json b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/pattern_matching_long.options.json new file mode 100644 index 0000000000000..60044b9854d9b --- /dev/null +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/pattern_matching_long.options.json @@ -0,0 +1 @@ +{"target_version": "py310"} \ No newline at end of file diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/pattern_matching_long.py b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/pattern_matching_long.py new file mode 100644 index 0000000000000..98b6ac56a2048 --- /dev/null +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/pattern_matching_long.py @@ -0,0 +1,7 @@ +match x: + case "abcd" | "abcd" | "abcd" : + pass + case "abcd" | "abcd" | "abcd" | "abcd" | "abcd" | "abcd" | "abcd" | "abcd" | "abcd" | "abcd" | "abcd" | "abcd" | "abcd" | "abcd" | "abcd": + pass + case xxxxxxxxxxxxxxxxxxxxxxx: + pass diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/pattern_matching_long.py.expect b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/pattern_matching_long.py.expect new file mode 100644 index 0000000000000..c60e5770a7a41 --- /dev/null +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/pattern_matching_long.py.expect @@ -0,0 +1,23 @@ +match x: + case "abcd" | "abcd" | "abcd": + pass + case ( + "abcd" + | "abcd" + | "abcd" + | "abcd" + | "abcd" + | "abcd" + | "abcd" + | "abcd" + | "abcd" + | "abcd" + | "abcd" + | "abcd" + | "abcd" + | "abcd" + | "abcd" + ): + pass + case xxxxxxxxxxxxxxxxxxxxxxx: + pass diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/pattern_matching_trailing_comma.options.json b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/pattern_matching_trailing_comma.options.json new file mode 100644 index 0000000000000..60044b9854d9b --- /dev/null +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/pattern_matching_trailing_comma.options.json @@ -0,0 +1 @@ +{"target_version": "py310"} \ No newline at end of file diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/pattern_matching_trailing_comma.py b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/pattern_matching_trailing_comma.py new file mode 100644 index 0000000000000..002ceea330686 --- /dev/null +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/pattern_matching_trailing_comma.py @@ -0,0 +1,14 @@ +match maybe, multiple: + case perhaps, 5: + pass + case perhaps, 6,: + pass + + +match more := (than, one), indeed,: + case _, (5, 6): + pass + case [[5], (6)], [7],: + pass + case _: + pass diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/pattern_matching_trailing_comma.py.expect b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/pattern_matching_trailing_comma.py.expect new file mode 100644 index 0000000000000..6e8ef32f18157 --- /dev/null +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/pattern_matching_trailing_comma.py.expect @@ -0,0 +1,20 @@ +match maybe, multiple: + case perhaps, 5: + pass + case ( + perhaps, + 6, + ): + pass + + +match more := (than, one), indeed,: + case _, (5, 6): + pass + case ( + [[5], (6)], + [7], + ): + pass + case _: + pass diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/pep604_union_types_line_breaks.options.json b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/pep604_union_types_line_breaks.options.json index 0aaaa152bc848..60044b9854d9b 100644 --- a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/pep604_union_types_line_breaks.options.json +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/pep604_union_types_line_breaks.options.json @@ -1 +1 @@ -{"preview": "enabled", "target_version": "py310"} \ No newline at end of file +{"target_version": "py310"} \ No newline at end of file diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/pep_572_py310.py b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/pep_572_py310.py index cb82b2d23f8a4..3487ac8536a13 100644 --- a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/pep_572_py310.py +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/pep_572_py310.py @@ -1,7 +1,7 @@ # Unparenthesized walruses are now allowed in indices since Python 3.10. -x[a:=0] -x[a:=0, b:=1] -x[5, b:=0] +x[a := 0] +x[a := 0, b := 1] +x[5, b := 0] # Walruses are allowed inside generator expressions on function calls since 3.10. if any(match := pattern_error.match(s) for s in buffer): diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/pep_572_py310.py.expect b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/pep_572_py310.py.expect index cb82b2d23f8a4..3487ac8536a13 100644 --- a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/pep_572_py310.py.expect +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/pep_572_py310.py.expect @@ -1,7 +1,7 @@ # Unparenthesized walruses are now allowed in indices since Python 3.10. -x[a:=0] -x[a:=0, b:=1] -x[5, b:=0] +x[a := 0] +x[a := 0, b := 1] +x[5, b := 0] # Walruses are allowed inside generator expressions on function calls since 3.10. if any(match := pattern_error.match(s) for s in buffer): diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/pep_572_remove_parens.py.expect b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/pep_572_remove_parens.py.expect index 37a6a85087aa4..ba85736ff340d 100644 --- a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/pep_572_remove_parens.py.expect +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/pep_572_remove_parens.py.expect @@ -23,18 +23,16 @@ assert (foo := 42 - 12) foo(x=(y := f(x))) -def foo(answer=(p := 42)): - ... +def foo(answer=(p := 42)): ... -def foo2(answer: (p := 42) = 5): - ... +def foo2(answer: (p := 42) = 5): ... lambda: (x := 1) a[(x := 12)] -a[:(x := 13)] +a[: (x := 13)] # we don't touch expressions in f-strings but if we do one day, don't break 'em f"{(x:=10)}" diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/pep_572_slices.py b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/pep_572_slices.py new file mode 100644 index 0000000000000..dff98530388cf --- /dev/null +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/pep_572_slices.py @@ -0,0 +1,2 @@ +x[(a:=0):] +x[:(a:=0)] diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/pep_572_slices.py.expect b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/pep_572_slices.py.expect new file mode 100644 index 0000000000000..a976b8a318bbb --- /dev/null +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/pep_572_slices.py.expect @@ -0,0 +1,2 @@ +x[(a := 0) :] +x[: (a := 0)] diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/percent_precedence.py b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/percent_precedence.py new file mode 100644 index 0000000000000..ab71177d2e1d7 --- /dev/null +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/percent_precedence.py @@ -0,0 +1,20 @@ +("" % a) ** 2 +("" % a)[0] +("" % a)() +("" % a).b + +2 * ("" % a) +2 @ ("" % a) +2 / ("" % a) +2 // ("" % a) +2 % ("" % a) ++("" % a) +b + ("" % a) +-("" % a) +b - ("" % a) +b + -("" % a) +~("" % a) +2 ** ("" % a) +await ("" % a) +b[("" % a)] +b(("" % a)) diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/percent_precedence.py.expect b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/percent_precedence.py.expect new file mode 100644 index 0000000000000..ab71177d2e1d7 --- /dev/null +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/percent_precedence.py.expect @@ -0,0 +1,20 @@ +("" % a) ** 2 +("" % a)[0] +("" % a)() +("" % a).b + +2 * ("" % a) +2 @ ("" % a) +2 / ("" % a) +2 // ("" % a) +2 % ("" % a) ++("" % a) +b + ("" % a) +-("" % a) +b - ("" % a) +b + -("" % a) +~("" % a) +2 ** ("" % a) +await ("" % a) +b[("" % a)] +b(("" % a)) diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/power_op_spacing_long.py b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/power_op_spacing_long.py new file mode 100644 index 0000000000000..af361012ea829 --- /dev/null +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/power_op_spacing_long.py @@ -0,0 +1,11 @@ +a = 1**1**1**1**1**1**1**1**1**1**1**1**1**1**1**1**1**1**1**1**1**1**1**1**1**1**1**1 +b = 1**1**1**1**1**1**1**1**1**1**1**1**1**1**1**1**1**1**1**1**1**1**1**1**1**1**1**1**1 +c = 1 ** 1 ** 1 ** 1 ** 1 ** 1 ** 1 ** 1 ** 1 ** 1 ** 1 ** 1 ** 1 ** 1 ** 1 ** 1 ** 1 ** 1 ** 1 ** 1 ** 1 ** 1 ** 1 ** 1 ** 1 ** 1 ** 1 ** 1 +d = 1**1 ** 1**1 ** 1**1 ** 1**1 ** 1**1**1 ** 1 ** 1**1 ** 1**1**1**1**1 ** 1 ** 1**1**1 **1**1** 1 ** 1 ** 1 +e = 𨉟**𨉟**𨉟**𨉟**𨉟**𨉟**𨉟**𨉟**𨉟**𨉟**𨉟**𨉟**𨉟**𨉟**𨉟**𨉟**𨉟**𨉟**𨉟**𨉟**𨉟 +f = 𨉟**𨉟**𨉟**𨉟**𨉟**𨉟**𨉟**𨉟**𨉟**𨉟**𨉟**𨉟**𨉟**𨉟**𨉟**𨉟**𨉟**𨉟**𨉟**𨉟**𨉟**𨉟 + +a = 1.0**1.0**1.0**1.0**1.0**1.0**1.0**1.0**1.0**1.0**1.0**1.0**1.0**1.0**1.0**1.0**1.0 +b = 1.0**1.0**1.0**1.0**1.0**1.0**1.0**1.0**1.0**1.0**1.0**1.0**1.0**1.0**1.0**1.0**1.0**1.0 +c = 1.0 ** 1.0 ** 1.0 ** 1.0 ** 1.0 ** 1.0 ** 1.0 ** 1.0 ** 1.0 ** 1.0 ** 1.0 ** 1.0 ** 1.0 ** 1.0 ** 1.0 ** 1.0 ** 1.0 +d = 1.0**1.0 ** 1.0**1.0 ** 1.0**1.0 ** 1.0**1.0 ** 1.0**1.0**1.0 ** 1.0 ** 1.0**1.0 ** 1.0**1.0**1.0 diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/power_op_spacing_long.py.expect b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/power_op_spacing_long.py.expect new file mode 100644 index 0000000000000..da8b91530b81d --- /dev/null +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/power_op_spacing_long.py.expect @@ -0,0 +1,83 @@ +a = 1**1**1**1**1**1**1**1**1**1**1**1**1**1**1**1**1**1**1**1**1**1**1**1**1**1**1**1 +b = ( + 1 + ** 1 + ** 1 + ** 1 + ** 1 + ** 1 + ** 1 + ** 1 + ** 1 + ** 1 + ** 1 + ** 1 + ** 1 + ** 1 + ** 1 + ** 1 + ** 1 + ** 1 + ** 1 + ** 1 + ** 1 + ** 1 + ** 1 + ** 1 + ** 1 + ** 1 + ** 1 + ** 1 + ** 1 +) +c = 1**1**1**1**1**1**1**1**1**1**1**1**1**1**1**1**1**1**1**1**1**1**1**1**1**1**1**1 +d = 1**1**1**1**1**1**1**1**1**1**1**1**1**1**1**1**1**1**1**1**1**1**1**1**1**1**1**1 +e = 𨉟**𨉟**𨉟**𨉟**𨉟**𨉟**𨉟**𨉟**𨉟**𨉟**𨉟**𨉟**𨉟**𨉟**𨉟**𨉟**𨉟**𨉟**𨉟**𨉟**𨉟 +f = ( + 𨉟 + ** 𨉟 + ** 𨉟 + ** 𨉟 + ** 𨉟 + ** 𨉟 + ** 𨉟 + ** 𨉟 + ** 𨉟 + ** 𨉟 + ** 𨉟 + ** 𨉟 + ** 𨉟 + ** 𨉟 + ** 𨉟 + ** 𨉟 + ** 𨉟 + ** 𨉟 + ** 𨉟 + ** 𨉟 + ** 𨉟 + ** 𨉟 +) + +a = 1.0**1.0**1.0**1.0**1.0**1.0**1.0**1.0**1.0**1.0**1.0**1.0**1.0**1.0**1.0**1.0**1.0 +b = ( + 1.0 + ** 1.0 + ** 1.0 + ** 1.0 + ** 1.0 + ** 1.0 + ** 1.0 + ** 1.0 + ** 1.0 + ** 1.0 + ** 1.0 + ** 1.0 + ** 1.0 + ** 1.0 + ** 1.0 + ** 1.0 + ** 1.0 + ** 1.0 +) +c = 1.0**1.0**1.0**1.0**1.0**1.0**1.0**1.0**1.0**1.0**1.0**1.0**1.0**1.0**1.0**1.0**1.0 +d = 1.0**1.0**1.0**1.0**1.0**1.0**1.0**1.0**1.0**1.0**1.0**1.0**1.0**1.0**1.0**1.0**1.0 diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/prefer_rhs_split.py b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/prefer_rhs_split.py new file mode 100644 index 0000000000000..f3d9fd672515c --- /dev/null +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/prefer_rhs_split.py @@ -0,0 +1,106 @@ +first_item, second_item = ( + some_looooooooong_module.some_looooooooooooooong_function_name( + first_argument, second_argument, third_argument + ) +) + +some_dict["with_a_long_key"] = ( + some_looooooooong_module.some_looooooooooooooong_function_name( + first_argument, second_argument, third_argument + ) +) + +# Make sure it works when the RHS only has one pair of (optional) parens. +first_item, second_item = ( + some_looooooooong_module.SomeClass.some_looooooooooooooong_variable_name +) + +some_dict["with_a_long_key"] = ( + some_looooooooong_module.SomeClass.some_looooooooooooooong_variable_name +) + +# Make sure chaining assignments work. +first_item, second_item, third_item, forth_item = m["everything"] = ( + some_looooooooong_module.some_looooooooooooooong_function_name( + first_argument, second_argument, third_argument + ) +) + +# Make sure when the RHS's first split at the non-optional paren fits, +# we split there instead of the outer RHS optional paren. +first_item, second_item = some_looooooooong_module.some_loooooog_function_name( + first_argument, second_argument, third_argument +) + +( + first_item, + second_item, + third_item, + forth_item, + fifth_item, + last_item_very_loooooong, +) = some_looooooooong_module.some_looooooooooooooong_function_name( + first_argument, second_argument, third_argument +) + +( + first_item, + second_item, + third_item, + forth_item, + fifth_item, + last_item_very_loooooong, +) = everything = some_looooong_function_name( + first_argument, second_argument, third_argument +) + + +# Make sure unsplittable type ignore won't be moved. +some_kind_of_table[some_key] = util.some_function( # type: ignore # noqa: E501 + some_arg +).intersection(pk_cols) + +some_kind_of_table[ + some_key +] = lambda obj: obj.some_long_named_method() # type: ignore # noqa: E501 + +some_kind_of_table[ + some_key # type: ignore # noqa: E501 +] = lambda obj: obj.some_long_named_method() + + +# Make when when the left side of assignment plus the opening paren "... = (" is +# exactly line length limit + 1, it won't be split like that. +xxxxxxxxx_yyy_zzzzzzzz[ + xx.xxxxxx(x_yyy_zzzzzz.xxxxx[0]), x_yyy_zzzzzz.xxxxxx(xxxx=1) +] = 1 + + +# Right side of assignment contains un-nested pairs of inner parens. +some_kind_of_instance.some_kind_of_map[a_key] = ( + isinstance(some_var, SomeClass) + and table.something_and_something != table.something_else +) or ( + isinstance(some_other_var, BaseClass) and table.something != table.some_other_thing +) + +# Multiple targets +a = b = ( + ccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc +) + +a = b = c = d = e = f = g = ( + hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh +) = i = j = ( + kkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkk +) + +a = ( + bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb +) = c + +a = ( + bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb +) = ( + cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc +) = ddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/prefer_rhs_split.py.expect b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/prefer_rhs_split.py.expect new file mode 100644 index 0000000000000..f3d9fd672515c --- /dev/null +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/prefer_rhs_split.py.expect @@ -0,0 +1,106 @@ +first_item, second_item = ( + some_looooooooong_module.some_looooooooooooooong_function_name( + first_argument, second_argument, third_argument + ) +) + +some_dict["with_a_long_key"] = ( + some_looooooooong_module.some_looooooooooooooong_function_name( + first_argument, second_argument, third_argument + ) +) + +# Make sure it works when the RHS only has one pair of (optional) parens. +first_item, second_item = ( + some_looooooooong_module.SomeClass.some_looooooooooooooong_variable_name +) + +some_dict["with_a_long_key"] = ( + some_looooooooong_module.SomeClass.some_looooooooooooooong_variable_name +) + +# Make sure chaining assignments work. +first_item, second_item, third_item, forth_item = m["everything"] = ( + some_looooooooong_module.some_looooooooooooooong_function_name( + first_argument, second_argument, third_argument + ) +) + +# Make sure when the RHS's first split at the non-optional paren fits, +# we split there instead of the outer RHS optional paren. +first_item, second_item = some_looooooooong_module.some_loooooog_function_name( + first_argument, second_argument, third_argument +) + +( + first_item, + second_item, + third_item, + forth_item, + fifth_item, + last_item_very_loooooong, +) = some_looooooooong_module.some_looooooooooooooong_function_name( + first_argument, second_argument, third_argument +) + +( + first_item, + second_item, + third_item, + forth_item, + fifth_item, + last_item_very_loooooong, +) = everything = some_looooong_function_name( + first_argument, second_argument, third_argument +) + + +# Make sure unsplittable type ignore won't be moved. +some_kind_of_table[some_key] = util.some_function( # type: ignore # noqa: E501 + some_arg +).intersection(pk_cols) + +some_kind_of_table[ + some_key +] = lambda obj: obj.some_long_named_method() # type: ignore # noqa: E501 + +some_kind_of_table[ + some_key # type: ignore # noqa: E501 +] = lambda obj: obj.some_long_named_method() + + +# Make when when the left side of assignment plus the opening paren "... = (" is +# exactly line length limit + 1, it won't be split like that. +xxxxxxxxx_yyy_zzzzzzzz[ + xx.xxxxxx(x_yyy_zzzzzz.xxxxx[0]), x_yyy_zzzzzz.xxxxxx(xxxx=1) +] = 1 + + +# Right side of assignment contains un-nested pairs of inner parens. +some_kind_of_instance.some_kind_of_map[a_key] = ( + isinstance(some_var, SomeClass) + and table.something_and_something != table.something_else +) or ( + isinstance(some_other_var, BaseClass) and table.something != table.some_other_thing +) + +# Multiple targets +a = b = ( + ccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc +) + +a = b = c = d = e = f = g = ( + hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh +) = i = j = ( + kkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkk +) + +a = ( + bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb +) = c + +a = ( + bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb +) = ( + cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc +) = ddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_cantfit.py b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_cantfit.py index 32ef2770cf719..278aa4393bd83 100644 --- a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_cantfit.py +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_cantfit.py @@ -19,12 +19,6 @@ normal_name = but_the_function_name_is_now_ridiculously_long_and_it_is_still_super_annoying( [1, 2, 3], arg1, [1, 2, 3], arg2, [1, 2, 3], arg3 ) -# long arguments -normal_name = normal_function_name( - "but with super long string arguments that on their own exceed the line limit so there's no way it can ever fit", - "eggs with spam and eggs and spam with eggs with spam and eggs and spam with eggs with spam and eggs and spam with eggs", - this_is_a_ridiculously_long_name_and_nobody_in_their_right_mind_would_use_one_like_it=0, -) string_variable_name = ( "a string that is waaaaaaaayyyyyyyy too long, even in parens, there's nothing you can do" # noqa ) diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_cantfit.py.expect b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_cantfit.py.expect index 27327be5f860f..0030d7bdc97d8 100644 --- a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_cantfit.py.expect +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_cantfit.py.expect @@ -33,14 +33,6 @@ normal_name = ( [1, 2, 3], arg1, [1, 2, 3], arg2, [1, 2, 3], arg3 ) ) -# long arguments -normal_name = normal_function_name( - "but with super long string arguments that on their own exceed the line limit so" - " there's no way it can ever fit", - "eggs with spam and eggs and spam with eggs with spam and eggs and spam with eggs" - " with spam and eggs and spam with eggs", - this_is_a_ridiculously_long_name_and_nobody_in_their_right_mind_would_use_one_like_it=0, -) string_variable_name = "a string that is waaaaaaaayyyyyyyy too long, even in parens, there's nothing you can do" # noqa for key in """ hostname diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_cantfit_string.py b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_cantfit_string.py new file mode 100644 index 0000000000000..af7464bc4dd92 --- /dev/null +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_cantfit_string.py @@ -0,0 +1,6 @@ +# long arguments +normal_name = normal_function_name( + "but with super long string arguments that on their own exceed the line limit so there's no way it can ever fit", + "eggs with spam and eggs and spam with eggs with spam and eggs and spam with eggs with spam and eggs and spam with eggs", + this_is_a_ridiculously_long_name_and_nobody_in_their_right_mind_would_use_one_like_it=0, +) diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_cantfit_string.py.expect b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_cantfit_string.py.expect new file mode 100644 index 0000000000000..765396342929a --- /dev/null +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_cantfit_string.py.expect @@ -0,0 +1,8 @@ +# long arguments +normal_name = normal_function_name( + "but with super long string arguments that on their own exceed the line limit so" + " there's no way it can ever fit", + "eggs with spam and eggs and spam with eggs with spam and eggs and spam with eggs" + " with spam and eggs and spam with eggs", + this_is_a_ridiculously_long_name_and_nobody_in_their_right_mind_would_use_one_like_it=0, +) diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_comments7.options.json b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_comments7.options.json deleted file mode 100644 index ce7c52b163497..0000000000000 --- a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_comments7.options.json +++ /dev/null @@ -1 +0,0 @@ -{"preview": "enabled"} \ No newline at end of file diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_hug_parens_with_braces_and_square_brackets.options.json b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_hug_parens_with_braces_and_square_brackets.options.json deleted file mode 100644 index ce7c52b163497..0000000000000 --- a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_hug_parens_with_braces_and_square_brackets.options.json +++ /dev/null @@ -1 +0,0 @@ -{"preview": "enabled"} \ No newline at end of file diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_hug_parens_with_braces_and_square_brackets_no_ll1.options.json b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_hug_parens_with_braces_and_square_brackets_no_ll1.options.json deleted file mode 100644 index ce7c52b163497..0000000000000 --- a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_hug_parens_with_braces_and_square_brackets_no_ll1.options.json +++ /dev/null @@ -1 +0,0 @@ -{"preview": "enabled"} \ No newline at end of file diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_long_dict_values.options.json b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_long_dict_values.options.json deleted file mode 100644 index ce7c52b163497..0000000000000 --- a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_long_dict_values.options.json +++ /dev/null @@ -1 +0,0 @@ -{"preview": "enabled"} \ No newline at end of file diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_long_strings.options.json b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_long_strings.options.json deleted file mode 100644 index ce7c52b163497..0000000000000 --- a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_long_strings.options.json +++ /dev/null @@ -1 +0,0 @@ -{"preview": "enabled"} \ No newline at end of file diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_long_strings__east_asian_width.options.json b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_long_strings__east_asian_width.options.json deleted file mode 100644 index ce7c52b163497..0000000000000 --- a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_long_strings__east_asian_width.options.json +++ /dev/null @@ -1 +0,0 @@ -{"preview": "enabled"} \ No newline at end of file diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_long_strings__edge_case.options.json b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_long_strings__edge_case.options.json deleted file mode 100644 index ce7c52b163497..0000000000000 --- a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_long_strings__edge_case.options.json +++ /dev/null @@ -1 +0,0 @@ -{"preview": "enabled"} \ No newline at end of file diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_long_strings__regression.options.json b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_long_strings__regression.options.json deleted file mode 100644 index ce7c52b163497..0000000000000 --- a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_long_strings__regression.options.json +++ /dev/null @@ -1 +0,0 @@ -{"preview": "enabled"} \ No newline at end of file diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_multiline_strings.options.json b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_multiline_strings.options.json deleted file mode 100644 index ce7c52b163497..0000000000000 --- a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_multiline_strings.options.json +++ /dev/null @@ -1 +0,0 @@ -{"preview": "enabled"} \ No newline at end of file diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_return_annotation_brackets_string.options.json b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_return_annotation_brackets_string.options.json deleted file mode 100644 index ce7c52b163497..0000000000000 --- a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_return_annotation_brackets_string.options.json +++ /dev/null @@ -1 +0,0 @@ -{"preview": "enabled"} \ No newline at end of file diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/py310_pep572.options.json b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/py310_pep572.options.json index 0aaaa152bc848..60044b9854d9b 100644 --- a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/py310_pep572.options.json +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/py310_pep572.options.json @@ -1 +1 @@ -{"preview": "enabled", "target_version": "py310"} \ No newline at end of file +{"target_version": "py310"} \ No newline at end of file diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/python39.py.expect b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/python39.py.expect index 99db2993cb218..4c3881428df93 100644 --- a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/python39.py.expect +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/python39.py.expect @@ -1,17 +1,14 @@ @relaxed_decorator[0] -def f(): - ... +def f(): ... @relaxed_decorator[ extremely_long_name_that_definitely_will_not_fit_on_one_line_of_standard_length ] -def f(): - ... +def f(): ... @extremely_long_variable_name_that_doesnt_fit := complex.expression( with_long="arguments_value_that_wont_fit_at_the_end_of_the_line" ) -def f(): - ... +def f(): ... diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/raw_docstring.options.json b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/raw_docstring.options.json deleted file mode 100644 index ce7c52b163497..0000000000000 --- a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/raw_docstring.options.json +++ /dev/null @@ -1 +0,0 @@ -{"preview": "enabled"} \ No newline at end of file diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/raw_docstring_no_string_normalization.py b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/raw_docstring_no_string_normalization.py new file mode 100644 index 0000000000000..338cc01d33e64 --- /dev/null +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/raw_docstring_no_string_normalization.py @@ -0,0 +1,10 @@ +def do_not_touch_this_prefix(): + R"""There was a bug where docstring prefixes would be normalized even with -S.""" + + +def do_not_touch_this_prefix2(): + FR'There was a bug where docstring prefixes would be normalized even with -S.' + + +def do_not_touch_this_prefix3(): + u'''There was a bug where docstring prefixes would be normalized even with -S.''' diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/raw_docstring_no_string_normalization.py.expect b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/raw_docstring_no_string_normalization.py.expect new file mode 100644 index 0000000000000..338cc01d33e64 --- /dev/null +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/raw_docstring_no_string_normalization.py.expect @@ -0,0 +1,10 @@ +def do_not_touch_this_prefix(): + R"""There was a bug where docstring prefixes would be normalized even with -S.""" + + +def do_not_touch_this_prefix2(): + FR'There was a bug where docstring prefixes would be normalized even with -S.' + + +def do_not_touch_this_prefix3(): + u'''There was a bug where docstring prefixes would be normalized even with -S.''' diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/remove_newline_after_code_block_open.py b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/remove_newline_after_code_block_open.py index 78232d55d0826..baa987c34c70d 100644 --- a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/remove_newline_after_code_block_open.py +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/remove_newline_after_code_block_open.py @@ -3,14 +3,14 @@ def foo1(): - print("The newline above me should be deleted!") + print("The newline above me should be kept!") def foo2(): - print("All the newlines above me should be deleted!") + print("All the newlines above me should be kept!") def foo3(): @@ -30,31 +30,31 @@ def foo4(): class Foo: def bar(self): - print("The newline above me should be deleted!") + print("The newline above me should be kept!") for i in range(5): - print(f"{i}) The line above me should be removed!") + print(f"{i}) The line above me should be kept!") for i in range(5): - print(f"{i}) The lines above me should be removed!") + print(f"{i}) The lines above me should be kept!") for i in range(5): for j in range(7): - print(f"{i}) The lines above me should be removed!") + print(f"{i}) The lines above me should be kept!") if random.randint(0, 3) == 0: - print("The new line above me is about to be removed!") + print("The new line above me will be kept!") if random.randint(0, 3) == 0: @@ -62,43 +62,45 @@ def bar(self): - print("The new lines above me is about to be removed!") + print("The new lines above me will be kept!") if random.randint(0, 3) == 0: + if random.uniform(0, 1) > 0.5: - print("Two lines above me are about to be removed!") + + print("Two lines above me will be kept!") while True: - print("The newline above me should be deleted!") + print("The newline above me should be kept!") while True: - print("The newlines above me should be deleted!") + print("The newlines above me should be kept!") while True: while False: - print("The newlines above me should be deleted!") + print("The newlines above me should be kept!") with open("/path/to/file.txt", mode="w") as file: - file.write("The new line above me is about to be removed!") + file.write("The new line above me will be kept!") with open("/path/to/file.txt", mode="w") as file: - file.write("The new lines above me is about to be removed!") + file.write("The new lines above me will be kept!") with open("/path/to/file.txt", mode="r") as read_file: diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/remove_newline_after_code_block_open.py.expect b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/remove_newline_after_code_block_open.py.expect index dff03aa7fee61..c30950e59094c 100644 --- a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/remove_newline_after_code_block_open.py.expect +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/remove_newline_after_code_block_open.py.expect @@ -2,20 +2,24 @@ import random def foo1(): - print("The newline above me should be deleted!") + + print("The newline above me should be kept!") def foo2(): - print("All the newlines above me should be deleted!") + + print("All the newlines above me should be kept!") def foo3(): + print("No newline above me!") print("There is a newline above me, and that's OK!") def foo4(): + # There is a comment here print("The newline above me should not be deleted!") @@ -23,56 +27,73 @@ def foo4(): class Foo: def bar(self): - print("The newline above me should be deleted!") + + print("The newline above me should be kept!") for i in range(5): - print(f"{i}) The line above me should be removed!") + + print(f"{i}) The line above me should be kept!") for i in range(5): - print(f"{i}) The lines above me should be removed!") + + print(f"{i}) The lines above me should be kept!") for i in range(5): + for j in range(7): - print(f"{i}) The lines above me should be removed!") + + print(f"{i}) The lines above me should be kept!") if random.randint(0, 3) == 0: - print("The new line above me is about to be removed!") + + print("The new line above me will be kept!") if random.randint(0, 3) == 0: - print("The new lines above me is about to be removed!") + + print("The new lines above me will be kept!") if random.randint(0, 3) == 0: + if random.uniform(0, 1) > 0.5: - print("Two lines above me are about to be removed!") + + print("Two lines above me will be kept!") while True: - print("The newline above me should be deleted!") + + print("The newline above me should be kept!") while True: - print("The newlines above me should be deleted!") + + print("The newlines above me should be kept!") while True: + while False: - print("The newlines above me should be deleted!") + + print("The newlines above me should be kept!") with open("/path/to/file.txt", mode="w") as file: - file.write("The new line above me is about to be removed!") + + file.write("The new line above me will be kept!") with open("/path/to/file.txt", mode="w") as file: - file.write("The new lines above me is about to be removed!") + + file.write("The new lines above me will be kept!") with open("/path/to/file.txt", mode="r") as read_file: + with open("/path/to/output_file.txt", mode="w") as write_file: + write_file.writelines(read_file.readlines()) diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/remove_redundant_parens_in_case_guard.options.json b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/remove_redundant_parens_in_case_guard.options.json new file mode 100644 index 0000000000000..185c73ab05c66 --- /dev/null +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/remove_redundant_parens_in_case_guard.options.json @@ -0,0 +1 @@ +{"preview": "enabled", "line_width": 79, "target_version": "py310"} \ No newline at end of file diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/remove_redundant_parens_in_case_guard.py b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/remove_redundant_parens_in_case_guard.py new file mode 100644 index 0000000000000..256b48abb7c4b --- /dev/null +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/remove_redundant_parens_in_case_guard.py @@ -0,0 +1,57 @@ +match 1: + case _ if (True): + pass + + +match 1: + case _ if ( + True + ): + pass + + +match 1: + case _ if ( + # this is a comment + True + ): + pass + + +match 1: + case _ if ( + True + # this is a comment + ): + pass + + +match 1: + case _ if ( + True # this is a comment + ): + pass + + +match 1: + case _ if ( # this is a comment + True + ): + pass + + +match 1: + case _ if ( + True + ): # this is a comment + pass + + +match 1: + case _ if (True): # comment over the line limit unless parens are removed x + pass + + +match 1: + case _ if (True): # comment over the line limit and parens should go to next line + pass diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/remove_redundant_parens_in_case_guard.py.expect b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/remove_redundant_parens_in_case_guard.py.expect new file mode 100644 index 0000000000000..9042cb697a0c6 --- /dev/null +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/remove_redundant_parens_in_case_guard.py.expect @@ -0,0 +1,51 @@ +match 1: + case _ if True: + pass + + +match 1: + case _ if True: + pass + + +match 1: + case _ if ( + # this is a comment + True + ): + pass + + +match 1: + case _ if ( + True + # this is a comment + ): + pass + + +match 1: + case _ if True: # this is a comment + pass + + +match 1: + case _ if True: # this is a comment + pass + + +match 1: + case _ if True: # this is a comment + pass + + +match 1: + case _ if True: # comment over the line limit unless parens are removed x + pass + + +match 1: + case ( + _ + ) if True: # comment over the line limit and parens should go to next line + pass diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/return_annotation_brackets.py b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/return_annotation_brackets.py index 8322c4c7ac1f2..3d0e113e0d73b 100644 --- a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/return_annotation_brackets.py +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/return_annotation_brackets.py @@ -88,6 +88,5 @@ def foo() -> tuple[int, int, int,]: return 2 # Magic trailing comma example, with params -# this is broken - the trailing comma is transferred to the param list. Fixed in preview def foo(a,b) -> tuple[int, int, int,]: return 2 diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/return_annotation_brackets.py.expect b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/return_annotation_brackets.py.expect index c8a8ce2ca29f7..940b1085ccdfb 100644 --- a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/return_annotation_brackets.py.expect +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/return_annotation_brackets.py.expect @@ -99,30 +99,27 @@ def foo() -> tuple[int, int, int]: return 2 -def foo() -> ( - tuple[ - loooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong, - loooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong, - loooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong, - ] -): +def foo() -> tuple[ + loooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong, + loooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong, + loooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong, +]: return 2 # Magic trailing comma example -def foo() -> ( - tuple[ - int, - int, - int, - ] -): +def foo() -> tuple[ + int, + int, + int, +]: return 2 # Magic trailing comma example, with params -# this is broken - the trailing comma is transferred to the param list. Fixed in preview -def foo( - a, b -) -> tuple[int, int, int,]: +def foo(a, b) -> tuple[ + int, + int, + int, +]: return 2 diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/single_line_format_skip_with_multiple_comments.py b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/single_line_format_skip_with_multiple_comments.py new file mode 100644 index 0000000000000..812febcf2a19f --- /dev/null +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/single_line_format_skip_with_multiple_comments.py @@ -0,0 +1,8 @@ +foo = 123 # fmt: skip # noqa: E501 # pylint +bar = ( + 123 , + ( 1 + 5 ) # pylint # fmt:skip +) +baz = "a" + "b" # pylint; fmt: skip; noqa: E501 +skip_will_not_work = "a" + "b" # pylint fmt:skip +skip_will_not_work2 = "a" + "b" # some text; fmt:skip happens to be part of it diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/single_line_format_skip_with_multiple_comments.py.expect b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/single_line_format_skip_with_multiple_comments.py.expect new file mode 100644 index 0000000000000..d799b0c790e27 --- /dev/null +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/single_line_format_skip_with_multiple_comments.py.expect @@ -0,0 +1,8 @@ +foo = 123 # fmt: skip # noqa: E501 # pylint +bar = ( + 123 , + ( 1 + 5 ) # pylint # fmt:skip +) +baz = "a" + "b" # pylint; fmt: skip; noqa: E501 +skip_will_not_work = "a" + "b" # pylint fmt:skip +skip_will_not_work2 = "a" + "b" # some text; fmt:skip happens to be part of it diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/trailing_comma.py b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/trailing_comma.py new file mode 100644 index 0000000000000..24c53091b9002 --- /dev/null +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/trailing_comma.py @@ -0,0 +1,24 @@ +e = { + "a": fun(msg, "ts"), + "longggggggggggggggid": ..., + "longgggggggggggggggggggkey": ..., "created": ... + # "longkey": ... +} +f = [ + arg1, + arg2, + arg3, arg4 + # comment +] +g = ( + arg1, + arg2, + arg3, arg4 + # comment +) +h = { + arg1, + arg2, + arg3, arg4 + # comment +} diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/trailing_comma.py.expect b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/trailing_comma.py.expect new file mode 100644 index 0000000000000..1b2bafa0287ec --- /dev/null +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/trailing_comma.py.expect @@ -0,0 +1,28 @@ +e = { + "a": fun(msg, "ts"), + "longggggggggggggggid": ..., + "longgggggggggggggggggggkey": ..., + "created": ..., + # "longkey": ... +} +f = [ + arg1, + arg2, + arg3, + arg4, + # comment +] +g = ( + arg1, + arg2, + arg3, + arg4, + # comment +) +h = { + arg1, + arg2, + arg3, + arg4, + # comment +} diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/module_docstring_4.options.json b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/typed_params_trailing_comma.options.json similarity index 100% rename from crates/ruff_python_formatter/resources/test/fixtures/black/cases/module_docstring_4.options.json rename to crates/ruff_python_formatter/resources/test/fixtures/black/cases/typed_params_trailing_comma.options.json diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/typed_params_trailing_comma.py b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/typed_params_trailing_comma.py new file mode 100644 index 0000000000000..c51916b1f23f9 --- /dev/null +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/typed_params_trailing_comma.py @@ -0,0 +1,10 @@ +def long_function_name_goes_here( + x: Callable[List[int]] +) -> Union[List[int], float, str, bytes, Tuple[int]]: + pass + + +def long_function_name_goes_here( + x: Callable[[str, Any], int] +) -> Union[List[int], float, str, bytes, Tuple[int]]: + pass diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/typed_params_trailing_comma.py.expect b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/typed_params_trailing_comma.py.expect new file mode 100644 index 0000000000000..fe174ad9d6c7c --- /dev/null +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/typed_params_trailing_comma.py.expect @@ -0,0 +1,10 @@ +def long_function_name_goes_here( + x: Callable[List[int]], +) -> Union[List[int], float, str, bytes, Tuple[int]]: + pass + + +def long_function_name_goes_here( + x: Callable[[str, Any], int], +) -> Union[List[int], float, str, bytes, Tuple[int]]: + pass diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/walrus_in_dict.py b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/walrus_in_dict.py index 6962789a353f5..3e3e63421f7de 100644 --- a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/walrus_in_dict.py +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/walrus_in_dict.py @@ -1,3 +1,4 @@ +# This is testing an issue that is specific to the preview style { "is_update": (up := commit.hash in update_hashes) } diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/walrus_in_dict.py.expect b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/walrus_in_dict.py.expect index ffe62d990d8c2..03aa2598fefbc 100644 --- a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/walrus_in_dict.py.expect +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/walrus_in_dict.py.expect @@ -1 +1,2 @@ +# This is testing an issue that is specific to the preview style {"is_update": (up := commit.hash in update_hashes)} diff --git a/crates/ruff_python_formatter/resources/test/fixtures/import_black_tests.py b/crates/ruff_python_formatter/resources/test/fixtures/import_black_tests.py index b53e86cdf6f54..3183ef05cf3cd 100755 --- a/crates/ruff_python_formatter/resources/test/fixtures/import_black_tests.py +++ b/crates/ruff_python_formatter/resources/test/fixtures/import_black_tests.py @@ -57,6 +57,7 @@ def import_fixture(fixture: Path, fixture_set: str): if "--minimum-version=" in flags: [_, version] = flags.split("--minimum-version=", 1) + version = version.split(" ", 1)[0] # Convert 3.10 to py310 options["target_version"] = f"py{version.strip().replace('.', '')}" diff --git a/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__allow_empty_first_line.py.snap b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__allow_empty_first_line.py.snap new file mode 100644 index 0000000000000..4931cdf636af8 --- /dev/null +++ b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__allow_empty_first_line.py.snap @@ -0,0 +1,324 @@ +--- +source: crates/ruff_python_formatter/tests/fixtures.rs +input_file: crates/ruff_python_formatter/resources/test/fixtures/black/cases/allow_empty_first_line.py +--- +## Input + +```python +def foo(): + """ + Docstring + """ + + # Here we go + if x: + + # This is also now fine + a = 123 + + else: + # But not necessary + a = 123 + + if y: + + while True: + + """ + Long comment here + """ + a = 123 + + if z: + + for _ in range(100): + a = 123 + else: + + try: + + # this should be ok + a = 123 + except: + + """also this""" + a = 123 + + +def bar(): + + if x: + a = 123 + + +def baz(): + + # OK + if x: + a = 123 + +def quux(): + + new_line = here + + +class Cls: + + def method(self): + + pass + + +async def async_fn(): + + """Docstring.""" + + +@decorated +async def async_fn(): + + """Docstring.""" + + +def top_level( + a: int, + b: str, +) -> Whatever[Generic, Something]: + + def nested(x: int) -> int: + pass +``` + +## Black Differences + +```diff +--- Black ++++ Ruff +@@ -5,7 +5,6 @@ + + # Here we go + if x: +- + # This is also now fine + a = 123 + +@@ -14,52 +13,41 @@ + a = 123 + + if y: +- + while True: +- + """ + Long comment here + """ + a = 123 + + if z: +- + for _ in range(100): + a = 123 + else: +- + try: +- + # this should be ok + a = 123 + except: +- + """also this""" + a = 123 + + + def bar(): +- + if x: + a = 123 + + + def baz(): +- + # OK + if x: + a = 123 + + + def quux(): +- + new_line = here + + + class Cls: +- + def method(self): +- + pass + + +@@ -76,6 +64,5 @@ + a: int, + b: str, + ) -> Whatever[Generic, Something]: +- + def nested(x: int) -> int: + pass +``` + +## Ruff Output + +```python +def foo(): + """ + Docstring + """ + + # Here we go + if x: + # This is also now fine + a = 123 + + else: + # But not necessary + a = 123 + + if y: + while True: + """ + Long comment here + """ + a = 123 + + if z: + for _ in range(100): + a = 123 + else: + try: + # this should be ok + a = 123 + except: + """also this""" + a = 123 + + +def bar(): + if x: + a = 123 + + +def baz(): + # OK + if x: + a = 123 + + +def quux(): + new_line = here + + +class Cls: + def method(self): + pass + + +async def async_fn(): + """Docstring.""" + + +@decorated +async def async_fn(): + """Docstring.""" + + +def top_level( + a: int, + b: str, +) -> Whatever[Generic, Something]: + def nested(x: int) -> int: + pass +``` + +## Black Output + +```python +def foo(): + """ + Docstring + """ + + # Here we go + if x: + + # This is also now fine + a = 123 + + else: + # But not necessary + a = 123 + + if y: + + while True: + + """ + Long comment here + """ + a = 123 + + if z: + + for _ in range(100): + a = 123 + else: + + try: + + # this should be ok + a = 123 + except: + + """also this""" + a = 123 + + +def bar(): + + if x: + a = 123 + + +def baz(): + + # OK + if x: + a = 123 + + +def quux(): + + new_line = here + + +class Cls: + + def method(self): + + pass + + +async def async_fn(): + """Docstring.""" + + +@decorated +async def async_fn(): + """Docstring.""" + + +def top_level( + a: int, + b: str, +) -> Whatever[Generic, Something]: + + def nested(x: int) -> int: + pass +``` diff --git a/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__comments5.py.snap b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__comments5.py.snap deleted file mode 100644 index a6e70835e0d97..0000000000000 --- a/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__comments5.py.snap +++ /dev/null @@ -1,273 +0,0 @@ ---- -source: crates/ruff_python_formatter/tests/fixtures.rs -input_file: crates/ruff_python_formatter/resources/test/fixtures/black/cases/comments5.py ---- -## Input - -```python -while True: - if something.changed: - do.stuff() # trailing comment - # Comment belongs to the `if` block. - # This one belongs to the `while` block. - - # Should this one, too? I guess so. - -# This one is properly standalone now. - -for i in range(100): - # first we do this - if i % 33 == 0: - break - - # then we do this - print(i) - # and finally we loop around - -with open(some_temp_file) as f: - data = f.read() - -try: - with open(some_other_file) as w: - w.write(data) - -except OSError: - print("problems") - -import sys - - -# leading function comment -def wat(): - ... - # trailing function comment - - -# SECTION COMMENT - - -# leading 1 -@deco1 -# leading 2 -@deco2(with_args=True) -# leading 3 -@deco3 -def decorated1(): - ... - - -# leading 1 -@deco1 -# leading 2 -@deco2(with_args=True) -# leading function comment -def decorated1(): - ... - - -# Note: this is fixed in -# Preview.empty_lines_before_class_or_def_with_leading_comments. -# In the current style, the user will have to split those lines by hand. -some_instruction - - -# This comment should be split from `some_instruction` by two lines but isn't. -def g(): - ... - - -if __name__ == "__main__": - main() -``` - -## Black Differences - -```diff ---- Black -+++ Ruff -@@ -45,8 +45,7 @@ - @deco2(with_args=True) - # leading 3 - @deco3 --def decorated1(): -- ... -+def decorated1(): ... - - - # leading 1 -@@ -54,8 +53,7 @@ - # leading 2 - @deco2(with_args=True) - # leading function comment --def decorated1(): -- ... -+def decorated1(): ... - - - # Note: this is fixed in -@@ -65,8 +63,7 @@ - - - # This comment should be split from `some_instruction` by two lines but isn't. --def g(): -- ... -+def g(): ... - - - if __name__ == "__main__": -``` - -## Ruff Output - -```python -while True: - if something.changed: - do.stuff() # trailing comment - # Comment belongs to the `if` block. - # This one belongs to the `while` block. - - # Should this one, too? I guess so. - -# This one is properly standalone now. - -for i in range(100): - # first we do this - if i % 33 == 0: - break - - # then we do this - print(i) - # and finally we loop around - -with open(some_temp_file) as f: - data = f.read() - -try: - with open(some_other_file) as w: - w.write(data) - -except OSError: - print("problems") - -import sys - - -# leading function comment -def wat(): - ... - # trailing function comment - - -# SECTION COMMENT - - -# leading 1 -@deco1 -# leading 2 -@deco2(with_args=True) -# leading 3 -@deco3 -def decorated1(): ... - - -# leading 1 -@deco1 -# leading 2 -@deco2(with_args=True) -# leading function comment -def decorated1(): ... - - -# Note: this is fixed in -# Preview.empty_lines_before_class_or_def_with_leading_comments. -# In the current style, the user will have to split those lines by hand. -some_instruction - - -# This comment should be split from `some_instruction` by two lines but isn't. -def g(): ... - - -if __name__ == "__main__": - main() -``` - -## Black Output - -```python -while True: - if something.changed: - do.stuff() # trailing comment - # Comment belongs to the `if` block. - # This one belongs to the `while` block. - - # Should this one, too? I guess so. - -# This one is properly standalone now. - -for i in range(100): - # first we do this - if i % 33 == 0: - break - - # then we do this - print(i) - # and finally we loop around - -with open(some_temp_file) as f: - data = f.read() - -try: - with open(some_other_file) as w: - w.write(data) - -except OSError: - print("problems") - -import sys - - -# leading function comment -def wat(): - ... - # trailing function comment - - -# SECTION COMMENT - - -# leading 1 -@deco1 -# leading 2 -@deco2(with_args=True) -# leading 3 -@deco3 -def decorated1(): - ... - - -# leading 1 -@deco1 -# leading 2 -@deco2(with_args=True) -# leading function comment -def decorated1(): - ... - - -# Note: this is fixed in -# Preview.empty_lines_before_class_or_def_with_leading_comments. -# In the current style, the user will have to split those lines by hand. -some_instruction - - -# This comment should be split from `some_instruction` by two lines but isn't. -def g(): - ... - - -if __name__ == "__main__": - main() -``` - - diff --git a/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__comments_in_double_parens.py.snap b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__comments_in_double_parens.py.snap new file mode 100644 index 0000000000000..a60996419c422 --- /dev/null +++ b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__comments_in_double_parens.py.snap @@ -0,0 +1,196 @@ +--- +source: crates/ruff_python_formatter/tests/fixtures.rs +input_file: crates/ruff_python_formatter/resources/test/fixtures/black/cases/comments_in_double_parens.py +--- +## Input + +```python +if ( + True + # sdf +): + print("hw") + +if (( + True + # sdf +)): + print("hw") + +if (( + # type: ignore + True +)): + print("hw") + +if (( + True + # type: ignore +)): + print("hw") + +if ( + # a long comment about + # the condition below + (a or b) +): + pass + +def return_true(): + return ( + ( + True # this comment gets removed accidentally + ) + ) + +def return_true(): + return (True) # this comment gets removed accidentally + + +if ( + # huh comment + (True) +): + ... + +if ( + # huh + ( + # comment + True + ) +): + ... +``` + +## Black Differences + +```diff +--- Black ++++ Ruff +@@ -25,8 +25,7 @@ + if ( + # a long comment about + # the condition below +- a +- or b ++ a or b + ): + pass + +``` + +## Ruff Output + +```python +if ( + True + # sdf +): + print("hw") + +if ( + True + # sdf +): + print("hw") + +if ( + # type: ignore + True +): + print("hw") + +if ( + True + # type: ignore +): + print("hw") + +if ( + # a long comment about + # the condition below + a or b +): + pass + + +def return_true(): + return True # this comment gets removed accidentally + + +def return_true(): + return True # this comment gets removed accidentally + + +if ( + # huh comment + True +): + ... + +if ( + # huh + # comment + True +): + ... +``` + +## Black Output + +```python +if ( + True + # sdf +): + print("hw") + +if ( + True + # sdf +): + print("hw") + +if ( + # type: ignore + True +): + print("hw") + +if ( + True + # type: ignore +): + print("hw") + +if ( + # a long comment about + # the condition below + a + or b +): + pass + + +def return_true(): + return True # this comment gets removed accidentally + + +def return_true(): + return True # this comment gets removed accidentally + + +if ( + # huh comment + True +): + ... + +if ( + # huh + # comment + True +): + ... +``` diff --git a/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__conditional_expression.py.snap b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__conditional_expression.py.snap index 603024b05b69e..87ff831191008 100644 --- a/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__conditional_expression.py.snap +++ b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__conditional_expression.py.snap @@ -274,10 +274,12 @@ def foo(wait: bool = True): time.sleep(1) if wait else None -a = "".join(( - "", # comment - "" if True else "", -)) +a = "".join( + ( + "", # comment + "" if True else "", + ) +) ``` ## Black Output @@ -389,10 +391,10 @@ def foo(wait: bool = True): time.sleep(1) if wait else None -a = "".join(( - "", # comment - "" if True else "", -)) +a = "".join( + ( + "", # comment + "" if True else "", + ) +) ``` - - diff --git a/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__empty_lines.py.snap b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__empty_lines.py.snap new file mode 100644 index 0000000000000..2ce5876a9512d --- /dev/null +++ b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__empty_lines.py.snap @@ -0,0 +1,304 @@ +--- +source: crates/ruff_python_formatter/tests/fixtures.rs +input_file: crates/ruff_python_formatter/resources/test/fixtures/black/cases/empty_lines.py +--- +## Input + +```python +"""Docstring.""" + + +# leading comment +def f(): + NO = '' + SPACE = ' ' + DOUBLESPACE = ' ' + + t = leaf.type + p = leaf.parent # trailing comment + v = leaf.value + + if t in ALWAYS_NO_SPACE: + pass + if t == token.COMMENT: # another trailing comment + return DOUBLESPACE + + + assert p is not None, f"INTERNAL ERROR: hand-made leaf without parent: {leaf!r}" + + + prev = leaf.prev_sibling + if not prev: + prevp = preceding_leaf(p) + if not prevp or prevp.type in OPENING_BRACKETS: + + + return NO + + + if prevp.type == token.EQUAL: + if prevp.parent and prevp.parent.type in { + syms.typedargslist, + syms.varargslist, + syms.parameters, + syms.arglist, + syms.argument, + }: + return NO + + elif prevp.type == token.DOUBLESTAR: + if prevp.parent and prevp.parent.type in { + syms.typedargslist, + syms.varargslist, + syms.parameters, + syms.arglist, + syms.dictsetmaker, + }: + return NO + +############################################################################### +# SECTION BECAUSE SECTIONS +############################################################################### + +def g(): + NO = '' + SPACE = ' ' + DOUBLESPACE = ' ' + + t = leaf.type + p = leaf.parent + v = leaf.value + + # Comment because comments + + if t in ALWAYS_NO_SPACE: + pass + if t == token.COMMENT: + return DOUBLESPACE + + # Another comment because more comments + assert p is not None, f'INTERNAL ERROR: hand-made leaf without parent: {leaf!r}' + + prev = leaf.prev_sibling + if not prev: + prevp = preceding_leaf(p) + + if not prevp or prevp.type in OPENING_BRACKETS: + # Start of the line or a bracketed expression. + # More than one line for the comment. + return NO + + if prevp.type == token.EQUAL: + if prevp.parent and prevp.parent.type in { + syms.typedargslist, + syms.varargslist, + syms.parameters, + syms.arglist, + syms.argument, + }: + return NO +``` + +## Black Differences + +```diff +--- Black ++++ Ruff +@@ -22,7 +22,6 @@ + if not prev: + prevp = preceding_leaf(p) + if not prevp or prevp.type in OPENING_BRACKETS: +- + return NO + + if prevp.type == token.EQUAL: +``` + +## Ruff Output + +```python +"""Docstring.""" + + +# leading comment +def f(): + NO = "" + SPACE = " " + DOUBLESPACE = " " + + t = leaf.type + p = leaf.parent # trailing comment + v = leaf.value + + if t in ALWAYS_NO_SPACE: + pass + if t == token.COMMENT: # another trailing comment + return DOUBLESPACE + + assert p is not None, f"INTERNAL ERROR: hand-made leaf without parent: {leaf!r}" + + prev = leaf.prev_sibling + if not prev: + prevp = preceding_leaf(p) + if not prevp or prevp.type in OPENING_BRACKETS: + return NO + + if prevp.type == token.EQUAL: + if prevp.parent and prevp.parent.type in { + syms.typedargslist, + syms.varargslist, + syms.parameters, + syms.arglist, + syms.argument, + }: + return NO + + elif prevp.type == token.DOUBLESTAR: + if prevp.parent and prevp.parent.type in { + syms.typedargslist, + syms.varargslist, + syms.parameters, + syms.arglist, + syms.dictsetmaker, + }: + return NO + + +############################################################################### +# SECTION BECAUSE SECTIONS +############################################################################### + + +def g(): + NO = "" + SPACE = " " + DOUBLESPACE = " " + + t = leaf.type + p = leaf.parent + v = leaf.value + + # Comment because comments + + if t in ALWAYS_NO_SPACE: + pass + if t == token.COMMENT: + return DOUBLESPACE + + # Another comment because more comments + assert p is not None, f"INTERNAL ERROR: hand-made leaf without parent: {leaf!r}" + + prev = leaf.prev_sibling + if not prev: + prevp = preceding_leaf(p) + + if not prevp or prevp.type in OPENING_BRACKETS: + # Start of the line or a bracketed expression. + # More than one line for the comment. + return NO + + if prevp.type == token.EQUAL: + if prevp.parent and prevp.parent.type in { + syms.typedargslist, + syms.varargslist, + syms.parameters, + syms.arglist, + syms.argument, + }: + return NO +``` + +## Black Output + +```python +"""Docstring.""" + + +# leading comment +def f(): + NO = "" + SPACE = " " + DOUBLESPACE = " " + + t = leaf.type + p = leaf.parent # trailing comment + v = leaf.value + + if t in ALWAYS_NO_SPACE: + pass + if t == token.COMMENT: # another trailing comment + return DOUBLESPACE + + assert p is not None, f"INTERNAL ERROR: hand-made leaf without parent: {leaf!r}" + + prev = leaf.prev_sibling + if not prev: + prevp = preceding_leaf(p) + if not prevp or prevp.type in OPENING_BRACKETS: + + return NO + + if prevp.type == token.EQUAL: + if prevp.parent and prevp.parent.type in { + syms.typedargslist, + syms.varargslist, + syms.parameters, + syms.arglist, + syms.argument, + }: + return NO + + elif prevp.type == token.DOUBLESTAR: + if prevp.parent and prevp.parent.type in { + syms.typedargslist, + syms.varargslist, + syms.parameters, + syms.arglist, + syms.dictsetmaker, + }: + return NO + + +############################################################################### +# SECTION BECAUSE SECTIONS +############################################################################### + + +def g(): + NO = "" + SPACE = " " + DOUBLESPACE = " " + + t = leaf.type + p = leaf.parent + v = leaf.value + + # Comment because comments + + if t in ALWAYS_NO_SPACE: + pass + if t == token.COMMENT: + return DOUBLESPACE + + # Another comment because more comments + assert p is not None, f"INTERNAL ERROR: hand-made leaf without parent: {leaf!r}" + + prev = leaf.prev_sibling + if not prev: + prevp = preceding_leaf(p) + + if not prevp or prevp.type in OPENING_BRACKETS: + # Start of the line or a bracketed expression. + # More than one line for the comment. + return NO + + if prevp.type == token.EQUAL: + if prevp.parent and prevp.parent.type in { + syms.typedargslist, + syms.varargslist, + syms.parameters, + syms.arglist, + syms.argument, + }: + return NO +``` diff --git a/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__fmtonoff.py.snap b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__fmtonoff.py.snap index 4b55126b9e887..a14ed936006f5 100644 --- a/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__fmtonoff.py.snap +++ b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__fmtonoff.py.snap @@ -206,19 +206,7 @@ d={'a':1, # fmt: off from third_party import (X, Y, Z) -@@ -53,25 +54,21 @@ - g: int = 1 if False else 2, - h: str = "", - i: str = r"", --): -- ... -- -- --def spaces2(result=_core.Value(None)): -- ... -+): ... -+def spaces2(result=_core.Value(None)): ... - +@@ -59,15 +60,15 @@ something = { # fmt: off @@ -237,7 +225,7 @@ d={'a':1, # fmt: on goes + here, andhere, -@@ -122,8 +119,10 @@ +@@ -118,8 +119,10 @@ """ # fmt: off @@ -249,7 +237,7 @@ d={'a':1, # fmt: on pass -@@ -138,7 +137,7 @@ +@@ -134,7 +137,7 @@ now . considers . multiple . fmt . directives . within . one . prefix # fmt: on # fmt: off @@ -258,7 +246,7 @@ d={'a':1, # fmt: on -@@ -178,14 +177,18 @@ +@@ -174,14 +177,18 @@ $ """, # fmt: off @@ -570,12 +558,8 @@ def spaces_types( g: int = 1 if False else 2, h: str = "", i: str = r"", -): - ... - - -def spaces2(result=_core.Value(None)): - ... +): ... +def spaces2(result=_core.Value(None)): ... something = { @@ -739,5 +723,3 @@ l=[1,2,3] d={'a':1, 'b':2} ``` - - diff --git a/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__fmtonoff5.py.snap b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__fmtonoff5.py.snap index 9e58c8142ef79..37aedd195561c 100644 --- a/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__fmtonoff5.py.snap +++ b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__fmtonoff5.py.snap @@ -110,17 +110,7 @@ elif unformatted: }, ) -@@ -72,8 +71,7 @@ - - - class Factory(t.Protocol): -- def this_will_be_formatted(self, **kwargs) -> Named: -- ... -+ def this_will_be_formatted(self, **kwargs) -> Named: ... - - # fmt: on - -@@ -82,6 +80,6 @@ +@@ -81,6 +80,6 @@ if x: return x # fmt: off @@ -297,8 +287,7 @@ class Named(t.Protocol): class Factory(t.Protocol): - def this_will_be_formatted(self, **kwargs) -> Named: - ... + def this_will_be_formatted(self, **kwargs) -> Named: ... # fmt: on @@ -311,5 +300,3 @@ elif unformatted: # fmt: on will_be_formatted() ``` - - diff --git a/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__fmtskip9.py.snap b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__fmtskip9.py.snap new file mode 100644 index 0000000000000..bb8af9d4231ab --- /dev/null +++ b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__fmtskip9.py.snap @@ -0,0 +1,36 @@ +--- +source: crates/ruff_python_formatter/tests/fixtures.rs +input_file: crates/ruff_python_formatter/resources/test/fixtures/black/cases/fmtskip9.py +--- +## Input + +```python +print () # fmt: skip +print () # fmt:skip +``` + +## Black Differences + +```diff +--- Black ++++ Ruff +@@ -1,2 +1,2 @@ +-print () # fmt: skip +-print () # fmt:skip ++print () # fmt: skip ++print () # fmt:skip +``` + +## Ruff Output + +```python +print () # fmt: skip +print () # fmt:skip +``` + +## Black Output + +```python +print () # fmt: skip +print () # fmt:skip +``` diff --git a/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__form_feeds.py.snap b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__form_feeds.py.snap new file mode 100644 index 0000000000000..23d9765fce4d6 --- /dev/null +++ b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__form_feeds.py.snap @@ -0,0 +1,456 @@ +--- +source: crates/ruff_python_formatter/tests/fixtures.rs +input_file: crates/ruff_python_formatter/resources/test/fixtures/black/cases/form_feeds.py +--- +## Input + +```python +# Warning! This file contains form feeds (ASCII 0x0C, often represented by \f or ^L). +# These may be invisible in your editor: ensure you can see them before making changes here. + +# There's one at the start that'll get stripped + +# Comment and statement processing is different enough that we'll test variations of both +# contexts here + +# + + +# + + +# + + + +# + + + +# + + + +# + + +# + + + +# + +# + +# + + \ +# +pass + +pass + + +pass + + +pass + + + +pass + + + +pass + + + +pass + + +pass + + + +pass + +pass + +pass + + +# form feed after a dedent +def foo(): + pass + +pass + + +# form feeds are prohibited inside blocks, or on a line with nonwhitespace + def bar( a = 1 ,b : bool = False ) : + + + pass + + +class Baz: + + def __init__(self): + pass + + + def something(self): + pass + + + +# +pass +pass # +a = 1 + # + pass + a = 1 + +a = [ + +] + +# as internal whitespace of a comment is allowed but why +"form feed literal in a string is okay " + +# form feeds at the very end get removed. +``` + +## Black Differences + +```diff +--- Black ++++ Ruff +@@ -5,62 +5,62 @@ + + # Comment and statement processing is different enough that we'll test variations of both + # contexts here +- ++ + # + +- ++ + # + +- ++ + # + +- ++ + # + +- ++ + # + +- ++ + # + +- ++ + # + +- ++ + # +- ++ + # +- ++ + # + + # + pass +- ++ + pass + +- ++ + pass + +- ++ + pass + +- ++ + pass + +- ++ + pass + +- ++ + pass + +- ++ + pass + +- ++ + pass +- ++ + pass +- ++ + pass + + +@@ -68,25 +68,23 @@ + def foo(): + pass + +- ++ + pass + + + # form feeds are prohibited inside blocks, or on a line with nonwhitespace + def bar(a=1, b: bool = False): +- + pass + + + class Baz: +- + def __init__(self): + pass + + def something(self): + pass + +- ++ + # + pass + pass # +``` + +## Ruff Output + +```python +# Warning! This file contains form feeds (ASCII 0x0C, often represented by \f or ^L). +# These may be invisible in your editor: ensure you can see them before making changes here. + +# There's one at the start that'll get stripped + +# Comment and statement processing is different enough that we'll test variations of both +# contexts here + +# + + +# + + +# + + +# + + +# + + +# + + +# + + +# + +# + +# + +# +pass + +pass + + +pass + + +pass + + +pass + + +pass + + +pass + + +pass + + +pass + +pass + +pass + + +# form feed after a dedent +def foo(): + pass + + +pass + + +# form feeds are prohibited inside blocks, or on a line with nonwhitespace +def bar(a=1, b: bool = False): + pass + + +class Baz: + def __init__(self): + pass + + def something(self): + pass + + +# +pass +pass # +a = 1 +# +pass +a = 1 + +a = [] + +# as internal whitespace of a comment is allowed but why +"form feed literal in a string is okay " + +# form feeds at the very end get removed. +``` + +## Black Output + +```python +# Warning! This file contains form feeds (ASCII 0x0C, often represented by \f or ^L). +# These may be invisible in your editor: ensure you can see them before making changes here. + +# There's one at the start that'll get stripped + +# Comment and statement processing is different enough that we'll test variations of both +# contexts here + +# + + +# + + +# + + +# + + +# + + +# + + +# + + +# + +# + +# + +# +pass + +pass + + +pass + + +pass + + +pass + + +pass + + +pass + + +pass + + +pass + +pass + +pass + + +# form feed after a dedent +def foo(): + pass + + +pass + + +# form feeds are prohibited inside blocks, or on a line with nonwhitespace +def bar(a=1, b: bool = False): + + pass + + +class Baz: + + def __init__(self): + pass + + def something(self): + pass + + +# +pass +pass # +a = 1 +# +pass +a = 1 + +a = [] + +# as internal whitespace of a comment is allowed but why +"form feed literal in a string is okay " + +# form feeds at the very end get removed. +``` diff --git a/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__function.py.snap b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__function.py.snap index 37a465d6dbf38..83dfa7e0b8d1d 100644 --- a/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__function.py.snap +++ b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__function.py.snap @@ -107,15 +107,8 @@ def __await__(): return (yield) ```diff --- Black +++ Ruff -@@ -59,12 +59,10 @@ - g: int = 1 if False else 2, - h: str = "", - i: str = r"", --): -- ... -- -- -+): ... +@@ -62,6 +62,7 @@ + ): ... def spaces2(result=_core.Value(None)): assert fut is self._read_fut, (fut, self._read_fut) + # EMPTY LINE WITH WHITESPACE (this comment will be removed) @@ -339,10 +332,7 @@ def spaces_types( g: int = 1 if False else 2, h: str = "", i: str = r"", -): - ... - - +): ... def spaces2(result=_core.Value(None)): assert fut is self._read_fut, (fut, self._read_fut) @@ -427,5 +417,3 @@ def f( def __await__(): return (yield) ``` - - diff --git a/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__keep_newline_after_match.py.snap b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__keep_newline_after_match.py.snap new file mode 100644 index 0000000000000..5e015547895b6 --- /dev/null +++ b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__keep_newline_after_match.py.snap @@ -0,0 +1,96 @@ +--- +source: crates/ruff_python_formatter/tests/fixtures.rs +input_file: crates/ruff_python_formatter/resources/test/fixtures/black/cases/keep_newline_after_match.py +--- +## Input + +```python +def http_status(status): + + match status: + + case 400: + + return "Bad request" + + case 401: + + return "Unauthorized" + + case 403: + + return "Forbidden" + + case 404: + + return "Not found" +``` + +## Black Differences + +```diff +--- Black ++++ Ruff +@@ -1,19 +1,13 @@ + def http_status(status): +- + match status: +- + case 400: +- + return "Bad request" + + case 401: +- + return "Unauthorized" + + case 403: +- + return "Forbidden" + + case 404: +- + return "Not found" +``` + +## Ruff Output + +```python +def http_status(status): + match status: + case 400: + return "Bad request" + + case 401: + return "Unauthorized" + + case 403: + return "Forbidden" + + case 404: + return "Not found" +``` + +## Black Output + +```python +def http_status(status): + + match status: + + case 400: + + return "Bad request" + + case 401: + + return "Unauthorized" + + case 403: + + return "Forbidden" + + case 404: + + return "Not found" +``` diff --git a/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__long_strings_flag_disabled.py.snap b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__long_strings_flag_disabled.py.snap index f397065d46b89..97505058f4bf0 100644 --- a/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__long_strings_flag_disabled.py.snap +++ b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__long_strings_flag_disabled.py.snap @@ -50,8 +50,10 @@ D4 = { % ( "formatted", "string", - ): "This is a really really really long string that has to go inside of a dictionary. It is %s bad (#%d)." - % ("soooo", 2), + ): ( + "This is a really really really long string that has to go inside of a dictionary. It is %s bad (#%d)." + % ("soooo", 2) + ), } func_with_keywords( @@ -261,10 +263,12 @@ annotated_variable: Final = ( + CONCATENATED + "using the '+' operator." ) -annotated_variable: Final = "This is a large string that has a type annotation attached to it. A type annotation should NOT stop a long string from being wrapped." -annotated_variable: Literal[ - "fakse_literal" -] = "This is a large string that has a type annotation attached to it. A type annotation should NOT stop a long string from being wrapped." +annotated_variable: Final = ( + "This is a large string that has a type annotation attached to it. A type annotation should NOT stop a long string from being wrapped." +) +annotated_variable: Literal["fakse_literal"] = ( + "This is a large string that has a type annotation attached to it. A type annotation should NOT stop a long string from being wrapped." +) backslashes = "This is a really long string with \"embedded\" double quotes and 'single' quotes that also handles checking for an even number of backslashes \\" backslashes = "This is a really long string with \"embedded\" double quotes and 'single' quotes that also handles checking for an even number of backslashes \\\\" @@ -304,7 +308,7 @@ long_unmergable_string_with_pragma = ( ```diff --- Black +++ Ruff -@@ -165,13 +165,9 @@ +@@ -167,13 +167,9 @@ triple_quote_string = """This is a really really really long triple quote string assignment and it should not be touched.""" @@ -320,19 +324,17 @@ long_unmergable_string_with_pragma = ( "formatting" ) -@@ -255,9 +251,9 @@ +@@ -256,9 +252,7 @@ + + CONCATENATED + "using the '+' operator." ) - annotated_variable: Final = "This is a large string that has a type annotation attached to it. A type annotation should NOT stop a long string from being wrapped." --annotated_variable: Literal[ -- "fakse_literal" --] = "This is a large string that has a type annotation attached to it. A type annotation should NOT stop a long string from being wrapped." -+annotated_variable: Literal["fakse_literal"] = ( -+ "This is a large string that has a type annotation attached to it. A type annotation should NOT stop a long string from being wrapped." -+) - - backslashes = "This is a really long string with \"embedded\" double quotes and 'single' quotes that also handles checking for an even number of backslashes \\" - backslashes = "This is a really long string with \"embedded\" double quotes and 'single' quotes that also handles checking for an even number of backslashes \\\\" +-annotated_variable: Final = ( +- "This is a large string that has a type annotation attached to it. A type annotation should NOT stop a long string from being wrapped." +-) ++annotated_variable: Final = "This is a large string that has a type annotation attached to it. A type annotation should NOT stop a long string from being wrapped." + annotated_variable: Literal["fakse_literal"] = ( + "This is a large string that has a type annotation attached to it. A type annotation should NOT stop a long string from being wrapped." + ) ``` ## Ruff Output @@ -383,8 +385,10 @@ D4 = { % ( "formatted", "string", - ): "This is a really really really long string that has to go inside of a dictionary. It is %s bad (#%d)." - % ("soooo", 2), + ): ( + "This is a really really really long string that has to go inside of a dictionary. It is %s bad (#%d)." + % ("soooo", 2) + ), } func_with_keywords( @@ -676,8 +680,10 @@ D4 = { % ( "formatted", "string", - ): "This is a really really really long string that has to go inside of a dictionary. It is %s bad (#%d)." - % ("soooo", 2), + ): ( + "This is a really really really long string that has to go inside of a dictionary. It is %s bad (#%d)." + % ("soooo", 2) + ), } func_with_keywords( @@ -887,10 +893,12 @@ annotated_variable: Final = ( + CONCATENATED + "using the '+' operator." ) -annotated_variable: Final = "This is a large string that has a type annotation attached to it. A type annotation should NOT stop a long string from being wrapped." -annotated_variable: Literal[ - "fakse_literal" -] = "This is a large string that has a type annotation attached to it. A type annotation should NOT stop a long string from being wrapped." +annotated_variable: Final = ( + "This is a large string that has a type annotation attached to it. A type annotation should NOT stop a long string from being wrapped." +) +annotated_variable: Literal["fakse_literal"] = ( + "This is a large string that has a type annotation attached to it. A type annotation should NOT stop a long string from being wrapped." +) backslashes = "This is a really long string with \"embedded\" double quotes and 'single' quotes that also handles checking for an even number of backslashes \\" backslashes = "This is a really long string with \"embedded\" double quotes and 'single' quotes that also handles checking for an even number of backslashes \\\\" @@ -924,5 +932,3 @@ long_unmergable_string_with_pragma = ( " of it." ) ``` - - diff --git a/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__no_blank_line_before_docstring.py.snap b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__no_blank_line_before_docstring.py.snap new file mode 100644 index 0000000000000..5d175b9b9b37f --- /dev/null +++ b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__no_blank_line_before_docstring.py.snap @@ -0,0 +1,121 @@ +--- +source: crates/ruff_python_formatter/tests/fixtures.rs +input_file: crates/ruff_python_formatter/resources/test/fixtures/black/cases/no_blank_line_before_docstring.py +--- +## Input + +```python +def line_before_docstring(): + + """Please move me up""" + + +class LineBeforeDocstring: + + """Please move me up""" + + +class EvenIfThereIsAMethodAfter: + + """I'm the docstring""" + def method(self): + pass + + +class TwoLinesBeforeDocstring: + + + """I want to be treated the same as if I were closer""" + + +class MultilineDocstringsAsWell: + + """I'm so far + + and on so many lines... + """ + +class SingleQuotedDocstring: + + "I'm a docstring but I don't even get triple quotes." +``` + +## Black Differences + +```diff +--- Black ++++ Ruff +@@ -25,5 +25,4 @@ + + + class SingleQuotedDocstring: +- + "I'm a docstring but I don't even get triple quotes." +``` + +## Ruff Output + +```python +def line_before_docstring(): + """Please move me up""" + + +class LineBeforeDocstring: + """Please move me up""" + + +class EvenIfThereIsAMethodAfter: + """I'm the docstring""" + + def method(self): + pass + + +class TwoLinesBeforeDocstring: + """I want to be treated the same as if I were closer""" + + +class MultilineDocstringsAsWell: + """I'm so far + + and on so many lines... + """ + + +class SingleQuotedDocstring: + "I'm a docstring but I don't even get triple quotes." +``` + +## Black Output + +```python +def line_before_docstring(): + """Please move me up""" + + +class LineBeforeDocstring: + """Please move me up""" + + +class EvenIfThereIsAMethodAfter: + """I'm the docstring""" + + def method(self): + pass + + +class TwoLinesBeforeDocstring: + """I want to be treated the same as if I were closer""" + + +class MultilineDocstringsAsWell: + """I'm so far + + and on so many lines... + """ + + +class SingleQuotedDocstring: + + "I'm a docstring but I don't even get triple quotes." +``` diff --git a/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__pattern_matching_trailing_comma.py.snap b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__pattern_matching_trailing_comma.py.snap new file mode 100644 index 0000000000000..8800ffc4edee4 --- /dev/null +++ b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__pattern_matching_trailing_comma.py.snap @@ -0,0 +1,102 @@ +--- +source: crates/ruff_python_formatter/tests/fixtures.rs +input_file: crates/ruff_python_formatter/resources/test/fixtures/black/cases/pattern_matching_trailing_comma.py +--- +## Input + +```python +match maybe, multiple: + case perhaps, 5: + pass + case perhaps, 6,: + pass + + +match more := (than, one), indeed,: + case _, (5, 6): + pass + case [[5], (6)], [7],: + pass + case _: + pass +``` + +## Black Differences + +```diff +--- Black ++++ Ruff +@@ -8,13 +8,16 @@ + pass + + +-match more := (than, one), indeed,: ++match ( ++ more := (than, one), ++ indeed, ++): + case _, (5, 6): + pass +- case ( ++ case [ + [[5], (6)], + [7], +- ): ++ ]: + pass + case _: + pass +``` + +## Ruff Output + +```python +match maybe, multiple: + case perhaps, 5: + pass + case ( + perhaps, + 6, + ): + pass + + +match ( + more := (than, one), + indeed, +): + case _, (5, 6): + pass + case [ + [[5], (6)], + [7], + ]: + pass + case _: + pass +``` + +## Black Output + +```python +match maybe, multiple: + case perhaps, 5: + pass + case ( + perhaps, + 6, + ): + pass + + +match more := (than, one), indeed,: + case _, (5, 6): + pass + case ( + [[5], (6)], + [7], + ): + pass + case _: + pass +``` diff --git a/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__pep_572_py310.py.snap b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__pep_572_py310.py.snap deleted file mode 100644 index a49397f429243..0000000000000 --- a/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__pep_572_py310.py.snap +++ /dev/null @@ -1,83 +0,0 @@ ---- -source: crates/ruff_python_formatter/tests/fixtures.rs -input_file: crates/ruff_python_formatter/resources/test/fixtures/black/cases/pep_572_py310.py ---- -## Input - -```python -# Unparenthesized walruses are now allowed in indices since Python 3.10. -x[a:=0] -x[a:=0, b:=1] -x[5, b:=0] - -# Walruses are allowed inside generator expressions on function calls since 3.10. -if any(match := pattern_error.match(s) for s in buffer): - if match.group(2) == data_not_available: - # Error OK to ignore. - pass - -f(a := b + c for c in range(10)) -f((a := b + c for c in range(10)), x) -f(y=(a := b + c for c in range(10))) -f(x, (a := b + c for c in range(10)), y=z, **q) -``` - -## Black Differences - -```diff ---- Black -+++ Ruff -@@ -1,7 +1,7 @@ - # Unparenthesized walruses are now allowed in indices since Python 3.10. --x[a:=0] --x[a:=0, b:=1] --x[5, b:=0] -+x[a := 0] -+x[a := 0, b := 1] -+x[5, b := 0] - - # Walruses are allowed inside generator expressions on function calls since 3.10. - if any(match := pattern_error.match(s) for s in buffer): -``` - -## Ruff Output - -```python -# Unparenthesized walruses are now allowed in indices since Python 3.10. -x[a := 0] -x[a := 0, b := 1] -x[5, b := 0] - -# Walruses are allowed inside generator expressions on function calls since 3.10. -if any(match := pattern_error.match(s) for s in buffer): - if match.group(2) == data_not_available: - # Error OK to ignore. - pass - -f(a := b + c for c in range(10)) -f((a := b + c for c in range(10)), x) -f(y=(a := b + c for c in range(10))) -f(x, (a := b + c for c in range(10)), y=z, **q) -``` - -## Black Output - -```python -# Unparenthesized walruses are now allowed in indices since Python 3.10. -x[a:=0] -x[a:=0, b:=1] -x[5, b:=0] - -# Walruses are allowed inside generator expressions on function calls since 3.10. -if any(match := pattern_error.match(s) for s in buffer): - if match.group(2) == data_not_available: - # Error OK to ignore. - pass - -f(a := b + c for c in range(10)) -f((a := b + c for c in range(10)), x) -f(y=(a := b + c for c in range(10))) -f(x, (a := b + c for c in range(10)), y=z, **q) -``` - - diff --git a/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__pep_572_remove_parens.py.snap b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__pep_572_remove_parens.py.snap deleted file mode 100644 index 8ba666a838056..0000000000000 --- a/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__pep_572_remove_parens.py.snap +++ /dev/null @@ -1,258 +0,0 @@ ---- -source: crates/ruff_python_formatter/tests/fixtures.rs -input_file: crates/ruff_python_formatter/resources/test/fixtures/black/cases/pep_572_remove_parens.py ---- -## Input - -```python -if (foo := 0): - pass - -if (foo := 1): - pass - -if (y := 5 + 5): - pass - -y = (x := 0) - -y += (x := 0) - -(y := 5 + 5) - -test: int = (test2 := 2) - -a, b = (test := (1, 2)) - -# see also https://github.com/psf/black/issues/2139 -assert (foo := 42 - 12) - -foo(x=(y := f(x))) - - -def foo(answer=(p := 42)): - ... - - -def foo2(answer: (p := 42) = 5): - ... - - -lambda: (x := 1) - -a[(x := 12)] -a[:(x := 13)] - -# we don't touch expressions in f-strings but if we do one day, don't break 'em -f'{(x:=10)}' - - -def a(): - return (x := 3) - await (b := 1) - yield (a := 2) - raise (c := 3) - -def this_is_so_dumb() -> (please := no): - pass - -async def await_the_walrus(): - with (x := y): - pass - - with (x := y) as z, (a := b) as c: - pass - - with (x := await y): - pass - - with (x := await a, y := await b): - pass - - with ((x := await a, y := await b)): - pass - - with (x := await a), (y := await b): - pass -``` - -## Black Differences - -```diff ---- Black -+++ Ruff -@@ -23,18 +23,16 @@ - foo(x=(y := f(x))) - - --def foo(answer=(p := 42)): -- ... -+def foo(answer=(p := 42)): ... - - --def foo2(answer: (p := 42) = 5): -- ... -+def foo2(answer: (p := 42) = 5): ... - - - lambda: (x := 1) - - a[(x := 12)] --a[:(x := 13)] -+a[: (x := 13)] - - # we don't touch expressions in f-strings but if we do one day, don't break 'em - f"{(x:=10)}" -``` - -## Ruff Output - -```python -if foo := 0: - pass - -if foo := 1: - pass - -if y := 5 + 5: - pass - -y = (x := 0) - -y += (x := 0) - -(y := 5 + 5) - -test: int = (test2 := 2) - -a, b = (test := (1, 2)) - -# see also https://github.com/psf/black/issues/2139 -assert (foo := 42 - 12) - -foo(x=(y := f(x))) - - -def foo(answer=(p := 42)): ... - - -def foo2(answer: (p := 42) = 5): ... - - -lambda: (x := 1) - -a[(x := 12)] -a[: (x := 13)] - -# we don't touch expressions in f-strings but if we do one day, don't break 'em -f"{(x:=10)}" - - -def a(): - return (x := 3) - await (b := 1) - yield (a := 2) - raise (c := 3) - - -def this_is_so_dumb() -> (please := no): - pass - - -async def await_the_walrus(): - with (x := y): - pass - - with (x := y) as z, (a := b) as c: - pass - - with (x := await y): - pass - - with (x := await a, y := await b): - pass - - with (x := await a, y := await b): - pass - - with (x := await a), (y := await b): - pass -``` - -## Black Output - -```python -if foo := 0: - pass - -if foo := 1: - pass - -if y := 5 + 5: - pass - -y = (x := 0) - -y += (x := 0) - -(y := 5 + 5) - -test: int = (test2 := 2) - -a, b = (test := (1, 2)) - -# see also https://github.com/psf/black/issues/2139 -assert (foo := 42 - 12) - -foo(x=(y := f(x))) - - -def foo(answer=(p := 42)): - ... - - -def foo2(answer: (p := 42) = 5): - ... - - -lambda: (x := 1) - -a[(x := 12)] -a[:(x := 13)] - -# we don't touch expressions in f-strings but if we do one day, don't break 'em -f"{(x:=10)}" - - -def a(): - return (x := 3) - await (b := 1) - yield (a := 2) - raise (c := 3) - - -def this_is_so_dumb() -> (please := no): - pass - - -async def await_the_walrus(): - with (x := y): - pass - - with (x := y) as z, (a := b) as c: - pass - - with (x := await y): - pass - - with (x := await a, y := await b): - pass - - with (x := await a, y := await b): - pass - - with (x := await a), (y := await b): - pass -``` - - diff --git a/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__prefer_rhs_split.py.snap b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__prefer_rhs_split.py.snap new file mode 100644 index 0000000000000..d20c2a8d20456 --- /dev/null +++ b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__prefer_rhs_split.py.snap @@ -0,0 +1,352 @@ +--- +source: crates/ruff_python_formatter/tests/fixtures.rs +input_file: crates/ruff_python_formatter/resources/test/fixtures/black/cases/prefer_rhs_split.py +--- +## Input + +```python +first_item, second_item = ( + some_looooooooong_module.some_looooooooooooooong_function_name( + first_argument, second_argument, third_argument + ) +) + +some_dict["with_a_long_key"] = ( + some_looooooooong_module.some_looooooooooooooong_function_name( + first_argument, second_argument, third_argument + ) +) + +# Make sure it works when the RHS only has one pair of (optional) parens. +first_item, second_item = ( + some_looooooooong_module.SomeClass.some_looooooooooooooong_variable_name +) + +some_dict["with_a_long_key"] = ( + some_looooooooong_module.SomeClass.some_looooooooooooooong_variable_name +) + +# Make sure chaining assignments work. +first_item, second_item, third_item, forth_item = m["everything"] = ( + some_looooooooong_module.some_looooooooooooooong_function_name( + first_argument, second_argument, third_argument + ) +) + +# Make sure when the RHS's first split at the non-optional paren fits, +# we split there instead of the outer RHS optional paren. +first_item, second_item = some_looooooooong_module.some_loooooog_function_name( + first_argument, second_argument, third_argument +) + +( + first_item, + second_item, + third_item, + forth_item, + fifth_item, + last_item_very_loooooong, +) = some_looooooooong_module.some_looooooooooooooong_function_name( + first_argument, second_argument, third_argument +) + +( + first_item, + second_item, + third_item, + forth_item, + fifth_item, + last_item_very_loooooong, +) = everything = some_looooong_function_name( + first_argument, second_argument, third_argument +) + + +# Make sure unsplittable type ignore won't be moved. +some_kind_of_table[some_key] = util.some_function( # type: ignore # noqa: E501 + some_arg +).intersection(pk_cols) + +some_kind_of_table[ + some_key +] = lambda obj: obj.some_long_named_method() # type: ignore # noqa: E501 + +some_kind_of_table[ + some_key # type: ignore # noqa: E501 +] = lambda obj: obj.some_long_named_method() + + +# Make when when the left side of assignment plus the opening paren "... = (" is +# exactly line length limit + 1, it won't be split like that. +xxxxxxxxx_yyy_zzzzzzzz[ + xx.xxxxxx(x_yyy_zzzzzz.xxxxx[0]), x_yyy_zzzzzz.xxxxxx(xxxx=1) +] = 1 + + +# Right side of assignment contains un-nested pairs of inner parens. +some_kind_of_instance.some_kind_of_map[a_key] = ( + isinstance(some_var, SomeClass) + and table.something_and_something != table.something_else +) or ( + isinstance(some_other_var, BaseClass) and table.something != table.some_other_thing +) + +# Multiple targets +a = b = ( + ccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc +) + +a = b = c = d = e = f = g = ( + hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh +) = i = j = ( + kkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkk +) + +a = ( + bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb +) = c + +a = ( + bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb +) = ( + cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc +) = ddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd +``` + +## Black Differences + +```diff +--- Black ++++ Ruff +@@ -60,9 +60,7 @@ + some_arg + ).intersection(pk_cols) + +-some_kind_of_table[ +- some_key +-] = lambda obj: obj.some_long_named_method() # type: ignore # noqa: E501 ++some_kind_of_table[some_key] = lambda obj: obj.some_long_named_method() # type: ignore # noqa: E501 + + some_kind_of_table[ + some_key # type: ignore # noqa: E501 +``` + +## Ruff Output + +```python +first_item, second_item = ( + some_looooooooong_module.some_looooooooooooooong_function_name( + first_argument, second_argument, third_argument + ) +) + +some_dict["with_a_long_key"] = ( + some_looooooooong_module.some_looooooooooooooong_function_name( + first_argument, second_argument, third_argument + ) +) + +# Make sure it works when the RHS only has one pair of (optional) parens. +first_item, second_item = ( + some_looooooooong_module.SomeClass.some_looooooooooooooong_variable_name +) + +some_dict["with_a_long_key"] = ( + some_looooooooong_module.SomeClass.some_looooooooooooooong_variable_name +) + +# Make sure chaining assignments work. +first_item, second_item, third_item, forth_item = m["everything"] = ( + some_looooooooong_module.some_looooooooooooooong_function_name( + first_argument, second_argument, third_argument + ) +) + +# Make sure when the RHS's first split at the non-optional paren fits, +# we split there instead of the outer RHS optional paren. +first_item, second_item = some_looooooooong_module.some_loooooog_function_name( + first_argument, second_argument, third_argument +) + +( + first_item, + second_item, + third_item, + forth_item, + fifth_item, + last_item_very_loooooong, +) = some_looooooooong_module.some_looooooooooooooong_function_name( + first_argument, second_argument, third_argument +) + +( + first_item, + second_item, + third_item, + forth_item, + fifth_item, + last_item_very_loooooong, +) = everything = some_looooong_function_name( + first_argument, second_argument, third_argument +) + + +# Make sure unsplittable type ignore won't be moved. +some_kind_of_table[some_key] = util.some_function( # type: ignore # noqa: E501 + some_arg +).intersection(pk_cols) + +some_kind_of_table[some_key] = lambda obj: obj.some_long_named_method() # type: ignore # noqa: E501 + +some_kind_of_table[ + some_key # type: ignore # noqa: E501 +] = lambda obj: obj.some_long_named_method() + + +# Make when when the left side of assignment plus the opening paren "... = (" is +# exactly line length limit + 1, it won't be split like that. +xxxxxxxxx_yyy_zzzzzzzz[ + xx.xxxxxx(x_yyy_zzzzzz.xxxxx[0]), x_yyy_zzzzzz.xxxxxx(xxxx=1) +] = 1 + + +# Right side of assignment contains un-nested pairs of inner parens. +some_kind_of_instance.some_kind_of_map[a_key] = ( + isinstance(some_var, SomeClass) + and table.something_and_something != table.something_else +) or ( + isinstance(some_other_var, BaseClass) and table.something != table.some_other_thing +) + +# Multiple targets +a = b = ( + ccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc +) + +a = b = c = d = e = f = g = ( + hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh +) = i = j = ( + kkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkk +) + +a = ( + bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb +) = c + +a = ( + bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb +) = ( + cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc +) = ddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd +``` + +## Black Output + +```python +first_item, second_item = ( + some_looooooooong_module.some_looooooooooooooong_function_name( + first_argument, second_argument, third_argument + ) +) + +some_dict["with_a_long_key"] = ( + some_looooooooong_module.some_looooooooooooooong_function_name( + first_argument, second_argument, third_argument + ) +) + +# Make sure it works when the RHS only has one pair of (optional) parens. +first_item, second_item = ( + some_looooooooong_module.SomeClass.some_looooooooooooooong_variable_name +) + +some_dict["with_a_long_key"] = ( + some_looooooooong_module.SomeClass.some_looooooooooooooong_variable_name +) + +# Make sure chaining assignments work. +first_item, second_item, third_item, forth_item = m["everything"] = ( + some_looooooooong_module.some_looooooooooooooong_function_name( + first_argument, second_argument, third_argument + ) +) + +# Make sure when the RHS's first split at the non-optional paren fits, +# we split there instead of the outer RHS optional paren. +first_item, second_item = some_looooooooong_module.some_loooooog_function_name( + first_argument, second_argument, third_argument +) + +( + first_item, + second_item, + third_item, + forth_item, + fifth_item, + last_item_very_loooooong, +) = some_looooooooong_module.some_looooooooooooooong_function_name( + first_argument, second_argument, third_argument +) + +( + first_item, + second_item, + third_item, + forth_item, + fifth_item, + last_item_very_loooooong, +) = everything = some_looooong_function_name( + first_argument, second_argument, third_argument +) + + +# Make sure unsplittable type ignore won't be moved. +some_kind_of_table[some_key] = util.some_function( # type: ignore # noqa: E501 + some_arg +).intersection(pk_cols) + +some_kind_of_table[ + some_key +] = lambda obj: obj.some_long_named_method() # type: ignore # noqa: E501 + +some_kind_of_table[ + some_key # type: ignore # noqa: E501 +] = lambda obj: obj.some_long_named_method() + + +# Make when when the left side of assignment plus the opening paren "... = (" is +# exactly line length limit + 1, it won't be split like that. +xxxxxxxxx_yyy_zzzzzzzz[ + xx.xxxxxx(x_yyy_zzzzzz.xxxxx[0]), x_yyy_zzzzzz.xxxxxx(xxxx=1) +] = 1 + + +# Right side of assignment contains un-nested pairs of inner parens. +some_kind_of_instance.some_kind_of_map[a_key] = ( + isinstance(some_var, SomeClass) + and table.something_and_something != table.something_else +) or ( + isinstance(some_other_var, BaseClass) and table.something != table.some_other_thing +) + +# Multiple targets +a = b = ( + ccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc +) + +a = b = c = d = e = f = g = ( + hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh +) = i = j = ( + kkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkk +) + +a = ( + bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb +) = c + +a = ( + bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb +) = ( + cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc +) = ddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd +``` diff --git a/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__preview_cantfit.py.snap b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__preview_cantfit.py.snap index c22c4b6ea4a72..1d93012d9b90d 100644 --- a/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__preview_cantfit.py.snap +++ b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__preview_cantfit.py.snap @@ -26,12 +26,6 @@ normal_name = but_the_function_name_is_now_ridiculously_long_and_it_is_still_sup normal_name = but_the_function_name_is_now_ridiculously_long_and_it_is_still_super_annoying( [1, 2, 3], arg1, [1, 2, 3], arg2, [1, 2, 3], arg3 ) -# long arguments -normal_name = normal_function_name( - "but with super long string arguments that on their own exceed the line limit so there's no way it can ever fit", - "eggs with spam and eggs and spam with eggs with spam and eggs and spam with eggs with spam and eggs and spam with eggs", - this_is_a_ridiculously_long_name_and_nobody_in_their_right_mind_would_use_one_like_it=0, -) string_variable_name = ( "a string that is waaaaaaaayyyyyyyy too long, even in parens, there's nothing you can do" # noqa ) @@ -73,19 +67,6 @@ del concatenated_strings, string_variable_name, normal_function_name, normal_nam this_is_a_ridiculously_long_name_and_nobody_in_their_right_mind_would_use_one_like_it = function( arg1, arg2, arg3 ) -@@ -35,10 +29,8 @@ - ) - # long arguments - normal_name = normal_function_name( -- "but with super long string arguments that on their own exceed the line limit so" -- " there's no way it can ever fit", -- "eggs with spam and eggs and spam with eggs with spam and eggs and spam with eggs" -- " with spam and eggs and spam with eggs", -+ "but with super long string arguments that on their own exceed the line limit so there's no way it can ever fit", -+ "eggs with spam and eggs and spam with eggs with spam and eggs and spam with eggs with spam and eggs and spam with eggs", - this_is_a_ridiculously_long_name_and_nobody_in_their_right_mind_would_use_one_like_it=0, - ) - string_variable_name = "a string that is waaaaaaaayyyyyyyy too long, even in parens, there's nothing you can do" # noqa ``` ## Ruff Output @@ -120,12 +101,6 @@ normal_name = ( [1, 2, 3], arg1, [1, 2, 3], arg2, [1, 2, 3], arg3 ) ) -# long arguments -normal_name = normal_function_name( - "but with super long string arguments that on their own exceed the line limit so there's no way it can ever fit", - "eggs with spam and eggs and spam with eggs with spam and eggs and spam with eggs with spam and eggs and spam with eggs", - this_is_a_ridiculously_long_name_and_nobody_in_their_right_mind_would_use_one_like_it=0, -) string_variable_name = "a string that is waaaaaaaayyyyyyyy too long, even in parens, there's nothing you can do" # noqa for key in """ hostname @@ -186,14 +161,6 @@ normal_name = ( [1, 2, 3], arg1, [1, 2, 3], arg2, [1, 2, 3], arg3 ) ) -# long arguments -normal_name = normal_function_name( - "but with super long string arguments that on their own exceed the line limit so" - " there's no way it can ever fit", - "eggs with spam and eggs and spam with eggs with spam and eggs and spam with eggs" - " with spam and eggs and spam with eggs", - this_is_a_ridiculously_long_name_and_nobody_in_their_right_mind_would_use_one_like_it=0, -) string_variable_name = "a string that is waaaaaaaayyyyyyyy too long, even in parens, there's nothing you can do" # noqa for key in """ hostname @@ -215,5 +182,3 @@ del ( need_more_to_make_the_line_long_enough, ) ``` - - diff --git a/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__preview_cantfit_string.py.snap b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__preview_cantfit_string.py.snap new file mode 100644 index 0000000000000..84a256884497d --- /dev/null +++ b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__preview_cantfit_string.py.snap @@ -0,0 +1,56 @@ +--- +source: crates/ruff_python_formatter/tests/fixtures.rs +input_file: crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_cantfit_string.py +--- +## Input + +```python +# long arguments +normal_name = normal_function_name( + "but with super long string arguments that on their own exceed the line limit so there's no way it can ever fit", + "eggs with spam and eggs and spam with eggs with spam and eggs and spam with eggs with spam and eggs and spam with eggs", + this_is_a_ridiculously_long_name_and_nobody_in_their_right_mind_would_use_one_like_it=0, +) +``` + +## Black Differences + +```diff +--- Black ++++ Ruff +@@ -1,8 +1,6 @@ + # long arguments + normal_name = normal_function_name( +- "but with super long string arguments that on their own exceed the line limit so" +- " there's no way it can ever fit", +- "eggs with spam and eggs and spam with eggs with spam and eggs and spam with eggs" +- " with spam and eggs and spam with eggs", ++ "but with super long string arguments that on their own exceed the line limit so there's no way it can ever fit", ++ "eggs with spam and eggs and spam with eggs with spam and eggs and spam with eggs with spam and eggs and spam with eggs", + this_is_a_ridiculously_long_name_and_nobody_in_their_right_mind_would_use_one_like_it=0, + ) +``` + +## Ruff Output + +```python +# long arguments +normal_name = normal_function_name( + "but with super long string arguments that on their own exceed the line limit so there's no way it can ever fit", + "eggs with spam and eggs and spam with eggs with spam and eggs and spam with eggs with spam and eggs and spam with eggs", + this_is_a_ridiculously_long_name_and_nobody_in_their_right_mind_would_use_one_like_it=0, +) +``` + +## Black Output + +```python +# long arguments +normal_name = normal_function_name( + "but with super long string arguments that on their own exceed the line limit so" + " there's no way it can ever fit", + "eggs with spam and eggs and spam with eggs with spam and eggs and spam with eggs" + " with spam and eggs and spam with eggs", + this_is_a_ridiculously_long_name_and_nobody_in_their_right_mind_would_use_one_like_it=0, +) +``` diff --git a/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__preview_hug_parens_with_braces_and_square_brackets.py.snap b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__preview_hug_parens_with_braces_and_square_brackets.py.snap index 34f957ec5dcbf..6460c6bfd3755 100644 --- a/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__preview_hug_parens_with_braces_and_square_brackets.py.snap +++ b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__preview_hug_parens_with_braces_and_square_brackets.py.snap @@ -177,7 +177,94 @@ for foo in ["a", "b"]: ```diff --- Black +++ Ruff -@@ -47,17 +47,21 @@ +@@ -1,43 +1,55 @@ + def foo_brackets(request): +- return JsonResponse({ +- "var_1": foo, +- "var_2": bar, +- }) ++ return JsonResponse( ++ { ++ "var_1": foo, ++ "var_2": bar, ++ } ++ ) + + + def foo_square_brackets(request): +- return JsonResponse([ +- "var_1", +- "var_2", +- ]) ++ return JsonResponse( ++ [ ++ "var_1", ++ "var_2", ++ ] ++ ) + + +-func({ +- "a": 37, +- "b": 42, +- "c": 927, +- "aaaaaaaaaaaaaaaaaaaaaaaaa": 11111111111111111111111111111111111111111, +-}) ++func( ++ { ++ "a": 37, ++ "b": 42, ++ "c": 927, ++ "aaaaaaaaaaaaaaaaaaaaaaaaa": 11111111111111111111111111111111111111111, ++ } ++) + +-func([ +- "random_string_number_one", +- "random_string_number_two", +- "random_string_number_three", +- "random_string_number_four", +-]) ++func( ++ [ ++ "random_string_number_one", ++ "random_string_number_two", ++ "random_string_number_three", ++ "random_string_number_four", ++ ] ++) + +-func({ +- # expand me +- "a": 37, +- "b": 42, +- "c": 927, +-}) ++func( ++ { ++ # expand me ++ "a": 37, ++ "b": 42, ++ "c": 927, ++ } ++) + +-func([ +- "a", +- "b", +- "c", +-]) ++func( ++ [ ++ "a", ++ "b", ++ "c", ++ ] ++) + + func( + [ +@@ -47,17 +59,21 @@ ], ) @@ -209,9 +296,67 @@ for foo in ["a", "b"]: func( # preserve me -@@ -95,11 +99,13 @@ - # preserve me but hug brackets - ]) +@@ -68,38 +84,48 @@ + ] + ) + +-func([ # preserve me but hug brackets +- "c", +- "d", +- "e", +-]) ++func( ++ [ # preserve me but hug brackets ++ "c", ++ "d", ++ "e", ++ ] ++) + +-func([ +- # preserve me but hug brackets +- "c", +- "d", +- "e", +-]) ++func( ++ [ ++ # preserve me but hug brackets ++ "c", ++ "d", ++ "e", ++ ] ++) + +-func([ +- "c", +- # preserve me but hug brackets +- "d", +- "e", +-]) ++func( ++ [ ++ "c", ++ # preserve me but hug brackets ++ "d", ++ "e", ++ ] ++) + +-func([ +- "c", +- "d", +- "e", +- # preserve me but hug brackets +-]) ++func( ++ [ ++ "c", ++ "d", ++ "e", ++ # preserve me but hug brackets ++ ] ++) -func([ - "c", @@ -228,21 +373,28 @@ for foo in ["a", "b"]: func( [ -@@ -111,10 +117,10 @@ +@@ -114,13 +140,15 @@ + func( + [x for x in "long line long line long line long line long line long line long line"] ) - - func([x for x in "short line"]) --func( -- [x for x in "long line long line long line long line long line long line long line"] --) - func([ -+ x for x in "long line long line long line long line long line long line long line" -+]) -+func([ - x - for x in [ +-func([ +- x +- for x in [ ++func( ++ [ x -@@ -131,10 +137,12 @@ +- for x in "long line long line long line long line long line long line long line" ++ for x in [ ++ x ++ for x in "long line long line long line long line long line long line long line" ++ ] + ] +-]) ++) + + foooooooooooooooooooo( + [{c: n + 1 for c in range(256)} for n in range(100)] + [{}], {size} +@@ -131,10 +159,12 @@ ) nested_mapping = { @@ -259,64 +411,135 @@ for foo in ["a", "b"]: } explicit_exploding = [ [ -@@ -164,9 +172,9 @@ - }) +@@ -144,24 +174,34 @@ + ], + ], + ] +-single_item_do_not_explode = Context({ +- "version": get_docs_version(), +-}) ++single_item_do_not_explode = Context( ++ { ++ "version": get_docs_version(), ++ } ++) + +-foo(*[ +- str(i) for i in range(100000000000000000000000000000000000000000000000000000000000) +-]) ++foo( ++ *[ ++ str(i) ++ for i in range(100000000000000000000000000000000000000000000000000000000000) ++ ] ++) + +-foo(**{ +- "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa": 1, +- "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb": 2, +- "ccccccccccccccccccccccccccccccccc": 3, +- **other, +-}) ++foo( ++ **{ ++ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa": 1, ++ "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb": 2, ++ "ccccccccccccccccccccccccccccccccc": 3, ++ **other, ++ } ++) + +-foo(**{ +- x: y for x, y in enumerate(["long long long long line", "long long long long line"]) +-}) ++foo( ++ **{ ++ x: y ++ for x, y in enumerate(["long long long long line", "long long long long line"]) ++ } ++) # Edge case when deciding whether to hug the brackets without inner content. --very_very_very_long_variable = very_very_very_long_module.VeryVeryVeryVeryLongClassName( -- [[]] --) -+very_very_very_long_variable = very_very_very_long_module.VeryVeryVeryVeryLongClassName([ -+ [] -+]) + very_very_very_long_variable = very_very_very_long_module.VeryVeryVeryVeryLongClassName( +@@ -169,11 +209,13 @@ + ) for foo in ["a", "b"]: - output.extend([ +- output.extend([ +- individual +- for +- # Foobar +- container in xs_by_y[foo] +- # Foobar +- for individual in container["nested"] +- ]) ++ output.extend( ++ [ ++ individual ++ for ++ # Foobar ++ container in xs_by_y[foo] ++ # Foobar ++ for individual in container["nested"] ++ ] ++ ) ``` ## Ruff Output ```python def foo_brackets(request): - return JsonResponse({ - "var_1": foo, - "var_2": bar, - }) + return JsonResponse( + { + "var_1": foo, + "var_2": bar, + } + ) def foo_square_brackets(request): - return JsonResponse([ - "var_1", - "var_2", - ]) + return JsonResponse( + [ + "var_1", + "var_2", + ] + ) -func({ - "a": 37, - "b": 42, - "c": 927, - "aaaaaaaaaaaaaaaaaaaaaaaaa": 11111111111111111111111111111111111111111, -}) +func( + { + "a": 37, + "b": 42, + "c": 927, + "aaaaaaaaaaaaaaaaaaaaaaaaa": 11111111111111111111111111111111111111111, + } +) -func([ - "random_string_number_one", - "random_string_number_two", - "random_string_number_three", - "random_string_number_four", -]) +func( + [ + "random_string_number_one", + "random_string_number_two", + "random_string_number_three", + "random_string_number_four", + ] +) -func({ - # expand me - "a": 37, - "b": 42, - "c": 927, -}) +func( + { + # expand me + "a": 37, + "b": 42, + "c": 927, + } +) -func([ - "a", - "b", - "c", -]) +func( + [ + "a", + "b", + "c", + ] +) func( [ @@ -351,32 +574,40 @@ func( ] ) -func([ # preserve me but hug brackets - "c", - "d", - "e", -]) +func( + [ # preserve me but hug brackets + "c", + "d", + "e", + ] +) -func([ - # preserve me but hug brackets - "c", - "d", - "e", -]) +func( + [ + # preserve me but hug brackets + "c", + "d", + "e", + ] +) -func([ - "c", - # preserve me but hug brackets - "d", - "e", -]) +func( + [ + "c", + # preserve me but hug brackets + "d", + "e", + ] +) -func([ - "c", - "d", - "e", - # preserve me but hug brackets -]) +func( + [ + "c", + "d", + "e", + # preserve me but hug brackets + ] +) func( [ @@ -396,16 +627,18 @@ func( ) func([x for x in "short line"]) -func([ - x for x in "long line long line long line long line long line long line long line" -]) -func([ - x - for x in [ +func( + [x for x in "long line long line long line long line long line long line long line"] +) +func( + [ x - for x in "long line long line long line long line long line long line long line" + for x in [ + x + for x in "long line long line long line long line long line long line long line" + ] ] -]) +) foooooooooooooooooooo( [{c: n + 1 for c in range(256)} for n in range(100)] + [{}], {size} @@ -431,39 +664,51 @@ explicit_exploding = [ ], ], ] -single_item_do_not_explode = Context({ - "version": get_docs_version(), -}) +single_item_do_not_explode = Context( + { + "version": get_docs_version(), + } +) -foo(*[ - str(i) for i in range(100000000000000000000000000000000000000000000000000000000000) -]) +foo( + *[ + str(i) + for i in range(100000000000000000000000000000000000000000000000000000000000) + ] +) -foo(**{ - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa": 1, - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb": 2, - "ccccccccccccccccccccccccccccccccc": 3, - **other, -}) +foo( + **{ + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa": 1, + "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb": 2, + "ccccccccccccccccccccccccccccccccc": 3, + **other, + } +) -foo(**{ - x: y for x, y in enumerate(["long long long long line", "long long long long line"]) -}) +foo( + **{ + x: y + for x, y in enumerate(["long long long long line", "long long long long line"]) + } +) # Edge case when deciding whether to hug the brackets without inner content. -very_very_very_long_variable = very_very_very_long_module.VeryVeryVeryVeryLongClassName([ - [] -]) +very_very_very_long_variable = very_very_very_long_module.VeryVeryVeryVeryLongClassName( + [[]] +) for foo in ["a", "b"]: - output.extend([ - individual - for - # Foobar - container in xs_by_y[foo] - # Foobar - for individual in container["nested"] - ]) + output.extend( + [ + individual + for + # Foobar + container in xs_by_y[foo] + # Foobar + for individual in container["nested"] + ] + ) ``` ## Black Output @@ -649,5 +894,3 @@ for foo in ["a", "b"]: for individual in container["nested"] ]) ``` - - diff --git a/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__preview_hug_parens_with_braces_and_square_brackets_no_ll1.py.snap b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__preview_hug_parens_with_braces_and_square_brackets_no_ll1.py.snap index c6034d6010d43..f2168b62aff92 100644 --- a/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__preview_hug_parens_with_braces_and_square_brackets_no_ll1.py.snap +++ b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__preview_hug_parens_with_braces_and_square_brackets_no_ll1.py.snap @@ -35,10 +35,30 @@ nested_array = [[["long line", "long long line", "long long long line", "long lo ```diff --- Black +++ Ruff -@@ -14,13 +14,15 @@ - "long long long long line", - "long long long long long line", - }) +@@ -1,47 +1,65 @@ + # split out from preview_hug_parens_with_brackes_and_square_brackets, as it produces + # different code on the second pass with line-length 1 in many cases. + # Seems to be about whether the last string in a sequence gets wrapped in parens or not. +-foo(*[ +- "long long long long long line", +- "long long long long long line", +- "long long long long long line", +-]) ++foo( ++ *[ ++ "long long long long long line", ++ "long long long long long line", ++ "long long long long long line", ++ ] ++) + func({"short line"}) +-func({ +- "long line", +- "long long line", +- "long long long line", +- "long long long long line", +- "long long long long long line", +-}) -func({{ - "long line", - "long long line", @@ -46,22 +66,20 @@ nested_array = [[["long line", "long long line", "long long long line", "long lo - "long long long long line", - "long long long long long line", -}}) -+func({ -+ { -+ "long line", -+ "long long line", -+ "long long long line", -+ "long long long long line", -+ "long long long long long line", -+ } -+}) - func(( - "long line", - "long long line", -@@ -35,31 +37,63 @@ - "long long long long line", - "long long long long long line", - ))) +-func(( +- "long line", +- "long long line", +- "long long long line", +- "long long long long line", +- "long long long long long line", +-)) +-func((( +- "long line", +- "long long line", +- "long long long line", +- "long long long long line", +- "long long long long long line", +-))) -func([[ - "long line", - "long long line", @@ -69,82 +87,61 @@ nested_array = [[["long line", "long long line", "long long long line", "long lo - "long long long long line", - "long long long long long line", -]]) -+func([ -+ [ ++func( ++ { ++ "long line", ++ "long long line", ++ "long long long line", ++ "long long long long line", ++ "long long long long long line", ++ } ++) ++func( ++ { ++ { ++ "long line", ++ "long long line", ++ "long long long line", ++ "long long long long line", ++ "long long long long long line", ++ } ++ } ++) ++func( ++ ( + "long line", + "long long line", + "long long long line", + "long long long long line", + "long long long long long line", ++ ) ++) ++func( ++ ( ++ ( ++ "long line", ++ "long long line", ++ "long long long line", ++ "long long long long line", ++ "long long long long long line", ++ ) ++ ) ++) ++func( ++ [ ++ [ ++ "long line", ++ "long long line", ++ "long long long line", ++ "long long long long line", ++ "long long long long long line", ++ ] + ] -+]) ++) # Do not hug if the argument fits on a single line. --func( -- {"fit line", "fit line", "fit line", "fit line", "fit line", "fit line", "fit line"} --) --func( -- ("fit line", "fit line", "fit line", "fit line", "fit line", "fit line", "fit line") --) --func( -- ["fit line", "fit line", "fit line", "fit line", "fit line", "fit line", "fit line"] --) --func( -- **{"fit line", "fit line", "fit line", "fit line", "fit line", "fit line", "fit---"} --) --func( -- *("fit line", "fit line", "fit line", "fit line", "fit line", "fit line", "fit----") --) -+func({ -+ "fit line", -+ "fit line", -+ "fit line", -+ "fit line", -+ "fit line", -+ "fit line", -+ "fit line", -+}) -+func(( -+ "fit line", -+ "fit line", -+ "fit line", -+ "fit line", -+ "fit line", -+ "fit line", -+ "fit line", -+)) -+func([ -+ "fit line", -+ "fit line", -+ "fit line", -+ "fit line", -+ "fit line", -+ "fit line", -+ "fit line", -+]) -+func(**{ -+ "fit line", -+ "fit line", -+ "fit line", -+ "fit line", -+ "fit line", -+ "fit line", -+ "fit---", -+}) -+func(*( -+ "fit line", -+ "fit line", -+ "fit line", -+ "fit line", -+ "fit line", -+ "fit line", -+ "fit----", -+)) - array = [ - {"fit line", "fit line", "fit line", "fit line", "fit line", "fit line", "fit line"} - ] -@@ -70,10 +104,14 @@ +@@ -70,10 +88,14 @@ ["fit line", "fit line", "fit line", "fit line", "fit line", "fit line", "fit line"] ] @@ -174,20 +171,15 @@ nested_array = [[["long line", "long long line", "long long long line", "long lo # split out from preview_hug_parens_with_brackes_and_square_brackets, as it produces # different code on the second pass with line-length 1 in many cases. # Seems to be about whether the last string in a sequence gets wrapped in parens or not. -foo(*[ - "long long long long long line", - "long long long long long line", - "long long long long long line", -]) +foo( + *[ + "long long long long long line", + "long long long long long line", + "long long long long long line", + ] +) func({"short line"}) -func({ - "long line", - "long long line", - "long long long line", - "long long long long line", - "long long long long long line", -}) -func({ +func( { "long line", "long long line", @@ -195,78 +187,67 @@ func({ "long long long long line", "long long long long long line", } -}) -func(( - "long line", - "long long line", - "long long long line", - "long long long long line", - "long long long long long line", -)) -func((( - "long line", - "long long line", - "long long long line", - "long long long long line", - "long long long long long line", -))) -func([ - [ +) +func( + { + { + "long line", + "long long line", + "long long long line", + "long long long long line", + "long long long long long line", + } + } +) +func( + ( "long line", "long long line", "long long long line", "long long long long line", "long long long long long line", + ) +) +func( + ( + ( + "long line", + "long long line", + "long long long line", + "long long long long line", + "long long long long long line", + ) + ) +) +func( + [ + [ + "long line", + "long long line", + "long long long line", + "long long long long line", + "long long long long long line", + ] ] -]) +) # Do not hug if the argument fits on a single line. -func({ - "fit line", - "fit line", - "fit line", - "fit line", - "fit line", - "fit line", - "fit line", -}) -func(( - "fit line", - "fit line", - "fit line", - "fit line", - "fit line", - "fit line", - "fit line", -)) -func([ - "fit line", - "fit line", - "fit line", - "fit line", - "fit line", - "fit line", - "fit line", -]) -func(**{ - "fit line", - "fit line", - "fit line", - "fit line", - "fit line", - "fit line", - "fit---", -}) -func(*( - "fit line", - "fit line", - "fit line", - "fit line", - "fit line", - "fit line", - "fit----", -)) +func( + {"fit line", "fit line", "fit line", "fit line", "fit line", "fit line", "fit line"} +) +func( + ("fit line", "fit line", "fit line", "fit line", "fit line", "fit line", "fit line") +) +func( + ["fit line", "fit line", "fit line", "fit line", "fit line", "fit line", "fit line"] +) +func( + **{"fit line", "fit line", "fit line", "fit line", "fit line", "fit line", "fit---"} +) +func( + *("fit line", "fit line", "fit line", "fit line", "fit line", "fit line", "fit----") +) array = [ {"fit line", "fit line", "fit line", "fit line", "fit line", "fit line", "fit line"} ] @@ -373,5 +354,3 @@ nested_array = [[[ "long long long long long line", ]]] ``` - - diff --git a/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__preview_long_dict_values.py.snap b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__preview_long_dict_values.py.snap index c0d7a4416ffad..b10b75ef236c8 100644 --- a/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__preview_long_dict_values.py.snap +++ b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__preview_long_dict_values.py.snap @@ -114,19 +114,37 @@ class Random: } { -@@ -58,9 +52,9 @@ - "timestamp": 1234, - "latitude": 1, - "longitude": 2, +@@ -52,16 +46,18 @@ + class Random: + def func(): + random_service.status.active_states.inactive = ( +- make_new_top_level_state_from_dict({ +- "topLevelBase": { +- "secondaryBase": { +- "timestamp": 1234, +- "latitude": 1, +- "longitude": 2, - "actionTimestamp": ( - Timestamp(seconds=1530584000, nanos=0).ToJsonString() - ), -+ "actionTimestamp": Timestamp( -+ seconds=1530584000, nanos=0 -+ ).ToJsonString(), - } - }, - }) +- } +- }, +- }) ++ make_new_top_level_state_from_dict( ++ { ++ "topLevelBase": { ++ "secondaryBase": { ++ "timestamp": 1234, ++ "latitude": 1, ++ "longitude": 2, ++ "actionTimestamp": Timestamp( ++ seconds=1530584000, nanos=0 ++ ).ToJsonString(), ++ } ++ }, ++ } ++ ) + ) ``` ## Ruff Output @@ -180,18 +198,20 @@ my_dict = { class Random: def func(): random_service.status.active_states.inactive = ( - make_new_top_level_state_from_dict({ - "topLevelBase": { - "secondaryBase": { - "timestamp": 1234, - "latitude": 1, - "longitude": 2, - "actionTimestamp": Timestamp( - seconds=1530584000, nanos=0 - ).ToJsonString(), - } - }, - }) + make_new_top_level_state_from_dict( + { + "topLevelBase": { + "secondaryBase": { + "timestamp": 1234, + "latitude": 1, + "longitude": 2, + "actionTimestamp": Timestamp( + seconds=1530584000, nanos=0 + ).ToJsonString(), + } + }, + } + ) ) ``` @@ -266,5 +286,3 @@ class Random: }) ) ``` - - diff --git a/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__preview_long_strings.py.snap b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__preview_long_strings.py.snap index 6e6a09b6a4717..8bce69a273c60 100644 --- a/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__preview_long_strings.py.snap +++ b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__preview_long_strings.py.snap @@ -902,7 +902,7 @@ log.info(f"""Skipping: {'a' == 'b'} {desc['ms_name']} {money=} {dte=} {pos_share ) dict_with_lambda_values = { -@@ -524,65 +383,58 @@ +@@ -524,61 +383,54 @@ # Complex string concatenations with a method call in the middle. code = ( @@ -941,7 +941,7 @@ log.info(f"""Skipping: {'a' == 'b'} {desc['ms_name']} {money=} {dte=} {pos_share log.info( - "Skipping:" - f" {desc['db_id']} {foo('bar',x=123)} {'foo' != 'bar'} {(x := 'abc=')} {pos_share=} {desc['status']} {desc['exposure_max']}" -+ f'Skipping: {desc["db_id"]} {foo("bar", x=123)} {"foo" != "bar"} {(x := "abc=")} {pos_share=} {desc["status"]} {desc["exposure_max"]}' ++ f'Skipping: {desc["db_id"]} {foo("bar",x=123)} {"foo" != "bar"} {(x := "abc=")} {pos_share=} {desc["status"]} {desc["exposure_max"]}' ) log.info( @@ -981,18 +981,6 @@ log.info(f"""Skipping: {'a' == 'b'} {desc['ms_name']} {money=} {dte=} {pos_share ) log.info( -- f"""Skipping: {"a" == 'b'} {desc["ms_name"]} {money=} {dte=} {pos_share=} {desc["status"]} {desc["exposure_max"]}""" -+ f"""Skipping: {"a" == "b"} {desc["ms_name"]} {money=} {dte=} {pos_share=} {desc["status"]} {desc["exposure_max"]}""" - ) - - log.info( -@@ -590,5 +442,5 @@ - ) - - log.info( -- f"""Skipping: {'a' == 'b'} {desc['ms_name']} {money=} {dte=} {pos_share=} {desc['status']} {desc['exposure_max']}""" -+ f"""Skipping: {"a" == "b"} {desc["ms_name"]} {money=} {dte=} {pos_share=} {desc["status"]} {desc["exposure_max"]}""" - ) ``` ## Ruff Output @@ -1406,7 +1394,7 @@ log.info( ) log.info( - f'Skipping: {desc["db_id"]} {foo("bar", x=123)} {"foo" != "bar"} {(x := "abc=")} {pos_share=} {desc["status"]} {desc["exposure_max"]}' + f'Skipping: {desc["db_id"]} {foo("bar",x=123)} {"foo" != "bar"} {(x := "abc=")} {pos_share=} {desc["status"]} {desc["exposure_max"]}' ) log.info( @@ -1434,7 +1422,7 @@ log.info( ) log.info( - f"""Skipping: {"a" == "b"} {desc["ms_name"]} {money=} {dte=} {pos_share=} {desc["status"]} {desc["exposure_max"]}""" + f"""Skipping: {"a" == 'b'} {desc["ms_name"]} {money=} {dte=} {pos_share=} {desc["status"]} {desc["exposure_max"]}""" ) log.info( @@ -1442,7 +1430,7 @@ log.info( ) log.info( - f"""Skipping: {"a" == "b"} {desc["ms_name"]} {money=} {dte=} {pos_share=} {desc["status"]} {desc["exposure_max"]}""" + f"""Skipping: {'a' == 'b'} {desc['ms_name']} {money=} {dte=} {pos_share=} {desc['status']} {desc['exposure_max']}""" ) ``` @@ -2044,5 +2032,3 @@ log.info( f"""Skipping: {'a' == 'b'} {desc['ms_name']} {money=} {dte=} {pos_share=} {desc['status']} {desc['exposure_max']}""" ) ``` - - diff --git a/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__preview_long_strings__regression.py.snap b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__preview_long_strings__regression.py.snap index 3d7e731a3659b..5e13bba19bb4b 100644 --- a/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__preview_long_strings__regression.py.snap +++ b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__preview_long_strings__regression.py.snap @@ -573,7 +573,7 @@ s = f'Lorem Ipsum is simply dummy text of the printing and typesetting industry: ```diff --- Black +++ Ruff -@@ -25,20 +25,17 @@ +@@ -25,41 +25,42 @@ "Jaguar", ) @@ -599,7 +599,27 @@ s = f'Lorem Ipsum is simply dummy text of the printing and typesetting industry: ) -@@ -57,9 +54,11 @@ + class A: + def foo(): +- XXXXXXXXXXXX.append(( +- "xxx_xxxxxxxxxx(xxxxx={}, xxxx={}, xxxxx, xxxx_xxxx_xxxxxxxxxx={})".format( +- xxxxx, xxxx, xxxx_xxxx_xxxxxxxxxx +- ), +- my_var, +- my_other_var, +- )) ++ XXXXXXXXXXXX.append( ++ ( ++ "xxx_xxxxxxxxxx(xxxxx={}, xxxx={}, xxxxx, xxxx_xxxx_xxxxxxxxxx={})".format( ++ xxxxx, xxxx, xxxx_xxxx_xxxxxxxxxx ++ ), ++ my_var, ++ my_other_var, ++ ) ++ ) + + + class A: class B: def foo(): bar( @@ -614,7 +634,7 @@ s = f'Lorem Ipsum is simply dummy text of the printing and typesetting industry: ), varX, varY, -@@ -70,9 +69,10 @@ +@@ -70,9 +71,10 @@ def foo(xxxx): for xxx_xxxx, _xxx_xxx, _xxx_xxxxx, xxx_xxxx in xxxx: for xxx in xxx_xxxx: @@ -628,7 +648,7 @@ s = f'Lorem Ipsum is simply dummy text of the printing and typesetting industry: ) -@@ -80,10 +80,11 @@ +@@ -80,10 +82,11 @@ def disappearing_comment(): return ( ( # xx -x xxxxxxx xx xxx xxxxxxx. @@ -642,7 +662,7 @@ s = f'Lorem Ipsum is simply dummy text of the printing and typesetting industry: "--xxxxxxx --xxxxxx=x --xxxxxx-xxxxx=xxxxxx" " --xxxxxx-xxxx=xxxxxxxxxxx.xxx" ) -@@ -113,18 +114,25 @@ +@@ -113,18 +116,25 @@ func_call_where_string_arg_has_method_call_and_bad_parens( @@ -674,7 +694,7 @@ s = f'Lorem Ipsum is simply dummy text of the printing and typesetting industry: ) -@@ -132,52 +140,60 @@ +@@ -132,52 +142,60 @@ def append(self): if True: xxxx.xxxxxxx.xxxxx( @@ -768,7 +788,7 @@ s = f'Lorem Ipsum is simply dummy text of the printing and typesetting industry: } -@@ -185,10 +201,10 @@ +@@ -185,10 +203,10 @@ def foo(self): if True: xxxxx_xxxxxxxxxxxx( @@ -783,7 +803,7 @@ s = f'Lorem Ipsum is simply dummy text of the printing and typesetting industry: ) -@@ -232,39 +248,24 @@ +@@ -232,39 +250,24 @@ some_dictionary = { "xxxxx006": [ @@ -832,7 +852,7 @@ s = f'Lorem Ipsum is simply dummy text of the printing and typesetting industry: some_commented_string = ( # This comment stays at the top. "This string is long but not so long that it needs hahahah toooooo be so greatttt" -@@ -279,37 +280,26 @@ +@@ -279,36 +282,25 @@ ) lpar_and_rpar_have_comments = func_call( # LPAR Comment @@ -852,13 +872,13 @@ s = f'Lorem Ipsum is simply dummy text of the printing and typesetting industry: - f" {'' if ID is None else ID} | perl -nE 'print if /^{field}:/'" -) +cmd_fstring = f"sudo -E deluge-console info --detailed --sort-reverse=time_added {'' if ID is None else ID} | perl -nE 'print if /^{field}:/'" -+ -+cmd_fstring = f"sudo -E deluge-console info --detailed --sort-reverse=time_added {'{{}}' if ID is None else ID} | perl -nE 'print if /^{field}:/'" -cmd_fstring = ( - "sudo -E deluge-console info --detailed --sort-reverse=time_added" - f" {'{{}}' if ID is None else ID} | perl -nE 'print if /^{field}:/'" -) ++cmd_fstring = f"sudo -E deluge-console info --detailed --sort-reverse=time_added {'{{}}' if ID is None else ID} | perl -nE 'print if /^{field}:/'" ++ +cmd_fstring = f"sudo -E deluge-console info --detailed --sort-reverse=time_added {{'' if ID is None else ID}} | perl -nE 'print if /^{field}:/'" -cmd_fstring = ( @@ -872,13 +892,12 @@ s = f'Lorem Ipsum is simply dummy text of the printing and typesetting industry: - f" certainly, absolutely {does}." + f"We have to remember to escape {braces}." " Like {these}." f" But not {this}." ) - --fstring = f"We have to remember to escape {braces}. Like {{these}}. But not {this}." - +-fstring = f"We have to remember to escape {braces}. Like {{these}}. But not {this}." + class A: - class B: -@@ -364,10 +354,7 @@ +@@ -364,10 +356,7 @@ def foo(): if not hasattr(module, name): raise ValueError( @@ -890,7 +909,7 @@ s = f'Lorem Ipsum is simply dummy text of the printing and typesetting industry: % (name, module_name, get_docs_version()) ) -@@ -382,23 +369,19 @@ +@@ -382,35 +371,33 @@ class Step(StepBase): def who(self): @@ -912,16 +931,29 @@ s = f'Lorem Ipsum is simply dummy text of the printing and typesetting industry: ) - xxxxxxx_xxxxxx_xxxxxxx = xxx([ - xxxxxxxxxxxx( - xxxxxx_xxxxxxx=( +-xxxxxxx_xxxxxx_xxxxxxx = xxx([ +- xxxxxxxxxxxx( +- xxxxxx_xxxxxxx=( - '((x.aaaaaaaaa = "xxxxxx.xxxxxxxxxxxxxxxxxxxxx") || (x.xxxxxxxxx =' - ' "xxxxxxxxxxxx")) && ' -+ '((x.aaaaaaaaa = "xxxxxx.xxxxxxxxxxxxxxxxxxxxx") || (x.xxxxxxxxx = "xxxxxxxxxxxx")) && ' - # xxxxx xxxxxxxxxxxx xxxx xxx (xxxxxxxxxxxxxxxx) xx x xxxxxxxxx xx xxxxxx. - "(x.bbbbbbbbbbbb.xxx != " - '"xxx:xxx:xxx::cccccccccccc:xxxxxxx-xxxx/xxxxxxxxxxx/xxxxxxxxxxxxxxxxx") && ' -@@ -409,8 +392,8 @@ +- # xxxxx xxxxxxxxxxxx xxxx xxx (xxxxxxxxxxxxxxxx) xx x xxxxxxxxx xx xxxxxx. +- "(x.bbbbbbbbbbbb.xxx != " +- '"xxx:xxx:xxx::cccccccccccc:xxxxxxx-xxxx/xxxxxxxxxxx/xxxxxxxxxxxxxxxxx") && ' ++xxxxxxx_xxxxxx_xxxxxxx = xxx( ++ [ ++ xxxxxxxxxxxx( ++ xxxxxx_xxxxxxx=( ++ '((x.aaaaaaaaa = "xxxxxx.xxxxxxxxxxxxxxxxxxxxx") || (x.xxxxxxxxx = "xxxxxxxxxxxx")) && ' ++ # xxxxx xxxxxxxxxxxx xxxx xxx (xxxxxxxxxxxxxxxx) xx x xxxxxxxxx xx xxxxxx. ++ "(x.bbbbbbbbbbbb.xxx != " ++ '"xxx:xxx:xxx::cccccccccccc:xxxxxxx-xxxx/xxxxxxxxxxx/xxxxxxxxxxxxxxxxx") && ' ++ ) + ) +- ) +-]) ++ ] ++) + if __name__ == "__main__": for i in range(4, 8): cmd = ( @@ -932,7 +964,7 @@ s = f'Lorem Ipsum is simply dummy text of the printing and typesetting industry: ) -@@ -432,9 +415,7 @@ +@@ -432,9 +419,7 @@ assert xxxxxxx_xxxx in [ x.xxxxx.xxxxxx.xxxxx.xxxxxx, x.xxxxx.xxxxxx.xxxxx.xxxx, @@ -943,7 +975,7 @@ s = f'Lorem Ipsum is simply dummy text of the printing and typesetting industry: value.__dict__[key] = ( -@@ -449,8 +430,7 @@ +@@ -449,8 +434,7 @@ RE_TWO_BACKSLASHES = { "asdf_hjkl_jkl": re.compile( @@ -953,23 +985,23 @@ s = f'Lorem Ipsum is simply dummy text of the printing and typesetting industry: ), } -@@ -462,13 +442,9 @@ +@@ -462,13 +446,9 @@ # We do NOT split on f-string expressions. print( - "Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam." - f" {[f'{i}' for i in range(10)]}" -+ f"Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam. {[f'{i}' for i in range(10)]}" - ) +-) -x = ( - "This is a long string which contains an f-expr that should not split" - f" {{{[i for i in range(5)]}}}." --) ++ f"Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam. {[f'{i}' for i in range(10)]}" + ) +x = f"This is a long string which contains an f-expr that should not split {{{[i for i in range(5)]}}}." # The parens should NOT be removed in this case. ( -@@ -478,8 +454,8 @@ +@@ -478,8 +458,8 @@ # The parens should NOT be removed in this case. ( @@ -980,13 +1012,7 @@ s = f'Lorem Ipsum is simply dummy text of the printing and typesetting industry: ) # The parens should NOT be removed in this case. -@@ -513,93 +489,83 @@ - - - temp_msg = ( -- f"{f'{humanize_number(pos)}.': <{pound_len+2}} " -+ f"{f'{humanize_number(pos)}.': <{pound_len + 2}} " - f"{balance: <{bal_len + 5}} " +@@ -518,88 +498,78 @@ f"<<{author.display_name}>>\n" ) @@ -1110,13 +1136,7 @@ s = f'Lorem Ipsum is simply dummy text of the printing and typesetting industry: "6. Click on Create Credential at the top." '7. At the top click the link for "API key".' "8. No application restrictions are needed. Click Create at the bottom." -@@ -608,60 +574,45 @@ - - # It shouldn't matter if the string prefixes are capitalized. - temp_msg = ( -- f"{F'{humanize_number(pos)}.': <{pound_len+2}} " -+ f"{f'{humanize_number(pos)}.': <{pound_len + 2}} " - f"{balance: <{bal_len + 5}} " +@@ -613,55 +583,40 @@ f"<<{author.display_name}>>\n" ) @@ -1189,7 +1209,7 @@ s = f'Lorem Ipsum is simply dummy text of the printing and typesetting industry: ) # Regression test for https://github.com/psf/black/issues/3455. -@@ -672,9 +623,11 @@ +@@ -672,9 +627,11 @@ } # Regression test for https://github.com/psf/black/issues/3506. @@ -1253,13 +1273,15 @@ class A: class A: def foo(): - XXXXXXXXXXXX.append(( - "xxx_xxxxxxxxxx(xxxxx={}, xxxx={}, xxxxx, xxxx_xxxx_xxxxxxxxxx={})".format( - xxxxx, xxxx, xxxx_xxxx_xxxxxxxxxx - ), - my_var, - my_other_var, - )) + XXXXXXXXXXXX.append( + ( + "xxx_xxxxxxxxxx(xxxxx={}, xxxx={}, xxxxx, xxxx_xxxx_xxxxxxxxxx={})".format( + xxxxx, xxxx, xxxx_xxxx_xxxxxxxxxx + ), + my_var, + my_other_var, + ) + ) class A: @@ -1590,16 +1612,18 @@ class Step(StepBase): ) -xxxxxxx_xxxxxx_xxxxxxx = xxx([ - xxxxxxxxxxxx( - xxxxxx_xxxxxxx=( - '((x.aaaaaaaaa = "xxxxxx.xxxxxxxxxxxxxxxxxxxxx") || (x.xxxxxxxxx = "xxxxxxxxxxxx")) && ' - # xxxxx xxxxxxxxxxxx xxxx xxx (xxxxxxxxxxxxxxxx) xx x xxxxxxxxx xx xxxxxx. - "(x.bbbbbbbbbbbb.xxx != " - '"xxx:xxx:xxx::cccccccccccc:xxxxxxx-xxxx/xxxxxxxxxxx/xxxxxxxxxxxxxxxxx") && ' +xxxxxxx_xxxxxx_xxxxxxx = xxx( + [ + xxxxxxxxxxxx( + xxxxxx_xxxxxxx=( + '((x.aaaaaaaaa = "xxxxxx.xxxxxxxxxxxxxxxxxxxxx") || (x.xxxxxxxxx = "xxxxxxxxxxxx")) && ' + # xxxxx xxxxxxxxxxxx xxxx xxx (xxxxxxxxxxxxxxxx) xx x xxxxxxxxx xx xxxxxx. + "(x.bbbbbbbbbbbb.xxx != " + '"xxx:xxx:xxx::cccccccccccc:xxxxxxx-xxxx/xxxxxxxxxxx/xxxxxxxxxxxxxxxxx") && ' + ) ) - ) -]) + ] +) if __name__ == "__main__": for i in range(4, 8): @@ -1701,7 +1725,7 @@ class X: temp_msg = ( - f"{f'{humanize_number(pos)}.': <{pound_len + 2}} " + f"{f'{humanize_number(pos)}.': <{pound_len+2}} " f"{balance: <{bal_len + 5}} " f"<<{author.display_name}>>\n" ) @@ -1786,7 +1810,7 @@ message = ( # It shouldn't matter if the string prefixes are capitalized. temp_msg = ( - f"{f'{humanize_number(pos)}.': <{pound_len + 2}} " + f"{F'{humanize_number(pos)}.': <{pound_len+2}} " f"{balance: <{bal_len + 5}} " f"<<{author.display_name}>>\n" ) @@ -2529,5 +2553,3 @@ s = ( f" industry:'{my_dict['foo']}'" ) ``` - - diff --git a/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__python39.py.snap b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__python39.py.snap deleted file mode 100644 index 246553339b6d5..0000000000000 --- a/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__python39.py.snap +++ /dev/null @@ -1,90 +0,0 @@ ---- -source: crates/ruff_python_formatter/tests/fixtures.rs -input_file: crates/ruff_python_formatter/resources/test/fixtures/black/cases/python39.py ---- -## Input - -```python -@relaxed_decorator[0] -def f(): - ... - -@relaxed_decorator[extremely_long_name_that_definitely_will_not_fit_on_one_line_of_standard_length] -def f(): - ... - -@extremely_long_variable_name_that_doesnt_fit := complex.expression(with_long="arguments_value_that_wont_fit_at_the_end_of_the_line") -def f(): - ... -``` - -## Black Differences - -```diff ---- Black -+++ Ruff -@@ -1,17 +1,14 @@ - @relaxed_decorator[0] --def f(): -- ... -+def f(): ... - - - @relaxed_decorator[ - extremely_long_name_that_definitely_will_not_fit_on_one_line_of_standard_length - ] --def f(): -- ... -+def f(): ... - - - @extremely_long_variable_name_that_doesnt_fit := complex.expression( - with_long="arguments_value_that_wont_fit_at_the_end_of_the_line" - ) --def f(): -- ... -+def f(): ... -``` - -## Ruff Output - -```python -@relaxed_decorator[0] -def f(): ... - - -@relaxed_decorator[ - extremely_long_name_that_definitely_will_not_fit_on_one_line_of_standard_length -] -def f(): ... - - -@extremely_long_variable_name_that_doesnt_fit := complex.expression( - with_long="arguments_value_that_wont_fit_at_the_end_of_the_line" -) -def f(): ... -``` - -## Black Output - -```python -@relaxed_decorator[0] -def f(): - ... - - -@relaxed_decorator[ - extremely_long_name_that_definitely_will_not_fit_on_one_line_of_standard_length -] -def f(): - ... - - -@extremely_long_variable_name_that_doesnt_fit := complex.expression( - with_long="arguments_value_that_wont_fit_at_the_end_of_the_line" -) -def f(): - ... -``` - - diff --git a/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__raw_docstring_no_string_normalization.py.snap b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__raw_docstring_no_string_normalization.py.snap new file mode 100644 index 0000000000000..b9c8f50f337cd --- /dev/null +++ b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__raw_docstring_no_string_normalization.py.snap @@ -0,0 +1,66 @@ +--- +source: crates/ruff_python_formatter/tests/fixtures.rs +input_file: crates/ruff_python_formatter/resources/test/fixtures/black/cases/raw_docstring_no_string_normalization.py +--- +## Input + +```python +def do_not_touch_this_prefix(): + R"""There was a bug where docstring prefixes would be normalized even with -S.""" + + +def do_not_touch_this_prefix2(): + FR'There was a bug where docstring prefixes would be normalized even with -S.' + + +def do_not_touch_this_prefix3(): + u'''There was a bug where docstring prefixes would be normalized even with -S.''' +``` + +## Black Differences + +```diff +--- Black ++++ Ruff +@@ -3,8 +3,8 @@ + + + def do_not_touch_this_prefix2(): +- FR'There was a bug where docstring prefixes would be normalized even with -S.' ++ Rf"There was a bug where docstring prefixes would be normalized even with -S." + + + def do_not_touch_this_prefix3(): +- u'''There was a bug where docstring prefixes would be normalized even with -S.''' ++ """There was a bug where docstring prefixes would be normalized even with -S.""" +``` + +## Ruff Output + +```python +def do_not_touch_this_prefix(): + R"""There was a bug where docstring prefixes would be normalized even with -S.""" + + +def do_not_touch_this_prefix2(): + Rf"There was a bug where docstring prefixes would be normalized even with -S." + + +def do_not_touch_this_prefix3(): + """There was a bug where docstring prefixes would be normalized even with -S.""" +``` + +## Black Output + +```python +def do_not_touch_this_prefix(): + R"""There was a bug where docstring prefixes would be normalized even with -S.""" + + +def do_not_touch_this_prefix2(): + FR'There was a bug where docstring prefixes would be normalized even with -S.' + + +def do_not_touch_this_prefix3(): + u'''There was a bug where docstring prefixes would be normalized even with -S.''' +``` diff --git a/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__remove_newline_after_code_block_open.py.snap b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__remove_newline_after_code_block_open.py.snap new file mode 100644 index 0000000000000..ca73f90ad32cc --- /dev/null +++ b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__remove_newline_after_code_block_open.py.snap @@ -0,0 +1,411 @@ +--- +source: crates/ruff_python_formatter/tests/fixtures.rs +input_file: crates/ruff_python_formatter/resources/test/fixtures/black/cases/remove_newline_after_code_block_open.py +--- +## Input + +```python +import random + + +def foo1(): + + print("The newline above me should be kept!") + + +def foo2(): + + + + print("All the newlines above me should be kept!") + + +def foo3(): + + print("No newline above me!") + + print("There is a newline above me, and that's OK!") + + +def foo4(): + + # There is a comment here + + print("The newline above me should not be deleted!") + + +class Foo: + def bar(self): + + print("The newline above me should be kept!") + + +for i in range(5): + + print(f"{i}) The line above me should be kept!") + + +for i in range(5): + + + + print(f"{i}) The lines above me should be kept!") + + +for i in range(5): + + for j in range(7): + + print(f"{i}) The lines above me should be kept!") + + +if random.randint(0, 3) == 0: + + print("The new line above me will be kept!") + + +if random.randint(0, 3) == 0: + + + + + print("The new lines above me will be kept!") + + +if random.randint(0, 3) == 0: + + if random.uniform(0, 1) > 0.5: + + print("Two lines above me will be kept!") + + +while True: + + print("The newline above me should be kept!") + + +while True: + + + + print("The newlines above me should be kept!") + + +while True: + + while False: + + print("The newlines above me should be kept!") + + +with open("/path/to/file.txt", mode="w") as file: + + file.write("The new line above me will be kept!") + + +with open("/path/to/file.txt", mode="w") as file: + + + + file.write("The new lines above me will be kept!") + + +with open("/path/to/file.txt", mode="r") as read_file: + + with open("/path/to/output_file.txt", mode="w") as write_file: + + write_file.writelines(read_file.readlines()) +``` + +## Black Differences + +```diff +--- Black ++++ Ruff +@@ -2,24 +2,20 @@ + + + def foo1(): +- + print("The newline above me should be kept!") + + + def foo2(): +- + print("All the newlines above me should be kept!") + + + def foo3(): +- + print("No newline above me!") + + print("There is a newline above me, and that's OK!") + + + def foo4(): +- + # There is a comment here + + print("The newline above me should not be deleted!") +@@ -27,73 +23,56 @@ + + class Foo: + def bar(self): +- + print("The newline above me should be kept!") + + + for i in range(5): +- + print(f"{i}) The line above me should be kept!") + + + for i in range(5): +- + print(f"{i}) The lines above me should be kept!") + + + for i in range(5): +- + for j in range(7): +- + print(f"{i}) The lines above me should be kept!") + + + if random.randint(0, 3) == 0: +- + print("The new line above me will be kept!") + + + if random.randint(0, 3) == 0: +- + print("The new lines above me will be kept!") + + + if random.randint(0, 3) == 0: +- + if random.uniform(0, 1) > 0.5: +- + print("Two lines above me will be kept!") + + + while True: +- + print("The newline above me should be kept!") + + + while True: +- + print("The newlines above me should be kept!") + + + while True: +- + while False: +- + print("The newlines above me should be kept!") + + + with open("/path/to/file.txt", mode="w") as file: +- + file.write("The new line above me will be kept!") + + + with open("/path/to/file.txt", mode="w") as file: +- + file.write("The new lines above me will be kept!") + + + with open("/path/to/file.txt", mode="r") as read_file: +- + with open("/path/to/output_file.txt", mode="w") as write_file: +- + write_file.writelines(read_file.readlines()) +``` + +## Ruff Output + +```python +import random + + +def foo1(): + print("The newline above me should be kept!") + + +def foo2(): + print("All the newlines above me should be kept!") + + +def foo3(): + print("No newline above me!") + + print("There is a newline above me, and that's OK!") + + +def foo4(): + # There is a comment here + + print("The newline above me should not be deleted!") + + +class Foo: + def bar(self): + print("The newline above me should be kept!") + + +for i in range(5): + print(f"{i}) The line above me should be kept!") + + +for i in range(5): + print(f"{i}) The lines above me should be kept!") + + +for i in range(5): + for j in range(7): + print(f"{i}) The lines above me should be kept!") + + +if random.randint(0, 3) == 0: + print("The new line above me will be kept!") + + +if random.randint(0, 3) == 0: + print("The new lines above me will be kept!") + + +if random.randint(0, 3) == 0: + if random.uniform(0, 1) > 0.5: + print("Two lines above me will be kept!") + + +while True: + print("The newline above me should be kept!") + + +while True: + print("The newlines above me should be kept!") + + +while True: + while False: + print("The newlines above me should be kept!") + + +with open("/path/to/file.txt", mode="w") as file: + file.write("The new line above me will be kept!") + + +with open("/path/to/file.txt", mode="w") as file: + file.write("The new lines above me will be kept!") + + +with open("/path/to/file.txt", mode="r") as read_file: + with open("/path/to/output_file.txt", mode="w") as write_file: + write_file.writelines(read_file.readlines()) +``` + +## Black Output + +```python +import random + + +def foo1(): + + print("The newline above me should be kept!") + + +def foo2(): + + print("All the newlines above me should be kept!") + + +def foo3(): + + print("No newline above me!") + + print("There is a newline above me, and that's OK!") + + +def foo4(): + + # There is a comment here + + print("The newline above me should not be deleted!") + + +class Foo: + def bar(self): + + print("The newline above me should be kept!") + + +for i in range(5): + + print(f"{i}) The line above me should be kept!") + + +for i in range(5): + + print(f"{i}) The lines above me should be kept!") + + +for i in range(5): + + for j in range(7): + + print(f"{i}) The lines above me should be kept!") + + +if random.randint(0, 3) == 0: + + print("The new line above me will be kept!") + + +if random.randint(0, 3) == 0: + + print("The new lines above me will be kept!") + + +if random.randint(0, 3) == 0: + + if random.uniform(0, 1) > 0.5: + + print("Two lines above me will be kept!") + + +while True: + + print("The newline above me should be kept!") + + +while True: + + print("The newlines above me should be kept!") + + +while True: + + while False: + + print("The newlines above me should be kept!") + + +with open("/path/to/file.txt", mode="w") as file: + + file.write("The new line above me will be kept!") + + +with open("/path/to/file.txt", mode="w") as file: + + file.write("The new lines above me will be kept!") + + +with open("/path/to/file.txt", mode="r") as read_file: + + with open("/path/to/output_file.txt", mode="w") as write_file: + + write_file.writelines(read_file.readlines()) +``` diff --git a/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__remove_redundant_parens_in_case_guard.py.snap b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__remove_redundant_parens_in_case_guard.py.snap new file mode 100644 index 0000000000000..6f0c6257e3a7a --- /dev/null +++ b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__remove_redundant_parens_in_case_guard.py.snap @@ -0,0 +1,244 @@ +--- +source: crates/ruff_python_formatter/tests/fixtures.rs +input_file: crates/ruff_python_formatter/resources/test/fixtures/black/cases/remove_redundant_parens_in_case_guard.py +--- +## Input + +```python +match 1: + case _ if (True): + pass + + +match 1: + case _ if ( + True + ): + pass + + +match 1: + case _ if ( + # this is a comment + True + ): + pass + + +match 1: + case _ if ( + True + # this is a comment + ): + pass + + +match 1: + case _ if ( + True # this is a comment + ): + pass + + +match 1: + case _ if ( # this is a comment + True + ): + pass + + +match 1: + case _ if ( + True + ): # this is a comment + pass + + +match 1: + case _ if (True): # comment over the line limit unless parens are removed x + pass + + +match 1: + case _ if (True): # comment over the line limit and parens should go to next line + pass +``` + +## Black Differences + +```diff +--- Black ++++ Ruff +@@ -1,10 +1,10 @@ + match 1: +- case _ if True: ++ case _ if (True): + pass + + + match 1: +- case _ if True: ++ case _ if (True): + pass + + +@@ -25,27 +25,33 @@ + + + match 1: +- case _ if True: # this is a comment ++ case _ if ( ++ True # this is a comment ++ ): + pass + + + match 1: +- case _ if True: # this is a comment ++ case _ if ( # this is a comment ++ True ++ ): + pass + + + match 1: +- case _ if True: # this is a comment ++ case _ if (True): # this is a comment + pass + + + match 1: +- case _ if True: # comment over the line limit unless parens are removed x ++ case _ if ( ++ True ++ ): # comment over the line limit unless parens are removed x + pass + + + match 1: +- case ( +- _ +- ) if True: # comment over the line limit and parens should go to next line ++ case _ if ( ++ True ++ ): # comment over the line limit and parens should go to next line + pass +``` + +## Ruff Output + +```python +match 1: + case _ if (True): + pass + + +match 1: + case _ if (True): + pass + + +match 1: + case _ if ( + # this is a comment + True + ): + pass + + +match 1: + case _ if ( + True + # this is a comment + ): + pass + + +match 1: + case _ if ( + True # this is a comment + ): + pass + + +match 1: + case _ if ( # this is a comment + True + ): + pass + + +match 1: + case _ if (True): # this is a comment + pass + + +match 1: + case _ if ( + True + ): # comment over the line limit unless parens are removed x + pass + + +match 1: + case _ if ( + True + ): # comment over the line limit and parens should go to next line + pass +``` + +## Black Output + +```python +match 1: + case _ if True: + pass + + +match 1: + case _ if True: + pass + + +match 1: + case _ if ( + # this is a comment + True + ): + pass + + +match 1: + case _ if ( + True + # this is a comment + ): + pass + + +match 1: + case _ if True: # this is a comment + pass + + +match 1: + case _ if True: # this is a comment + pass + + +match 1: + case _ if True: # this is a comment + pass + + +match 1: + case _ if True: # comment over the line limit unless parens are removed x + pass + + +match 1: + case ( + _ + ) if True: # comment over the line limit and parens should go to next line + pass +``` diff --git a/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__return_annotation_brackets.py.snap b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__return_annotation_brackets.py.snap index 9bf6a451152c9..660fd0b731079 100644 --- a/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__return_annotation_brackets.py.snap +++ b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__return_annotation_brackets.py.snap @@ -95,7 +95,6 @@ def foo() -> tuple[int, int, int,]: return 2 # Magic trailing comma example, with params -# this is broken - the trailing comma is transferred to the param list. Fixed in preview def foo(a,b) -> tuple[int, int, int,]: return 2 ``` @@ -127,17 +126,49 @@ def foo(a,b) -> tuple[int, int, int,]: return 2 * a -@@ -124,5 +132,9 @@ - # this is broken - the trailing comma is transferred to the param list. Fixed in preview - def foo( - a, b --) -> tuple[int, int, int,]: -+) -> tuple[ -+ int, -+ int, -+ int, -+]: +@@ -99,25 +107,31 @@ + return 2 + + +-def foo() -> tuple[ +- loooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong, +- loooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong, +- loooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong, +-]: ++def foo() -> ( ++ tuple[ ++ loooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong, ++ loooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong, ++ loooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong, ++ ] ++): return 2 + + + # Magic trailing comma example +-def foo() -> tuple[ +- int, +- int, +- int, +-]: ++def foo() -> ( ++ tuple[ ++ int, ++ int, ++ int, ++ ] ++): + return 2 + + + # Magic trailing comma example, with params +-def foo(a, b) -> tuple[ ++def foo( ++ a, b ++) -> tuple[ + int, + int, + int, ``` ## Ruff Output @@ -274,7 +305,6 @@ def foo() -> ( # Magic trailing comma example, with params -# this is broken - the trailing comma is transferred to the param list. Fixed in preview def foo( a, b ) -> tuple[ @@ -389,33 +419,28 @@ def foo() -> tuple[int, int, int]: return 2 -def foo() -> ( - tuple[ - loooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong, - loooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong, - loooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong, - ] -): +def foo() -> tuple[ + loooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong, + loooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong, + loooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong, +]: return 2 # Magic trailing comma example -def foo() -> ( - tuple[ - int, - int, - int, - ] -): +def foo() -> tuple[ + int, + int, + int, +]: return 2 # Magic trailing comma example, with params -# this is broken - the trailing comma is transferred to the param list. Fixed in preview -def foo( - a, b -) -> tuple[int, int, int,]: +def foo(a, b) -> tuple[ + int, + int, + int, +]: return 2 ``` - - diff --git a/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__single_line_format_skip_with_multiple_comments.py.snap b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__single_line_format_skip_with_multiple_comments.py.snap new file mode 100644 index 0000000000000..644db86aea51e --- /dev/null +++ b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__single_line_format_skip_with_multiple_comments.py.snap @@ -0,0 +1,61 @@ +--- +source: crates/ruff_python_formatter/tests/fixtures.rs +input_file: crates/ruff_python_formatter/resources/test/fixtures/black/cases/single_line_format_skip_with_multiple_comments.py +--- +## Input + +```python +foo = 123 # fmt: skip # noqa: E501 # pylint +bar = ( + 123 , + ( 1 + 5 ) # pylint # fmt:skip +) +baz = "a" + "b" # pylint; fmt: skip; noqa: E501 +skip_will_not_work = "a" + "b" # pylint fmt:skip +skip_will_not_work2 = "a" + "b" # some text; fmt:skip happens to be part of it +``` + +## Black Differences + +```diff +--- Black ++++ Ruff +@@ -1,8 +1,8 @@ + foo = 123 # fmt: skip # noqa: E501 # pylint + bar = ( +- 123 , +- ( 1 + 5 ) # pylint # fmt:skip ++ 123, ++ (1 + 5), # pylint # fmt:skip + ) +-baz = "a" + "b" # pylint; fmt: skip; noqa: E501 ++baz = "a" + "b" # pylint; fmt: skip; noqa: E501 + skip_will_not_work = "a" + "b" # pylint fmt:skip + skip_will_not_work2 = "a" + "b" # some text; fmt:skip happens to be part of it +``` + +## Ruff Output + +```python +foo = 123 # fmt: skip # noqa: E501 # pylint +bar = ( + 123, + (1 + 5), # pylint # fmt:skip +) +baz = "a" + "b" # pylint; fmt: skip; noqa: E501 +skip_will_not_work = "a" + "b" # pylint fmt:skip +skip_will_not_work2 = "a" + "b" # some text; fmt:skip happens to be part of it +``` + +## Black Output + +```python +foo = 123 # fmt: skip # noqa: E501 # pylint +bar = ( + 123 , + ( 1 + 5 ) # pylint # fmt:skip +) +baz = "a" + "b" # pylint; fmt: skip; noqa: E501 +skip_will_not_work = "a" + "b" # pylint fmt:skip +skip_will_not_work2 = "a" + "b" # some text; fmt:skip happens to be part of it +```