diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/annotations.py b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/annotations.py new file mode 100644 index 0000000000..acba780f0d --- /dev/null +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/annotations.py @@ -0,0 +1,7 @@ +# regression test for #1765 +class Foo: + def foo(self): + if True: + content_ids: Mapping[ + str, Optional[ContentId] + ] = self.publisher_content_store.store_config_contents(files) diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/annotations.py.expect b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/annotations.py.expect new file mode 100644 index 0000000000..c256da26b7 --- /dev/null +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/annotations.py.expect @@ -0,0 +1,7 @@ +# regression test for #1765 +class Foo: + def foo(self): + if True: + content_ids: Mapping[str, Optional[ContentId]] = ( + self.publisher_content_store.store_config_contents(files) + ) diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/backslash_before_indent.options.json b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/backslash_before_indent.options.json index 54724b66c5..717b73130f 100644 --- a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/backslash_before_indent.options.json +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/backslash_before_indent.options.json @@ -1 +1 @@ -{"target_version": "3.10"} +{"target_version": "3.10"} \ No newline at end of file diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/cantfit.py b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/cantfit.py new file mode 100644 index 0000000000..df8ae398bf --- /dev/null +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/cantfit.py @@ -0,0 +1,35 @@ +# long variable name +this_is_a_ridiculously_long_name_and_nobody_in_their_right_mind_would_use_one_like_it = 0 +this_is_a_ridiculously_long_name_and_nobody_in_their_right_mind_would_use_one_like_it = 1 # with a comment +this_is_a_ridiculously_long_name_and_nobody_in_their_right_mind_would_use_one_like_it = [ + 1, 2, 3 +] +this_is_a_ridiculously_long_name_and_nobody_in_their_right_mind_would_use_one_like_it = function() +this_is_a_ridiculously_long_name_and_nobody_in_their_right_mind_would_use_one_like_it = function( + arg1, arg2, arg3 +) +this_is_a_ridiculously_long_name_and_nobody_in_their_right_mind_would_use_one_like_it = function( + [1, 2, 3], arg1, [1, 2, 3], arg2, [1, 2, 3], arg3 +) +# long function name +normal_name = but_the_function_name_is_now_ridiculously_long_and_it_is_still_super_annoying() +normal_name = but_the_function_name_is_now_ridiculously_long_and_it_is_still_super_annoying( + arg1, arg2, arg3 +) +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 +) +string_variable_name = ( + "a string that is waaaaaaaayyyyyyyy too long, even in parens, there's nothing you can do" # noqa +) +for key in """ + hostname + port + username +""".split(): + if key in self.connect_kwargs: + raise ValueError(err.format(key)) +concatenated_strings = "some strings that are " "concatenated implicitly, so if you put them on separate " "lines it will fit" +del concatenated_strings, string_variable_name, normal_function_name, normal_name, need_more_to_make_the_line_long_enough +del ([], name_1, name_2), [(), [], name_4, name_3], name_1[[name_2 for name_1 in name_0]] +del (), diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/cantfit.py.expect b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/cantfit.py.expect new file mode 100644 index 0000000000..708959ceec --- /dev/null +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/cantfit.py.expect @@ -0,0 +1,61 @@ +# long variable name +this_is_a_ridiculously_long_name_and_nobody_in_their_right_mind_would_use_one_like_it = ( + 0 +) +this_is_a_ridiculously_long_name_and_nobody_in_their_right_mind_would_use_one_like_it = ( + 1 # with a comment +) +this_is_a_ridiculously_long_name_and_nobody_in_their_right_mind_would_use_one_like_it = [ + 1, + 2, + 3, +] +this_is_a_ridiculously_long_name_and_nobody_in_their_right_mind_would_use_one_like_it = ( + function() +) +this_is_a_ridiculously_long_name_and_nobody_in_their_right_mind_would_use_one_like_it = function( + arg1, arg2, arg3 +) +this_is_a_ridiculously_long_name_and_nobody_in_their_right_mind_would_use_one_like_it = function( + [1, 2, 3], arg1, [1, 2, 3], arg2, [1, 2, 3], arg3 +) +# long function name +normal_name = ( + but_the_function_name_is_now_ridiculously_long_and_it_is_still_super_annoying() +) +normal_name = ( + but_the_function_name_is_now_ridiculously_long_and_it_is_still_super_annoying( + arg1, arg2, arg3 + ) +) +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 + ) +) +string_variable_name = "a string that is waaaaaaaayyyyyyyy too long, even in parens, there's nothing you can do" # noqa +for key in """ + hostname + port + username +""".split(): + if key in self.connect_kwargs: + raise ValueError(err.format(key)) +concatenated_strings = ( + "some strings that are " + "concatenated implicitly, so if you put them on separate " + "lines it will fit" +) +del ( + concatenated_strings, + string_variable_name, + normal_function_name, + normal_name, + need_more_to_make_the_line_long_enough, +) +del ( + ([], name_1, name_2), + [(), [], name_4, name_3], + name_1[[name_2 for name_1 in name_0]], +) +del ((),) 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 index fa1bf06046..01777c6d8f 100644 --- 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 @@ -1 +1 @@ -{"target_version": "3.8"} +{"target_version": "3.8"} \ 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 index f0ae005363..ec4ceb907c 100644 --- 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 @@ -82,3 +82,9 @@ async def func(): argument1, argument2, argument3="some_value" ): pass + + + +# don't remove the brackets here, it changes the meaning of the code. +with (x, y) as z: + 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 index 1b8e865933..ccf073c802 100644 --- 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 @@ -83,3 +83,8 @@ async def func(): some_other_function(argument1, argument2, argument3="some_value"), ): pass + + +# don't remove the brackets here, it changes the meaning of the code. +with (x, y) as z: + pass diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/docstring_newline.py b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/docstring_newline.py new file mode 100644 index 0000000000..75b8db4817 --- /dev/null +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/docstring_newline.py @@ -0,0 +1,3 @@ +""" +87 characters ............................................................................ +""" diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/docstring_newline.py.expect b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/docstring_newline.py.expect new file mode 100644 index 0000000000..75b8db4817 --- /dev/null +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/docstring_newline.py.expect @@ -0,0 +1,3 @@ +""" +87 characters ............................................................................ +""" diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/fmtskip9.options.json b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/fmtskip10.options.json similarity index 100% rename from crates/ruff_python_formatter/resources/test/fixtures/black/cases/fmtskip9.options.json rename to crates/ruff_python_formatter/resources/test/fixtures/black/cases/fmtskip10.options.json diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/fmtskip10.py b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/fmtskip10.py new file mode 100644 index 0000000000..f271d57aa8 --- /dev/null +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/fmtskip10.py @@ -0,0 +1,8 @@ +def foo(): return "mock" # fmt: skip +if True: print("yay") # fmt: skip +for i in range(10): print(i) # fmt: skip + +j = 1 # fmt: skip +while j < 10: j += 1 # fmt: skip + +b = [c for c in "A very long string that would normally generate some kind of collapse, since it is this long"] # fmt: skip diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/fmtskip10.py.expect b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/fmtskip10.py.expect new file mode 100644 index 0000000000..f271d57aa8 --- /dev/null +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/fmtskip10.py.expect @@ -0,0 +1,8 @@ +def foo(): return "mock" # fmt: skip +if True: print("yay") # fmt: skip +for i in range(10): print(i) # fmt: skip + +j = 1 # fmt: skip +while j < 10: j += 1 # fmt: skip + +b = [c for c in "A very long string that would normally generate some kind of collapse, since it is this long"] # fmt: skip diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/fmtskip11.py b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/fmtskip11.py new file mode 100644 index 0000000000..5d3f7874e5 --- /dev/null +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/fmtskip11.py @@ -0,0 +1,6 @@ +def foo(): + pass + + +# comment 1 # fmt: skip +# comment 2 diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/fmtskip11.py.expect b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/fmtskip11.py.expect new file mode 100644 index 0000000000..5d3f7874e5 --- /dev/null +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/fmtskip11.py.expect @@ -0,0 +1,6 @@ +def foo(): + pass + + +# comment 1 # fmt: skip +# comment 2 diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/format_unicode_escape_seq.py b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/format_unicode_escape_seq.py new file mode 100644 index 0000000000..70699c5663 --- /dev/null +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/format_unicode_escape_seq.py @@ -0,0 +1,15 @@ +x = "\x1F" +x = "\\x1B" +x = "\\\x1B" +x = "\U0001F60E" +x = "\u0001F60E" +x = r"\u0001F60E" +x = "don't format me" +x = "\xA3" +x = "\u2717" +x = "\uFaCe" +x = "\N{ox}\N{OX}" +x = "\N{lAtIn smaLL letteR x}" +x = "\N{CYRILLIC small LETTER BYELORUSSIAN-UKRAINIAN I}" +x = b"\x1Fdon't byte" +x = rb"\x1Fdon't format" diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/format_unicode_escape_seq.py.expect b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/format_unicode_escape_seq.py.expect new file mode 100644 index 0000000000..d12e858bf0 --- /dev/null +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/format_unicode_escape_seq.py.expect @@ -0,0 +1,15 @@ +x = "\x1f" +x = "\\x1B" +x = "\\\x1b" +x = "\U0001f60e" +x = "\u0001F60E" +x = r"\u0001F60E" +x = "don't format me" +x = "\xa3" +x = "\u2717" +x = "\uface" +x = "\N{OX}\N{OX}" +x = "\N{LATIN SMALL LETTER X}" +x = "\N{CYRILLIC SMALL LETTER BYELORUSSIAN-UKRAINIAN I}" +x = b"\x1fdon't byte" +x = rb"\x1Fdon't format" diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/fstring.options.json b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/fstring.options.json deleted file mode 100644 index a97114e048..0000000000 --- a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/fstring.options.json +++ /dev/null @@ -1 +0,0 @@ -{"target_version": "3.12"} diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/fstring_quotations.py b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/fstring_quotations.py new file mode 100644 index 0000000000..d82ce7980f --- /dev/null +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/fstring_quotations.py @@ -0,0 +1,34 @@ +# Regression tests for long f-strings, including examples from issue #3623 + +a = ( + 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa' + f'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"{"b"}"' +) + +a = ( + f'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"{"b"}"' + 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa' +) + +a = 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa' + \ + f'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"{"b"}"' + +a = f'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"{"b"}"' + \ + f'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"{"b"}"' + +a = ( + f'bbbbbbb"{"b"}"' + 'aaaaaaaa' +) + +a = ( + f'"{"b"}"' +) + +a = ( + f'\"{"b"}\"' +) + +a = ( + r'\"{"b"}\"' +) diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/fstring_quotations.py.expect b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/fstring_quotations.py.expect new file mode 100644 index 0000000000..30f456cd22 --- /dev/null +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/fstring_quotations.py.expect @@ -0,0 +1,29 @@ +# Regression tests for long f-strings, including examples from issue #3623 + +a = ( + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + f'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"{"b"}"' +) + +a = ( + f'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"{"b"}"' + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" +) + +a = ( + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + + f'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"{"b"}"' +) + +a = ( + f'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"{"b"}"' + + f'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"{"b"}"' +) + +a = f'bbbbbbb"{"b"}"' "aaaaaaaa" + +a = f'"{"b"}"' + +a = f'"{"b"}"' + +a = r'\"{"b"}\"' diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/funcdef_return_type_trailing_comma.options.json b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/funcdef_return_type_trailing_comma.options.json index 1266ed1e66..717b73130f 100644 --- a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/funcdef_return_type_trailing_comma.options.json +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/funcdef_return_type_trailing_comma.options.json @@ -1 +1 @@ -{"preview": "enabled", "target_version": "3.10"} \ No newline at end of file +{"target_version": "3.10"} \ No newline at end of file diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/generics_wrapping.options.json b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/generics_wrapping.options.json new file mode 100644 index 0000000000..4295ffe1df --- /dev/null +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/generics_wrapping.options.json @@ -0,0 +1 @@ +{"target_version": "3.12"} \ No newline at end of file diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/generics_wrapping.py b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/generics_wrapping.py new file mode 100644 index 0000000000..ab85998300 --- /dev/null +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/generics_wrapping.py @@ -0,0 +1,133 @@ +def plain[T, B](a: T, b: T) -> T: + return a + +def arg_magic[T, B](a: T, b: T,) -> T: + return a + +def type_param_magic[T, B,](a: T, b: T) -> T: + return a + +def both_magic[T, B,](a: T, b: T,) -> T: + return a + + +def plain_multiline[ + T, + B +]( + a: T, + b: T +) -> T: + return a + +def arg_magic_multiline[ + T, + B +]( + a: T, + b: T, +) -> T: + return a + +def type_param_magic_multiline[ + T, + B, +]( + a: T, + b: T +) -> T: + return a + +def both_magic_multiline[ + T, + B, +]( + a: T, + b: T, +) -> T: + return a + + +def plain_mixed1[ + T, + B +](a: T, b: T) -> T: + return a + +def plain_mixed2[T, B]( + a: T, + b: T +) -> T: + return a + +def arg_magic_mixed1[ + T, + B +](a: T, b: T,) -> T: + return a + +def arg_magic_mixed2[T, B]( + a: T, + b: T, +) -> T: + return a + +def type_param_magic_mixed1[ + T, + B, +](a: T, b: T) -> T: + return a + +def type_param_magic_mixed2[T, B,]( + a: T, + b: T +) -> T: + return a + +def both_magic_mixed1[ + T, + B, +](a: T, b: T,) -> T: + return a + +def both_magic_mixed2[T, B,]( + a: T, + b: T, +) -> T: + return a + +def something_something_function[ + T: Model +](param: list[int], other_param: type[T], *, some_other_param: bool = True) -> QuerySet[ + T +]: + pass + + +def func[A_LOT_OF_GENERIC_TYPES: AreBeingDefinedHere, LIKE_THIS, AND_THIS, ANOTHER_ONE, AND_YET_ANOTHER_ONE: ThisOneHasTyping](a: T, b: T, c: T, d: T, e: T, f: T, g: T, h: T, i: T, j: T, k: T, l: T, m: T, n: T, o: T, p: T) -> T: + return a + + +def with_random_comments[ + Z + # bye +](): + return a + + +def func[ + T, # comment + U # comment + , + Z: # comment + int +](): pass + + +def func[ + T, # comment but it's long so it doesn't just move to the end of the line + U # comment comment comm comm ent ent + , + Z: # comment ent ent comm comm comment + int +](): pass diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/generics_wrapping.py.expect b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/generics_wrapping.py.expect new file mode 100644 index 0000000000..1631dd320a --- /dev/null +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/generics_wrapping.py.expect @@ -0,0 +1,170 @@ +def plain[T, B](a: T, b: T) -> T: + return a + + +def arg_magic[T, B]( + a: T, + b: T, +) -> T: + return a + + +def type_param_magic[ + T, + B, +]( + a: T, b: T +) -> T: + return a + + +def both_magic[ + T, + B, +]( + a: T, + b: T, +) -> T: + return a + + +def plain_multiline[T, B](a: T, b: T) -> T: + return a + + +def arg_magic_multiline[T, B]( + a: T, + b: T, +) -> T: + return a + + +def type_param_magic_multiline[ + T, + B, +]( + a: T, b: T +) -> T: + return a + + +def both_magic_multiline[ + T, + B, +]( + a: T, + b: T, +) -> T: + return a + + +def plain_mixed1[T, B](a: T, b: T) -> T: + return a + + +def plain_mixed2[T, B](a: T, b: T) -> T: + return a + + +def arg_magic_mixed1[T, B]( + a: T, + b: T, +) -> T: + return a + + +def arg_magic_mixed2[T, B]( + a: T, + b: T, +) -> T: + return a + + +def type_param_magic_mixed1[ + T, + B, +]( + a: T, b: T +) -> T: + return a + + +def type_param_magic_mixed2[ + T, + B, +]( + a: T, b: T +) -> T: + return a + + +def both_magic_mixed1[ + T, + B, +]( + a: T, + b: T, +) -> T: + return a + + +def both_magic_mixed2[ + T, + B, +]( + a: T, + b: T, +) -> T: + return a + + +def something_something_function[T: Model]( + param: list[int], other_param: type[T], *, some_other_param: bool = True +) -> QuerySet[T]: + pass + + +def func[ + A_LOT_OF_GENERIC_TYPES: AreBeingDefinedHere, + LIKE_THIS, + AND_THIS, + ANOTHER_ONE, + AND_YET_ANOTHER_ONE: ThisOneHasTyping, +]( + a: T, + b: T, + c: T, + d: T, + e: T, + f: T, + g: T, + h: T, + i: T, + j: T, + k: T, + l: T, + m: T, + n: T, + o: T, + p: T, +) -> T: + return a + + +def with_random_comments[ + Z + # bye +](): + return a + + +def func[T, U, Z: int](): # comment # comment # comment + pass + + +def func[ + T, # comment but it's long so it doesn't just move to the end of the line + U, # comment comment comm comm ent ent + Z: int, # comment ent ent comm comm comment +](): + pass diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/line_ranges_basic.py b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/line_ranges_basic.py index 3dc4e3af71..c39bb99bcf 100644 --- a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/line_ranges_basic.py +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/line_ranges_basic.py @@ -6,7 +6,7 @@ def foo2(parameter_1, parameter_2, parameter_3, parameter_4, parameter_5, parame def foo3(parameter_1, parameter_2, parameter_3, parameter_4, parameter_5, parameter_6, parameter_7): pass def foo4(parameter_1, parameter_2, parameter_3, parameter_4, parameter_5, parameter_6, parameter_7): pass -# Adding some unformatted code covering a wide range of syntaxes. +# Adding some unformated code covering a wide range of syntaxes. if True: # Incorrectly indented prefix comments. diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/line_ranges_basic.py.expect b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/line_ranges_basic.py.expect index 01c9c002e3..7fdfdfd0db 100644 --- a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/line_ranges_basic.py.expect +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/line_ranges_basic.py.expect @@ -28,7 +28,7 @@ def foo3( def foo4(parameter_1, parameter_2, parameter_3, parameter_4, parameter_5, parameter_6, parameter_7): pass -# Adding some unformatted code covering a wide range of syntaxes. +# Adding some unformated code covering a wide range of syntaxes. if True: # Incorrectly indented prefix comments. diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/line_ranges_decorator_edge_case.py b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/line_ranges_decorator_edge_case.py new file mode 100644 index 0000000000..483fbe8c57 --- /dev/null +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/line_ranges_decorator_edge_case.py @@ -0,0 +1,8 @@ +# flags: --line-ranges=6-7 +class Foo: + + @overload + def foo(): ... + + def fox(self): + print() diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/line_ranges_decorator_edge_case.py.expect b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/line_ranges_decorator_edge_case.py.expect new file mode 100644 index 0000000000..483fbe8c57 --- /dev/null +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/line_ranges_decorator_edge_case.py.expect @@ -0,0 +1,8 @@ +# flags: --line-ranges=6-7 +class Foo: + + @overload + def foo(): ... + + def fox(self): + print() diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/long_strings__type_annotations.py b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/long_strings__type_annotations.py new file mode 100644 index 0000000000..4907fec71c --- /dev/null +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/long_strings__type_annotations.py @@ -0,0 +1,28 @@ +def func( + arg1, + arg2, +) -> Set["this_is_a_very_long_module_name.AndAVeryLongClasName" + ".WithAVeryVeryVeryVeryVeryLongSubClassName"]: + pass + + +def func( + argument: ( + "VeryLongClassNameWithAwkwardGenericSubtype[int] |" + "VeryLongClassNameWithAwkwardGenericSubtype[str]" + ), +) -> ( + "VeryLongClassNameWithAwkwardGenericSubtype[int] |" + "VeryLongClassNameWithAwkwardGenericSubtype[str]" +): + pass + + +def func( + argument: ( + "int |" + "str" + ), +) -> Set["int |" + " str"]: + pass diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/long_strings__type_annotations.py.expect b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/long_strings__type_annotations.py.expect new file mode 100644 index 0000000000..679df21eed --- /dev/null +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/long_strings__type_annotations.py.expect @@ -0,0 +1,26 @@ +def func( + arg1, + arg2, +) -> Set[ + "this_is_a_very_long_module_name.AndAVeryLongClasName" + ".WithAVeryVeryVeryVeryVeryLongSubClassName" +]: + pass + + +def func( + argument: ( + "VeryLongClassNameWithAwkwardGenericSubtype[int] |" + "VeryLongClassNameWithAwkwardGenericSubtype[str]" + ), +) -> ( + "VeryLongClassNameWithAwkwardGenericSubtype[int] |" + "VeryLongClassNameWithAwkwardGenericSubtype[str]" +): + pass + + +def func( + argument: "int |" "str", +) -> Set["int |" " str"]: + pass 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 0e29b14bf4..724e28cd4e 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 (only removed with unify_docstring_detection on): +With trailing spaces: 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 7c31ef75cf..9b270c521b 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 (only removed with unify_docstring_detection on): +With trailing spaces: 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/is_simple_lookup_for_doublestar_expression.options.json b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/module_docstring_after_comment.options.json similarity index 100% rename from crates/ruff_python_formatter/resources/test/fixtures/black/cases/is_simple_lookup_for_doublestar_expression.options.json rename to crates/ruff_python_formatter/resources/test/fixtures/black/cases/module_docstring_after_comment.options.json diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/module_docstring_after_comment.py b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/module_docstring_after_comment.py new file mode 100644 index 0000000000..7374829c44 --- /dev/null +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/module_docstring_after_comment.py @@ -0,0 +1,9 @@ +#!/python + +# regression test for #4762 +""" +docstring +""" +from __future__ import annotations + +import os diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/module_docstring_after_comment.py.expect b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/module_docstring_after_comment.py.expect new file mode 100644 index 0000000000..50c57b9d8a --- /dev/null +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/module_docstring_after_comment.py.expect @@ -0,0 +1,10 @@ +#!/python + +# regression test for #4762 +""" +docstring +""" + +from __future__ import annotations + +import os 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 index 3e1118fba6..fa042f4933 100644 --- 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 @@ -25,5 +25,4 @@ class MultilineDocstringsAsWell: 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_with_if_stmt.options.json b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/pattern_matching_with_if_stmt.options.json index 1266ed1e66..717b73130f 100644 --- a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/pattern_matching_with_if_stmt.options.json +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/pattern_matching_with_if_stmt.options.json @@ -1 +1 @@ -{"preview": "enabled", "target_version": "3.10"} \ No newline at end of file +{"target_version": "3.10"} \ No newline at end of file diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/pep604_union_types_line_breaks.py b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/pep604_union_types_line_breaks.py index 930759735b..bd3e48417b 100644 --- a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/pep604_union_types_line_breaks.py +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/pep604_union_types_line_breaks.py @@ -19,7 +19,7 @@ z: (Short z: (int) = 2.3 z: ((int)) = foo() -# In case I go for not enforcing parentheses, this might get improved at the same time +# In case I go for not enforcing parantheses, this might get improved at the same time x = ( z == 9999999999999999999999999999999999999999 diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/pep604_union_types_line_breaks.py.expect b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/pep604_union_types_line_breaks.py.expect index e9c2f75f7e..ab0a4d9677 100644 --- a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/pep604_union_types_line_breaks.py.expect +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/pep604_union_types_line_breaks.py.expect @@ -28,7 +28,7 @@ z: Short | Short2 | Short3 | Short4 = 8 z: int = 2.3 z: int = foo() -# In case I go for not enforcing parentheses, this might get improved at the same time +# In case I go for not enforcing parantheses, this might get improved at the same time x = ( z == 9999999999999999999999999999999999999999 diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/pep646_typed_star_arg_type_var_tuple.options.json b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/pep646_typed_star_arg_type_var_tuple.options.json new file mode 100644 index 0000000000..dcb1b48257 --- /dev/null +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/pep646_typed_star_arg_type_var_tuple.options.json @@ -0,0 +1 @@ +{"target_version": "3.11"} \ No newline at end of file diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/pep646_typed_star_arg_type_var_tuple.py b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/pep646_typed_star_arg_type_var_tuple.py new file mode 100644 index 0000000000..cd44304ce3 --- /dev/null +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/pep646_typed_star_arg_type_var_tuple.py @@ -0,0 +1,5 @@ +def fn(*args: *tuple[*A, B]) -> None: + pass + + +fn.__annotations__ diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/pep646_typed_star_arg_type_var_tuple.py.expect b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/pep646_typed_star_arg_type_var_tuple.py.expect new file mode 100644 index 0000000000..cd44304ce3 --- /dev/null +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/pep646_typed_star_arg_type_var_tuple.py.expect @@ -0,0 +1,5 @@ +def fn(*args: *tuple[*A, B]) -> None: + pass + + +fn.__annotations__ 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 3487ac8536..defb0f99e3 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 @@ -13,3 +13,8 @@ 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) + + +# Don't remove parens when assignment expr is one of the exprs in a with statement +with x, (a := b): + pass 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 3487ac8536..defb0f99e3 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 @@ -13,3 +13,8 @@ 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) + + +# Don't remove parens when assignment expr is one of the exprs in a with statement +with x, (a := b): + pass diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/pep_701.py b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/pep_701.py index 8224f38ea2..eff207ff8a 100644 --- a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/pep_701.py +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/pep_701.py @@ -73,8 +73,9 @@ x = f"a{2+2:=^{foo(x+y**2):something else}}b" x = f"a{2+2:=^{foo(x+y**2):something else}one more}b" f'{(abc:=10)}' -f"This is a really long string, but just make sure that you reflow fstrings { - 2+2:d}" +f"""This is a really long string, but just make sure that you reflow fstrings { + 2+2:d +}""" f"This is a really long string, but just make sure that you reflow fstrings correctly {2+2:d}" f"{2+2=}" diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/pep_701.py.expect b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/pep_701.py.expect index 74a6ecd5e7..85b8db2ff6 100644 --- a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/pep_701.py.expect +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/pep_701.py.expect @@ -73,9 +73,9 @@ x = f"a{2+2:=^{foo(x+y**2):something else}}b" x = f"a{2+2:=^{foo(x+y**2):something else}one more}b" f"{(abc:=10)}" -f"This is a really long string, but just make sure that you reflow fstrings { +f"""This is a really long string, but just make sure that you reflow fstrings { 2+2:d -}" +}""" f"This is a really long string, but just make sure that you reflow fstrings correctly {2+2:d}" f"{2+2=}" diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/prefer_rhs_split_reformatted.py b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/prefer_rhs_split_reformatted.py index 6d91909dfe..7cd0478474 100644 --- a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/prefer_rhs_split_reformatted.py +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/prefer_rhs_split_reformatted.py @@ -10,3 +10,12 @@ first_value, (m1, m2,), third_value = xxxxxx_yyyyyy_zzzzzz_wwwwww_uuuuuuu_vvvvvv # 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 + +# Regression test for #1187 +print( + dict( + a=1, + b=2 if some_kind_of_data is not None else some_other_kind_of_data, # some explanation of why this is actually necessary + c=3, + ) +) diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/prefer_rhs_split_reformatted.py.expect b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/prefer_rhs_split_reformatted.py.expect index 9a9ef1356e..392cb3ba15 100644 --- a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/prefer_rhs_split_reformatted.py.expect +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/prefer_rhs_split_reformatted.py.expect @@ -19,3 +19,14 @@ xxxxxxxxx_yyy_zzzzzzzz[ xx.xxxxxx(x_yyy_zzzzzz.xxxxx[0]), x_yyy_zzzzzz.xxxxxx(xxxx=1) ] = 1 + +# Regression test for #1187 +print( + dict( + a=1, + b=( + 2 if some_kind_of_data is not None else some_other_kind_of_data + ), # some explanation of why this is actually necessary + c=3, + ) +) diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_comments7.py.expect b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_comments7.py.expect index 9e583ab571..bddab7e5da 100644 --- a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_comments7.py.expect +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_comments7.py.expect @@ -29,7 +29,6 @@ from com.my_lovely_company.my_lovely_team.my_lovely_project.my_lovely_component MyLovelyCompanyTeamProjectComponent as component, # DRY ) - result = 1 # look ma, no comment migration xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx result = 1 # look ma, no comment migration xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/module_docstring_2.options.json b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_fstring.options.json similarity index 100% rename from crates/ruff_python_formatter/resources/test/fixtures/black/cases/module_docstring_2.options.json rename to crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_fstring.options.json diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_fstring.py b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_fstring.py new file mode 100644 index 0000000000..152cae13f0 --- /dev/null +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_fstring.py @@ -0,0 +1 @@ +f"{''=}" f'{""=}' diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_fstring.py.expect b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_fstring.py.expect new file mode 100644 index 0000000000..152cae13f0 --- /dev/null +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_fstring.py.expect @@ -0,0 +1 @@ +f"{''=}" f'{""=}' diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/typed_params_trailing_comma.options.json b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_import_line_collapse.options.json similarity index 100% rename from crates/ruff_python_formatter/resources/test/fixtures/black/cases/typed_params_trailing_comma.options.json rename to crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_import_line_collapse.options.json diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_import_line_collapse.py b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_import_line_collapse.py new file mode 100644 index 0000000000..08195ec4cb --- /dev/null +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_import_line_collapse.py @@ -0,0 +1,90 @@ +from middleman.authentication import validate_oauth_token + + +logger = logging.getLogger(__name__) + + +# case 2 comment after import +from middleman.authentication import validate_oauth_token +#comment + +logger = logging.getLogger(__name__) + + +# case 3 comment after import +from middleman.authentication import validate_oauth_token +# comment +logger = logging.getLogger(__name__) + + +from middleman.authentication import validate_oauth_token + + + +logger = logging.getLogger(__name__) + + +# case 4 try catch with import after import +import os +import os + + + +try: + import os +except Exception: + pass + +try: + import os + def func(): + a = 1 +except Exception: + pass + + +# case 5 multiple imports +import os +import os + +import os +import os + + + + + +for i in range(10): + print(i) + + +# case 6 import in function +def func(): + print() + import os + def func(): + pass + print() + + +def func(): + import os + a = 1 + print() + + +def func(): + import os + + + a = 1 + print() + + +def func(): + import os + + + + a = 1 + print() diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_import_line_collapse.py.expect b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_import_line_collapse.py.expect new file mode 100644 index 0000000000..c6dea1666e --- /dev/null +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_import_line_collapse.py.expect @@ -0,0 +1,85 @@ +from middleman.authentication import validate_oauth_token + +logger = logging.getLogger(__name__) + + +# case 2 comment after import +from middleman.authentication import validate_oauth_token + +# comment + +logger = logging.getLogger(__name__) + + +# case 3 comment after import +from middleman.authentication import validate_oauth_token + +# comment +logger = logging.getLogger(__name__) + + +from middleman.authentication import validate_oauth_token + +logger = logging.getLogger(__name__) + + +# case 4 try catch with import after import +import os +import os + +try: + import os +except Exception: + pass + +try: + import os + + def func(): + a = 1 + +except Exception: + pass + + +# case 5 multiple imports +import os +import os + +import os +import os + +for i in range(10): + print(i) + + +# case 6 import in function +def func(): + print() + import os + + def func(): + pass + + print() + + +def func(): + import os + + a = 1 + print() + + +def func(): + import os + + a = 1 + print() + + +def func(): + import os + + a = 1 + print() diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_long_dict_values.py b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_long_dict_values.py index 04355270d9..a6e137e1f4 100644 --- a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_long_dict_values.py +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_long_dict_values.py @@ -1,3 +1,24 @@ +x = { + "xx_xxxxx_xxxxxxxxxx_xxxxxxxxx_xx": ( + "xx:xxxxxxxxxxxxxxxxx_xxxxx_xxxxxxx_xxxxxxxxxxx{xx}xxx_xxxxx_xxxxxxxxx_xxxxxxxxxxxx_xxxx" + ) +} +x = { + "xx_xxxxx_xxxxxxxxxx_xxxxxxxxx_xx": ( + "xx:xxxxxxxxxxxxxxxxx_xxxxx_xxxxxxx_xxxxxxxxxxx{xx}xxx_xxxxx_xxxxxxxxx_xxxxxxxxxxxx_xxxx" + ), +} +x = { + "foo": bar, + "foo": bar, + "foo": ( + xx_xxxxxxxxxxxxxxxxx_xxxxx_xxxxxxx_xxxxxxxxxxxxxx_xxxxx_xxxxxxxxx_xxxxxxxxxxxx_xxxx + ), +} +x = { + "xx_xxxxx_xxxxxxxxxx_xxxxxxxxx_xx": "xx:xxxxxxxxxxxxxxxxx_xxxxx_xxxxxxx_xxxxxxxxxx" +} + my_dict = { "something_something": r"Lorem ipsum dolor sit amet, an sed convenire eloquentiam \t" @@ -5,23 +26,90 @@ my_dict = { r"signiferumque, duo ea vocibus consetetur scriptorem. Facer \t", } +# Function calls as keys +tasks = { + get_key_name( + foo, + bar, + baz, + ): src, + loop.run_in_executor(): src, + loop.run_in_executor(xx_xxxxxxxxxxxxxxxxx_xxxxx_xxxxxxx_xxxxxxxxxxxxxx): src, + loop.run_in_executor( + xx_xxxxxxxxxxxxxxxxx_xxxxx_xxxxxxx_xxxxxxxxxxxxxx_xxxxx_xxxxx + ): src, + loop.run_in_executor(): ( + xx_xxxxxxxxxxxxxxxxx_xxxxx_xxxxxxx_xxxxxxxxxxxxxx_xxxxx_xxxxxxxxx_xxxxxxxxxxxx_xxxx + ), +} + +# Dictionary comprehensions +tasks = { + key_name: ( + xx_xxxxxxxxxxxxxxxxx_xxxxx_xxxxxxx_xxxxxxxxxxxxxx_xxxxx_xxxxxxxxx_xxxxxxxxxxxx_xxxx + ) + for src in sources +} +tasks = {key_name: foobar for src in sources} +tasks = { + get_key_name( + src, + ): "foo" + for src in sources +} +tasks = { + get_key_name( + foo, + bar, + baz, + ): src + for src in sources +} +tasks = { + get_key_name(): ( + xx_xxxxxxxxxxxxxxxxx_xxxxx_xxxxxxx_xxxxxxxxxxxxxx_xxxxx_xxxxxxxxx_xxxxxxxxxxxx_xxxx + ) + for src in sources +} +tasks = {get_key_name(): foobar for src in sources} + + +# Delimiters inside the value +def foo(): + def bar(): + x = { + common.models.DateTimeField: datetime(2020, 1, 31, tzinfo=utc) + timedelta( + days=i + ), + } + x = { + common.models.DateTimeField: ( + datetime(2020, 1, 31, tzinfo=utc) + timedelta(days=i) + ), + } + x = { + "foobar": (123 + 456), + } + x = { + "foobar": (123) + 456, + } + + my_dict = { "a key in my dict": a_very_long_variable * and_a_very_long_function_call() / 100000.0 } - my_dict = { "a key in my dict": a_very_long_variable * and_a_very_long_function_call() * and_another_long_func() / 100000.0 } - my_dict = { "a key in my dict": MyClass.some_attribute.first_call().second_call().third_call(some_args="some value") } { - 'xxxxxx': + "xxxxxx": xxxxxxxxxxxxxxxxxxx.xxxxxxxxxxxxxx( xxxxxxxxxxxxxx={ - 'x': + "x": xxxxxxxxxxxxxxxxxxxxxxxxxx.xxxxxxxxxxxxxxxxxxxxxxxxxxxxx( xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx=( xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx @@ -29,8 +117,8 @@ my_dict = { xxxxxxxxxxxxx=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx .xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx( xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx={ - 'x': x.xx, - 'x': x.x, + "x": x.xx, + "x": x.x, })))) }), } diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_long_dict_values.py.expect b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_long_dict_values.py.expect index 40582b3632..7d7e2fad5e 100644 --- a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_long_dict_values.py.expect +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_long_dict_values.py.expect @@ -1,3 +1,24 @@ +x = { + "xx_xxxxx_xxxxxxxxxx_xxxxxxxxx_xx": ( + "xx:xxxxxxxxxxxxxxxxx_xxxxx_xxxxxxx_xxxxxxxxxxx{xx}xxx_xxxxx_xxxxxxxxx_xxxxxxxxxxxx_xxxx" + ) +} +x = { + "xx_xxxxx_xxxxxxxxxx_xxxxxxxxx_xx": ( + "xx:xxxxxxxxxxxxxxxxx_xxxxx_xxxxxxx_xxxxxxxxxxx{xx}xxx_xxxxx_xxxxxxxxx_xxxxxxxxxxxx_xxxx" + ), +} +x = { + "foo": bar, + "foo": bar, + "foo": ( + xx_xxxxxxxxxxxxxxxxx_xxxxx_xxxxxxx_xxxxxxxxxxxxxx_xxxxx_xxxxxxxxx_xxxxxxxxxxxx_xxxx + ), +} +x = { + "xx_xxxxx_xxxxxxxxxx_xxxxxxxxx_xx": "xx:xxxxxxxxxxxxxxxxx_xxxxx_xxxxxxx_xxxxxxxxxx" +} + my_dict = { "something_something": ( r"Lorem ipsum dolor sit amet, an sed convenire eloquentiam \t" @@ -6,12 +27,80 @@ my_dict = { ), } +# Function calls as keys +tasks = { + get_key_name( + foo, + bar, + baz, + ): src, + loop.run_in_executor(): src, + loop.run_in_executor(xx_xxxxxxxxxxxxxxxxx_xxxxx_xxxxxxx_xxxxxxxxxxxxxx): src, + loop.run_in_executor( + xx_xxxxxxxxxxxxxxxxx_xxxxx_xxxxxxx_xxxxxxxxxxxxxx_xxxxx_xxxxx + ): src, + loop.run_in_executor(): ( + xx_xxxxxxxxxxxxxxxxx_xxxxx_xxxxxxx_xxxxxxxxxxxxxx_xxxxx_xxxxxxxxx_xxxxxxxxxxxx_xxxx + ), +} + +# Dictionary comprehensions +tasks = { + key_name: ( + xx_xxxxxxxxxxxxxxxxx_xxxxx_xxxxxxx_xxxxxxxxxxxxxx_xxxxx_xxxxxxxxx_xxxxxxxxxxxx_xxxx + ) + for src in sources +} +tasks = {key_name: foobar for src in sources} +tasks = { + get_key_name( + src, + ): "foo" + for src in sources +} +tasks = { + get_key_name( + foo, + bar, + baz, + ): src + for src in sources +} +tasks = { + get_key_name(): ( + xx_xxxxxxxxxxxxxxxxx_xxxxx_xxxxxxx_xxxxxxxxxxxxxx_xxxxx_xxxxxxxxx_xxxxxxxxxxxx_xxxx + ) + for src in sources +} +tasks = {get_key_name(): foobar for src in sources} + + +# Delimiters inside the value +def foo(): + def bar(): + x = { + common.models.DateTimeField: ( + datetime(2020, 1, 31, tzinfo=utc) + timedelta(days=i) + ), + } + x = { + common.models.DateTimeField: ( + datetime(2020, 1, 31, tzinfo=utc) + timedelta(days=i) + ), + } + x = { + "foobar": 123 + 456, + } + x = { + "foobar": (123) + 456, + } + + my_dict = { "a key in my dict": ( a_very_long_variable * and_a_very_long_function_call() / 100000.0 ) } - my_dict = { "a key in my dict": ( a_very_long_variable @@ -20,7 +109,6 @@ my_dict = { / 100000.0 ) } - my_dict = { "a key in my dict": ( MyClass.some_attribute.first_call() @@ -51,8 +139,8 @@ my_dict = { class Random: def func(): - random_service.status.active_states.inactive = ( - make_new_top_level_state_from_dict({ + random_service.status.active_states.inactive = make_new_top_level_state_from_dict( + { "topLevelBase": { "secondaryBase": { "timestamp": 1234, @@ -63,5 +151,5 @@ class Random: ), } }, - }) + } ) diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_long_strings.py b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_long_strings.py index 017f679115..411c03f540 100644 --- a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_long_strings.py +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_long_strings.py @@ -278,7 +278,7 @@ string_with_escaped_nameescape = ( "........................................................................... \\N{LAO KO LA}" ) -msg = lambda x: f"this is a very very very long lambda value {x} that doesn't fit on a single line" +msg = lambda x: f"this is a very very very very long lambda value {x} that doesn't fit on a single line" dict_with_lambda_values = { "join": lambda j: ( @@ -327,3 +327,17 @@ 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"]}''') log.info(f"""Skipping: {'a' == 'b'} {desc['ms_name']} {money=} {dte=} {pos_share=} {desc['status']} {desc['exposure_max']}""") + +x = { + "xx_xxxxx_xxxxxxxxxx_xxxxxxxxx_xx": ( + "xx:xxxxxxxxxxxxxxxxx_xxxxx_xxxxxxx_xxxxxxxxxxx{xx}xxx_xxxxx_xxxxxxxxx_xxxxxxxxxxxx_xxxx" + ) +} +x = { + "xx_xxxxx_xxxxxxxxxx_xxxxxxxxx_xx": "xx:xxxxxxxxxxxxxxxxx_xxxxx_xxxxxxx_xxxxxxxxxxx{xx}xxx_xxxxx_xxxxxxxxx_xxxxxxxxxxxx_xxxx", +} +x = { + "xx_xxxxx_xxxxxxxxxx_xxxxxxxxx_xx": ( + "xx:xxxxxxxxxxxxxxxxx_xxxxx_xxxxxxx_xxxxxxxxxx" + ) +} diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_long_strings.py.expect b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_long_strings.py.expect index 4b7bdd86d2..f1b0985ef1 100644 --- a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_long_strings.py.expect +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_long_strings.py.expect @@ -508,11 +508,9 @@ string_with_escaped_nameescape = ( " \\N{LAO KO LA}" ) -msg = ( - lambda x: ( - f"this is a very very very long lambda value {x} that doesn't fit on a single" - " line" - ) +msg = lambda x: ( + f"this is a very very very very long lambda value {x} that doesn't fit on a" + " single line" ) dict_with_lambda_values = { @@ -537,43 +535,43 @@ code = ( call(body="%s %s" % (",".join(items), suffix)) log.info( - "Skipping:" - f' {desc["db_id"]=} {desc["ms_name"]} {money=} {dte=} {pos_share=} {desc["status"]=} {desc["exposure_max"]=}' + f'Skipping: {desc["db_id"]=} {desc["ms_name"]} {money=} {dte=} {pos_share=}' + f' {desc["status"]=} {desc["exposure_max"]=}' ) log.info( - "Skipping:" - f" {desc['db_id']=} {desc['ms_name']} {money=} {dte=} {pos_share=} {desc['status']=} {desc['exposure_max']=}" + f"Skipping: {desc['db_id']=} {desc['ms_name']} {money=} {dte=} {pos_share=}" + f" {desc['status']=} {desc['exposure_max']=}" ) 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=")}' + f' {pos_share=} {desc["status"]} {desc["exposure_max"]}' ) log.info( - "Skipping:" - f' {desc["db_id"]} {desc["ms_name"]} {money=} {(x := "abc=")=} {pos_share=} {desc["status"]} {desc["exposure_max"]}' + f'Skipping: {desc["db_id"]} {desc["ms_name"]} {money=} {(x := "abc=")=}' + f' {pos_share=} {desc["status"]} {desc["exposure_max"]}' ) log.info( - "Skipping:" - f' {desc["db_id"]} {foo("bar",x=123)=} {money=} {dte=} {pos_share=} {desc["status"]} {desc["exposure_max"]}' + f'Skipping: {desc["db_id"]} {foo("bar",x=123)=} {money=} {dte=} {pos_share=}' + f' {desc["status"]} {desc["exposure_max"]}' ) log.info( - "Skipping:" - f' {foo("asdf")=} {desc["ms_name"]} {money=} {dte=} {pos_share=} {desc["status"]} {desc["exposure_max"]}' + f'Skipping: {foo("asdf")=} {desc["ms_name"]} {money=} {dte=} {pos_share=}' + f' {desc["status"]} {desc["exposure_max"]}' ) log.info( - "Skipping:" - f" {'a' == 'b' == 'c' == 'd'} {desc['ms_name']} {money=} {dte=} {pos_share=} {desc['status']} {desc['exposure_max']}" + f'Skipping: {"a" == "b" == "c" == "d"} {desc["ms_name"]} {money=} {dte=}' + f' {pos_share=} {desc["status"]} {desc["exposure_max"]}' ) log.info( - "Skipping:" - f' {"a" == "b" == "c" == "d"=} {desc["ms_name"]} {money=} {dte=} {pos_share=} {desc["status"]} {desc["exposure_max"]}' + f'Skipping: {"a" == "b" == "c" == "d"=} {desc["ms_name"]} {money=} {dte=}' + f' {pos_share=} {desc["status"]} {desc["exposure_max"]}' ) log.info( @@ -592,3 +590,17 @@ log.info( log.info( f"""Skipping: {'a' == 'b'} {desc['ms_name']} {money=} {dte=} {pos_share=} {desc['status']} {desc['exposure_max']}""" ) + +x = { + "xx_xxxxx_xxxxxxxxxx_xxxxxxxxx_xx": ( + "xx:xxxxxxxxxxxxxxxxx_xxxxx_xxxxxxx_xxxxxxxxxxx{xx}xxx_xxxxx_xxxxxxxxx_xxxxxxxxxxxx_xxxx" + ) +} +x = { + "xx_xxxxx_xxxxxxxxxx_xxxxxxxxx_xx": ( + "xx:xxxxxxxxxxxxxxxxx_xxxxx_xxxxxxx_xxxxxxxxxxx{xx}xxx_xxxxx_xxxxxxxxx_xxxxxxxxxxxx_xxxx" + ), +} +x = { + "xx_xxxxx_xxxxxxxxxx_xxxxxxxxx_xx": "xx:xxxxxxxxxxxxxxxxx_xxxxx_xxxxxxx_xxxxxxxxxx" +} diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_long_strings__regression.py b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_long_strings__regression.py index 74b6cd43a2..ac0d3a3585 100644 --- a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_long_strings__regression.py +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_long_strings__regression.py @@ -551,6 +551,7 @@ a_dict = { } # Regression test for https://github.com/psf/black/issues/3506. +# Regressed again by https://github.com/psf/black/pull/4498 s = ( "With single quote: ' " f" {my_dict['foo']}" diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_long_strings__regression.py.expect b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_long_strings__regression.py.expect index 15c91275af..bfbfaedef0 100644 --- a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_long_strings__regression.py.expect +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_long_strings__regression.py.expect @@ -672,9 +672,15 @@ a_dict = { } # Regression test for https://github.com/psf/black/issues/3506. -s = f"With single quote: ' {my_dict['foo']} With double quote: \" {my_dict['bar']}" +# Regressed again by https://github.com/psf/black/pull/4498 +s = ( + "With single quote: ' " + f" {my_dict['foo']}" + ' With double quote: " ' + f' {my_dict["bar"]}' +) s = ( "Lorem Ipsum is simply dummy text of the printing and typesetting" - f" industry:'{my_dict['foo']}'" + f' industry:\'{my_dict["foo"]}\'' ) diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_multiline_strings.py b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_multiline_strings.py index 82a8657d6e..8293424771 100644 --- a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_multiline_strings.py +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_multiline_strings.py @@ -180,3 +180,70 @@ test assert some_var == expected_result, f""" expected: {expected_result} actual: {some_var}""" + + +def foo(): + a = { + xxxx_xxxxxxx.xxxxxx_xxxxxxxxxxxx_xxxxxx_xx_xxx_xxxxxx: { + "xxxxx": """Sxxxxx xxxxxxxxxxxx xxx xxxxx (xxxxxx xxx xxxxxxx)""", + "xxxxxxxx": ( + """Sxxxxxxx xxxxxxxx, xxxxxxx xx xxxxxxxxx + xxxxxxxxxxxxx xxxxxxx xxxxxxxxx xxx-xxxxxxxxxx xxxxxx xx xxx-xxxxxx""" + ), + "xxxxxxxx": """Sxxxxxxx xxxxxxxx, xxxxxxx xx xxxxxxxxx + xxxxxxxxxxxxx xxxxxxx xxxxxxxxx xxx-xxxxxxxxxx xxxxxx xx xxx-xxxxxx""" + }, + } + + +xxxx_xxxxxxx.xxxxxx_xxxxxxxxxxxx_xxxxxx_xx_xxx_xxxxxx = { + "xxxxx": """Sxxxxx xxxxxxxxxxxx xxx xxxxx (xxxxxx xxx xxxxxxx)""", + "xxxxxxxx": ( + """Sxxxxxxx xxxxxxxx, xxxxxxx xx xxxxxxxxx + xxxxxxxxxxxxx xxxxxxx xxxxxxxxx xxx-xxxxxxxxxx xxxxxx xx xxx-xxxxxx""" + ), + "xxxxx_xxxxxxxxxx_xxxxxxxxx_xx": ( + """ +a +a +a +a +a""" + ), + "xx_xxxxx_xxxxxxxxxx_xxxxxxxxx_xx": """ +a +a +a +a +a""", +} + +a = """ +""" if """ +""" == """ +""" else """ +""" + +a = """ +""" if b else """ +""" + +a = """ +""" if """ +""" == """ +""" else b + +a = b if """ +""" == """ +""" else """ +""" + +a = """ +""" if b else c + +a = c if b else """ +""" + +a = b if """ +""" == """ +""" else c diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_multiline_strings.py.expect b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_multiline_strings.py.expect index 942ee085ea..0259c82b3e 100644 --- a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_multiline_strings.py.expect +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_multiline_strings.py.expect @@ -214,3 +214,106 @@ test assert some_var == expected_result, f""" expected: {expected_result} actual: {some_var}""" + + +def foo(): + a = { + xxxx_xxxxxxx.xxxxxx_xxxxxxxxxxxx_xxxxxx_xx_xxx_xxxxxx: { + "xxxxx": """Sxxxxx xxxxxxxxxxxx xxx xxxxx (xxxxxx xxx xxxxxxx)""", + "xxxxxxxx": ( + """Sxxxxxxx xxxxxxxx, xxxxxxx xx xxxxxxxxx + xxxxxxxxxxxxx xxxxxxx xxxxxxxxx xxx-xxxxxxxxxx xxxxxx xx xxx-xxxxxx""" + ), + "xxxxxxxx": ( + """Sxxxxxxx xxxxxxxx, xxxxxxx xx xxxxxxxxx + xxxxxxxxxxxxx xxxxxxx xxxxxxxxx xxx-xxxxxxxxxx xxxxxx xx xxx-xxxxxx""" + ), + }, + } + + +xxxx_xxxxxxx.xxxxxx_xxxxxxxxxxxx_xxxxxx_xx_xxx_xxxxxx = { + "xxxxx": """Sxxxxx xxxxxxxxxxxx xxx xxxxx (xxxxxx xxx xxxxxxx)""", + "xxxxxxxx": ( + """Sxxxxxxx xxxxxxxx, xxxxxxx xx xxxxxxxxx + xxxxxxxxxxxxx xxxxxxx xxxxxxxxx xxx-xxxxxxxxxx xxxxxx xx xxx-xxxxxx""" + ), + "xxxxx_xxxxxxxxxx_xxxxxxxxx_xx": ( + """ +a +a +a +a +a""" + ), + "xx_xxxxx_xxxxxxxxxx_xxxxxxxxx_xx": ( + """ +a +a +a +a +a""" + ), +} + +a = ( + """ +""" + if """ +""" + == """ +""" + else """ +""" +) + +a = ( + """ +""" + if b + else """ +""" +) + +a = ( + """ +""" + if """ +""" + == """ +""" + else b +) + +a = ( + b + if """ +""" + == """ +""" + else """ +""" +) + +a = ( + """ +""" + if b + else c +) + +a = ( + c + if b + else """ +""" +) + +a = ( + b + if """ +""" + == """ +""" + else c +) diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_remove_multiline_lone_list_item_parens.options.json b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_remove_multiline_lone_list_item_parens.options.json new file mode 100644 index 0000000000..ce7c52b163 --- /dev/null +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_remove_multiline_lone_list_item_parens.options.json @@ -0,0 +1 @@ +{"preview": "enabled"} \ No newline at end of file diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_remove_multiline_lone_list_item_parens.py b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_remove_multiline_lone_list_item_parens.py new file mode 100644 index 0000000000..8c24f9e829 --- /dev/null +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_remove_multiline_lone_list_item_parens.py @@ -0,0 +1,136 @@ +items = [(x for x in [1])] + +items = [ + ( + {"key1": "val1", "key2": "val2", "key3": "val3"} + if some_var == "long strings" + else {"key": "val"} + ) +] +items = [ + ( + {"key1": "val1", "key2": "val2"} + if some_var == "" + else {"key": "val"} + ) +] +items = [ + ( + "123456890123457890123468901234567890" + if some_var == "long strings" + else "123467890123467890" + ) +] +items = [ + ( + {"key1": "val1", "key2": "val2", "key3": "val3"} + and some_var == "long strings" + and {"key": "val"} + ) +] +items = [ + ( + "123456890123457890123468901234567890" + and some_var == "long strings" + and "123467890123467890" + ) +] +items = [ + ( + long_variable_name + and even_longer_variable_name + and yet_another_very_long_variable_name + ) +] + +# Shouldn't remove trailing commas +items = [ + ( + {"key1": "val1", "key2": "val2", "key3": "val3"} + if some_var == "long strings" + else {"key": "val"} + ), +] +items = [ + ( + {"key1": "val1", "key2": "val2", "key3": "val3"} + and some_var == "long strings" + and {"key": "val"} + ), +] +items = [ + ( + "123456890123457890123468901234567890" + and some_var == "long strings" + and "123467890123467890" + ), +] +items = [ + ( + long_variable_name + and even_longer_variable_name + and yet_another_very_long_variable_name + ), +] + +# Shouldn't add parentheses +items = [ + {"key1": "val1", "key2": "val2", "key3": "val3"} + if some_var == "long strings" + else {"key": "val"} +] +items = [{"key1": "val1", "key2": "val2"} if some_var == "" else {"key": "val"}] + +# Shouldn't crash with comments +items = [ + ( # comment + {"key1": "val1", "key2": "val2", "key3": "val3"} + if some_var == "long strings" + else {"key": "val"} + ) +] +items = [ + ( + {"key1": "val1", "key2": "val2", "key3": "val3"} + if some_var == "long strings" + else {"key": "val"} + ) # comment +] + +items = [ # comment + ( + {"key1": "val1", "key2": "val2", "key3": "val3"} + if some_var == "long strings" + else {"key": "val"} + ) +] +items = [ + ( + {"key1": "val1", "key2": "val2", "key3": "val3"} + if some_var == "long strings" + else {"key": "val"} + ) +] # comment + +items = [ + ( + {"key1": "val1", "key2": "val2", "key3": "val3"} # comment + if some_var == "long strings" + else {"key": "val"} + ) +] + +items = [ # comment + ( # comment + {"key1": "val1", "key2": "val2", "key3": "val3"} + if some_var == "long strings" + else {"key": "val"} + ) +] +items = [ + ( + {"key1": "val1", "key2": "val2", "key3": "val3"} + if some_var == "long strings" + else {"key": "val"} + ) # comment +] # comment diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_remove_multiline_lone_list_item_parens.py.expect b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_remove_multiline_lone_list_item_parens.py.expect new file mode 100644 index 0000000000..e4e9641e16 --- /dev/null +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_remove_multiline_lone_list_item_parens.py.expect @@ -0,0 +1,106 @@ +items = [(x for x in [1])] + +items = [ + {"key1": "val1", "key2": "val2", "key3": "val3"} + if some_var == "long strings" + else {"key": "val"} +] +items = [{"key1": "val1", "key2": "val2"} if some_var == "" else {"key": "val"}] +items = [ + "123456890123457890123468901234567890" + if some_var == "long strings" + else "123467890123467890" +] +items = [ + {"key1": "val1", "key2": "val2", "key3": "val3"} + and some_var == "long strings" + and {"key": "val"} +] +items = [ + "123456890123457890123468901234567890" + and some_var == "long strings" + and "123467890123467890" +] +items = [ + long_variable_name + and even_longer_variable_name + and yet_another_very_long_variable_name +] + +# Shouldn't remove trailing commas +items = [ + ( + {"key1": "val1", "key2": "val2", "key3": "val3"} + if some_var == "long strings" + else {"key": "val"} + ), +] +items = [ + ( + {"key1": "val1", "key2": "val2", "key3": "val3"} + and some_var == "long strings" + and {"key": "val"} + ), +] +items = [ + ( + "123456890123457890123468901234567890" + and some_var == "long strings" + and "123467890123467890" + ), +] +items = [ + ( + long_variable_name + and even_longer_variable_name + and yet_another_very_long_variable_name + ), +] + +# Shouldn't add parentheses +items = [ + {"key1": "val1", "key2": "val2", "key3": "val3"} + if some_var == "long strings" + else {"key": "val"} +] +items = [{"key1": "val1", "key2": "val2"} if some_var == "" else {"key": "val"}] + +# Shouldn't crash with comments +items = [ # comment + {"key1": "val1", "key2": "val2", "key3": "val3"} + if some_var == "long strings" + else {"key": "val"} +] +items = [ + {"key1": "val1", "key2": "val2", "key3": "val3"} + if some_var == "long strings" + else {"key": "val"} +] # comment + +items = [ # comment + {"key1": "val1", "key2": "val2", "key3": "val3"} + if some_var == "long strings" + else {"key": "val"} +] +items = [ + {"key1": "val1", "key2": "val2", "key3": "val3"} + if some_var == "long strings" + else {"key": "val"} +] # comment + +items = [ + {"key1": "val1", "key2": "val2", "key3": "val3"} # comment + if some_var == "long strings" + else {"key": "val"} +] + +items = [ # comment # comment + {"key1": "val1", "key2": "val2", "key3": "val3"} + if some_var == "long strings" + else {"key": "val"} +] +items = [ + {"key1": "val1", "key2": "val2", "key3": "val3"} + if some_var == "long strings" + else {"key": "val"} +] # comment # comment diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_wrap_comprehension_in.options.json b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_wrap_comprehension_in.options.json new file mode 100644 index 0000000000..9f5eb7c209 --- /dev/null +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_wrap_comprehension_in.options.json @@ -0,0 +1 @@ +{"preview": "enabled", "line_width": 79} \ No newline at end of file diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_wrap_comprehension_in.py b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_wrap_comprehension_in.py new file mode 100644 index 0000000000..ec7e802674 --- /dev/null +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_wrap_comprehension_in.py @@ -0,0 +1,69 @@ +[a for graph_path_expression in refined_constraint.condition_as_predicate.variables] +[ + a + for graph_path_expression in refined_constraint.condition_as_predicate.variables +] +[ + a + for graph_path_expression + in refined_constraint.condition_as_predicate.variables +] +[ + a + for graph_path_expression in ( + refined_constraint.condition_as_predicate.variables + ) +] + +[ + (foobar_very_long_key, foobar_very_long_value) + for foobar_very_long_key, foobar_very_long_value in foobar_very_long_dictionary.items() +] + +# Don't split the `in` if it's not too long +lcomp3 = [ + element.split("\n", 1)[0] + for element in collection.select_elements() + # right + if element is not None +] + +# Don't remove parens around ternaries +expected = [i for i in (a if b else c)] + +# Nested arrays +# First in will not be split because it would still be too long +[[ + x + for x in bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb + for y in xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +]] + +# Multiple comprehensions, only split the second `in` +graph_path_expressions_in_local_constraint_refinements = [ + graph_path_expression + for refined_constraint in self._local_constraint_refinements.values() + if refined_constraint is not None + for graph_path_expression in refined_constraint.condition_as_predicate.variables +] + +# Dictionary comprehensions +dict_with_really_long_names = { + really_really_long_key_name: an_even_longer_really_really_long_key_value + for really_really_long_key_name, an_even_longer_really_really_long_key_value in really_really_really_long_dict_name.items() +} +{ + key_with_super_really_long_name: key_with_super_really_long_name + for key_with_super_really_long_name in dictionary_with_super_really_long_name +} +{ + key_with_super_really_long_name: key_with_super_really_long_name + for key_with_super_really_long_name + in dictionary_with_super_really_long_name +} +{ + key_with_super_really_long_name: key_with_super_really_long_name + for key in ( + dictionary + ) +} diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_wrap_comprehension_in.py.expect b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_wrap_comprehension_in.py.expect new file mode 100644 index 0000000000..00c23c0b5c --- /dev/null +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_wrap_comprehension_in.py.expect @@ -0,0 +1,88 @@ +[ + a + for graph_path_expression in ( + refined_constraint.condition_as_predicate.variables + ) +] +[ + a + for graph_path_expression in ( + refined_constraint.condition_as_predicate.variables + ) +] +[ + a + for graph_path_expression in ( + refined_constraint.condition_as_predicate.variables + ) +] +[ + a + for graph_path_expression in ( + refined_constraint.condition_as_predicate.variables + ) +] + +[ + (foobar_very_long_key, foobar_very_long_value) + for foobar_very_long_key, foobar_very_long_value in ( + foobar_very_long_dictionary.items() + ) +] + +# Don't split the `in` if it's not too long +lcomp3 = [ + element.split("\n", 1)[0] + for element in collection.select_elements() + # right + if element is not None +] + +# Don't remove parens around ternaries +expected = [i for i in (a if b else c)] + +# Nested arrays +# First in will not be split because it would still be too long +[ + [ + x + for x in bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb + for y in ( + xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx + ) + ] +] + +# Multiple comprehensions, only split the second `in` +graph_path_expressions_in_local_constraint_refinements = [ + graph_path_expression + for refined_constraint in self._local_constraint_refinements.values() + if refined_constraint is not None + for graph_path_expression in ( + refined_constraint.condition_as_predicate.variables + ) +] + +# Dictionary comprehensions +dict_with_really_long_names = { + really_really_long_key_name: an_even_longer_really_really_long_key_value + for really_really_long_key_name, an_even_longer_really_really_long_key_value in ( + really_really_really_long_dict_name.items() + ) +} +{ + key_with_super_really_long_name: key_with_super_really_long_name + for key_with_super_really_long_name in ( + dictionary_with_super_really_long_name + ) +} +{ + key_with_super_really_long_name: key_with_super_really_long_name + for key_with_super_really_long_name in ( + dictionary_with_super_really_long_name + ) +} +{ + key_with_super_really_long_name: key_with_super_really_long_name + for key in dictionary +} diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/python37.py b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/python37.py index 3fcf6e0ffc..d0ef56e1fa 100644 --- a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/python37.py +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/python37.py @@ -10,6 +10,7 @@ def g(): async def func(): + await ... if test: out_batched = [ i diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/python37.py.expect b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/python37.py.expect index 3fcf6e0ffc..d0ef56e1fa 100644 --- a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/python37.py.expect +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/python37.py.expect @@ -10,6 +10,7 @@ def g(): async def func(): + await ... if test: out_batched = [ i diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/remove_except_types_parens.options.json b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/remove_except_types_parens.options.json new file mode 100644 index 0000000000..084ace6a74 --- /dev/null +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/remove_except_types_parens.options.json @@ -0,0 +1 @@ +{"preview": "enabled", "target_version": "3.14"} \ No newline at end of file diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/remove_except_types_parens.py b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/remove_except_types_parens.py new file mode 100644 index 0000000000..92914e7442 --- /dev/null +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/remove_except_types_parens.py @@ -0,0 +1,123 @@ +# SEE PEP 758 FOR MORE DETAILS +# remains unchanged +try: + pass +except: + pass + +# remains unchanged +try: + pass +except ValueError: + pass + +try: + pass +except* ValueError: + pass + +# parenthesis are removed +try: + pass +except (ValueError): + pass + +try: + pass +except* (ValueError): + pass + +# parenthesis are removed +try: + pass +except (ValueError) as e: + pass + +try: + pass +except* (ValueError) as e: + pass + +# remains unchanged +try: + pass +except (ValueError,): + pass + +try: + pass +except* (ValueError,): + pass + +# remains unchanged +try: + pass +except (ValueError,) as e: + pass + +try: + pass +except* (ValueError,) as e: + pass + +# remains unchanged +try: + pass +except ValueError, TypeError, KeyboardInterrupt: + pass + +try: + pass +except* ValueError, TypeError, KeyboardInterrupt: + pass + +# parenthesis are removed +try: + pass +except (ValueError, TypeError, KeyboardInterrupt): + pass + +try: + pass +except* (ValueError, TypeError, KeyboardInterrupt): + pass + +# parenthesis are not removed +try: + pass +except (ValueError, TypeError, KeyboardInterrupt) as e: + pass + +try: + pass +except* (ValueError, TypeError, KeyboardInterrupt) as e: + pass + +# parenthesis are removed +try: + pass +except (ValueError if True else TypeError): + pass + +try: + pass +except* (ValueError if True else TypeError): + pass + +# inner except: parenthesis are removed +# outer except: parenthsis are not removed +try: + try: + pass + except (TypeError, KeyboardInterrupt): + pass +except (ValueError,): + pass + +try: + try: + pass + except* (TypeError, KeyboardInterrupt): + pass +except* (ValueError,): + pass diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/remove_except_types_parens.py.expect b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/remove_except_types_parens.py.expect new file mode 100644 index 0000000000..9b7021868f --- /dev/null +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/remove_except_types_parens.py.expect @@ -0,0 +1,123 @@ +# SEE PEP 758 FOR MORE DETAILS +# remains unchanged +try: + pass +except: + pass + +# remains unchanged +try: + pass +except ValueError: + pass + +try: + pass +except* ValueError: + pass + +# parenthesis are removed +try: + pass +except ValueError: + pass + +try: + pass +except* ValueError: + pass + +# parenthesis are removed +try: + pass +except ValueError as e: + pass + +try: + pass +except* ValueError as e: + pass + +# remains unchanged +try: + pass +except (ValueError,): + pass + +try: + pass +except* (ValueError,): + pass + +# remains unchanged +try: + pass +except (ValueError,) as e: + pass + +try: + pass +except* (ValueError,) as e: + pass + +# remains unchanged +try: + pass +except ValueError, TypeError, KeyboardInterrupt: + pass + +try: + pass +except* ValueError, TypeError, KeyboardInterrupt: + pass + +# parenthesis are removed +try: + pass +except ValueError, TypeError, KeyboardInterrupt: + pass + +try: + pass +except* ValueError, TypeError, KeyboardInterrupt: + pass + +# parenthesis are not removed +try: + pass +except (ValueError, TypeError, KeyboardInterrupt) as e: + pass + +try: + pass +except* (ValueError, TypeError, KeyboardInterrupt) as e: + pass + +# parenthesis are removed +try: + pass +except ValueError if True else TypeError: + pass + +try: + pass +except* ValueError if True else TypeError: + pass + +# inner except: parenthesis are removed +# outer except: parenthsis are not removed +try: + try: + pass + except TypeError, KeyboardInterrupt: + pass +except (ValueError,): + pass + +try: + try: + pass + except* TypeError, KeyboardInterrupt: + pass +except* (ValueError,): + pass diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/remove_except_types_parens_pre_py314.options.json b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/remove_except_types_parens_pre_py314.options.json new file mode 100644 index 0000000000..938747847c --- /dev/null +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/remove_except_types_parens_pre_py314.options.json @@ -0,0 +1 @@ +{"preview": "enabled", "target_version": "3.11"} \ No newline at end of file diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/remove_except_types_parens_pre_py314.py b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/remove_except_types_parens_pre_py314.py new file mode 100644 index 0000000000..0164513c1f --- /dev/null +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/remove_except_types_parens_pre_py314.py @@ -0,0 +1,111 @@ +# SEE PEP 758 FOR MORE DETAILS +# remains unchanged +try: + pass +except: + pass + +# remains unchanged +try: + pass +except ValueError: + pass + +try: + pass +except* ValueError: + pass + +# parenthesis are removed +try: + pass +except (ValueError): + pass + +try: + pass +except* (ValueError): + pass + +# parenthesis are removed +try: + pass +except (ValueError) as e: + pass + +try: + pass +except* (ValueError) as e: + pass + +# remains unchanged +try: + pass +except (ValueError,): + pass + +try: + pass +except* (ValueError,): + pass + +# remains unchanged +try: + pass +except (ValueError,) as e: + pass + +try: + pass +except* (ValueError,) as e: + pass + +# parenthesis are not removed +try: + pass +except (ValueError, TypeError, KeyboardInterrupt): + pass + +try: + pass +except* (ValueError, TypeError, KeyboardInterrupt): + pass + +# parenthesis are not removed +try: + pass +except (ValueError, TypeError, KeyboardInterrupt) as e: + pass + +try: + pass +except* (ValueError, TypeError, KeyboardInterrupt) as e: + pass + +# parenthesis are removed +try: + pass +except (ValueError if True else TypeError): + pass + +try: + pass +except* (ValueError if True else TypeError): + pass + +# parenthesis are not removed +try: + try: + pass + except (TypeError, KeyboardInterrupt): + pass +except (ValueError,): + pass + +try: + try: + pass + except* (TypeError, KeyboardInterrupt): + pass +except* (ValueError,): + pass diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/remove_except_types_parens_pre_py314.py.expect b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/remove_except_types_parens_pre_py314.py.expect new file mode 100644 index 0000000000..86bdb37d3e --- /dev/null +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/remove_except_types_parens_pre_py314.py.expect @@ -0,0 +1,111 @@ +# SEE PEP 758 FOR MORE DETAILS +# remains unchanged +try: + pass +except: + pass + +# remains unchanged +try: + pass +except ValueError: + pass + +try: + pass +except* ValueError: + pass + +# parenthesis are removed +try: + pass +except ValueError: + pass + +try: + pass +except* ValueError: + pass + +# parenthesis are removed +try: + pass +except ValueError as e: + pass + +try: + pass +except* ValueError as e: + pass + +# remains unchanged +try: + pass +except (ValueError,): + pass + +try: + pass +except* (ValueError,): + pass + +# remains unchanged +try: + pass +except (ValueError,) as e: + pass + +try: + pass +except* (ValueError,) as e: + pass + +# parenthesis are not removed +try: + pass +except (ValueError, TypeError, KeyboardInterrupt): + pass + +try: + pass +except* (ValueError, TypeError, KeyboardInterrupt): + pass + +# parenthesis are not removed +try: + pass +except (ValueError, TypeError, KeyboardInterrupt) as e: + pass + +try: + pass +except* (ValueError, TypeError, KeyboardInterrupt) as e: + pass + +# parenthesis are removed +try: + pass +except ValueError if True else TypeError: + pass + +try: + pass +except* ValueError if True else TypeError: + pass + +# parenthesis are not removed +try: + try: + pass + except (TypeError, KeyboardInterrupt): + pass +except (ValueError,): + pass + +try: + try: + pass + except* (TypeError, KeyboardInterrupt): + pass +except* (ValueError,): + pass diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/remove_lone_list_item_parens.py b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/remove_lone_list_item_parens.py new file mode 100644 index 0000000000..22a829da68 --- /dev/null +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/remove_lone_list_item_parens.py @@ -0,0 +1,80 @@ +items = [(123)] +items = [(True)] +items = [(((((True)))))] +items = [(((((True,)))))] +items = [((((()))))] +items = [(x for x in [1])] +items = {(123)} +items = {(True)} +items = {(((((True)))))} + +# Requires `hug_parens_with_braces_and_square_brackets` unstable style to remove parentheses +# around multiline values +items = [ + ( + {"key1": "val1", "key2": "val2", "key3": "val3"} + if some_var == "long strings" + else {"key": "val"} + ) +] +items = [ + ( + {"key1": "val1", "key2": "val2"} + if some_var == "" + else {"key": "val"} + ) +] + +# Comments should not cause crashes +items = [ + ( # comment + {"key1": "val1", "key2": "val2", "key3": "val3"} + if some_var == "long strings" + else {"key": "val"} + ) +] +items = [ + ( + {"key1": "val1", "key2": "val2", "key3": "val3"} + if some_var == "long strings" + else {"key": "val"} + ) # comment +] + +items = [ # comment + ( + {"key1": "val1", "key2": "val2", "key3": "val3"} + if some_var == "long strings" + else {"key": "val"} + ) +] +items = [ + ( + {"key1": "val1", "key2": "val2", "key3": "val3"} + if some_var == "long strings" + else {"key": "val"} + ) +] # comment + +items = [ + ( + {"key1": "val1", "key2": "val2", "key3": "val3"} # comment + if some_var == "long strings" + else {"key": "val"} + ) +] + +items = [ # comment + ( # comment + {"key1": "val1", "key2": "val2", "key3": "val3"} + if some_var == "long strings" + else {"key": "val"} + ) +] +items = [ + ( + {"key1": "val1", "key2": "val2", "key3": "val3"} + if some_var == "long strings" + else {"key": "val"} + ) # comment +] # comment diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/remove_lone_list_item_parens.py.expect b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/remove_lone_list_item_parens.py.expect new file mode 100644 index 0000000000..71cfcd7026 --- /dev/null +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/remove_lone_list_item_parens.py.expect @@ -0,0 +1,74 @@ +items = [123] +items = [True] +items = [True] +items = [(True,)] +items = [()] +items = [(x for x in [1])] +items = {123} +items = {True} +items = {True} + +# Requires `hug_parens_with_braces_and_square_brackets` unstable style to remove parentheses +# around multiline values +items = [ + ( + {"key1": "val1", "key2": "val2", "key3": "val3"} + if some_var == "long strings" + else {"key": "val"} + ) +] +items = [{"key1": "val1", "key2": "val2"} if some_var == "" else {"key": "val"}] + +# Comments should not cause crashes +items = [ + ( # comment + {"key1": "val1", "key2": "val2", "key3": "val3"} + if some_var == "long strings" + else {"key": "val"} + ) +] +items = [ + ( + {"key1": "val1", "key2": "val2", "key3": "val3"} + if some_var == "long strings" + else {"key": "val"} + ) # comment +] + +items = [ # comment + ( + {"key1": "val1", "key2": "val2", "key3": "val3"} + if some_var == "long strings" + else {"key": "val"} + ) +] +items = [ + ( + {"key1": "val1", "key2": "val2", "key3": "val3"} + if some_var == "long strings" + else {"key": "val"} + ) +] # comment + +items = [ + ( + {"key1": "val1", "key2": "val2", "key3": "val3"} # comment + if some_var == "long strings" + else {"key": "val"} + ) +] + +items = [ # comment + ( # comment + {"key1": "val1", "key2": "val2", "key3": "val3"} + if some_var == "long strings" + else {"key": "val"} + ) +] +items = [ + ( + {"key1": "val1", "key2": "val2", "key3": "val3"} + if some_var == "long strings" + else {"key": "val"} + ) # comment +] # comment 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 index 172715c6e4..cd2518bb62 100644 --- 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 @@ -1 +1 @@ -{"preview": "enabled", "line_width": 79, "target_version": "3.10"} \ No newline at end of file +{"line_width": 79, "target_version": "3.10"} \ No newline at end of file diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/remove_with_brackets.py b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/remove_with_brackets.py index 9634bab444..6113e5679c 100644 --- a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/remove_with_brackets.py +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/remove_with_brackets.py @@ -52,3 +52,16 @@ with ((((open("bla.txt")))) as f): with ((((CtxManager1()))) as example1, (((CtxManager2()))) as example2): ... + +# regression tests for #3678 +with (a, *b): + pass + +with (a, (b, *c)): + pass + +with (a for b in c): + pass + +with (a, (b for c in d)): + pass diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/remove_with_brackets.py.expect b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/remove_with_brackets.py.expect index e70d01b18d..f08cd13ff1 100644 --- a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/remove_with_brackets.py.expect +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/remove_with_brackets.py.expect @@ -61,3 +61,16 @@ with open("bla.txt") as f: with CtxManager1() as example1, CtxManager2() as example2: ... + +# regression tests for #3678 +with (a, *b): + pass + +with a, (b, *c): + pass + +with (a for b in c): + pass + +with a, (b for c in d): + pass diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/skip_magic_trailing_comma_generic_wrap.options.json b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/skip_magic_trailing_comma_generic_wrap.options.json new file mode 100644 index 0000000000..4d80c0fb92 --- /dev/null +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/skip_magic_trailing_comma_generic_wrap.options.json @@ -0,0 +1 @@ +{"target_version": "3.12", "magic_trailing_comma": "ignore"} \ No newline at end of file diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/skip_magic_trailing_comma_generic_wrap.py b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/skip_magic_trailing_comma_generic_wrap.py new file mode 100644 index 0000000000..8d2a6b9299 --- /dev/null +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/skip_magic_trailing_comma_generic_wrap.py @@ -0,0 +1,97 @@ +def plain[T, B](a: T, b: T) -> T: + return a + +def arg_magic[T, B](a: T, b: T,) -> T: + return a + +def type_param_magic[T, B,](a: T, b: T) -> T: + return a + +def both_magic[T, B,](a: T, b: T,) -> T: + return a + + +def plain_multiline[ + T, + B +]( + a: T, + b: T +) -> T: + return a + +def arg_magic_multiline[ + T, + B +]( + a: T, + b: T, +) -> T: + return a + +def type_param_magic_multiline[ + T, + B, +]( + a: T, + b: T +) -> T: + return a + +def both_magic_multiline[ + T, + B, +]( + a: T, + b: T, +) -> T: + return a + + +def plain_mixed1[ + T, + B +](a: T, b: T) -> T: + return a + +def plain_mixed2[T, B]( + a: T, + b: T +) -> T: + return a + +def arg_magic_mixed1[ + T, + B +](a: T, b: T,) -> T: + return a + +def arg_magic_mixed2[T, B]( + a: T, + b: T, +) -> T: + return a + +def type_param_magic_mixed1[ + T, + B, +](a: T, b: T) -> T: + return a + +def type_param_magic_mixed2[T, B,]( + a: T, + b: T +) -> T: + return a + +def both_magic_mixed1[ + T, + B, +](a: T, b: T,) -> T: + return a + +def both_magic_mixed2[T, B,]( + a: T, + b: T, +) -> T: + return a diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/skip_magic_trailing_comma_generic_wrap.py.expect b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/skip_magic_trailing_comma_generic_wrap.py.expect new file mode 100644 index 0000000000..54bab4e46b --- /dev/null +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/skip_magic_trailing_comma_generic_wrap.py.expect @@ -0,0 +1,62 @@ +def plain[T, B](a: T, b: T) -> T: + return a + + +def arg_magic[T, B](a: T, b: T) -> T: + return a + + +def type_param_magic[T, B](a: T, b: T) -> T: + return a + + +def both_magic[T, B](a: T, b: T) -> T: + return a + + +def plain_multiline[T, B](a: T, b: T) -> T: + return a + + +def arg_magic_multiline[T, B](a: T, b: T) -> T: + return a + + +def type_param_magic_multiline[T, B](a: T, b: T) -> T: + return a + + +def both_magic_multiline[T, B](a: T, b: T) -> T: + return a + + +def plain_mixed1[T, B](a: T, b: T) -> T: + return a + + +def plain_mixed2[T, B](a: T, b: T) -> T: + return a + + +def arg_magic_mixed1[T, B](a: T, b: T) -> T: + return a + + +def arg_magic_mixed2[T, B](a: T, b: T) -> T: + return a + + +def type_param_magic_mixed1[T, B](a: T, b: T) -> T: + return a + + +def type_param_magic_mixed2[T, B](a: T, b: T) -> T: + return a + + +def both_magic_mixed1[T, B](a: T, b: T) -> T: + return a + + +def both_magic_mixed2[T, B](a: T, b: T) -> T: + return a diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/target_version_flag.options.json b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/target_version_flag.options.json new file mode 100644 index 0000000000..4295ffe1df --- /dev/null +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/target_version_flag.options.json @@ -0,0 +1 @@ +{"target_version": "3.12"} \ No newline at end of file diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/target_version_flag.py b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/target_version_flag.py new file mode 100644 index 0000000000..ff72fbe4b9 --- /dev/null +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/target_version_flag.py @@ -0,0 +1,4 @@ +# this is invalid in versions below py312 +class ClassA[T: str]: + def method1(self) -> T: + ... diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/target_version_flag.py.expect b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/target_version_flag.py.expect new file mode 100644 index 0000000000..6795f0fe09 --- /dev/null +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/target_version_flag.py.expect @@ -0,0 +1,3 @@ +# this is invalid in versions below py312 +class ClassA[T: str]: + def method1(self) -> T: ... diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/tuple_with_stmt.py b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/tuple_with_stmt.py new file mode 100644 index 0000000000..885a300f44 --- /dev/null +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/tuple_with_stmt.py @@ -0,0 +1,30 @@ +# don't remove the brackets here, it changes the meaning of the code. +# even though the code will always trigger a runtime error +with (name_5, name_4), name_5: + pass + + +with c, (a, b): + pass + + +with c, (a, b), d: + pass + + +with c, (a, b, e, f, g), d: + pass + + +def test_tuple_as_contextmanager(): + from contextlib import nullcontext + + try: + with (nullcontext(), nullcontext()), nullcontext(): + pass + except TypeError: + # test passed + pass + else: + # this should be a type error + assert False diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/tuple_with_stmt.py.expect b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/tuple_with_stmt.py.expect new file mode 100644 index 0000000000..885a300f44 --- /dev/null +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/tuple_with_stmt.py.expect @@ -0,0 +1,30 @@ +# don't remove the brackets here, it changes the meaning of the code. +# even though the code will always trigger a runtime error +with (name_5, name_4), name_5: + pass + + +with c, (a, b): + pass + + +with c, (a, b), d: + pass + + +with c, (a, b, e, f, g), d: + pass + + +def test_tuple_as_contextmanager(): + from contextlib import nullcontext + + try: + with (nullcontext(), nullcontext()), nullcontext(): + pass + except TypeError: + # test passed + pass + else: + # this should be a type error + assert False diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/type_expansion.options.json b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/type_expansion.options.json new file mode 100644 index 0000000000..7b38e093e4 --- /dev/null +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/type_expansion.options.json @@ -0,0 +1 @@ +{"preview": "enabled", "target_version": "3.12"} \ No newline at end of file diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/type_expansion.py b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/type_expansion.py new file mode 100644 index 0000000000..037f401511 --- /dev/null +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/type_expansion.py @@ -0,0 +1,13 @@ +def f1[T: (int, str)](a,): pass + +def f2[T: (int, str)](a: int, b,): pass + +def g1[T: (int,)](a,): pass + +def g2[T: (int, str, bytes)](a,): pass + +def g3[T: ((int, str), (bytes,))](a,): pass + +def g4[T: (int, (str, bytes))](a,): pass + +def g5[T: ((int,),)](a: int, b,): pass diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/type_expansion.py.expect b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/type_expansion.py.expect new file mode 100644 index 0000000000..61ddf22789 --- /dev/null +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/type_expansion.py.expect @@ -0,0 +1,42 @@ +def f1[T: (int, str)]( + a, +): + pass + + +def f2[T: (int, str)]( + a: int, + b, +): + pass + + +def g1[T: (int,)]( + a, +): + pass + + +def g2[T: (int, str, bytes)]( + a, +): + pass + + +def g3[T: ((int, str), (bytes,))]( + a, +): + pass + + +def g4[T: (int, (str, bytes))]( + a, +): + pass + + +def g5[T: ((int,),)]( + a: int, + b, +): + pass diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/type_param_defaults.py b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/type_param_defaults.py index de25e7c9a9..ff5166c1cf 100644 --- a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/type_param_defaults.py +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/type_param_defaults.py @@ -17,3 +17,5 @@ def trailing_comma1[T=int,](a: str): def trailing_comma2[T=int](a: str,): pass + +def weird_syntax[T=lambda: 42, **P=lambda: 43, *Ts=lambda: 44](): pass diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/type_param_defaults.py.expect b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/type_param_defaults.py.expect index af09a67e5c..1d9a2ee545 100644 --- a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/type_param_defaults.py.expect +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/type_param_defaults.py.expect @@ -13,25 +13,31 @@ type something_that_is_long[ ] = something_that_is_long -def simple[ - T = something_that_is_long -](short1: int, short2: str, short3: bytes) -> float: +def simple[T = something_that_is_long]( + short1: int, short2: str, short3: bytes +) -> float: pass -def longer[ - something_that_is_long = something_that_is_long -](something_that_is_long: something_that_is_long) -> something_that_is_long: +def longer[something_that_is_long = something_that_is_long]( + something_that_is_long: something_that_is_long, +) -> something_that_is_long: pass def trailing_comma1[ T = int, -](a: str): +]( + a: str, +): pass -def trailing_comma2[ - T = int -](a: str,): +def trailing_comma2[T = int]( + a: str, +): + pass + + +def weird_syntax[T = lambda: 42, **P = lambda: 43, *Ts = lambda: 44](): pass diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/type_params.py b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/type_params.py index 7ce47eb01f..ba4385116e 100644 --- a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/type_params.py +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/type_params.py @@ -11,3 +11,7 @@ def even_longer[WhatIsTheLongestTypeVarNameYouCanThinkOfEnoughToMakeBlackSplitTh def it_gets_worse[WhatIsTheLongestTypeVarNameYouCanThinkOfEnoughToMakeBlackSplitThisLine, ItCouldBeGenericOverMultipleTypeVars](): pass def magic[Trailing, Comma,](): pass + +def weird_syntax[T: lambda: 42, U: a or b](): pass + +def name_3[name_0: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa if aaaaaaaaaaa else name_3](): pass diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/type_params.py.expect b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/type_params.py.expect index 72b1e032f4..d14276130e 100644 --- a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/type_params.py.expect +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/type_params.py.expect @@ -38,3 +38,13 @@ def magic[ Comma, ](): pass + + +def weird_syntax[T: lambda: 42, U: a or b](): + pass + + +def name_3[ + name_0: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa if aaaaaaaaaaa else name_3 +](): + 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 3e3e63421f..1723d32d20 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,4 +1,4 @@ -# This is testing an issue that is specific to the preview style +# This is testing an issue that is specific to the preview style (wrap_long_dict_values_in_parens) { "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 03aa2598fe..603ab02701 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,2 +1,2 @@ -# This is testing an issue that is specific to the preview style +# This is testing an issue that is specific to the preview style (wrap_long_dict_values_in_parens) {"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 a9cbb2cc6d..c32bd71af7 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 @@ -113,10 +113,10 @@ IGNORE_LIST = [ # Specs for which to override the formatter options OPTIONS_OVERRIDES = { "context_managers_38.py": { - "target_version": "py38" + "target_version": "3.8" }, "context_managers_autodetect_38.py" : { - "target_version": "py38" + "target_version": "3.8" } } diff --git a/crates/ruff_python_formatter/tests/fixtures.rs b/crates/ruff_python_formatter/tests/fixtures.rs index a5f25dfcd1..776b272d21 100644 --- a/crates/ruff_python_formatter/tests/fixtures.rs +++ b/crates/ruff_python_formatter/tests/fixtures.rs @@ -108,7 +108,8 @@ fn black_compatibility() { let expected_output = fs::read_to_string(&expected_path) .unwrap_or_else(|_| panic!("Expected Black output file '{expected_path:?}' to exist")); - ensure_unchanged_ast(&content, &formatted_code, &options, input_path); + let unsupported_syntax_errors = + ensure_unchanged_ast(&content, &formatted_code, &options, input_path); if formatted_code == expected_output { // Black and Ruff formatting matches. Delete any existing snapshot files because the Black output @@ -163,6 +164,20 @@ fn black_compatibility() { write!(snapshot, "{}", Header::new("Black Output")).unwrap(); write!(snapshot, "{}", CodeFrame::new("python", &expected_output)).unwrap(); + if !unsupported_syntax_errors.is_empty() { + write!(snapshot, "{}", Header::new("New Unsupported Syntax Errors")).unwrap(); + writeln!( + snapshot, + "{}", + DisplayDiagnostics::new( + &DummyFileResolver, + &DisplayDiagnosticConfig::default().format(DiagnosticFormat::Full), + &unsupported_syntax_errors + ) + ) + .unwrap(); + } + insta::with_settings!({ omit_expression => true, input_file => input_path, diff --git a/crates/ruff_python_formatter/tests/normalizer.rs b/crates/ruff_python_formatter/tests/normalizer.rs index 2b943948eb..d8fb8fd54e 100644 --- a/crates/ruff_python_formatter/tests/normalizer.rs +++ b/crates/ruff_python_formatter/tests/normalizer.rs @@ -1,8 +1,5 @@ +use regex::Regex; use std::sync::LazyLock; -use { - itertools::Either::{Left, Right}, - regex::Regex, -}; use ruff_python_ast::{ self as ast, BytesLiteralFlags, Expr, FStringFlags, FStringPart, InterpolatedStringElement, @@ -46,18 +43,9 @@ impl Transformer for Normalizer { fn visit_stmt(&self, stmt: &mut Stmt) { if let Stmt::Delete(delete) = stmt { // Treat `del a, b` and `del (a, b)` equivalently. - delete.targets = delete - .targets - .clone() - .into_iter() - .flat_map(|target| { - if let Expr::Tuple(tuple) = target { - Left(tuple.elts.into_iter()) - } else { - Right(std::iter::once(target)) - } - }) - .collect(); + if let [Expr::Tuple(tuple)] = delete.targets.as_slice() { + delete.targets = tuple.elts.clone(); + } } transformer::walk_stmt(self, stmt); diff --git a/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__cantfit.py.snap b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__cantfit.py.snap new file mode 100644 index 0000000000..68496fbb9a --- /dev/null +++ b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__cantfit.py.snap @@ -0,0 +1,204 @@ +--- +source: crates/ruff_python_formatter/tests/fixtures.rs +input_file: crates/ruff_python_formatter/resources/test/fixtures/black/cases/cantfit.py +--- +## Input + +```python +# long variable name +this_is_a_ridiculously_long_name_and_nobody_in_their_right_mind_would_use_one_like_it = 0 +this_is_a_ridiculously_long_name_and_nobody_in_their_right_mind_would_use_one_like_it = 1 # with a comment +this_is_a_ridiculously_long_name_and_nobody_in_their_right_mind_would_use_one_like_it = [ + 1, 2, 3 +] +this_is_a_ridiculously_long_name_and_nobody_in_their_right_mind_would_use_one_like_it = function() +this_is_a_ridiculously_long_name_and_nobody_in_their_right_mind_would_use_one_like_it = function( + arg1, arg2, arg3 +) +this_is_a_ridiculously_long_name_and_nobody_in_their_right_mind_would_use_one_like_it = function( + [1, 2, 3], arg1, [1, 2, 3], arg2, [1, 2, 3], arg3 +) +# long function name +normal_name = but_the_function_name_is_now_ridiculously_long_and_it_is_still_super_annoying() +normal_name = but_the_function_name_is_now_ridiculously_long_and_it_is_still_super_annoying( + arg1, arg2, arg3 +) +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 +) +string_variable_name = ( + "a string that is waaaaaaaayyyyyyyy too long, even in parens, there's nothing you can do" # noqa +) +for key in """ + hostname + port + username +""".split(): + if key in self.connect_kwargs: + raise ValueError(err.format(key)) +concatenated_strings = "some strings that are " "concatenated implicitly, so if you put them on separate " "lines it will fit" +del concatenated_strings, string_variable_name, normal_function_name, normal_name, need_more_to_make_the_line_long_enough +del ([], name_1, name_2), [(), [], name_4, name_3], name_1[[name_2 for name_1 in name_0]] +del (), +``` + +## Black Differences + +```diff +--- Black ++++ Ruff +@@ -1,18 +1,12 @@ + # long variable name +-this_is_a_ridiculously_long_name_and_nobody_in_their_right_mind_would_use_one_like_it = ( +- 0 +-) +-this_is_a_ridiculously_long_name_and_nobody_in_their_right_mind_would_use_one_like_it = ( +- 1 # with a comment +-) ++this_is_a_ridiculously_long_name_and_nobody_in_their_right_mind_would_use_one_like_it = 0 ++this_is_a_ridiculously_long_name_and_nobody_in_their_right_mind_would_use_one_like_it = 1 # with a comment + this_is_a_ridiculously_long_name_and_nobody_in_their_right_mind_would_use_one_like_it = [ + 1, + 2, + 3, + ] +-this_is_a_ridiculously_long_name_and_nobody_in_their_right_mind_would_use_one_like_it = ( +- function() +-) ++this_is_a_ridiculously_long_name_and_nobody_in_their_right_mind_would_use_one_like_it = function() + this_is_a_ridiculously_long_name_and_nobody_in_their_right_mind_would_use_one_like_it = function( + arg1, arg2, arg3 + ) +@@ -58,4 +52,4 @@ + [(), [], name_4, name_3], + name_1[[name_2 for name_1 in name_0]], + ) +-del ((),) ++del () +``` + +## Ruff Output + +```python +# long variable name +this_is_a_ridiculously_long_name_and_nobody_in_their_right_mind_would_use_one_like_it = 0 +this_is_a_ridiculously_long_name_and_nobody_in_their_right_mind_would_use_one_like_it = 1 # with a comment +this_is_a_ridiculously_long_name_and_nobody_in_their_right_mind_would_use_one_like_it = [ + 1, + 2, + 3, +] +this_is_a_ridiculously_long_name_and_nobody_in_their_right_mind_would_use_one_like_it = function() +this_is_a_ridiculously_long_name_and_nobody_in_their_right_mind_would_use_one_like_it = function( + arg1, arg2, arg3 +) +this_is_a_ridiculously_long_name_and_nobody_in_their_right_mind_would_use_one_like_it = function( + [1, 2, 3], arg1, [1, 2, 3], arg2, [1, 2, 3], arg3 +) +# long function name +normal_name = ( + but_the_function_name_is_now_ridiculously_long_and_it_is_still_super_annoying() +) +normal_name = ( + but_the_function_name_is_now_ridiculously_long_and_it_is_still_super_annoying( + arg1, arg2, arg3 + ) +) +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 + ) +) +string_variable_name = "a string that is waaaaaaaayyyyyyyy too long, even in parens, there's nothing you can do" # noqa +for key in """ + hostname + port + username +""".split(): + if key in self.connect_kwargs: + raise ValueError(err.format(key)) +concatenated_strings = ( + "some strings that are " + "concatenated implicitly, so if you put them on separate " + "lines it will fit" +) +del ( + concatenated_strings, + string_variable_name, + normal_function_name, + normal_name, + need_more_to_make_the_line_long_enough, +) +del ( + ([], name_1, name_2), + [(), [], name_4, name_3], + name_1[[name_2 for name_1 in name_0]], +) +del () +``` + +## Black Output + +```python +# long variable name +this_is_a_ridiculously_long_name_and_nobody_in_their_right_mind_would_use_one_like_it = ( + 0 +) +this_is_a_ridiculously_long_name_and_nobody_in_their_right_mind_would_use_one_like_it = ( + 1 # with a comment +) +this_is_a_ridiculously_long_name_and_nobody_in_their_right_mind_would_use_one_like_it = [ + 1, + 2, + 3, +] +this_is_a_ridiculously_long_name_and_nobody_in_their_right_mind_would_use_one_like_it = ( + function() +) +this_is_a_ridiculously_long_name_and_nobody_in_their_right_mind_would_use_one_like_it = function( + arg1, arg2, arg3 +) +this_is_a_ridiculously_long_name_and_nobody_in_their_right_mind_would_use_one_like_it = function( + [1, 2, 3], arg1, [1, 2, 3], arg2, [1, 2, 3], arg3 +) +# long function name +normal_name = ( + but_the_function_name_is_now_ridiculously_long_and_it_is_still_super_annoying() +) +normal_name = ( + but_the_function_name_is_now_ridiculously_long_and_it_is_still_super_annoying( + arg1, arg2, arg3 + ) +) +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 + ) +) +string_variable_name = "a string that is waaaaaaaayyyyyyyy too long, even in parens, there's nothing you can do" # noqa +for key in """ + hostname + port + username +""".split(): + if key in self.connect_kwargs: + raise ValueError(err.format(key)) +concatenated_strings = ( + "some strings that are " + "concatenated implicitly, so if you put them on separate " + "lines it will fit" +) +del ( + concatenated_strings, + string_variable_name, + normal_function_name, + normal_name, + need_more_to_make_the_line_long_enough, +) +del ( + ([], name_1, name_2), + [(), [], name_4, name_3], + name_1[[name_2 for name_1 in name_0]], +) +del ((),) +``` diff --git a/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__fmtskip10.py.snap b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__fmtskip10.py.snap new file mode 100644 index 0000000000..81404baffc --- /dev/null +++ b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__fmtskip10.py.snap @@ -0,0 +1,76 @@ +--- +source: crates/ruff_python_formatter/tests/fixtures.rs +input_file: crates/ruff_python_formatter/resources/test/fixtures/black/cases/fmtskip10.py +--- +## Input + +```python +def foo(): return "mock" # fmt: skip +if True: print("yay") # fmt: skip +for i in range(10): print(i) # fmt: skip + +j = 1 # fmt: skip +while j < 10: j += 1 # fmt: skip + +b = [c for c in "A very long string that would normally generate some kind of collapse, since it is this long"] # fmt: skip +``` + +## Black Differences + +```diff +--- Black ++++ Ruff +@@ -1,8 +1,14 @@ +-def foo(): return "mock" # fmt: skip +-if True: print("yay") # fmt: skip +-for i in range(10): print(i) # fmt: skip ++def foo(): ++ return "mock" # fmt: skip ++ ++ ++if True: ++ print("yay") # fmt: skip ++for i in range(10): ++ print(i) # fmt: skip + +-j = 1 # fmt: skip +-while j < 10: j += 1 # fmt: skip ++j = 1 # fmt: skip ++while j < 10: ++ j += 1 # fmt: skip + +-b = [c for c in "A very long string that would normally generate some kind of collapse, since it is this long"] # fmt: skip ++b = [c for c in "A very long string that would normally generate some kind of collapse, since it is this long"] # fmt: skip +``` + +## Ruff Output + +```python +def foo(): + return "mock" # fmt: skip + + +if True: + print("yay") # fmt: skip +for i in range(10): + print(i) # fmt: skip + +j = 1 # fmt: skip +while j < 10: + j += 1 # fmt: skip + +b = [c for c in "A very long string that would normally generate some kind of collapse, since it is this long"] # fmt: skip +``` + +## Black Output + +```python +def foo(): return "mock" # fmt: skip +if True: print("yay") # fmt: skip +for i in range(10): print(i) # fmt: skip + +j = 1 # fmt: skip +while j < 10: j += 1 # fmt: skip + +b = [c for c in "A very long string that would normally generate some kind of collapse, since it is this long"] # fmt: skip +``` diff --git a/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__fstring.py.snap b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__fstring.py.snap index 82def26815..6ff1f73fd7 100644 --- a/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__fstring.py.snap +++ b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__fstring.py.snap @@ -72,3 +72,17 @@ f'Hello \'{tricky + "example"}\'' f"Tried directories {str(rootdirs)} \ but none started with prefix {parentdir_prefix}" ``` + +## New Unsupported Syntax Errors + +error[invalid-syntax]: Cannot reuse outer quote character in f-strings on Python 3.10 (syntax was added in Python 3.12) + --> fstring.py:6:9 + | +4 | f"some f-string with {a} {few():.2f} {formatted.values!r}" +5 | f"some f-string with {a} {few(''):.2f} {formatted.values!r}" +6 | f"{f'''{"nested"} inner'''} outer" + | ^ +7 | f'"{f"{nested} inner"}" outer' +8 | f"space between opening braces: { {a for a in (1, 2, 3)} }" + | +warning: Only accept new syntax errors if they are also present in the input. The formatter should not introduce syntax errors. diff --git a/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__fstring_quotations.py.snap b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__fstring_quotations.py.snap new file mode 100644 index 0000000000..0657669f2e --- /dev/null +++ b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__fstring_quotations.py.snap @@ -0,0 +1,126 @@ +--- +source: crates/ruff_python_formatter/tests/fixtures.rs +input_file: crates/ruff_python_formatter/resources/test/fixtures/black/cases/fstring_quotations.py +--- +## Input + +```python +# Regression tests for long f-strings, including examples from issue #3623 + +a = ( + 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa' + f'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"{"b"}"' +) + +a = ( + f'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"{"b"}"' + 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa' +) + +a = 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa' + \ + f'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"{"b"}"' + +a = f'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"{"b"}"' + \ + f'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"{"b"}"' + +a = ( + f'bbbbbbb"{"b"}"' + 'aaaaaaaa' +) + +a = ( + f'"{"b"}"' +) + +a = ( + f'\"{"b"}\"' +) + +a = ( + r'\"{"b"}\"' +) +``` + +## Black Differences + +```diff +--- Black ++++ Ruff +@@ -20,7 +20,7 @@ + + f'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"{"b"}"' + ) + +-a = f'bbbbbbb"{"b"}"' "aaaaaaaa" ++a = f'bbbbbbb"{"b"}"aaaaaaaa' + + a = f'"{"b"}"' + +``` + +## Ruff Output + +```python +# Regression tests for long f-strings, including examples from issue #3623 + +a = ( + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + f'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"{"b"}"' +) + +a = ( + f'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"{"b"}"' + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" +) + +a = ( + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + + f'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"{"b"}"' +) + +a = ( + f'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"{"b"}"' + + f'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"{"b"}"' +) + +a = f'bbbbbbb"{"b"}"aaaaaaaa' + +a = f'"{"b"}"' + +a = f'"{"b"}"' + +a = r'\"{"b"}\"' +``` + +## Black Output + +```python +# Regression tests for long f-strings, including examples from issue #3623 + +a = ( + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + f'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"{"b"}"' +) + +a = ( + f'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"{"b"}"' + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" +) + +a = ( + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + + f'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"{"b"}"' +) + +a = ( + f'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"{"b"}"' + + f'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"{"b"}"' +) + +a = f'bbbbbbb"{"b"}"' "aaaaaaaa" + +a = f'"{"b"}"' + +a = f'"{"b"}"' + +a = r'\"{"b"}\"' +``` diff --git a/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__generics_wrapping.py.snap b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__generics_wrapping.py.snap new file mode 100644 index 0000000000..6df7d9b964 --- /dev/null +++ b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__generics_wrapping.py.snap @@ -0,0 +1,561 @@ +--- +source: crates/ruff_python_formatter/tests/fixtures.rs +input_file: crates/ruff_python_formatter/resources/test/fixtures/black/cases/generics_wrapping.py +--- +## Input + +```python +def plain[T, B](a: T, b: T) -> T: + return a + +def arg_magic[T, B](a: T, b: T,) -> T: + return a + +def type_param_magic[T, B,](a: T, b: T) -> T: + return a + +def both_magic[T, B,](a: T, b: T,) -> T: + return a + + +def plain_multiline[ + T, + B +]( + a: T, + b: T +) -> T: + return a + +def arg_magic_multiline[ + T, + B +]( + a: T, + b: T, +) -> T: + return a + +def type_param_magic_multiline[ + T, + B, +]( + a: T, + b: T +) -> T: + return a + +def both_magic_multiline[ + T, + B, +]( + a: T, + b: T, +) -> T: + return a + + +def plain_mixed1[ + T, + B +](a: T, b: T) -> T: + return a + +def plain_mixed2[T, B]( + a: T, + b: T +) -> T: + return a + +def arg_magic_mixed1[ + T, + B +](a: T, b: T,) -> T: + return a + +def arg_magic_mixed2[T, B]( + a: T, + b: T, +) -> T: + return a + +def type_param_magic_mixed1[ + T, + B, +](a: T, b: T) -> T: + return a + +def type_param_magic_mixed2[T, B,]( + a: T, + b: T +) -> T: + return a + +def both_magic_mixed1[ + T, + B, +](a: T, b: T,) -> T: + return a + +def both_magic_mixed2[T, B,]( + a: T, + b: T, +) -> T: + return a + +def something_something_function[ + T: Model +](param: list[int], other_param: type[T], *, some_other_param: bool = True) -> QuerySet[ + T +]: + pass + + +def func[A_LOT_OF_GENERIC_TYPES: AreBeingDefinedHere, LIKE_THIS, AND_THIS, ANOTHER_ONE, AND_YET_ANOTHER_ONE: ThisOneHasTyping](a: T, b: T, c: T, d: T, e: T, f: T, g: T, h: T, i: T, j: T, k: T, l: T, m: T, n: T, o: T, p: T) -> T: + return a + + +def with_random_comments[ + Z + # bye +](): + return a + + +def func[ + T, # comment + U # comment + , + Z: # comment + int +](): pass + + +def func[ + T, # comment but it's long so it doesn't just move to the end of the line + U # comment comment comm comm ent ent + , + Z: # comment ent ent comm comm comment + int +](): pass +``` + +## Black Differences + +```diff +--- Black ++++ Ruff +@@ -12,9 +12,7 @@ + def type_param_magic[ + T, + B, +-]( +- a: T, b: T +-) -> T: ++](a: T, b: T) -> T: + return a + + +@@ -42,9 +40,7 @@ + def type_param_magic_multiline[ + T, + B, +-]( +- a: T, b: T +-) -> T: ++](a: T, b: T) -> T: + return a + + +@@ -83,18 +79,14 @@ + def type_param_magic_mixed1[ + T, + B, +-]( +- a: T, b: T +-) -> T: ++](a: T, b: T) -> T: + return a + + + def type_param_magic_mixed2[ + T, + B, +-]( +- a: T, b: T +-) -> T: ++](a: T, b: T) -> T: + return a + + +@@ -158,13 +150,19 @@ + return a + + +-def func[T, U, Z: int](): # comment # comment # comment ++def func[ ++ T, # comment ++ U, # comment ++ Z: # comment ++ int, ++](): + pass + + + def func[ + T, # comment but it's long so it doesn't just move to the end of the line + U, # comment comment comm comm ent ent +- Z: int, # comment ent ent comm comm comment ++ Z: # comment ent ent comm comm comment ++ int, + ](): + pass +``` + +## Ruff Output + +```python +def plain[T, B](a: T, b: T) -> T: + return a + + +def arg_magic[T, B]( + a: T, + b: T, +) -> T: + return a + + +def type_param_magic[ + T, + B, +](a: T, b: T) -> T: + return a + + +def both_magic[ + T, + B, +]( + a: T, + b: T, +) -> T: + return a + + +def plain_multiline[T, B](a: T, b: T) -> T: + return a + + +def arg_magic_multiline[T, B]( + a: T, + b: T, +) -> T: + return a + + +def type_param_magic_multiline[ + T, + B, +](a: T, b: T) -> T: + return a + + +def both_magic_multiline[ + T, + B, +]( + a: T, + b: T, +) -> T: + return a + + +def plain_mixed1[T, B](a: T, b: T) -> T: + return a + + +def plain_mixed2[T, B](a: T, b: T) -> T: + return a + + +def arg_magic_mixed1[T, B]( + a: T, + b: T, +) -> T: + return a + + +def arg_magic_mixed2[T, B]( + a: T, + b: T, +) -> T: + return a + + +def type_param_magic_mixed1[ + T, + B, +](a: T, b: T) -> T: + return a + + +def type_param_magic_mixed2[ + T, + B, +](a: T, b: T) -> T: + return a + + +def both_magic_mixed1[ + T, + B, +]( + a: T, + b: T, +) -> T: + return a + + +def both_magic_mixed2[ + T, + B, +]( + a: T, + b: T, +) -> T: + return a + + +def something_something_function[T: Model]( + param: list[int], other_param: type[T], *, some_other_param: bool = True +) -> QuerySet[T]: + pass + + +def func[ + A_LOT_OF_GENERIC_TYPES: AreBeingDefinedHere, + LIKE_THIS, + AND_THIS, + ANOTHER_ONE, + AND_YET_ANOTHER_ONE: ThisOneHasTyping, +]( + a: T, + b: T, + c: T, + d: T, + e: T, + f: T, + g: T, + h: T, + i: T, + j: T, + k: T, + l: T, + m: T, + n: T, + o: T, + p: T, +) -> T: + return a + + +def with_random_comments[ + Z + # bye +](): + return a + + +def func[ + T, # comment + U, # comment + Z: # comment + int, +](): + pass + + +def func[ + T, # comment but it's long so it doesn't just move to the end of the line + U, # comment comment comm comm ent ent + Z: # comment ent ent comm comm comment + int, +](): + pass +``` + +## Black Output + +```python +def plain[T, B](a: T, b: T) -> T: + return a + + +def arg_magic[T, B]( + a: T, + b: T, +) -> T: + return a + + +def type_param_magic[ + T, + B, +]( + a: T, b: T +) -> T: + return a + + +def both_magic[ + T, + B, +]( + a: T, + b: T, +) -> T: + return a + + +def plain_multiline[T, B](a: T, b: T) -> T: + return a + + +def arg_magic_multiline[T, B]( + a: T, + b: T, +) -> T: + return a + + +def type_param_magic_multiline[ + T, + B, +]( + a: T, b: T +) -> T: + return a + + +def both_magic_multiline[ + T, + B, +]( + a: T, + b: T, +) -> T: + return a + + +def plain_mixed1[T, B](a: T, b: T) -> T: + return a + + +def plain_mixed2[T, B](a: T, b: T) -> T: + return a + + +def arg_magic_mixed1[T, B]( + a: T, + b: T, +) -> T: + return a + + +def arg_magic_mixed2[T, B]( + a: T, + b: T, +) -> T: + return a + + +def type_param_magic_mixed1[ + T, + B, +]( + a: T, b: T +) -> T: + return a + + +def type_param_magic_mixed2[ + T, + B, +]( + a: T, b: T +) -> T: + return a + + +def both_magic_mixed1[ + T, + B, +]( + a: T, + b: T, +) -> T: + return a + + +def both_magic_mixed2[ + T, + B, +]( + a: T, + b: T, +) -> T: + return a + + +def something_something_function[T: Model]( + param: list[int], other_param: type[T], *, some_other_param: bool = True +) -> QuerySet[T]: + pass + + +def func[ + A_LOT_OF_GENERIC_TYPES: AreBeingDefinedHere, + LIKE_THIS, + AND_THIS, + ANOTHER_ONE, + AND_YET_ANOTHER_ONE: ThisOneHasTyping, +]( + a: T, + b: T, + c: T, + d: T, + e: T, + f: T, + g: T, + h: T, + i: T, + j: T, + k: T, + l: T, + m: T, + n: T, + o: T, + p: T, +) -> T: + return a + + +def with_random_comments[ + Z + # bye +](): + return a + + +def func[T, U, Z: int](): # comment # comment # comment + pass + + +def func[ + T, # comment but it's long so it doesn't just move to the end of the line + U, # comment comment comm comm ent ent + Z: int, # comment ent ent comm comm comment +](): + pass +``` diff --git a/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__long_strings__type_annotations.py.snap b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__long_strings__type_annotations.py.snap new file mode 100644 index 0000000000..903cb24cdb --- /dev/null +++ b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__long_strings__type_annotations.py.snap @@ -0,0 +1,114 @@ +--- +source: crates/ruff_python_formatter/tests/fixtures.rs +input_file: crates/ruff_python_formatter/resources/test/fixtures/black/cases/long_strings__type_annotations.py +--- +## Input + +```python +def func( + arg1, + arg2, +) -> Set["this_is_a_very_long_module_name.AndAVeryLongClasName" + ".WithAVeryVeryVeryVeryVeryLongSubClassName"]: + pass + + +def func( + argument: ( + "VeryLongClassNameWithAwkwardGenericSubtype[int] |" + "VeryLongClassNameWithAwkwardGenericSubtype[str]" + ), +) -> ( + "VeryLongClassNameWithAwkwardGenericSubtype[int] |" + "VeryLongClassNameWithAwkwardGenericSubtype[str]" +): + pass + + +def func( + argument: ( + "int |" + "str" + ), +) -> Set["int |" + " str"]: + pass +``` + +## Black Differences + +```diff +--- Black ++++ Ruff +@@ -21,6 +21,6 @@ + + + def func( +- argument: "int |" "str", +-) -> Set["int |" " str"]: ++ argument: ("int |str"), ++) -> Set["int | str"]: + pass +``` + +## Ruff Output + +```python +def func( + arg1, + arg2, +) -> Set[ + "this_is_a_very_long_module_name.AndAVeryLongClasName" + ".WithAVeryVeryVeryVeryVeryLongSubClassName" +]: + pass + + +def func( + argument: ( + "VeryLongClassNameWithAwkwardGenericSubtype[int] |" + "VeryLongClassNameWithAwkwardGenericSubtype[str]" + ), +) -> ( + "VeryLongClassNameWithAwkwardGenericSubtype[int] |" + "VeryLongClassNameWithAwkwardGenericSubtype[str]" +): + pass + + +def func( + argument: ("int |str"), +) -> Set["int | str"]: + pass +``` + +## Black Output + +```python +def func( + arg1, + arg2, +) -> Set[ + "this_is_a_very_long_module_name.AndAVeryLongClasName" + ".WithAVeryVeryVeryVeryVeryLongSubClassName" +]: + pass + + +def func( + argument: ( + "VeryLongClassNameWithAwkwardGenericSubtype[int] |" + "VeryLongClassNameWithAwkwardGenericSubtype[str]" + ), +) -> ( + "VeryLongClassNameWithAwkwardGenericSubtype[int] |" + "VeryLongClassNameWithAwkwardGenericSubtype[str]" +): + pass + + +def func( + argument: "int |" "str", +) -> Set["int |" " str"]: + pass +``` 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 deleted file mode 100644 index 379f746fae..0000000000 --- a/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__no_blank_line_before_docstring.py.snap +++ /dev/null @@ -1,122 +0,0 @@ ---- -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 -snapshot_kind: text ---- -## 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__pep604_union_types_line_breaks.py.snap b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__pep604_union_types_line_breaks.py.snap index dc61db96dc..f5eba916b2 100644 --- a/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__pep604_union_types_line_breaks.py.snap +++ b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__pep604_union_types_line_breaks.py.snap @@ -1,7 +1,6 @@ --- source: crates/ruff_python_formatter/tests/fixtures.rs input_file: crates/ruff_python_formatter/resources/test/fixtures/black/cases/pep604_union_types_line_breaks.py -snapshot_kind: text --- ## Input @@ -27,7 +26,7 @@ z: (Short z: (int) = 2.3 z: ((int)) = foo() -# In case I go for not enforcing parentheses, this might get improved at the same time +# In case I go for not enforcing parantheses, this might get improved at the same time x = ( z == 9999999999999999999999999999999999999999 @@ -166,7 +165,7 @@ z: Short | Short2 | Short3 | Short4 = 8 z: int = 2.3 z: int = foo() -# In case I go for not enforcing parentheses, this might get improved at the same time +# In case I go for not enforcing parantheses, this might get improved at the same time x = ( z == 9999999999999999999999999999999999999999 @@ -270,7 +269,7 @@ z: Short | Short2 | Short3 | Short4 = 8 z: int = 2.3 z: int = foo() -# In case I go for not enforcing parentheses, this might get improved at the same time +# In case I go for not enforcing parantheses, this might get improved at the same time x = ( z == 9999999999999999999999999999999999999999 diff --git a/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__pep_701.py.snap b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__pep_701.py.snap index 63d6544faa..0b1894126c 100644 --- a/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__pep_701.py.snap +++ b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__pep_701.py.snap @@ -80,8 +80,9 @@ x = f"a{2+2:=^{foo(x+y**2):something else}}b" x = f"a{2+2:=^{foo(x+y**2):something else}one more}b" f'{(abc:=10)}' -f"This is a really long string, but just make sure that you reflow fstrings { - 2+2:d}" +f"""This is a really long string, but just make sure that you reflow fstrings { + 2+2:d +}""" f"This is a really long string, but just make sure that you reflow fstrings correctly {2+2:d}" f"{2+2=}" @@ -168,7 +169,7 @@ rf"\{"a"}" x = """foo {{ {2 + 2}bar baz""" -@@ -28,55 +26,48 @@ +@@ -28,55 +26,50 @@ x = f"""foo {{ {2 + 2}bar {{ baz""" @@ -231,16 +232,16 @@ rf"\{"a"}" +x = f"a{2 + 2:=^{foo(x + y**2):something else}one more}b" +f"{(abc := 10)}" --f"This is a really long string, but just make sure that you reflow fstrings { + f"""This is a really long string, but just make sure that you reflow fstrings { - 2+2:d --}" ++ 2 + 2:d + }""" -f"This is a really long string, but just make sure that you reflow fstrings correctly {2+2:d}" -+f"This is a really long string, but just make sure that you reflow fstrings {2 + 2:d}" +f"This is a really long string, but just make sure that you reflow fstrings correctly {2 + 2:d}" f"{2+2=}" f"{2+2 = }" -@@ -88,14 +79,10 @@ +@@ -88,14 +81,10 @@ %d }""" @@ -257,7 +258,7 @@ rf"\{"a"}" ) f"`escape` only permitted in {{'html', 'latex', 'latex-math'}}, \ -@@ -105,8 +92,10 @@ +@@ -105,8 +94,10 @@ rf"\{{\}}" f""" @@ -270,7 +271,7 @@ rf"\{"a"}" """ value: str = f"""foo -@@ -124,13 +113,15 @@ +@@ -124,13 +115,15 @@ f'{{\\"kind\\":\\"ConfigMap\\",\\"metadata\\":{{\\"annotations\\":{{}},\\"name\\":\\"cluster-info\\",\\"namespace\\":\\"amazon-cloudwatch\\"}}}}' @@ -364,7 +365,9 @@ x = f"a{2 + 2:=^{foo(x + y**2):something else}}b" x = f"a{2 + 2:=^{foo(x + y**2):something else}one more}b" f"{(abc := 10)}" -f"This is a really long string, but just make sure that you reflow fstrings {2 + 2:d}" +f"""This is a really long string, but just make sure that you reflow fstrings { + 2 + 2:d +}""" f"This is a really long string, but just make sure that you reflow fstrings correctly {2 + 2:d}" f"{2+2=}" @@ -503,9 +506,9 @@ x = f"a{2+2:=^{foo(x+y**2):something else}}b" x = f"a{2+2:=^{foo(x+y**2):something else}one more}b" f"{(abc:=10)}" -f"This is a really long string, but just make sure that you reflow fstrings { +f"""This is a really long string, but just make sure that you reflow fstrings { 2+2:d -}" +}""" f"This is a really long string, but just make sure that you reflow fstrings correctly {2+2:d}" f"{2+2=}" diff --git a/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__prefer_rhs_split_reformatted.py.snap b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__prefer_rhs_split_reformatted.py.snap new file mode 100644 index 0000000000..a21a90481e --- /dev/null +++ b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__prefer_rhs_split_reformatted.py.snap @@ -0,0 +1,123 @@ +--- +source: crates/ruff_python_formatter/tests/fixtures.rs +input_file: crates/ruff_python_formatter/resources/test/fixtures/black/cases/prefer_rhs_split_reformatted.py +--- +## Input + +```python +# Test cases separate from `prefer_rhs_split.py` that contains unformatted source. + +# Left hand side fits in a single line but will still be exploded by the +# magic trailing comma. +first_value, (m1, m2,), third_value = xxxxxx_yyyyyy_zzzzzz_wwwwww_uuuuuuu_vvvvvvvvvvv( + arg1, + arg2, +) + +# 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 + +# Regression test for #1187 +print( + dict( + a=1, + b=2 if some_kind_of_data is not None else some_other_kind_of_data, # some explanation of why this is actually necessary + c=3, + ) +) +``` + +## Black Differences + +```diff +--- Black ++++ Ruff +@@ -24,9 +24,9 @@ + print( + dict( + a=1, +- b=( +- 2 if some_kind_of_data is not None else some_other_kind_of_data +- ), # some explanation of why this is actually necessary ++ b=2 ++ if some_kind_of_data is not None ++ else some_other_kind_of_data, # some explanation of why this is actually necessary + c=3, + ) + ) +``` + +## Ruff Output + +```python +# Test cases separate from `prefer_rhs_split.py` that contains unformatted source. + +# Left hand side fits in a single line but will still be exploded by the +# magic trailing comma. +( + first_value, + ( + m1, + m2, + ), + third_value, +) = xxxxxx_yyyyyy_zzzzzz_wwwwww_uuuuuuu_vvvvvvvvvvv( + arg1, + arg2, +) + +# 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 + +# Regression test for #1187 +print( + dict( + a=1, + b=2 + if some_kind_of_data is not None + else some_other_kind_of_data, # some explanation of why this is actually necessary + c=3, + ) +) +``` + +## Black Output + +```python +# Test cases separate from `prefer_rhs_split.py` that contains unformatted source. + +# Left hand side fits in a single line but will still be exploded by the +# magic trailing comma. +( + first_value, + ( + m1, + m2, + ), + third_value, +) = xxxxxx_yyyyyy_zzzzzz_wwwwww_uuuuuuu_vvvvvvvvvvv( + arg1, + arg2, +) + +# 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 + +# Regression test for #1187 +print( + dict( + a=1, + b=( + 2 if some_kind_of_data is not None else some_other_kind_of_data + ), # some explanation of why this is actually necessary + c=3, + ) +) +``` diff --git a/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__preview_comments7.py.snap b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__preview_comments7.py.snap index 36f29c9177..ab98186210 100644 --- a/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__preview_comments7.py.snap +++ b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__preview_comments7.py.snap @@ -1,7 +1,6 @@ --- source: crates/ruff_python_formatter/tests/fixtures.rs input_file: crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_comments7.py -snapshot_kind: text --- ## Input @@ -157,7 +156,12 @@ square = Square(4) # type: Optional[Square] ```diff --- Black +++ Ruff -@@ -34,13 +34,9 @@ +@@ -29,17 +29,14 @@ + MyLovelyCompanyTeamProjectComponent as component, # DRY + ) + ++ + result = 1 # look ma, no comment migration xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx result = 1 # look ma, no comment migration xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx @@ -173,7 +177,7 @@ square = Square(4) # type: Optional[Square] def func(): -@@ -52,12 +48,19 @@ +@@ -51,12 +48,19 @@ 0.0789, a[-1], # type: ignore ) @@ -194,7 +198,7 @@ square = Square(4) # type: Optional[Square] 0.0456, 0.0789, 0.0123, -@@ -91,53 +94,39 @@ +@@ -90,53 +94,39 @@ # metadata_version errors. ( {}, @@ -264,7 +268,7 @@ square = Square(4) # type: Optional[Square] ), ], ) -@@ -150,8 +139,8 @@ +@@ -149,8 +139,8 @@ # Regression test for https://github.com/psf/black/issues/3756. [ @@ -466,7 +470,6 @@ from com.my_lovely_company.my_lovely_team.my_lovely_project.my_lovely_component MyLovelyCompanyTeamProjectComponent as component, # DRY ) - result = 1 # look ma, no comment migration xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx result = 1 # look ma, no comment migration xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx diff --git a/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__preview_import_line_collapse.py.snap b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__preview_import_line_collapse.py.snap new file mode 100644 index 0000000000..535c0d3426 --- /dev/null +++ b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__preview_import_line_collapse.py.snap @@ -0,0 +1,325 @@ +--- +source: crates/ruff_python_formatter/tests/fixtures.rs +input_file: crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_import_line_collapse.py +--- +## Input + +```python +from middleman.authentication import validate_oauth_token + + +logger = logging.getLogger(__name__) + + +# case 2 comment after import +from middleman.authentication import validate_oauth_token +#comment + +logger = logging.getLogger(__name__) + + +# case 3 comment after import +from middleman.authentication import validate_oauth_token +# comment +logger = logging.getLogger(__name__) + + +from middleman.authentication import validate_oauth_token + + + +logger = logging.getLogger(__name__) + + +# case 4 try catch with import after import +import os +import os + + + +try: + import os +except Exception: + pass + +try: + import os + def func(): + a = 1 +except Exception: + pass + + +# case 5 multiple imports +import os +import os + +import os +import os + + + + + +for i in range(10): + print(i) + + +# case 6 import in function +def func(): + print() + import os + def func(): + pass + print() + + +def func(): + import os + a = 1 + print() + + +def func(): + import os + + + a = 1 + print() + + +def func(): + import os + + + + a = 1 + print() +``` + +## Black Differences + +```diff +--- Black ++++ Ruff +@@ -1,11 +1,11 @@ + from middleman.authentication import validate_oauth_token + ++ + logger = logging.getLogger(__name__) + + + # case 2 comment after import + from middleman.authentication import validate_oauth_token +- + # comment + + logger = logging.getLogger(__name__) +@@ -20,6 +20,7 @@ + + from middleman.authentication import validate_oauth_token + ++ + logger = logging.getLogger(__name__) + + +@@ -27,6 +28,7 @@ + import os + import os + ++ + try: + import os + except Exception: +@@ -49,6 +51,7 @@ + import os + import os + ++ + for i in range(10): + print(i) + +``` + +## Ruff Output + +```python +from middleman.authentication import validate_oauth_token + + +logger = logging.getLogger(__name__) + + +# case 2 comment after import +from middleman.authentication import validate_oauth_token +# comment + +logger = logging.getLogger(__name__) + + +# case 3 comment after import +from middleman.authentication import validate_oauth_token + +# comment +logger = logging.getLogger(__name__) + + +from middleman.authentication import validate_oauth_token + + +logger = logging.getLogger(__name__) + + +# case 4 try catch with import after import +import os +import os + + +try: + import os +except Exception: + pass + +try: + import os + + def func(): + a = 1 + +except Exception: + pass + + +# case 5 multiple imports +import os +import os + +import os +import os + + +for i in range(10): + print(i) + + +# case 6 import in function +def func(): + print() + import os + + def func(): + pass + + print() + + +def func(): + import os + + a = 1 + print() + + +def func(): + import os + + a = 1 + print() + + +def func(): + import os + + a = 1 + print() +``` + +## Black Output + +```python +from middleman.authentication import validate_oauth_token + +logger = logging.getLogger(__name__) + + +# case 2 comment after import +from middleman.authentication import validate_oauth_token + +# comment + +logger = logging.getLogger(__name__) + + +# case 3 comment after import +from middleman.authentication import validate_oauth_token + +# comment +logger = logging.getLogger(__name__) + + +from middleman.authentication import validate_oauth_token + +logger = logging.getLogger(__name__) + + +# case 4 try catch with import after import +import os +import os + +try: + import os +except Exception: + pass + +try: + import os + + def func(): + a = 1 + +except Exception: + pass + + +# case 5 multiple imports +import os +import os + +import os +import os + +for i in range(10): + print(i) + + +# case 6 import in function +def func(): + print() + import os + + def func(): + pass + + print() + + +def func(): + import os + + a = 1 + print() + + +def func(): + import os + + a = 1 + print() + + +def func(): + import os + + a = 1 + print() +``` 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 f679636113..93f28b8669 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 @@ -1,11 +1,31 @@ --- source: crates/ruff_python_formatter/tests/fixtures.rs input_file: crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_long_dict_values.py -snapshot_kind: text --- ## Input ```python +x = { + "xx_xxxxx_xxxxxxxxxx_xxxxxxxxx_xx": ( + "xx:xxxxxxxxxxxxxxxxx_xxxxx_xxxxxxx_xxxxxxxxxxx{xx}xxx_xxxxx_xxxxxxxxx_xxxxxxxxxxxx_xxxx" + ) +} +x = { + "xx_xxxxx_xxxxxxxxxx_xxxxxxxxx_xx": ( + "xx:xxxxxxxxxxxxxxxxx_xxxxx_xxxxxxx_xxxxxxxxxxx{xx}xxx_xxxxx_xxxxxxxxx_xxxxxxxxxxxx_xxxx" + ), +} +x = { + "foo": bar, + "foo": bar, + "foo": ( + xx_xxxxxxxxxxxxxxxxx_xxxxx_xxxxxxx_xxxxxxxxxxxxxx_xxxxx_xxxxxxxxx_xxxxxxxxxxxx_xxxx + ), +} +x = { + "xx_xxxxx_xxxxxxxxxx_xxxxxxxxx_xx": "xx:xxxxxxxxxxxxxxxxx_xxxxx_xxxxxxx_xxxxxxxxxx" +} + my_dict = { "something_something": r"Lorem ipsum dolor sit amet, an sed convenire eloquentiam \t" @@ -13,23 +33,90 @@ my_dict = { r"signiferumque, duo ea vocibus consetetur scriptorem. Facer \t", } +# Function calls as keys +tasks = { + get_key_name( + foo, + bar, + baz, + ): src, + loop.run_in_executor(): src, + loop.run_in_executor(xx_xxxxxxxxxxxxxxxxx_xxxxx_xxxxxxx_xxxxxxxxxxxxxx): src, + loop.run_in_executor( + xx_xxxxxxxxxxxxxxxxx_xxxxx_xxxxxxx_xxxxxxxxxxxxxx_xxxxx_xxxxx + ): src, + loop.run_in_executor(): ( + xx_xxxxxxxxxxxxxxxxx_xxxxx_xxxxxxx_xxxxxxxxxxxxxx_xxxxx_xxxxxxxxx_xxxxxxxxxxxx_xxxx + ), +} + +# Dictionary comprehensions +tasks = { + key_name: ( + xx_xxxxxxxxxxxxxxxxx_xxxxx_xxxxxxx_xxxxxxxxxxxxxx_xxxxx_xxxxxxxxx_xxxxxxxxxxxx_xxxx + ) + for src in sources +} +tasks = {key_name: foobar for src in sources} +tasks = { + get_key_name( + src, + ): "foo" + for src in sources +} +tasks = { + get_key_name( + foo, + bar, + baz, + ): src + for src in sources +} +tasks = { + get_key_name(): ( + xx_xxxxxxxxxxxxxxxxx_xxxxx_xxxxxxx_xxxxxxxxxxxxxx_xxxxx_xxxxxxxxx_xxxxxxxxxxxx_xxxx + ) + for src in sources +} +tasks = {get_key_name(): foobar for src in sources} + + +# Delimiters inside the value +def foo(): + def bar(): + x = { + common.models.DateTimeField: datetime(2020, 1, 31, tzinfo=utc) + timedelta( + days=i + ), + } + x = { + common.models.DateTimeField: ( + datetime(2020, 1, 31, tzinfo=utc) + timedelta(days=i) + ), + } + x = { + "foobar": (123 + 456), + } + x = { + "foobar": (123) + 456, + } + + my_dict = { "a key in my dict": a_very_long_variable * and_a_very_long_function_call() / 100000.0 } - my_dict = { "a key in my dict": a_very_long_variable * and_a_very_long_function_call() * and_another_long_func() / 100000.0 } - my_dict = { "a key in my dict": MyClass.some_attribute.first_call().second_call().third_call(some_args="some value") } { - 'xxxxxx': + "xxxxxx": xxxxxxxxxxxxxxxxxxx.xxxxxxxxxxxxxx( xxxxxxxxxxxxxx={ - 'x': + "x": xxxxxxxxxxxxxxxxxxxxxxxxxx.xxxxxxxxxxxxxxxxxxxxxxxxxxxxx( xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx=( xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx @@ -37,8 +124,8 @@ my_dict = { xxxxxxxxxxxxx=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx .xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx( xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx={ - 'x': x.xx, - 'x': x.x, + "x": x.xx, + "x": x.x, })))) }), } @@ -69,7 +156,9 @@ class Random: ```diff --- Black +++ Ruff -@@ -1,32 +1,26 @@ +@@ -20,11 +20,9 @@ + } + my_dict = { - "something_something": ( - r"Lorem ipsum dolor sit amet, an sed convenire eloquentiam \t" @@ -81,6 +170,31 @@ class Random: + r"signiferumque, duo ea vocibus consetetur scriptorem. Facer \t", } + # Function calls as keys +@@ -79,9 +77,8 @@ + def foo(): + def bar(): + x = { +- common.models.DateTimeField: ( +- datetime(2020, 1, 31, tzinfo=utc) + timedelta(days=i) +- ), ++ common.models.DateTimeField: datetime(2020, 1, 31, tzinfo=utc) ++ + timedelta(days=i), + } + x = { + common.models.DateTimeField: ( +@@ -89,7 +86,7 @@ + ), + } + x = { +- "foobar": 123 + 456, ++ "foobar": (123 + 456), + } + x = { + "foobar": (123) + 456, +@@ -97,24 +94,20 @@ + + my_dict = { - "a key in my dict": ( - a_very_long_variable * and_a_very_long_function_call() / 100000.0 @@ -89,7 +203,6 @@ class Random: + * and_a_very_long_function_call() + / 100000.0 } - my_dict = { - "a key in my dict": ( - a_very_long_variable @@ -102,7 +215,6 @@ class Random: + * and_another_long_func() + / 100000.0 } - my_dict = { - "a key in my dict": ( - MyClass.some_attribute.first_call() @@ -115,7 +227,16 @@ class Random: } { -@@ -58,9 +52,9 @@ +@@ -139,17 +132,17 @@ + + class Random: + def func(): +- random_service.status.active_states.inactive = make_new_top_level_state_from_dict( +- { ++ random_service.status.active_states.inactive = ( ++ make_new_top_level_state_from_dict({ + "topLevelBase": { + "secondaryBase": { "timestamp": 1234, "latitude": 1, "longitude": 2, @@ -127,31 +248,120 @@ class Random: + ).ToJsonString(), } }, - }) +- } ++ }) + ) ``` ## Ruff Output ```python +x = { + "xx_xxxxx_xxxxxxxxxx_xxxxxxxxx_xx": ( + "xx:xxxxxxxxxxxxxxxxx_xxxxx_xxxxxxx_xxxxxxxxxxx{xx}xxx_xxxxx_xxxxxxxxx_xxxxxxxxxxxx_xxxx" + ) +} +x = { + "xx_xxxxx_xxxxxxxxxx_xxxxxxxxx_xx": ( + "xx:xxxxxxxxxxxxxxxxx_xxxxx_xxxxxxx_xxxxxxxxxxx{xx}xxx_xxxxx_xxxxxxxxx_xxxxxxxxxxxx_xxxx" + ), +} +x = { + "foo": bar, + "foo": bar, + "foo": ( + xx_xxxxxxxxxxxxxxxxx_xxxxx_xxxxxxx_xxxxxxxxxxxxxx_xxxxx_xxxxxxxxx_xxxxxxxxxxxx_xxxx + ), +} +x = { + "xx_xxxxx_xxxxxxxxxx_xxxxxxxxx_xx": "xx:xxxxxxxxxxxxxxxxx_xxxxx_xxxxxxx_xxxxxxxxxx" +} + my_dict = { "something_something": r"Lorem ipsum dolor sit amet, an sed convenire eloquentiam \t" r"signiferumque, duo ea vocibus consetetur scriptorem. Facer \t" r"signiferumque, duo ea vocibus consetetur scriptorem. Facer \t", } +# Function calls as keys +tasks = { + get_key_name( + foo, + bar, + baz, + ): src, + loop.run_in_executor(): src, + loop.run_in_executor(xx_xxxxxxxxxxxxxxxxx_xxxxx_xxxxxxx_xxxxxxxxxxxxxx): src, + loop.run_in_executor( + xx_xxxxxxxxxxxxxxxxx_xxxxx_xxxxxxx_xxxxxxxxxxxxxx_xxxxx_xxxxx + ): src, + loop.run_in_executor(): ( + xx_xxxxxxxxxxxxxxxxx_xxxxx_xxxxxxx_xxxxxxxxxxxxxx_xxxxx_xxxxxxxxx_xxxxxxxxxxxx_xxxx + ), +} + +# Dictionary comprehensions +tasks = { + key_name: ( + xx_xxxxxxxxxxxxxxxxx_xxxxx_xxxxxxx_xxxxxxxxxxxxxx_xxxxx_xxxxxxxxx_xxxxxxxxxxxx_xxxx + ) + for src in sources +} +tasks = {key_name: foobar for src in sources} +tasks = { + get_key_name( + src, + ): "foo" + for src in sources +} +tasks = { + get_key_name( + foo, + bar, + baz, + ): src + for src in sources +} +tasks = { + get_key_name(): ( + xx_xxxxxxxxxxxxxxxxx_xxxxx_xxxxxxx_xxxxxxxxxxxxxx_xxxxx_xxxxxxxxx_xxxxxxxxxxxx_xxxx + ) + for src in sources +} +tasks = {get_key_name(): foobar for src in sources} + + +# Delimiters inside the value +def foo(): + def bar(): + x = { + common.models.DateTimeField: datetime(2020, 1, 31, tzinfo=utc) + + timedelta(days=i), + } + x = { + common.models.DateTimeField: ( + datetime(2020, 1, 31, tzinfo=utc) + timedelta(days=i) + ), + } + x = { + "foobar": (123 + 456), + } + x = { + "foobar": (123) + 456, + } + + my_dict = { "a key in my dict": a_very_long_variable * and_a_very_long_function_call() / 100000.0 } - my_dict = { "a key in my dict": a_very_long_variable * and_a_very_long_function_call() * and_another_long_func() / 100000.0 } - my_dict = { "a key in my dict": MyClass.some_attribute.first_call() .second_call() @@ -199,6 +409,27 @@ class Random: ## Black Output ```python +x = { + "xx_xxxxx_xxxxxxxxxx_xxxxxxxxx_xx": ( + "xx:xxxxxxxxxxxxxxxxx_xxxxx_xxxxxxx_xxxxxxxxxxx{xx}xxx_xxxxx_xxxxxxxxx_xxxxxxxxxxxx_xxxx" + ) +} +x = { + "xx_xxxxx_xxxxxxxxxx_xxxxxxxxx_xx": ( + "xx:xxxxxxxxxxxxxxxxx_xxxxx_xxxxxxx_xxxxxxxxxxx{xx}xxx_xxxxx_xxxxxxxxx_xxxxxxxxxxxx_xxxx" + ), +} +x = { + "foo": bar, + "foo": bar, + "foo": ( + xx_xxxxxxxxxxxxxxxxx_xxxxx_xxxxxxx_xxxxxxxxxxxxxx_xxxxx_xxxxxxxxx_xxxxxxxxxxxx_xxxx + ), +} +x = { + "xx_xxxxx_xxxxxxxxxx_xxxxxxxxx_xx": "xx:xxxxxxxxxxxxxxxxx_xxxxx_xxxxxxx_xxxxxxxxxx" +} + my_dict = { "something_something": ( r"Lorem ipsum dolor sit amet, an sed convenire eloquentiam \t" @@ -207,12 +438,80 @@ my_dict = { ), } +# Function calls as keys +tasks = { + get_key_name( + foo, + bar, + baz, + ): src, + loop.run_in_executor(): src, + loop.run_in_executor(xx_xxxxxxxxxxxxxxxxx_xxxxx_xxxxxxx_xxxxxxxxxxxxxx): src, + loop.run_in_executor( + xx_xxxxxxxxxxxxxxxxx_xxxxx_xxxxxxx_xxxxxxxxxxxxxx_xxxxx_xxxxx + ): src, + loop.run_in_executor(): ( + xx_xxxxxxxxxxxxxxxxx_xxxxx_xxxxxxx_xxxxxxxxxxxxxx_xxxxx_xxxxxxxxx_xxxxxxxxxxxx_xxxx + ), +} + +# Dictionary comprehensions +tasks = { + key_name: ( + xx_xxxxxxxxxxxxxxxxx_xxxxx_xxxxxxx_xxxxxxxxxxxxxx_xxxxx_xxxxxxxxx_xxxxxxxxxxxx_xxxx + ) + for src in sources +} +tasks = {key_name: foobar for src in sources} +tasks = { + get_key_name( + src, + ): "foo" + for src in sources +} +tasks = { + get_key_name( + foo, + bar, + baz, + ): src + for src in sources +} +tasks = { + get_key_name(): ( + xx_xxxxxxxxxxxxxxxxx_xxxxx_xxxxxxx_xxxxxxxxxxxxxx_xxxxx_xxxxxxxxx_xxxxxxxxxxxx_xxxx + ) + for src in sources +} +tasks = {get_key_name(): foobar for src in sources} + + +# Delimiters inside the value +def foo(): + def bar(): + x = { + common.models.DateTimeField: ( + datetime(2020, 1, 31, tzinfo=utc) + timedelta(days=i) + ), + } + x = { + common.models.DateTimeField: ( + datetime(2020, 1, 31, tzinfo=utc) + timedelta(days=i) + ), + } + x = { + "foobar": 123 + 456, + } + x = { + "foobar": (123) + 456, + } + + my_dict = { "a key in my dict": ( a_very_long_variable * and_a_very_long_function_call() / 100000.0 ) } - my_dict = { "a key in my dict": ( a_very_long_variable @@ -221,7 +520,6 @@ my_dict = { / 100000.0 ) } - my_dict = { "a key in my dict": ( MyClass.some_attribute.first_call() @@ -252,8 +550,8 @@ my_dict = { class Random: def func(): - random_service.status.active_states.inactive = ( - make_new_top_level_state_from_dict({ + random_service.status.active_states.inactive = make_new_top_level_state_from_dict( + { "topLevelBase": { "secondaryBase": { "timestamp": 1234, @@ -264,6 +562,6 @@ 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 3b7b47cb74..7b36236110 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 @@ -1,7 +1,6 @@ --- source: crates/ruff_python_formatter/tests/fixtures.rs input_file: crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_long_strings.py -snapshot_kind: text --- ## Input @@ -286,7 +285,7 @@ string_with_escaped_nameescape = ( "........................................................................... \\N{LAO KO LA}" ) -msg = lambda x: f"this is a very very very long lambda value {x} that doesn't fit on a single line" +msg = lambda x: f"this is a very very very very long lambda value {x} that doesn't fit on a single line" dict_with_lambda_values = { "join": lambda j: ( @@ -335,6 +334,20 @@ 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"]}''') log.info(f"""Skipping: {'a' == 'b'} {desc['ms_name']} {money=} {dte=} {pos_share=} {desc['status']} {desc['exposure_max']}""") + +x = { + "xx_xxxxx_xxxxxxxxxx_xxxxxxxxx_xx": ( + "xx:xxxxxxxxxxxxxxxxx_xxxxx_xxxxxxx_xxxxxxxxxxx{xx}xxx_xxxxx_xxxxxxxxx_xxxxxxxxxxxx_xxxx" + ) +} +x = { + "xx_xxxxx_xxxxxxxxxx_xxxxxxxxx_xx": "xx:xxxxxxxxxxxxxxxxx_xxxxx_xxxxxxx_xxxxxxxxxxx{xx}xxx_xxxxx_xxxxxxxxx_xxxxxxxxxxxx_xxxx", +} +x = { + "xx_xxxxx_xxxxxxxxxx_xxxxxxxxx_xx": ( + "xx:xxxxxxxxxxxxxxxxx_xxxxx_xxxxxxx_xxxxxxxxxx" + ) +} ``` ## Black Differences @@ -841,7 +854,7 @@ log.info(f"""Skipping: {'a' == 'b'} {desc['ms_name']} {money=} {dte=} {pos_share long_unmergable_string_with_pragma = ( "This is a really long string that can't be merged because it has a likely pragma at the end" # type: ignore -@@ -468,51 +358,24 @@ +@@ -468,49 +358,24 @@ " of it." ) @@ -893,16 +906,15 @@ log.info(f"""Skipping: {'a' == 'b'} {desc['ms_name']} {money=} {dte=} {pos_share -) +string_with_escaped_nameescape = "........................................................................... \\N{LAO KO LA}" - msg = ( -- lambda x: ( -- f"this is a very very very long lambda value {x} that doesn't fit on a single" -- " line" -- ) -+ lambda x: f"this is a very very very long lambda value {x} that doesn't fit on a single line" +-msg = lambda x: ( +- f"this is a very very very very long lambda value {x} that doesn't fit on a" +- " single line" ++msg = ( ++ lambda x: f"this is a very very very very long lambda value {x} that doesn't fit on a single line" ) dict_with_lambda_values = { -@@ -524,65 +387,58 @@ +@@ -522,65 +387,58 @@ # Complex string concatenations with a method call in the middle. code = ( @@ -927,50 +939,50 @@ log.info(f"""Skipping: {'a' == 'b'} {desc['ms_name']} {money=} {dte=} {pos_share +call(body=("%s %s" % ((",".join(items)), suffix))) log.info( -- "Skipping:" -- f' {desc["db_id"]=} {desc["ms_name"]} {money=} {dte=} {pos_share=} {desc["status"]=} {desc["exposure_max"]=}' +- f'Skipping: {desc["db_id"]=} {desc["ms_name"]} {money=} {dte=} {pos_share=}' +- f' {desc["status"]=} {desc["exposure_max"]=}' + f'Skipping: {desc["db_id"]=} {desc["ms_name"]} {money=} {dte=} {pos_share=} {desc["status"]=} {desc["exposure_max"]=}' ) log.info( -- "Skipping:" -- f" {desc['db_id']=} {desc['ms_name']} {money=} {dte=} {pos_share=} {desc['status']=} {desc['exposure_max']=}" +- f"Skipping: {desc['db_id']=} {desc['ms_name']} {money=} {dte=} {pos_share=}" +- f" {desc['status']=} {desc['exposure_max']=}" + f"Skipping: {desc['db_id']=} {desc['ms_name']} {money=} {dte=} {pos_share=} {desc['status']=} {desc['exposure_max']=}" ) 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=")}' +- f' {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( -- "Skipping:" -- f' {desc["db_id"]} {desc["ms_name"]} {money=} {(x := "abc=")=} {pos_share=} {desc["status"]} {desc["exposure_max"]}' +- f'Skipping: {desc["db_id"]} {desc["ms_name"]} {money=} {(x := "abc=")=}' +- f' {pos_share=} {desc["status"]} {desc["exposure_max"]}' + f'Skipping: {desc["db_id"]} {desc["ms_name"]} {money=} {(x := "abc=")=} {pos_share=} {desc["status"]} {desc["exposure_max"]}' ) log.info( -- "Skipping:" -- f' {desc["db_id"]} {foo("bar",x=123)=} {money=} {dte=} {pos_share=} {desc["status"]} {desc["exposure_max"]}' +- f'Skipping: {desc["db_id"]} {foo("bar",x=123)=} {money=} {dte=} {pos_share=}' +- f' {desc["status"]} {desc["exposure_max"]}' + f'Skipping: {desc["db_id"]} {foo("bar",x=123)=} {money=} {dte=} {pos_share=} {desc["status"]} {desc["exposure_max"]}' ) log.info( -- "Skipping:" -- f' {foo("asdf")=} {desc["ms_name"]} {money=} {dte=} {pos_share=} {desc["status"]} {desc["exposure_max"]}' +- f'Skipping: {foo("asdf")=} {desc["ms_name"]} {money=} {dte=} {pos_share=}' +- f' {desc["status"]} {desc["exposure_max"]}' + f'Skipping: {foo("asdf")=} {desc["ms_name"]} {money=} {dte=} {pos_share=} {desc["status"]} {desc["exposure_max"]}' ) log.info( -- "Skipping:" -- f" {'a' == 'b' == 'c' == 'd'} {desc['ms_name']} {money=} {dte=} {pos_share=} {desc['status']} {desc['exposure_max']}" +- f'Skipping: {"a" == "b" == "c" == "d"} {desc["ms_name"]} {money=} {dte=}' +- f' {pos_share=} {desc["status"]} {desc["exposure_max"]}' + f"Skipping: {'a' == 'b' == 'c' == 'd'} {desc['ms_name']} {money=} {dte=} {pos_share=} {desc['status']} {desc['exposure_max']}" ) log.info( -- "Skipping:" -- f' {"a" == "b" == "c" == "d"=} {desc["ms_name"]} {money=} {dte=} {pos_share=} {desc["status"]} {desc["exposure_max"]}' +- f'Skipping: {"a" == "b" == "c" == "d"=} {desc["ms_name"]} {money=} {dte=}' +- f' {pos_share=} {desc["status"]} {desc["exposure_max"]}' + f'Skipping: {"a" == "b" == "c" == "d"=} {desc["ms_name"]} {money=} {dte=} {pos_share=} {desc["status"]} {desc["exposure_max"]}' ) @@ -986,13 +998,30 @@ log.info(f"""Skipping: {'a' == 'b'} {desc['ms_name']} {money=} {dte=} {pos_share ) log.info( -@@ -590,5 +446,5 @@ +@@ -588,7 +446,7 @@ ) 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"]}""" ) + + x = { +@@ -597,10 +455,10 @@ + ) + } + x = { +- "xx_xxxxx_xxxxxxxxxx_xxxxxxxxx_xx": ( +- "xx:xxxxxxxxxxxxxxxxx_xxxxx_xxxxxxx_xxxxxxxxxxx{xx}xxx_xxxxx_xxxxxxxxx_xxxxxxxxxxxx_xxxx" +- ), ++ "xx_xxxxx_xxxxxxxxxx_xxxxxxxxx_xx": "xx:xxxxxxxxxxxxxxxxx_xxxxx_xxxxxxx_xxxxxxxxxxx{xx}xxx_xxxxx_xxxxxxxxx_xxxxxxxxxxxx_xxxx", + } + x = { +- "xx_xxxxx_xxxxxxxxxx_xxxxxxxxx_xx": "xx:xxxxxxxxxxxxxxxxx_xxxxx_xxxxxxx_xxxxxxxxxx" ++ "xx_xxxxx_xxxxxxxxxx_xxxxxxxxx_xx": ( ++ "xx:xxxxxxxxxxxxxxxxx_xxxxx_xxxxxxx_xxxxxxxxxx" ++ ) + } ``` ## Ruff Output @@ -1375,7 +1404,7 @@ string_with_escaped_nameescape = ".............................................. string_with_escaped_nameescape = "........................................................................... \\N{LAO KO LA}" msg = ( - lambda x: f"this is a very very very long lambda value {x} that doesn't fit on a single line" + lambda x: f"this is a very very very very long lambda value {x} that doesn't fit on a single line" ) dict_with_lambda_values = { @@ -1448,6 +1477,20 @@ log.info( log.info( f"""Skipping: {"a" == "b"} {desc["ms_name"]} {money=} {dte=} {pos_share=} {desc["status"]} {desc["exposure_max"]}""" ) + +x = { + "xx_xxxxx_xxxxxxxxxx_xxxxxxxxx_xx": ( + "xx:xxxxxxxxxxxxxxxxx_xxxxx_xxxxxxx_xxxxxxxxxxx{xx}xxx_xxxxx_xxxxxxxxx_xxxxxxxxxxxx_xxxx" + ) +} +x = { + "xx_xxxxx_xxxxxxxxxx_xxxxxxxxx_xx": "xx:xxxxxxxxxxxxxxxxx_xxxxx_xxxxxxx_xxxxxxxxxxx{xx}xxx_xxxxx_xxxxxxxxx_xxxxxxxxxxxx_xxxx", +} +x = { + "xx_xxxxx_xxxxxxxxxx_xxxxxxxxx_xx": ( + "xx:xxxxxxxxxxxxxxxxx_xxxxx_xxxxxxx_xxxxxxxxxx" + ) +} ``` ## Black Output @@ -1963,11 +2006,9 @@ string_with_escaped_nameescape = ( " \\N{LAO KO LA}" ) -msg = ( - lambda x: ( - f"this is a very very very long lambda value {x} that doesn't fit on a single" - " line" - ) +msg = lambda x: ( + f"this is a very very very very long lambda value {x} that doesn't fit on a" + " single line" ) dict_with_lambda_values = { @@ -1992,43 +2033,43 @@ code = ( call(body="%s %s" % (",".join(items), suffix)) log.info( - "Skipping:" - f' {desc["db_id"]=} {desc["ms_name"]} {money=} {dte=} {pos_share=} {desc["status"]=} {desc["exposure_max"]=}' + f'Skipping: {desc["db_id"]=} {desc["ms_name"]} {money=} {dte=} {pos_share=}' + f' {desc["status"]=} {desc["exposure_max"]=}' ) log.info( - "Skipping:" - f" {desc['db_id']=} {desc['ms_name']} {money=} {dte=} {pos_share=} {desc['status']=} {desc['exposure_max']=}" + f"Skipping: {desc['db_id']=} {desc['ms_name']} {money=} {dte=} {pos_share=}" + f" {desc['status']=} {desc['exposure_max']=}" ) 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=")}' + f' {pos_share=} {desc["status"]} {desc["exposure_max"]}' ) log.info( - "Skipping:" - f' {desc["db_id"]} {desc["ms_name"]} {money=} {(x := "abc=")=} {pos_share=} {desc["status"]} {desc["exposure_max"]}' + f'Skipping: {desc["db_id"]} {desc["ms_name"]} {money=} {(x := "abc=")=}' + f' {pos_share=} {desc["status"]} {desc["exposure_max"]}' ) log.info( - "Skipping:" - f' {desc["db_id"]} {foo("bar",x=123)=} {money=} {dte=} {pos_share=} {desc["status"]} {desc["exposure_max"]}' + f'Skipping: {desc["db_id"]} {foo("bar",x=123)=} {money=} {dte=} {pos_share=}' + f' {desc["status"]} {desc["exposure_max"]}' ) log.info( - "Skipping:" - f' {foo("asdf")=} {desc["ms_name"]} {money=} {dte=} {pos_share=} {desc["status"]} {desc["exposure_max"]}' + f'Skipping: {foo("asdf")=} {desc["ms_name"]} {money=} {dte=} {pos_share=}' + f' {desc["status"]} {desc["exposure_max"]}' ) log.info( - "Skipping:" - f" {'a' == 'b' == 'c' == 'd'} {desc['ms_name']} {money=} {dte=} {pos_share=} {desc['status']} {desc['exposure_max']}" + f'Skipping: {"a" == "b" == "c" == "d"} {desc["ms_name"]} {money=} {dte=}' + f' {pos_share=} {desc["status"]} {desc["exposure_max"]}' ) log.info( - "Skipping:" - f' {"a" == "b" == "c" == "d"=} {desc["ms_name"]} {money=} {dte=} {pos_share=} {desc["status"]} {desc["exposure_max"]}' + f'Skipping: {"a" == "b" == "c" == "d"=} {desc["ms_name"]} {money=} {dte=}' + f' {pos_share=} {desc["status"]} {desc["exposure_max"]}' ) log.info( @@ -2047,4 +2088,18 @@ log.info( log.info( f"""Skipping: {'a' == 'b'} {desc['ms_name']} {money=} {dte=} {pos_share=} {desc['status']} {desc['exposure_max']}""" ) + +x = { + "xx_xxxxx_xxxxxxxxxx_xxxxxxxxx_xx": ( + "xx:xxxxxxxxxxxxxxxxx_xxxxx_xxxxxxx_xxxxxxxxxxx{xx}xxx_xxxxx_xxxxxxxxx_xxxxxxxxxxxx_xxxx" + ) +} +x = { + "xx_xxxxx_xxxxxxxxxx_xxxxxxxxx_xx": ( + "xx:xxxxxxxxxxxxxxxxx_xxxxx_xxxxxxx_xxxxxxxxxxx{xx}xxx_xxxxx_xxxxxxxxx_xxxxxxxxxxxx_xxxx" + ), +} +x = { + "xx_xxxxx_xxxxxxxxxx_xxxxxxxxx_xx": "xx:xxxxxxxxxxxxxxxxx_xxxxx_xxxxxxx_xxxxxxxxxx" +} ``` 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 7f61408c3c..615da9cd12 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 @@ -1,7 +1,6 @@ --- source: crates/ruff_python_formatter/tests/fixtures.rs input_file: crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_long_strings__regression.py -snapshot_kind: text --- ## Input @@ -559,6 +558,7 @@ a_dict = { } # Regression test for https://github.com/psf/black/issues/3506. +# Regressed again by https://github.com/psf/black/pull/4498 s = ( "With single quote: ' " f" {my_dict['foo']}" @@ -684,7 +684,7 @@ s = f'Lorem Ipsum is simply dummy text of the printing and typesetting industry: + ( + "xxxxxxxxxx xxxx xx xxxxxx(%x) xx %x xxxx xx xxx %x.xx" + % (len(self) + 1, xxxx.xxxxxxxxxx, xxxx.xxxxxxxxxx) - ) ++ ) + + ( + " %.3f (%s) to %.3f (%s).\n" + % ( @@ -693,7 +693,7 @@ s = f'Lorem Ipsum is simply dummy text of the printing and typesetting industry: + x, + xxxx.xxxxxxxxxxxxxx(xx), + ) -+ ) + ) ) @@ -1171,13 +1171,21 @@ s = f'Lorem Ipsum is simply dummy text of the printing and typesetting industry: ) # Regression test for https://github.com/psf/black/issues/3455. -@@ -674,7 +621,4 @@ +@@ -673,14 +620,6 @@ + # Regression test for https://github.com/psf/black/issues/3506. - s = f"With single quote: ' {my_dict['foo']} With double quote: \" {my_dict['bar']}" + # Regressed again by https://github.com/psf/black/pull/4498 +-s = ( +- "With single quote: ' " +- f" {my_dict['foo']}" +- ' With double quote: " ' +- f' {my_dict["bar"]}' +-) ++s = f"With single quote: ' {my_dict['foo']} With double quote: \" {my_dict['bar']}" -s = ( - "Lorem Ipsum is simply dummy text of the printing and typesetting" -- f" industry:'{my_dict['foo']}'" +- f' industry:\'{my_dict["foo"]}\'' -) +s = f"Lorem Ipsum is simply dummy text of the printing and typesetting industry:'{my_dict['foo']}'" ``` @@ -1806,6 +1814,7 @@ a_dict = { } # Regression test for https://github.com/psf/black/issues/3506. +# Regressed again by https://github.com/psf/black/pull/4498 s = f"With single quote: ' {my_dict['foo']} With double quote: \" {my_dict['bar']}" s = f"Lorem Ipsum is simply dummy text of the printing and typesetting industry:'{my_dict['foo']}'" @@ -2488,10 +2497,16 @@ a_dict = { } # Regression test for https://github.com/psf/black/issues/3506. -s = f"With single quote: ' {my_dict['foo']} With double quote: \" {my_dict['bar']}" +# Regressed again by https://github.com/psf/black/pull/4498 +s = ( + "With single quote: ' " + f" {my_dict['foo']}" + ' With double quote: " ' + f' {my_dict["bar"]}' +) s = ( "Lorem Ipsum is simply dummy text of the printing and typesetting" - f" industry:'{my_dict['foo']}'" + f' industry:\'{my_dict["foo"]}\'' ) ``` diff --git a/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__preview_multiline_strings.py.snap b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__preview_multiline_strings.py.snap index 8d58214ce1..353d573496 100644 --- a/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__preview_multiline_strings.py.snap +++ b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__preview_multiline_strings.py.snap @@ -1,7 +1,6 @@ --- source: crates/ruff_python_formatter/tests/fixtures.rs input_file: crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_multiline_strings.py -snapshot_kind: text --- ## Input @@ -188,6 +187,73 @@ test assert some_var == expected_result, f""" expected: {expected_result} actual: {some_var}""" + + +def foo(): + a = { + xxxx_xxxxxxx.xxxxxx_xxxxxxxxxxxx_xxxxxx_xx_xxx_xxxxxx: { + "xxxxx": """Sxxxxx xxxxxxxxxxxx xxx xxxxx (xxxxxx xxx xxxxxxx)""", + "xxxxxxxx": ( + """Sxxxxxxx xxxxxxxx, xxxxxxx xx xxxxxxxxx + xxxxxxxxxxxxx xxxxxxx xxxxxxxxx xxx-xxxxxxxxxx xxxxxx xx xxx-xxxxxx""" + ), + "xxxxxxxx": """Sxxxxxxx xxxxxxxx, xxxxxxx xx xxxxxxxxx + xxxxxxxxxxxxx xxxxxxx xxxxxxxxx xxx-xxxxxxxxxx xxxxxx xx xxx-xxxxxx""" + }, + } + + +xxxx_xxxxxxx.xxxxxx_xxxxxxxxxxxx_xxxxxx_xx_xxx_xxxxxx = { + "xxxxx": """Sxxxxx xxxxxxxxxxxx xxx xxxxx (xxxxxx xxx xxxxxxx)""", + "xxxxxxxx": ( + """Sxxxxxxx xxxxxxxx, xxxxxxx xx xxxxxxxxx + xxxxxxxxxxxxx xxxxxxx xxxxxxxxx xxx-xxxxxxxxxx xxxxxx xx xxx-xxxxxx""" + ), + "xxxxx_xxxxxxxxxx_xxxxxxxxx_xx": ( + """ +a +a +a +a +a""" + ), + "xx_xxxxx_xxxxxxxxxx_xxxxxxxxx_xx": """ +a +a +a +a +a""", +} + +a = """ +""" if """ +""" == """ +""" else """ +""" + +a = """ +""" if b else """ +""" + +a = """ +""" if """ +""" == """ +""" else b + +a = b if """ +""" == """ +""" else """ +""" + +a = """ +""" if b else c + +a = c if b else """ +""" + +a = b if """ +""" == """ +""" else c ``` ## Black Differences @@ -378,6 +444,36 @@ actual: {some_var}""" assert some_var == expected_result, """ test +@@ -224,10 +267,8 @@ + """Sxxxxxxx xxxxxxxx, xxxxxxx xx xxxxxxxxx + xxxxxxxxxxxxx xxxxxxx xxxxxxxxx xxx-xxxxxxxxxx xxxxxx xx xxx-xxxxxx""" + ), +- "xxxxxxxx": ( +- """Sxxxxxxx xxxxxxxx, xxxxxxx xx xxxxxxxxx +- xxxxxxxxxxxxx xxxxxxx xxxxxxxxx xxx-xxxxxxxxxx xxxxxx xx xxx-xxxxxx""" +- ), ++ "xxxxxxxx": """Sxxxxxxx xxxxxxxx, xxxxxxx xx xxxxxxxxx ++ xxxxxxxxxxxxx xxxxxxx xxxxxxxxx xxx-xxxxxxxxxx xxxxxx xx xxx-xxxxxx""", + }, + } + +@@ -246,14 +287,12 @@ + a + a""" + ), +- "xx_xxxxx_xxxxxxxxxx_xxxxxxxxx_xx": ( +- """ ++ "xx_xxxxx_xxxxxxxxxx_xxxxxxxxx_xx": """ + a + a + a + a +-a""" +- ), ++a""", + } + + a = ( ``` ## Ruff Output @@ -642,6 +738,105 @@ test assert some_var == expected_result, f""" expected: {expected_result} actual: {some_var}""" + + +def foo(): + a = { + xxxx_xxxxxxx.xxxxxx_xxxxxxxxxxxx_xxxxxx_xx_xxx_xxxxxx: { + "xxxxx": """Sxxxxx xxxxxxxxxxxx xxx xxxxx (xxxxxx xxx xxxxxxx)""", + "xxxxxxxx": ( + """Sxxxxxxx xxxxxxxx, xxxxxxx xx xxxxxxxxx + xxxxxxxxxxxxx xxxxxxx xxxxxxxxx xxx-xxxxxxxxxx xxxxxx xx xxx-xxxxxx""" + ), + "xxxxxxxx": """Sxxxxxxx xxxxxxxx, xxxxxxx xx xxxxxxxxx + xxxxxxxxxxxxx xxxxxxx xxxxxxxxx xxx-xxxxxxxxxx xxxxxx xx xxx-xxxxxx""", + }, + } + + +xxxx_xxxxxxx.xxxxxx_xxxxxxxxxxxx_xxxxxx_xx_xxx_xxxxxx = { + "xxxxx": """Sxxxxx xxxxxxxxxxxx xxx xxxxx (xxxxxx xxx xxxxxxx)""", + "xxxxxxxx": ( + """Sxxxxxxx xxxxxxxx, xxxxxxx xx xxxxxxxxx + xxxxxxxxxxxxx xxxxxxx xxxxxxxxx xxx-xxxxxxxxxx xxxxxx xx xxx-xxxxxx""" + ), + "xxxxx_xxxxxxxxxx_xxxxxxxxx_xx": ( + """ +a +a +a +a +a""" + ), + "xx_xxxxx_xxxxxxxxxx_xxxxxxxxx_xx": """ +a +a +a +a +a""", +} + +a = ( + """ +""" + if """ +""" + == """ +""" + else """ +""" +) + +a = ( + """ +""" + if b + else """ +""" +) + +a = ( + """ +""" + if """ +""" + == """ +""" + else b +) + +a = ( + b + if """ +""" + == """ +""" + else """ +""" +) + +a = ( + """ +""" + if b + else c +) + +a = ( + c + if b + else """ +""" +) + +a = ( + b + if """ +""" + == """ +""" + else c +) ``` ## Black Output @@ -863,4 +1058,107 @@ test assert some_var == expected_result, f""" expected: {expected_result} actual: {some_var}""" + + +def foo(): + a = { + xxxx_xxxxxxx.xxxxxx_xxxxxxxxxxxx_xxxxxx_xx_xxx_xxxxxx: { + "xxxxx": """Sxxxxx xxxxxxxxxxxx xxx xxxxx (xxxxxx xxx xxxxxxx)""", + "xxxxxxxx": ( + """Sxxxxxxx xxxxxxxx, xxxxxxx xx xxxxxxxxx + xxxxxxxxxxxxx xxxxxxx xxxxxxxxx xxx-xxxxxxxxxx xxxxxx xx xxx-xxxxxx""" + ), + "xxxxxxxx": ( + """Sxxxxxxx xxxxxxxx, xxxxxxx xx xxxxxxxxx + xxxxxxxxxxxxx xxxxxxx xxxxxxxxx xxx-xxxxxxxxxx xxxxxx xx xxx-xxxxxx""" + ), + }, + } + + +xxxx_xxxxxxx.xxxxxx_xxxxxxxxxxxx_xxxxxx_xx_xxx_xxxxxx = { + "xxxxx": """Sxxxxx xxxxxxxxxxxx xxx xxxxx (xxxxxx xxx xxxxxxx)""", + "xxxxxxxx": ( + """Sxxxxxxx xxxxxxxx, xxxxxxx xx xxxxxxxxx + xxxxxxxxxxxxx xxxxxxx xxxxxxxxx xxx-xxxxxxxxxx xxxxxx xx xxx-xxxxxx""" + ), + "xxxxx_xxxxxxxxxx_xxxxxxxxx_xx": ( + """ +a +a +a +a +a""" + ), + "xx_xxxxx_xxxxxxxxxx_xxxxxxxxx_xx": ( + """ +a +a +a +a +a""" + ), +} + +a = ( + """ +""" + if """ +""" + == """ +""" + else """ +""" +) + +a = ( + """ +""" + if b + else """ +""" +) + +a = ( + """ +""" + if """ +""" + == """ +""" + else b +) + +a = ( + b + if """ +""" + == """ +""" + else """ +""" +) + +a = ( + """ +""" + if b + else c +) + +a = ( + c + if b + else """ +""" +) + +a = ( + b + if """ +""" + == """ +""" + else c +) ``` diff --git a/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__preview_remove_multiline_lone_list_item_parens.py.snap b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__preview_remove_multiline_lone_list_item_parens.py.snap new file mode 100644 index 0000000000..1abf5ac61e --- /dev/null +++ b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__preview_remove_multiline_lone_list_item_parens.py.snap @@ -0,0 +1,535 @@ +--- +source: crates/ruff_python_formatter/tests/fixtures.rs +input_file: crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_remove_multiline_lone_list_item_parens.py +--- +## Input + +```python +items = [(x for x in [1])] + +items = [ + ( + {"key1": "val1", "key2": "val2", "key3": "val3"} + if some_var == "long strings" + else {"key": "val"} + ) +] +items = [ + ( + {"key1": "val1", "key2": "val2"} + if some_var == "" + else {"key": "val"} + ) +] +items = [ + ( + "123456890123457890123468901234567890" + if some_var == "long strings" + else "123467890123467890" + ) +] +items = [ + ( + {"key1": "val1", "key2": "val2", "key3": "val3"} + and some_var == "long strings" + and {"key": "val"} + ) +] +items = [ + ( + "123456890123457890123468901234567890" + and some_var == "long strings" + and "123467890123467890" + ) +] +items = [ + ( + long_variable_name + and even_longer_variable_name + and yet_another_very_long_variable_name + ) +] + +# Shouldn't remove trailing commas +items = [ + ( + {"key1": "val1", "key2": "val2", "key3": "val3"} + if some_var == "long strings" + else {"key": "val"} + ), +] +items = [ + ( + {"key1": "val1", "key2": "val2", "key3": "val3"} + and some_var == "long strings" + and {"key": "val"} + ), +] +items = [ + ( + "123456890123457890123468901234567890" + and some_var == "long strings" + and "123467890123467890" + ), +] +items = [ + ( + long_variable_name + and even_longer_variable_name + and yet_another_very_long_variable_name + ), +] + +# Shouldn't add parentheses +items = [ + {"key1": "val1", "key2": "val2", "key3": "val3"} + if some_var == "long strings" + else {"key": "val"} +] +items = [{"key1": "val1", "key2": "val2"} if some_var == "" else {"key": "val"}] + +# Shouldn't crash with comments +items = [ + ( # comment + {"key1": "val1", "key2": "val2", "key3": "val3"} + if some_var == "long strings" + else {"key": "val"} + ) +] +items = [ + ( + {"key1": "val1", "key2": "val2", "key3": "val3"} + if some_var == "long strings" + else {"key": "val"} + ) # comment +] + +items = [ # comment + ( + {"key1": "val1", "key2": "val2", "key3": "val3"} + if some_var == "long strings" + else {"key": "val"} + ) +] +items = [ + ( + {"key1": "val1", "key2": "val2", "key3": "val3"} + if some_var == "long strings" + else {"key": "val"} + ) +] # comment + +items = [ + ( + {"key1": "val1", "key2": "val2", "key3": "val3"} # comment + if some_var == "long strings" + else {"key": "val"} + ) +] + +items = [ # comment + ( # comment + {"key1": "val1", "key2": "val2", "key3": "val3"} + if some_var == "long strings" + else {"key": "val"} + ) +] +items = [ + ( + {"key1": "val1", "key2": "val2", "key3": "val3"} + if some_var == "long strings" + else {"key": "val"} + ) # comment +] # comment +``` + +## Black Differences + +```diff +--- Black ++++ Ruff +@@ -1,30 +1,40 @@ + items = [(x for x in [1])] + + items = [ +- {"key1": "val1", "key2": "val2", "key3": "val3"} +- if some_var == "long strings" +- else {"key": "val"} ++ ( ++ {"key1": "val1", "key2": "val2", "key3": "val3"} ++ if some_var == "long strings" ++ else {"key": "val"} ++ ) + ] +-items = [{"key1": "val1", "key2": "val2"} if some_var == "" else {"key": "val"}] ++items = [({"key1": "val1", "key2": "val2"} if some_var == "" else {"key": "val"})] + items = [ +- "123456890123457890123468901234567890" +- if some_var == "long strings" +- else "123467890123467890" ++ ( ++ "123456890123457890123468901234567890" ++ if some_var == "long strings" ++ else "123467890123467890" ++ ) + ] + items = [ +- {"key1": "val1", "key2": "val2", "key3": "val3"} +- and some_var == "long strings" +- and {"key": "val"} ++ ( ++ {"key1": "val1", "key2": "val2", "key3": "val3"} ++ and some_var == "long strings" ++ and {"key": "val"} ++ ) + ] + items = [ +- "123456890123457890123468901234567890" +- and some_var == "long strings" +- and "123467890123467890" ++ ( ++ "123456890123457890123468901234567890" ++ and some_var == "long strings" ++ and "123467890123467890" ++ ) + ] + items = [ +- long_variable_name +- and even_longer_variable_name +- and yet_another_very_long_variable_name ++ ( ++ long_variable_name ++ and even_longer_variable_name ++ and yet_another_very_long_variable_name ++ ) + ] + + # Shouldn't remove trailing commas +@@ -66,41 +76,55 @@ + items = [{"key1": "val1", "key2": "val2"} if some_var == "" else {"key": "val"}] + + # Shouldn't crash with comments +-items = [ # comment +- {"key1": "val1", "key2": "val2", "key3": "val3"} +- if some_var == "long strings" +- else {"key": "val"} ++items = [ ++ ( # comment ++ {"key1": "val1", "key2": "val2", "key3": "val3"} ++ if some_var == "long strings" ++ else {"key": "val"} ++ ) + ] + items = [ +- {"key1": "val1", "key2": "val2", "key3": "val3"} +- if some_var == "long strings" +- else {"key": "val"} +-] # comment ++ ( ++ {"key1": "val1", "key2": "val2", "key3": "val3"} ++ if some_var == "long strings" ++ else {"key": "val"} ++ ) # comment ++] + + items = [ # comment +- {"key1": "val1", "key2": "val2", "key3": "val3"} +- if some_var == "long strings" +- else {"key": "val"} ++ ( ++ {"key1": "val1", "key2": "val2", "key3": "val3"} ++ if some_var == "long strings" ++ else {"key": "val"} ++ ) + ] + items = [ +- {"key1": "val1", "key2": "val2", "key3": "val3"} +- if some_var == "long strings" +- else {"key": "val"} ++ ( ++ {"key1": "val1", "key2": "val2", "key3": "val3"} ++ if some_var == "long strings" ++ else {"key": "val"} ++ ) + ] # comment + + items = [ +- {"key1": "val1", "key2": "val2", "key3": "val3"} # comment +- if some_var == "long strings" +- else {"key": "val"} ++ ( ++ {"key1": "val1", "key2": "val2", "key3": "val3"} # comment ++ if some_var == "long strings" ++ else {"key": "val"} ++ ) + ] + +-items = [ # comment # comment +- {"key1": "val1", "key2": "val2", "key3": "val3"} +- if some_var == "long strings" +- else {"key": "val"} ++items = [ # comment ++ ( # comment ++ {"key1": "val1", "key2": "val2", "key3": "val3"} ++ if some_var == "long strings" ++ else {"key": "val"} ++ ) + ] + items = [ +- {"key1": "val1", "key2": "val2", "key3": "val3"} +- if some_var == "long strings" +- else {"key": "val"} +-] # comment # comment ++ ( ++ {"key1": "val1", "key2": "val2", "key3": "val3"} ++ if some_var == "long strings" ++ else {"key": "val"} ++ ) # comment ++] # comment +``` + +## Ruff Output + +```python +items = [(x for x in [1])] + +items = [ + ( + {"key1": "val1", "key2": "val2", "key3": "val3"} + if some_var == "long strings" + else {"key": "val"} + ) +] +items = [({"key1": "val1", "key2": "val2"} if some_var == "" else {"key": "val"})] +items = [ + ( + "123456890123457890123468901234567890" + if some_var == "long strings" + else "123467890123467890" + ) +] +items = [ + ( + {"key1": "val1", "key2": "val2", "key3": "val3"} + and some_var == "long strings" + and {"key": "val"} + ) +] +items = [ + ( + "123456890123457890123468901234567890" + and some_var == "long strings" + and "123467890123467890" + ) +] +items = [ + ( + long_variable_name + and even_longer_variable_name + and yet_another_very_long_variable_name + ) +] + +# Shouldn't remove trailing commas +items = [ + ( + {"key1": "val1", "key2": "val2", "key3": "val3"} + if some_var == "long strings" + else {"key": "val"} + ), +] +items = [ + ( + {"key1": "val1", "key2": "val2", "key3": "val3"} + and some_var == "long strings" + and {"key": "val"} + ), +] +items = [ + ( + "123456890123457890123468901234567890" + and some_var == "long strings" + and "123467890123467890" + ), +] +items = [ + ( + long_variable_name + and even_longer_variable_name + and yet_another_very_long_variable_name + ), +] + +# Shouldn't add parentheses +items = [ + {"key1": "val1", "key2": "val2", "key3": "val3"} + if some_var == "long strings" + else {"key": "val"} +] +items = [{"key1": "val1", "key2": "val2"} if some_var == "" else {"key": "val"}] + +# Shouldn't crash with comments +items = [ + ( # comment + {"key1": "val1", "key2": "val2", "key3": "val3"} + if some_var == "long strings" + else {"key": "val"} + ) +] +items = [ + ( + {"key1": "val1", "key2": "val2", "key3": "val3"} + if some_var == "long strings" + else {"key": "val"} + ) # comment +] + +items = [ # comment + ( + {"key1": "val1", "key2": "val2", "key3": "val3"} + if some_var == "long strings" + else {"key": "val"} + ) +] +items = [ + ( + {"key1": "val1", "key2": "val2", "key3": "val3"} + if some_var == "long strings" + else {"key": "val"} + ) +] # comment + +items = [ + ( + {"key1": "val1", "key2": "val2", "key3": "val3"} # comment + if some_var == "long strings" + else {"key": "val"} + ) +] + +items = [ # comment + ( # comment + {"key1": "val1", "key2": "val2", "key3": "val3"} + if some_var == "long strings" + else {"key": "val"} + ) +] +items = [ + ( + {"key1": "val1", "key2": "val2", "key3": "val3"} + if some_var == "long strings" + else {"key": "val"} + ) # comment +] # comment +``` + +## Black Output + +```python +items = [(x for x in [1])] + +items = [ + {"key1": "val1", "key2": "val2", "key3": "val3"} + if some_var == "long strings" + else {"key": "val"} +] +items = [{"key1": "val1", "key2": "val2"} if some_var == "" else {"key": "val"}] +items = [ + "123456890123457890123468901234567890" + if some_var == "long strings" + else "123467890123467890" +] +items = [ + {"key1": "val1", "key2": "val2", "key3": "val3"} + and some_var == "long strings" + and {"key": "val"} +] +items = [ + "123456890123457890123468901234567890" + and some_var == "long strings" + and "123467890123467890" +] +items = [ + long_variable_name + and even_longer_variable_name + and yet_another_very_long_variable_name +] + +# Shouldn't remove trailing commas +items = [ + ( + {"key1": "val1", "key2": "val2", "key3": "val3"} + if some_var == "long strings" + else {"key": "val"} + ), +] +items = [ + ( + {"key1": "val1", "key2": "val2", "key3": "val3"} + and some_var == "long strings" + and {"key": "val"} + ), +] +items = [ + ( + "123456890123457890123468901234567890" + and some_var == "long strings" + and "123467890123467890" + ), +] +items = [ + ( + long_variable_name + and even_longer_variable_name + and yet_another_very_long_variable_name + ), +] + +# Shouldn't add parentheses +items = [ + {"key1": "val1", "key2": "val2", "key3": "val3"} + if some_var == "long strings" + else {"key": "val"} +] +items = [{"key1": "val1", "key2": "val2"} if some_var == "" else {"key": "val"}] + +# Shouldn't crash with comments +items = [ # comment + {"key1": "val1", "key2": "val2", "key3": "val3"} + if some_var == "long strings" + else {"key": "val"} +] +items = [ + {"key1": "val1", "key2": "val2", "key3": "val3"} + if some_var == "long strings" + else {"key": "val"} +] # comment + +items = [ # comment + {"key1": "val1", "key2": "val2", "key3": "val3"} + if some_var == "long strings" + else {"key": "val"} +] +items = [ + {"key1": "val1", "key2": "val2", "key3": "val3"} + if some_var == "long strings" + else {"key": "val"} +] # comment + +items = [ + {"key1": "val1", "key2": "val2", "key3": "val3"} # comment + if some_var == "long strings" + else {"key": "val"} +] + +items = [ # comment # comment + {"key1": "val1", "key2": "val2", "key3": "val3"} + if some_var == "long strings" + else {"key": "val"} +] +items = [ + {"key1": "val1", "key2": "val2", "key3": "val3"} + if some_var == "long strings" + else {"key": "val"} +] # comment # comment +``` diff --git a/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__preview_wrap_comprehension_in.py.snap b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__preview_wrap_comprehension_in.py.snap new file mode 100644 index 0000000000..94553912b7 --- /dev/null +++ b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__preview_wrap_comprehension_in.py.snap @@ -0,0 +1,335 @@ +--- +source: crates/ruff_python_formatter/tests/fixtures.rs +input_file: crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_wrap_comprehension_in.py +--- +## Input + +```python +[a for graph_path_expression in refined_constraint.condition_as_predicate.variables] +[ + a + for graph_path_expression in refined_constraint.condition_as_predicate.variables +] +[ + a + for graph_path_expression + in refined_constraint.condition_as_predicate.variables +] +[ + a + for graph_path_expression in ( + refined_constraint.condition_as_predicate.variables + ) +] + +[ + (foobar_very_long_key, foobar_very_long_value) + for foobar_very_long_key, foobar_very_long_value in foobar_very_long_dictionary.items() +] + +# Don't split the `in` if it's not too long +lcomp3 = [ + element.split("\n", 1)[0] + for element in collection.select_elements() + # right + if element is not None +] + +# Don't remove parens around ternaries +expected = [i for i in (a if b else c)] + +# Nested arrays +# First in will not be split because it would still be too long +[[ + x + for x in bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb + for y in xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +]] + +# Multiple comprehensions, only split the second `in` +graph_path_expressions_in_local_constraint_refinements = [ + graph_path_expression + for refined_constraint in self._local_constraint_refinements.values() + if refined_constraint is not None + for graph_path_expression in refined_constraint.condition_as_predicate.variables +] + +# Dictionary comprehensions +dict_with_really_long_names = { + really_really_long_key_name: an_even_longer_really_really_long_key_value + for really_really_long_key_name, an_even_longer_really_really_long_key_value in really_really_really_long_dict_name.items() +} +{ + key_with_super_really_long_name: key_with_super_really_long_name + for key_with_super_really_long_name in dictionary_with_super_really_long_name +} +{ + key_with_super_really_long_name: key_with_super_really_long_name + for key_with_super_really_long_name + in dictionary_with_super_really_long_name +} +{ + key_with_super_really_long_name: key_with_super_really_long_name + for key in ( + dictionary + ) +} +``` + +## Black Differences + +```diff +--- Black ++++ Ruff +@@ -1,20 +1,14 @@ + [ + a +- for graph_path_expression in ( +- refined_constraint.condition_as_predicate.variables +- ) ++ for graph_path_expression in refined_constraint.condition_as_predicate.variables + ] + [ + a +- for graph_path_expression in ( +- refined_constraint.condition_as_predicate.variables +- ) ++ for graph_path_expression in refined_constraint.condition_as_predicate.variables + ] + [ + a +- for graph_path_expression in ( +- refined_constraint.condition_as_predicate.variables +- ) ++ for graph_path_expression in refined_constraint.condition_as_predicate.variables + ] + [ + a +@@ -25,9 +19,7 @@ + + [ + (foobar_very_long_key, foobar_very_long_value) +- for foobar_very_long_key, foobar_very_long_value in ( +- foobar_very_long_dictionary.items() +- ) ++ for foobar_very_long_key, foobar_very_long_value in foobar_very_long_dictionary.items() + ] + + # Don't split the `in` if it's not too long +@@ -47,9 +39,7 @@ + [ + x + for x in bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb +- for y in ( +- xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +- ) ++ for y in xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx + ] + ] + +@@ -58,31 +48,23 @@ + graph_path_expression + for refined_constraint in self._local_constraint_refinements.values() + if refined_constraint is not None +- for graph_path_expression in ( +- refined_constraint.condition_as_predicate.variables +- ) ++ for graph_path_expression in refined_constraint.condition_as_predicate.variables + ] + + # Dictionary comprehensions + dict_with_really_long_names = { + really_really_long_key_name: an_even_longer_really_really_long_key_value +- for really_really_long_key_name, an_even_longer_really_really_long_key_value in ( +- really_really_really_long_dict_name.items() +- ) ++ for really_really_long_key_name, an_even_longer_really_really_long_key_value in really_really_really_long_dict_name.items() + } + { + key_with_super_really_long_name: key_with_super_really_long_name +- for key_with_super_really_long_name in ( +- dictionary_with_super_really_long_name +- ) ++ for key_with_super_really_long_name in dictionary_with_super_really_long_name + } + { + key_with_super_really_long_name: key_with_super_really_long_name +- for key_with_super_really_long_name in ( +- dictionary_with_super_really_long_name +- ) ++ for key_with_super_really_long_name in dictionary_with_super_really_long_name + } + { + key_with_super_really_long_name: key_with_super_really_long_name +- for key in dictionary ++ for key in (dictionary) + } +``` + +## Ruff Output + +```python +[ + a + for graph_path_expression in refined_constraint.condition_as_predicate.variables +] +[ + a + for graph_path_expression in refined_constraint.condition_as_predicate.variables +] +[ + a + for graph_path_expression in refined_constraint.condition_as_predicate.variables +] +[ + a + for graph_path_expression in ( + refined_constraint.condition_as_predicate.variables + ) +] + +[ + (foobar_very_long_key, foobar_very_long_value) + for foobar_very_long_key, foobar_very_long_value in foobar_very_long_dictionary.items() +] + +# Don't split the `in` if it's not too long +lcomp3 = [ + element.split("\n", 1)[0] + for element in collection.select_elements() + # right + if element is not None +] + +# Don't remove parens around ternaries +expected = [i for i in (a if b else c)] + +# Nested arrays +# First in will not be split because it would still be too long +[ + [ + x + for x in bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb + for y in xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx + ] +] + +# Multiple comprehensions, only split the second `in` +graph_path_expressions_in_local_constraint_refinements = [ + graph_path_expression + for refined_constraint in self._local_constraint_refinements.values() + if refined_constraint is not None + for graph_path_expression in refined_constraint.condition_as_predicate.variables +] + +# Dictionary comprehensions +dict_with_really_long_names = { + really_really_long_key_name: an_even_longer_really_really_long_key_value + for really_really_long_key_name, an_even_longer_really_really_long_key_value in really_really_really_long_dict_name.items() +} +{ + key_with_super_really_long_name: key_with_super_really_long_name + for key_with_super_really_long_name in dictionary_with_super_really_long_name +} +{ + key_with_super_really_long_name: key_with_super_really_long_name + for key_with_super_really_long_name in dictionary_with_super_really_long_name +} +{ + key_with_super_really_long_name: key_with_super_really_long_name + for key in (dictionary) +} +``` + +## Black Output + +```python +[ + a + for graph_path_expression in ( + refined_constraint.condition_as_predicate.variables + ) +] +[ + a + for graph_path_expression in ( + refined_constraint.condition_as_predicate.variables + ) +] +[ + a + for graph_path_expression in ( + refined_constraint.condition_as_predicate.variables + ) +] +[ + a + for graph_path_expression in ( + refined_constraint.condition_as_predicate.variables + ) +] + +[ + (foobar_very_long_key, foobar_very_long_value) + for foobar_very_long_key, foobar_very_long_value in ( + foobar_very_long_dictionary.items() + ) +] + +# Don't split the `in` if it's not too long +lcomp3 = [ + element.split("\n", 1)[0] + for element in collection.select_elements() + # right + if element is not None +] + +# Don't remove parens around ternaries +expected = [i for i in (a if b else c)] + +# Nested arrays +# First in will not be split because it would still be too long +[ + [ + x + for x in bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb + for y in ( + xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx + ) + ] +] + +# Multiple comprehensions, only split the second `in` +graph_path_expressions_in_local_constraint_refinements = [ + graph_path_expression + for refined_constraint in self._local_constraint_refinements.values() + if refined_constraint is not None + for graph_path_expression in ( + refined_constraint.condition_as_predicate.variables + ) +] + +# Dictionary comprehensions +dict_with_really_long_names = { + really_really_long_key_name: an_even_longer_really_really_long_key_value + for really_really_long_key_name, an_even_longer_really_really_long_key_value in ( + really_really_really_long_dict_name.items() + ) +} +{ + key_with_super_really_long_name: key_with_super_really_long_name + for key_with_super_really_long_name in ( + dictionary_with_super_really_long_name + ) +} +{ + key_with_super_really_long_name: key_with_super_really_long_name + for key_with_super_really_long_name in ( + dictionary_with_super_really_long_name + ) +} +{ + key_with_super_really_long_name: key_with_super_really_long_name + for key in dictionary +} +``` diff --git a/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__remove_except_types_parens.py.snap b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__remove_except_types_parens.py.snap new file mode 100644 index 0000000000..1562391b41 --- /dev/null +++ b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__remove_except_types_parens.py.snap @@ -0,0 +1,427 @@ +--- +source: crates/ruff_python_formatter/tests/fixtures.rs +input_file: crates/ruff_python_formatter/resources/test/fixtures/black/cases/remove_except_types_parens.py +--- +## Input + +```python +# SEE PEP 758 FOR MORE DETAILS +# remains unchanged +try: + pass +except: + pass + +# remains unchanged +try: + pass +except ValueError: + pass + +try: + pass +except* ValueError: + pass + +# parenthesis are removed +try: + pass +except (ValueError): + pass + +try: + pass +except* (ValueError): + pass + +# parenthesis are removed +try: + pass +except (ValueError) as e: + pass + +try: + pass +except* (ValueError) as e: + pass + +# remains unchanged +try: + pass +except (ValueError,): + pass + +try: + pass +except* (ValueError,): + pass + +# remains unchanged +try: + pass +except (ValueError,) as e: + pass + +try: + pass +except* (ValueError,) as e: + pass + +# remains unchanged +try: + pass +except ValueError, TypeError, KeyboardInterrupt: + pass + +try: + pass +except* ValueError, TypeError, KeyboardInterrupt: + pass + +# parenthesis are removed +try: + pass +except (ValueError, TypeError, KeyboardInterrupt): + pass + +try: + pass +except* (ValueError, TypeError, KeyboardInterrupt): + pass + +# parenthesis are not removed +try: + pass +except (ValueError, TypeError, KeyboardInterrupt) as e: + pass + +try: + pass +except* (ValueError, TypeError, KeyboardInterrupt) as e: + pass + +# parenthesis are removed +try: + pass +except (ValueError if True else TypeError): + pass + +try: + pass +except* (ValueError if True else TypeError): + pass + +# inner except: parenthesis are removed +# outer except: parenthsis are not removed +try: + try: + pass + except (TypeError, KeyboardInterrupt): + pass +except (ValueError,): + pass + +try: + try: + pass + except* (TypeError, KeyboardInterrupt): + pass +except* (ValueError,): + pass +``` + +## Black Differences + +```diff +--- Black ++++ Ruff +@@ -74,12 +74,12 @@ + # parenthesis are removed + try: + pass +-except ValueError, TypeError, KeyboardInterrupt: ++except (ValueError, TypeError, KeyboardInterrupt): + pass + + try: + pass +-except* ValueError, TypeError, KeyboardInterrupt: ++except* (ValueError, TypeError, KeyboardInterrupt): + pass + + # parenthesis are not removed +@@ -109,7 +109,7 @@ + try: + try: + pass +- except TypeError, KeyboardInterrupt: ++ except (TypeError, KeyboardInterrupt): + pass + except (ValueError,): + pass +@@ -117,7 +117,7 @@ + try: + try: + pass +- except* TypeError, KeyboardInterrupt: ++ except* (TypeError, KeyboardInterrupt): + pass + except* (ValueError,): + pass +``` + +## Ruff Output + +```python +# SEE PEP 758 FOR MORE DETAILS +# remains unchanged +try: + pass +except: + pass + +# remains unchanged +try: + pass +except ValueError: + pass + +try: + pass +except* ValueError: + pass + +# parenthesis are removed +try: + pass +except ValueError: + pass + +try: + pass +except* ValueError: + pass + +# parenthesis are removed +try: + pass +except ValueError as e: + pass + +try: + pass +except* ValueError as e: + pass + +# remains unchanged +try: + pass +except (ValueError,): + pass + +try: + pass +except* (ValueError,): + pass + +# remains unchanged +try: + pass +except (ValueError,) as e: + pass + +try: + pass +except* (ValueError,) as e: + pass + +# remains unchanged +try: + pass +except ValueError, TypeError, KeyboardInterrupt: + pass + +try: + pass +except* ValueError, TypeError, KeyboardInterrupt: + pass + +# parenthesis are removed +try: + pass +except (ValueError, TypeError, KeyboardInterrupt): + pass + +try: + pass +except* (ValueError, TypeError, KeyboardInterrupt): + pass + +# parenthesis are not removed +try: + pass +except (ValueError, TypeError, KeyboardInterrupt) as e: + pass + +try: + pass +except* (ValueError, TypeError, KeyboardInterrupt) as e: + pass + +# parenthesis are removed +try: + pass +except ValueError if True else TypeError: + pass + +try: + pass +except* ValueError if True else TypeError: + pass + +# inner except: parenthesis are removed +# outer except: parenthsis are not removed +try: + try: + pass + except (TypeError, KeyboardInterrupt): + pass +except (ValueError,): + pass + +try: + try: + pass + except* (TypeError, KeyboardInterrupt): + pass +except* (ValueError,): + pass +``` + +## Black Output + +```python +# SEE PEP 758 FOR MORE DETAILS +# remains unchanged +try: + pass +except: + pass + +# remains unchanged +try: + pass +except ValueError: + pass + +try: + pass +except* ValueError: + pass + +# parenthesis are removed +try: + pass +except ValueError: + pass + +try: + pass +except* ValueError: + pass + +# parenthesis are removed +try: + pass +except ValueError as e: + pass + +try: + pass +except* ValueError as e: + pass + +# remains unchanged +try: + pass +except (ValueError,): + pass + +try: + pass +except* (ValueError,): + pass + +# remains unchanged +try: + pass +except (ValueError,) as e: + pass + +try: + pass +except* (ValueError,) as e: + pass + +# remains unchanged +try: + pass +except ValueError, TypeError, KeyboardInterrupt: + pass + +try: + pass +except* ValueError, TypeError, KeyboardInterrupt: + pass + +# parenthesis are removed +try: + pass +except ValueError, TypeError, KeyboardInterrupt: + pass + +try: + pass +except* ValueError, TypeError, KeyboardInterrupt: + pass + +# parenthesis are not removed +try: + pass +except (ValueError, TypeError, KeyboardInterrupt) as e: + pass + +try: + pass +except* (ValueError, TypeError, KeyboardInterrupt) as e: + pass + +# parenthesis are removed +try: + pass +except ValueError if True else TypeError: + pass + +try: + pass +except* ValueError if True else TypeError: + pass + +# inner except: parenthesis are removed +# outer except: parenthsis are not removed +try: + try: + pass + except TypeError, KeyboardInterrupt: + pass +except (ValueError,): + pass + +try: + try: + pass + except* TypeError, KeyboardInterrupt: + pass +except* (ValueError,): + pass +``` diff --git a/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__remove_lone_list_item_parens.py.snap b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__remove_lone_list_item_parens.py.snap new file mode 100644 index 0000000000..4b3a527f74 --- /dev/null +++ b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__remove_lone_list_item_parens.py.snap @@ -0,0 +1,283 @@ +--- +source: crates/ruff_python_formatter/tests/fixtures.rs +input_file: crates/ruff_python_formatter/resources/test/fixtures/black/cases/remove_lone_list_item_parens.py +--- +## Input + +```python +items = [(123)] +items = [(True)] +items = [(((((True)))))] +items = [(((((True,)))))] +items = [((((()))))] +items = [(x for x in [1])] +items = {(123)} +items = {(True)} +items = {(((((True)))))} + +# Requires `hug_parens_with_braces_and_square_brackets` unstable style to remove parentheses +# around multiline values +items = [ + ( + {"key1": "val1", "key2": "val2", "key3": "val3"} + if some_var == "long strings" + else {"key": "val"} + ) +] +items = [ + ( + {"key1": "val1", "key2": "val2"} + if some_var == "" + else {"key": "val"} + ) +] + +# Comments should not cause crashes +items = [ + ( # comment + {"key1": "val1", "key2": "val2", "key3": "val3"} + if some_var == "long strings" + else {"key": "val"} + ) +] +items = [ + ( + {"key1": "val1", "key2": "val2", "key3": "val3"} + if some_var == "long strings" + else {"key": "val"} + ) # comment +] + +items = [ # comment + ( + {"key1": "val1", "key2": "val2", "key3": "val3"} + if some_var == "long strings" + else {"key": "val"} + ) +] +items = [ + ( + {"key1": "val1", "key2": "val2", "key3": "val3"} + if some_var == "long strings" + else {"key": "val"} + ) +] # comment + +items = [ + ( + {"key1": "val1", "key2": "val2", "key3": "val3"} # comment + if some_var == "long strings" + else {"key": "val"} + ) +] + +items = [ # comment + ( # comment + {"key1": "val1", "key2": "val2", "key3": "val3"} + if some_var == "long strings" + else {"key": "val"} + ) +] +items = [ + ( + {"key1": "val1", "key2": "val2", "key3": "val3"} + if some_var == "long strings" + else {"key": "val"} + ) # comment +] # comment +``` + +## Black Differences + +```diff +--- Black ++++ Ruff +@@ -1,12 +1,12 @@ +-items = [123] +-items = [True] +-items = [True] +-items = [(True,)] +-items = [()] ++items = [(123)] ++items = [(True)] ++items = [(True)] ++items = [((True,))] ++items = [(())] + items = [(x for x in [1])] +-items = {123} +-items = {True} +-items = {True} ++items = {(123)} ++items = {(True)} ++items = {(True)} + + # Requires `hug_parens_with_braces_and_square_brackets` unstable style to remove parentheses + # around multiline values +@@ -17,7 +17,7 @@ + else {"key": "val"} + ) + ] +-items = [{"key1": "val1", "key2": "val2"} if some_var == "" else {"key": "val"}] ++items = [({"key1": "val1", "key2": "val2"} if some_var == "" else {"key": "val"})] + + # Comments should not cause crashes + items = [ +``` + +## Ruff Output + +```python +items = [(123)] +items = [(True)] +items = [(True)] +items = [((True,))] +items = [(())] +items = [(x for x in [1])] +items = {(123)} +items = {(True)} +items = {(True)} + +# Requires `hug_parens_with_braces_and_square_brackets` unstable style to remove parentheses +# around multiline values +items = [ + ( + {"key1": "val1", "key2": "val2", "key3": "val3"} + if some_var == "long strings" + else {"key": "val"} + ) +] +items = [({"key1": "val1", "key2": "val2"} if some_var == "" else {"key": "val"})] + +# Comments should not cause crashes +items = [ + ( # comment + {"key1": "val1", "key2": "val2", "key3": "val3"} + if some_var == "long strings" + else {"key": "val"} + ) +] +items = [ + ( + {"key1": "val1", "key2": "val2", "key3": "val3"} + if some_var == "long strings" + else {"key": "val"} + ) # comment +] + +items = [ # comment + ( + {"key1": "val1", "key2": "val2", "key3": "val3"} + if some_var == "long strings" + else {"key": "val"} + ) +] +items = [ + ( + {"key1": "val1", "key2": "val2", "key3": "val3"} + if some_var == "long strings" + else {"key": "val"} + ) +] # comment + +items = [ + ( + {"key1": "val1", "key2": "val2", "key3": "val3"} # comment + if some_var == "long strings" + else {"key": "val"} + ) +] + +items = [ # comment + ( # comment + {"key1": "val1", "key2": "val2", "key3": "val3"} + if some_var == "long strings" + else {"key": "val"} + ) +] +items = [ + ( + {"key1": "val1", "key2": "val2", "key3": "val3"} + if some_var == "long strings" + else {"key": "val"} + ) # comment +] # comment +``` + +## Black Output + +```python +items = [123] +items = [True] +items = [True] +items = [(True,)] +items = [()] +items = [(x for x in [1])] +items = {123} +items = {True} +items = {True} + +# Requires `hug_parens_with_braces_and_square_brackets` unstable style to remove parentheses +# around multiline values +items = [ + ( + {"key1": "val1", "key2": "val2", "key3": "val3"} + if some_var == "long strings" + else {"key": "val"} + ) +] +items = [{"key1": "val1", "key2": "val2"} if some_var == "" else {"key": "val"}] + +# Comments should not cause crashes +items = [ + ( # comment + {"key1": "val1", "key2": "val2", "key3": "val3"} + if some_var == "long strings" + else {"key": "val"} + ) +] +items = [ + ( + {"key1": "val1", "key2": "val2", "key3": "val3"} + if some_var == "long strings" + else {"key": "val"} + ) # comment +] + +items = [ # comment + ( + {"key1": "val1", "key2": "val2", "key3": "val3"} + if some_var == "long strings" + else {"key": "val"} + ) +] +items = [ + ( + {"key1": "val1", "key2": "val2", "key3": "val3"} + if some_var == "long strings" + else {"key": "val"} + ) +] # comment + +items = [ + ( + {"key1": "val1", "key2": "val2", "key3": "val3"} # comment + if some_var == "long strings" + else {"key": "val"} + ) +] + +items = [ # comment + ( # comment + {"key1": "val1", "key2": "val2", "key3": "val3"} + if some_var == "long strings" + else {"key": "val"} + ) +] +items = [ + ( + {"key1": "val1", "key2": "val2", "key3": "val3"} + if some_var == "long strings" + else {"key": "val"} + ) # comment +] # comment +``` diff --git a/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__type_param_defaults.py.snap b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__type_param_defaults.py.snap index 3910a7cf56..f4a4a2163a 100644 --- a/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__type_param_defaults.py.snap +++ b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__type_param_defaults.py.snap @@ -1,7 +1,6 @@ --- source: crates/ruff_python_formatter/tests/fixtures.rs input_file: crates/ruff_python_formatter/resources/test/fixtures/black/cases/type_param_defaults.py -snapshot_kind: text --- ## Input @@ -25,6 +24,8 @@ def trailing_comma1[T=int,](a: str): def trailing_comma2[T=int](a: str,): pass + +def weird_syntax[T=lambda: 42, **P=lambda: 43, *Ts=lambda: 44](): pass ``` ## Black Differences @@ -32,7 +33,7 @@ def trailing_comma2[T=int](a: str,): ```diff --- Black +++ Ruff -@@ -8,20 +8,20 @@ +@@ -8,9 +8,9 @@ type D[ *something_that_is_very_very_very_long = *something_that_is_very_very_very_long ] = float @@ -44,35 +45,18 @@ def trailing_comma2[T=int](a: str,): +) --def simple[ -- T = something_that_is_long --](short1: int, short2: str, short3: bytes) -> float: -+def simple[T = something_that_is_long]( -+ short1: int, short2: str, short3: bytes -+) -> float: + def simple[T = something_that_is_long]( +@@ -27,9 +27,7 @@ + + def trailing_comma1[ + T = int, +-]( +- a: str, +-): ++](a: str): pass --def longer[ -- something_that_is_long = something_that_is_long --](something_that_is_long: something_that_is_long) -> something_that_is_long: -+def longer[something_that_is_long = something_that_is_long]( -+ something_that_is_long: something_that_is_long, -+) -> something_that_is_long: - pass - - -@@ -31,7 +31,7 @@ - pass - - --def trailing_comma2[ -- T = int --](a: str,): -+def trailing_comma2[T = int]( -+ a: str, -+): - pass ``` ## Ruff Output @@ -115,6 +99,10 @@ def trailing_comma2[T = int]( a: str, ): pass + + +def weird_syntax[T = lambda: 42, **P = lambda: 43, *Ts = lambda: 44](): + pass ``` ## Black Output @@ -135,26 +123,32 @@ type something_that_is_long[ ] = something_that_is_long -def simple[ - T = something_that_is_long -](short1: int, short2: str, short3: bytes) -> float: +def simple[T = something_that_is_long]( + short1: int, short2: str, short3: bytes +) -> float: pass -def longer[ - something_that_is_long = something_that_is_long -](something_that_is_long: something_that_is_long) -> something_that_is_long: +def longer[something_that_is_long = something_that_is_long]( + something_that_is_long: something_that_is_long, +) -> something_that_is_long: pass def trailing_comma1[ T = int, -](a: str): +]( + a: str, +): pass -def trailing_comma2[ - T = int -](a: str,): +def trailing_comma2[T = int]( + a: str, +): + pass + + +def weird_syntax[T = lambda: 42, **P = lambda: 43, *Ts = lambda: 44](): pass ```