From 1ed9b215b981d0283022185f988957d22a02b918 Mon Sep 17 00:00:00 2001 From: Brent Westbrook <36778786+ntBre@users.noreply.github.com> Date: Tue, 14 Oct 2025 10:14:59 -0400 Subject: [PATCH] Update Black tests (#20794) Summary -- ```shell git clone git@github.com:psf/black.git ../other/black crates/ruff_python_formatter/resources/test/fixtures/import_black_tests.py ../other/black ``` Then ran our tests and accepted the snapshots I had to make a small fix to our tuple normalization logic for `del` statements in the second commit, otherwise the tests were panicking at a changed AST. I think the new implementation is closer to the intention described in the nearby comment anyway, though. The first commit adds the new Python, settings, and `.expect` files, the next three commits make some small fixes to help get the tests running, and then the fifth commit accepts all but one of the new snapshots. The last commit includes the new unsupported syntax error for one f-string example, tracked in #20774. Test Plan -- Newly imported tests. I went through all of the new snapshots and added review comments below. I think they're all expected, except a few cases I wasn't 100% sure about. --- .../test/fixtures/black/cases/annotations.py | 7 + .../black/cases/annotations.py.expect | 7 + .../backslash_before_indent.options.json | 2 +- .../test/fixtures/black/cases/cantfit.py | 35 ++ .../fixtures/black/cases/cantfit.py.expect | 61 ++ .../cases/context_managers_38.options.json | 2 +- .../black/cases/context_managers_39.py | 6 + .../black/cases/context_managers_39.py.expect | 5 + .../fixtures/black/cases/docstring_newline.py | 3 + .../black/cases/docstring_newline.py.expect | 3 + ...p9.options.json => fmtskip10.options.json} | 0 .../test/fixtures/black/cases/fmtskip10.py | 8 + .../fixtures/black/cases/fmtskip10.py.expect | 8 + .../test/fixtures/black/cases/fmtskip11.py | 6 + .../fixtures/black/cases/fmtskip11.py.expect | 6 + .../black/cases/format_unicode_escape_seq.py | 15 + .../cases/format_unicode_escape_seq.py.expect | 15 + .../fixtures/black/cases/fstring.options.json | 1 - .../black/cases/fstring_quotations.py | 34 ++ .../black/cases/fstring_quotations.py.expect | 29 + ...ef_return_type_trailing_comma.options.json | 2 +- .../cases/generics_wrapping.options.json | 1 + .../fixtures/black/cases/generics_wrapping.py | 133 +++++ .../black/cases/generics_wrapping.py.expect | 170 ++++++ .../fixtures/black/cases/line_ranges_basic.py | 2 +- .../black/cases/line_ranges_basic.py.expect | 2 +- .../cases/line_ranges_decorator_edge_case.py | 8 + .../line_ranges_decorator_edge_case.py.expect | 8 + .../cases/long_strings__type_annotations.py | 28 + .../long_strings__type_annotations.py.expect | 26 + .../black/cases/module_docstring_2.py | 2 +- .../black/cases/module_docstring_2.py.expect | 2 +- ...dule_docstring_after_comment.options.json} | 0 .../cases/module_docstring_after_comment.py | 9 + .../module_docstring_after_comment.py.expect | 10 + .../no_blank_line_before_docstring.py.expect | 1 - ...pattern_matching_with_if_stmt.options.json | 2 +- .../cases/pep604_union_types_line_breaks.py | 2 +- .../pep604_union_types_line_breaks.py.expect | 2 +- ...typed_star_arg_type_var_tuple.options.json | 1 + .../pep646_typed_star_arg_type_var_tuple.py | 5 + ...46_typed_star_arg_type_var_tuple.py.expect | 5 + .../fixtures/black/cases/pep_572_py310.py | 5 + .../black/cases/pep_572_py310.py.expect | 5 + .../test/fixtures/black/cases/pep_701.py | 5 +- .../fixtures/black/cases/pep_701.py.expect | 4 +- .../cases/prefer_rhs_split_reformatted.py | 9 + .../prefer_rhs_split_reformatted.py.expect | 11 + .../black/cases/preview_comments7.py.expect | 1 - ...ions.json => preview_fstring.options.json} | 0 .../fixtures/black/cases/preview_fstring.py | 1 + .../black/cases/preview_fstring.py.expect | 1 + ...preview_import_line_collapse.options.json} | 0 .../cases/preview_import_line_collapse.py | 90 +++ .../preview_import_line_collapse.py.expect | 85 +++ .../black/cases/preview_long_dict_values.py | 100 +++- .../cases/preview_long_dict_values.py.expect | 98 ++- .../black/cases/preview_long_strings.py | 16 +- .../cases/preview_long_strings.py.expect | 54 +- .../cases/preview_long_strings__regression.py | 1 + ...preview_long_strings__regression.py.expect | 10 +- .../black/cases/preview_multiline_strings.py | 67 +++ .../cases/preview_multiline_strings.py.expect | 103 ++++ ...ltiline_lone_list_item_parens.options.json | 1 + ..._remove_multiline_lone_list_item_parens.py | 136 +++++ ..._multiline_lone_list_item_parens.py.expect | 106 ++++ ...preview_wrap_comprehension_in.options.json | 1 + .../cases/preview_wrap_comprehension_in.py | 69 +++ .../preview_wrap_comprehension_in.py.expect | 88 +++ .../test/fixtures/black/cases/python37.py | 1 + .../fixtures/black/cases/python37.py.expect | 1 + .../remove_except_types_parens.options.json | 1 + .../black/cases/remove_except_types_parens.py | 123 ++++ .../remove_except_types_parens.py.expect | 123 ++++ ...except_types_parens_pre_py314.options.json | 1 + .../remove_except_types_parens_pre_py314.py | 111 ++++ ...ve_except_types_parens_pre_py314.py.expect | 111 ++++ .../cases/remove_lone_list_item_parens.py | 80 +++ .../remove_lone_list_item_parens.py.expect | 74 +++ ...edundant_parens_in_case_guard.options.json | 2 +- .../black/cases/remove_with_brackets.py | 13 + .../cases/remove_with_brackets.py.expect | 13 + ...c_trailing_comma_generic_wrap.options.json | 1 + .../skip_magic_trailing_comma_generic_wrap.py | 97 +++ ...agic_trailing_comma_generic_wrap.py.expect | 62 ++ .../cases/target_version_flag.options.json | 1 + .../black/cases/target_version_flag.py | 4 + .../black/cases/target_version_flag.py.expect | 3 + .../fixtures/black/cases/tuple_with_stmt.py | 30 + .../black/cases/tuple_with_stmt.py.expect | 30 + .../black/cases/type_expansion.options.json | 1 + .../fixtures/black/cases/type_expansion.py | 13 + .../black/cases/type_expansion.py.expect | 42 ++ .../black/cases/type_param_defaults.py | 2 + .../black/cases/type_param_defaults.py.expect | 26 +- .../test/fixtures/black/cases/type_params.py | 4 + .../black/cases/type_params.py.expect | 10 + .../fixtures/black/cases/walrus_in_dict.py | 2 +- .../black/cases/walrus_in_dict.py.expect | 2 +- .../test/fixtures/import_black_tests.py | 4 +- .../ruff_python_formatter/tests/fixtures.rs | 17 +- .../ruff_python_formatter/tests/normalizer.rs | 20 +- ...black_compatibility@cases__cantfit.py.snap | 204 +++++++ ...ack_compatibility@cases__fmtskip10.py.snap | 76 +++ ...black_compatibility@cases__fstring.py.snap | 14 + ...tibility@cases__fstring_quotations.py.snap | 126 ++++ ...atibility@cases__generics_wrapping.py.snap | 561 ++++++++++++++++++ ...es__long_strings__type_annotations.py.snap | 114 ++++ ...es__no_blank_line_before_docstring.py.snap | 122 ---- ...es__pep604_union_types_line_breaks.py.snap | 7 +- ...black_compatibility@cases__pep_701.py.snap | 27 +- ...ases__prefer_rhs_split_reformatted.py.snap | 123 ++++ ...atibility@cases__preview_comments7.py.snap | 15 +- ...ases__preview_import_line_collapse.py.snap | 325 ++++++++++ ...ty@cases__preview_long_dict_values.py.snap | 336 ++++++++++- ...bility@cases__preview_long_strings.py.snap | 153 +++-- ...__preview_long_strings__regression.py.snap | 31 +- ...y@cases__preview_multiline_strings.py.snap | 300 +++++++++- ...ve_multiline_lone_list_item_parens.py.snap | 535 +++++++++++++++++ ...ses__preview_wrap_comprehension_in.py.snap | 335 +++++++++++ ...@cases__remove_except_types_parens.py.snap | 427 +++++++++++++ ...ases__remove_lone_list_item_parens.py.snap | 283 +++++++++ ...ibility@cases__type_param_defaults.py.snap | 70 +-- 123 files changed, 6607 insertions(+), 343 deletions(-) create mode 100644 crates/ruff_python_formatter/resources/test/fixtures/black/cases/annotations.py create mode 100644 crates/ruff_python_formatter/resources/test/fixtures/black/cases/annotations.py.expect create mode 100644 crates/ruff_python_formatter/resources/test/fixtures/black/cases/cantfit.py create mode 100644 crates/ruff_python_formatter/resources/test/fixtures/black/cases/cantfit.py.expect create mode 100644 crates/ruff_python_formatter/resources/test/fixtures/black/cases/docstring_newline.py create mode 100644 crates/ruff_python_formatter/resources/test/fixtures/black/cases/docstring_newline.py.expect rename crates/ruff_python_formatter/resources/test/fixtures/black/cases/{fmtskip9.options.json => fmtskip10.options.json} (100%) create mode 100644 crates/ruff_python_formatter/resources/test/fixtures/black/cases/fmtskip10.py create mode 100644 crates/ruff_python_formatter/resources/test/fixtures/black/cases/fmtskip10.py.expect create mode 100644 crates/ruff_python_formatter/resources/test/fixtures/black/cases/fmtskip11.py create mode 100644 crates/ruff_python_formatter/resources/test/fixtures/black/cases/fmtskip11.py.expect create mode 100644 crates/ruff_python_formatter/resources/test/fixtures/black/cases/format_unicode_escape_seq.py create mode 100644 crates/ruff_python_formatter/resources/test/fixtures/black/cases/format_unicode_escape_seq.py.expect delete mode 100644 crates/ruff_python_formatter/resources/test/fixtures/black/cases/fstring.options.json create mode 100644 crates/ruff_python_formatter/resources/test/fixtures/black/cases/fstring_quotations.py create mode 100644 crates/ruff_python_formatter/resources/test/fixtures/black/cases/fstring_quotations.py.expect create mode 100644 crates/ruff_python_formatter/resources/test/fixtures/black/cases/generics_wrapping.options.json create mode 100644 crates/ruff_python_formatter/resources/test/fixtures/black/cases/generics_wrapping.py create mode 100644 crates/ruff_python_formatter/resources/test/fixtures/black/cases/generics_wrapping.py.expect create mode 100644 crates/ruff_python_formatter/resources/test/fixtures/black/cases/line_ranges_decorator_edge_case.py create mode 100644 crates/ruff_python_formatter/resources/test/fixtures/black/cases/line_ranges_decorator_edge_case.py.expect create mode 100644 crates/ruff_python_formatter/resources/test/fixtures/black/cases/long_strings__type_annotations.py create mode 100644 crates/ruff_python_formatter/resources/test/fixtures/black/cases/long_strings__type_annotations.py.expect rename crates/ruff_python_formatter/resources/test/fixtures/black/cases/{is_simple_lookup_for_doublestar_expression.options.json => module_docstring_after_comment.options.json} (100%) create mode 100644 crates/ruff_python_formatter/resources/test/fixtures/black/cases/module_docstring_after_comment.py create mode 100644 crates/ruff_python_formatter/resources/test/fixtures/black/cases/module_docstring_after_comment.py.expect create mode 100644 crates/ruff_python_formatter/resources/test/fixtures/black/cases/pep646_typed_star_arg_type_var_tuple.options.json create mode 100644 crates/ruff_python_formatter/resources/test/fixtures/black/cases/pep646_typed_star_arg_type_var_tuple.py create mode 100644 crates/ruff_python_formatter/resources/test/fixtures/black/cases/pep646_typed_star_arg_type_var_tuple.py.expect rename crates/ruff_python_formatter/resources/test/fixtures/black/cases/{module_docstring_2.options.json => preview_fstring.options.json} (100%) create mode 100644 crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_fstring.py create mode 100644 crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_fstring.py.expect rename crates/ruff_python_formatter/resources/test/fixtures/black/cases/{typed_params_trailing_comma.options.json => preview_import_line_collapse.options.json} (100%) create mode 100644 crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_import_line_collapse.py create mode 100644 crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_import_line_collapse.py.expect create mode 100644 crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_remove_multiline_lone_list_item_parens.options.json create mode 100644 crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_remove_multiline_lone_list_item_parens.py create mode 100644 crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_remove_multiline_lone_list_item_parens.py.expect create mode 100644 crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_wrap_comprehension_in.options.json create mode 100644 crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_wrap_comprehension_in.py create mode 100644 crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_wrap_comprehension_in.py.expect create mode 100644 crates/ruff_python_formatter/resources/test/fixtures/black/cases/remove_except_types_parens.options.json create mode 100644 crates/ruff_python_formatter/resources/test/fixtures/black/cases/remove_except_types_parens.py create mode 100644 crates/ruff_python_formatter/resources/test/fixtures/black/cases/remove_except_types_parens.py.expect create mode 100644 crates/ruff_python_formatter/resources/test/fixtures/black/cases/remove_except_types_parens_pre_py314.options.json create mode 100644 crates/ruff_python_formatter/resources/test/fixtures/black/cases/remove_except_types_parens_pre_py314.py create mode 100644 crates/ruff_python_formatter/resources/test/fixtures/black/cases/remove_except_types_parens_pre_py314.py.expect create mode 100644 crates/ruff_python_formatter/resources/test/fixtures/black/cases/remove_lone_list_item_parens.py create mode 100644 crates/ruff_python_formatter/resources/test/fixtures/black/cases/remove_lone_list_item_parens.py.expect create mode 100644 crates/ruff_python_formatter/resources/test/fixtures/black/cases/skip_magic_trailing_comma_generic_wrap.options.json create mode 100644 crates/ruff_python_formatter/resources/test/fixtures/black/cases/skip_magic_trailing_comma_generic_wrap.py create mode 100644 crates/ruff_python_formatter/resources/test/fixtures/black/cases/skip_magic_trailing_comma_generic_wrap.py.expect create mode 100644 crates/ruff_python_formatter/resources/test/fixtures/black/cases/target_version_flag.options.json create mode 100644 crates/ruff_python_formatter/resources/test/fixtures/black/cases/target_version_flag.py create mode 100644 crates/ruff_python_formatter/resources/test/fixtures/black/cases/target_version_flag.py.expect create mode 100644 crates/ruff_python_formatter/resources/test/fixtures/black/cases/tuple_with_stmt.py create mode 100644 crates/ruff_python_formatter/resources/test/fixtures/black/cases/tuple_with_stmt.py.expect create mode 100644 crates/ruff_python_formatter/resources/test/fixtures/black/cases/type_expansion.options.json create mode 100644 crates/ruff_python_formatter/resources/test/fixtures/black/cases/type_expansion.py create mode 100644 crates/ruff_python_formatter/resources/test/fixtures/black/cases/type_expansion.py.expect create mode 100644 crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__cantfit.py.snap create mode 100644 crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__fmtskip10.py.snap create mode 100644 crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__fstring_quotations.py.snap create mode 100644 crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__generics_wrapping.py.snap create mode 100644 crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__long_strings__type_annotations.py.snap delete mode 100644 crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__no_blank_line_before_docstring.py.snap create mode 100644 crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__prefer_rhs_split_reformatted.py.snap create mode 100644 crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__preview_import_line_collapse.py.snap create mode 100644 crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__preview_remove_multiline_lone_list_item_parens.py.snap create mode 100644 crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__preview_wrap_comprehension_in.py.snap create mode 100644 crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__remove_except_types_parens.py.snap create mode 100644 crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__remove_lone_list_item_parens.py.snap 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 ```