From d86e5ad031f984064b30f2608955b52cc5b9e5ef Mon Sep 17 00:00:00 2001 From: Micha Reiser Date: Tue, 17 Sep 2024 11:16:50 +0200 Subject: [PATCH] Update Black tests (#13375) --- .../backslash_before_indent.options.json | 1 + .../black/cases/backslash_before_indent.py | 10 + .../cases/backslash_before_indent.py.expect | 10 + .../comment_after_escaped_newline.py.expect | 4 +- .../black/cases/dummy_implementations.py | 60 ++ .../cases/dummy_implementations.py.expect | 44 ++ .../test/fixtures/black/cases/fmtonoff6.py | 13 + .../fixtures/black/cases/fmtonoff6.py.expect | 13 + .../fixtures/black/cases/form_feeds.py.expect | 1 + .../black/cases/function_trailing_comma.py | 58 ++ .../cases/function_trailing_comma.py.expect | 72 +++ .../fixtures/black/cases/line_ranges_basic.py | 2 +- .../black/cases/line_ranges_basic.py.expect | 2 +- .../black/cases/line_ranges_exceeding_end.py | 7 + .../cases/line_ranges_exceeding_end.py.expect | 27 + .../black/cases/pattern_matching_complex.py | 2 +- .../cases/pattern_matching_complex.py.expect | 2 +- ...pattern_matching_with_if_stmt.options.json | 1 + .../cases/pattern_matching_with_if_stmt.py | 32 + .../pattern_matching_with_if_stmt.py.expect | 36 ++ .../cases/pep604_union_types_line_breaks.py | 2 +- .../pep604_union_types_line_breaks.py.expect | 2 +- .../fixtures/black/cases/pep_701.options.json | 1 + .../test/fixtures/black/cases/pep_701.py | 136 +++++ .../fixtures/black/cases/pep_701.py.expect | 136 +++++ .../black/cases/preview_multiline_strings.py | 7 + .../cases/preview_multiline_strings.py.expect | 7 + ...typed_star_arg_type_var_tuple.options.json | 1 + ...ew_pep646_typed_star_arg_type_var_tuple.py | 5 + ...46_typed_star_arg_type_var_tuple.py.expect | 5 + .../black/cases/split_delimiter_comments.py | 23 + .../cases/split_delimiter_comments.py.expect | 23 + .../cases/type_param_defaults.options.json | 1 + .../black/cases/type_param_defaults.py | 19 + .../black/cases/type_param_defaults.py.expect | 37 ++ .../test/fixtures/import_black_tests.py | 3 + ...ity@cases__backslash_before_indent.py.snap | 60 ++ ...ses__comment_after_escaped_newline.py.snap | 14 +- ...ility@cases__dummy_implementations.py.snap | 399 ++++++++++++ ...ck_compatibility@cases__form_feeds.py.snap | 10 +- ...ity@cases__function_trailing_comma.py.snap | 569 ++++++++++++++++++ ...ses__pattern_matching_with_if_stmt.py.snap | 185 ++++++ ...es__pep604_union_types_line_breaks.py.snap | 6 +- ...black_compatibility@cases__pep_701.py.snap | 442 ++++++++++++++ ...y@cases__preview_multiline_strings.py.snap | 28 +- ...ibility@cases__type_param_defaults.py.snap | 159 +++++ 46 files changed, 2653 insertions(+), 24 deletions(-) create mode 100644 crates/ruff_python_formatter/resources/test/fixtures/black/cases/backslash_before_indent.options.json create mode 100644 crates/ruff_python_formatter/resources/test/fixtures/black/cases/backslash_before_indent.py create mode 100644 crates/ruff_python_formatter/resources/test/fixtures/black/cases/backslash_before_indent.py.expect create mode 100644 crates/ruff_python_formatter/resources/test/fixtures/black/cases/fmtonoff6.py create mode 100644 crates/ruff_python_formatter/resources/test/fixtures/black/cases/fmtonoff6.py.expect create mode 100644 crates/ruff_python_formatter/resources/test/fixtures/black/cases/line_ranges_exceeding_end.py create mode 100644 crates/ruff_python_formatter/resources/test/fixtures/black/cases/line_ranges_exceeding_end.py.expect create mode 100644 crates/ruff_python_formatter/resources/test/fixtures/black/cases/pattern_matching_with_if_stmt.options.json create mode 100644 crates/ruff_python_formatter/resources/test/fixtures/black/cases/pattern_matching_with_if_stmt.py create mode 100644 crates/ruff_python_formatter/resources/test/fixtures/black/cases/pattern_matching_with_if_stmt.py.expect create mode 100644 crates/ruff_python_formatter/resources/test/fixtures/black/cases/pep_701.options.json create mode 100644 crates/ruff_python_formatter/resources/test/fixtures/black/cases/pep_701.py create mode 100644 crates/ruff_python_formatter/resources/test/fixtures/black/cases/pep_701.py.expect create mode 100644 crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_pep646_typed_star_arg_type_var_tuple.options.json create mode 100644 crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_pep646_typed_star_arg_type_var_tuple.py create mode 100644 crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_pep646_typed_star_arg_type_var_tuple.py.expect create mode 100644 crates/ruff_python_formatter/resources/test/fixtures/black/cases/split_delimiter_comments.py create mode 100644 crates/ruff_python_formatter/resources/test/fixtures/black/cases/split_delimiter_comments.py.expect create mode 100644 crates/ruff_python_formatter/resources/test/fixtures/black/cases/type_param_defaults.options.json create mode 100644 crates/ruff_python_formatter/resources/test/fixtures/black/cases/type_param_defaults.py create mode 100644 crates/ruff_python_formatter/resources/test/fixtures/black/cases/type_param_defaults.py.expect create mode 100644 crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__backslash_before_indent.py.snap create mode 100644 crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__dummy_implementations.py.snap create mode 100644 crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__function_trailing_comma.py.snap create mode 100644 crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__pattern_matching_with_if_stmt.py.snap create mode 100644 crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__pep_701.py.snap create mode 100644 crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__type_param_defaults.py.snap 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 new file mode 100644 index 0000000000..60044b9854 --- /dev/null +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/backslash_before_indent.options.json @@ -0,0 +1 @@ +{"target_version": "py310"} \ No newline at end of file diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/backslash_before_indent.py b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/backslash_before_indent.py new file mode 100644 index 0000000000..888c8475bb --- /dev/null +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/backslash_before_indent.py @@ -0,0 +1,10 @@ +class Plotter: +\ + pass + +class AnotherCase: + \ + """Some + \ + Docstring + """ diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/backslash_before_indent.py.expect b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/backslash_before_indent.py.expect new file mode 100644 index 0000000000..66afe56cfb --- /dev/null +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/backslash_before_indent.py.expect @@ -0,0 +1,10 @@ +class Plotter: + + pass + + +class AnotherCase: + """Some + \ + Docstring + """ diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/comment_after_escaped_newline.py.expect b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/comment_after_escaped_newline.py.expect index 209204f0b6..7ca36db4bc 100644 --- a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/comment_after_escaped_newline.py.expect +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/comment_after_escaped_newline.py.expect @@ -2,5 +2,7 @@ def bob(): # pylint: disable=W9016 pass -def bobtwo(): # some comment here +def bobtwo(): + + # some comment here pass diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/dummy_implementations.py b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/dummy_implementations.py index 739359ee25..1551f11104 100644 --- a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/dummy_implementations.py +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/dummy_implementations.py @@ -67,3 +67,63 @@ async def async_function(self): @decorated async def async_function(self): ... + +class ClassA: + def f(self): + + ... + + +class ClassB: + def f(self): + + + + + + + + + + ... + + +class ClassC: + def f(self): + + ... + # Comment + + +class ClassD: + def f(self):# Comment 1 + + ...# Comment 2 + # Comment 3 + + +class ClassE: + def f(self): + + ... + def f2(self): + print(10) + + +class ClassF: + def f(self): + + ...# Comment 2 + + +class ClassG: + def f(self):#Comment 1 + + ...# Comment 2 + + +class ClassH: + def f(self): + #Comment + + ... diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/dummy_implementations.py.expect b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/dummy_implementations.py.expect index 369e442152..7fda2037e8 100644 --- a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/dummy_implementations.py.expect +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/dummy_implementations.py.expect @@ -70,3 +70,47 @@ async def async_function(self): ... @decorated async def async_function(self): ... + + +class ClassA: + def f(self): ... + + +class ClassB: + def f(self): ... + + +class ClassC: + def f(self): + + ... + # Comment + + +class ClassD: + def f(self): # Comment 1 + + ... # Comment 2 + # Comment 3 + + +class ClassE: + def f(self): ... + def f2(self): + print(10) + + +class ClassF: + def f(self): ... # Comment 2 + + +class ClassG: + def f(self): # Comment 1 + ... # Comment 2 + + +class ClassH: + def f(self): + # Comment + + ... diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/fmtonoff6.py b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/fmtonoff6.py new file mode 100644 index 0000000000..9d23925063 --- /dev/null +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/fmtonoff6.py @@ -0,0 +1,13 @@ +# Regression test for https://github.com/psf/black/issues/2478. +def foo(): + arr = ( + (3833567325051000, 5, 1, 2, 4229.25, 6, 0), + # fmt: off + ) + + +# Regression test for https://github.com/psf/black/issues/3458. +dependencies = { + a: b, + # fmt: off +} diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/fmtonoff6.py.expect b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/fmtonoff6.py.expect new file mode 100644 index 0000000000..9d23925063 --- /dev/null +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/fmtonoff6.py.expect @@ -0,0 +1,13 @@ +# Regression test for https://github.com/psf/black/issues/2478. +def foo(): + arr = ( + (3833567325051000, 5, 1, 2, 4229.25, 6, 0), + # fmt: off + ) + + +# Regression test for https://github.com/psf/black/issues/3458. +dependencies = { + a: b, + # fmt: off +} diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/form_feeds.py.expect b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/form_feeds.py.expect index 4ce3d2832b..ae1a3a864c 100644 --- a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/form_feeds.py.expect +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/form_feeds.py.expect @@ -33,6 +33,7 @@ # + # pass diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/function_trailing_comma.py b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/function_trailing_comma.py index 7b01a0d801..3164e9a8df 100644 --- a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/function_trailing_comma.py +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/function_trailing_comma.py @@ -59,3 +59,61 @@ some_module.some_function( some_module.some_function( argument1, (one, two,), argument4, argument5, argument6 ) + +def foo() -> ( + # comment inside parenthesised return type + int +): + ... + +def foo() -> ( + # comment inside parenthesised return type + # more + int + # another +): + ... + +def foo() -> ( + # comment inside parenthesised new union return type + int | str | bytes +): + ... + +def foo() -> ( + # comment inside plain tuple +): + pass + +def foo(arg: (# comment with non-return annotation + int + # comment with non-return annotation +)): + pass + +def foo(arg: (# comment with non-return annotation + int | range | memoryview + # comment with non-return annotation +)): + pass + +def foo(arg: (# only before + int +)): + pass + +def foo(arg: ( + int + # only after +)): + pass + +variable: ( # annotation + because + # why not +) + +variable: ( + because + # why not +) diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/function_trailing_comma.py.expect b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/function_trailing_comma.py.expect index 2411226eb4..be40f688db 100644 --- a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/function_trailing_comma.py.expect +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/function_trailing_comma.py.expect @@ -112,3 +112,75 @@ some_module.some_function( argument5, argument6, ) + + +def foo() -> ( + # comment inside parenthesised return type + int +): ... + + +def foo() -> ( + # comment inside parenthesised return type + # more + int + # another +): ... + + +def foo() -> ( + # comment inside parenthesised new union return type + int + | str + | bytes +): ... + + +def foo() -> ( + # comment inside plain tuple +): + pass + + +def foo( + arg: ( # comment with non-return annotation + int + # comment with non-return annotation + ), +): + pass + + +def foo( + arg: ( # comment with non-return annotation + int + | range + | memoryview + # comment with non-return annotation + ), +): + pass + + +def foo(arg: int): # only before + pass + + +def foo( + arg: ( + int + # only after + ), +): + pass + + +variable: ( # annotation + because + # why not +) + +variable: ( + because + # why not +) 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_exceeding_end.py b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/line_ranges_exceeding_end.py new file mode 100644 index 0000000000..e18c387abe --- /dev/null +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/line_ranges_exceeding_end.py @@ -0,0 +1,7 @@ +# flags: --line-ranges=6-1000 +# NOTE: If you need to modify this file, pay special attention to the --line-ranges= +# flag above as it's formatting specifically these lines. +def foo1(parameter_1, parameter_2, parameter_3, parameter_4, parameter_5, parameter_6, parameter_7): pass +def foo2(parameter_1, parameter_2, parameter_3, parameter_4, parameter_5, parameter_6, parameter_7): pass +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 diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/line_ranges_exceeding_end.py.expect b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/line_ranges_exceeding_end.py.expect new file mode 100644 index 0000000000..aa8774723b --- /dev/null +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/line_ranges_exceeding_end.py.expect @@ -0,0 +1,27 @@ +# flags: --line-ranges=6-1000 +# NOTE: If you need to modify this file, pay special attention to the --line-ranges= +# flag above as it's formatting specifically these lines. +def foo1(parameter_1, parameter_2, parameter_3, parameter_4, parameter_5, parameter_6, parameter_7): pass +def foo2(parameter_1, parameter_2, parameter_3, parameter_4, parameter_5, parameter_6, parameter_7): pass +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 diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/pattern_matching_complex.py b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/pattern_matching_complex.py index 60c80b9ea1..419fb7eb3f 100644 --- a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/pattern_matching_complex.py +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/pattern_matching_complex.py @@ -82,7 +82,7 @@ match (0, 1, 2): match x: case [0]: y = 0 - case [1, 0] if (x := x[:0]): + case [1, 0] if x := x[:0]: y = 1 case [1, 0]: y = 2 diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/pattern_matching_complex.py.expect b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/pattern_matching_complex.py.expect index 60c80b9ea1..419fb7eb3f 100644 --- a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/pattern_matching_complex.py.expect +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/pattern_matching_complex.py.expect @@ -82,7 +82,7 @@ match (0, 1, 2): match x: case [0]: y = 0 - case [1, 0] if (x := x[:0]): + case [1, 0] if x := x[:0]: y = 1 case [1, 0]: y = 2 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 new file mode 100644 index 0000000000..0aaaa152bc --- /dev/null +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/pattern_matching_with_if_stmt.options.json @@ -0,0 +1 @@ +{"preview": "enabled", "target_version": "py310"} \ No newline at end of file diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/pattern_matching_with_if_stmt.py b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/pattern_matching_with_if_stmt.py new file mode 100644 index 0000000000..192a19d587 --- /dev/null +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/pattern_matching_with_if_stmt.py @@ -0,0 +1,32 @@ +match match: + case "test" if case != "not very loooooooooooooog condition": # comment + pass + +match smth: + case "test" if "any long condition" != "another long condition" and "this is a long condition": + pass + case test if "any long condition" != "another long condition" and "this is a looooong condition": + pass + case test if "any long condition" != "another long condition" and "this is a looooong condition": # some additional comments + pass + case test if (True): # some comment + pass + case test if (False + ): # some comment + pass + case test if (True # some comment + ): + pass # some comment + case cases if (True # some comment + ): # some other comment + pass # some comment + case match if (True # some comment + ): + pass # some comment + +# case black_test_patma_052 (originally in the pattern_matching_complex test case) +match x: + case [1, 0] if x := x[:0]: + y = 1 + case [1, 0] if (x := x[:0]): + y = 1 diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/pattern_matching_with_if_stmt.py.expect b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/pattern_matching_with_if_stmt.py.expect new file mode 100644 index 0000000000..fb24ad0f01 --- /dev/null +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/pattern_matching_with_if_stmt.py.expect @@ -0,0 +1,36 @@ +match match: + case "test" if case != "not very loooooooooooooog condition": # comment + pass + +match smth: + case "test" if ( + "any long condition" != "another long condition" and "this is a long condition" + ): + pass + case test if ( + "any long condition" != "another long condition" + and "this is a looooong condition" + ): + pass + case test if ( + "any long condition" != "another long condition" + and "this is a looooong condition" + ): # some additional comments + pass + case test if True: # some comment + pass + case test if False: # some comment + pass + case test if True: # some comment + pass # some comment + case cases if True: # some comment # some other comment + pass # some comment + case match if True: # some comment + pass # some comment + +# case black_test_patma_052 (originally in the pattern_matching_complex test case) +match x: + case [1, 0] if x := x[:0]: + y = 1 + case [1, 0] if x := x[:0]: + y = 1 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/pep_701.options.json b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/pep_701.options.json new file mode 100644 index 0000000000..cf266d3540 --- /dev/null +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/pep_701.options.json @@ -0,0 +1 @@ +{"target_version": "py312"} \ No newline at end of file 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 new file mode 100644 index 0000000000..ba6a1b208c --- /dev/null +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/pep_701.py @@ -0,0 +1,136 @@ +x = f"foo" +x = f'foo' +x = f"""foo""" +x = f'''foo''' +x = f"foo {{ bar {{ baz" +x = f"foo {{ {2 + 2}bar {{ baz" +x = f'foo {{ {2 + 2}bar {{ baz' +x = f"""foo {{ {2 + 2}bar {{ baz""" +x = f'''foo {{ {2 + 2}bar {{ baz''' + +# edge case: FSTRING_MIDDLE containing only whitespace should not be stripped +x = f"{a} {b}" + +x = f"foo { + 2 + 2 +} bar baz" + +x = f"foo {{ {"a {2 + 2} b"}bar {{ baz" +x = f"foo {{ {f'a {2 + 2} b'}bar {{ baz" +x = f"foo {{ {f"a {2 + 2} b"}bar {{ baz" + +x = f"foo {{ {f'a {f"a {2 + 2} b"} b'}bar {{ baz" +x = f"foo {{ {f"a {f"a {2 + 2} b"} b"}bar {{ baz" + +x = """foo {{ {2 + 2}bar +baz""" + + +x = f"""foo {{ {2 + 2}bar {{ baz""" + +x = f"""foo {{ { + 2 + 2 +}bar {{ baz""" + + +x = f"""foo {{ { + 2 + 2 +}bar +baz""" + +x = f"""foo {{ a + foo {2 + 2}bar {{ baz + + x = f"foo {{ { + 2 + 2 # comment + }bar" + + {{ baz + + }} buzz + + {print("abc" + "def" +)} +abc""" + +# edge case: end triple quotes at index zero +f"""foo {2+2} bar +""" + +f' \' {f"'"} \' ' +f" \" {f'"'} \" " + +x = f"a{2+2:=^72}b" +x = f"a{2+2:x}b" + +rf'foo' +rf'{foo}' + +f"{x:{y}d}" + +x = f"a{2+2:=^{x}}b" +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 correctly {2+2:d}" + +f"{2+2=}" +f"{2+2 = }" +f"{ 2 + 2 = }" + +f"""foo { + datetime.datetime.now():%Y +%m +%d +}""" + +f"{ +X +!r +}" + +raise ValueError( + "xxxxxxxxxxxIncorrect --line-ranges format, expect START-END, found" + f" {lines_str!r}" + ) + +f"`escape` only permitted in {{'html', 'latex', 'latex-math'}}, \ +got {escape}" + +x = f'\N{GREEK CAPITAL LETTER DELTA} \N{SNOWMAN} {x}' +fr'\{{\}}' + +f""" + WITH {f''' + {1}_cte AS ()'''} +""" + +value: str = f'''foo +''' + +log( + f"Received operation {server_operation.name} from " + f"{self.writer._transport.get_extra_info('peername')}", # type: ignore[attr-defined] + level=0, +) + +f"{1:{f'{2}'}}" +f'{1:{f'{2}'}}' +f'{1:{2}d}' + +f'{{\\"kind\\":\\"ConfigMap\\",\\"metadata\\":{{\\"annotations\\":{{}},\\"name\\":\\"cluster-info\\",\\"namespace\\":\\"amazon-cloudwatch\\"}}}}' + +f"""{''' +'''}""" + +f"{'\''}" +f"{f'\''}" + +f'{1}\{{' +f'{2} foo \{{[\}}' +f'\{3}' +rf"\{"a"}" 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 new file mode 100644 index 0000000000..74a6ecd5e7 --- /dev/null +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/pep_701.py.expect @@ -0,0 +1,136 @@ +x = f"foo" +x = f"foo" +x = f"""foo""" +x = f"""foo""" +x = f"foo {{ bar {{ baz" +x = f"foo {{ {2 + 2}bar {{ baz" +x = f"foo {{ {2 + 2}bar {{ baz" +x = f"""foo {{ {2 + 2}bar {{ baz""" +x = f"""foo {{ {2 + 2}bar {{ baz""" + +# edge case: FSTRING_MIDDLE containing only whitespace should not be stripped +x = f"{a} {b}" + +x = f"foo { + 2 + 2 +} bar baz" + +x = f"foo {{ {"a {2 + 2} b"}bar {{ baz" +x = f"foo {{ {f'a {2 + 2} b'}bar {{ baz" +x = f"foo {{ {f"a {2 + 2} b"}bar {{ baz" + +x = f"foo {{ {f'a {f"a {2 + 2} b"} b'}bar {{ baz" +x = f"foo {{ {f"a {f"a {2 + 2} b"} b"}bar {{ baz" + +x = """foo {{ {2 + 2}bar +baz""" + + +x = f"""foo {{ {2 + 2}bar {{ baz""" + +x = f"""foo {{ { + 2 + 2 +}bar {{ baz""" + + +x = f"""foo {{ { + 2 + 2 +}bar +baz""" + +x = f"""foo {{ a + foo {2 + 2}bar {{ baz + + x = f"foo {{ { + 2 + 2 # comment + }bar" + + {{ baz + + }} buzz + + {print("abc" + "def" +)} +abc""" + +# edge case: end triple quotes at index zero +f"""foo {2+2} bar +""" + +f' \' {f"'"} \' ' +f" \" {f'"'} \" " + +x = f"a{2+2:=^72}b" +x = f"a{2+2:x}b" + +rf"foo" +rf"{foo}" + +f"{x:{y}d}" + +x = f"a{2+2:=^{x}}b" +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 correctly {2+2:d}" + +f"{2+2=}" +f"{2+2 = }" +f"{ 2 + 2 = }" + +f"""foo { + datetime.datetime.now():%Y +%m +%d +}""" + +f"{ +X +!r +}" + +raise ValueError( + "xxxxxxxxxxxIncorrect --line-ranges format, expect START-END, found" + f" {lines_str!r}" +) + +f"`escape` only permitted in {{'html', 'latex', 'latex-math'}}, \ +got {escape}" + +x = f"\N{GREEK CAPITAL LETTER DELTA} \N{SNOWMAN} {x}" +rf"\{{\}}" + +f""" + WITH {f''' + {1}_cte AS ()'''} +""" + +value: str = f"""foo +""" + +log( + f"Received operation {server_operation.name} from " + f"{self.writer._transport.get_extra_info('peername')}", # type: ignore[attr-defined] + level=0, +) + +f"{1:{f'{2}'}}" +f"{1:{f'{2}'}}" +f"{1:{2}d}" + +f'{{\\"kind\\":\\"ConfigMap\\",\\"metadata\\":{{\\"annotations\\":{{}},\\"name\\":\\"cluster-info\\",\\"namespace\\":\\"amazon-cloudwatch\\"}}}}' + +f"""{''' +'''}""" + +f"{'\''}" +f"{f'\''}" + +f"{1}\{{" +f"{2} foo \{{[\}}" +f"\{3}" +rf"\{"a"}" 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 717f7b52e8..82a8657d6e 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 @@ -173,3 +173,10 @@ this_will_also_become_one_line = ( # comment "b" "c" ) + +assert some_var == expected_result, """ +test +""" +assert some_var == expected_result, f""" +expected: {expected_result} +actual: {some_var}""" 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 3983178da9..942ee085ea 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 @@ -207,3 +207,10 @@ this_will_stay_on_three_lines = ( ) this_will_also_become_one_line = "abc" # comment + +assert some_var == expected_result, """ +test +""" +assert some_var == expected_result, f""" +expected: {expected_result} +actual: {some_var}""" diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_pep646_typed_star_arg_type_var_tuple.options.json b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_pep646_typed_star_arg_type_var_tuple.options.json new file mode 100644 index 0000000000..f1b92cd916 --- /dev/null +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_pep646_typed_star_arg_type_var_tuple.options.json @@ -0,0 +1 @@ +{"preview": "enabled", "target_version": "py311"} \ No newline at end of file diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_pep646_typed_star_arg_type_var_tuple.py b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_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/preview_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/preview_pep646_typed_star_arg_type_var_tuple.py.expect b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_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/preview_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/split_delimiter_comments.py b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/split_delimiter_comments.py new file mode 100644 index 0000000000..e20760556e --- /dev/null +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/split_delimiter_comments.py @@ -0,0 +1,23 @@ +a = ( + 1 + # type: ignore + 2 # type: ignore +) +a = ( + 1 # type: ignore + + 2 # type: ignore +) +bad_split3 = ( + "What if we have inline comments on " # First Comment + "each line of a bad split? In that " # Second Comment + "case, we should just leave it alone." # Third Comment +) +parametrize( + ( + {}, + {}, + ), + ( # foobar + {}, + {}, + ), +) diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/split_delimiter_comments.py.expect b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/split_delimiter_comments.py.expect new file mode 100644 index 0000000000..b2607eac9e --- /dev/null +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/split_delimiter_comments.py.expect @@ -0,0 +1,23 @@ +a = ( + 1 # type: ignore + + 2 # type: ignore +) +a = ( + 1 # type: ignore + + 2 # type: ignore +) +bad_split3 = ( + "What if we have inline comments on " # First Comment + "each line of a bad split? In that " # Second Comment + "case, we should just leave it alone." # Third Comment +) +parametrize( + ( + {}, + {}, + ), + ( # foobar + {}, + {}, + ), +) diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/type_param_defaults.options.json b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/type_param_defaults.options.json new file mode 100644 index 0000000000..b9b2345df9 --- /dev/null +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/type_param_defaults.options.json @@ -0,0 +1 @@ +{"target_version": "py313"} \ No newline at end of file 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 new file mode 100644 index 0000000000..de25e7c9a9 --- /dev/null +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/type_param_defaults.py @@ -0,0 +1,19 @@ +type A[T=int] = float +type B[*P=int] = float +type C[*Ts=int] = float +type D[*Ts=*int] = float +type D[something_that_is_very_very_very_long=something_that_is_very_very_very_long] = float +type D[*something_that_is_very_very_very_long=*something_that_is_very_very_very_long] = float +type something_that_is_long[something_that_is_long=something_that_is_long] = something_that_is_long + +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: + pass + +def trailing_comma1[T=int,](a: str): + pass + +def trailing_comma2[T=int](a: str,): + 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 new file mode 100644 index 0000000000..af09a67e5c --- /dev/null +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/type_param_defaults.py.expect @@ -0,0 +1,37 @@ +type A[T = int] = float +type B[*P = int] = float +type C[*Ts = int] = float +type D[*Ts = *int] = float +type D[ + something_that_is_very_very_very_long = something_that_is_very_very_very_long +] = float +type D[ + *something_that_is_very_very_very_long = *something_that_is_very_very_very_long +] = float +type something_that_is_long[ + something_that_is_long = something_that_is_long +] = something_that_is_long + + +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: + pass + + +def trailing_comma1[ + T = int, +](a: str): + pass + + +def trailing_comma2[ + T = int +](a: str,): + pass 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 3183ef05cf..6031b2dbbc 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 @@ -104,6 +104,9 @@ IGNORE_LIST = [ # Uses a different output format "decorators.py", + + # Tests line ranges that fall outside the source range. This is a CLI test case and not a formatting test case. + "line_ranges_outside_source.py", ] diff --git a/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__backslash_before_indent.py.snap b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__backslash_before_indent.py.snap new file mode 100644 index 0000000000..f00bd36029 --- /dev/null +++ b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__backslash_before_indent.py.snap @@ -0,0 +1,60 @@ +--- +source: crates/ruff_python_formatter/tests/fixtures.rs +input_file: crates/ruff_python_formatter/resources/test/fixtures/black/cases/backslash_before_indent.py +--- +## Input + +```python +class Plotter: +\ + pass + +class AnotherCase: + \ + """Some + \ + Docstring + """ +``` + +## Black Differences + +```diff +--- Black ++++ Ruff +@@ -1,5 +1,4 @@ + class Plotter: +- + pass + + +``` + +## Ruff Output + +```python +class Plotter: + pass + + +class AnotherCase: + """Some + \ + Docstring + """ +``` + +## Black Output + +```python +class Plotter: + + pass + + +class AnotherCase: + """Some + \ + Docstring + """ +``` diff --git a/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__comment_after_escaped_newline.py.snap b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__comment_after_escaped_newline.py.snap index d936affef1..9d7369e3f1 100644 --- a/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__comment_after_escaped_newline.py.snap +++ b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__comment_after_escaped_newline.py.snap @@ -21,16 +21,16 @@ def bobtwo(): \ ```diff --- Black +++ Ruff -@@ -1,6 +1,8 @@ +@@ -1,8 +1,8 @@ -def bob(): # pylint: disable=W9016 +def bob(): + # pylint: disable=W9016 pass --def bobtwo(): # some comment here -+def bobtwo(): -+ # some comment here + def bobtwo(): +- + # some comment here pass ``` @@ -54,8 +54,8 @@ def bob(): # pylint: disable=W9016 pass -def bobtwo(): # some comment here +def bobtwo(): + + # some comment here pass ``` - - diff --git a/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__dummy_implementations.py.snap b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__dummy_implementations.py.snap new file mode 100644 index 0000000000..c5887596ae --- /dev/null +++ b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__dummy_implementations.py.snap @@ -0,0 +1,399 @@ +--- +source: crates/ruff_python_formatter/tests/fixtures.rs +input_file: crates/ruff_python_formatter/resources/test/fixtures/black/cases/dummy_implementations.py +--- +## Input + +```python +from typing import NoReturn, Protocol, Union, overload + +class Empty: + ... + +def dummy(a): ... +async def other(b): ... + + +@overload +def a(arg: int) -> int: ... +@overload +def a(arg: str) -> str: ... +@overload +def a(arg: object) -> NoReturn: ... +def a(arg: Union[int, str, object]) -> Union[int, str]: + if not isinstance(arg, (int, str)): + raise TypeError + return arg + +class Proto(Protocol): + def foo(self, a: int) -> int: + ... + + def bar(self, b: str) -> str: ... + def baz(self, c: bytes) -> str: + ... + + +def dummy_two(): + ... +@dummy +def dummy_three(): + ... + +def dummy_four(): + ... + +@overload +def b(arg: int) -> int: ... + +@overload +def b(arg: str) -> str: ... +@overload +def b(arg: object) -> NoReturn: ... + +def b(arg: Union[int, str, object]) -> Union[int, str]: + if not isinstance(arg, (int, str)): + raise TypeError + return arg + +def has_comment(): + ... # still a dummy + +if some_condition: + ... + +if already_dummy: ... + +class AsyncCls: + async def async_method(self): + ... + +async def async_function(self): + ... + +@decorated +async def async_function(self): + ... + +class ClassA: + def f(self): + + ... + + +class ClassB: + def f(self): + + + + + + + + + + ... + + +class ClassC: + def f(self): + + ... + # Comment + + +class ClassD: + def f(self):# Comment 1 + + ...# Comment 2 + # Comment 3 + + +class ClassE: + def f(self): + + ... + def f2(self): + print(10) + + +class ClassF: + def f(self): + + ...# Comment 2 + + +class ClassG: + def f(self):#Comment 1 + + ...# Comment 2 + + +class ClassH: + def f(self): + #Comment + + ... +``` + +## Black Differences + +```diff +--- Black ++++ Ruff +@@ -82,14 +82,12 @@ + + class ClassC: + def f(self): +- + ... + # Comment + + + class ClassD: + def f(self): # Comment 1 +- + ... # Comment 2 + # Comment 3 + +``` + +## Ruff Output + +```python +from typing import NoReturn, Protocol, Union, overload + + +class Empty: ... + + +def dummy(a): ... +async def other(b): ... + + +@overload +def a(arg: int) -> int: ... +@overload +def a(arg: str) -> str: ... +@overload +def a(arg: object) -> NoReturn: ... +def a(arg: Union[int, str, object]) -> Union[int, str]: + if not isinstance(arg, (int, str)): + raise TypeError + return arg + + +class Proto(Protocol): + def foo(self, a: int) -> int: ... + + def bar(self, b: str) -> str: ... + def baz(self, c: bytes) -> str: ... + + +def dummy_two(): ... +@dummy +def dummy_three(): ... + + +def dummy_four(): ... + + +@overload +def b(arg: int) -> int: ... + + +@overload +def b(arg: str) -> str: ... +@overload +def b(arg: object) -> NoReturn: ... + + +def b(arg: Union[int, str, object]) -> Union[int, str]: + if not isinstance(arg, (int, str)): + raise TypeError + return arg + + +def has_comment(): ... # still a dummy + + +if some_condition: + ... + +if already_dummy: + ... + + +class AsyncCls: + async def async_method(self): ... + + +async def async_function(self): ... + + +@decorated +async def async_function(self): ... + + +class ClassA: + def f(self): ... + + +class ClassB: + def f(self): ... + + +class ClassC: + def f(self): + ... + # Comment + + +class ClassD: + def f(self): # Comment 1 + ... # Comment 2 + # Comment 3 + + +class ClassE: + def f(self): ... + def f2(self): + print(10) + + +class ClassF: + def f(self): ... # Comment 2 + + +class ClassG: + def f(self): # Comment 1 + ... # Comment 2 + + +class ClassH: + def f(self): + # Comment + + ... +``` + +## Black Output + +```python +from typing import NoReturn, Protocol, Union, overload + + +class Empty: ... + + +def dummy(a): ... +async def other(b): ... + + +@overload +def a(arg: int) -> int: ... +@overload +def a(arg: str) -> str: ... +@overload +def a(arg: object) -> NoReturn: ... +def a(arg: Union[int, str, object]) -> Union[int, str]: + if not isinstance(arg, (int, str)): + raise TypeError + return arg + + +class Proto(Protocol): + def foo(self, a: int) -> int: ... + + def bar(self, b: str) -> str: ... + def baz(self, c: bytes) -> str: ... + + +def dummy_two(): ... +@dummy +def dummy_three(): ... + + +def dummy_four(): ... + + +@overload +def b(arg: int) -> int: ... + + +@overload +def b(arg: str) -> str: ... +@overload +def b(arg: object) -> NoReturn: ... + + +def b(arg: Union[int, str, object]) -> Union[int, str]: + if not isinstance(arg, (int, str)): + raise TypeError + return arg + + +def has_comment(): ... # still a dummy + + +if some_condition: + ... + +if already_dummy: + ... + + +class AsyncCls: + async def async_method(self): ... + + +async def async_function(self): ... + + +@decorated +async def async_function(self): ... + + +class ClassA: + def f(self): ... + + +class ClassB: + def f(self): ... + + +class ClassC: + def f(self): + + ... + # Comment + + +class ClassD: + def f(self): # Comment 1 + + ... # Comment 2 + # Comment 3 + + +class ClassE: + def f(self): ... + def f2(self): + print(10) + + +class ClassF: + def f(self): ... # Comment 2 + + +class ClassG: + def f(self): # Comment 1 + ... # Comment 2 + + +class ClassH: + def f(self): + # Comment + + ... +``` diff --git a/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__form_feeds.py.snap b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__form_feeds.py.snap index 23d9765fce..e6625b01a7 100644 --- a/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__form_feeds.py.snap +++ b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__form_feeds.py.snap @@ -128,7 +128,7 @@ a = [ ```diff --- Black +++ Ruff -@@ -5,62 +5,62 @@ +@@ -5,63 +5,62 @@ # Comment and statement processing is different enough that we'll test variations of both # contexts here @@ -167,8 +167,9 @@ a = [ + # - -+ - # +-# + ++# # pass @@ -211,7 +212,7 @@ a = [ pass -@@ -68,25 +68,23 @@ +@@ -69,25 +68,23 @@ def foo(): pass @@ -385,6 +386,7 @@ a = [] # + # pass diff --git a/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__function_trailing_comma.py.snap b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__function_trailing_comma.py.snap new file mode 100644 index 0000000000..3bdabdf35a --- /dev/null +++ b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__function_trailing_comma.py.snap @@ -0,0 +1,569 @@ +--- +source: crates/ruff_python_formatter/tests/fixtures.rs +input_file: crates/ruff_python_formatter/resources/test/fixtures/black/cases/function_trailing_comma.py +--- +## Input + +```python +def f(a,): + d = {'key': 'value',} + tup = (1,) + +def f2(a,b,): + d = {'key': 'value', 'key2': 'value2',} + tup = (1,2,) + +def f(a:int=1,): + call(arg={'explode': 'this',}) + call2(arg=[1,2,3],) + x = { + "a": 1, + "b": 2, + }["a"] + if a == {"a": 1,"b": 2,"c": 3,"d": 4,"e": 5,"f": 6,"g": 7,"h": 8,}["a"]: + pass + +def xxxxxxxxxxxxxxxxxxxxxxxxxxxx() -> Set[ + "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" +]: + json = {"k": {"k2": {"k3": [1,]}}} + + + +# The type annotation shouldn't get a trailing comma since that would change its type. +# Relevant bug report: https://github.com/psf/black/issues/2381. +def some_function_with_a_really_long_name() -> ( + returning_a_deeply_nested_import_of_a_type_i_suppose +): + pass + + +def some_method_with_a_really_long_name(very_long_parameter_so_yeah: str, another_long_parameter: int) -> ( + another_case_of_returning_a_deeply_nested_import_of_a_type_i_suppose_cause_why_not +): + pass + + +def func() -> ( + also_super_long_type_annotation_that_may_cause_an_AST_related_crash_in_black(this_shouldn_t_get_a_trailing_comma_too) +): + pass + + +def func() -> ((also_super_long_type_annotation_that_may_cause_an_AST_related_crash_in_black( + this_shouldn_t_get_a_trailing_comma_too + )) +): + pass + + +# Make sure inner one-element tuple won't explode +some_module.some_function( + argument1, (one_element_tuple,), argument4, argument5, argument6 +) + +# Inner trailing comma causes outer to explode +some_module.some_function( + argument1, (one, two,), argument4, argument5, argument6 +) + +def foo() -> ( + # comment inside parenthesised return type + int +): + ... + +def foo() -> ( + # comment inside parenthesised return type + # more + int + # another +): + ... + +def foo() -> ( + # comment inside parenthesised new union return type + int | str | bytes +): + ... + +def foo() -> ( + # comment inside plain tuple +): + pass + +def foo(arg: (# comment with non-return annotation + int + # comment with non-return annotation +)): + pass + +def foo(arg: (# comment with non-return annotation + int | range | memoryview + # comment with non-return annotation +)): + pass + +def foo(arg: (# only before + int +)): + pass + +def foo(arg: ( + int + # only after +)): + pass + +variable: ( # annotation + because + # why not +) + +variable: ( + because + # why not +) +``` + +## Black Differences + +```diff +--- Black ++++ Ruff +@@ -130,9 +130,7 @@ + + def foo() -> ( + # comment inside parenthesised new union return type +- int +- | str +- | bytes ++ int | str | bytes + ): ... + + +@@ -143,34 +141,31 @@ + + + def foo( +- arg: ( # comment with non-return annotation +- int +- # comment with non-return annotation +- ), ++ # comment with non-return annotation ++ # comment with non-return annotation ++ arg: (int), + ): + pass + + + def foo( +- arg: ( # comment with non-return annotation +- int +- | range +- | memoryview +- # comment with non-return annotation +- ), ++ # comment with non-return annotation ++ # comment with non-return annotation ++ arg: (int | range | memoryview), + ): + pass + + +-def foo(arg: int): # only before ++def foo( ++ # only before ++ arg: (int), ++): + pass + + + def foo( +- arg: ( +- int +- # only after +- ), ++ # only after ++ arg: (int), + ): + pass + +``` + +## Ruff Output + +```python +def f( + a, +): + d = { + "key": "value", + } + tup = (1,) + + +def f2( + a, + b, +): + d = { + "key": "value", + "key2": "value2", + } + tup = ( + 1, + 2, + ) + + +def f( + a: int = 1, +): + call( + arg={ + "explode": "this", + } + ) + call2( + arg=[1, 2, 3], + ) + x = { + "a": 1, + "b": 2, + }["a"] + if ( + a + == { + "a": 1, + "b": 2, + "c": 3, + "d": 4, + "e": 5, + "f": 6, + "g": 7, + "h": 8, + }["a"] + ): + pass + + +def xxxxxxxxxxxxxxxxxxxxxxxxxxxx() -> ( + Set["xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"] +): + json = { + "k": { + "k2": { + "k3": [ + 1, + ] + } + } + } + + +# The type annotation shouldn't get a trailing comma since that would change its type. +# Relevant bug report: https://github.com/psf/black/issues/2381. +def some_function_with_a_really_long_name() -> ( + returning_a_deeply_nested_import_of_a_type_i_suppose +): + pass + + +def some_method_with_a_really_long_name( + very_long_parameter_so_yeah: str, another_long_parameter: int +) -> another_case_of_returning_a_deeply_nested_import_of_a_type_i_suppose_cause_why_not: + pass + + +def func() -> ( + also_super_long_type_annotation_that_may_cause_an_AST_related_crash_in_black( + this_shouldn_t_get_a_trailing_comma_too + ) +): + pass + + +def func() -> ( + also_super_long_type_annotation_that_may_cause_an_AST_related_crash_in_black( + this_shouldn_t_get_a_trailing_comma_too + ) +): + pass + + +# Make sure inner one-element tuple won't explode +some_module.some_function( + argument1, (one_element_tuple,), argument4, argument5, argument6 +) + +# Inner trailing comma causes outer to explode +some_module.some_function( + argument1, + ( + one, + two, + ), + argument4, + argument5, + argument6, +) + + +def foo() -> ( + # comment inside parenthesised return type + int +): ... + + +def foo() -> ( + # comment inside parenthesised return type + # more + int + # another +): ... + + +def foo() -> ( + # comment inside parenthesised new union return type + int | str | bytes +): ... + + +def foo() -> ( + # comment inside plain tuple +): + pass + + +def foo( + # comment with non-return annotation + # comment with non-return annotation + arg: (int), +): + pass + + +def foo( + # comment with non-return annotation + # comment with non-return annotation + arg: (int | range | memoryview), +): + pass + + +def foo( + # only before + arg: (int), +): + pass + + +def foo( + # only after + arg: (int), +): + pass + + +variable: ( # annotation + because + # why not +) + +variable: ( + because + # why not +) +``` + +## Black Output + +```python +def f( + a, +): + d = { + "key": "value", + } + tup = (1,) + + +def f2( + a, + b, +): + d = { + "key": "value", + "key2": "value2", + } + tup = ( + 1, + 2, + ) + + +def f( + a: int = 1, +): + call( + arg={ + "explode": "this", + } + ) + call2( + arg=[1, 2, 3], + ) + x = { + "a": 1, + "b": 2, + }["a"] + if ( + a + == { + "a": 1, + "b": 2, + "c": 3, + "d": 4, + "e": 5, + "f": 6, + "g": 7, + "h": 8, + }["a"] + ): + pass + + +def xxxxxxxxxxxxxxxxxxxxxxxxxxxx() -> ( + Set["xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"] +): + json = { + "k": { + "k2": { + "k3": [ + 1, + ] + } + } + } + + +# The type annotation shouldn't get a trailing comma since that would change its type. +# Relevant bug report: https://github.com/psf/black/issues/2381. +def some_function_with_a_really_long_name() -> ( + returning_a_deeply_nested_import_of_a_type_i_suppose +): + pass + + +def some_method_with_a_really_long_name( + very_long_parameter_so_yeah: str, another_long_parameter: int +) -> another_case_of_returning_a_deeply_nested_import_of_a_type_i_suppose_cause_why_not: + pass + + +def func() -> ( + also_super_long_type_annotation_that_may_cause_an_AST_related_crash_in_black( + this_shouldn_t_get_a_trailing_comma_too + ) +): + pass + + +def func() -> ( + also_super_long_type_annotation_that_may_cause_an_AST_related_crash_in_black( + this_shouldn_t_get_a_trailing_comma_too + ) +): + pass + + +# Make sure inner one-element tuple won't explode +some_module.some_function( + argument1, (one_element_tuple,), argument4, argument5, argument6 +) + +# Inner trailing comma causes outer to explode +some_module.some_function( + argument1, + ( + one, + two, + ), + argument4, + argument5, + argument6, +) + + +def foo() -> ( + # comment inside parenthesised return type + int +): ... + + +def foo() -> ( + # comment inside parenthesised return type + # more + int + # another +): ... + + +def foo() -> ( + # comment inside parenthesised new union return type + int + | str + | bytes +): ... + + +def foo() -> ( + # comment inside plain tuple +): + pass + + +def foo( + arg: ( # comment with non-return annotation + int + # comment with non-return annotation + ), +): + pass + + +def foo( + arg: ( # comment with non-return annotation + int + | range + | memoryview + # comment with non-return annotation + ), +): + pass + + +def foo(arg: int): # only before + pass + + +def foo( + arg: ( + int + # only after + ), +): + pass + + +variable: ( # annotation + because + # why not +) + +variable: ( + because + # why not +) +``` diff --git a/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__pattern_matching_with_if_stmt.py.snap b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__pattern_matching_with_if_stmt.py.snap new file mode 100644 index 0000000000..ff72c23d00 --- /dev/null +++ b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__pattern_matching_with_if_stmt.py.snap @@ -0,0 +1,185 @@ +--- +source: crates/ruff_python_formatter/tests/fixtures.rs +input_file: crates/ruff_python_formatter/resources/test/fixtures/black/cases/pattern_matching_with_if_stmt.py +--- +## Input + +```python +match match: + case "test" if case != "not very loooooooooooooog condition": # comment + pass + +match smth: + case "test" if "any long condition" != "another long condition" and "this is a long condition": + pass + case test if "any long condition" != "another long condition" and "this is a looooong condition": + pass + case test if "any long condition" != "another long condition" and "this is a looooong condition": # some additional comments + pass + case test if (True): # some comment + pass + case test if (False + ): # some comment + pass + case test if (True # some comment + ): + pass # some comment + case cases if (True # some comment + ): # some other comment + pass # some comment + case match if (True # some comment + ): + pass # some comment + +# case black_test_patma_052 (originally in the pattern_matching_complex test case) +match x: + case [1, 0] if x := x[:0]: + y = 1 + case [1, 0] if (x := x[:0]): + y = 1 +``` + +## Black Differences + +```diff +--- Black ++++ Ruff +@@ -3,34 +3,36 @@ + pass + + match smth: +- case "test" if ( +- "any long condition" != "another long condition" and "this is a long condition" +- ): ++ case "test" if "any long condition" != "another long condition" and "this is a long condition": + pass +- case test if ( +- "any long condition" != "another long condition" +- and "this is a looooong condition" +- ): ++ case ( ++ test ++ ) if "any long condition" != "another long condition" and "this is a looooong condition": + pass +- case test if ( +- "any long condition" != "another long condition" +- and "this is a looooong condition" +- ): # some additional comments ++ case ( ++ test ++ ) if "any long condition" != "another long condition" and "this is a looooong condition": # some additional comments + pass +- case test if True: # some comment ++ case test if (True): # some comment + pass +- case test if False: # some comment ++ case test if (False): # some comment + pass +- case test if True: # some comment ++ case test if ( ++ True # some comment ++ ): + pass # some comment +- case cases if True: # some comment # some other comment ++ case cases if ( ++ True # some comment ++ ): # some other comment + pass # some comment +- case match if True: # some comment ++ case match if ( ++ True # some comment ++ ): + pass # some comment + + # case black_test_patma_052 (originally in the pattern_matching_complex test case) + match x: + case [1, 0] if x := x[:0]: + y = 1 +- case [1, 0] if x := x[:0]: ++ case [1, 0] if (x := x[:0]): + y = 1 +``` + +## Ruff Output + +```python +match match: + case "test" if case != "not very loooooooooooooog condition": # comment + pass + +match smth: + case "test" if "any long condition" != "another long condition" and "this is a long condition": + pass + case ( + test + ) if "any long condition" != "another long condition" and "this is a looooong condition": + pass + case ( + test + ) if "any long condition" != "another long condition" and "this is a looooong condition": # some additional comments + pass + case test if (True): # some comment + pass + case test if (False): # some comment + pass + case test if ( + True # some comment + ): + pass # some comment + case cases if ( + True # some comment + ): # some other comment + pass # some comment + case match if ( + True # some comment + ): + pass # some comment + +# case black_test_patma_052 (originally in the pattern_matching_complex test case) +match x: + case [1, 0] if x := x[:0]: + y = 1 + case [1, 0] if (x := x[:0]): + y = 1 +``` + +## Black Output + +```python +match match: + case "test" if case != "not very loooooooooooooog condition": # comment + pass + +match smth: + case "test" if ( + "any long condition" != "another long condition" and "this is a long condition" + ): + pass + case test if ( + "any long condition" != "another long condition" + and "this is a looooong condition" + ): + pass + case test if ( + "any long condition" != "another long condition" + and "this is a looooong condition" + ): # some additional comments + pass + case test if True: # some comment + pass + case test if False: # some comment + pass + case test if True: # some comment + pass # some comment + case cases if True: # some comment # some other comment + pass # some comment + case match if True: # some comment + pass # some comment + +# case black_test_patma_052 (originally in the pattern_matching_complex test case) +match x: + case [1, 0] if x := x[:0]: + y = 1 + case [1, 0] if x := x[:0]: + y = 1 +``` 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 a0c99b1bff..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 @@ -26,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 @@ -165,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 @@ -269,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 new file mode 100644 index 0000000000..1a7bc07b67 --- /dev/null +++ b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__pep_701.py.snap @@ -0,0 +1,442 @@ +--- +source: crates/ruff_python_formatter/tests/fixtures.rs +input_file: crates/ruff_python_formatter/resources/test/fixtures/black/cases/pep_701.py +--- +## Input + +```python +x = f"foo" +x = f'foo' +x = f"""foo""" +x = f'''foo''' +x = f"foo {{ bar {{ baz" +x = f"foo {{ {2 + 2}bar {{ baz" +x = f'foo {{ {2 + 2}bar {{ baz' +x = f"""foo {{ {2 + 2}bar {{ baz""" +x = f'''foo {{ {2 + 2}bar {{ baz''' + +# edge case: FSTRING_MIDDLE containing only whitespace should not be stripped +x = f"{a} {b}" + +x = f"foo { + 2 + 2 +} bar baz" + +x = f"foo {{ {"a {2 + 2} b"}bar {{ baz" +x = f"foo {{ {f'a {2 + 2} b'}bar {{ baz" +x = f"foo {{ {f"a {2 + 2} b"}bar {{ baz" + +x = f"foo {{ {f'a {f"a {2 + 2} b"} b'}bar {{ baz" +x = f"foo {{ {f"a {f"a {2 + 2} b"} b"}bar {{ baz" + +x = """foo {{ {2 + 2}bar +baz""" + + +x = f"""foo {{ {2 + 2}bar {{ baz""" + +x = f"""foo {{ { + 2 + 2 +}bar {{ baz""" + + +x = f"""foo {{ { + 2 + 2 +}bar +baz""" + +x = f"""foo {{ a + foo {2 + 2}bar {{ baz + + x = f"foo {{ { + 2 + 2 # comment + }bar" + + {{ baz + + }} buzz + + {print("abc" + "def" +)} +abc""" + +# edge case: end triple quotes at index zero +f"""foo {2+2} bar +""" + +f' \' {f"'"} \' ' +f" \" {f'"'} \" " + +x = f"a{2+2:=^72}b" +x = f"a{2+2:x}b" + +rf'foo' +rf'{foo}' + +f"{x:{y}d}" + +x = f"a{2+2:=^{x}}b" +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 correctly {2+2:d}" + +f"{2+2=}" +f"{2+2 = }" +f"{ 2 + 2 = }" + +f"""foo { + datetime.datetime.now():%Y +%m +%d +}""" + +f"{ +X +!r +}" + +raise ValueError( + "xxxxxxxxxxxIncorrect --line-ranges format, expect START-END, found" + f" {lines_str!r}" + ) + +f"`escape` only permitted in {{'html', 'latex', 'latex-math'}}, \ +got {escape}" + +x = f'\N{GREEK CAPITAL LETTER DELTA} \N{SNOWMAN} {x}' +fr'\{{\}}' + +f""" + WITH {f''' + {1}_cte AS ()'''} +""" + +value: str = f'''foo +''' + +log( + f"Received operation {server_operation.name} from " + f"{self.writer._transport.get_extra_info('peername')}", # type: ignore[attr-defined] + level=0, +) + +f"{1:{f'{2}'}}" +f'{1:{f'{2}'}}' +f'{1:{2}d}' + +f'{{\\"kind\\":\\"ConfigMap\\",\\"metadata\\":{{\\"annotations\\":{{}},\\"name\\":\\"cluster-info\\",\\"namespace\\":\\"amazon-cloudwatch\\"}}}}' + +f"""{''' +'''}""" + +f"{'\''}" +f"{f'\''}" + +f'{1}\{{' +f'{2} foo \{{[\}}' +f'\{3}' +rf"\{"a"}" +``` + +## Black Differences + +```diff +--- Black ++++ Ruff +@@ -119,7 +119,7 @@ + ) + + f"{1:{f'{2}'}}" +-f"{1:{f'{2}'}}" ++f'{1:{f'{2}'}}' + f"{1:{2}d}" + + f'{{\\"kind\\":\\"ConfigMap\\",\\"metadata\\":{{\\"annotations\\":{{}},\\"name\\":\\"cluster-info\\",\\"namespace\\":\\"amazon-cloudwatch\\"}}}}' +``` + +## Ruff Output + +```python +x = f"foo" +x = f"foo" +x = f"""foo""" +x = f"""foo""" +x = f"foo {{ bar {{ baz" +x = f"foo {{ {2 + 2}bar {{ baz" +x = f"foo {{ {2 + 2}bar {{ baz" +x = f"""foo {{ {2 + 2}bar {{ baz""" +x = f"""foo {{ {2 + 2}bar {{ baz""" + +# edge case: FSTRING_MIDDLE containing only whitespace should not be stripped +x = f"{a} {b}" + +x = f"foo { + 2 + 2 +} bar baz" + +x = f"foo {{ {"a {2 + 2} b"}bar {{ baz" +x = f"foo {{ {f'a {2 + 2} b'}bar {{ baz" +x = f"foo {{ {f"a {2 + 2} b"}bar {{ baz" + +x = f"foo {{ {f'a {f"a {2 + 2} b"} b'}bar {{ baz" +x = f"foo {{ {f"a {f"a {2 + 2} b"} b"}bar {{ baz" + +x = """foo {{ {2 + 2}bar +baz""" + + +x = f"""foo {{ {2 + 2}bar {{ baz""" + +x = f"""foo {{ { + 2 + 2 +}bar {{ baz""" + + +x = f"""foo {{ { + 2 + 2 +}bar +baz""" + +x = f"""foo {{ a + foo {2 + 2}bar {{ baz + + x = f"foo {{ { + 2 + 2 # comment + }bar" + + {{ baz + + }} buzz + + {print("abc" + "def" +)} +abc""" + +# edge case: end triple quotes at index zero +f"""foo {2+2} bar +""" + +f' \' {f"'"} \' ' +f" \" {f'"'} \" " + +x = f"a{2+2:=^72}b" +x = f"a{2+2:x}b" + +rf"foo" +rf"{foo}" + +f"{x:{y}d}" + +x = f"a{2+2:=^{x}}b" +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 correctly {2+2:d}" + +f"{2+2=}" +f"{2+2 = }" +f"{ 2 + 2 = }" + +f"""foo { + datetime.datetime.now():%Y +%m +%d +}""" + +f"{ +X +!r +}" + +raise ValueError( + "xxxxxxxxxxxIncorrect --line-ranges format, expect START-END, found" + f" {lines_str!r}" +) + +f"`escape` only permitted in {{'html', 'latex', 'latex-math'}}, \ +got {escape}" + +x = f"\N{GREEK CAPITAL LETTER DELTA} \N{SNOWMAN} {x}" +rf"\{{\}}" + +f""" + WITH {f''' + {1}_cte AS ()'''} +""" + +value: str = f"""foo +""" + +log( + f"Received operation {server_operation.name} from " + f"{self.writer._transport.get_extra_info('peername')}", # type: ignore[attr-defined] + level=0, +) + +f"{1:{f'{2}'}}" +f'{1:{f'{2}'}}' +f"{1:{2}d}" + +f'{{\\"kind\\":\\"ConfigMap\\",\\"metadata\\":{{\\"annotations\\":{{}},\\"name\\":\\"cluster-info\\",\\"namespace\\":\\"amazon-cloudwatch\\"}}}}' + +f"""{''' +'''}""" + +f"{'\''}" +f"{f'\''}" + +f"{1}\{{" +f"{2} foo \{{[\}}" +f"\{3}" +rf"\{"a"}" +``` + +## Black Output + +```python +x = f"foo" +x = f"foo" +x = f"""foo""" +x = f"""foo""" +x = f"foo {{ bar {{ baz" +x = f"foo {{ {2 + 2}bar {{ baz" +x = f"foo {{ {2 + 2}bar {{ baz" +x = f"""foo {{ {2 + 2}bar {{ baz""" +x = f"""foo {{ {2 + 2}bar {{ baz""" + +# edge case: FSTRING_MIDDLE containing only whitespace should not be stripped +x = f"{a} {b}" + +x = f"foo { + 2 + 2 +} bar baz" + +x = f"foo {{ {"a {2 + 2} b"}bar {{ baz" +x = f"foo {{ {f'a {2 + 2} b'}bar {{ baz" +x = f"foo {{ {f"a {2 + 2} b"}bar {{ baz" + +x = f"foo {{ {f'a {f"a {2 + 2} b"} b'}bar {{ baz" +x = f"foo {{ {f"a {f"a {2 + 2} b"} b"}bar {{ baz" + +x = """foo {{ {2 + 2}bar +baz""" + + +x = f"""foo {{ {2 + 2}bar {{ baz""" + +x = f"""foo {{ { + 2 + 2 +}bar {{ baz""" + + +x = f"""foo {{ { + 2 + 2 +}bar +baz""" + +x = f"""foo {{ a + foo {2 + 2}bar {{ baz + + x = f"foo {{ { + 2 + 2 # comment + }bar" + + {{ baz + + }} buzz + + {print("abc" + "def" +)} +abc""" + +# edge case: end triple quotes at index zero +f"""foo {2+2} bar +""" + +f' \' {f"'"} \' ' +f" \" {f'"'} \" " + +x = f"a{2+2:=^72}b" +x = f"a{2+2:x}b" + +rf"foo" +rf"{foo}" + +f"{x:{y}d}" + +x = f"a{2+2:=^{x}}b" +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 correctly {2+2:d}" + +f"{2+2=}" +f"{2+2 = }" +f"{ 2 + 2 = }" + +f"""foo { + datetime.datetime.now():%Y +%m +%d +}""" + +f"{ +X +!r +}" + +raise ValueError( + "xxxxxxxxxxxIncorrect --line-ranges format, expect START-END, found" + f" {lines_str!r}" +) + +f"`escape` only permitted in {{'html', 'latex', 'latex-math'}}, \ +got {escape}" + +x = f"\N{GREEK CAPITAL LETTER DELTA} \N{SNOWMAN} {x}" +rf"\{{\}}" + +f""" + WITH {f''' + {1}_cte AS ()'''} +""" + +value: str = f"""foo +""" + +log( + f"Received operation {server_operation.name} from " + f"{self.writer._transport.get_extra_info('peername')}", # type: ignore[attr-defined] + level=0, +) + +f"{1:{f'{2}'}}" +f"{1:{f'{2}'}}" +f"{1:{2}d}" + +f'{{\\"kind\\":\\"ConfigMap\\",\\"metadata\\":{{\\"annotations\\":{{}},\\"name\\":\\"cluster-info\\",\\"namespace\\":\\"amazon-cloudwatch\\"}}}}' + +f"""{''' +'''}""" + +f"{'\''}" +f"{f'\''}" + +f"{1}\{{" +f"{2} foo \{{[\}}" +f"\{3}" +rf"\{"a"}" +``` 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 02d35ab0c0..25ed182111 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 @@ -180,6 +180,13 @@ this_will_also_become_one_line = ( # comment "b" "c" ) + +assert some_var == expected_result, """ +test +""" +assert some_var == expected_result, f""" +expected: {expected_result} +actual: {some_var}""" ``` ## Black Differences @@ -368,7 +375,7 @@ this_will_also_become_one_line = ( # comment this_will_stay_on_three_lines = ( "a" # comment -@@ -206,4 +247,6 @@ +@@ -206,7 +247,9 @@ "c" ) @@ -376,6 +383,9 @@ this_will_also_become_one_line = ( # comment +this_will_also_become_one_line = ( # comment + "a" "b" "c" +) + + assert some_var == expected_result, """ + test ``` ## Ruff Output @@ -633,6 +643,13 @@ this_will_stay_on_three_lines = ( this_will_also_become_one_line = ( # comment "a" "b" "c" ) + +assert some_var == expected_result, """ +test +""" +assert some_var == expected_result, f""" +expected: {expected_result} +actual: {some_var}""" ``` ## Black Output @@ -847,6 +864,11 @@ this_will_stay_on_three_lines = ( ) this_will_also_become_one_line = "abc" # comment + +assert some_var == expected_result, """ +test +""" +assert some_var == expected_result, f""" +expected: {expected_result} +actual: {some_var}""" ``` - - 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 new file mode 100644 index 0000000000..19c55af550 --- /dev/null +++ b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__type_param_defaults.py.snap @@ -0,0 +1,159 @@ +--- +source: crates/ruff_python_formatter/tests/fixtures.rs +input_file: crates/ruff_python_formatter/resources/test/fixtures/black/cases/type_param_defaults.py +--- +## Input + +```python +type A[T=int] = float +type B[*P=int] = float +type C[*Ts=int] = float +type D[*Ts=*int] = float +type D[something_that_is_very_very_very_long=something_that_is_very_very_very_long] = float +type D[*something_that_is_very_very_very_long=*something_that_is_very_very_very_long] = float +type something_that_is_long[something_that_is_long=something_that_is_long] = something_that_is_long + +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: + pass + +def trailing_comma1[T=int,](a: str): + pass + +def trailing_comma2[T=int](a: str,): + pass +``` + +## Black Differences + +```diff +--- Black ++++ Ruff +@@ -8,20 +8,20 @@ + type D[ + *something_that_is_very_very_very_long = *something_that_is_very_very_very_long + ] = float +-type something_that_is_long[ +- something_that_is_long = something_that_is_long +-] = something_that_is_long ++type something_that_is_long[something_that_is_long = 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 + + +@@ -31,7 +31,7 @@ + pass + + +-def trailing_comma2[ +- T = int +-](a: str,): ++def trailing_comma2[T = int]( ++ a: str, ++): + pass +``` + +## Ruff Output + +```python +type A[T = int] = float +type B[*P = int] = float +type C[*Ts = int] = float +type D[*Ts = *int] = float +type D[ + something_that_is_very_very_very_long = something_that_is_very_very_very_long +] = float +type D[ + *something_that_is_very_very_very_long = *something_that_is_very_very_very_long +] = float +type something_that_is_long[something_that_is_long = something_that_is_long] = ( + something_that_is_long +) + + +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: + pass + + +def trailing_comma1[ + T = int, +](a: str): + pass + + +def trailing_comma2[T = int]( + a: str, +): + pass +``` + +## Black Output + +```python +type A[T = int] = float +type B[*P = int] = float +type C[*Ts = int] = float +type D[*Ts = *int] = float +type D[ + something_that_is_very_very_very_long = something_that_is_very_very_very_long +] = float +type D[ + *something_that_is_very_very_very_long = *something_that_is_very_very_very_long +] = float +type something_that_is_long[ + something_that_is_long = something_that_is_long +] = something_that_is_long + + +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: + pass + + +def trailing_comma1[ + T = int, +](a: str): + pass + + +def trailing_comma2[ + T = int +](a: str,): + pass +```