diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/simple_cases/attribute_access_on_number_literals.py b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/attribute_access_on_number_literals.py similarity index 100% rename from crates/ruff_python_formatter/resources/test/fixtures/black/simple_cases/attribute_access_on_number_literals.py rename to crates/ruff_python_formatter/resources/test/fixtures/black/cases/attribute_access_on_number_literals.py diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/simple_cases/attribute_access_on_number_literals.py.expect b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/attribute_access_on_number_literals.py.expect similarity index 100% rename from crates/ruff_python_formatter/resources/test/fixtures/black/simple_cases/attribute_access_on_number_literals.py.expect rename to crates/ruff_python_formatter/resources/test/fixtures/black/cases/attribute_access_on_number_literals.py.expect diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/simple_cases/beginning_backslash.py b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/beginning_backslash.py similarity index 100% rename from crates/ruff_python_formatter/resources/test/fixtures/black/simple_cases/beginning_backslash.py rename to crates/ruff_python_formatter/resources/test/fixtures/black/cases/beginning_backslash.py diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/simple_cases/beginning_backslash.py.expect b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/beginning_backslash.py.expect similarity index 100% rename from crates/ruff_python_formatter/resources/test/fixtures/black/simple_cases/beginning_backslash.py.expect rename to crates/ruff_python_formatter/resources/test/fixtures/black/cases/beginning_backslash.py.expect diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/simple_cases/bracketmatch.py b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/bracketmatch.py similarity index 100% rename from crates/ruff_python_formatter/resources/test/fixtures/black/simple_cases/bracketmatch.py rename to crates/ruff_python_formatter/resources/test/fixtures/black/cases/bracketmatch.py diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/simple_cases/bracketmatch.py.expect b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/bracketmatch.py.expect similarity index 100% rename from crates/ruff_python_formatter/resources/test/fixtures/black/simple_cases/bracketmatch.py.expect rename to crates/ruff_python_formatter/resources/test/fixtures/black/cases/bracketmatch.py.expect diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/bytes_docstring.py b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/bytes_docstring.py new file mode 100644 index 0000000000..488aef1ed7 --- /dev/null +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/bytes_docstring.py @@ -0,0 +1,14 @@ +def bitey(): + b" not a docstring" + +def bitey2(): + b' also not a docstring' + +def triple_quoted_bytes(): + b""" not a docstring""" + +def triple_quoted_bytes2(): + b''' also not a docstring''' + +def capitalized_bytes(): + B" NOT A DOCSTRING" diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/bytes_docstring.py.expect b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/bytes_docstring.py.expect new file mode 100644 index 0000000000..d7ce57746e --- /dev/null +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/bytes_docstring.py.expect @@ -0,0 +1,18 @@ +def bitey(): + b" not a docstring" + + +def bitey2(): + b" also not a docstring" + + +def triple_quoted_bytes(): + b""" not a docstring""" + + +def triple_quoted_bytes2(): + b""" also not a docstring""" + + +def capitalized_bytes(): + b" NOT A DOCSTRING" diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/simple_cases/class_blank_parentheses.py b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/class_blank_parentheses.py similarity index 100% rename from crates/ruff_python_formatter/resources/test/fixtures/black/simple_cases/class_blank_parentheses.py rename to crates/ruff_python_formatter/resources/test/fixtures/black/cases/class_blank_parentheses.py diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/simple_cases/class_blank_parentheses.py.expect b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/class_blank_parentheses.py.expect similarity index 100% rename from crates/ruff_python_formatter/resources/test/fixtures/black/simple_cases/class_blank_parentheses.py.expect rename to crates/ruff_python_formatter/resources/test/fixtures/black/cases/class_blank_parentheses.py.expect diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/simple_cases/class_methods_new_line.py b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/class_methods_new_line.py similarity index 100% rename from crates/ruff_python_formatter/resources/test/fixtures/black/simple_cases/class_methods_new_line.py rename to crates/ruff_python_formatter/resources/test/fixtures/black/cases/class_methods_new_line.py diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/simple_cases/class_methods_new_line.py.expect b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/class_methods_new_line.py.expect similarity index 100% rename from crates/ruff_python_formatter/resources/test/fixtures/black/simple_cases/class_methods_new_line.py.expect rename to crates/ruff_python_formatter/resources/test/fixtures/black/cases/class_methods_new_line.py.expect diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/simple_cases/collections.py b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/collections.py similarity index 100% rename from crates/ruff_python_formatter/resources/test/fixtures/black/simple_cases/collections.py rename to crates/ruff_python_formatter/resources/test/fixtures/black/cases/collections.py diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/simple_cases/collections.py.expect b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/collections.py.expect similarity index 100% rename from crates/ruff_python_formatter/resources/test/fixtures/black/simple_cases/collections.py.expect rename to crates/ruff_python_formatter/resources/test/fixtures/black/cases/collections.py.expect diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/simple_cases/comment_after_escaped_newline.py b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/comment_after_escaped_newline.py similarity index 100% rename from crates/ruff_python_formatter/resources/test/fixtures/black/simple_cases/comment_after_escaped_newline.py rename to crates/ruff_python_formatter/resources/test/fixtures/black/cases/comment_after_escaped_newline.py diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/simple_cases/comment_after_escaped_newline.py.expect b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/comment_after_escaped_newline.py.expect similarity index 100% rename from crates/ruff_python_formatter/resources/test/fixtures/black/simple_cases/comment_after_escaped_newline.py.expect rename to crates/ruff_python_formatter/resources/test/fixtures/black/cases/comment_after_escaped_newline.py.expect diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/simple_cases/comments.py b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/comments.py similarity index 100% rename from crates/ruff_python_formatter/resources/test/fixtures/black/simple_cases/comments.py rename to crates/ruff_python_formatter/resources/test/fixtures/black/cases/comments.py diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/simple_cases/comments.py.expect b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/comments.py.expect similarity index 100% rename from crates/ruff_python_formatter/resources/test/fixtures/black/simple_cases/comments.py.expect rename to crates/ruff_python_formatter/resources/test/fixtures/black/cases/comments.py.expect diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/simple_cases/comments2.py b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/comments2.py similarity index 100% rename from crates/ruff_python_formatter/resources/test/fixtures/black/simple_cases/comments2.py rename to crates/ruff_python_formatter/resources/test/fixtures/black/cases/comments2.py diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/simple_cases/comments2.py.expect b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/comments2.py.expect similarity index 100% rename from crates/ruff_python_formatter/resources/test/fixtures/black/simple_cases/comments2.py.expect rename to crates/ruff_python_formatter/resources/test/fixtures/black/cases/comments2.py.expect diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/simple_cases/comments3.py b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/comments3.py similarity index 100% rename from crates/ruff_python_formatter/resources/test/fixtures/black/simple_cases/comments3.py rename to crates/ruff_python_formatter/resources/test/fixtures/black/cases/comments3.py diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/simple_cases/comments3.py.expect b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/comments3.py.expect similarity index 100% rename from crates/ruff_python_formatter/resources/test/fixtures/black/simple_cases/comments3.py.expect rename to crates/ruff_python_formatter/resources/test/fixtures/black/cases/comments3.py.expect diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/simple_cases/comments4.py b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/comments4.py similarity index 100% rename from crates/ruff_python_formatter/resources/test/fixtures/black/simple_cases/comments4.py rename to crates/ruff_python_formatter/resources/test/fixtures/black/cases/comments4.py diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/simple_cases/comments4.py.expect b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/comments4.py.expect similarity index 100% rename from crates/ruff_python_formatter/resources/test/fixtures/black/simple_cases/comments4.py.expect rename to crates/ruff_python_formatter/resources/test/fixtures/black/cases/comments4.py.expect diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/simple_cases/comments5.py b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/comments5.py similarity index 100% rename from crates/ruff_python_formatter/resources/test/fixtures/black/simple_cases/comments5.py rename to crates/ruff_python_formatter/resources/test/fixtures/black/cases/comments5.py diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/simple_cases/comments5.py.expect b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/comments5.py.expect similarity index 100% rename from crates/ruff_python_formatter/resources/test/fixtures/black/simple_cases/comments5.py.expect rename to crates/ruff_python_formatter/resources/test/fixtures/black/cases/comments5.py.expect diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/simple_cases/comments6.py b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/comments6.py similarity index 100% rename from crates/ruff_python_formatter/resources/test/fixtures/black/simple_cases/comments6.py rename to crates/ruff_python_formatter/resources/test/fixtures/black/cases/comments6.py diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/simple_cases/comments6.py.expect b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/comments6.py.expect similarity index 100% rename from crates/ruff_python_formatter/resources/test/fixtures/black/simple_cases/comments6.py.expect rename to crates/ruff_python_formatter/resources/test/fixtures/black/cases/comments6.py.expect diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/simple_cases/comments8.py b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/comments8.py similarity index 100% rename from crates/ruff_python_formatter/resources/test/fixtures/black/simple_cases/comments8.py rename to crates/ruff_python_formatter/resources/test/fixtures/black/cases/comments8.py diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/simple_cases/comments8.py.expect b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/comments8.py.expect similarity index 100% rename from crates/ruff_python_formatter/resources/test/fixtures/black/simple_cases/comments8.py.expect rename to crates/ruff_python_formatter/resources/test/fixtures/black/cases/comments8.py.expect diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/simple_cases/comments9.py b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/comments9.py similarity index 100% rename from crates/ruff_python_formatter/resources/test/fixtures/black/simple_cases/comments9.py rename to crates/ruff_python_formatter/resources/test/fixtures/black/cases/comments9.py diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/simple_cases/comments9.py.expect b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/comments9.py.expect similarity index 100% rename from crates/ruff_python_formatter/resources/test/fixtures/black/simple_cases/comments9.py.expect rename to crates/ruff_python_formatter/resources/test/fixtures/black/cases/comments9.py.expect diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/comments_in_blocks.py b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/comments_in_blocks.py new file mode 100644 index 0000000000..1221139b6d --- /dev/null +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/comments_in_blocks.py @@ -0,0 +1,111 @@ +# Test cases from: +# - https://github.com/psf/black/issues/1798 +# - https://github.com/psf/black/issues/1499 +# - https://github.com/psf/black/issues/1211 +# - https://github.com/psf/black/issues/563 + +( + lambda + # a comment + : None +) + +( + lambda: + # b comment + None +) + +( + lambda + # a comment + : + # b comment + None +) + +[ + x + # Let's do this + for + # OK? + x + # Some comment + # And another + in + # One more + y +] + +return [ + (offers[offer_index], 1.0) + for offer_index, _ + # avoid returning any offers that don't match the grammar so + # that the return values here are consistent with what would be + # returned in AcceptValidHeader + in self._parse_and_normalize_offers(offers) +] + +from foo import ( + bar, + # qux +) + + +def convert(collection): + # replace all variables by integers + replacement_dict = { + variable: f"{index}" + for index, variable + # 0 is reserved as line terminator + in enumerate(collection.variables(), start=1) + } + + +{ + i: i + for i + # a comment + in range(5) +} + + +def get_subtree_proof_nodes( + chunk_index_groups: Sequence[Tuple[int, ...], ...], +) -> Tuple[int, ...]: + subtree_node_paths = ( + # We take a candidate element from each group and shift it to + # remove the bits that are not common to other group members, then + # we convert it to a tree path that all elements from this group + # have in common. + chunk_index + for chunk_index, bits_to_truncate + # Each group will contain an even "power-of-two" number of# elements. + # This tells us how many tailing bits each element has# which need to + # be truncated to get the group's common prefix. + in ((group[0], (len(group) - 1).bit_length()) for group in chunk_index_groups) + ) + return subtree_node_paths + + +if ( + # comment1 + a + # comment2 + or ( + # comment3 + ( + # comment4 + b + ) + # comment5 + and + # comment6 + c + or ( + # comment7 + d + ) + ) +): + print("Foo") diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/comments_in_blocks.py.expect b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/comments_in_blocks.py.expect new file mode 100644 index 0000000000..1221139b6d --- /dev/null +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/comments_in_blocks.py.expect @@ -0,0 +1,111 @@ +# Test cases from: +# - https://github.com/psf/black/issues/1798 +# - https://github.com/psf/black/issues/1499 +# - https://github.com/psf/black/issues/1211 +# - https://github.com/psf/black/issues/563 + +( + lambda + # a comment + : None +) + +( + lambda: + # b comment + None +) + +( + lambda + # a comment + : + # b comment + None +) + +[ + x + # Let's do this + for + # OK? + x + # Some comment + # And another + in + # One more + y +] + +return [ + (offers[offer_index], 1.0) + for offer_index, _ + # avoid returning any offers that don't match the grammar so + # that the return values here are consistent with what would be + # returned in AcceptValidHeader + in self._parse_and_normalize_offers(offers) +] + +from foo import ( + bar, + # qux +) + + +def convert(collection): + # replace all variables by integers + replacement_dict = { + variable: f"{index}" + for index, variable + # 0 is reserved as line terminator + in enumerate(collection.variables(), start=1) + } + + +{ + i: i + for i + # a comment + in range(5) +} + + +def get_subtree_proof_nodes( + chunk_index_groups: Sequence[Tuple[int, ...], ...], +) -> Tuple[int, ...]: + subtree_node_paths = ( + # We take a candidate element from each group and shift it to + # remove the bits that are not common to other group members, then + # we convert it to a tree path that all elements from this group + # have in common. + chunk_index + for chunk_index, bits_to_truncate + # Each group will contain an even "power-of-two" number of# elements. + # This tells us how many tailing bits each element has# which need to + # be truncated to get the group's common prefix. + in ((group[0], (len(group) - 1).bit_length()) for group in chunk_index_groups) + ) + return subtree_node_paths + + +if ( + # comment1 + a + # comment2 + or ( + # comment3 + ( + # comment4 + b + ) + # comment5 + and + # comment6 + c + or ( + # comment7 + d + ) + ) +): + print("Foo") diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/simple_cases/comments_non_breaking_space.py b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/comments_non_breaking_space.py similarity index 90% rename from crates/ruff_python_formatter/resources/test/fixtures/black/simple_cases/comments_non_breaking_space.py rename to crates/ruff_python_formatter/resources/test/fixtures/black/cases/comments_non_breaking_space.py index 0c5f5660eb..d1d42f0259 100644 --- a/crates/ruff_python_formatter/resources/test/fixtures/black/simple_cases/comments_non_breaking_space.py +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/comments_non_breaking_space.py @@ -14,6 +14,6 @@ def function(a:int=42): a b """ - #    There's a NBSP + 3 spaces before + #  There's a NBSP + 3 spaces before # And 4 spaces on the next line pass diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/simple_cases/comments_non_breaking_space.py.expect b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/comments_non_breaking_space.py.expect similarity index 100% rename from crates/ruff_python_formatter/resources/test/fixtures/black/simple_cases/comments_non_breaking_space.py.expect rename to crates/ruff_python_formatter/resources/test/fixtures/black/cases/comments_non_breaking_space.py.expect diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/simple_cases/composition.py b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/composition.py similarity index 100% rename from crates/ruff_python_formatter/resources/test/fixtures/black/simple_cases/composition.py rename to crates/ruff_python_formatter/resources/test/fixtures/black/cases/composition.py diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/simple_cases/composition.py.expect b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/composition.py.expect similarity index 100% rename from crates/ruff_python_formatter/resources/test/fixtures/black/simple_cases/composition.py.expect rename to crates/ruff_python_formatter/resources/test/fixtures/black/cases/composition.py.expect diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/simple_cases/composition_no_trailing_comma.py b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/composition_no_trailing_comma.py similarity index 100% rename from crates/ruff_python_formatter/resources/test/fixtures/black/simple_cases/composition_no_trailing_comma.py rename to crates/ruff_python_formatter/resources/test/fixtures/black/cases/composition_no_trailing_comma.py diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/simple_cases/composition_no_trailing_comma.py.expect b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/composition_no_trailing_comma.py.expect similarity index 100% rename from crates/ruff_python_formatter/resources/test/fixtures/black/simple_cases/composition_no_trailing_comma.py.expect rename to crates/ruff_python_formatter/resources/test/fixtures/black/cases/composition_no_trailing_comma.py.expect diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/conditional_expression.options.json b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/conditional_expression.options.json new file mode 100644 index 0000000000..ce7c52b163 --- /dev/null +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/conditional_expression.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/conditional_expression.py b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/conditional_expression.py new file mode 100644 index 0000000000..bbe56623c6 --- /dev/null +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/conditional_expression.py @@ -0,0 +1,67 @@ +long_kwargs_single_line = my_function( + foo="test, this is a sample value", + bar=some_long_value_name_foo_bar_baz if some_boolean_variable else some_fallback_value_foo_bar_baz, + baz="hello, this is a another value", +) + +multiline_kwargs_indented = my_function( + foo="test, this is a sample value", + bar=some_long_value_name_foo_bar_baz + if some_boolean_variable + else some_fallback_value_foo_bar_baz, + baz="hello, this is a another value", +) + +imploding_kwargs = my_function( + foo="test, this is a sample value", + bar=a + if foo + else b, + baz="hello, this is a another value", +) + +imploding_line = ( + 1 + if 1 + 1 == 2 + else 0 +) + +exploding_line = "hello this is a slightly long string" if some_long_value_name_foo_bar_baz else "this one is a little shorter" + +positional_argument_test(some_long_value_name_foo_bar_baz if some_boolean_variable else some_fallback_value_foo_bar_baz) + +def weird_default_argument(x=some_long_value_name_foo_bar_baz + if SOME_CONSTANT + else some_fallback_value_foo_bar_baz): + pass + +nested = "hello this is a slightly long string" if (some_long_value_name_foo_bar_baz if + nesting_test_expressions else some_fallback_value_foo_bar_baz) \ + else "this one is a little shorter" + +generator_expression = ( + some_long_value_name_foo_bar_baz if some_boolean_variable else some_fallback_value_foo_bar_baz for some_boolean_variable in some_iterable +) + + +def limit_offset_sql(self, low_mark, high_mark): + """Return LIMIT/OFFSET SQL clause.""" + limit, offset = self._get_limit_offset_params(low_mark, high_mark) + return " ".join( + sql + for sql in ( + "LIMIT %d" % limit if limit else None, + ("OFFSET %d" % offset) if offset else None, + ) + if sql + ) + + +def something(): + clone._iterable_class = ( + NamedValuesListIterable + if named + else FlatValuesListIterable + if flat + else ValuesListIterable + ) diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/conditional_expression.py.expect b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/conditional_expression.py.expect new file mode 100644 index 0000000000..122ea7860d --- /dev/null +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/conditional_expression.py.expect @@ -0,0 +1,90 @@ +long_kwargs_single_line = my_function( + foo="test, this is a sample value", + bar=( + some_long_value_name_foo_bar_baz + if some_boolean_variable + else some_fallback_value_foo_bar_baz + ), + baz="hello, this is a another value", +) + +multiline_kwargs_indented = my_function( + foo="test, this is a sample value", + bar=( + some_long_value_name_foo_bar_baz + if some_boolean_variable + else some_fallback_value_foo_bar_baz + ), + baz="hello, this is a another value", +) + +imploding_kwargs = my_function( + foo="test, this is a sample value", + bar=a if foo else b, + baz="hello, this is a another value", +) + +imploding_line = 1 if 1 + 1 == 2 else 0 + +exploding_line = ( + "hello this is a slightly long string" + if some_long_value_name_foo_bar_baz + else "this one is a little shorter" +) + +positional_argument_test( + some_long_value_name_foo_bar_baz + if some_boolean_variable + else some_fallback_value_foo_bar_baz +) + + +def weird_default_argument( + x=( + some_long_value_name_foo_bar_baz + if SOME_CONSTANT + else some_fallback_value_foo_bar_baz + ), +): + pass + + +nested = ( + "hello this is a slightly long string" + if ( + some_long_value_name_foo_bar_baz + if nesting_test_expressions + else some_fallback_value_foo_bar_baz + ) + else "this one is a little shorter" +) + +generator_expression = ( + ( + some_long_value_name_foo_bar_baz + if some_boolean_variable + else some_fallback_value_foo_bar_baz + ) + for some_boolean_variable in some_iterable +) + + +def limit_offset_sql(self, low_mark, high_mark): + """Return LIMIT/OFFSET SQL clause.""" + limit, offset = self._get_limit_offset_params(low_mark, high_mark) + return " ".join( + sql + for sql in ( + "LIMIT %d" % limit if limit else None, + ("OFFSET %d" % offset) if offset else None, + ) + if sql + ) + + +def something(): + clone._iterable_class = ( + NamedValuesListIterable + if named + else FlatValuesListIterable if flat else ValuesListIterable + ) diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/simple_cases/docstring.py b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/docstring.py similarity index 97% rename from crates/ruff_python_formatter/resources/test/fixtures/black/simple_cases/docstring.py rename to crates/ruff_python_formatter/resources/test/fixtures/black/cases/docstring.py index e1725f1f4f..c3e6b9fca1 100644 --- a/crates/ruff_python_formatter/resources/test/fixtures/black/simple_cases/docstring.py +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/docstring.py @@ -219,3 +219,10 @@ def stable_quote_normalization_with_immediate_inner_single_quote(self): ''' + + +def foo(): + """ + Docstring with a backslash followed by a space\ + and then another line + """ diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/simple_cases/docstring.py.expect b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/docstring.py.expect similarity index 97% rename from crates/ruff_python_formatter/resources/test/fixtures/black/simple_cases/docstring.py.expect rename to crates/ruff_python_formatter/resources/test/fixtures/black/cases/docstring.py.expect index a8f12a5b51..2259a0cf01 100644 --- a/crates/ruff_python_formatter/resources/test/fixtures/black/simple_cases/docstring.py.expect +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/docstring.py.expect @@ -217,3 +217,10 @@ def stable_quote_normalization_with_immediate_inner_single_quote(self): """ + + +def foo(): + """ + Docstring with a backslash followed by a space\ + and then another line + """ diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/simple_cases/docstring_no_extra_empty_line_before_eof.py b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/docstring_no_extra_empty_line_before_eof.py similarity index 100% rename from crates/ruff_python_formatter/resources/test/fixtures/black/simple_cases/docstring_no_extra_empty_line_before_eof.py rename to crates/ruff_python_formatter/resources/test/fixtures/black/cases/docstring_no_extra_empty_line_before_eof.py diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/simple_cases/docstring_no_extra_empty_line_before_eof.py.expect b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/docstring_no_extra_empty_line_before_eof.py.expect similarity index 100% rename from crates/ruff_python_formatter/resources/test/fixtures/black/simple_cases/docstring_no_extra_empty_line_before_eof.py.expect rename to crates/ruff_python_formatter/resources/test/fixtures/black/cases/docstring_no_extra_empty_line_before_eof.py.expect diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/miscellaneous/docstring_no_string_normalization.py b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/docstring_no_string_normalization.py similarity index 100% rename from crates/ruff_python_formatter/resources/test/fixtures/black/miscellaneous/docstring_no_string_normalization.py rename to crates/ruff_python_formatter/resources/test/fixtures/black/cases/docstring_no_string_normalization.py diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/miscellaneous/docstring_no_string_normalization.py.expect b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/docstring_no_string_normalization.py.expect similarity index 100% rename from crates/ruff_python_formatter/resources/test/fixtures/black/miscellaneous/docstring_no_string_normalization.py.expect rename to crates/ruff_python_formatter/resources/test/fixtures/black/cases/docstring_no_string_normalization.py.expect diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/simple_cases/docstring_preview.py b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/docstring_preview.py similarity index 100% rename from crates/ruff_python_formatter/resources/test/fixtures/black/simple_cases/docstring_preview.py rename to crates/ruff_python_formatter/resources/test/fixtures/black/cases/docstring_preview.py diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/simple_cases/docstring_preview.py.expect b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/docstring_preview.py.expect similarity index 98% rename from crates/ruff_python_formatter/resources/test/fixtures/black/simple_cases/docstring_preview.py.expect rename to crates/ruff_python_formatter/resources/test/fixtures/black/cases/docstring_preview.py.expect index dc453dcc76..859c295511 100644 --- a/crates/ruff_python_formatter/resources/test/fixtures/black/simple_cases/docstring_preview.py.expect +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/docstring_preview.py.expect @@ -3,7 +3,8 @@ def docstring_almost_at_line_limit(): def docstring_almost_at_line_limit_with_prefix(): - f"""long docstring................................................................""" + f"""long docstring................................................................ + """ def mulitline_docstring_almost_at_line_limit(): diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/simple_cases/empty_lines.py b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/empty_lines.py similarity index 100% rename from crates/ruff_python_formatter/resources/test/fixtures/black/simple_cases/empty_lines.py rename to crates/ruff_python_formatter/resources/test/fixtures/black/cases/empty_lines.py diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/simple_cases/empty_lines.py.expect b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/empty_lines.py.expect similarity index 100% rename from crates/ruff_python_formatter/resources/test/fixtures/black/simple_cases/empty_lines.py.expect rename to crates/ruff_python_formatter/resources/test/fixtures/black/cases/empty_lines.py.expect diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/simple_cases/expression.py b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/expression.py similarity index 100% rename from crates/ruff_python_formatter/resources/test/fixtures/black/simple_cases/expression.py rename to crates/ruff_python_formatter/resources/test/fixtures/black/cases/expression.py diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/simple_cases/expression.py.expect b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/expression.py.expect similarity index 100% rename from crates/ruff_python_formatter/resources/test/fixtures/black/simple_cases/expression.py.expect rename to crates/ruff_python_formatter/resources/test/fixtures/black/cases/expression.py.expect diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/f_docstring.py b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/f_docstring.py new file mode 100644 index 0000000000..88dab4840f --- /dev/null +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/f_docstring.py @@ -0,0 +1,8 @@ +def foo(e): + f""" {'.'.join(e)}""" + +def bar(e): + f"{'.'.join(e)}" + +def baz(e): + F""" {'.'.join(e)}""" diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/f_docstring.py.expect b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/f_docstring.py.expect new file mode 100644 index 0000000000..7081a229ed --- /dev/null +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/f_docstring.py.expect @@ -0,0 +1,10 @@ +def foo(e): + f""" {'.'.join(e)}""" + + +def bar(e): + f"{'.'.join(e)}" + + +def baz(e): + f""" {'.'.join(e)}""" diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/simple_cases/fmtonoff.py b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/fmtonoff.py similarity index 100% rename from crates/ruff_python_formatter/resources/test/fixtures/black/simple_cases/fmtonoff.py rename to crates/ruff_python_formatter/resources/test/fixtures/black/cases/fmtonoff.py diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/simple_cases/fmtonoff.py.expect b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/fmtonoff.py.expect similarity index 100% rename from crates/ruff_python_formatter/resources/test/fixtures/black/simple_cases/fmtonoff.py.expect rename to crates/ruff_python_formatter/resources/test/fixtures/black/cases/fmtonoff.py.expect diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/simple_cases/fmtonoff2.py b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/fmtonoff2.py similarity index 100% rename from crates/ruff_python_formatter/resources/test/fixtures/black/simple_cases/fmtonoff2.py rename to crates/ruff_python_formatter/resources/test/fixtures/black/cases/fmtonoff2.py diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/simple_cases/fmtonoff2.py.expect b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/fmtonoff2.py.expect similarity index 100% rename from crates/ruff_python_formatter/resources/test/fixtures/black/simple_cases/fmtonoff2.py.expect rename to crates/ruff_python_formatter/resources/test/fixtures/black/cases/fmtonoff2.py.expect diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/simple_cases/fmtonoff3.py b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/fmtonoff3.py similarity index 100% rename from crates/ruff_python_formatter/resources/test/fixtures/black/simple_cases/fmtonoff3.py rename to crates/ruff_python_formatter/resources/test/fixtures/black/cases/fmtonoff3.py diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/simple_cases/fmtonoff3.py.expect b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/fmtonoff3.py.expect similarity index 100% rename from crates/ruff_python_formatter/resources/test/fixtures/black/simple_cases/fmtonoff3.py.expect rename to crates/ruff_python_formatter/resources/test/fixtures/black/cases/fmtonoff3.py.expect diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/simple_cases/fmtonoff4.py b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/fmtonoff4.py similarity index 100% rename from crates/ruff_python_formatter/resources/test/fixtures/black/simple_cases/fmtonoff4.py rename to crates/ruff_python_formatter/resources/test/fixtures/black/cases/fmtonoff4.py diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/simple_cases/fmtonoff4.py.expect b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/fmtonoff4.py.expect similarity index 100% rename from crates/ruff_python_formatter/resources/test/fixtures/black/simple_cases/fmtonoff4.py.expect rename to crates/ruff_python_formatter/resources/test/fixtures/black/cases/fmtonoff4.py.expect diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/simple_cases/fmtonoff5.py b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/fmtonoff5.py similarity index 100% rename from crates/ruff_python_formatter/resources/test/fixtures/black/simple_cases/fmtonoff5.py rename to crates/ruff_python_formatter/resources/test/fixtures/black/cases/fmtonoff5.py diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/simple_cases/fmtonoff5.py.expect b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/fmtonoff5.py.expect similarity index 100% rename from crates/ruff_python_formatter/resources/test/fixtures/black/simple_cases/fmtonoff5.py.expect rename to crates/ruff_python_formatter/resources/test/fixtures/black/cases/fmtonoff5.py.expect diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/simple_cases/fmtpass_imports.py b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/fmtpass_imports.py similarity index 100% rename from crates/ruff_python_formatter/resources/test/fixtures/black/simple_cases/fmtpass_imports.py rename to crates/ruff_python_formatter/resources/test/fixtures/black/cases/fmtpass_imports.py diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/simple_cases/fmtpass_imports.py.expect b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/fmtpass_imports.py.expect similarity index 100% rename from crates/ruff_python_formatter/resources/test/fixtures/black/simple_cases/fmtpass_imports.py.expect rename to crates/ruff_python_formatter/resources/test/fixtures/black/cases/fmtpass_imports.py.expect diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/simple_cases/fmtskip.py b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/fmtskip.py similarity index 100% rename from crates/ruff_python_formatter/resources/test/fixtures/black/simple_cases/fmtskip.py rename to crates/ruff_python_formatter/resources/test/fixtures/black/cases/fmtskip.py diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/simple_cases/fmtskip.py.expect b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/fmtskip.py.expect similarity index 100% rename from crates/ruff_python_formatter/resources/test/fixtures/black/simple_cases/fmtskip.py.expect rename to crates/ruff_python_formatter/resources/test/fixtures/black/cases/fmtskip.py.expect diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/simple_cases/fmtskip2.py b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/fmtskip2.py similarity index 100% rename from crates/ruff_python_formatter/resources/test/fixtures/black/simple_cases/fmtskip2.py rename to crates/ruff_python_formatter/resources/test/fixtures/black/cases/fmtskip2.py diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/simple_cases/fmtskip2.py.expect b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/fmtskip2.py.expect similarity index 100% rename from crates/ruff_python_formatter/resources/test/fixtures/black/simple_cases/fmtskip2.py.expect rename to crates/ruff_python_formatter/resources/test/fixtures/black/cases/fmtskip2.py.expect diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/simple_cases/fmtskip3.py b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/fmtskip3.py similarity index 100% rename from crates/ruff_python_formatter/resources/test/fixtures/black/simple_cases/fmtskip3.py rename to crates/ruff_python_formatter/resources/test/fixtures/black/cases/fmtskip3.py diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/simple_cases/fmtskip3.py.expect b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/fmtskip3.py.expect similarity index 100% rename from crates/ruff_python_formatter/resources/test/fixtures/black/simple_cases/fmtskip3.py.expect rename to crates/ruff_python_formatter/resources/test/fixtures/black/cases/fmtskip3.py.expect diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/simple_cases/fmtskip4.py b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/fmtskip4.py similarity index 100% rename from crates/ruff_python_formatter/resources/test/fixtures/black/simple_cases/fmtskip4.py rename to crates/ruff_python_formatter/resources/test/fixtures/black/cases/fmtskip4.py diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/simple_cases/fmtskip4.py.expect b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/fmtskip4.py.expect similarity index 100% rename from crates/ruff_python_formatter/resources/test/fixtures/black/simple_cases/fmtskip4.py.expect rename to crates/ruff_python_formatter/resources/test/fixtures/black/cases/fmtskip4.py.expect diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/simple_cases/fmtskip5.py b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/fmtskip5.py similarity index 100% rename from crates/ruff_python_formatter/resources/test/fixtures/black/simple_cases/fmtskip5.py rename to crates/ruff_python_formatter/resources/test/fixtures/black/cases/fmtskip5.py diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/simple_cases/fmtskip5.py.expect b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/fmtskip5.py.expect similarity index 100% rename from crates/ruff_python_formatter/resources/test/fixtures/black/simple_cases/fmtskip5.py.expect rename to crates/ruff_python_formatter/resources/test/fixtures/black/cases/fmtskip5.py.expect diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/simple_cases/fmtskip6.py b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/fmtskip6.py similarity index 100% rename from crates/ruff_python_formatter/resources/test/fixtures/black/simple_cases/fmtskip6.py rename to crates/ruff_python_formatter/resources/test/fixtures/black/cases/fmtskip6.py diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/simple_cases/fmtskip6.py.expect b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/fmtskip6.py.expect similarity index 100% rename from crates/ruff_python_formatter/resources/test/fixtures/black/simple_cases/fmtskip6.py.expect rename to crates/ruff_python_formatter/resources/test/fixtures/black/cases/fmtskip6.py.expect diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/simple_cases/fmtskip7.py b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/fmtskip7.py similarity index 100% rename from crates/ruff_python_formatter/resources/test/fixtures/black/simple_cases/fmtskip7.py rename to crates/ruff_python_formatter/resources/test/fixtures/black/cases/fmtskip7.py diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/simple_cases/fmtskip7.py.expect b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/fmtskip7.py.expect similarity index 100% rename from crates/ruff_python_formatter/resources/test/fixtures/black/simple_cases/fmtskip7.py.expect rename to crates/ruff_python_formatter/resources/test/fixtures/black/cases/fmtskip7.py.expect diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/simple_cases/fmtskip8.py b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/fmtskip8.py similarity index 100% rename from crates/ruff_python_formatter/resources/test/fixtures/black/simple_cases/fmtskip8.py rename to crates/ruff_python_formatter/resources/test/fixtures/black/cases/fmtskip8.py diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/simple_cases/fmtskip8.py.expect b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/fmtskip8.py.expect similarity index 100% rename from crates/ruff_python_formatter/resources/test/fixtures/black/simple_cases/fmtskip8.py.expect rename to crates/ruff_python_formatter/resources/test/fixtures/black/cases/fmtskip8.py.expect diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/simple_cases/fstring.py b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/fstring.py similarity index 100% rename from crates/ruff_python_formatter/resources/test/fixtures/black/simple_cases/fstring.py rename to crates/ruff_python_formatter/resources/test/fixtures/black/cases/fstring.py diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/simple_cases/fstring.py.expect b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/fstring.py.expect similarity index 100% rename from crates/ruff_python_formatter/resources/test/fixtures/black/simple_cases/fstring.py.expect rename to crates/ruff_python_formatter/resources/test/fixtures/black/cases/fstring.py.expect 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 new file mode 100644 index 0000000000..ce7c52b163 --- /dev/null +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/funcdef_return_type_trailing_comma.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/funcdef_return_type_trailing_comma.py b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/funcdef_return_type_trailing_comma.py new file mode 100644 index 0000000000..16915fc220 --- /dev/null +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/funcdef_return_type_trailing_comma.py @@ -0,0 +1,143 @@ +# normal, short, function definition +def foo(a, b) -> tuple[int, float]: ... + + +# normal, short, function definition w/o return type +def foo(a, b): ... + + +# no splitting +def foo(a: A, b: B) -> list[p, q]: + pass + + +# magic trailing comma in param list +def foo(a, b,): ... + + +# magic trailing comma in nested params in param list +def foo(a, b: tuple[int, float,]): ... + + +# magic trailing comma in return type, no params +def a() -> tuple[ + a, + b, +]: ... + + +# magic trailing comma in return type, params +def foo(a: A, b: B) -> list[ + p, + q, +]: + pass + + +# magic trailing comma in param list and in return type +def foo( + a: a, + b: b, +) -> list[ + a, + a, +]: + pass + + +# long function definition, param list is longer +def aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa( + bbbbbbbbbbbbbbbbbb, +) -> cccccccccccccccccccccccccccccc: ... + + +# long function definition, return type is longer +# this should maybe split on rhs? +def aaaaaaaaaaaaaaaaa(bbbbbbbbbbbbbbbbbb) -> list[ + Ccccccccccccccccccccccccccccccccccccccccccccccccccc, Dddddd +]: ... + + +# long return type, no param list +def foo() -> list[ + Loooooooooooooooooooooooooooooooooooong, + Loooooooooooooooooooong, + Looooooooooooong, +]: ... + + +# long function name, no param list, no return value +def thiiiiiiiiiiiiiiiiiis_iiiiiiiiiiiiiiiiiiiiiiiiiiiiiis_veeeeeeeeeeeeeeeeeeeeeeery_looooooong(): + pass + + +# long function name, no param list +def thiiiiiiiiiiiiiiiiiis_iiiiiiiiiiiiiiiiiiiiiiiiiiiiiis_veeeeeeeeeeeeeeeeeeeeeeery_looooooong() -> ( + list[int, float] +): ... + + +# long function name, no return value +def thiiiiiiiiiiiiiiiiiis_iiiiiiiiiiiiiiiiiiiiiiiiiiiiiis_veeeeeeeeeeeeeeeeeeeeeeery_looooooong( + a, b +): ... + + +# unskippable type hint (??) +def foo(a) -> list[aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa, aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa]: # type: ignore + pass + + +def foo(a) -> list[ + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa, aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +]: # abpedeifnore + pass + +def foo(a, b: list[Bad],): ... # type: ignore + +# don't lose any comments (no magic) +def foo( # 1 + a, # 2 + b) -> list[ # 3 + a, # 4 + b]: # 5 + ... # 6 + + +# don't lose any comments (param list magic) +def foo( # 1 + a, # 2 + b,) -> list[ # 3 + a, # 4 + b]: # 5 + ... # 6 + + +# don't lose any comments (return type magic) +def foo( # 1 + a, # 2 + b) -> list[ # 3 + a, # 4 + b,]: # 5 + ... # 6 + + +# don't lose any comments (both magic) +def foo( # 1 + a, # 2 + b,) -> list[ # 3 + a, # 4 + b,]: # 5 + ... # 6 + +# real life example +def SimplePyFn( + context: hl.GeneratorContext, + buffer_input: Buffer[UInt8, 2], + func_input: Buffer[Int32, 2], + float_arg: Scalar[Float32], + offset: int = 0, +) -> tuple[ + Buffer[UInt8, 2], + Buffer[UInt8, 2], +]: ... diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/funcdef_return_type_trailing_comma.py.expect b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/funcdef_return_type_trailing_comma.py.expect new file mode 100644 index 0000000000..bb12b80672 --- /dev/null +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/funcdef_return_type_trailing_comma.py.expect @@ -0,0 +1,156 @@ +# normal, short, function definition +def foo(a, b) -> tuple[int, float]: ... + + +# normal, short, function definition w/o return type +def foo(a, b): ... + + +# no splitting +def foo(a: A, b: B) -> list[p, q]: + pass + + +# magic trailing comma in param list +def foo( + a, + b, +): ... + + +# magic trailing comma in nested params in param list +def foo( + a, + b: tuple[ + int, + float, + ], +): ... + + +# magic trailing comma in return type, no params +def a() -> tuple[ + a, + b, +]: ... + + +# magic trailing comma in return type, params +def foo(a: A, b: B) -> list[ + p, + q, +]: + pass + + +# magic trailing comma in param list and in return type +def foo( + a: a, + b: b, +) -> list[ + a, + a, +]: + pass + + +# long function definition, param list is longer +def aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa( + bbbbbbbbbbbbbbbbbb, +) -> cccccccccccccccccccccccccccccc: ... + + +# long function definition, return type is longer +# this should maybe split on rhs? +def aaaaaaaaaaaaaaaaa( + bbbbbbbbbbbbbbbbbb, +) -> list[Ccccccccccccccccccccccccccccccccccccccccccccccccccc, Dddddd]: ... + + +# long return type, no param list +def foo() -> list[ + Loooooooooooooooooooooooooooooooooooong, + Loooooooooooooooooooong, + Looooooooooooong, +]: ... + + +# long function name, no param list, no return value +def thiiiiiiiiiiiiiiiiiis_iiiiiiiiiiiiiiiiiiiiiiiiiiiiiis_veeeeeeeeeeeeeeeeeeeeeeery_looooooong(): + pass + + +# long function name, no param list +def thiiiiiiiiiiiiiiiiiis_iiiiiiiiiiiiiiiiiiiiiiiiiiiiiis_veeeeeeeeeeeeeeeeeeeeeeery_looooooong() -> ( + list[int, float] +): ... + + +# long function name, no return value +def thiiiiiiiiiiiiiiiiiis_iiiiiiiiiiiiiiiiiiiiiiiiiiiiiis_veeeeeeeeeeeeeeeeeeeeeeery_looooooong( + a, b +): ... + + +# unskippable type hint (??) +def foo(a) -> list[aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa, aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa]: # type: ignore + pass + + +def foo( + a, +) -> list[ + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa, aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +]: # abpedeifnore + pass + + +def foo( + a, + b: list[Bad], +): ... # type: ignore + + +# don't lose any comments (no magic) +def foo(a, b) -> list[a, b]: # 1 # 2 # 3 # 4 # 5 + ... # 6 + + +# don't lose any comments (param list magic) +def foo( # 1 + a, # 2 + b, +) -> list[a, b]: # 3 # 4 # 5 + ... # 6 + + +# don't lose any comments (return type magic) +def foo(a, b) -> list[ # 1 # 2 # 3 + a, # 4 + b, +]: # 5 + ... # 6 + + +# don't lose any comments (both magic) +def foo( # 1 + a, # 2 + b, +) -> list[ # 3 + a, # 4 + b, +]: # 5 + ... # 6 + + +# real life example +def SimplePyFn( + context: hl.GeneratorContext, + buffer_input: Buffer[UInt8, 2], + func_input: Buffer[Int32, 2], + float_arg: Scalar[Float32], + offset: int = 0, +) -> tuple[ + Buffer[UInt8, 2], + Buffer[UInt8, 2], +]: ... diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/simple_cases/function.py b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/function.py similarity index 100% rename from crates/ruff_python_formatter/resources/test/fixtures/black/simple_cases/function.py rename to crates/ruff_python_formatter/resources/test/fixtures/black/cases/function.py diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/simple_cases/function.py.expect b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/function.py.expect similarity index 100% rename from crates/ruff_python_formatter/resources/test/fixtures/black/simple_cases/function.py.expect rename to crates/ruff_python_formatter/resources/test/fixtures/black/cases/function.py.expect diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/simple_cases/function2.py b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/function2.py similarity index 100% rename from crates/ruff_python_formatter/resources/test/fixtures/black/simple_cases/function2.py rename to crates/ruff_python_formatter/resources/test/fixtures/black/cases/function2.py diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/simple_cases/function2.py.expect b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/function2.py.expect similarity index 100% rename from crates/ruff_python_formatter/resources/test/fixtures/black/simple_cases/function2.py.expect rename to crates/ruff_python_formatter/resources/test/fixtures/black/cases/function2.py.expect diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/simple_cases/function_trailing_comma.py b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/function_trailing_comma.py similarity index 100% rename from crates/ruff_python_formatter/resources/test/fixtures/black/simple_cases/function_trailing_comma.py rename to crates/ruff_python_formatter/resources/test/fixtures/black/cases/function_trailing_comma.py diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/simple_cases/function_trailing_comma.py.expect b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/function_trailing_comma.py.expect similarity index 100% rename from crates/ruff_python_formatter/resources/test/fixtures/black/simple_cases/function_trailing_comma.py.expect rename to crates/ruff_python_formatter/resources/test/fixtures/black/cases/function_trailing_comma.py.expect diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/simple_cases/ignore_pyi.py b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/ignore_pyi.pyi similarity index 71% rename from crates/ruff_python_formatter/resources/test/fixtures/black/simple_cases/ignore_pyi.py rename to crates/ruff_python_formatter/resources/test/fixtures/black/cases/ignore_pyi.pyi index 5e643ea38e..6423059067 100644 --- a/crates/ruff_python_formatter/resources/test/fixtures/black/simple_cases/ignore_pyi.py +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/ignore_pyi.pyi @@ -15,6 +15,7 @@ def g(): # hi ... -def h(): - ... - # bye +# FIXME(#8905): Uncomment, leads to unstable formatting +# def h(): +# ... +# # bye diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/simple_cases/ignore_pyi.py.expect b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/ignore_pyi.pyi.expect similarity index 100% rename from crates/ruff_python_formatter/resources/test/fixtures/black/simple_cases/ignore_pyi.py.expect rename to crates/ruff_python_formatter/resources/test/fixtures/black/cases/ignore_pyi.pyi.expect diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/simple_cases/import_spacing.py b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/import_spacing.py similarity index 100% rename from crates/ruff_python_formatter/resources/test/fixtures/black/simple_cases/import_spacing.py rename to crates/ruff_python_formatter/resources/test/fixtures/black/cases/import_spacing.py diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/simple_cases/import_spacing.py.expect b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/import_spacing.py.expect similarity index 100% rename from crates/ruff_python_formatter/resources/test/fixtures/black/simple_cases/import_spacing.py.expect rename to crates/ruff_python_formatter/resources/test/fixtures/black/cases/import_spacing.py.expect 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 new file mode 100644 index 0000000000..40d0f7053b --- /dev/null +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/line_ranges_basic.py @@ -0,0 +1,40 @@ +# NOTE: If you need to modify this file, pay special attention to the --line-ranges= +# flag above as it's formatting specifically these lines. +def foo1(parameter_1, parameter_2, parameter_3, parameter_4, parameter_5, parameter_6, parameter_7): pass +def foo2(parameter_1, parameter_2, parameter_3, parameter_4, parameter_5, parameter_6, parameter_7): pass +def foo3(parameter_1, parameter_2, parameter_3, parameter_4, parameter_5, parameter_6, parameter_7): pass +def foo4(parameter_1, parameter_2, parameter_3, parameter_4, parameter_5, parameter_6, parameter_7): pass + +# Adding some unformated code covering a wide range of syntaxes. + +if True: + # Incorrectly indented prefix comments. + pass + +import typing +from typing import ( + Any , + ) +class MyClass( object): # Trailing comment with extra leading space. + #NOTE: The following indentation is incorrect: + @decor( 1 * 3 ) + def my_func( arg): + pass + +try: # Trailing comment with extra leading space. + for i in range(10): # Trailing comment with extra leading space. + while condition: + if something: + then_something( ) + elif something_else: + then_something_else( ) +except ValueError as e: + unformatted( ) +finally: + unformatted( ) + +async def test_async_unformatted( ): # Trailing comment with extra leading space. + async for i in some_iter( unformatted ): # Trailing comment with extra leading space. + await asyncio.sleep( 1 ) + async with some_context( unformatted ): + print( "unformatted" ) 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 new file mode 100644 index 0000000000..7fdfdfd0db --- /dev/null +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/line_ranges_basic.py.expect @@ -0,0 +1,63 @@ +# flags: --line-ranges=5-6 +# NOTE: If you need to modify this file, pay special attention to the --line-ranges= +# flag above as it's formatting specifically these lines. +def foo1(parameter_1, parameter_2, parameter_3, parameter_4, parameter_5, parameter_6, parameter_7): pass +def foo2( + parameter_1, + parameter_2, + parameter_3, + parameter_4, + parameter_5, + parameter_6, + parameter_7, +): + pass + + +def foo3( + parameter_1, + parameter_2, + parameter_3, + parameter_4, + parameter_5, + parameter_6, + parameter_7, +): + pass + + +def foo4(parameter_1, parameter_2, parameter_3, parameter_4, parameter_5, parameter_6, parameter_7): pass + +# Adding some unformated code covering a wide range of syntaxes. + +if True: + # Incorrectly indented prefix comments. + pass + +import typing +from typing import ( + Any , + ) +class MyClass( object): # Trailing comment with extra leading space. + #NOTE: The following indentation is incorrect: + @decor( 1 * 3 ) + def my_func( arg): + pass + +try: # Trailing comment with extra leading space. + for i in range(10): # Trailing comment with extra leading space. + while condition: + if something: + then_something( ) + elif something_else: + then_something_else( ) +except ValueError as e: + unformatted( ) +finally: + unformatted( ) + +async def test_async_unformatted( ): # Trailing comment with extra leading space. + async for i in some_iter( unformatted ): # Trailing comment with extra leading space. + await asyncio.sleep( 1 ) + async with some_context( unformatted ): + print( "unformatted" ) diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/line_ranges_diff_edge_case.py b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/line_ranges_diff_edge_case.py new file mode 100644 index 0000000000..17613eb8db --- /dev/null +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/line_ranges_diff_edge_case.py @@ -0,0 +1,12 @@ +# NOTE: If you need to modify this file, pay special attention to the --line-ranges= +# flag above as it's formatting specifically these lines. + +# Reproducible example for https://github.com/psf/black/issues/4033. +# This can be fixed in the future if we use a better diffing algorithm, or make Black +# perform formatting in a single pass. + +print ( "format me" ) +print ( "format me" ) +print ( "format me" ) +print ( "format me" ) +print ( "format me" ) diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/line_ranges_diff_edge_case.py.expect b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/line_ranges_diff_edge_case.py.expect new file mode 100644 index 0000000000..96106c6988 --- /dev/null +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/line_ranges_diff_edge_case.py.expect @@ -0,0 +1,13 @@ +# flags: --line-ranges=10-11 +# NOTE: If you need to modify this file, pay special attention to the --line-ranges= +# flag above as it's formatting specifically these lines. + +# Reproducible example for https://github.com/psf/black/issues/4033. +# This can be fixed in the future if we use a better diffing algorithm, or make Black +# perform formatting in a single pass. + +print ( "format me" ) +print("format me") +print("format me") +print("format me") +print("format me") diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/line_ranges_fmt_off.py b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/line_ranges_fmt_off.py new file mode 100644 index 0000000000..bcb36284a8 --- /dev/null +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/line_ranges_fmt_off.py @@ -0,0 +1,22 @@ +# NOTE: If you need to modify this file, pay special attention to the --line-ranges= +# flag above as it's formatting specifically these lines. + +# fmt: off +import os +def myfunc( ): # Intentionally unformatted. + pass +# fmt: on + + +def myfunc( ): # This will not be reformatted. + print( {"also won't be reformatted"} ) +# fmt: off +def myfunc( ): # This will not be reformatted. + print( {"also won't be reformatted"} ) +def myfunc( ): # This will not be reformatted. + print( {"also won't be reformatted"} ) +# fmt: on + + +def myfunc( ): # This will be reformatted. + print( {"this will be reformatted"} ) diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/line_ranges_fmt_off.py.expect b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/line_ranges_fmt_off.py.expect new file mode 100644 index 0000000000..03cd50db75 --- /dev/null +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/line_ranges_fmt_off.py.expect @@ -0,0 +1,23 @@ +# flags: --line-ranges=7-7 --line-ranges=17-23 +# NOTE: If you need to modify this file, pay special attention to the --line-ranges= +# flag above as it's formatting specifically these lines. + +# fmt: off +import os +def myfunc( ): # Intentionally unformatted. + pass +# fmt: on + + +def myfunc( ): # This will not be reformatted. + print( {"also won't be reformatted"} ) +# fmt: off +def myfunc( ): # This will not be reformatted. + print( {"also won't be reformatted"} ) +def myfunc( ): # This will not be reformatted. + print( {"also won't be reformatted"} ) +# fmt: on + + +def myfunc(): # This will be reformatted. + print({"this will be reformatted"}) diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/line_ranges_fmt_off_decorator.py b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/line_ranges_fmt_off_decorator.py new file mode 100644 index 0000000000..3c9e616d83 --- /dev/null +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/line_ranges_fmt_off_decorator.py @@ -0,0 +1,11 @@ +# NOTE: If you need to modify this file, pay special attention to the --line-ranges= +# flag above as it's formatting specifically these lines. + +# Regression test for an edge case involving decorators and fmt: off/on. +class MyClass: + + # fmt: off + @decorator ( ) + # fmt: on + def method(): + print ( "str" ) diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/line_ranges_fmt_off_decorator.py.expect b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/line_ranges_fmt_off_decorator.py.expect new file mode 100644 index 0000000000..326b48df6b --- /dev/null +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/line_ranges_fmt_off_decorator.py.expect @@ -0,0 +1,12 @@ +# flags: --line-ranges=12-12 +# NOTE: If you need to modify this file, pay special attention to the --line-ranges= +# flag above as it's formatting specifically these lines. + +# Regression test for an edge case involving decorators and fmt: off/on. +class MyClass: + + # fmt: off + @decorator ( ) + # fmt: on + def method(): + print("str") diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/line_ranges_fmt_off_overlap.py b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/line_ranges_fmt_off_overlap.py new file mode 100644 index 0000000000..4d3db25824 --- /dev/null +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/line_ranges_fmt_off_overlap.py @@ -0,0 +1,16 @@ +# NOTE: If you need to modify this file, pay special attention to the --line-ranges= +# flag above as it's formatting specifically these lines. + + +def myfunc( ): # This will not be reformatted. + print( {"also won't be reformatted"} ) +# fmt: off +def myfunc( ): # This will not be reformatted. + print( {"also won't be reformatted"} ) +def myfunc( ): # This will not be reformatted. + print( {"also won't be reformatted"} ) +# fmt: on + + +def myfunc( ): # This will be reformatted. + print( {"this will be reformatted"} ) diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/line_ranges_fmt_off_overlap.py.expect b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/line_ranges_fmt_off_overlap.py.expect new file mode 100644 index 0000000000..fa7c683b3a --- /dev/null +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/line_ranges_fmt_off_overlap.py.expect @@ -0,0 +1,17 @@ +# flags: --line-ranges=11-17 +# NOTE: If you need to modify this file, pay special attention to the --line-ranges= +# flag above as it's formatting specifically these lines. + + +def myfunc( ): # This will not be reformatted. + print( {"also won't be reformatted"} ) +# fmt: off +def myfunc( ): # This will not be reformatted. + print( {"also won't be reformatted"} ) +def myfunc( ): # This will not be reformatted. + print( {"also won't be reformatted"} ) +# fmt: on + + +def myfunc(): # This will be reformatted. + print({"this will be reformatted"}) diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/line_ranges_imports.py b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/line_ranges_imports.py new file mode 100644 index 0000000000..0e516f2822 --- /dev/null +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/line_ranges_imports.py @@ -0,0 +1,8 @@ +# NOTE: If you need to modify this file, pay special attention to the --line-ranges= +# flag above as it's formatting specifically these lines. + +# This test ensures no empty lines are added around import lines. +# It caused an issue before https://github.com/psf/black/pull/3610 is merged. +import os +import re +import sys diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/line_ranges_imports.py.expect b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/line_ranges_imports.py.expect new file mode 100644 index 0000000000..0e516f2822 --- /dev/null +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/line_ranges_imports.py.expect @@ -0,0 +1,8 @@ +# NOTE: If you need to modify this file, pay special attention to the --line-ranges= +# flag above as it's formatting specifically these lines. + +# This test ensures no empty lines are added around import lines. +# It caused an issue before https://github.com/psf/black/pull/3610 is merged. +import os +import re +import sys diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/line_ranges_indentation.py b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/line_ranges_indentation.py new file mode 100644 index 0000000000..ee2269d061 --- /dev/null +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/line_ranges_indentation.py @@ -0,0 +1,11 @@ +# NOTE: If you need to modify this file, pay special attention to the --line-ranges= +# flag above as it's formatting specifically these lines. +if cond1: + print("first") + if cond2: + print("second") + else: + print("else") + +if another_cond: + print("will not be changed") diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/line_ranges_indentation.py.expect b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/line_ranges_indentation.py.expect new file mode 100644 index 0000000000..e6265c1ded --- /dev/null +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/line_ranges_indentation.py.expect @@ -0,0 +1,12 @@ +# flags: --line-ranges=5-5 +# NOTE: If you need to modify this file, pay special attention to the --line-ranges= +# flag above as it's formatting specifically these lines. +if cond1: + print("first") + if cond2: + print("second") + else: + print("else") + +if another_cond: + print("will not be changed") diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/line_ranges_two_passes.py b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/line_ranges_two_passes.py new file mode 100644 index 0000000000..c8a57fdf77 --- /dev/null +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/line_ranges_two_passes.py @@ -0,0 +1,12 @@ +# NOTE: If you need to modify this file, pay special attention to the --line-ranges= +# flag above as it's formatting specifically these lines. + +# This is a specific case for Black's two-pass formatting behavior in `format_str`. +# The second pass must respect the line ranges before the first pass. + + +def restrict_to_this_line(arg1, + arg2, + arg3): + print ( "This should not be formatted." ) + print ( "Note that in the second pass, the original line range 9-11 will cover these print lines.") diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/line_ranges_two_passes.py.expect b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/line_ranges_two_passes.py.expect new file mode 100644 index 0000000000..593ef6d331 --- /dev/null +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/line_ranges_two_passes.py.expect @@ -0,0 +1,11 @@ +# flags: --line-ranges=9-11 +# NOTE: If you need to modify this file, pay special attention to the --line-ranges= +# flag above as it's formatting specifically these lines. + +# This is a specific case for Black's two-pass formatting behavior in `format_str`. +# The second pass must respect the line ranges before the first pass. + + +def restrict_to_this_line(arg1, arg2, arg3): + print ( "This should not be formatted." ) + print ( "Note that in the second pass, the original line range 9-11 will cover these print lines.") diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/line_ranges_unwrapping.py b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/line_ranges_unwrapping.py new file mode 100644 index 0000000000..c5db777884 --- /dev/null +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/line_ranges_unwrapping.py @@ -0,0 +1,13 @@ +# NOTE: If you need to modify this file, pay special attention to the --line-ranges= +# flag above as it's formatting specifically these lines. +alist = [ + 1, 2 +] + +adict = { + "key" : "value" +} + +func_call ( + arg = value +) diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/line_ranges_unwrapping.py.expect b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/line_ranges_unwrapping.py.expect new file mode 100644 index 0000000000..4d8de0a45d --- /dev/null +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/line_ranges_unwrapping.py.expect @@ -0,0 +1,8 @@ +# flags: --line-ranges=5-5 --line-ranges=9-9 --line-ranges=13-13 +# NOTE: If you need to modify this file, pay special attention to the --line-ranges= +# flag above as it's formatting specifically these lines. +alist = [1, 2] + +adict = {"key": "value"} + +func_call(arg=value) diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/linelength6.options.json b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/linelength6.options.json new file mode 100644 index 0000000000..f6d0b5fa4c --- /dev/null +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/linelength6.options.json @@ -0,0 +1 @@ +{"line_length": 6} \ No newline at end of file diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/miscellaneous/linelength6.py b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/linelength6.py similarity index 100% rename from crates/ruff_python_formatter/resources/test/fixtures/black/miscellaneous/linelength6.py rename to crates/ruff_python_formatter/resources/test/fixtures/black/cases/linelength6.py diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/miscellaneous/linelength6.py.expect b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/linelength6.py.expect similarity index 100% rename from crates/ruff_python_formatter/resources/test/fixtures/black/miscellaneous/linelength6.py.expect rename to crates/ruff_python_formatter/resources/test/fixtures/black/cases/linelength6.py.expect diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/miscellaneous/long_strings_flag_disabled.py b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/long_strings_flag_disabled.py similarity index 100% rename from crates/ruff_python_formatter/resources/test/fixtures/black/miscellaneous/long_strings_flag_disabled.py rename to crates/ruff_python_formatter/resources/test/fixtures/black/cases/long_strings_flag_disabled.py diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/miscellaneous/long_strings_flag_disabled.py.expect b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/long_strings_flag_disabled.py.expect similarity index 100% rename from crates/ruff_python_formatter/resources/test/fixtures/black/miscellaneous/long_strings_flag_disabled.py.expect rename to crates/ruff_python_formatter/resources/test/fixtures/black/cases/long_strings_flag_disabled.py.expect diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/module_docstring_1.options.json b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/module_docstring_1.options.json new file mode 100644 index 0000000000..ce7c52b163 --- /dev/null +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/module_docstring_1.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/module_docstring_1.py b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/module_docstring_1.py new file mode 100644 index 0000000000..6868801be3 --- /dev/null +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/module_docstring_1.py @@ -0,0 +1,14 @@ +"""Single line module-level docstring should be followed by single newline.""" + + + + +a = 1 + + +"""I'm just a string so should be followed by 2 newlines.""" + + + + +b = 2 diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/module_docstring_1.py.expect b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/module_docstring_1.py.expect new file mode 100644 index 0000000000..09fc6076ab --- /dev/null +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/module_docstring_1.py.expect @@ -0,0 +1,9 @@ +"""Single line module-level docstring should be followed by single newline.""" + +a = 1 + + +"""I'm just a string so should be followed by 2 newlines.""" + + +b = 2 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/module_docstring_2.options.json new file mode 100644 index 0000000000..ce7c52b163 --- /dev/null +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/module_docstring_2.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/module_docstring_2.py b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/module_docstring_2.py new file mode 100644 index 0000000000..48be31c41c --- /dev/null +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/module_docstring_2.py @@ -0,0 +1,35 @@ +"""I am a very helpful module docstring. + +Lorem ipsum dolor sit amet, consectetur adipiscing elit, +sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. +Ut enim ad minim veniam, +quis nostrud exercitation ullamco laboris +nisi ut aliquip ex ea commodo consequat. +Duis aute irure dolor in reprehenderit in voluptate +velit esse cillum dolore eu fugiat nulla pariatur. +Excepteur sint occaecat cupidatat non proident, +sunt in culpa qui officia deserunt mollit anim id est laborum. +""" + + + + +a = 1 + + +"""Look at me I'm a docstring... + +............................................................ +............................................................ +............................................................ +............................................................ +............................................................ +............................................................ +............................................................ +........................................................NOT! +""" + + + + +b = 2 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 new file mode 100644 index 0000000000..6c07d2d169 --- /dev/null +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/module_docstring_2.py.expect @@ -0,0 +1,30 @@ +"""I am a very helpful module docstring. + +Lorem ipsum dolor sit amet, consectetur adipiscing elit, +sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. +Ut enim ad minim veniam, +quis nostrud exercitation ullamco laboris +nisi ut aliquip ex ea commodo consequat. +Duis aute irure dolor in reprehenderit in voluptate +velit esse cillum dolore eu fugiat nulla pariatur. +Excepteur sint occaecat cupidatat non proident, +sunt in culpa qui officia deserunt mollit anim id est laborum. +""" + +a = 1 + + +"""Look at me I'm a docstring... + +............................................................ +............................................................ +............................................................ +............................................................ +............................................................ +............................................................ +............................................................ +........................................................NOT! +""" + + +b = 2 diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/module_docstring_3.options.json b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/module_docstring_3.options.json new file mode 100644 index 0000000000..ce7c52b163 --- /dev/null +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/module_docstring_3.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/module_docstring_3.py b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/module_docstring_3.py new file mode 100644 index 0000000000..f785054cef --- /dev/null +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/module_docstring_3.py @@ -0,0 +1,2 @@ +"""Single line module-level docstring should be followed by single newline.""" +a = 1 diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/module_docstring_3.py.expect b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/module_docstring_3.py.expect new file mode 100644 index 0000000000..98e7364384 --- /dev/null +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/module_docstring_3.py.expect @@ -0,0 +1,3 @@ +"""Single line module-level docstring should be followed by single newline.""" + +a = 1 diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/module_docstring_4.options.json b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/module_docstring_4.options.json new file mode 100644 index 0000000000..ce7c52b163 --- /dev/null +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/module_docstring_4.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/module_docstring_4.py b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/module_docstring_4.py new file mode 100644 index 0000000000..98e7364384 --- /dev/null +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/module_docstring_4.py @@ -0,0 +1,3 @@ +"""Single line module-level docstring should be followed by single newline.""" + +a = 1 diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/module_docstring_4.py.expect b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/module_docstring_4.py.expect new file mode 100644 index 0000000000..98e7364384 --- /dev/null +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/module_docstring_4.py.expect @@ -0,0 +1,3 @@ +"""Single line module-level docstring should be followed by single newline.""" + +a = 1 diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/module_docstring_followed_by_class.options.json b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/module_docstring_followed_by_class.options.json new file mode 100644 index 0000000000..ce7c52b163 --- /dev/null +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/module_docstring_followed_by_class.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/module_docstring_followed_by_class.py b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/module_docstring_followed_by_class.py new file mode 100644 index 0000000000..5a10eeb881 --- /dev/null +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/module_docstring_followed_by_class.py @@ -0,0 +1,3 @@ +"""Two blank lines between module docstring and a class.""" +class MyClass: + pass diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/module_docstring_followed_by_class.py.expect b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/module_docstring_followed_by_class.py.expect new file mode 100644 index 0000000000..5761185532 --- /dev/null +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/module_docstring_followed_by_class.py.expect @@ -0,0 +1,5 @@ +"""Two blank lines between module docstring and a class.""" + + +class MyClass: + pass diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/module_docstring_followed_by_function.options.json b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/module_docstring_followed_by_function.options.json new file mode 100644 index 0000000000..ce7c52b163 --- /dev/null +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/module_docstring_followed_by_function.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/module_docstring_followed_by_function.py b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/module_docstring_followed_by_function.py new file mode 100644 index 0000000000..5d8a52a85b --- /dev/null +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/module_docstring_followed_by_function.py @@ -0,0 +1,3 @@ +"""Two blank lines between module docstring and a function def.""" +def function(): + pass diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/module_docstring_followed_by_function.py.expect b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/module_docstring_followed_by_function.py.expect new file mode 100644 index 0000000000..73ac8469ce --- /dev/null +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/module_docstring_followed_by_function.py.expect @@ -0,0 +1,5 @@ +"""Two blank lines between module docstring and a function def.""" + + +def function(): + pass diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/simple_cases/multiline_consecutive_open_parentheses_ignore.py b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/multiline_consecutive_open_parentheses_ignore.py similarity index 100% rename from crates/ruff_python_formatter/resources/test/fixtures/black/simple_cases/multiline_consecutive_open_parentheses_ignore.py rename to crates/ruff_python_formatter/resources/test/fixtures/black/cases/multiline_consecutive_open_parentheses_ignore.py diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/simple_cases/multiline_consecutive_open_parentheses_ignore.py.expect b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/multiline_consecutive_open_parentheses_ignore.py.expect similarity index 100% rename from crates/ruff_python_formatter/resources/test/fixtures/black/simple_cases/multiline_consecutive_open_parentheses_ignore.py.expect rename to crates/ruff_python_formatter/resources/test/fixtures/black/cases/multiline_consecutive_open_parentheses_ignore.py.expect diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/nested_stub.options.json b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/nested_stub.options.json new file mode 100644 index 0000000000..ce7c52b163 --- /dev/null +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/nested_stub.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/nested_stub.pyi b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/nested_stub.pyi new file mode 100644 index 0000000000..2cb19a8ba0 --- /dev/null +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/nested_stub.pyi @@ -0,0 +1,18 @@ +import sys + +class Outer: + class InnerStub: ... + outer_attr_after_inner_stub: int + class Inner: + inner_attr: int + outer_attr: int + +if sys.version_info > (3, 7): + if sys.platform == "win32": + assignment = 1 + def function_definition(self): ... + def f1(self) -> str: ... + if sys.platform != "win32": + def function_definition(self): ... + assignment = 1 + def f2(self) -> str: ... diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/nested_stub.pyi.expect b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/nested_stub.pyi.expect new file mode 100644 index 0000000000..da431eb849 --- /dev/null +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/nested_stub.pyi.expect @@ -0,0 +1,22 @@ +import sys + +class Outer: + class InnerStub: ... + outer_attr_after_inner_stub: int + + class Inner: + inner_attr: int + + outer_attr: int + +if sys.version_info > (3, 7): + if sys.platform == "win32": + assignment = 1 + def function_definition(self): ... + + def f1(self) -> str: ... + if sys.platform != "win32": + def function_definition(self): ... + assignment = 1 + + def f2(self) -> str: ... diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/py_36/numeric_literals.py b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/numeric_literals.py similarity index 91% rename from crates/ruff_python_formatter/resources/test/fixtures/black/py_36/numeric_literals.py rename to crates/ruff_python_formatter/resources/test/fixtures/black/cases/numeric_literals.py index 6da4ba68d6..0aa2aef4eb 100644 --- a/crates/ruff_python_formatter/resources/test/fixtures/black/py_36/numeric_literals.py +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/numeric_literals.py @@ -1,5 +1,3 @@ -#!/usr/bin/env python3.6 - x = 123456789 x = 123456 x = .1 diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/py_36/numeric_literals.py.expect b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/numeric_literals.py.expect similarity index 91% rename from crates/ruff_python_formatter/resources/test/fixtures/black/py_36/numeric_literals.py.expect rename to crates/ruff_python_formatter/resources/test/fixtures/black/cases/numeric_literals.py.expect index e263924b4e..6514de6cb9 100644 --- a/crates/ruff_python_formatter/resources/test/fixtures/black/py_36/numeric_literals.py.expect +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/numeric_literals.py.expect @@ -1,5 +1,3 @@ -#!/usr/bin/env python3.6 - x = 123456789 x = 123456 x = 0.1 diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/py_36/numeric_literals_skip_underscores.py b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/numeric_literals_skip_underscores.py similarity index 79% rename from crates/ruff_python_formatter/resources/test/fixtures/black/py_36/numeric_literals_skip_underscores.py rename to crates/ruff_python_formatter/resources/test/fixtures/black/cases/numeric_literals_skip_underscores.py index d77116a832..9e8f89736c 100644 --- a/crates/ruff_python_formatter/resources/test/fixtures/black/py_36/numeric_literals_skip_underscores.py +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/numeric_literals_skip_underscores.py @@ -1,5 +1,3 @@ -#!/usr/bin/env python3.6 - x = 123456789 x = 1_2_3_4_5_6_7 x = 1E+1 diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/py_36/numeric_literals_skip_underscores.py.expect b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/numeric_literals_skip_underscores.py.expect similarity index 79% rename from crates/ruff_python_formatter/resources/test/fixtures/black/py_36/numeric_literals_skip_underscores.py.expect rename to crates/ruff_python_formatter/resources/test/fixtures/black/cases/numeric_literals_skip_underscores.py.expect index a81ada11e5..e57b5f0944 100644 --- a/crates/ruff_python_formatter/resources/test/fixtures/black/py_36/numeric_literals_skip_underscores.py.expect +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/numeric_literals_skip_underscores.py.expect @@ -1,5 +1,3 @@ -#!/usr/bin/env python3.6 - x = 123456789 x = 1_2_3_4_5_6_7 x = 1e1 diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/simple_cases/one_element_subscript.py b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/one_element_subscript.py similarity index 100% rename from crates/ruff_python_formatter/resources/test/fixtures/black/simple_cases/one_element_subscript.py rename to crates/ruff_python_formatter/resources/test/fixtures/black/cases/one_element_subscript.py diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/simple_cases/one_element_subscript.py.expect b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/one_element_subscript.py.expect similarity index 100% rename from crates/ruff_python_formatter/resources/test/fixtures/black/simple_cases/one_element_subscript.py.expect rename to crates/ruff_python_formatter/resources/test/fixtures/black/cases/one_element_subscript.py.expect diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/py_310/parenthesized_context_managers.py b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/parenthesized_context_managers.py similarity index 100% rename from crates/ruff_python_formatter/resources/test/fixtures/black/py_310/parenthesized_context_managers.py rename to crates/ruff_python_formatter/resources/test/fixtures/black/cases/parenthesized_context_managers.py diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/py_310/parenthesized_context_managers.py.expect b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/parenthesized_context_managers.py.expect similarity index 100% rename from crates/ruff_python_formatter/resources/test/fixtures/black/py_310/parenthesized_context_managers.py.expect rename to crates/ruff_python_formatter/resources/test/fixtures/black/cases/parenthesized_context_managers.py.expect diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/py_310/pattern_matching_complex.py b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/pattern_matching_complex.py similarity index 90% rename from crates/ruff_python_formatter/resources/test/fixtures/black/py_310/pattern_matching_complex.py rename to crates/ruff_python_formatter/resources/test/fixtures/black/cases/pattern_matching_complex.py index 97ee194fd3..60c80b9ea1 100644 --- a/crates/ruff_python_formatter/resources/test/fixtures/black/py_310/pattern_matching_complex.py +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/pattern_matching_complex.py @@ -142,3 +142,13 @@ match x: y = 1 case []: y = 2 +# issue 3790 +match (X.type, Y): + case _: + pass +# issue 3487 +match = re.match(r"(?PLD|MD|HD)(?PAL|SS)", "HDSS") + +match (match.group("grade"), match.group("material")): + case ("MD" | "HD", "SS" as code): + print("You will get here") diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/py_310/pattern_matching_complex.py.expect b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/pattern_matching_complex.py.expect similarity index 90% rename from crates/ruff_python_formatter/resources/test/fixtures/black/py_310/pattern_matching_complex.py.expect rename to crates/ruff_python_formatter/resources/test/fixtures/black/cases/pattern_matching_complex.py.expect index 97ee194fd3..60c80b9ea1 100644 --- a/crates/ruff_python_formatter/resources/test/fixtures/black/py_310/pattern_matching_complex.py.expect +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/pattern_matching_complex.py.expect @@ -142,3 +142,13 @@ match x: y = 1 case []: y = 2 +# issue 3790 +match (X.type, Y): + case _: + pass +# issue 3487 +match = re.match(r"(?PLD|MD|HD)(?PAL|SS)", "HDSS") + +match (match.group("grade"), match.group("material")): + case ("MD" | "HD", "SS" as code): + print("You will get here") diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/py_310/pattern_matching_extras.py b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/pattern_matching_extras.py similarity index 88% rename from crates/ruff_python_formatter/resources/test/fixtures/black/py_310/pattern_matching_extras.py rename to crates/ruff_python_formatter/resources/test/fixtures/black/cases/pattern_matching_extras.py index 0242d264e5..5afe69920e 100644 --- a/crates/ruff_python_formatter/resources/test/fixtures/black/py_310/pattern_matching_extras.py +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/pattern_matching_extras.py @@ -29,22 +29,6 @@ def func(match: case, case: match) -> case: ... -match maybe, multiple: - case perhaps, 5: - pass - case perhaps, 6,: - pass - - -match more := (than, one), indeed,: - case _, (5, 6): - pass - case [[5], (6)], [7],: - pass - case _: - pass - - match a, *b, c: case [*_]: assert "seq" == _ @@ -66,12 +50,12 @@ match match( ), ): pass - case [a as match]: pass - case case: pass + case something: + pass match match: @@ -97,10 +81,8 @@ match something: match something: case 1 as a: pass - case 2 as b, 3 as c: pass - case 4 as d, (5 as e), (6 | 7 as g), *h: pass diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/py_310/pattern_matching_extras.py.expect b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/pattern_matching_extras.py.expect similarity index 88% rename from crates/ruff_python_formatter/resources/test/fixtures/black/py_310/pattern_matching_extras.py.expect rename to crates/ruff_python_formatter/resources/test/fixtures/black/cases/pattern_matching_extras.py.expect index 0242d264e5..5afe69920e 100644 --- a/crates/ruff_python_formatter/resources/test/fixtures/black/py_310/pattern_matching_extras.py.expect +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/pattern_matching_extras.py.expect @@ -29,22 +29,6 @@ def func(match: case, case: match) -> case: ... -match maybe, multiple: - case perhaps, 5: - pass - case perhaps, 6,: - pass - - -match more := (than, one), indeed,: - case _, (5, 6): - pass - case [[5], (6)], [7],: - pass - case _: - pass - - match a, *b, c: case [*_]: assert "seq" == _ @@ -66,12 +50,12 @@ match match( ), ): pass - case [a as match]: pass - case case: pass + case something: + pass match match: @@ -97,10 +81,8 @@ match something: match something: case 1 as a: pass - case 2 as b, 3 as c: pass - case 4 as d, (5 as e), (6 | 7 as g), *h: pass diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/py_310/pattern_matching_generic.py b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/pattern_matching_generic.py similarity index 100% rename from crates/ruff_python_formatter/resources/test/fixtures/black/py_310/pattern_matching_generic.py rename to crates/ruff_python_formatter/resources/test/fixtures/black/cases/pattern_matching_generic.py diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/py_310/pattern_matching_generic.py.expect b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/pattern_matching_generic.py.expect similarity index 100% rename from crates/ruff_python_formatter/resources/test/fixtures/black/py_310/pattern_matching_generic.py.expect rename to crates/ruff_python_formatter/resources/test/fixtures/black/cases/pattern_matching_generic.py.expect diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/py_310/pattern_matching_simple.py b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/pattern_matching_simple.py similarity index 100% rename from crates/ruff_python_formatter/resources/test/fixtures/black/py_310/pattern_matching_simple.py rename to crates/ruff_python_formatter/resources/test/fixtures/black/cases/pattern_matching_simple.py diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/py_310/pattern_matching_simple.py.expect b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/pattern_matching_simple.py.expect similarity index 100% rename from crates/ruff_python_formatter/resources/test/fixtures/black/py_310/pattern_matching_simple.py.expect rename to crates/ruff_python_formatter/resources/test/fixtures/black/cases/pattern_matching_simple.py.expect diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/py_310/pattern_matching_style.py b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/pattern_matching_style.py similarity index 100% rename from crates/ruff_python_formatter/resources/test/fixtures/black/py_310/pattern_matching_style.py rename to crates/ruff_python_formatter/resources/test/fixtures/black/cases/pattern_matching_style.py diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/py_310/pattern_matching_style.py.expect b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/pattern_matching_style.py.expect similarity index 100% rename from crates/ruff_python_formatter/resources/test/fixtures/black/py_310/pattern_matching_style.py.expect rename to crates/ruff_python_formatter/resources/test/fixtures/black/cases/pattern_matching_style.py.expect diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/pep604_union_types_line_breaks.options.json b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/pep604_union_types_line_breaks.options.json new file mode 100644 index 0000000000..ce7c52b163 --- /dev/null +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/pep604_union_types_line_breaks.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/pep604_union_types_line_breaks.py b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/pep604_union_types_line_breaks.py new file mode 100644 index 0000000000..bd3e48417b --- /dev/null +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/pep604_union_types_line_breaks.py @@ -0,0 +1,83 @@ +# This has always worked +z= Loooooooooooooooooooooooong | Loooooooooooooooooooooooong | Loooooooooooooooooooooooong | Loooooooooooooooooooooooong + +# "AnnAssign"s now also work +z: Loooooooooooooooooooooooong | Loooooooooooooooooooooooong | Loooooooooooooooooooooooong | Loooooooooooooooooooooooong +z: (Short + | Short2 + | Short3 + | Short4) +z: (int) +z: ((int)) + + +z: Loooooooooooooooooooooooong | Loooooooooooooooooooooooong | Loooooooooooooooooooooooong | Loooooooooooooooooooooooong = 7 +z: (Short + | Short2 + | Short3 + | Short4) = 8 +z: (int) = 2.3 +z: ((int)) = foo() + +# In case I go for not enforcing parantheses, this might get improved at the same time +x = ( + z + == 9999999999999999999999999999999999999999 + | 9999999999999999999999999999999999999999 + | 9999999999999999999999999999999999999999 + | 9999999999999999999999999999999999999999, + y + == 9999999999999999999999999999999999999999 + + 9999999999999999999999999999999999999999 + + 9999999999999999999999999999999999999999 + + 9999999999999999999999999999999999999999, +) + +x = ( + z == (9999999999999999999999999999999999999999 + | 9999999999999999999999999999999999999999 + | 9999999999999999999999999999999999999999 + | 9999999999999999999999999999999999999999), + y == (9999999999999999999999999999999999999999 + + 9999999999999999999999999999999999999999 + + 9999999999999999999999999999999999999999 + + 9999999999999999999999999999999999999999), +) + +# handle formatting of "tname"s in parameter list + +# remove unnecessary paren +def foo(i: (int)) -> None: ... + + +# this is a syntax error in the type annotation according to mypy, but it's not invalid *python* code, so make sure we don't mess with it and make it so. +def foo(i: (int,)) -> None: ... + +def foo( + i: int, + x: Loooooooooooooooooooooooong + | Looooooooooooooooong + | Looooooooooooooooooooong + | Looooooong, + *, + s: str, +) -> None: + pass + + +@app.get("/path/") +async def foo( + q: str + | None = Query(None, title="Some long title", description="Some long description") +): + pass + + +def f( + max_jobs: int + | None = Option( + None, help="Maximum number of jobs to launch. And some additional text." + ), + another_option: bool = False + ): + ... 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 new file mode 100644 index 0000000000..ab0a4d9677 --- /dev/null +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/pep604_union_types_line_breaks.py.expect @@ -0,0 +1,101 @@ +# This has always worked +z = ( + Loooooooooooooooooooooooong + | Loooooooooooooooooooooooong + | Loooooooooooooooooooooooong + | Loooooooooooooooooooooooong +) + +# "AnnAssign"s now also work +z: ( + Loooooooooooooooooooooooong + | Loooooooooooooooooooooooong + | Loooooooooooooooooooooooong + | Loooooooooooooooooooooooong +) +z: Short | Short2 | Short3 | Short4 +z: int +z: int + + +z: ( + Loooooooooooooooooooooooong + | Loooooooooooooooooooooooong + | Loooooooooooooooooooooooong + | Loooooooooooooooooooooooong +) = 7 +z: Short | Short2 | Short3 | Short4 = 8 +z: int = 2.3 +z: int = foo() + +# In case I go for not enforcing parantheses, this might get improved at the same time +x = ( + z + == 9999999999999999999999999999999999999999 + | 9999999999999999999999999999999999999999 + | 9999999999999999999999999999999999999999 + | 9999999999999999999999999999999999999999, + y + == 9999999999999999999999999999999999999999 + + 9999999999999999999999999999999999999999 + + 9999999999999999999999999999999999999999 + + 9999999999999999999999999999999999999999, +) + +x = ( + z + == ( + 9999999999999999999999999999999999999999 + | 9999999999999999999999999999999999999999 + | 9999999999999999999999999999999999999999 + | 9999999999999999999999999999999999999999 + ), + y + == ( + 9999999999999999999999999999999999999999 + + 9999999999999999999999999999999999999999 + + 9999999999999999999999999999999999999999 + + 9999999999999999999999999999999999999999 + ), +) + +# handle formatting of "tname"s in parameter list + + +# remove unnecessary paren +def foo(i: int) -> None: ... + + +# this is a syntax error in the type annotation according to mypy, but it's not invalid *python* code, so make sure we don't mess with it and make it so. +def foo(i: (int,)) -> None: ... + + +def foo( + i: int, + x: ( + Loooooooooooooooooooooooong + | Looooooooooooooooong + | Looooooooooooooooooooong + | Looooooong + ), + *, + s: str, +) -> None: + pass + + +@app.get("/path/") +async def foo( + q: str | None = Query( + None, title="Some long title", description="Some long description" + ) +): + pass + + +def f( + max_jobs: int | None = Option( + None, help="Maximum number of jobs to launch. And some additional text." + ), + another_option: bool = False, +): ... diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/py_38/pep_570.py b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/pep_570.py similarity index 100% rename from crates/ruff_python_formatter/resources/test/fixtures/black/py_38/pep_570.py rename to crates/ruff_python_formatter/resources/test/fixtures/black/cases/pep_570.py diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/py_38/pep_570.py.expect b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/pep_570.py.expect similarity index 100% rename from crates/ruff_python_formatter/resources/test/fixtures/black/py_38/pep_570.py.expect rename to crates/ruff_python_formatter/resources/test/fixtures/black/cases/pep_570.py.expect diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/py_38/pep_572.py b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/pep_572.py similarity index 100% rename from crates/ruff_python_formatter/resources/test/fixtures/black/py_38/pep_572.py rename to crates/ruff_python_formatter/resources/test/fixtures/black/cases/pep_572.py diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/py_38/pep_572.py.expect b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/pep_572.py.expect similarity index 100% rename from crates/ruff_python_formatter/resources/test/fixtures/black/py_38/pep_572.py.expect rename to crates/ruff_python_formatter/resources/test/fixtures/black/cases/pep_572.py.expect diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/py_310/pep_572_py310.py b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/pep_572_py310.py similarity index 100% rename from crates/ruff_python_formatter/resources/test/fixtures/black/py_310/pep_572_py310.py rename to crates/ruff_python_formatter/resources/test/fixtures/black/cases/pep_572_py310.py diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/py_310/pep_572_py310.py.expect b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/pep_572_py310.py.expect similarity index 100% rename from crates/ruff_python_formatter/resources/test/fixtures/black/py_310/pep_572_py310.py.expect rename to crates/ruff_python_formatter/resources/test/fixtures/black/cases/pep_572_py310.py.expect diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/py_39/pep_572_py39.py b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/pep_572_py39.py similarity index 100% rename from crates/ruff_python_formatter/resources/test/fixtures/black/py_39/pep_572_py39.py rename to crates/ruff_python_formatter/resources/test/fixtures/black/cases/pep_572_py39.py diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/py_39/pep_572_py39.py.expect b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/pep_572_py39.py.expect similarity index 100% rename from crates/ruff_python_formatter/resources/test/fixtures/black/py_39/pep_572_py39.py.expect rename to crates/ruff_python_formatter/resources/test/fixtures/black/cases/pep_572_py39.py.expect diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/py_38/pep_572_remove_parens.py b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/pep_572_remove_parens.py similarity index 100% rename from crates/ruff_python_formatter/resources/test/fixtures/black/py_38/pep_572_remove_parens.py rename to crates/ruff_python_formatter/resources/test/fixtures/black/cases/pep_572_remove_parens.py diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/py_38/pep_572_remove_parens.py.expect b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/pep_572_remove_parens.py.expect similarity index 100% rename from crates/ruff_python_formatter/resources/test/fixtures/black/py_38/pep_572_remove_parens.py.expect rename to crates/ruff_python_formatter/resources/test/fixtures/black/cases/pep_572_remove_parens.py.expect diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/simple_cases/pep_604.py b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/pep_604.py similarity index 100% rename from crates/ruff_python_formatter/resources/test/fixtures/black/simple_cases/pep_604.py rename to crates/ruff_python_formatter/resources/test/fixtures/black/cases/pep_604.py diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/simple_cases/pep_604.py.expect b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/pep_604.py.expect similarity index 100% rename from crates/ruff_python_formatter/resources/test/fixtures/black/simple_cases/pep_604.py.expect rename to crates/ruff_python_formatter/resources/test/fixtures/black/cases/pep_604.py.expect diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/py_311/pep_646.py b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/pep_646.py similarity index 100% rename from crates/ruff_python_formatter/resources/test/fixtures/black/py_311/pep_646.py rename to crates/ruff_python_formatter/resources/test/fixtures/black/cases/pep_646.py diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/py_311/pep_646.py.expect b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/pep_646.py.expect similarity index 100% rename from crates/ruff_python_formatter/resources/test/fixtures/black/py_311/pep_646.py.expect rename to crates/ruff_python_formatter/resources/test/fixtures/black/cases/pep_646.py.expect diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/py_311/pep_654.py b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/pep_654.py similarity index 100% rename from crates/ruff_python_formatter/resources/test/fixtures/black/py_311/pep_654.py rename to crates/ruff_python_formatter/resources/test/fixtures/black/cases/pep_654.py diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/py_311/pep_654.py.expect b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/pep_654.py.expect similarity index 100% rename from crates/ruff_python_formatter/resources/test/fixtures/black/py_311/pep_654.py.expect rename to crates/ruff_python_formatter/resources/test/fixtures/black/cases/pep_654.py.expect diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/py_311/pep_654_style.py b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/pep_654_style.py similarity index 100% rename from crates/ruff_python_formatter/resources/test/fixtures/black/py_311/pep_654_style.py rename to crates/ruff_python_formatter/resources/test/fixtures/black/cases/pep_654_style.py diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/py_311/pep_654_style.py.expect b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/pep_654_style.py.expect similarity index 100% rename from crates/ruff_python_formatter/resources/test/fixtures/black/py_311/pep_654_style.py.expect rename to crates/ruff_python_formatter/resources/test/fixtures/black/cases/pep_654_style.py.expect diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/power_op_newline.options.json b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/power_op_newline.options.json new file mode 100644 index 0000000000..80ad04dcfc --- /dev/null +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/power_op_newline.options.json @@ -0,0 +1 @@ +{"line_length": 0} \ No newline at end of file diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/miscellaneous/power_op_newline.py b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/power_op_newline.py similarity index 100% rename from crates/ruff_python_formatter/resources/test/fixtures/black/miscellaneous/power_op_newline.py rename to crates/ruff_python_formatter/resources/test/fixtures/black/cases/power_op_newline.py diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/miscellaneous/power_op_newline.py.expect b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/power_op_newline.py.expect similarity index 100% rename from crates/ruff_python_formatter/resources/test/fixtures/black/miscellaneous/power_op_newline.py.expect rename to crates/ruff_python_formatter/resources/test/fixtures/black/cases/power_op_newline.py.expect diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/simple_cases/power_op_spacing.py b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/power_op_spacing.py similarity index 93% rename from crates/ruff_python_formatter/resources/test/fixtures/black/simple_cases/power_op_spacing.py rename to crates/ruff_python_formatter/resources/test/fixtures/black/cases/power_op_spacing.py index 1ae3fc2b4f..14b29b3feb 100644 --- a/crates/ruff_python_formatter/resources/test/fixtures/black/simple_cases/power_op_spacing.py +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/power_op_spacing.py @@ -29,6 +29,13 @@ o = settings(max_examples=10**6) p = {(k, k**2): v**2 for k, v in pairs} q = [10**i for i in range(6)] r = x**y +s = 1 ** 1 +t = ( + 1 + ** 1 + **1 + ** 1 +) a = 5.0**~4.0 b = 5.0 ** f() @@ -47,6 +54,13 @@ n = count <= 10**5.0 o = settings(max_examples=10**6.0) p = {(k, k**2): v**2.0 for k, v in pairs} q = [10.5**i for i in range(6)] +s = 1.0 ** 1.0 +t = ( + 1.0 + ** 1.0 + **1.0 + ** 1.0 +) # WE SHOULD DEFINITELY NOT EAT THESE COMMENTS (https://github.com/psf/black/issues/2873) diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/simple_cases/power_op_spacing.py.expect b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/power_op_spacing.py.expect similarity index 96% rename from crates/ruff_python_formatter/resources/test/fixtures/black/simple_cases/power_op_spacing.py.expect rename to crates/ruff_python_formatter/resources/test/fixtures/black/cases/power_op_spacing.py.expect index ffc2be7e78..ca00fba8b3 100644 --- a/crates/ruff_python_formatter/resources/test/fixtures/black/simple_cases/power_op_spacing.py.expect +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/power_op_spacing.py.expect @@ -29,6 +29,8 @@ o = settings(max_examples=10**6) p = {(k, k**2): v**2 for k, v in pairs} q = [10**i for i in range(6)] r = x**y +s = 1**1 +t = 1**1**1**1 a = 5.0**~4.0 b = 5.0 ** f() @@ -47,6 +49,8 @@ n = count <= 10**5.0 o = settings(max_examples=10**6.0) p = {(k, k**2): v**2.0 for k, v in pairs} q = [10.5**i for i in range(6)] +s = 1.0**1.0 +t = 1.0**1.0**1.0**1.0 # WE SHOULD DEFINITELY NOT EAT THESE COMMENTS (https://github.com/psf/black/issues/2873) diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/simple_cases/prefer_rhs_split_reformatted.py b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/prefer_rhs_split_reformatted.py similarity index 100% rename from crates/ruff_python_formatter/resources/test/fixtures/black/simple_cases/prefer_rhs_split_reformatted.py rename to crates/ruff_python_formatter/resources/test/fixtures/black/cases/prefer_rhs_split_reformatted.py diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/simple_cases/prefer_rhs_split_reformatted.py.expect b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/prefer_rhs_split_reformatted.py.expect similarity index 100% rename from crates/ruff_python_formatter/resources/test/fixtures/black/simple_cases/prefer_rhs_split_reformatted.py.expect rename to crates/ruff_python_formatter/resources/test/fixtures/black/cases/prefer_rhs_split_reformatted.py.expect diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_allow_empty_first_line_in_special_cases.options.json b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_allow_empty_first_line_in_special_cases.options.json new file mode 100644 index 0000000000..ce7c52b163 --- /dev/null +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_allow_empty_first_line_in_special_cases.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_allow_empty_first_line_in_special_cases.py b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_allow_empty_first_line_in_special_cases.py new file mode 100644 index 0000000000..7cf2fc1b8c --- /dev/null +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_allow_empty_first_line_in_special_cases.py @@ -0,0 +1,51 @@ +def foo(): + """ + Docstring + """ + + # Here we go + if x: + + # This is also now fine + a = 123 + + else: + # But not necessary + a = 123 + + if y: + + while True: + + """ + Long comment here + """ + a = 123 + + if z: + + for _ in range(100): + a = 123 + else: + + try: + + # this should be ok + a = 123 + except: + + """also this""" + a = 123 + + +def bar(): + + if x: + a = 123 + + +def baz(): + + # OK + if x: + a = 123 diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_allow_empty_first_line_in_special_cases.py.expect b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_allow_empty_first_line_in_special_cases.py.expect new file mode 100644 index 0000000000..570a63b1d3 --- /dev/null +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_allow_empty_first_line_in_special_cases.py.expect @@ -0,0 +1,51 @@ +def foo(): + """ + Docstring + """ + + # Here we go + if x: + + # This is also now fine + a = 123 + + else: + # But not necessary + a = 123 + + if y: + + while True: + + """ + Long comment here + """ + a = 123 + + if z: + + for _ in range(100): + a = 123 + else: + + try: + + # this should be ok + a = 123 + except: + + """also this""" + a = 123 + + +def bar(): + + if x: + a = 123 + + +def baz(): + + # OK + if x: + a = 123 diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_async_stmts.options.json b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_async_stmts.options.json new file mode 100644 index 0000000000..ce7c52b163 --- /dev/null +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_async_stmts.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_async_stmts.py b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_async_stmts.py new file mode 100644 index 0000000000..88767abb88 --- /dev/null +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_async_stmts.py @@ -0,0 +1,11 @@ +async def func() -> (int): + return 0 + + +@decorated +async def func() -> (int): + return 0 + + +async for (item) in async_iter: + pass diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_async_stmts.py.expect b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_async_stmts.py.expect new file mode 100644 index 0000000000..13423345b6 --- /dev/null +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_async_stmts.py.expect @@ -0,0 +1,11 @@ +async def func() -> int: + return 0 + + +@decorated +async def func() -> int: + return 0 + + +async for item in async_iter: + pass diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_cantfit.options.json b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_cantfit.options.json new file mode 100644 index 0000000000..ce7c52b163 --- /dev/null +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_cantfit.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_cantfit.py b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_cantfit.py new file mode 100644 index 0000000000..32ef2770cf --- /dev/null +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_cantfit.py @@ -0,0 +1,39 @@ +# 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 +) +# long arguments +normal_name = normal_function_name( + "but with super long string arguments that on their own exceed the line limit so there's no way it can ever fit", + "eggs with spam and eggs and spam with eggs with spam and eggs and spam with eggs with spam and eggs and spam with eggs", + this_is_a_ridiculously_long_name_and_nobody_in_their_right_mind_would_use_one_like_it=0, +) +string_variable_name = ( + "a string that is waaaaaaaayyyyyyyy too long, even in parens, there's nothing you can do" # noqa +) +for key in """ + hostname + 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 diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_cantfit.py.expect b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_cantfit.py.expect new file mode 100644 index 0000000000..27327be5f8 --- /dev/null +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_cantfit.py.expect @@ -0,0 +1,63 @@ +# 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 + ) +) +# long arguments +normal_name = normal_function_name( + "but with super long string arguments that on their own exceed the line limit so" + " there's no way it can ever fit", + "eggs with spam and eggs and spam with eggs with spam and eggs and spam with eggs" + " with spam and eggs and spam with eggs", + this_is_a_ridiculously_long_name_and_nobody_in_their_right_mind_would_use_one_like_it=0, +) +string_variable_name = "a string that is waaaaaaaayyyyyyyy too long, even in parens, there's nothing you can do" # noqa +for key in """ + hostname + 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, +) diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_comments7.options.json b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_comments7.options.json new file mode 100644 index 0000000000..ce7c52b163 --- /dev/null +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_comments7.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_comments7.py b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_comments7.py new file mode 100644 index 0000000000..14fe9b2f8b --- /dev/null +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_comments7.py @@ -0,0 +1,144 @@ +from .config import ( + Any, + Bool, + ConfigType, + ConfigTypeAttributes, + Int, + Path, + # String, + # resolve_to_config_type, + # DEFAULT_TYPE_ATTRIBUTES, +) + + +from .config import ( + Any, + Bool, + ConfigType, + ConfigTypeAttributes, + Int, + no_comma_here_yet + # and some comments, + # resolve_to_config_type, + # DEFAULT_TYPE_ATTRIBUTES, +) +from com.my_lovely_company.my_lovely_team.my_lovely_project.my_lovely_component import ( + MyLovelyCompanyTeamProjectComponent # NOT DRY +) +from com.my_lovely_company.my_lovely_team.my_lovely_project.my_lovely_component import ( + MyLovelyCompanyTeamProjectComponent as component # DRY +) + + +result = 1 # look ma, no comment migration xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx + +result = ( + 1 # look ma, no comment migration xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +) + +result = ( + "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" # aaa +) + +result = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" # aaa + + +def func(): + c = call( + 0.0123, + 0.0456, + 0.0789, + 0.0123, + 0.0789, + a[-1], # type: ignore + ) + c = call( + 0.0123, + 0.0456, + 0.0789, + 0.0123, + 0.0789, + a[-1] # type: ignore + ) + c = call( + 0.0123, + 0.0456, + 0.0789, + 0.0123, + 0.0456, + 0.0789, + 0.0123, + 0.0456, + 0.0789, + a[-1] # type: ignore + ) + + # The type: ignore exception only applies to line length, not + # other types of formatting. + c = call( + "aaaaaaaa", "aaaaaaaa", "aaaaaaaa", "aaaaaaaa", "aaaaaaaa", "aaaaaaaa", # type: ignore + "aaaaaaaa", "aaaaaaaa", "aaaaaaaa", "aaaaaaaa", "aaaaaaaa", "aaaaaaaa" + ) + + +class C: + @pytest.mark.parametrize( + ("post_data", "message"), + [ + # metadata_version errors. + ( + {}, + "None is an invalid value for Metadata-Version. Error: This field is" + " required. see" + " https://packaging.python.org/specifications/core-metadata" + ), + ( + {"metadata_version": "-1"}, + "'-1' is an invalid value for Metadata-Version. Error: Unknown Metadata" + " Version see" + " https://packaging.python.org/specifications/core-metadata" + ), + # name errors. + ( + {"metadata_version": "1.2"}, + "'' is an invalid value for Name. Error: This field is required. see" + " https://packaging.python.org/specifications/core-metadata" + ), + ( + {"metadata_version": "1.2", "name": "foo-"}, + "'foo-' is an invalid value for Name. Error: Must start and end with a" + " letter or numeral and contain only ascii numeric and '.', '_' and" + " '-'. see https://packaging.python.org/specifications/core-metadata" + ), + # version errors. + ( + {"metadata_version": "1.2", "name": "example"}, + "'' is an invalid value for Version. Error: This field is required. see" + " https://packaging.python.org/specifications/core-metadata" + ), + ( + {"metadata_version": "1.2", "name": "example", "version": "dog"}, + "'dog' is an invalid value for Version. Error: Must start and end with" + " a letter or numeral and contain only ascii numeric and '.', '_' and" + " '-'. see https://packaging.python.org/specifications/core-metadata" + ) + ] + ) + def test_fails_invalid_post_data( + self, pyramid_config, db_request, post_data, message + ): + ... + +square = Square(4) # type: Optional[Square] + +# Regression test for https://github.com/psf/black/issues/3756. +[ + ( + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" # aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa + ), +] +[ + ( # aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" # aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa + ), +] 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 new file mode 100644 index 0000000000..9e583ab571 --- /dev/null +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_comments7.py.expect @@ -0,0 +1,161 @@ +from .config import ( + Any, + Bool, + ConfigType, + ConfigTypeAttributes, + Int, + Path, + # String, + # resolve_to_config_type, + # DEFAULT_TYPE_ATTRIBUTES, +) + + +from .config import ( + Any, + Bool, + ConfigType, + ConfigTypeAttributes, + Int, + no_comma_here_yet, + # and some comments, + # resolve_to_config_type, + # DEFAULT_TYPE_ATTRIBUTES, +) +from com.my_lovely_company.my_lovely_team.my_lovely_project.my_lovely_component import ( + MyLovelyCompanyTeamProjectComponent, # NOT DRY +) +from com.my_lovely_company.my_lovely_team.my_lovely_project.my_lovely_component import ( + MyLovelyCompanyTeamProjectComponent as component, # DRY +) + + +result = 1 # look ma, no comment migration xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx + +result = 1 # look ma, no comment migration xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx + +result = ( # aaa + "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" +) + +result = ( # aaa + "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" +) + + +def func(): + c = call( + 0.0123, + 0.0456, + 0.0789, + 0.0123, + 0.0789, + a[-1], # type: ignore + ) + c = call(0.0123, 0.0456, 0.0789, 0.0123, 0.0789, a[-1]) # type: ignore + c = call( + 0.0123, + 0.0456, + 0.0789, + 0.0123, + 0.0456, + 0.0789, + 0.0123, + 0.0456, + 0.0789, + a[-1], # type: ignore + ) + + # The type: ignore exception only applies to line length, not + # other types of formatting. + c = call( + "aaaaaaaa", + "aaaaaaaa", + "aaaaaaaa", + "aaaaaaaa", + "aaaaaaaa", + "aaaaaaaa", # type: ignore + "aaaaaaaa", + "aaaaaaaa", + "aaaaaaaa", + "aaaaaaaa", + "aaaaaaaa", + "aaaaaaaa", + ) + + +class C: + @pytest.mark.parametrize( + ("post_data", "message"), + [ + # metadata_version errors. + ( + {}, + ( + "None is an invalid value for Metadata-Version. Error: This field" + " is required. see" + " https://packaging.python.org/specifications/core-metadata" + ), + ), + ( + {"metadata_version": "-1"}, + ( + "'-1' is an invalid value for Metadata-Version. Error: Unknown" + " Metadata Version see" + " https://packaging.python.org/specifications/core-metadata" + ), + ), + # name errors. + ( + {"metadata_version": "1.2"}, + ( + "'' is an invalid value for Name. Error: This field is required." + " see https://packaging.python.org/specifications/core-metadata" + ), + ), + ( + {"metadata_version": "1.2", "name": "foo-"}, + ( + "'foo-' is an invalid value for Name. Error: Must start and end" + " with a letter or numeral and contain only ascii numeric and '.'," + " '_' and '-'. see" + " https://packaging.python.org/specifications/core-metadata" + ), + ), + # version errors. + ( + {"metadata_version": "1.2", "name": "example"}, + ( + "'' is an invalid value for Version. Error: This field is required." + " see https://packaging.python.org/specifications/core-metadata" + ), + ), + ( + {"metadata_version": "1.2", "name": "example", "version": "dog"}, + ( + "'dog' is an invalid value for Version. Error: Must start and end" + " with a letter or numeral and contain only ascii numeric and '.'," + " '_' and '-'. see" + " https://packaging.python.org/specifications/core-metadata" + ), + ), + ], + ) + def test_fails_invalid_post_data( + self, pyramid_config, db_request, post_data, message + ): ... + + +square = Square(4) # type: Optional[Square] + +# Regression test for https://github.com/psf/black/issues/3756. +[ + ( # aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + ), +] +[ + ( # aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" # aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa + ), +] diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_context_managers_38.options.json b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_context_managers_38.options.json new file mode 100644 index 0000000000..ce7c52b163 --- /dev/null +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_context_managers_38.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_context_managers_38.py b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_context_managers_38.py new file mode 100644 index 0000000000..811be47176 --- /dev/null +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_context_managers_38.py @@ -0,0 +1,31 @@ +with \ + make_context_manager1() as cm1, \ + make_context_manager2() as cm2, \ + make_context_manager3() as cm3, \ + make_context_manager4() as cm4 \ +: + pass + + +with \ + make_context_manager1() as cm1, \ + make_context_manager2(), \ + make_context_manager3() as cm3, \ + make_context_manager4() \ +: + pass + + +with \ + new_new_new1() as cm1, \ + new_new_new2() \ +: + pass + + +with mock.patch.object( + self.my_runner, "first_method", autospec=True +) as mock_run_adb, mock.patch.object( + self.my_runner, "second_method", autospec=True, return_value="foo" +): + pass diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_context_managers_38.py.expect b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_context_managers_38.py.expect new file mode 100644 index 0000000000..5c0cdde0d2 --- /dev/null +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_context_managers_38.py.expect @@ -0,0 +1,18 @@ +with make_context_manager1() as cm1, make_context_manager2() as cm2, make_context_manager3() as cm3, make_context_manager4() as cm4: + pass + + +with make_context_manager1() as cm1, make_context_manager2(), make_context_manager3() as cm3, make_context_manager4(): + pass + + +with new_new_new1() as cm1, new_new_new2(): + pass + + +with mock.patch.object( + self.my_runner, "first_method", autospec=True +) as mock_run_adb, mock.patch.object( + self.my_runner, "second_method", autospec=True, return_value="foo" +): + pass diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_context_managers_39.options.json b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_context_managers_39.options.json new file mode 100644 index 0000000000..ce7c52b163 --- /dev/null +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_context_managers_39.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_context_managers_39.py b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_context_managers_39.py new file mode 100644 index 0000000000..f0ae005363 --- /dev/null +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_context_managers_39.py @@ -0,0 +1,84 @@ +with \ + make_context_manager1() as cm1, \ + make_context_manager2() as cm2, \ + make_context_manager3() as cm3, \ + make_context_manager4() as cm4 \ +: + pass + + +# Leading comment +with \ + make_context_manager1() as cm1, \ + make_context_manager2(), \ + make_context_manager3() as cm3, \ + make_context_manager4() \ +: + pass + + +with \ + new_new_new1() as cm1, \ + new_new_new2() \ +: + pass + + +with ( + new_new_new1() as cm1, + new_new_new2() +): + pass + + +# Leading comment. +with ( + # First comment. + new_new_new1() as cm1, + # Second comment. + new_new_new2() + # Last comment. +): + pass + + +with \ + this_is_a_very_long_call(looong_arg1=looong_value1, looong_arg2=looong_value2) as cm1, \ + this_is_a_very_long_call(looong_arg1=looong_value1, looong_arg2=looong_value2, looong_arg3=looong_value3, looong_arg4=looong_value4) as cm2 \ +: + pass + + +with mock.patch.object( + self.my_runner, "first_method", autospec=True +) as mock_run_adb, mock.patch.object( + self.my_runner, "second_method", autospec=True, return_value="foo" +): + pass + + +with xxxxxxxx.some_kind_of_method( + some_argument=[ + "first", + "second", + "third", + ] +).another_method() as cmd: + pass + + +async def func(): + async with \ + make_context_manager1() as cm1, \ + make_context_manager2() as cm2, \ + make_context_manager3() as cm3, \ + make_context_manager4() as cm4 \ + : + pass + + async with some_function( + argument1, argument2, argument3="some_value" + ) as some_cm, some_other_function( + argument1, argument2, argument3="some_value" + ): + pass diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_context_managers_39.py.expect b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_context_managers_39.py.expect new file mode 100644 index 0000000000..1b8e865933 --- /dev/null +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_context_managers_39.py.expect @@ -0,0 +1,85 @@ +with ( + make_context_manager1() as cm1, + make_context_manager2() as cm2, + make_context_manager3() as cm3, + make_context_manager4() as cm4, +): + pass + + +# Leading comment +with ( + make_context_manager1() as cm1, + make_context_manager2(), + make_context_manager3() as cm3, + make_context_manager4(), +): + pass + + +with new_new_new1() as cm1, new_new_new2(): + pass + + +with new_new_new1() as cm1, new_new_new2(): + pass + + +# Leading comment. +with ( + # First comment. + new_new_new1() as cm1, + # Second comment. + new_new_new2(), + # Last comment. +): + pass + + +with ( + this_is_a_very_long_call( + looong_arg1=looong_value1, looong_arg2=looong_value2 + ) as cm1, + this_is_a_very_long_call( + looong_arg1=looong_value1, + looong_arg2=looong_value2, + looong_arg3=looong_value3, + looong_arg4=looong_value4, + ) as cm2, +): + pass + + +with ( + mock.patch.object(self.my_runner, "first_method", autospec=True) as mock_run_adb, + mock.patch.object( + self.my_runner, "second_method", autospec=True, return_value="foo" + ), +): + pass + + +with xxxxxxxx.some_kind_of_method( + some_argument=[ + "first", + "second", + "third", + ] +).another_method() as cmd: + pass + + +async def func(): + async with ( + make_context_manager1() as cm1, + make_context_manager2() as cm2, + make_context_manager3() as cm3, + make_context_manager4() as cm4, + ): + pass + + async with ( + some_function(argument1, argument2, argument3="some_value") as some_cm, + some_other_function(argument1, argument2, argument3="some_value"), + ): + pass diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_context_managers_autodetect_310.options.json b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_context_managers_autodetect_310.options.json new file mode 100644 index 0000000000..ce7c52b163 --- /dev/null +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_context_managers_autodetect_310.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_context_managers_autodetect_310.py b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_context_managers_autodetect_310.py new file mode 100644 index 0000000000..493ad21108 --- /dev/null +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_context_managers_autodetect_310.py @@ -0,0 +1,15 @@ +# This file uses pattern matching introduced in Python 3.10. + + +match http_code: + case 404: + print("Not found") + + +with \ + make_context_manager1() as cm1, \ + make_context_manager2() as cm2, \ + make_context_manager3() as cm3, \ + make_context_manager4() as cm4 \ +: + pass diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_context_managers_autodetect_310.py.expect b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_context_managers_autodetect_310.py.expect new file mode 100644 index 0000000000..4c35978000 --- /dev/null +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_context_managers_autodetect_310.py.expect @@ -0,0 +1,15 @@ +# This file uses pattern matching introduced in Python 3.10. + + +match http_code: + case 404: + print("Not found") + + +with ( + make_context_manager1() as cm1, + make_context_manager2() as cm2, + make_context_manager3() as cm3, + make_context_manager4() as cm4, +): + pass diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_context_managers_autodetect_311.options.json b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_context_managers_autodetect_311.options.json new file mode 100644 index 0000000000..ce7c52b163 --- /dev/null +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_context_managers_autodetect_311.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_context_managers_autodetect_311.py b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_context_managers_autodetect_311.py new file mode 100644 index 0000000000..fb3fb65a31 --- /dev/null +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_context_managers_autodetect_311.py @@ -0,0 +1,16 @@ +# This file uses except* clause in Python 3.11. + + +try: + some_call() +except* Error as e: + pass + + +with \ + make_context_manager1() as cm1, \ + make_context_manager2() as cm2, \ + make_context_manager3() as cm3, \ + make_context_manager4() as cm4 \ +: + pass diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_context_managers_autodetect_311.py.expect b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_context_managers_autodetect_311.py.expect new file mode 100644 index 0000000000..b5ca9c0b14 --- /dev/null +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_context_managers_autodetect_311.py.expect @@ -0,0 +1,16 @@ +# This file uses except* clause in Python 3.11. + + +try: + some_call() +except* Error as e: + pass + + +with ( + make_context_manager1() as cm1, + make_context_manager2() as cm2, + make_context_manager3() as cm3, + make_context_manager4() as cm4, +): + pass diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_context_managers_autodetect_38.options.json b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_context_managers_autodetect_38.options.json new file mode 100644 index 0000000000..ce7c52b163 --- /dev/null +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_context_managers_autodetect_38.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_context_managers_autodetect_38.py b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_context_managers_autodetect_38.py new file mode 100644 index 0000000000..f48fbe78e3 --- /dev/null +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_context_managers_autodetect_38.py @@ -0,0 +1,24 @@ +# This file doesn't use any Python 3.9+ only grammars. + + +# Make sure parens around a single context manager don't get autodetected as +# Python 3.9+. +with (a): + pass + + +with \ + make_context_manager1() as cm1, \ + make_context_manager2() as cm2, \ + make_context_manager3() as cm3, \ + make_context_manager4() as cm4 \ +: + pass + + +with mock.patch.object( + self.my_runner, "first_method", autospec=True +) as mock_run_adb, mock.patch.object( + self.my_runner, "second_method", autospec=True, return_value="foo" +): + pass diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_context_managers_autodetect_38.py.expect b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_context_managers_autodetect_38.py.expect new file mode 100644 index 0000000000..d43eb9f091 --- /dev/null +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_context_managers_autodetect_38.py.expect @@ -0,0 +1,19 @@ +# This file doesn't use any Python 3.9+ only grammars. + + +# Make sure parens around a single context manager don't get autodetected as +# Python 3.9+. +with a: + pass + + +with make_context_manager1() as cm1, make_context_manager2() as cm2, make_context_manager3() as cm3, make_context_manager4() as cm4: + pass + + +with mock.patch.object( + self.my_runner, "first_method", autospec=True +) as mock_run_adb, mock.patch.object( + self.my_runner, "second_method", autospec=True, return_value="foo" +): + pass diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_context_managers_autodetect_39.options.json b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_context_managers_autodetect_39.options.json new file mode 100644 index 0000000000..ce7c52b163 --- /dev/null +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_context_managers_autodetect_39.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_context_managers_autodetect_39.py b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_context_managers_autodetect_39.py new file mode 100644 index 0000000000..c69d3a7289 --- /dev/null +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_context_managers_autodetect_39.py @@ -0,0 +1,17 @@ +# This file uses parenthesized context managers introduced in Python 3.9. + + +with \ + make_context_manager1() as cm1, \ + make_context_manager2() as cm2, \ + make_context_manager3() as cm3, \ + make_context_manager4() as cm4 \ +: + pass + + +with ( + new_new_new1() as cm1, + new_new_new2() +): + pass diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_context_managers_autodetect_39.py.expect b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_context_managers_autodetect_39.py.expect new file mode 100644 index 0000000000..ff6ce99917 --- /dev/null +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_context_managers_autodetect_39.py.expect @@ -0,0 +1,14 @@ +# This file uses parenthesized context managers introduced in Python 3.9. + + +with ( + make_context_manager1() as cm1, + make_context_manager2() as cm2, + make_context_manager3() as cm3, + make_context_manager4() as cm4, +): + pass + + +with new_new_new1() as cm1, new_new_new2(): + pass diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_docstring_no_string_normalization.options.json b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_docstring_no_string_normalization.options.json new file mode 100644 index 0000000000..ce7c52b163 --- /dev/null +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_docstring_no_string_normalization.options.json @@ -0,0 +1 @@ +{"preview": "enabled"} \ No newline at end of file diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/miscellaneous/docstring_preview_no_string_normalization.py b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_docstring_no_string_normalization.py similarity index 100% rename from crates/ruff_python_formatter/resources/test/fixtures/black/miscellaneous/docstring_preview_no_string_normalization.py rename to crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_docstring_no_string_normalization.py diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/miscellaneous/docstring_preview_no_string_normalization.py.expect b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_docstring_no_string_normalization.py.expect similarity index 100% rename from crates/ruff_python_formatter/resources/test/fixtures/black/miscellaneous/docstring_preview_no_string_normalization.py.expect rename to crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_docstring_no_string_normalization.py.expect diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_dummy_implementations.options.json b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_dummy_implementations.options.json new file mode 100644 index 0000000000..ce7c52b163 --- /dev/null +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_dummy_implementations.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_dummy_implementations.py b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_dummy_implementations.py new file mode 100644 index 0000000000..99729fed0a --- /dev/null +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_dummy_implementations.py @@ -0,0 +1,48 @@ +from typing import NoReturn, Protocol, Union, overload + + +def dummy(a): ... +def other(b): ... + + +@overload +def a(arg: int) -> int: ... +@overload +def a(arg: str) -> str: ... +@overload +def a(arg: object) -> NoReturn: ... +def a(arg: Union[int, str, object]) -> Union[int, str]: + if not isinstance(arg, (int, str)): + raise TypeError + return arg + +class Proto(Protocol): + def foo(self, a: int) -> int: + ... + + def bar(self, b: str) -> str: ... + def baz(self, c: bytes) -> str: + ... + + +def dummy_two(): + ... +@dummy +def dummy_three(): + ... + +def dummy_four(): + ... + +@overload +def b(arg: int) -> int: ... + +@overload +def b(arg: str) -> str: ... +@overload +def b(arg: object) -> NoReturn: ... + +def b(arg: Union[int, str, object]) -> Union[int, str]: + if not isinstance(arg, (int, str)): + raise TypeError + return arg diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_dummy_implementations.py.expect b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_dummy_implementations.py.expect new file mode 100644 index 0000000000..24068c5d18 --- /dev/null +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_dummy_implementations.py.expect @@ -0,0 +1,48 @@ +from typing import NoReturn, Protocol, Union, overload + + +def dummy(a): ... +def other(b): ... + + +@overload +def a(arg: int) -> int: ... +@overload +def a(arg: str) -> str: ... +@overload +def a(arg: object) -> NoReturn: ... +def a(arg: Union[int, str, object]) -> Union[int, str]: + if not isinstance(arg, (int, str)): + raise TypeError + return arg + + +class Proto(Protocol): + def foo(self, a: int) -> int: ... + + def bar(self, b: str) -> str: ... + def baz(self, c: bytes) -> str: ... + + +def dummy_two(): ... +@dummy +def dummy_three(): ... + + +def dummy_four(): ... + + +@overload +def b(arg: int) -> int: ... + + +@overload +def b(arg: str) -> str: ... +@overload +def b(arg: object) -> NoReturn: ... + + +def b(arg: Union[int, str, object]) -> Union[int, str]: + if not isinstance(arg, (int, str)): + raise TypeError + return arg diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_form_feeds.options.json b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_form_feeds.options.json new file mode 100644 index 0000000000..ce7c52b163 --- /dev/null +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_form_feeds.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_form_feeds.py b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_form_feeds.py new file mode 100644 index 0000000000..42ba8fe243 --- /dev/null +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_form_feeds.py @@ -0,0 +1,116 @@ +# Warning! This file contains form feeds (ASCII 0x0C, often represented by \f or ^L). +# These may be invisible in your editor: ensure you can see them before making changes here. + +# There's one at the start that'll get stripped + +# Comment and statement processing is different enough that we'll test variations of both +# contexts here + +# + + +# + + +# + + + +# + + + +# + + + +# + + +# + + + +# + +# + +# + + \ +# +pass + +pass + + +pass + + +pass + + + +pass + + + +pass + + + +pass + + +pass + + + +pass + +pass + +pass + + +# form feed after a dedent +def foo(): + pass + +pass + + +# form feeds are prohibited inside blocks, or on a line with nonwhitespace + def bar( a = 1 ,b : bool = False ) : + + + pass + + +class Baz: + + def __init__(self): + pass + + + def something(self): + pass + + + +# +pass +pass # +a = 1 + # + pass + a = 1 + +a = [ + +] + +# as internal whitespace of a comment is allowed but why +"form feed literal in a string is okay " + +# form feeds at the very end get removed. diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_form_feeds.py.expect b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_form_feeds.py.expect new file mode 100644 index 0000000000..4895f2af69 --- /dev/null +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_form_feeds.py.expect @@ -0,0 +1,101 @@ +# Warning! This file contains form feeds (ASCII 0x0C, often represented by \f or ^L). +# These may be invisible in your editor: ensure you can see them before making changes here. + +# There's one at the start that'll get stripped + +# Comment and statement processing is different enough that we'll test variations of both +# contexts here + +# + + +# + + +# + + +# + + +# + + +# + + +# + + +# + +# + +# + +# +pass + +pass + + +pass + + +pass + + +pass + + +pass + + +pass + + +pass + + +pass + +pass + +pass + + +# form feed after a dedent +def foo(): + pass + + +pass + + +# form feeds are prohibited inside blocks, or on a line with nonwhitespace +def bar(a=1, b: bool = False): + pass + + +class Baz: + def __init__(self): + pass + + def something(self): + pass + + +# +pass +pass # +a = 1 +# +pass +a = 1 + +a = [] + +# as internal whitespace of a comment is allowed but why +"form feed literal in a string is okay " + +# form feeds at the very end get removed. diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_format_unicode_escape_seq.options.json b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_format_unicode_escape_seq.options.json new file mode 100644 index 0000000000..ce7c52b163 --- /dev/null +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_format_unicode_escape_seq.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_format_unicode_escape_seq.py b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_format_unicode_escape_seq.py new file mode 100644 index 0000000000..70699c5663 --- /dev/null +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_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/preview_format_unicode_escape_seq.py.expect b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_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/preview_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/preview_hug_parens_with_braces_and_square_brackets.options.json b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_hug_parens_with_braces_and_square_brackets.options.json new file mode 100644 index 0000000000..ce7c52b163 --- /dev/null +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_hug_parens_with_braces_and_square_brackets.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_hug_parens_with_braces_and_square_brackets.py b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_hug_parens_with_braces_and_square_brackets.py new file mode 100644 index 0000000000..2560ff5c1f --- /dev/null +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_hug_parens_with_braces_and_square_brackets.py @@ -0,0 +1,185 @@ +def foo_brackets(request): + return JsonResponse( + { + "var_1": foo, + "var_2": bar, + } + ) + +def foo_square_brackets(request): + return JsonResponse( + [ + "var_1", + "var_2", + ] + ) + +func({"a": 37, "b": 42, "c": 927, "aaaaaaaaaaaaaaaaaaaaaaaaa": 11111111111111111111111111111111111111111}) + +func(["random_string_number_one","random_string_number_two","random_string_number_three","random_string_number_four"]) + +func( + { + # expand me + 'a':37, + 'b':42, + 'c':927 + } +) + +func( + [ + 'a', + 'b', + 'c', + ] +) + +func( + [ + 'a', + 'b', + 'c', + ], +) + +func( # a + [ # b + "c", # c + "d", # d + "e", # e + ] # f +) # g + +func( # a + { # b + "c": 1, # c + "d": 2, # d + "e": 3, # e + } # f +) # g + +func( + # preserve me + [ + "c", + "d", + "e", + ] +) + +func( + [ # preserve me but hug brackets + "c", + "d", + "e", + ] +) + +func( + [ + # preserve me but hug brackets + "c", + "d", + "e", + ] +) + +func( + [ + "c", + # preserve me but hug brackets + "d", + "e", + ] +) + +func( + [ + "c", + "d", + "e", + # preserve me but hug brackets + ] +) + +func( + [ + "c", + "d", + "e", + ] # preserve me but hug brackets +) + +func( + [ + "c", + "d", + "e", + ] + # preserve me +) + +func([x for x in "short line"]) +func([x for x in "long line long line long line long line long line long line long line"]) +func([x for x in [x for x in "long line long line long line long line long line long line long line"]]) + +func({"short line"}) +func({"long line", "long long line", "long long long line", "long long long long line", "long long long long long line"}) +func({{"long line", "long long line", "long long long line", "long long long long line", "long long long long long line"}}) +func(("long line", "long long line", "long long long line", "long long long long line", "long long long long long line")) +func((("long line", "long long line", "long long long line", "long long long long line", "long long long long long line"))) +func([["long line", "long long line", "long long long line", "long long long long line", "long long long long long line"]]) + +# Do not hug if the argument fits on a single line. +func({"fit line", "fit line", "fit line", "fit line", "fit line", "fit line", "fit line"}) +func(("fit line", "fit line", "fit line", "fit line", "fit line", "fit line", "fit line")) +func(["fit line", "fit line", "fit line", "fit line", "fit line", "fit line", "fit line"]) +func(**{"fit line", "fit line", "fit line", "fit line", "fit line", "fit line", "fit---"}) +func(*("fit line", "fit line", "fit line", "fit line", "fit line", "fit line", "fit----")) +array = [{"fit line", "fit line", "fit line", "fit line", "fit line", "fit line", "fit line"}] +array = [("fit line", "fit line", "fit line", "fit line", "fit line", "fit line", "fit line")] +array = [["fit line", "fit line", "fit line", "fit line", "fit line", "fit line", "fit line"]] + +foooooooooooooooooooo( + [{c: n + 1 for c in range(256)} for n in range(100)] + [{}], {size} +) + +baaaaaaaaaaaaar( + [1, 2, 3, 4, 5, 6, 7, 8, 9, 10], {x}, "a string", [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] +) + +nested_mapping = {"key": [{"a very long key 1": "with a very long value", "a very long key 2": "with a very long value"}]} +nested_array = [[["long line", "long long line", "long long long line", "long long long long line", "long long long long long line"]]] +explicit_exploding = [[["short", "line",],],] +single_item_do_not_explode = Context({ + "version": get_docs_version(), +}) + +foo(*["long long long long long line", "long long long long long line", "long long long long long line"]) + +foo(*[str(i) for i in range(100000000000000000000000000000000000000000000000000000000000)]) + +foo( + **{ + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa": 1, + "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb": 2, + "ccccccccccccccccccccccccccccccccc": 3, + **other, + } +) + +foo(**{x: y for x, y in enumerate(["long long long long line","long long long long line"])}) + +# Edge case when deciding whether to hug the brackets without inner content. +very_very_very_long_variable = very_very_very_long_module.VeryVeryVeryVeryLongClassName([[]]) + +for foo in ["a", "b"]: + output.extend([ + individual + for + # Foobar + container in xs_by_y[foo] + # Foobar + for individual in container["nested"] + ]) diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_hug_parens_with_braces_and_square_brackets.py.expect b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_hug_parens_with_braces_and_square_brackets.py.expect new file mode 100644 index 0000000000..a4f879e6de --- /dev/null +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_hug_parens_with_braces_and_square_brackets.py.expect @@ -0,0 +1,255 @@ +def foo_brackets(request): + return JsonResponse({ + "var_1": foo, + "var_2": bar, + }) + + +def foo_square_brackets(request): + return JsonResponse([ + "var_1", + "var_2", + ]) + + +func({ + "a": 37, + "b": 42, + "c": 927, + "aaaaaaaaaaaaaaaaaaaaaaaaa": 11111111111111111111111111111111111111111, +}) + +func([ + "random_string_number_one", + "random_string_number_two", + "random_string_number_three", + "random_string_number_four", +]) + +func({ + # expand me + "a": 37, + "b": 42, + "c": 927, +}) + +func([ + "a", + "b", + "c", +]) + +func( + [ + "a", + "b", + "c", + ], +) + +func([ # a # b + "c", # c + "d", # d + "e", # e +]) # f # g + +func({ # a # b + "c": 1, # c + "d": 2, # d + "e": 3, # e +}) # f # g + +func( + # preserve me + [ + "c", + "d", + "e", + ] +) + +func([ # preserve me but hug brackets + "c", + "d", + "e", +]) + +func([ + # preserve me but hug brackets + "c", + "d", + "e", +]) + +func([ + "c", + # preserve me but hug brackets + "d", + "e", +]) + +func([ + "c", + "d", + "e", + # preserve me but hug brackets +]) + +func([ + "c", + "d", + "e", +]) # preserve me but hug brackets + +func( + [ + "c", + "d", + "e", + ] + # preserve me +) + +func([x for x in "short line"]) +func( + [x for x in "long line long line long line long line long line long line long line"] +) +func([ + x + for x in [ + x + for x in "long line long line long line long line long line long line long line" + ] +]) + +func({"short line"}) +func({ + "long line", + "long long line", + "long long long line", + "long long long long line", + "long long long long long line", +}) +func({{ + "long line", + "long long line", + "long long long line", + "long long long long line", + "long long long long long line", +}}) +func(( + "long line", + "long long line", + "long long long line", + "long long long long line", + "long long long long long line", +)) +func((( + "long line", + "long long line", + "long long long line", + "long long long long line", + "long long long long long line", +))) +func([[ + "long line", + "long long line", + "long long long line", + "long long long long line", + "long long long long long line", +]]) + +# Do not hug if the argument fits on a single line. +func( + {"fit line", "fit line", "fit line", "fit line", "fit line", "fit line", "fit line"} +) +func( + ("fit line", "fit line", "fit line", "fit line", "fit line", "fit line", "fit line") +) +func( + ["fit line", "fit line", "fit line", "fit line", "fit line", "fit line", "fit line"] +) +func( + **{"fit line", "fit line", "fit line", "fit line", "fit line", "fit line", "fit---"} +) +func( + *("fit line", "fit line", "fit line", "fit line", "fit line", "fit line", "fit----") +) +array = [ + {"fit line", "fit line", "fit line", "fit line", "fit line", "fit line", "fit line"} +] +array = [ + ("fit line", "fit line", "fit line", "fit line", "fit line", "fit line", "fit line") +] +array = [ + ["fit line", "fit line", "fit line", "fit line", "fit line", "fit line", "fit line"] +] + +foooooooooooooooooooo( + [{c: n + 1 for c in range(256)} for n in range(100)] + [{}], {size} +) + +baaaaaaaaaaaaar( + [1, 2, 3, 4, 5, 6, 7, 8, 9, 10], {x}, "a string", [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] +) + +nested_mapping = { + "key": [{ + "a very long key 1": "with a very long value", + "a very long key 2": "with a very long value", + }] +} +nested_array = [[[ + "long line", + "long long line", + "long long long line", + "long long long long line", + "long long long long long line", +]]] +explicit_exploding = [ + [ + [ + "short", + "line", + ], + ], +] +single_item_do_not_explode = Context({ + "version": get_docs_version(), +}) + +foo(*[ + "long long long long long line", + "long long long long long line", + "long long long long long line", +]) + +foo(*[ + str(i) for i in range(100000000000000000000000000000000000000000000000000000000000) +]) + +foo(**{ + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa": 1, + "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb": 2, + "ccccccccccccccccccccccccccccccccc": 3, + **other, +}) + +foo(**{ + x: y for x, y in enumerate(["long long long long line", "long long long long line"]) +}) + +# Edge case when deciding whether to hug the brackets without inner content. +very_very_very_long_variable = very_very_very_long_module.VeryVeryVeryVeryLongClassName( + [[]] +) + +for foo in ["a", "b"]: + output.extend([ + individual + for + # Foobar + container in xs_by_y[foo] + # Foobar + for individual in container["nested"] + ]) diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_long_dict_values.options.json b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_long_dict_values.options.json new file mode 100644 index 0000000000..ce7c52b163 --- /dev/null +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_long_dict_values.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_long_dict_values.py b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_long_dict_values.py new file mode 100644 index 0000000000..67ff7b671c --- /dev/null +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_long_dict_values.py @@ -0,0 +1,36 @@ +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", +} + +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': + xxxxxxxxxxxxxxxxxxx.xxxxxxxxxxxxxx( + xxxxxxxxxxxxxx={ + 'x': + xxxxxxxxxxxxxxxxxxxxxxxxxx.xxxxxxxxxxxxxxxxxxxxxxxxxxxxx( + xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx=( + xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx + .xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx( + xxxxxxxxxxxxx=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx + .xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx( + xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx={ + '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 new file mode 100644 index 0000000000..e9ec664093 --- /dev/null +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_long_dict_values.py.expect @@ -0,0 +1,49 @@ +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" + ), +} + +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": xxxxxxxxxxxxxxxxxxx.xxxxxxxxxxxxxx( + xxxxxxxxxxxxxx={ + "x": xxxxxxxxxxxxxxxxxxxxxxxxxx.xxxxxxxxxxxxxxxxxxxxxxxxxxxxx( + xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx=( + xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx.xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx( + xxxxxxxxxxxxx=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx.xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx( + xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx={ + "x": x.xx, + "x": x.x, + } + ) + ) + ) + ) + } + ), +} diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_long_strings.options.json b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_long_strings.options.json new file mode 100644 index 0000000000..ce7c52b163 --- /dev/null +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_long_strings.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_long_strings.py b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_long_strings.py new file mode 100644 index 0000000000..017f679115 --- /dev/null +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_long_strings.py @@ -0,0 +1,329 @@ +x = "This is a really long string that can't possibly be expected to fit all together on one line. In fact it may even take up three or more lines... like four or five... but probably just three." + +x += "This is a really long string that can't possibly be expected to fit all together on one line. In fact it may even take up three or more lines... like four or five... but probably just three." + +y = ( + 'Short string' +) + +print('This is a really long string inside of a print statement with extra arguments attached at the end of it.', x, y, z) + +print("This is a really long string inside of a print statement with no extra arguments attached at the end of it.") + +D1 = {"The First": "This is a really long string that can't possibly be expected to fit all together on one line. Also it is inside a dictionary, so formatting is more difficult.", "The Second": "This is another really really (not really) long string that also can't be expected to fit on one line and is, like the other string, inside a dictionary."} + +D2 = {1.0: "This is a really long string that can't possibly be expected to fit all together on one line. Also it is inside a dictionary, so formatting is more difficult.", 2.0: "This is another really really (not really) long string that also can't be expected to fit on one line and is, like the other string, inside a dictionary."} + +D3 = {x: "This is a really long string that can't possibly be expected to fit all together on one line. Also it is inside a dictionary, so formatting is more difficult.", y: "This is another really really (not really) long string that also can't be expected to fit on one line and is, like the other string, inside a dictionary."} + +D4 = {"A long and ridiculous {}".format(string_key): "This is a really really really long string that has to go i,side of a dictionary. It is soooo bad.", some_func("calling", "some", "stuff"): "This is a really really really long string that has to go inside of a dictionary. It is {soooo} bad (#{x}).".format(sooo="soooo", x=2), "A %s %s" % ("formatted", "string"): "This is a really really really long string that has to go inside of a dictionary. It is %s bad (#%d)." % ("soooo", 2)} + +D5 = { # Test for https://github.com/psf/black/issues/3261 + ("This is a really long string that can't be expected to fit in one line and is used as a nested dict's key"): {"inner": "value"}, +} + +D6 = { # Test for https://github.com/psf/black/issues/3261 + ("This is a really long string that can't be expected to fit in one line and is used as a dict's key"): ["value1", "value2"], +} + +L1 = ["The is a short string", "This is a really long string that can't possibly be expected to fit all together on one line. Also it is inside a list literal, so it's expected to be wrapped in parens when splitting to avoid implicit str concatenation.", short_call("arg", {"key": "value"}), "This is another really really (not really) long string that also can't be expected to fit on one line and is, like the other string, inside a list literal.", ("parens should be stripped for short string in list")] + +L2 = ["This is a really long string that can't be expected to fit in one line and is the only child of a list literal."] + +S1 = {"The is a short string", "This is a really long string that can't possibly be expected to fit all together on one line. Also it is inside a set literal, so it's expected to be wrapped in parens when splitting to avoid implicit str concatenation.", short_call("arg", {"key": "value"}), "This is another really really (not really) long string that also can't be expected to fit on one line and is, like the other string, inside a set literal.", ("parens should be stripped for short string in set")} + +S2 = {"This is a really long string that can't be expected to fit in one line and is the only child of a set literal."} + +T1 = ("The is a short string", "This is a really long string that can't possibly be expected to fit all together on one line. Also it is inside a tuple literal, so it's expected to be wrapped in parens when splitting to avoid implicit str concatenation.", short_call("arg", {"key": "value"}), "This is another really really (not really) long string that also can't be expected to fit on one line and is, like the other string, inside a tuple literal.", ("parens should be stripped for short string in list")) + +T2 = ("This is a really long string that can't be expected to fit in one line and is the only child of a tuple literal.",) + +func_with_keywords(my_arg, my_kwarg="Long keyword strings also need to be wrapped, but they will probably need to be handled a little bit differently.") + +bad_split1 = ( + 'But what should happen when code has already been formatted but in the wrong way? Like' + " with a space at the end instead of the beginning. Or what about when it is split too soon?" +) + +bad_split2 = "But what should happen when code has already " \ + "been formatted but in the wrong way? Like " \ + "with a space at the end instead of the " \ + "beginning. Or what about when it is split too " \ + "soon? In the case of a split that is too " \ + "short, black will try to honer the custom " \ + "split." + +bad_split3 = ( + "What if we have inline comments on " # First Comment + "each line of a bad split? In that " # Second Comment + "case, we should just leave it alone." # Third Comment +) + +bad_split_func1( + "But what should happen when code has already " + "been formatted but in the wrong way? Like " + "with a space at the end instead of the " + "beginning. Or what about when it is split too " + "soon? In the case of a split that is too " + "short, black will try to honer the custom " + "split.", + xxx, yyy, zzz +) + +bad_split_func2( + xxx, yyy, zzz, + long_string_kwarg="But what should happen when code has already been formatted but in the wrong way? Like " + "with a space at the end instead of the beginning. Or what about when it is split too " + "soon?", +) + +bad_split_func3( + ( + "But what should happen when code has already " + r"been formatted but in the wrong way? Like " + "with a space at the end instead of the " + r"beginning. Or what about when it is split too " + r"soon? In the case of a split that is too " + "short, black will try to honer the custom " + "split." + ), + xxx, + yyy, + zzz, +) + +inline_comments_func1( + "if there are inline " + "comments in the middle " + # Here is the standard alone comment. + "of the implicitly concatenated " + "string, we should handle " + "them correctly", + xxx, +) + +inline_comments_func2( + "what if the string is very very very very very very very very very very long and this part does " + "not fit into a single line? " + # Here is the standard alone comment. + "then the string should still be properly handled by merging and splitting " + "it into parts that fit in line length.", + xxx, +) + +raw_string = r"This is a long raw string. When re-formatting this string, black needs to make sure it prepends the 'r' onto the new string." + +fmt_string1 = "We also need to be sure to preserve any and all {} which may or may not be attached to the string in question.".format("method calls") + +fmt_string2 = "But what about when the string is {} but {}".format("short", "the method call is really really really really really really really really long?") + +old_fmt_string1 = "While we are on the topic of %s, we should also note that old-style formatting must also be preserved, since some %s still uses it." % ("formatting", "code") + +old_fmt_string2 = "This is a %s %s %s %s" % ("really really really really really", "old", "way to format strings!", "Use f-strings instead!") + +old_fmt_string3 = "Whereas only the strings after the percent sign were long in the last example, this example uses a long initial string as well. This is another %s %s %s %s" % ("really really really really really", "old", "way to format strings!", "Use f-strings instead!") + +fstring = f"f-strings definitely make things more {difficult} than they need to be for {{black}}. But boy they sure are handy. The problem is that some lines will need to have the 'f' whereas others do not. This {line}, for example, needs one." + +fstring_with_no_fexprs = f"Some regular string that needs to get split certainly but is NOT an fstring by any means whatsoever." + +comment_string = "Long lines with inline comments should have their comments appended to the reformatted string's enclosing right parentheses." # This comment gets thrown to the top. + +arg_comment_string = print("Long lines with inline comments which are apart of (and not the only member of) an argument list should have their comments appended to the reformatted string's enclosing left parentheses.", # This comment gets thrown to the top. + "Arg #2", "Arg #3", "Arg #4", "Arg #5") + +pragma_comment_string1 = "Lines which end with an inline pragma comment of the form `# : <...>` should be left alone." # noqa: E501 + +pragma_comment_string2 = "Lines which end with an inline pragma comment of the form `# : <...>` should be left alone." # noqa + +"""This is a really really really long triple quote string and it should not be touched.""" + +triple_quote_string = """This is a really really really long triple quote string assignment and it should not be touched.""" + +assert some_type_of_boolean_expression, "Followed by a really really really long string that is used to provide context to the AssertionError exception." + +assert some_type_of_boolean_expression, "Followed by a really really really long string that is used to provide context to the AssertionError exception, which uses dynamic string {}.".format("formatting") + +assert some_type_of_boolean_expression, "Followed by a really really really long string that is used to provide context to the AssertionError exception, which uses dynamic string %s." % "formatting" + +assert some_type_of_boolean_expression, "Followed by a really really really long string that is used to provide context to the AssertionError exception, which uses dynamic %s %s." % ("string", "formatting") + +some_function_call("With a reallly generic name and with a really really long string that is, at some point down the line, " + added + " to a variable and then added to another string.") + +some_function_call("With a reallly generic name and with a really really long string that is, at some point down the line, " + added + " to a variable and then added to another string. But then what happens when the final string is also supppppperrrrr long?! Well then that second (realllllllly long) string should be split too.", "and a second argument", and_a_third) + +return "A really really really really really really really really really really really really really long {} {}".format("return", "value") + +func_with_bad_comma( + "This is a really long string argument to a function that has a trailing comma which should NOT be there.", +) + +func_with_bad_comma( + "This is a really long string argument to a function that has a trailing comma which should NOT be there.", # comment after comma +) + +func_with_bad_comma( + ( + "This is a really long string argument to a function that has a trailing comma" + " which should NOT be there." + ), +) + +func_with_bad_comma( + ( + "This is a really long string argument to a function that has a trailing comma" + " which should NOT be there." + ), # comment after comma +) + +func_with_bad_parens_that_wont_fit_in_one_line( + ("short string that should have parens stripped"), + x, + y, + z +) + +func_with_bad_parens_that_wont_fit_in_one_line( + x, + y, + ("short string that should have parens stripped"), + z +) + +func_with_bad_parens( + ("short string that should have parens stripped"), + x, + y, + z, +) + +func_with_bad_parens( + x, + y, + ("short string that should have parens stripped"), + z, +) + +annotated_variable: Final = "This is a large " + STRING + " that has been " + CONCATENATED + "using the '+' operator." +annotated_variable: Final = "This is a large string that has a type annotation attached to it. A type annotation should NOT stop a long string from being wrapped." +annotated_variable: Literal["fakse_literal"] = "This is a large string that has a type annotation attached to it. A type annotation should NOT stop a long string from being wrapped." + +backslashes = "This is a really long string with \"embedded\" double quotes and 'single' quotes that also handles checking for an even number of backslashes \\" +backslashes = "This is a really long string with \"embedded\" double quotes and 'single' quotes that also handles checking for an even number of backslashes \\\\" +backslashes = "This is a really 'long' string with \"embedded double quotes\" and 'single' quotes that also handles checking for an odd number of backslashes \\\", like this...\\\\\\" + +short_string = ( + "Hi" + " there." +) + +func_call( + short_string=( + "Hi" + " there." + ) +) + +raw_strings = r"Don't" " get" r" merged" " unless they are all raw." + +def foo(): + yield "This is a really long string that can't possibly be expected to fit all together on one line. In fact it may even take up three or more lines... like four or five... but probably just three." + +x = f"This is a {{really}} long string that needs to be split without a doubt (i.e. most definitely). In short, this {string} that can't possibly be {{expected}} to fit all together on one line. In {fact} it may even take up three or more lines... like four or five... but probably just four." + +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 + " of it." +) + +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" # noqa + " of it." +) + +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" # pylint: disable=some-pylint-check + " of it." +) + +string_with_nameescape = ( + "........................................................................ \N{LAO KO LA}" +) + +string_with_nameescape = ( + "........................................................................... \N{LAO KO LA}" +) + +string_with_nameescape = ( + "............................................................................ \N{LAO KO LA}" +) + +string_with_nameescape_and_escaped_backslash = ( + "...................................................................... \\\N{LAO KO LA}" +) + +string_with_nameescape_and_escaped_backslash = ( + "......................................................................... \\\N{LAO KO LA}" +) + +string_with_nameescape_and_escaped_backslash = ( + ".......................................................................... \\\N{LAO KO LA}" +) + +string_with_escaped_nameescape = ( + "........................................................................ \\N{LAO KO LA}" +) + +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" + +dict_with_lambda_values = { + "join": lambda j: ( + f"{j.__class__.__name__}({some_function_call(j.left)}, " + f"{some_function_call(j.right)})" + ), +} + +# Complex string concatenations with a method call in the middle. +code = ( + (" return [\n") + + ( + ", \n".join( + " (%r, self.%s, visitor.%s)" + % (attrname, attrname, visit_name) + for attrname, visit_name in names + ) + ) + + ("\n ]\n") +) + + +# Test case of an outer string' parens enclose an inner string's parens. +call(body=("%s %s" % ((",".join(items)), suffix))) + +log.info(f'Skipping: {desc["db_id"]=} {desc["ms_name"]} {money=} {dte=} {pos_share=} {desc["status"]=} {desc["exposure_max"]=}') + +log.info(f"Skipping: {desc['db_id']=} {desc['ms_name']} {money=} {dte=} {pos_share=} {desc['status']=} {desc['exposure_max']=}") + +log.info(f'Skipping: {desc["db_id"]} {foo("bar",x=123)} {"foo" != "bar"} {(x := "abc=")} {pos_share=} {desc["status"]} {desc["exposure_max"]}') + +log.info(f'Skipping: {desc["db_id"]} {desc["ms_name"]} {money=} {(x := "abc=")=} {pos_share=} {desc["status"]} {desc["exposure_max"]}') + +log.info(f'Skipping: {desc["db_id"]} {foo("bar",x=123)=} {money=} {dte=} {pos_share=} {desc["status"]} {desc["exposure_max"]}') + +log.info(f'Skipping: {foo("asdf")=} {desc["ms_name"]} {money=} {dte=} {pos_share=} {desc["status"]} {desc["exposure_max"]}') + +log.info(f'Skipping: {"a" == "b" == "c" == "d"} {desc["ms_name"]} {money=} {dte=} {pos_share=} {desc["status"]} {desc["exposure_max"]}') + +log.info(f'Skipping: {"a" == "b" == "c" == "d"=} {desc["ms_name"]} {money=} {dte=} {pos_share=} {desc["status"]} {desc["exposure_max"]}') + +log.info(f'Skipping: { longer_longer_longer_longer_longer_longer_name [ "db_id" ] [ "another_key" ] = : .3f }') + +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"]}''') + +log.info(f"""Skipping: {'a' == 'b'} {desc['ms_name']} {money=} {dte=} {pos_share=} {desc['status']} {desc['exposure_max']}""") diff --git a/crates/ruff_python_formatter/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 new file mode 100644 index 0000000000..4b7bdd86d2 --- /dev/null +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_long_strings.py.expect @@ -0,0 +1,594 @@ +x = ( + "This is a really long string that can't possibly be expected to fit all together" + " on one line. In fact it may even take up three or more lines... like four or" + " five... but probably just three." +) + +x += ( + "This is a really long string that can't possibly be expected to fit all together" + " on one line. In fact it may even take up three or more lines... like four or" + " five... but probably just three." +) + +y = "Short string" + +print( + "This is a really long string inside of a print statement with extra arguments" + " attached at the end of it.", + x, + y, + z, +) + +print( + "This is a really long string inside of a print statement with no extra arguments" + " attached at the end of it." +) + +D1 = { + "The First": ( + "This is a really long string that can't possibly be expected to fit all" + " together on one line. Also it is inside a dictionary, so formatting is more" + " difficult." + ), + "The Second": ( + "This is another really really (not really) long string that also can't be" + " expected to fit on one line and is, like the other string, inside a" + " dictionary." + ), +} + +D2 = { + 1.0: ( + "This is a really long string that can't possibly be expected to fit all" + " together on one line. Also it is inside a dictionary, so formatting is more" + " difficult." + ), + 2.0: ( + "This is another really really (not really) long string that also can't be" + " expected to fit on one line and is, like the other string, inside a" + " dictionary." + ), +} + +D3 = { + x: ( + "This is a really long string that can't possibly be expected to fit all" + " together on one line. Also it is inside a dictionary, so formatting is more" + " difficult." + ), + y: ( + "This is another really really (not really) long string that also can't be" + " expected to fit on one line and is, like the other string, inside a" + " dictionary." + ), +} + +D4 = { + "A long and ridiculous {}".format(string_key): ( + "This is a really really really long string that has to go i,side of a" + " dictionary. It is soooo bad." + ), + some_func("calling", "some", "stuff"): ( + "This is a really really really long string that has to go inside of a" + " dictionary. It is {soooo} bad (#{x}).".format(sooo="soooo", x=2) + ), + "A %s %s" + % ("formatted", "string"): ( + "This is a really really really long string that has to go inside of a" + " dictionary. It is %s bad (#%d)." % ("soooo", 2) + ), +} + +D5 = { # Test for https://github.com/psf/black/issues/3261 + "This is a really long string that can't be expected to fit in one line and is used as a nested dict's key": { + "inner": "value" + }, +} + +D6 = { # Test for https://github.com/psf/black/issues/3261 + "This is a really long string that can't be expected to fit in one line and is used as a dict's key": [ + "value1", + "value2", + ], +} + +L1 = [ + "The is a short string", + ( + "This is a really long string that can't possibly be expected to fit all" + " together on one line. Also it is inside a list literal, so it's expected to" + " be wrapped in parens when splitting to avoid implicit str concatenation." + ), + short_call("arg", {"key": "value"}), + ( + "This is another really really (not really) long string that also can't be" + " expected to fit on one line and is, like the other string, inside a list" + " literal." + ), + "parens should be stripped for short string in list", +] + +L2 = [ + "This is a really long string that can't be expected to fit in one line and is the" + " only child of a list literal." +] + +S1 = { + "The is a short string", + ( + "This is a really long string that can't possibly be expected to fit all" + " together on one line. Also it is inside a set literal, so it's expected to be" + " wrapped in parens when splitting to avoid implicit str concatenation." + ), + short_call("arg", {"key": "value"}), + ( + "This is another really really (not really) long string that also can't be" + " expected to fit on one line and is, like the other string, inside a set" + " literal." + ), + "parens should be stripped for short string in set", +} + +S2 = { + "This is a really long string that can't be expected to fit in one line and is the" + " only child of a set literal." +} + +T1 = ( + "The is a short string", + ( + "This is a really long string that can't possibly be expected to fit all" + " together on one line. Also it is inside a tuple literal, so it's expected to" + " be wrapped in parens when splitting to avoid implicit str concatenation." + ), + short_call("arg", {"key": "value"}), + ( + "This is another really really (not really) long string that also can't be" + " expected to fit on one line and is, like the other string, inside a tuple" + " literal." + ), + "parens should be stripped for short string in list", +) + +T2 = ( + ( + "This is a really long string that can't be expected to fit in one line and is" + " the only child of a tuple literal." + ), +) + +func_with_keywords( + my_arg, + my_kwarg=( + "Long keyword strings also need to be wrapped, but they will probably need to" + " be handled a little bit differently." + ), +) + +bad_split1 = ( + "But what should happen when code has already been formatted but in the wrong way?" + " Like with a space at the end instead of the beginning. Or what about when it is" + " split too soon?" +) + +bad_split2 = ( + "But what should happen when code has already " + "been formatted but in the wrong way? Like " + "with a space at the end instead of the " + "beginning. Or what about when it is split too " + "soon? In the case of a split that is too " + "short, black will try to honer the custom " + "split." +) + +bad_split3 = ( + "What if we have inline comments on " # First Comment + "each line of a bad split? In that " # Second Comment + "case, we should just leave it alone." # Third Comment +) + +bad_split_func1( + "But what should happen when code has already " + "been formatted but in the wrong way? Like " + "with a space at the end instead of the " + "beginning. Or what about when it is split too " + "soon? In the case of a split that is too " + "short, black will try to honer the custom " + "split.", + xxx, + yyy, + zzz, +) + +bad_split_func2( + xxx, + yyy, + zzz, + long_string_kwarg=( + "But what should happen when code has already been formatted but in the wrong" + " way? Like with a space at the end instead of the beginning. Or what about" + " when it is split too soon?" + ), +) + +bad_split_func3( + ( + "But what should happen when code has already " + r"been formatted but in the wrong way? Like " + "with a space at the end instead of the " + r"beginning. Or what about when it is split too " + r"soon? In the case of a split that is too " + "short, black will try to honer the custom " + "split." + ), + xxx, + yyy, + zzz, +) + +inline_comments_func1( + "if there are inline comments in the middle " + # Here is the standard alone comment. + "of the implicitly concatenated string, we should handle them correctly", + xxx, +) + +inline_comments_func2( + "what if the string is very very very very very very very very very very long and" + " this part does not fit into a single line? " + # Here is the standard alone comment. + "then the string should still be properly handled by merging and splitting " + "it into parts that fit in line length.", + xxx, +) + +raw_string = ( + r"This is a long raw string. When re-formatting this string, black needs to make" + r" sure it prepends the 'r' onto the new string." +) + +fmt_string1 = ( + "We also need to be sure to preserve any and all {} which may or may not be" + " attached to the string in question.".format("method calls") +) + +fmt_string2 = "But what about when the string is {} but {}".format( + "short", + "the method call is really really really really really really really really long?", +) + +old_fmt_string1 = ( + "While we are on the topic of %s, we should also note that old-style formatting" + " must also be preserved, since some %s still uses it." % ("formatting", "code") +) + +old_fmt_string2 = "This is a %s %s %s %s" % ( + "really really really really really", + "old", + "way to format strings!", + "Use f-strings instead!", +) + +old_fmt_string3 = ( + "Whereas only the strings after the percent sign were long in the last example," + " this example uses a long initial string as well. This is another %s %s %s %s" + % ( + "really really really really really", + "old", + "way to format strings!", + "Use f-strings instead!", + ) +) + +fstring = ( + f"f-strings definitely make things more {difficult} than they need to be for" + " {black}. But boy they sure are handy. The problem is that some lines will need" + f" to have the 'f' whereas others do not. This {line}, for example, needs one." +) + +fstring_with_no_fexprs = ( + f"Some regular string that needs to get split certainly but is NOT an fstring by" + f" any means whatsoever." +) + +comment_string = ( # This comment gets thrown to the top. + "Long lines with inline comments should have their comments appended to the" + " reformatted string's enclosing right parentheses." +) + +arg_comment_string = print( + "Long lines with inline comments which are apart of (and not the only member of) an" + " argument list should have their comments appended to the reformatted string's" + " enclosing left parentheses.", # This comment gets thrown to the top. + "Arg #2", + "Arg #3", + "Arg #4", + "Arg #5", +) + +pragma_comment_string1 = "Lines which end with an inline pragma comment of the form `# : <...>` should be left alone." # noqa: E501 + +pragma_comment_string2 = "Lines which end with an inline pragma comment of the form `# : <...>` should be left alone." # noqa + +"""This is a really really really long triple quote string and it should not be touched.""" + +triple_quote_string = """This is a really really really long triple quote string assignment and it should not be touched.""" + +assert some_type_of_boolean_expression, ( + "Followed by a really really really long string that is used to provide context to" + " the AssertionError exception." +) + +assert some_type_of_boolean_expression, ( + "Followed by a really really really long string that is used to provide context to" + " the AssertionError exception, which uses dynamic string {}.".format("formatting") +) + +assert some_type_of_boolean_expression, ( + "Followed by a really really really long string that is used to provide context to" + " the AssertionError exception, which uses dynamic string %s." % "formatting" +) + +assert some_type_of_boolean_expression, ( + "Followed by a really really really long string that is used to provide context to" + " the AssertionError exception, which uses dynamic %s %s." + % ("string", "formatting") +) + +some_function_call( + "With a reallly generic name and with a really really long string that is, at some" + " point down the line, " + + added + + " to a variable and then added to another string." +) + +some_function_call( + "With a reallly generic name and with a really really long string that is, at some" + " point down the line, " + + added + + " to a variable and then added to another string. But then what happens when the" + " final string is also supppppperrrrr long?! Well then that second (realllllllly" + " long) string should be split too.", + "and a second argument", + and_a_third, +) + +return ( + "A really really really really really really really really really really really" + " really really long {} {}".format("return", "value") +) + +func_with_bad_comma( + "This is a really long string argument to a function that has a trailing comma" + " which should NOT be there.", +) + +func_with_bad_comma( + "This is a really long string argument to a function that has a trailing comma" + " which should NOT be there.", # comment after comma +) + +func_with_bad_comma( + "This is a really long string argument to a function that has a trailing comma" + " which should NOT be there.", +) + +func_with_bad_comma( + "This is a really long string argument to a function that has a trailing comma" + " which should NOT be there.", # comment after comma +) + +func_with_bad_parens_that_wont_fit_in_one_line( + "short string that should have parens stripped", x, y, z +) + +func_with_bad_parens_that_wont_fit_in_one_line( + x, y, "short string that should have parens stripped", z +) + +func_with_bad_parens( + "short string that should have parens stripped", + x, + y, + z, +) + +func_with_bad_parens( + x, + y, + "short string that should have parens stripped", + z, +) + +annotated_variable: Final = ( + "This is a large " + + STRING + + " that has been " + + CONCATENATED + + "using the '+' operator." +) +annotated_variable: Final = ( + "This is a large string that has a type annotation attached to it. A type" + " annotation should NOT stop a long string from being wrapped." +) +annotated_variable: Literal["fakse_literal"] = ( + "This is a large string that has a type annotation attached to it. A type" + " annotation should NOT stop a long string from being wrapped." +) + +backslashes = ( + "This is a really long string with \"embedded\" double quotes and 'single' quotes" + " that also handles checking for an even number of backslashes \\" +) +backslashes = ( + "This is a really long string with \"embedded\" double quotes and 'single' quotes" + " that also handles checking for an even number of backslashes \\\\" +) +backslashes = ( + "This is a really 'long' string with \"embedded double quotes\" and 'single' quotes" + ' that also handles checking for an odd number of backslashes \\", like' + " this...\\\\\\" +) + +short_string = "Hi there." + +func_call(short_string="Hi there.") + +raw_strings = r"Don't" " get" r" merged" " unless they are all raw." + + +def foo(): + yield ( + "This is a really long string that can't possibly be expected to fit all" + " together on one line. In fact it may even take up three or more lines... like" + " four or five... but probably just three." + ) + + +x = ( + "This is a {really} long string that needs to be split without a doubt (i.e." + f" most definitely). In short, this {string} that can't possibly be {{expected}} to" + f" fit all together on one line. In {fact} it may even take up three or more" + " lines... like four or five... but probably just four." +) + +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 + " of it." +) + +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" # noqa + " of it." +) + +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" # pylint: disable=some-pylint-check + " of it." +) + +string_with_nameescape = ( + "........................................................................" + " \N{LAO KO LA}" +) + +string_with_nameescape = ( + "..........................................................................." + " \N{LAO KO LA}" +) + +string_with_nameescape = ( + "............................................................................" + " \N{LAO KO LA}" +) + +string_with_nameescape_and_escaped_backslash = ( + "......................................................................" + " \\\N{LAO KO LA}" +) + +string_with_nameescape_and_escaped_backslash = ( + "........................................................................." + " \\\N{LAO KO LA}" +) + +string_with_nameescape_and_escaped_backslash = ( + ".........................................................................." + " \\\N{LAO KO LA}" +) + +string_with_escaped_nameescape = ( + "........................................................................ \\N{LAO" + " KO LA}" +) + +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" + ) +) + +dict_with_lambda_values = { + "join": lambda j: ( + f"{j.__class__.__name__}({some_function_call(j.left)}, " + f"{some_function_call(j.right)})" + ), +} + +# Complex string concatenations with a method call in the middle. +code = ( + " return [\n" + + ", \n".join( + " (%r, self.%s, visitor.%s)" % (attrname, attrname, visit_name) + for attrname, visit_name in names + ) + + "\n ]\n" +) + + +# Test case of an outer string' parens enclose an inner string's parens. +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"]=}' +) + +log.info( + "Skipping:" + f" {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']}" +) + +log.info( + "Skipping:" + f' {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"]}' +) + +log.info( + "Skipping:" + f' {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']}" +) + +log.info( + "Skipping:" + f' {"a" == "b" == "c" == "d"=} {desc["ms_name"]} {money=} {dte=} {pos_share=} {desc["status"]} {desc["exposure_max"]}' +) + +log.info( + "Skipping:" + f' { longer_longer_longer_longer_longer_longer_name [ "db_id" ] [ "another_key" ] = : .3f }' +) + +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"]}""" +) + +log.info( + f"""Skipping: {'a' == 'b'} {desc['ms_name']} {money=} {dte=} {pos_share=} {desc['status']} {desc['exposure_max']}""" +) diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_long_strings__east_asian_width.options.json b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_long_strings__east_asian_width.options.json new file mode 100644 index 0000000000..ce7c52b163 --- /dev/null +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_long_strings__east_asian_width.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_long_strings__east_asian_width.py b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_long_strings__east_asian_width.py new file mode 100644 index 0000000000..e807dc2035 --- /dev/null +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_long_strings__east_asian_width.py @@ -0,0 +1,6 @@ +# The following strings do not have not-so-many chars, but are long enough +# when these are rendered in a monospace font (if the renderer respects +# Unicode East Asian Width properties). +hangul = '코드포인트 수는 적으나 실제 터미널이나 에디터에서 렌더링될 땐 너무 길어서 줄바꿈이 필요한 문자열' +hanzi = '中文測試:代碼點數量少,但在真正的終端模擬器或編輯器中呈現時太長,因此需要換行的字符串。' +japanese = 'コードポイントの数は少ないが、実際の端末エミュレータやエディタでレンダリングされる時は長すぎる為、改行が要る文字列' diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_long_strings__east_asian_width.py.expect b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_long_strings__east_asian_width.py.expect new file mode 100644 index 0000000000..c5c05b31fa --- /dev/null +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_long_strings__east_asian_width.py.expect @@ -0,0 +1,16 @@ +# The following strings do not have not-so-many chars, but are long enough +# when these are rendered in a monospace font (if the renderer respects +# Unicode East Asian Width properties). +hangul = ( + "코드포인트 수는 적으나 실제 터미널이나 에디터에서 렌더링될 땐 너무 길어서 줄바꿈이" + " 필요한 문자열" +) +hanzi = ( + "中文測試:代碼點數量少,但在真正的終端模擬器或編輯器中呈現時太長," + "因此需要換行的字符串。" +) +japanese = ( + "コードポイントの数は少ないが、" + "実際の端末エミュレータやエディタでレンダリングされる時は長すぎる為、" + "改行が要る文字列" +) diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_long_strings__edge_case.options.json b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_long_strings__edge_case.options.json new file mode 100644 index 0000000000..ce7c52b163 --- /dev/null +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_long_strings__edge_case.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_long_strings__edge_case.py b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_long_strings__edge_case.py new file mode 100644 index 0000000000..c747ad3886 --- /dev/null +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_long_strings__edge_case.py @@ -0,0 +1,37 @@ +some_variable = "This string is long but not so long that it needs to be split just yet" +some_variable = 'This string is long but not so long that it needs to be split just yet' +some_variable = "This string is long, just long enough that it needs to be split, u get?" +some_variable = 'This string is long, just long enough that it needs to be split, u get?' +some_variable = "This string is long, just long enough that it needs to be split, u get? So we stay" +some_variable = 'This string is long, just long enough that it needs to be split, u get? So we stay' +some_variable = "This string is long, just long enough that it needs to be split, u get? So we split" +some_variable = 'This string is long, just long enough that it needs to be split, u get? So we split' +some_variable = "This string is long but not so long that it needs hahahah toooooo be so greatttt {} that I just can't think of any more good words to say about it at alll".format("ha") +some_variable = "This string is long but not so long that it needs hahahah toooooo be so greatttt {} that I just can't think of any more good words to say about it at allll".format("ha") +some_variable = "This string is long but not so long that it needs hahahah toooooo be so greatttt {} that I just can't think of any more good words to say about it at alllllllllll".format("ha") +some_variable = "This string is long but not so long that it needs hahahah toooooo be so greatttt {} that I just can't think of any more good words to say about it at allllllllllll".format("ha") +some_variable = "This is a long string that will end with a method that is not calleddd".format +addition_inside_tuple = ( + some_string_inside_a_variable + + "Some string that is just long enough to cause a split to take place.............", + xyz, + "Some really long string that needs to get split eventually but I'm running out of things to say" + some_string_inside_a_variable +) +addition_inside_tuple = ( + some_string_inside_a_variable + + "Some string that is just long enough to cause a split to take place.............." +) +return "Hi there. This is areally really reallllly long string that needs to be split!!!" +ternary_expression = ( + "Short String" + if some_condition + else "This is a really long string that will eventually need to be split right here." +) +return f'{x}/b/c/d/d/d/dadfjsadjsaidoaisjdsfjaofjdfijaidfjaodfjaoifjodjafojdoajaaaaaaaaaaa' +return f'{x}/b/c/d/d/d/dadfjsadjsaidoaisjdsfjaofjdfijaidfjaodfjaoifjodjafojdoajaaaaaaaaaaaa' +assert str(result) == "This long string should be split at some point right close to or around hereeeeeee" +assert str(result) < "This long string should be split at some point right close to or around hereeeeee" +assert "A format string: %s" % "This long string should be split at some point right close to or around hereeeeeee" != result +msg += "This long string should be wrapped in parens at some point right around hereeeee" +msg += "This long string should be split at some point right close to or around hereeeeeeee" +msg += "This long string should not be split at any point ever since it is just righttt" diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_long_strings__edge_case.py.expect b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_long_strings__edge_case.py.expect new file mode 100644 index 0000000000..12ffc671d2 --- /dev/null +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_long_strings__edge_case.py.expect @@ -0,0 +1,98 @@ +some_variable = "This string is long but not so long that it needs to be split just yet" +some_variable = "This string is long but not so long that it needs to be split just yet" +some_variable = ( + "This string is long, just long enough that it needs to be split, u get?" +) +some_variable = ( + "This string is long, just long enough that it needs to be split, u get?" +) +some_variable = ( + "This string is long, just long enough that it needs to be split, u get? So we stay" +) +some_variable = ( + "This string is long, just long enough that it needs to be split, u get? So we stay" +) +some_variable = ( + "This string is long, just long enough that it needs to be split, u get? So we" + " split" +) +some_variable = ( + "This string is long, just long enough that it needs to be split, u get? So we" + " split" +) +some_variable = ( + "This string is long but not so long that it needs hahahah toooooo be so greatttt" + " {} that I just can't think of any more good words to say about it at alll".format( + "ha" + ) +) +some_variable = ( + "This string is long but not so long that it needs hahahah toooooo be so greatttt" + " {} that I just can't think of any more good words to say about it at allll" + .format("ha") +) +some_variable = ( + "This string is long but not so long that it needs hahahah toooooo be so greatttt" + " {} that I just can't think of any more good words to say about it at alllllllllll" + .format("ha") +) +some_variable = ( + "This string is long but not so long that it needs hahahah toooooo be so greatttt" + " {} that I just can't think of any more good words to say about it at" + " allllllllllll".format("ha") +) +some_variable = ( + "This is a long string that will end with a method that is not calleddd".format +) +addition_inside_tuple = ( + some_string_inside_a_variable + + "Some string that is just long enough to cause a split to take" + " place.............", + xyz, + "Some really long string that needs to get split eventually but I'm running out of" + " things to say" + + some_string_inside_a_variable, +) +addition_inside_tuple = ( + some_string_inside_a_variable + + "Some string that is just long enough to cause a split to take" + " place.............." +) +return ( + "Hi there. This is areally really reallllly long string that needs to be split!!!" +) +ternary_expression = ( + "Short String" + if some_condition + else ( + "This is a really long string that will eventually need to be split right here." + ) +) +return ( + f"{x}/b/c/d/d/d/dadfjsadjsaidoaisjdsfjaofjdfijaidfjaodfjaoifjodjafojdoajaaaaaaaaaaa" +) +return f"{x}/b/c/d/d/d/dadfjsadjsaidoaisjdsfjaofjdfijaidfjaodfjaoifjodjafojdoajaaaaaaaaaaaa" +assert ( + str(result) + == "This long string should be split at some point right close to or around" + " hereeeeeee" +) +assert ( + str(result) + < "This long string should be split at some point right close to or around" + " hereeeeee" +) +assert ( + "A format string: %s" + % "This long string should be split at some point right close to or around" + " hereeeeeee" + != result +) +msg += ( + "This long string should be wrapped in parens at some point right around hereeeee" +) +msg += ( + "This long string should be split at some point right close to or around" + " hereeeeeeee" +) +msg += "This long string should not be split at any point ever since it is just righttt" diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_long_strings__regression.options.json b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_long_strings__regression.options.json new file mode 100644 index 0000000000..ce7c52b163 --- /dev/null +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_long_strings__regression.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_long_strings__regression.py b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_long_strings__regression.py new file mode 100644 index 0000000000..74b6cd43a2 --- /dev/null +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_long_strings__regression.py @@ -0,0 +1,561 @@ +class A: + def foo(): + result = type(message)("") + + +# Don't merge multiline (e.g. triple-quoted) strings. +def foo(): + query = ( + """SELECT xxxxxxxxxxxxxxxxxxxx(xxx)""" + """ FROM xxxxxxxxxxxxxxxx WHERE xxxxxxxxxx AND xxx <> xxxxxxxxxxxxxx()""") + +# There was a bug where tuples were being identified as long strings. +long_tuple = ('Apple', 'Berry', 'Cherry', 'Dill', 'Evergreen', 'Fig', + 'Grape', 'Harry', 'Iglu', 'Jaguar') + +stupid_format_method_bug = "Some really long string that just so happens to be the {} {} to force the 'format' method to hang over the line length boundary. This is pretty annoying.".format("perfect", "length") + +class A: + def foo(): + os.system("This is a regression test. xxx xxx xxx xxx xxx xxx xxx xxx xxx xxx xxx xxx xxx xxx xxx xxx xxx xxx xxx xxx xxx xxx xxx xxx xxx xxx xxx xxx xxx xxx xxxx.".format("xxxxxxxxxx", "xxxxxx", "xxxxxxxxxx")) + + +class A: + def foo(): + XXXXXXXXXXXX.append( + ( + "xxx_xxxxxxxxxx(xxxxx={}, xxxx={}, xxxxx, xxxx_xxxx_xxxxxxxxxx={})".format( + xxxxx, xxxx, xxxx_xxxx_xxxxxxxxxx + ), + my_var, + my_other_var, + ) + ) + +class A: + class B: + def foo(): + bar( + ( + "[{}]: xxx_xxxxxxxxxx(xxxxx={}, xxxx={}, xxxxx={}" + " xxxx_xxxx_xxxxxxxxxx={}, xxxx={})" + .format(xxxx._xxxxxxxxxxxxxx, xxxxx, xxxx, xxxx_xxxx_xxxxxxxxxx, xxxxxxx) + ), + varX, + varY, + varZ, + ) + +def foo(xxxx): + for (xxx_xxxx, _xxx_xxx, _xxx_xxxxx, xxx_xxxx) in xxxx: + for xxx in xxx_xxxx: + assert ("x" in xxx) or ( + xxx in xxx_xxx_xxxxx + ), "{0} xxxxxxx xx {1}, xxx {1} xx xxx xx xxxx xx xxx xxxx: xxx xxxx {2}".format( + xxx_xxxx, xxx, xxxxxx.xxxxxxx(xxx_xxx_xxxxx) + ) + +class A: + def disappearing_comment(): + return ( + ( # xx -x xxxxxxx xx xxx xxxxxxx. + '{{xxx_xxxxxxxxxx_xxxxxxxx}} xxx xxxx' + ' {} {{xxxx}} >&2' + .format( + "{xxxx} {xxxxxx}" + if xxxxx.xx_xxxxxxxxxx + else ( # Disappearing Comment + "--xxxxxxx --xxxxxx=x --xxxxxx-xxxxx=xxxxxx" + " --xxxxxx-xxxx=xxxxxxxxxxx.xxx" + ) + ) + ), + (x, y, z), + ) + +class A: + class B: + def foo(): + xxxxx_xxxx( + xx, "\t" + "@xxxxxx '{xxxx_xxx}\t' > {xxxxxx_xxxx}.xxxxxxx;" + "{xxxx_xxx} >> {xxxxxx_xxxx}.xxxxxxx 2>&1; xx=$$?;" + "xxxx $$xx" + .format(xxxx_xxx=xxxx_xxxxxxx, xxxxxx_xxxx=xxxxxxx + "/" + xxxx_xxx_xxxx, x=xxx_xxxxx_xxxxx_xxx), + x, + y, + z, + ) + +func_call_where_string_arg_has_method_call_and_bad_parens( + ( + "A long string with {}. This string is so long that it is ridiculous. It can't fit on one line at alllll.".format("formatting") + ), +) + +func_call_where_string_arg_has_old_fmt_and_bad_parens( + ( + "A long string with {}. This string is so long that it is ridiculous. It can't fit on one line at alllll." % "formatting" + ), +) + +func_call_where_string_arg_has_old_fmt_and_bad_parens( + ( + "A long string with {}. This {} is so long that it is ridiculous. It can't fit on one line at alllll." % ("formatting", "string") + ), +) + +class A: + def append(self): + if True: + xxxx.xxxxxxx.xxxxx( ('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' + % (xxxx.xxxxxxxxx, + xxxx.xxxxxxxxxxxxxx(xxxx.xxxxxxxxx), + x, + xxxx.xxxxxxxxxxxxxx( xx) + ))) + +class A: + def foo(): + some_func_call( + 'xxxxxxxxxx', + ( + "xx {xxxxxxxxxxx}/xxxxxxxxxxx.xxx xxxx.xxx && xxxxxx -x " + "\"xxxx xxxxxxx xxxxxx xxxx; xxxx xxxxxx_xxxxx xxxxxx xxxx; " + "xxxx.xxxx_xxxxxx(['xxxx.xxx'], xxxx.xxxxxxx().xxxxxxxxxx)\" " + ), + None, + ('xxxxxxxxxxx',), + ), + +class A: + def foo(): + some_func_call( + ( + "xx {xxxxxxxxxxx}/xxxxxxxxxxx.xxx xxxx.xxx && xxxxxx -x " + "xxxx, ('xxxxxxx xxxxxx xxxx, xxxx') xxxxxx_xxxxx xxxxxx xxxx; " + "xxxx.xxxx_xxxxxx(['xxxx.xxx'], xxxx.xxxxxxx().xxxxxxxxxx)\" " + ), + None, + ('xxxxxxxxxxx',), + ), + +xxxxxxx = { 'xx' : 'xxxx xxxxxxx xxxxxxxxx -x xxx -x /xxx/{0} -x xxx,xxx -xx {1} \ +-xx {1} -xx xxx=xxx_xxxx,xxx_xx,xxx_xxx,xxx_xxxx,xxx_xx,xxx_xxx |\ + xxxxxx -x xxxxxxxx -x xxxxxxxx -x', + 'xx' : 'xxxx xxxxxxx xxxxxxxxx -x xxx -x /xxx/{0} -x xxx,xxx -xx {1} \ +-xx {1} -xx xxx=xxx_xxxx_xxx_xxxx,xxx_xx_xxx_xxxx,xxx_xxxx_xxx_xxxx,\ +xxx_xx_xxxx_xxxx,xxx_xxx_xxxx,xxx_xxx_xxxx xxxx=xxx | xxxxxx -x xxxxxxxx -x xxxxxxxx -x' +} + +class A: + def foo(self): + if True: + xxxxx_xxxxxxxxxxxx('xxx xxxxxx xxx xxxxxxxxx.xx xx xxxxxxxx. xxx xxxxxxxxxxxxx.xx xxxxxxx ' + + 'xx xxxxxx xxxxxx xxxxxx xx xxxxxxx xxx xxx ${0} xx x xxxxxxxx xxxxx'.xxxxxx(xxxxxx_xxxxxx_xxx)) + +class A: + class B: + def foo(): + row = { + 'xxxxxxxxxxxxxxx' : xxxxxx_xxxxx_xxxx, + # 'xxxxxxxxxxxxxxxxxxxxxxx' + # 'xxxxxxxxxxxxxxxxxxxxxx' + # 'xxxxxxxxxxxxxxxxxx' + # 'xxxxxxxxxxxxxxxxx' + 'xxxxxxxxxx' : xxxxx_xxxxx, + } + +class A: + def xxxx_xxx_xx_xxxxxxxxxx_xxxx_xxxxxxxxx(xxxx): + xxxxxxxx = [ + xxxxxxxxxxxxxxxx( + 'xxxx', + xxxxxxxxxxx={ + 'xxxx' : 1.0, + }, + xxxxxx={'xxxxxx 1' : xxxxxx(xxxx='xxxxxx 1', xxxxxx=600.0)}, + xxxxxxxx_xxxxxxx=0.0, + ), + xxxxxxxxxxxxxxxx( + 'xxxxxxx', + xxxxxxxxxxx={ + 'xxxx' : 1.0, + }, + xxxxxx={'xxxxxx 1' : xxxxxx(xxxx='xxxxxx 1', xxxxxx=200.0)}, + xxxxxxxx_xxxxxxx=0.0, + ), + xxxxxxxxxxxxxxxx( + 'xxxx', + ), + ] + +some_dictionary = { + 'xxxxx006': ['xxx-xxx xxxxx3xxxx1xx2xxxxxxxxxxxxxx0xx6xxxxxxxxxx2xxxxxx9xxxxxxxxxx0xxxxx1xxx2x/xx9xx6+x+xxxxxxxxxxxxxx4xxxxxxxxxxxxxxxxxxxxx43xxx2xx2x4x++xxx6xxxxxxxxx+xxxxx/xx9x+xxxxxxxxxxxxxx8x15xxxxxxxxxxxxxxxxx82xx/xxxxxxxxxxxxxx/x5xxxxxxxxxxxxxx6xxxxxx74x4/xxx4x+xxxxxxxxx2xxxxxxxx87xxxxx4xxxxxxxx3xx0xxxxx4xxx1xx9xx5xxxxxxx/xxxxx5xx6xx4xxxx1x/x2xxxxxxxxxxxx64xxxxxxx1x0xx5xxxxxxxxxxxxxx== xxxxx000 xxxxxxxxxx\n', + 'xxx-xxx xxxxx3xxxx1xx2xxxxxxxxxxxxxx6xxxxxxxxxxxxxx9xxxxxxxxxxxxx3xxx9xxxxxxxxxxxxxxxx0xxxxxxxxxxxxxxxxx2xxxx2xxx6xxxxx/xx54xxxxxxxxx4xxx3xxxxxx9xx3xxxxx39xxxxxxxxx5xx91xxxx7xxxxxx8xxxxxxxxxxxxxxxx9xxx93xxxxxxxxxxxxxxxxx7xxx8xx8xx4/x1xxxxx1x3xxxxxxxxxxxxx3xxxxxx9xx4xx4x7xxxxxxxxxxxxx1xxxxxxxxx7xxxxxxxxxxxxxx4xx6xxxxxxxxx9xxx7xxxx2xxxxxxxxxxxxxxxxxxxxxx8xxxxxxxxxxxxxxxxxxxx6xx== xxxxx010 xxxxxxxxxx\n'], + 'xxxxx016': ['xxx-xxx xxxxx3xxxx1xx2xxxxxxxxxxxxxx0xx6xxxxxxxxxx2xxxxxx9xxxxxxxxxx0xxxxx1xxx2x/xx9xx6+x+xxxxxxxxxxxxxx4xxxxxxxxxxxxxxxxxxxxx43xxx2xx2x4x++xxx6xxxxxxxxx+xxxxx/xx9x+xxxxxxxxxxxxxx8x15xxxxxxxxxxxxxxxxx82xx/xxxxxxxxxxxxxx/x5xxxxxxxxxxxxxx6xxxxxx74x4/xxx4x+xxxxxxxxx2xxxxxxxx87xxxxx4xxxxxxxx3xx0xxxxx4xxx1xx9xx5xxxxxxx/xxxxx5xx6xx4xxxx1x/x2xxxxxxxxxxxx64xxxxxxx1x0xx5xxxxxxxxxxxxxx== xxxxx000 xxxxxxxxxx\n', + 'xxx-xxx xxxxx3xxxx1xx2xxxxxxxxxxxxxx6xxxxxxxxxxxxxx9xxxxxxxxxxxxx3xxx9xxxxxxxxxxxxxxxx0xxxxxxxxxxxxxxxxx2xxxx2xxx6xxxxx/xx54xxxxxxxxx4xxx3xxxxxx9xx3xxxxx39xxxxxxxxx5xx91xxxx7xxxxxx8xxxxxxxxxxxxxxxx9xxx93xxxxxxxxxxxxxxxxx7xxx8xx8xx4/x1xxxxx1x3xxxxxxxxxxxxx3xxxxxx9xx4xx4x7xxxxxxxxxxxxx1xxxxxxxxx7xxxxxxxxxxxxxx4xx6xxxxxxxxx9xxx7xxxx2xxxxxxxxxxxxxxxxxxxxxx8xxxxxxxxxxxxxxxxxxxx6xx== xxxxx010 xxxxxxxxxx\n'] +} + +def foo(): + xxx_xxx = ( + 'xxxx xxx xxxxxxxx_xxxx xx "xxxxxxxxxx".' + '\n xxx: xxxxxx xxxxxxxx_xxxx=xxxxxxxxxx' + ) # xxxx xxxxxxxxxx xxxx xx xxxx xx xxx xxxxxxxx xxxxxx xxxxx. + +some_tuple = ("some string", "some string" " which should be joined") + +some_commented_string = ( # This comment stays at the top. + "This string is long but not so long that it needs hahahah toooooo be so greatttt" + " {} that I just can't think of any more good words to say about it at" + " allllllllllll".format("ha") # comments here are fine +) + +some_commented_string = ( + "This string is long but not so long that it needs hahahah toooooo be so greatttt" # But these + " {} that I just can't think of any more good words to say about it at" # comments will stay + " allllllllllll".format("ha") # comments here are fine +) + +lpar_and_rpar_have_comments = func_call( # LPAR Comment + "Long really ridiculous type of string that shouldn't really even exist at all. I mean commmme onnn!!!", # Comma Comment +) # RPAR Comment + +cmd_fstring = ( + f"sudo -E deluge-console info --detailed --sort-reverse=time_added " + f"{'' if ID is None else ID} | perl -nE 'print if /^{field}:/'" +) + +cmd_fstring = f"sudo -E deluge-console info --detailed --sort-reverse=time_added {'' if ID is None else ID} | perl -nE 'print if /^{field}:/'" + +cmd_fstring = f"sudo -E deluge-console info --detailed --sort-reverse=time_added {'{{}}' if ID is None else ID} | perl -nE 'print if /^{field}:/'" + +cmd_fstring = f"sudo -E deluge-console info --detailed --sort-reverse=time_added {{'' if ID is None else ID}} | perl -nE 'print if /^{field}:/'" + +fstring = f"This string really doesn't need to be an {{{{fstring}}}}, but this one most certainly, absolutely {does}." + +fstring = ( + f"We have to remember to escape {braces}." + " Like {these}." + f" But not {this}." +) + +class A: + class B: + def foo(): + st_error = STError( + f"This string ({string_leaf.value}) appears to be pointless (i.e. has" + " no parent)." + ) + +def foo(): + user_regex = _lazy_re_compile( + r"(^[-!#$%&'*+/=?^_`{}|~0-9A-Z]+(\.[-!#$%&'*+/=?^_`{}|~0-9A-Z]+)*\Z" # dot-atom + r'|^"([\001-\010\013\014\016-\037!#-\[\]-\177]|\\[\001-\011\013\014\016-\177])*"\Z)', # quoted-string + re.IGNORECASE) + +def foo(): + user_regex = _lazy_re_compile( + "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" # dot-atom + 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx', # quoted-string + xyz + ) + +def foo(): + user_regex = _lazy_re_compile( + "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" # dot-atom + 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx', # quoted-string + xyz + ) + +class A: + class B: + def foo(): + if not hasattr(module, name): + raise ValueError( + "Could not find object %s in %s.\n" + "Please note that you cannot serialize things like inner " + "classes. Please move the object into the main module " + "body to use migrations.\n" + "For more information, see " + "https://docs.djangoproject.com/en/%s/topics/migrations/#serializing-values" + % (name, module_name, get_docs_version())) + +class A: + class B: + def foo(): + if not hasattr(module, name): + raise ValueError( + "Could not find object %s in %s.\nPlease note that you cannot serialize things like inner classes. Please move the object into the main module body to use migrations.\nFor more information, see https://docs.djangoproject.com/en/%s/topics/migrations/#serializing-values" + % (name, module_name, get_docs_version())) + +x = ( + "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" + "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" + "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" + "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" +) + +class Step(StepBase): + def who(self): + self.cmd = 'SR AAAA-CORRECT NAME IS {last_name} {first_name}{middle_name} {title}/P{passenger_association}'.format( + last_name=last_name, + first_name=first_name, + middle_name=middle_name, + title=title, + passenger_association=passenger_association, + ) + +xxxxxxx_xxxxxx_xxxxxxx = xxx( + [ + xxxxxxxxxxxx( + xxxxxx_xxxxxxx=( + '((x.aaaaaaaaa = "xxxxxx.xxxxxxxxxxxxxxxxxxxxx") || (x.xxxxxxxxx = "xxxxxxxxxxxx")) && ' + # xxxxx xxxxxxxxxxxx xxxx xxx (xxxxxxxxxxxxxxxx) xx x xxxxxxxxx xx xxxxxx. + "(x.bbbbbbbbbbbb.xxx != " + '"xxx:xxx:xxx::cccccccccccc:xxxxxxx-xxxx/xxxxxxxxxxx/xxxxxxxxxxxxxxxxx") && ' + ) + ) + ] +) + +if __name__ == "__main__": + for i in range(4, 8): + cmd = ( + r"for pid in $(ps aux | grep paster | grep -v grep | grep '\-%d' | awk '{print $2}'); do kill $pid; done" + % (i) + ) + +def A(): + def B(): + def C(): + def D(): + def E(): + def F(): + def G(): + assert ( + c_float(val[0][0] / val[0][1]).value + == c_float(value[0][0] / value[0][1]).value + ), "%s didn't roundtrip" % tag + +class xxxxxxxxxxxxxxxxxxxxx(xxxx.xxxxxxxxxxxxx): + def xxxxxxx_xxxxxx(xxxx): + assert xxxxxxx_xxxx in [ + x.xxxxx.xxxxxx.xxxxx.xxxxxx, + x.xxxxx.xxxxxx.xxxxx.xxxx, + ], ("xxxxxxxxxxx xxxxxxx xxxx (xxxxxx xxxx) %x xxx xxxxx" % xxxxxxx_xxxx) + +value.__dict__[ + key +] = "test" # set some Thrift field to non-None in the struct aa bb cc dd ee + +RE_ONE_BACKSLASH = { + "asdf_hjkl_jkl": re.compile( + r"(?>\n" +) + +assert str(suffix_arr) == ( + "['$', 'angaroo$', 'angrykangaroo$', 'aroo$', 'garoo$', " + "'grykangaroo$', 'kangaroo$', 'ngaroo$', 'ngrykangaroo$', " + "'o$', 'oo$', 'roo$', 'rykangaroo$', 'ykangaroo$']" +) +assert str(suffix_arr) != ( + "['$', 'angaroo$', 'angrykangaroo$', 'aroo$', 'garoo$', " + "'grykangaroo$', 'kangaroo$', 'ngaroo$', 'ngrykangaroo$', " + "'o$', 'oo$', 'roo$', 'rykangaroo$', 'ykangaroo$']" +) +assert str(suffix_arr) <= ( + "['$', 'angaroo$', 'angrykangaroo$', 'aroo$', 'garoo$', " + "'grykangaroo$', 'kangaroo$', 'ngaroo$', 'ngrykangaroo$', " + "'o$', 'oo$', 'roo$', 'rykangaroo$', 'ykangaroo$']" +) +assert str(suffix_arr) >= ( + "['$', 'angaroo$', 'angrykangaroo$', 'aroo$', 'garoo$', " + "'grykangaroo$', 'kangaroo$', 'ngaroo$', 'ngrykangaroo$', " + "'o$', 'oo$', 'roo$', 'rykangaroo$', 'ykangaroo$']" +) +assert str(suffix_arr) < ( + "['$', 'angaroo$', 'angrykangaroo$', 'aroo$', 'garoo$', " + "'grykangaroo$', 'kangaroo$', 'ngaroo$', 'ngrykangaroo$', " + "'o$', 'oo$', 'roo$', 'rykangaroo$', 'ykangaroo$']" +) +assert str(suffix_arr) > ( + "['$', 'angaroo$', 'angrykangaroo$', 'aroo$', 'garoo$', " + "'grykangaroo$', 'kangaroo$', 'ngaroo$', 'ngrykangaroo$', " + "'o$', 'oo$', 'roo$', 'rykangaroo$', 'ykangaroo$']" +) +assert str(suffix_arr) in "['$', 'angaroo$', 'angrykangaroo$', 'aroo$', 'garoo$', 'grykangaroo$', 'kangaroo$', 'ngaroo$', 'ngrykangaroo$', 'o$', 'oo$', 'roo$', 'rykangaroo$', 'ykangaroo$']" +assert str(suffix_arr) not in "['$', 'angaroo$', 'angrykangaroo$', 'aroo$', 'garoo$', 'grykangaroo$', 'kangaroo$', 'ngaroo$', 'ngrykangaroo$', 'o$', 'oo$', 'roo$', 'rykangaroo$', 'ykangaroo$']" +message = ( + f"1. Go to Google Developers Console and log in with your Google account." + "(https://console.developers.google.com/)" + "2. You should be prompted to create a new project (name does not matter)." + "3. Click on Enable APIs and Services at the top." + "4. In the list of APIs choose or search for YouTube Data API v3 and " + "click on it. Choose Enable." + "5. Click on Credentials on the left navigation bar." + "6. Click on Create Credential at the top." + '7. At the top click the link for "API key".' + "8. No application restrictions are needed. Click Create at the bottom." + "9. You now have a key to add to `{prefix}set api youtube api_key`" +) +message = ( + f"1. Go to Google Developers Console and log in with your Google account." + "(https://console.developers.google.com/)" + "2. You should be prompted to create a new project (name does not matter)." + f"3. Click on Enable APIs and Services at the top." + "4. In the list of APIs choose or search for YouTube Data API v3 and " + "click on it. Choose Enable." + f"5. Click on Credentials on the left navigation bar." + "6. Click on Create Credential at the top." + '7. At the top click the link for "API key".' + "8. No application restrictions are needed. Click Create at the bottom." + "9. You now have a key to add to `{prefix}set api youtube api_key`" +) +message = ( + f"1. Go to Google Developers Console and log in with your Google account." + "(https://console.developers.google.com/)" + "2. You should be prompted to create a new project (name does not matter)." + f"3. Click on Enable APIs and Services at the top." + "4. In the list of APIs choose or search for YouTube Data API v3 and " + "click on it. Choose Enable." + f"5. Click on Credentials on the left navigation bar." + "6. Click on Create Credential at the top." + '7. At the top click the link for "API key".' + "8. No application restrictions are needed. Click Create at the bottom." + f"9. You now have a key to add to `{prefix}set api youtube api_key`" +) + +# It shouldn't matter if the string prefixes are capitalized. +temp_msg = ( + F"{F'{humanize_number(pos)}.': <{pound_len+2}} " + F"{balance: <{bal_len + 5}} " + F"<<{author.display_name}>>\n" +) + +fstring = ( + F"We have to remember to escape {braces}." + " Like {these}." + F" But not {this}." +) + +welcome_to_programming = R"hello," R" world!" + +fstring = F"f-strings definitely make things more {difficult} than they need to be for {{black}}. But boy they sure are handy. The problem is that some lines will need to have the 'f' whereas others do not. This {line}, for example, needs one." + +x = F"This is a long string which contains an f-expr that should not split {{{[i for i in range(5)]}}}." + +x = ( + "\N{BLACK RIGHT-POINTING TRIANGLE WITH DOUBLE VERTICAL BAR}\N{VARIATION SELECTOR-16}" +) + +xxxxxx_xxx_xxxx_xx_xxxxx_xxxxxxxx_xxxxxxxx_xxxxxxxxxx_xxxx_xxxx_xxxxx = xxxx.xxxxxx.xxxxxxxxx.xxxxxxxxxxxxxxxxxxxx( + xx_xxxxxx={ + "x3_xxxxxxxx": "xxx3_xxxxx_xxxxxxxx_xxxxxxxx_xxxxxxxxxx_xxxxxxxx_xxxxxx_xxxxxxx", + }, +) + +# Regression test for https://github.com/psf/black/issues/3117. +some_dict = { + "something_something": + r"Lorem ipsum dolor sit amet, an sed convenire eloquentiam \t" + r"signiferumque, duo ea vocibus consetetur scriptorem. Facer \t", +} + +# Regression test for https://github.com/psf/black/issues/3459. +xxxx( + empty_str_as_first_split='' + f'xxxxxxx {xxxxxxxxxx} xxx xxxxxxxxxx xxxxx xxx xxx xx ' + 'xxxxx xxxxxxxxx xxxxxxx, xxx xxxxxxxxxxx xxx xxxxx. ' + f'xxxxxxxxxxxxx xxxx xx xxxxxxxxxx. xxxxx: {x.xxx}', + empty_u_str_as_first_split=u'' + f'xxxxxxx {xxxxxxxxxx} xxx xxxxxxxxxx xxxxx xxx xxx xx ' + 'xxxxx xxxxxxxxx xxxxxxx, xxx xxxxxxxxxxx xxx xxxxx. ' + f'xxxxxxxxxxxxx xxxx xx xxxxxxxxxx. xxxxx: {x.xxx}', +) + +# Regression test for https://github.com/psf/black/issues/3455. +a_dict = { + "/this/is/a/very/very/very/very/very/very/very/very/very/very/long/key/without/spaces": + # And there is a comment before the value + ("item1", "item2", "item3"), +} + +# Regression test for https://github.com/psf/black/issues/3506. +s = ( + "With single quote: ' " + f" {my_dict['foo']}" + ' With double quote: " ' + f' {my_dict["bar"]}' +) + +s = f'Lorem Ipsum is simply dummy text of the printing and typesetting industry:\'{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 new file mode 100644 index 0000000000..15c91275af --- /dev/null +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_long_strings__regression.py.expect @@ -0,0 +1,680 @@ +class A: + def foo(): + result = type(message)("") + + +# Don't merge multiline (e.g. triple-quoted) strings. +def foo(): + query = ( + """SELECT xxxxxxxxxxxxxxxxxxxx(xxx)""" + """ FROM xxxxxxxxxxxxxxxx WHERE xxxxxxxxxx AND xxx <> xxxxxxxxxxxxxx()""" + ) + + +# There was a bug where tuples were being identified as long strings. +long_tuple = ( + "Apple", + "Berry", + "Cherry", + "Dill", + "Evergreen", + "Fig", + "Grape", + "Harry", + "Iglu", + "Jaguar", +) + +stupid_format_method_bug = ( + "Some really long string that just so happens to be the {} {} to force the 'format'" + " method to hang over the line length boundary. This is pretty annoying.".format( + "perfect", "length" + ) +) + + +class A: + def foo(): + os.system( + "This is a regression test. xxx xxx xxx xxx xxx xxx xxx xxx xxx xxx xxx xxx" + " xxx xxx xxx xxx xxx xxx xxx xxx xxx xxx xxx xxx xxx xxx xxx xxx xxx xxx" + " xxxx.".format("xxxxxxxxxx", "xxxxxx", "xxxxxxxxxx") + ) + + +class A: + def foo(): + XXXXXXXXXXXX.append(( + "xxx_xxxxxxxxxx(xxxxx={}, xxxx={}, xxxxx, xxxx_xxxx_xxxxxxxxxx={})".format( + xxxxx, xxxx, xxxx_xxxx_xxxxxxxxxx + ), + my_var, + my_other_var, + )) + + +class A: + class B: + def foo(): + bar( + "[{}]: xxx_xxxxxxxxxx(xxxxx={}, xxxx={}, xxxxx={}" + " xxxx_xxxx_xxxxxxxxxx={}, xxxx={})".format( + xxxx._xxxxxxxxxxxxxx, xxxxx, xxxx, xxxx_xxxx_xxxxxxxxxx, xxxxxxx + ), + varX, + varY, + varZ, + ) + + +def foo(xxxx): + for xxx_xxxx, _xxx_xxx, _xxx_xxxxx, xxx_xxxx in xxxx: + for xxx in xxx_xxxx: + assert ("x" in xxx) or (xxx in xxx_xxx_xxxxx), ( + "{0} xxxxxxx xx {1}, xxx {1} xx xxx xx xxxx xx xxx xxxx: xxx xxxx {2}" + .format(xxx_xxxx, xxx, xxxxxx.xxxxxxx(xxx_xxx_xxxxx)) + ) + + +class A: + def disappearing_comment(): + return ( + ( # xx -x xxxxxxx xx xxx xxxxxxx. + "{{xxx_xxxxxxxxxx_xxxxxxxx}} xxx xxxx {} {{xxxx}} >&2".format( + "{xxxx} {xxxxxx}" + if xxxxx.xx_xxxxxxxxxx + else ( # Disappearing Comment + "--xxxxxxx --xxxxxx=x --xxxxxx-xxxxx=xxxxxx" + " --xxxxxx-xxxx=xxxxxxxxxxx.xxx" + ) + ) + ), + (x, y, z), + ) + + +class A: + class B: + def foo(): + xxxxx_xxxx( + xx, + "\t" + "@xxxxxx '{xxxx_xxx}\t' > {xxxxxx_xxxx}.xxxxxxx;" + "{xxxx_xxx} >> {xxxxxx_xxxx}.xxxxxxx 2>&1; xx=$$?;" + "xxxx $$xx".format( + xxxx_xxx=xxxx_xxxxxxx, + xxxxxx_xxxx=xxxxxxx + "/" + xxxx_xxx_xxxx, + x=xxx_xxxxx_xxxxx_xxx, + ), + x, + y, + z, + ) + + +func_call_where_string_arg_has_method_call_and_bad_parens( + "A long string with {}. This string is so long that it is ridiculous. It can't fit" + " on one line at alllll.".format("formatting"), +) + +func_call_where_string_arg_has_old_fmt_and_bad_parens( + "A long string with {}. This string is so long that it is ridiculous. It can't fit" + " on one line at alllll." % "formatting", +) + +func_call_where_string_arg_has_old_fmt_and_bad_parens( + "A long string with {}. This {} is so long that it is ridiculous. It can't fit on" + " one line at alllll." % ("formatting", "string"), +) + + +class A: + def append(self): + if True: + xxxx.xxxxxxx.xxxxx( + "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" + % ( + xxxx.xxxxxxxxx, + xxxx.xxxxxxxxxxxxxx(xxxx.xxxxxxxxx), + x, + xxxx.xxxxxxxxxxxxxx(xx), + ) + ) + + +class A: + def foo(): + some_func_call( + "xxxxxxxxxx", + "xx {xxxxxxxxxxx}/xxxxxxxxxxx.xxx xxxx.xxx && xxxxxx -x " + '"xxxx xxxxxxx xxxxxx xxxx; xxxx xxxxxx_xxxxx xxxxxx xxxx; ' + "xxxx.xxxx_xxxxxx(['xxxx.xxx'], xxxx.xxxxxxx().xxxxxxxxxx)\" ", + None, + ("xxxxxxxxxxx",), + ), + + +class A: + def foo(): + some_func_call( + "xx {xxxxxxxxxxx}/xxxxxxxxxxx.xxx xxxx.xxx && xxxxxx -x " + "xxxx, ('xxxxxxx xxxxxx xxxx, xxxx') xxxxxx_xxxxx xxxxxx xxxx; " + "xxxx.xxxx_xxxxxx(['xxxx.xxx'], xxxx.xxxxxxx().xxxxxxxxxx)\" ", + None, + ("xxxxxxxxxxx",), + ), + + +xxxxxxx = { + "xx": ( + "xxxx xxxxxxx xxxxxxxxx -x xxx -x /xxx/{0} -x xxx,xxx -xx {1} -xx {1} -xx" + " xxx=xxx_xxxx,xxx_xx,xxx_xxx,xxx_xxxx,xxx_xx,xxx_xxx | xxxxxx -x xxxxxxxx -x" + " xxxxxxxx -x" + ), + "xx": ( + "xxxx xxxxxxx xxxxxxxxx -x xxx -x /xxx/{0} -x xxx,xxx -xx {1} -xx {1} -xx" + " xxx=xxx_xxxx_xxx_xxxx,xxx_xx_xxx_xxxx,xxx_xxxx_xxx_xxxx,xxx_xx_xxxx_xxxx,xxx_xxx_xxxx,xxx_xxx_xxxx" + " xxxx=xxx | xxxxxx -x xxxxxxxx -x xxxxxxxx -x" + ), +} + + +class A: + def foo(self): + if True: + xxxxx_xxxxxxxxxxxx( + "xxx xxxxxx xxx xxxxxxxxx.xx xx xxxxxxxx. xxx xxxxxxxxxxxxx.xx" + " xxxxxxx " + + "xx xxxxxx xxxxxx xxxxxx xx xxxxxxx xxx xxx ${0} xx x xxxxxxxx xxxxx" + .xxxxxx(xxxxxx_xxxxxx_xxx) + ) + + +class A: + class B: + def foo(): + row = { + "xxxxxxxxxxxxxxx": xxxxxx_xxxxx_xxxx, + # 'xxxxxxxxxxxxxxxxxxxxxxx' + # 'xxxxxxxxxxxxxxxxxxxxxx' + # 'xxxxxxxxxxxxxxxxxx' + # 'xxxxxxxxxxxxxxxxx' + "xxxxxxxxxx": xxxxx_xxxxx, + } + + +class A: + def xxxx_xxx_xx_xxxxxxxxxx_xxxx_xxxxxxxxx(xxxx): + xxxxxxxx = [ + xxxxxxxxxxxxxxxx( + "xxxx", + xxxxxxxxxxx={ + "xxxx": 1.0, + }, + xxxxxx={"xxxxxx 1": xxxxxx(xxxx="xxxxxx 1", xxxxxx=600.0)}, + xxxxxxxx_xxxxxxx=0.0, + ), + xxxxxxxxxxxxxxxx( + "xxxxxxx", + xxxxxxxxxxx={ + "xxxx": 1.0, + }, + xxxxxx={"xxxxxx 1": xxxxxx(xxxx="xxxxxx 1", xxxxxx=200.0)}, + xxxxxxxx_xxxxxxx=0.0, + ), + xxxxxxxxxxxxxxxx( + "xxxx", + ), + ] + + +some_dictionary = { + "xxxxx006": [ + ( + "xxx-xxx" + " xxxxx3xxxx1xx2xxxxxxxxxxxxxx0xx6xxxxxxxxxx2xxxxxx9xxxxxxxxxx0xxxxx1xxx2x/xx9xx6+x+xxxxxxxxxxxxxx4xxxxxxxxxxxxxxxxxxxxx43xxx2xx2x4x++xxx6xxxxxxxxx+xxxxx/xx9x+xxxxxxxxxxxxxx8x15xxxxxxxxxxxxxxxxx82xx/xxxxxxxxxxxxxx/x5xxxxxxxxxxxxxx6xxxxxx74x4/xxx4x+xxxxxxxxx2xxxxxxxx87xxxxx4xxxxxxxx3xx0xxxxx4xxx1xx9xx5xxxxxxx/xxxxx5xx6xx4xxxx1x/x2xxxxxxxxxxxx64xxxxxxx1x0xx5xxxxxxxxxxxxxx==" + " xxxxx000 xxxxxxxxxx\n" + ), + ( + "xxx-xxx" + " xxxxx3xxxx1xx2xxxxxxxxxxxxxx6xxxxxxxxxxxxxx9xxxxxxxxxxxxx3xxx9xxxxxxxxxxxxxxxx0xxxxxxxxxxxxxxxxx2xxxx2xxx6xxxxx/xx54xxxxxxxxx4xxx3xxxxxx9xx3xxxxx39xxxxxxxxx5xx91xxxx7xxxxxx8xxxxxxxxxxxxxxxx9xxx93xxxxxxxxxxxxxxxxx7xxx8xx8xx4/x1xxxxx1x3xxxxxxxxxxxxx3xxxxxx9xx4xx4x7xxxxxxxxxxxxx1xxxxxxxxx7xxxxxxxxxxxxxx4xx6xxxxxxxxx9xxx7xxxx2xxxxxxxxxxxxxxxxxxxxxx8xxxxxxxxxxxxxxxxxxxx6xx==" + " xxxxx010 xxxxxxxxxx\n" + ), + ], + "xxxxx016": [ + ( + "xxx-xxx" + " xxxxx3xxxx1xx2xxxxxxxxxxxxxx0xx6xxxxxxxxxx2xxxxxx9xxxxxxxxxx0xxxxx1xxx2x/xx9xx6+x+xxxxxxxxxxxxxx4xxxxxxxxxxxxxxxxxxxxx43xxx2xx2x4x++xxx6xxxxxxxxx+xxxxx/xx9x+xxxxxxxxxxxxxx8x15xxxxxxxxxxxxxxxxx82xx/xxxxxxxxxxxxxx/x5xxxxxxxxxxxxxx6xxxxxx74x4/xxx4x+xxxxxxxxx2xxxxxxxx87xxxxx4xxxxxxxx3xx0xxxxx4xxx1xx9xx5xxxxxxx/xxxxx5xx6xx4xxxx1x/x2xxxxxxxxxxxx64xxxxxxx1x0xx5xxxxxxxxxxxxxx==" + " xxxxx000 xxxxxxxxxx\n" + ), + ( + "xxx-xxx" + " xxxxx3xxxx1xx2xxxxxxxxxxxxxx6xxxxxxxxxxxxxx9xxxxxxxxxxxxx3xxx9xxxxxxxxxxxxxxxx0xxxxxxxxxxxxxxxxx2xxxx2xxx6xxxxx/xx54xxxxxxxxx4xxx3xxxxxx9xx3xxxxx39xxxxxxxxx5xx91xxxx7xxxxxx8xxxxxxxxxxxxxxxx9xxx93xxxxxxxxxxxxxxxxx7xxx8xx8xx4/x1xxxxx1x3xxxxxxxxxxxxx3xxxxxx9xx4xx4x7xxxxxxxxxxxxx1xxxxxxxxx7xxxxxxxxxxxxxx4xx6xxxxxxxxx9xxx7xxxx2xxxxxxxxxxxxxxxxxxxxxx8xxxxxxxxxxxxxxxxxxxx6xx==" + " xxxxx010 xxxxxxxxxx\n" + ), + ], +} + + +def foo(): + xxx_xxx = ( # xxxx xxxxxxxxxx xxxx xx xxxx xx xxx xxxxxxxx xxxxxx xxxxx. + 'xxxx xxx xxxxxxxx_xxxx xx "xxxxxxxxxx".\n xxx: xxxxxx xxxxxxxx_xxxx=xxxxxxxxxx' + ) + + +some_tuple = ("some string", "some string which should be joined") + +some_commented_string = ( # This comment stays at the top. + "This string is long but not so long that it needs hahahah toooooo be so greatttt" + " {} that I just can't think of any more good words to say about it at" + " allllllllllll".format("ha") # comments here are fine +) + +some_commented_string = ( + "This string is long but not so long that it needs hahahah toooooo be so greatttt" # But these + " {} that I just can't think of any more good words to say about it at" # comments will stay + " allllllllllll".format("ha") # comments here are fine +) + +lpar_and_rpar_have_comments = func_call( # LPAR Comment + "Long really ridiculous type of string that shouldn't really even exist at all. I" + " mean commmme onnn!!!", # Comma Comment +) # RPAR Comment + +cmd_fstring = ( + "sudo -E deluge-console info --detailed --sort-reverse=time_added " + f"{'' if ID is None else ID} | perl -nE 'print if /^{field}:/'" +) + +cmd_fstring = ( + "sudo -E deluge-console info --detailed --sort-reverse=time_added" + f" {'' if ID is None else ID} | perl -nE 'print if /^{field}:/'" +) + +cmd_fstring = ( + "sudo -E deluge-console info --detailed --sort-reverse=time_added" + f" {'{{}}' if ID is None else ID} | perl -nE 'print if /^{field}:/'" +) + +cmd_fstring = ( + "sudo -E deluge-console info --detailed --sort-reverse=time_added {'' if ID is" + f" None else ID}} | perl -nE 'print if /^{field}:/'" +) + +fstring = ( + "This string really doesn't need to be an {{fstring}}, but this one most" + f" certainly, absolutely {does}." +) + +fstring = f"We have to remember to escape {braces}. Like {{these}}. But not {this}." + + +class A: + class B: + def foo(): + st_error = STError( + f"This string ({string_leaf.value}) appears to be pointless (i.e. has" + " no parent)." + ) + + +def foo(): + user_regex = _lazy_re_compile( + r"(^[-!#$%&'*+/=?^_`{}|~0-9A-Z]+(\.[-!#$%&'*+/=?^_`{}|~0-9A-Z]+)*\Z" # dot-atom + r'|^"([\001-\010\013\014\016-\037!#-\[\]-\177]|\\[\001-\011\013\014\016-\177])*"\Z)', # quoted-string + re.IGNORECASE, + ) + + +def foo(): + user_regex = _lazy_re_compile( + "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" # dot-atom + "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", # quoted-string + xyz, + ) + + +def foo(): + user_regex = _lazy_re_compile( + "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" # dot-atom + "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", # quoted-string + xyz, + ) + + +class A: + class B: + def foo(): + if not hasattr(module, name): + raise ValueError( + "Could not find object %s in %s.\n" + "Please note that you cannot serialize things like inner " + "classes. Please move the object into the main module " + "body to use migrations.\n" + "For more information, see " + "https://docs.djangoproject.com/en/%s/topics/migrations/#serializing-values" + % (name, module_name, get_docs_version()) + ) + + +class A: + class B: + def foo(): + if not hasattr(module, name): + raise ValueError( + "Could not find object %s in %s.\nPlease note that you cannot" + " serialize things like inner classes. Please move the object into" + " the main module body to use migrations.\nFor more information," + " see https://docs.djangoproject.com/en/%s/topics/migrations/#serializing-values" + % (name, module_name, get_docs_version()) + ) + + +x = ( + "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" + "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" + "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" + "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" +) + + +class Step(StepBase): + def who(self): + self.cmd = ( + "SR AAAA-CORRECT NAME IS {last_name} {first_name}{middle_name}" + " {title}/P{passenger_association}".format( + last_name=last_name, + first_name=first_name, + middle_name=middle_name, + title=title, + passenger_association=passenger_association, + ) + ) + + +xxxxxxx_xxxxxx_xxxxxxx = xxx([ + xxxxxxxxxxxx( + xxxxxx_xxxxxxx=( + '((x.aaaaaaaaa = "xxxxxx.xxxxxxxxxxxxxxxxxxxxx") || (x.xxxxxxxxx =' + ' "xxxxxxxxxxxx")) && ' + # xxxxx xxxxxxxxxxxx xxxx xxx (xxxxxxxxxxxxxxxx) xx x xxxxxxxxx xx xxxxxx. + "(x.bbbbbbbbbbbb.xxx != " + '"xxx:xxx:xxx::cccccccccccc:xxxxxxx-xxxx/xxxxxxxxxxx/xxxxxxxxxxxxxxxxx") && ' + ) + ) +]) + +if __name__ == "__main__": + for i in range(4, 8): + cmd = ( + r"for pid in $(ps aux | grep paster | grep -v grep | grep '\-%d' | awk" + r" '{print $2}'); do kill $pid; done" % (i) + ) + + +def A(): + def B(): + def C(): + def D(): + def E(): + def F(): + def G(): + assert ( + c_float(val[0][0] / val[0][1]).value + == c_float(value[0][0] / value[0][1]).value + ), "%s didn't roundtrip" % tag + + +class xxxxxxxxxxxxxxxxxxxxx(xxxx.xxxxxxxxxxxxx): + def xxxxxxx_xxxxxx(xxxx): + assert xxxxxxx_xxxx in [ + x.xxxxx.xxxxxx.xxxxx.xxxxxx, + x.xxxxx.xxxxxx.xxxxx.xxxx, + ], ( + "xxxxxxxxxxx xxxxxxx xxxx (xxxxxx xxxx) %x xxx xxxxx" % xxxxxxx_xxxx + ) + + +value.__dict__[key] = ( + "test" # set some Thrift field to non-None in the struct aa bb cc dd ee +) + +RE_ONE_BACKSLASH = { + "asdf_hjkl_jkl": re.compile( + r"(?>\n" +) + +assert ( + str(suffix_arr) + == "['$', 'angaroo$', 'angrykangaroo$', 'aroo$', 'garoo$', " + "'grykangaroo$', 'kangaroo$', 'ngaroo$', 'ngrykangaroo$', " + "'o$', 'oo$', 'roo$', 'rykangaroo$', 'ykangaroo$']" +) +assert ( + str(suffix_arr) + != "['$', 'angaroo$', 'angrykangaroo$', 'aroo$', 'garoo$', " + "'grykangaroo$', 'kangaroo$', 'ngaroo$', 'ngrykangaroo$', " + "'o$', 'oo$', 'roo$', 'rykangaroo$', 'ykangaroo$']" +) +assert ( + str(suffix_arr) + <= "['$', 'angaroo$', 'angrykangaroo$', 'aroo$', 'garoo$', " + "'grykangaroo$', 'kangaroo$', 'ngaroo$', 'ngrykangaroo$', " + "'o$', 'oo$', 'roo$', 'rykangaroo$', 'ykangaroo$']" +) +assert ( + str(suffix_arr) + >= "['$', 'angaroo$', 'angrykangaroo$', 'aroo$', 'garoo$', " + "'grykangaroo$', 'kangaroo$', 'ngaroo$', 'ngrykangaroo$', " + "'o$', 'oo$', 'roo$', 'rykangaroo$', 'ykangaroo$']" +) +assert ( + str(suffix_arr) + < "['$', 'angaroo$', 'angrykangaroo$', 'aroo$', 'garoo$', " + "'grykangaroo$', 'kangaroo$', 'ngaroo$', 'ngrykangaroo$', " + "'o$', 'oo$', 'roo$', 'rykangaroo$', 'ykangaroo$']" +) +assert ( + str(suffix_arr) + > "['$', 'angaroo$', 'angrykangaroo$', 'aroo$', 'garoo$', " + "'grykangaroo$', 'kangaroo$', 'ngaroo$', 'ngrykangaroo$', " + "'o$', 'oo$', 'roo$', 'rykangaroo$', 'ykangaroo$']" +) +assert ( + str(suffix_arr) + in "['$', 'angaroo$', 'angrykangaroo$', 'aroo$', 'garoo$', 'grykangaroo$'," + " 'kangaroo$', 'ngaroo$', 'ngrykangaroo$', 'o$', 'oo$', 'roo$', 'rykangaroo$'," + " 'ykangaroo$']" +) +assert ( + str(suffix_arr) + not in "['$', 'angaroo$', 'angrykangaroo$', 'aroo$', 'garoo$', 'grykangaroo$'," + " 'kangaroo$', 'ngaroo$', 'ngrykangaroo$', 'o$', 'oo$', 'roo$'," + " 'rykangaroo$', 'ykangaroo$']" +) +message = ( + f"1. Go to Google Developers Console and log in with your Google account." + f"(https://console.developers.google.com/)" + f"2. You should be prompted to create a new project (name does not matter)." + f"3. Click on Enable APIs and Services at the top." + f"4. In the list of APIs choose or search for YouTube Data API v3 and " + f"click on it. Choose Enable." + f"5. Click on Credentials on the left navigation bar." + f"6. Click on Create Credential at the top." + f'7. At the top click the link for "API key".' + f"8. No application restrictions are needed. Click Create at the bottom." + f"9. You now have a key to add to `{{prefix}}set api youtube api_key`" +) +message = ( + f"1. Go to Google Developers Console and log in with your Google account." + f"(https://console.developers.google.com/)" + f"2. You should be prompted to create a new project (name does not matter)." + f"3. Click on Enable APIs and Services at the top." + f"4. In the list of APIs choose or search for YouTube Data API v3 and " + f"click on it. Choose Enable." + f"5. Click on Credentials on the left navigation bar." + f"6. Click on Create Credential at the top." + f'7. At the top click the link for "API key".' + f"8. No application restrictions are needed. Click Create at the bottom." + f"9. You now have a key to add to `{{prefix}}set api youtube api_key`" +) +message = ( + "1. Go to Google Developers Console and log in with your Google account." + "(https://console.developers.google.com/)" + "2. You should be prompted to create a new project (name does not matter)." + "3. Click on Enable APIs and Services at the top." + "4. In the list of APIs choose or search for YouTube Data API v3 and " + "click on it. Choose Enable." + "5. Click on Credentials on the left navigation bar." + "6. Click on Create Credential at the top." + '7. At the top click the link for "API key".' + "8. No application restrictions are needed. Click Create at the bottom." + f"9. You now have a key to add to `{prefix}set api youtube api_key`" +) + +# It shouldn't matter if the string prefixes are capitalized. +temp_msg = ( + f"{F'{humanize_number(pos)}.': <{pound_len+2}} " + f"{balance: <{bal_len + 5}} " + f"<<{author.display_name}>>\n" +) + +fstring = f"We have to remember to escape {braces}. Like {{these}}. But not {this}." + +welcome_to_programming = R"hello," R" world!" + +fstring = ( + f"f-strings definitely make things more {difficult} than they need to be for" + " {black}. But boy they sure are handy. The problem is that some lines will need" + f" to have the 'f' whereas others do not. This {line}, for example, needs one." +) + +x = ( + "This is a long string which contains an f-expr that should not split" + f" {{{[i for i in range(5)]}}}." +) + +x = ( + "\N{BLACK RIGHT-POINTING TRIANGLE WITH DOUBLE VERTICAL BAR}\N{VARIATION SELECTOR-16}" +) + +xxxxxx_xxx_xxxx_xx_xxxxx_xxxxxxxx_xxxxxxxx_xxxxxxxxxx_xxxx_xxxx_xxxxx = xxxx.xxxxxx.xxxxxxxxx.xxxxxxxxxxxxxxxxxxxx( + xx_xxxxxx={ + "x3_xxxxxxxx": ( + "xxx3_xxxxx_xxxxxxxx_xxxxxxxx_xxxxxxxxxx_xxxxxxxx_xxxxxx_xxxxxxx" + ), + }, +) + +# Regression test for https://github.com/psf/black/issues/3117. +some_dict = { + "something_something": ( + r"Lorem ipsum dolor sit amet, an sed convenire eloquentiam \t" + r"signiferumque, duo ea vocibus consetetur scriptorem. Facer \t" + ), +} + +# Regression test for https://github.com/psf/black/issues/3459. +xxxx( + empty_str_as_first_split=( + "" + f"xxxxxxx {xxxxxxxxxx} xxx xxxxxxxxxx xxxxx xxx xxx xx " + "xxxxx xxxxxxxxx xxxxxxx, xxx xxxxxxxxxxx xxx xxxxx. " + f"xxxxxxxxxxxxx xxxx xx xxxxxxxxxx. xxxxx: {x.xxx}" + ), + empty_u_str_as_first_split=( + "" + f"xxxxxxx {xxxxxxxxxx} xxx xxxxxxxxxx xxxxx xxx xxx xx " + "xxxxx xxxxxxxxx xxxxxxx, xxx xxxxxxxxxxx xxx xxxxx. " + f"xxxxxxxxxxxxx xxxx xx xxxxxxxxxx. xxxxx: {x.xxx}" + ), +) + +# Regression test for https://github.com/psf/black/issues/3455. +a_dict = { + "/this/is/a/very/very/very/very/very/very/very/very/very/very/long/key/without/spaces": + # And there is a comment before the value + ("item1", "item2", "item3"), +} + +# Regression test for https://github.com/psf/black/issues/3506. +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']}'" +) diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_long_strings__type_annotations.options.json b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_long_strings__type_annotations.options.json new file mode 100644 index 0000000000..ce7c52b163 --- /dev/null +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_long_strings__type_annotations.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_long_strings__type_annotations.py b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_long_strings__type_annotations.py new file mode 100644 index 0000000000..4907fec71c --- /dev/null +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_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/preview_long_strings__type_annotations.py.expect b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_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/preview_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/preview_multiline_strings.options.json b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_multiline_strings.options.json new file mode 100644 index 0000000000..ce7c52b163 --- /dev/null +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_multiline_strings.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_multiline_strings.py b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_multiline_strings.py new file mode 100644 index 0000000000..717f7b52e8 --- /dev/null +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_multiline_strings.py @@ -0,0 +1,175 @@ +"""cow +say""", +call(3, "dogsay", textwrap.dedent("""dove + coo""" % "cowabunga")) +call(3, "dogsay", textwrap.dedent("""dove +coo""" % "cowabunga")) +call(3, textwrap.dedent("""cow + moo""" % "cowabunga"), "dogsay") +call(3, "dogsay", textwrap.dedent("""crow + caw""" % "cowabunga"),) +call(3, textwrap.dedent("""cat + meow""" % "cowabunga"), {"dog", "say"}) +call(3, {"dog", "say"}, textwrap.dedent("""horse + neigh""" % "cowabunga")) +call(3, {"dog", "say"}, textwrap.dedent("""pig + oink""" % "cowabunga"),) +textwrap.dedent("""A one-line triple-quoted string.""") +textwrap.dedent("""A two-line triple-quoted string +since it goes to the next line.""") +textwrap.dedent("""A three-line triple-quoted string +that not only goes to the next line +but also goes one line beyond.""") +textwrap.dedent("""\ + A triple-quoted string + actually leveraging the textwrap.dedent functionality + that ends in a trailing newline, + representing e.g. file contents. +""") +path.write_text(textwrap.dedent("""\ + A triple-quoted string + actually leveraging the textwrap.dedent functionality + that ends in a trailing newline, + representing e.g. file contents. +""")) +path.write_text(textwrap.dedent("""\ + A triple-quoted string + actually leveraging the textwrap.dedent functionality + that ends in a trailing newline, + representing e.g. {config_filename} file contents. +""".format("config_filename", config_filename))) +# Another use case +data = yaml.load("""\ +a: 1 +b: 2 +""") +data = yaml.load("""\ +a: 1 +b: 2 +""",) +data = yaml.load( + """\ + a: 1 + b: 2 +""" +) + +MULTILINE = """ +foo +""".replace("\n", "") +generated_readme = lambda project_name: """ +{} + + +""".strip().format(project_name) +parser.usage += """ +Custom extra help summary. + +Extra test: +- with +- bullets +""" + + +def get_stuff(cr, value): + # original + cr.execute(""" + SELECT whatever + FROM some_table t + WHERE id = %s + """, [value]) + return cr.fetchone() + + +def get_stuff(cr, value): + # preferred + cr.execute( + """ + SELECT whatever + FROM some_table t + WHERE id = %s + """, + [value], + ) + return cr.fetchone() + + +call(arg1, arg2, """ +short +""", arg3=True) +test_vectors = [ + "one-liner\n", + "two\nliner\n", + """expressed +as a three line +mulitline string""", +] + +_wat = re.compile( + r""" + regex + """, + re.MULTILINE | re.VERBOSE, +) +dis_c_instance_method = """\ +%3d 0 LOAD_FAST 1 (x) + 2 LOAD_CONST 1 (1) + 4 COMPARE_OP 2 (==) + 6 LOAD_FAST 0 (self) + 8 STORE_ATTR 0 (x) + 10 LOAD_CONST 0 (None) + 12 RETURN_VALUE +""" % (_C.__init__.__code__.co_firstlineno + 1,) +path.write_text(textwrap.dedent("""\ + A triple-quoted string + actually {verb} the textwrap.dedent functionality + that ends in a trailing newline, + representing e.g. {file_type} file contents. +""".format(verb="using", file_type="test"))) +{"""cow +moos"""} +["""cow +moos"""] +["""cow +moos""", """dog +woofs +and +barks"""] +def dastardly_default_value( + cow: String = json.loads("""this +is +quite +the +dastadardly +value!"""), + **kwargs, +): + pass + +print(f""" + This {animal} + moos and barks +{animal} say +""") +msg = f"""The arguments {bad_arguments} were passed in. +Please use `--build-option` instead, +`--global-option` is reserved to flags like `--verbose` or `--quiet`. +""" + +this_will_become_one_line = ( + "a" + "b" + "c" +) + +this_will_stay_on_three_lines = ( + "a" # comment + "b" + "c" +) + +this_will_also_become_one_line = ( # comment + "a" + "b" + "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 new file mode 100644 index 0000000000..3983178da9 --- /dev/null +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_multiline_strings.py.expect @@ -0,0 +1,209 @@ +"""cow +say""", +call( + 3, + "dogsay", + textwrap.dedent("""dove + coo""" % "cowabunga"), +) +call( + 3, + "dogsay", + textwrap.dedent("""dove +coo""" % "cowabunga"), +) +call( + 3, + textwrap.dedent("""cow + moo""" % "cowabunga"), + "dogsay", +) +call( + 3, + "dogsay", + textwrap.dedent("""crow + caw""" % "cowabunga"), +) +call( + 3, + textwrap.dedent("""cat + meow""" % "cowabunga"), + {"dog", "say"}, +) +call( + 3, + {"dog", "say"}, + textwrap.dedent("""horse + neigh""" % "cowabunga"), +) +call( + 3, + {"dog", "say"}, + textwrap.dedent("""pig + oink""" % "cowabunga"), +) +textwrap.dedent("""A one-line triple-quoted string.""") +textwrap.dedent("""A two-line triple-quoted string +since it goes to the next line.""") +textwrap.dedent("""A three-line triple-quoted string +that not only goes to the next line +but also goes one line beyond.""") +textwrap.dedent("""\ + A triple-quoted string + actually leveraging the textwrap.dedent functionality + that ends in a trailing newline, + representing e.g. file contents. +""") +path.write_text(textwrap.dedent("""\ + A triple-quoted string + actually leveraging the textwrap.dedent functionality + that ends in a trailing newline, + representing e.g. file contents. +""")) +path.write_text(textwrap.dedent("""\ + A triple-quoted string + actually leveraging the textwrap.dedent functionality + that ends in a trailing newline, + representing e.g. {config_filename} file contents. +""".format("config_filename", config_filename))) +# Another use case +data = yaml.load("""\ +a: 1 +b: 2 +""") +data = yaml.load( + """\ +a: 1 +b: 2 +""", +) +data = yaml.load("""\ + a: 1 + b: 2 +""") + +MULTILINE = """ +foo +""".replace("\n", "") +generated_readme = lambda project_name: """ +{} + + +""".strip().format(project_name) +parser.usage += """ +Custom extra help summary. + +Extra test: +- with +- bullets +""" + + +def get_stuff(cr, value): + # original + cr.execute( + """ + SELECT whatever + FROM some_table t + WHERE id = %s + """, + [value], + ) + return cr.fetchone() + + +def get_stuff(cr, value): + # preferred + cr.execute( + """ + SELECT whatever + FROM some_table t + WHERE id = %s + """, + [value], + ) + return cr.fetchone() + + +call( + arg1, + arg2, + """ +short +""", + arg3=True, +) +test_vectors = [ + "one-liner\n", + "two\nliner\n", + """expressed +as a three line +mulitline string""", +] + +_wat = re.compile( + r""" + regex + """, + re.MULTILINE | re.VERBOSE, +) +dis_c_instance_method = """\ +%3d 0 LOAD_FAST 1 (x) + 2 LOAD_CONST 1 (1) + 4 COMPARE_OP 2 (==) + 6 LOAD_FAST 0 (self) + 8 STORE_ATTR 0 (x) + 10 LOAD_CONST 0 (None) + 12 RETURN_VALUE +""" % (_C.__init__.__code__.co_firstlineno + 1,) +path.write_text(textwrap.dedent("""\ + A triple-quoted string + actually {verb} the textwrap.dedent functionality + that ends in a trailing newline, + representing e.g. {file_type} file contents. +""".format(verb="using", file_type="test"))) +{"""cow +moos"""} +["""cow +moos"""] +[ + """cow +moos""", + """dog +woofs +and +barks""", +] + + +def dastardly_default_value( + cow: String = json.loads("""this +is +quite +the +dastadardly +value!"""), + **kwargs, +): + pass + + +print(f""" + This {animal} + moos and barks +{animal} say +""") +msg = f"""The arguments {bad_arguments} were passed in. +Please use `--build-option` instead, +`--global-option` is reserved to flags like `--verbose` or `--quiet`. +""" + +this_will_become_one_line = "abc" + +this_will_stay_on_three_lines = ( + "a" # comment + "b" + "c" +) + +this_will_also_become_one_line = "abc" # comment diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_no_blank_line_before_docstring.options.json b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_no_blank_line_before_docstring.options.json new file mode 100644 index 0000000000..ce7c52b163 --- /dev/null +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_no_blank_line_before_docstring.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_no_blank_line_before_docstring.py b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_no_blank_line_before_docstring.py new file mode 100644 index 0000000000..43ba2bd202 --- /dev/null +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_no_blank_line_before_docstring.py @@ -0,0 +1,29 @@ +def line_before_docstring(): + + """Please move me up""" + + +class LineBeforeDocstring: + + """Please move me up""" + + +class EvenIfThereIsAMethodAfter: + + """I'm the docstring""" + def method(self): + pass + + +class TwoLinesBeforeDocstring: + + + """I want to be treated the same as if I were closer""" + + +class MultilineDocstringsAsWell: + + """I'm so far + + and on so many lines... + """ diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_no_blank_line_before_docstring.py.expect b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_no_blank_line_before_docstring.py.expect new file mode 100644 index 0000000000..78604d9c92 --- /dev/null +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_no_blank_line_before_docstring.py.expect @@ -0,0 +1,24 @@ +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... + """ diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_pattern_matching_long.options.json b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_pattern_matching_long.options.json new file mode 100644 index 0000000000..ce7c52b163 --- /dev/null +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_pattern_matching_long.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_pattern_matching_long.py b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_pattern_matching_long.py new file mode 100644 index 0000000000..98b6ac56a2 --- /dev/null +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_pattern_matching_long.py @@ -0,0 +1,7 @@ +match x: + case "abcd" | "abcd" | "abcd" : + pass + case "abcd" | "abcd" | "abcd" | "abcd" | "abcd" | "abcd" | "abcd" | "abcd" | "abcd" | "abcd" | "abcd" | "abcd" | "abcd" | "abcd" | "abcd": + pass + case xxxxxxxxxxxxxxxxxxxxxxx: + pass diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_pattern_matching_long.py.expect b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_pattern_matching_long.py.expect new file mode 100644 index 0000000000..c60e5770a7 --- /dev/null +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_pattern_matching_long.py.expect @@ -0,0 +1,23 @@ +match x: + case "abcd" | "abcd" | "abcd": + pass + case ( + "abcd" + | "abcd" + | "abcd" + | "abcd" + | "abcd" + | "abcd" + | "abcd" + | "abcd" + | "abcd" + | "abcd" + | "abcd" + | "abcd" + | "abcd" + | "abcd" + | "abcd" + ): + pass + case xxxxxxxxxxxxxxxxxxxxxxx: + pass diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_pattern_matching_trailing_comma.options.json b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_pattern_matching_trailing_comma.options.json new file mode 100644 index 0000000000..ce7c52b163 --- /dev/null +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_pattern_matching_trailing_comma.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_pattern_matching_trailing_comma.py b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_pattern_matching_trailing_comma.py new file mode 100644 index 0000000000..002ceea330 --- /dev/null +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_pattern_matching_trailing_comma.py @@ -0,0 +1,14 @@ +match maybe, multiple: + case perhaps, 5: + pass + case perhaps, 6,: + pass + + +match more := (than, one), indeed,: + case _, (5, 6): + pass + case [[5], (6)], [7],: + pass + case _: + pass diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_pattern_matching_trailing_comma.py.expect b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_pattern_matching_trailing_comma.py.expect new file mode 100644 index 0000000000..6e8ef32f18 --- /dev/null +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_pattern_matching_trailing_comma.py.expect @@ -0,0 +1,20 @@ +match maybe, multiple: + case perhaps, 5: + pass + case ( + perhaps, + 6, + ): + pass + + +match more := (than, one), indeed,: + case _, (5, 6): + pass + case ( + [[5], (6)], + [7], + ): + pass + case _: + pass diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_pep_572.options.json b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_pep_572.options.json new file mode 100644 index 0000000000..ce7c52b163 --- /dev/null +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_pep_572.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_pep_572.py b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_pep_572.py new file mode 100644 index 0000000000..dff9853038 --- /dev/null +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_pep_572.py @@ -0,0 +1,2 @@ +x[(a:=0):] +x[:(a:=0)] diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_pep_572.py.expect b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_pep_572.py.expect new file mode 100644 index 0000000000..0ae5b9769d --- /dev/null +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_pep_572.py.expect @@ -0,0 +1,2 @@ +x[(a := 0):] +x[:(a := 0)] diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_percent_precedence.options.json b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_percent_precedence.options.json new file mode 100644 index 0000000000..ce7c52b163 --- /dev/null +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_percent_precedence.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_percent_precedence.py b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_percent_precedence.py new file mode 100644 index 0000000000..ab71177d2e --- /dev/null +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_percent_precedence.py @@ -0,0 +1,20 @@ +("" % a) ** 2 +("" % a)[0] +("" % a)() +("" % a).b + +2 * ("" % a) +2 @ ("" % a) +2 / ("" % a) +2 // ("" % a) +2 % ("" % a) ++("" % a) +b + ("" % a) +-("" % a) +b - ("" % a) +b + -("" % a) +~("" % a) +2 ** ("" % a) +await ("" % a) +b[("" % a)] +b(("" % a)) diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_percent_precedence.py.expect b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_percent_precedence.py.expect new file mode 100644 index 0000000000..bd77adeeb7 --- /dev/null +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_percent_precedence.py.expect @@ -0,0 +1,20 @@ +("" % a) ** 2 +("" % a)[0] +("" % a)() +("" % a).b + +2 * ("" % a) +2 @ ("" % a) +2 / ("" % a) +2 // ("" % a) +2 % ("" % a) ++("" % a) +b + "" % a +-("" % a) +b - "" % a +b + -("" % a) +~("" % a) +2 ** ("" % a) +await ("" % a) +b[("" % a)] +b(("" % a)) diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_power_op_spacing.options.json b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_power_op_spacing.options.json new file mode 100644 index 0000000000..ce7c52b163 --- /dev/null +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_power_op_spacing.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_power_op_spacing.py b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_power_op_spacing.py new file mode 100644 index 0000000000..af361012ea --- /dev/null +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_power_op_spacing.py @@ -0,0 +1,11 @@ +a = 1**1**1**1**1**1**1**1**1**1**1**1**1**1**1**1**1**1**1**1**1**1**1**1**1**1**1**1 +b = 1**1**1**1**1**1**1**1**1**1**1**1**1**1**1**1**1**1**1**1**1**1**1**1**1**1**1**1**1 +c = 1 ** 1 ** 1 ** 1 ** 1 ** 1 ** 1 ** 1 ** 1 ** 1 ** 1 ** 1 ** 1 ** 1 ** 1 ** 1 ** 1 ** 1 ** 1 ** 1 ** 1 ** 1 ** 1 ** 1 ** 1 ** 1 ** 1 ** 1 +d = 1**1 ** 1**1 ** 1**1 ** 1**1 ** 1**1**1 ** 1 ** 1**1 ** 1**1**1**1**1 ** 1 ** 1**1**1 **1**1** 1 ** 1 ** 1 +e = 𨉟**𨉟**𨉟**𨉟**𨉟**𨉟**𨉟**𨉟**𨉟**𨉟**𨉟**𨉟**𨉟**𨉟**𨉟**𨉟**𨉟**𨉟**𨉟**𨉟**𨉟 +f = 𨉟**𨉟**𨉟**𨉟**𨉟**𨉟**𨉟**𨉟**𨉟**𨉟**𨉟**𨉟**𨉟**𨉟**𨉟**𨉟**𨉟**𨉟**𨉟**𨉟**𨉟**𨉟 + +a = 1.0**1.0**1.0**1.0**1.0**1.0**1.0**1.0**1.0**1.0**1.0**1.0**1.0**1.0**1.0**1.0**1.0 +b = 1.0**1.0**1.0**1.0**1.0**1.0**1.0**1.0**1.0**1.0**1.0**1.0**1.0**1.0**1.0**1.0**1.0**1.0 +c = 1.0 ** 1.0 ** 1.0 ** 1.0 ** 1.0 ** 1.0 ** 1.0 ** 1.0 ** 1.0 ** 1.0 ** 1.0 ** 1.0 ** 1.0 ** 1.0 ** 1.0 ** 1.0 ** 1.0 +d = 1.0**1.0 ** 1.0**1.0 ** 1.0**1.0 ** 1.0**1.0 ** 1.0**1.0**1.0 ** 1.0 ** 1.0**1.0 ** 1.0**1.0**1.0 diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_power_op_spacing.py.expect b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_power_op_spacing.py.expect new file mode 100644 index 0000000000..da8b91530b --- /dev/null +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_power_op_spacing.py.expect @@ -0,0 +1,83 @@ +a = 1**1**1**1**1**1**1**1**1**1**1**1**1**1**1**1**1**1**1**1**1**1**1**1**1**1**1**1 +b = ( + 1 + ** 1 + ** 1 + ** 1 + ** 1 + ** 1 + ** 1 + ** 1 + ** 1 + ** 1 + ** 1 + ** 1 + ** 1 + ** 1 + ** 1 + ** 1 + ** 1 + ** 1 + ** 1 + ** 1 + ** 1 + ** 1 + ** 1 + ** 1 + ** 1 + ** 1 + ** 1 + ** 1 + ** 1 +) +c = 1**1**1**1**1**1**1**1**1**1**1**1**1**1**1**1**1**1**1**1**1**1**1**1**1**1**1**1 +d = 1**1**1**1**1**1**1**1**1**1**1**1**1**1**1**1**1**1**1**1**1**1**1**1**1**1**1**1 +e = 𨉟**𨉟**𨉟**𨉟**𨉟**𨉟**𨉟**𨉟**𨉟**𨉟**𨉟**𨉟**𨉟**𨉟**𨉟**𨉟**𨉟**𨉟**𨉟**𨉟**𨉟 +f = ( + 𨉟 + ** 𨉟 + ** 𨉟 + ** 𨉟 + ** 𨉟 + ** 𨉟 + ** 𨉟 + ** 𨉟 + ** 𨉟 + ** 𨉟 + ** 𨉟 + ** 𨉟 + ** 𨉟 + ** 𨉟 + ** 𨉟 + ** 𨉟 + ** 𨉟 + ** 𨉟 + ** 𨉟 + ** 𨉟 + ** 𨉟 + ** 𨉟 +) + +a = 1.0**1.0**1.0**1.0**1.0**1.0**1.0**1.0**1.0**1.0**1.0**1.0**1.0**1.0**1.0**1.0**1.0 +b = ( + 1.0 + ** 1.0 + ** 1.0 + ** 1.0 + ** 1.0 + ** 1.0 + ** 1.0 + ** 1.0 + ** 1.0 + ** 1.0 + ** 1.0 + ** 1.0 + ** 1.0 + ** 1.0 + ** 1.0 + ** 1.0 + ** 1.0 + ** 1.0 +) +c = 1.0**1.0**1.0**1.0**1.0**1.0**1.0**1.0**1.0**1.0**1.0**1.0**1.0**1.0**1.0**1.0**1.0 +d = 1.0**1.0**1.0**1.0**1.0**1.0**1.0**1.0**1.0**1.0**1.0**1.0**1.0**1.0**1.0**1.0**1.0 diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_prefer_rhs_split.options.json b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_prefer_rhs_split.options.json new file mode 100644 index 0000000000..ce7c52b163 --- /dev/null +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_prefer_rhs_split.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_prefer_rhs_split.py b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_prefer_rhs_split.py new file mode 100644 index 0000000000..f3d9fd6725 --- /dev/null +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_prefer_rhs_split.py @@ -0,0 +1,106 @@ +first_item, second_item = ( + some_looooooooong_module.some_looooooooooooooong_function_name( + first_argument, second_argument, third_argument + ) +) + +some_dict["with_a_long_key"] = ( + some_looooooooong_module.some_looooooooooooooong_function_name( + first_argument, second_argument, third_argument + ) +) + +# Make sure it works when the RHS only has one pair of (optional) parens. +first_item, second_item = ( + some_looooooooong_module.SomeClass.some_looooooooooooooong_variable_name +) + +some_dict["with_a_long_key"] = ( + some_looooooooong_module.SomeClass.some_looooooooooooooong_variable_name +) + +# Make sure chaining assignments work. +first_item, second_item, third_item, forth_item = m["everything"] = ( + some_looooooooong_module.some_looooooooooooooong_function_name( + first_argument, second_argument, third_argument + ) +) + +# Make sure when the RHS's first split at the non-optional paren fits, +# we split there instead of the outer RHS optional paren. +first_item, second_item = some_looooooooong_module.some_loooooog_function_name( + first_argument, second_argument, third_argument +) + +( + first_item, + second_item, + third_item, + forth_item, + fifth_item, + last_item_very_loooooong, +) = some_looooooooong_module.some_looooooooooooooong_function_name( + first_argument, second_argument, third_argument +) + +( + first_item, + second_item, + third_item, + forth_item, + fifth_item, + last_item_very_loooooong, +) = everything = some_looooong_function_name( + first_argument, second_argument, third_argument +) + + +# Make sure unsplittable type ignore won't be moved. +some_kind_of_table[some_key] = util.some_function( # type: ignore # noqa: E501 + some_arg +).intersection(pk_cols) + +some_kind_of_table[ + some_key +] = lambda obj: obj.some_long_named_method() # type: ignore # noqa: E501 + +some_kind_of_table[ + some_key # type: ignore # noqa: E501 +] = lambda obj: obj.some_long_named_method() + + +# Make when when the left side of assignment plus the opening paren "... = (" is +# exactly line length limit + 1, it won't be split like that. +xxxxxxxxx_yyy_zzzzzzzz[ + xx.xxxxxx(x_yyy_zzzzzz.xxxxx[0]), x_yyy_zzzzzz.xxxxxx(xxxx=1) +] = 1 + + +# Right side of assignment contains un-nested pairs of inner parens. +some_kind_of_instance.some_kind_of_map[a_key] = ( + isinstance(some_var, SomeClass) + and table.something_and_something != table.something_else +) or ( + isinstance(some_other_var, BaseClass) and table.something != table.some_other_thing +) + +# Multiple targets +a = b = ( + ccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc +) + +a = b = c = d = e = f = g = ( + hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh +) = i = j = ( + kkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkk +) + +a = ( + bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb +) = c + +a = ( + bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb +) = ( + cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc +) = ddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_prefer_rhs_split.py.expect b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_prefer_rhs_split.py.expect new file mode 100644 index 0000000000..f3d9fd6725 --- /dev/null +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_prefer_rhs_split.py.expect @@ -0,0 +1,106 @@ +first_item, second_item = ( + some_looooooooong_module.some_looooooooooooooong_function_name( + first_argument, second_argument, third_argument + ) +) + +some_dict["with_a_long_key"] = ( + some_looooooooong_module.some_looooooooooooooong_function_name( + first_argument, second_argument, third_argument + ) +) + +# Make sure it works when the RHS only has one pair of (optional) parens. +first_item, second_item = ( + some_looooooooong_module.SomeClass.some_looooooooooooooong_variable_name +) + +some_dict["with_a_long_key"] = ( + some_looooooooong_module.SomeClass.some_looooooooooooooong_variable_name +) + +# Make sure chaining assignments work. +first_item, second_item, third_item, forth_item = m["everything"] = ( + some_looooooooong_module.some_looooooooooooooong_function_name( + first_argument, second_argument, third_argument + ) +) + +# Make sure when the RHS's first split at the non-optional paren fits, +# we split there instead of the outer RHS optional paren. +first_item, second_item = some_looooooooong_module.some_loooooog_function_name( + first_argument, second_argument, third_argument +) + +( + first_item, + second_item, + third_item, + forth_item, + fifth_item, + last_item_very_loooooong, +) = some_looooooooong_module.some_looooooooooooooong_function_name( + first_argument, second_argument, third_argument +) + +( + first_item, + second_item, + third_item, + forth_item, + fifth_item, + last_item_very_loooooong, +) = everything = some_looooong_function_name( + first_argument, second_argument, third_argument +) + + +# Make sure unsplittable type ignore won't be moved. +some_kind_of_table[some_key] = util.some_function( # type: ignore # noqa: E501 + some_arg +).intersection(pk_cols) + +some_kind_of_table[ + some_key +] = lambda obj: obj.some_long_named_method() # type: ignore # noqa: E501 + +some_kind_of_table[ + some_key # type: ignore # noqa: E501 +] = lambda obj: obj.some_long_named_method() + + +# Make when when the left side of assignment plus the opening paren "... = (" is +# exactly line length limit + 1, it won't be split like that. +xxxxxxxxx_yyy_zzzzzzzz[ + xx.xxxxxx(x_yyy_zzzzzz.xxxxx[0]), x_yyy_zzzzzz.xxxxxx(xxxx=1) +] = 1 + + +# Right side of assignment contains un-nested pairs of inner parens. +some_kind_of_instance.some_kind_of_map[a_key] = ( + isinstance(some_var, SomeClass) + and table.something_and_something != table.something_else +) or ( + isinstance(some_other_var, BaseClass) and table.something != table.some_other_thing +) + +# Multiple targets +a = b = ( + ccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc +) + +a = b = c = d = e = f = g = ( + hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh +) = i = j = ( + kkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkk +) + +a = ( + bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb +) = c + +a = ( + bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb +) = ( + cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc +) = ddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_return_annotation_brackets_string.options.json b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_return_annotation_brackets_string.options.json new file mode 100644 index 0000000000..ce7c52b163 --- /dev/null +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_return_annotation_brackets_string.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_return_annotation_brackets_string.py b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_return_annotation_brackets_string.py new file mode 100644 index 0000000000..474130e48f --- /dev/null +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_return_annotation_brackets_string.py @@ -0,0 +1,7 @@ +# Long string example +def frobnicate() -> "ThisIsTrulyUnreasonablyExtremelyLongClassName | list[ThisIsTrulyUnreasonablyExtremelyLongClassName]": + pass + +# splitting the string breaks if there's any parameters +def frobnicate(a) -> "ThisIsTrulyUnreasonablyExtremelyLongClassName | list[ThisIsTrulyUnreasonablyExtremelyLongClassName]": + pass diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_return_annotation_brackets_string.py.expect b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_return_annotation_brackets_string.py.expect new file mode 100644 index 0000000000..610e399b4a --- /dev/null +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_return_annotation_brackets_string.py.expect @@ -0,0 +1,13 @@ +# Long string example +def frobnicate() -> ( + "ThisIsTrulyUnreasonablyExtremelyLongClassName |" + " list[ThisIsTrulyUnreasonablyExtremelyLongClassName]" +): + pass + + +# splitting the string breaks if there's any parameters +def frobnicate( + a, +) -> "ThisIsTrulyUnreasonablyExtremelyLongClassName | list[ThisIsTrulyUnreasonablyExtremelyLongClassName]": + pass diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_single_line_format_skip_with_multiple_comments.options.json b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_single_line_format_skip_with_multiple_comments.options.json new file mode 100644 index 0000000000..ce7c52b163 --- /dev/null +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_single_line_format_skip_with_multiple_comments.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_single_line_format_skip_with_multiple_comments.py b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_single_line_format_skip_with_multiple_comments.py new file mode 100644 index 0000000000..812febcf2a --- /dev/null +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_single_line_format_skip_with_multiple_comments.py @@ -0,0 +1,8 @@ +foo = 123 # fmt: skip # noqa: E501 # pylint +bar = ( + 123 , + ( 1 + 5 ) # pylint # fmt:skip +) +baz = "a" + "b" # pylint; fmt: skip; noqa: E501 +skip_will_not_work = "a" + "b" # pylint fmt:skip +skip_will_not_work2 = "a" + "b" # some text; fmt:skip happens to be part of it diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_single_line_format_skip_with_multiple_comments.py.expect b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_single_line_format_skip_with_multiple_comments.py.expect new file mode 100644 index 0000000000..d799b0c790 --- /dev/null +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_single_line_format_skip_with_multiple_comments.py.expect @@ -0,0 +1,8 @@ +foo = 123 # fmt: skip # noqa: E501 # pylint +bar = ( + 123 , + ( 1 + 5 ) # pylint # fmt:skip +) +baz = "a" + "b" # pylint; fmt: skip; noqa: E501 +skip_will_not_work = "a" + "b" # pylint fmt:skip +skip_will_not_work2 = "a" + "b" # some text; fmt:skip happens to be part of it diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_trailing_comma.options.json b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_trailing_comma.options.json new file mode 100644 index 0000000000..ce7c52b163 --- /dev/null +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_trailing_comma.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_trailing_comma.py b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_trailing_comma.py new file mode 100644 index 0000000000..24c53091b9 --- /dev/null +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_trailing_comma.py @@ -0,0 +1,24 @@ +e = { + "a": fun(msg, "ts"), + "longggggggggggggggid": ..., + "longgggggggggggggggggggkey": ..., "created": ... + # "longkey": ... +} +f = [ + arg1, + arg2, + arg3, arg4 + # comment +] +g = ( + arg1, + arg2, + arg3, arg4 + # comment +) +h = { + arg1, + arg2, + arg3, arg4 + # comment +} diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_trailing_comma.py.expect b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_trailing_comma.py.expect new file mode 100644 index 0000000000..1b2bafa028 --- /dev/null +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_trailing_comma.py.expect @@ -0,0 +1,28 @@ +e = { + "a": fun(msg, "ts"), + "longggggggggggggggid": ..., + "longgggggggggggggggggggkey": ..., + "created": ..., + # "longkey": ... +} +f = [ + arg1, + arg2, + arg3, + arg4, + # comment +] +g = ( + arg1, + arg2, + arg3, + arg4, + # comment +) +h = { + arg1, + arg2, + arg3, + arg4, + # comment +} diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/py310_pep572.options.json b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/py310_pep572.options.json new file mode 100644 index 0000000000..ce7c52b163 --- /dev/null +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/py310_pep572.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/py310_pep572.py b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/py310_pep572.py new file mode 100644 index 0000000000..1f2a3b903f --- /dev/null +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/py310_pep572.py @@ -0,0 +1,5 @@ +x[a:=0] +x[a := 0] +x[a := 0, b := 1] +x[5, b := 0] +x[a:=0,b:=1] diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/py310_pep572.py.expect b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/py310_pep572.py.expect new file mode 100644 index 0000000000..1b3b90bcc0 --- /dev/null +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/py310_pep572.py.expect @@ -0,0 +1,5 @@ +x[a := 0] +x[a := 0] +x[a := 0, b := 1] +x[5, b := 0] +x[a := 0, b := 1] diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/py_37/python37.py b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/python37.py similarity index 95% rename from crates/ruff_python_formatter/resources/test/fixtures/black/py_37/python37.py rename to crates/ruff_python_formatter/resources/test/fixtures/black/cases/python37.py index 01fd7eede3..3fcf6e0ffc 100644 --- a/crates/ruff_python_formatter/resources/test/fixtures/black/py_37/python37.py +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/python37.py @@ -1,6 +1,3 @@ -#!/usr/bin/env python3.7 - - def f(): return (i * 2 async for i in arange(42)) diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/py_37/python37.py.expect b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/python37.py.expect similarity index 95% rename from crates/ruff_python_formatter/resources/test/fixtures/black/py_37/python37.py.expect rename to crates/ruff_python_formatter/resources/test/fixtures/black/cases/python37.py.expect index 01fd7eede3..3fcf6e0ffc 100644 --- a/crates/ruff_python_formatter/resources/test/fixtures/black/py_37/python37.py.expect +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/python37.py.expect @@ -1,6 +1,3 @@ -#!/usr/bin/env python3.7 - - def f(): return (i * 2 async for i in arange(42)) diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/py_38/python38.py b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/python38.py similarity index 93% rename from crates/ruff_python_formatter/resources/test/fixtures/black/py_38/python38.py rename to crates/ruff_python_formatter/resources/test/fixtures/black/cases/python38.py index 391b52f8ce..54d87b3400 100644 --- a/crates/ruff_python_formatter/resources/test/fixtures/black/py_38/python38.py +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/python38.py @@ -1,6 +1,3 @@ -#!/usr/bin/env python3.8 - - def starred_return(): my_list = ["value2", "value3"] return "value1", *my_list diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/py_38/python38.py.expect b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/python38.py.expect similarity index 93% rename from crates/ruff_python_formatter/resources/test/fixtures/black/py_38/python38.py.expect rename to crates/ruff_python_formatter/resources/test/fixtures/black/cases/python38.py.expect index 5df012410a..76e2ae438e 100644 --- a/crates/ruff_python_formatter/resources/test/fixtures/black/py_38/python38.py.expect +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/python38.py.expect @@ -1,6 +1,3 @@ -#!/usr/bin/env python3.8 - - def starred_return(): my_list = ["value2", "value3"] return "value1", *my_list diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/py_39/python39.py b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/python39.py similarity index 92% rename from crates/ruff_python_formatter/resources/test/fixtures/black/py_39/python39.py rename to crates/ruff_python_formatter/resources/test/fixtures/black/cases/python39.py index 227faca09a..c0434277f6 100644 --- a/crates/ruff_python_formatter/resources/test/fixtures/black/py_39/python39.py +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/python39.py @@ -1,5 +1,3 @@ -#!/usr/bin/env python3.9 - @relaxed_decorator[0] def f(): ... diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/py_39/python39.py.expect b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/python39.py.expect similarity index 92% rename from crates/ruff_python_formatter/resources/test/fixtures/black/py_39/python39.py.expect rename to crates/ruff_python_formatter/resources/test/fixtures/black/cases/python39.py.expect index 4af4beebb2..99db2993cb 100644 --- a/crates/ruff_python_formatter/resources/test/fixtures/black/py_39/python39.py.expect +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/python39.py.expect @@ -1,6 +1,3 @@ -#!/usr/bin/env python3.9 - - @relaxed_decorator[0] def f(): ... diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/raw_docstring.options.json b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/raw_docstring.options.json new file mode 100644 index 0000000000..ce7c52b163 --- /dev/null +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/raw_docstring.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/raw_docstring.py b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/raw_docstring.py new file mode 100644 index 0000000000..72fe443843 --- /dev/null +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/raw_docstring.py @@ -0,0 +1,15 @@ +class C: + + r"""Raw""" + +def f(): + + r"""Raw""" + +class SingleQuotes: + + + r'''Raw''' + +class UpperCaseR: + R"""Raw""" diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/raw_docstring.py.expect b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/raw_docstring.py.expect new file mode 100644 index 0000000000..5be480f7f0 --- /dev/null +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/raw_docstring.py.expect @@ -0,0 +1,14 @@ +class C: + r"""Raw""" + + +def f(): + r"""Raw""" + + +class SingleQuotes: + r'''Raw''' + + +class UpperCaseR: + R"""Raw""" diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/simple_cases/remove_await_parens.py b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/remove_await_parens.py similarity index 90% rename from crates/ruff_python_formatter/resources/test/fixtures/black/simple_cases/remove_await_parens.py rename to crates/ruff_python_formatter/resources/test/fixtures/black/cases/remove_await_parens.py index 6b8d15f755..f65336615d 100644 --- a/crates/ruff_python_formatter/resources/test/fixtures/black/simple_cases/remove_await_parens.py +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/remove_await_parens.py @@ -79,3 +79,12 @@ async def main(): async def main(): await (yield) + +async def main(): + await (a ** b) + await (a[b] ** c) + await (a ** b[c]) + await ((a + b) ** (c + d)) + await (a + b) + await (a[b]) + await (a[b ** c]) diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/simple_cases/remove_await_parens.py.expect b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/remove_await_parens.py.expect similarity index 90% rename from crates/ruff_python_formatter/resources/test/fixtures/black/simple_cases/remove_await_parens.py.expect rename to crates/ruff_python_formatter/resources/test/fixtures/black/cases/remove_await_parens.py.expect index 91e7eb39e1..58e879d217 100644 --- a/crates/ruff_python_formatter/resources/test/fixtures/black/simple_cases/remove_await_parens.py.expect +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/remove_await_parens.py.expect @@ -91,3 +91,13 @@ async def main(): async def main(): await (yield) + + +async def main(): + await (a**b) + await (a[b] ** c) + await (a ** b[c]) + await ((a + b) ** (c + d)) + await (a + b) + await a[b] + await a[b**c] diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/simple_cases/remove_except_parens.py b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/remove_except_parens.py similarity index 100% rename from crates/ruff_python_formatter/resources/test/fixtures/black/simple_cases/remove_except_parens.py rename to crates/ruff_python_formatter/resources/test/fixtures/black/cases/remove_except_parens.py diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/simple_cases/remove_except_parens.py.expect b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/remove_except_parens.py.expect similarity index 100% rename from crates/ruff_python_formatter/resources/test/fixtures/black/simple_cases/remove_except_parens.py.expect rename to crates/ruff_python_formatter/resources/test/fixtures/black/cases/remove_except_parens.py.expect diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/simple_cases/remove_for_brackets.py b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/remove_for_brackets.py similarity index 100% rename from crates/ruff_python_formatter/resources/test/fixtures/black/simple_cases/remove_for_brackets.py rename to crates/ruff_python_formatter/resources/test/fixtures/black/cases/remove_for_brackets.py diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/simple_cases/remove_for_brackets.py.expect b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/remove_for_brackets.py.expect similarity index 100% rename from crates/ruff_python_formatter/resources/test/fixtures/black/simple_cases/remove_for_brackets.py.expect rename to crates/ruff_python_formatter/resources/test/fixtures/black/cases/remove_for_brackets.py.expect diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/simple_cases/remove_newline_after_code_block_open.py b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/remove_newline_after_code_block_open.py similarity index 100% rename from crates/ruff_python_formatter/resources/test/fixtures/black/simple_cases/remove_newline_after_code_block_open.py rename to crates/ruff_python_formatter/resources/test/fixtures/black/cases/remove_newline_after_code_block_open.py diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/simple_cases/remove_newline_after_code_block_open.py.expect b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/remove_newline_after_code_block_open.py.expect similarity index 100% rename from crates/ruff_python_formatter/resources/test/fixtures/black/simple_cases/remove_newline_after_code_block_open.py.expect rename to crates/ruff_python_formatter/resources/test/fixtures/black/cases/remove_newline_after_code_block_open.py.expect diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/py_310/remove_newline_after_match.py b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/remove_newline_after_match.py similarity index 100% rename from crates/ruff_python_formatter/resources/test/fixtures/black/py_310/remove_newline_after_match.py rename to crates/ruff_python_formatter/resources/test/fixtures/black/cases/remove_newline_after_match.py diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/py_310/remove_newline_after_match.py.expect b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/remove_newline_after_match.py.expect similarity index 100% rename from crates/ruff_python_formatter/resources/test/fixtures/black/py_310/remove_newline_after_match.py.expect rename to crates/ruff_python_formatter/resources/test/fixtures/black/cases/remove_newline_after_match.py.expect diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/simple_cases/remove_parens.py b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/remove_parens.py similarity index 100% rename from crates/ruff_python_formatter/resources/test/fixtures/black/simple_cases/remove_parens.py rename to crates/ruff_python_formatter/resources/test/fixtures/black/cases/remove_parens.py diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/simple_cases/remove_parens.py.expect b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/remove_parens.py.expect similarity index 100% rename from crates/ruff_python_formatter/resources/test/fixtures/black/simple_cases/remove_parens.py.expect rename to crates/ruff_python_formatter/resources/test/fixtures/black/cases/remove_parens.py.expect diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/py_39/remove_with_brackets.py b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/remove_with_brackets.py similarity index 100% rename from crates/ruff_python_formatter/resources/test/fixtures/black/py_39/remove_with_brackets.py rename to crates/ruff_python_formatter/resources/test/fixtures/black/cases/remove_with_brackets.py diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/py_39/remove_with_brackets.py.expect b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/remove_with_brackets.py.expect similarity index 100% rename from crates/ruff_python_formatter/resources/test/fixtures/black/py_39/remove_with_brackets.py.expect rename to crates/ruff_python_formatter/resources/test/fixtures/black/cases/remove_with_brackets.py.expect diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/simple_cases/return_annotation_brackets.py b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/return_annotation_brackets.py similarity index 93% rename from crates/ruff_python_formatter/resources/test/fixtures/black/simple_cases/return_annotation_brackets.py rename to crates/ruff_python_formatter/resources/test/fixtures/black/cases/return_annotation_brackets.py index 3e6b45147f..8322c4c7ac 100644 --- a/crates/ruff_python_formatter/resources/test/fixtures/black/simple_cases/return_annotation_brackets.py +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/return_annotation_brackets.py @@ -86,3 +86,8 @@ def foo() -> tuple[loooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo # Magic trailing comma example def foo() -> tuple[int, int, int,]: return 2 + +# Magic trailing comma example, with params +# this is broken - the trailing comma is transferred to the param list. Fixed in preview +def foo(a,b) -> tuple[int, int, int,]: + return 2 diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/simple_cases/return_annotation_brackets.py.expect b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/return_annotation_brackets.py.expect similarity index 92% rename from crates/ruff_python_formatter/resources/test/fixtures/black/simple_cases/return_annotation_brackets.py.expect rename to crates/ruff_python_formatter/resources/test/fixtures/black/cases/return_annotation_brackets.py.expect index 06880ecbe0..c8a8ce2ca2 100644 --- a/crates/ruff_python_formatter/resources/test/fixtures/black/simple_cases/return_annotation_brackets.py.expect +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/return_annotation_brackets.py.expect @@ -118,3 +118,11 @@ def foo() -> ( ] ): return 2 + + +# Magic trailing comma example, with params +# this is broken - the trailing comma is transferred to the param list. Fixed in preview +def foo( + a, b +) -> tuple[int, int, int,]: + return 2 diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/skip_magic_trailing_comma.options.json b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/skip_magic_trailing_comma.options.json new file mode 100644 index 0000000000..cbb67df0c6 --- /dev/null +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/skip_magic_trailing_comma.options.json @@ -0,0 +1 @@ +{"magic_trailing_comma": "ignore"} \ No newline at end of file diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/simple_cases/skip_magic_trailing_comma.py b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/skip_magic_trailing_comma.py similarity index 100% rename from crates/ruff_python_formatter/resources/test/fixtures/black/simple_cases/skip_magic_trailing_comma.py rename to crates/ruff_python_formatter/resources/test/fixtures/black/cases/skip_magic_trailing_comma.py diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/simple_cases/skip_magic_trailing_comma.py.expect b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/skip_magic_trailing_comma.py.expect similarity index 100% rename from crates/ruff_python_formatter/resources/test/fixtures/black/simple_cases/skip_magic_trailing_comma.py.expect rename to crates/ruff_python_formatter/resources/test/fixtures/black/cases/skip_magic_trailing_comma.py.expect diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/simple_cases/slices.py b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/slices.py similarity index 100% rename from crates/ruff_python_formatter/resources/test/fixtures/black/simple_cases/slices.py rename to crates/ruff_python_formatter/resources/test/fixtures/black/cases/slices.py diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/simple_cases/slices.py.expect b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/slices.py.expect similarity index 100% rename from crates/ruff_python_formatter/resources/test/fixtures/black/simple_cases/slices.py.expect rename to crates/ruff_python_formatter/resources/test/fixtures/black/cases/slices.py.expect diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/py_310/starred_for_target.py b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/starred_for_target.py similarity index 100% rename from crates/ruff_python_formatter/resources/test/fixtures/black/py_310/starred_for_target.py rename to crates/ruff_python_formatter/resources/test/fixtures/black/cases/starred_for_target.py diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/py_310/starred_for_target.py.expect b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/starred_for_target.py.expect similarity index 100% rename from crates/ruff_python_formatter/resources/test/fixtures/black/py_310/starred_for_target.py.expect rename to crates/ruff_python_formatter/resources/test/fixtures/black/cases/starred_for_target.py.expect diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/simple_cases/string_prefixes.py b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/string_prefixes.py similarity index 100% rename from crates/ruff_python_formatter/resources/test/fixtures/black/simple_cases/string_prefixes.py rename to crates/ruff_python_formatter/resources/test/fixtures/black/cases/string_prefixes.py diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/simple_cases/string_prefixes.py.expect b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/string_prefixes.py.expect similarity index 100% rename from crates/ruff_python_formatter/resources/test/fixtures/black/simple_cases/string_prefixes.py.expect rename to crates/ruff_python_formatter/resources/test/fixtures/black/cases/string_prefixes.py.expect diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/stub.pyi b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/stub.pyi new file mode 100644 index 0000000000..d3ad0ca1dd --- /dev/null +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/stub.pyi @@ -0,0 +1,75 @@ +X: int + +def f(): ... + + +class D: + ... + + +class C: + ... + +class B: + this_lack_of_newline_should_be_kept: int + def b(self) -> None: ... + + but_this_newline_should_also_be_kept: int + +class A: + attr: int + attr2: str + + def f(self) -> int: + ... + + def g(self) -> str: ... + + + +def g(): + ... + +def h(): ... + +if sys.version_info >= (3, 8): + class E: + def f(self): ... + class F: + + def f(self): ... + class G: ... + class H: ... +else: + class I: ... + class J: ... + def f(): ... + + class K: + def f(self): ... + def f(): ... + +class Nested: + class dirty: ... + class little: ... + class secret: + def who_has_to_know(self): ... + def verse(self): ... + +class Conditional: + def f(self): ... + if sys.version_info >= (3, 8): + def g(self): ... + else: + def g(self): ... + def h(self): ... + def i(self): ... + if sys.version_info >= (3, 8): + def j(self): ... + def k(self): ... + if sys.version_info >= (3, 8): + class A: ... + class B: ... + class C: + def l(self): ... + def m(self): ... diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/cases/stub.pyi.expect b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/stub.pyi.expect new file mode 100644 index 0000000000..5a7ab3a033 --- /dev/null +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/stub.pyi.expect @@ -0,0 +1,73 @@ +X: int + +def f(): ... + +class D: ... +class C: ... + +class B: + this_lack_of_newline_should_be_kept: int + def b(self) -> None: ... + + but_this_newline_should_also_be_kept: int + +class A: + attr: int + attr2: str + + def f(self) -> int: ... + def g(self) -> str: ... + +def g(): ... +def h(): ... + +if sys.version_info >= (3, 8): + class E: + def f(self): ... + + class F: + def f(self): ... + + class G: ... + class H: ... + +else: + class I: ... + class J: ... + + def f(): ... + + class K: + def f(self): ... + + def f(): ... + +class Nested: + class dirty: ... + class little: ... + + class secret: + def who_has_to_know(self): ... + + def verse(self): ... + +class Conditional: + def f(self): ... + if sys.version_info >= (3, 8): + def g(self): ... + else: + def g(self): ... + + def h(self): ... + def i(self): ... + if sys.version_info >= (3, 8): + def j(self): ... + + def k(self): ... + if sys.version_info >= (3, 8): + class A: ... + class B: ... + + class C: + def l(self): ... + def m(self): ... diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/simple_cases/torture.py b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/torture.py similarity index 100% rename from crates/ruff_python_formatter/resources/test/fixtures/black/simple_cases/torture.py rename to crates/ruff_python_formatter/resources/test/fixtures/black/cases/torture.py diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/simple_cases/torture.py.expect b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/torture.py.expect similarity index 100% rename from crates/ruff_python_formatter/resources/test/fixtures/black/simple_cases/torture.py.expect rename to crates/ruff_python_formatter/resources/test/fixtures/black/cases/torture.py.expect diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/simple_cases/trailing_comma_optional_parens1.py b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/trailing_comma_optional_parens1.py similarity index 100% rename from crates/ruff_python_formatter/resources/test/fixtures/black/simple_cases/trailing_comma_optional_parens1.py rename to crates/ruff_python_formatter/resources/test/fixtures/black/cases/trailing_comma_optional_parens1.py diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/simple_cases/trailing_comma_optional_parens1.py.expect b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/trailing_comma_optional_parens1.py.expect similarity index 100% rename from crates/ruff_python_formatter/resources/test/fixtures/black/simple_cases/trailing_comma_optional_parens1.py.expect rename to crates/ruff_python_formatter/resources/test/fixtures/black/cases/trailing_comma_optional_parens1.py.expect diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/simple_cases/trailing_comma_optional_parens2.py b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/trailing_comma_optional_parens2.py similarity index 100% rename from crates/ruff_python_formatter/resources/test/fixtures/black/simple_cases/trailing_comma_optional_parens2.py rename to crates/ruff_python_formatter/resources/test/fixtures/black/cases/trailing_comma_optional_parens2.py diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/simple_cases/trailing_comma_optional_parens2.py.expect b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/trailing_comma_optional_parens2.py.expect similarity index 100% rename from crates/ruff_python_formatter/resources/test/fixtures/black/simple_cases/trailing_comma_optional_parens2.py.expect rename to crates/ruff_python_formatter/resources/test/fixtures/black/cases/trailing_comma_optional_parens2.py.expect diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/simple_cases/trailing_comma_optional_parens3.py b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/trailing_comma_optional_parens3.py similarity index 100% rename from crates/ruff_python_formatter/resources/test/fixtures/black/simple_cases/trailing_comma_optional_parens3.py rename to crates/ruff_python_formatter/resources/test/fixtures/black/cases/trailing_comma_optional_parens3.py diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/simple_cases/trailing_comma_optional_parens3.py.expect b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/trailing_comma_optional_parens3.py.expect similarity index 100% rename from crates/ruff_python_formatter/resources/test/fixtures/black/simple_cases/trailing_comma_optional_parens3.py.expect rename to crates/ruff_python_formatter/resources/test/fixtures/black/cases/trailing_comma_optional_parens3.py.expect diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/simple_cases/trailing_commas_in_leading_parts.py b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/trailing_commas_in_leading_parts.py similarity index 100% rename from crates/ruff_python_formatter/resources/test/fixtures/black/simple_cases/trailing_commas_in_leading_parts.py rename to crates/ruff_python_formatter/resources/test/fixtures/black/cases/trailing_commas_in_leading_parts.py diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/simple_cases/trailing_commas_in_leading_parts.py.expect b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/trailing_commas_in_leading_parts.py.expect similarity index 100% rename from crates/ruff_python_formatter/resources/test/fixtures/black/simple_cases/trailing_commas_in_leading_parts.py.expect rename to crates/ruff_python_formatter/resources/test/fixtures/black/cases/trailing_commas_in_leading_parts.py.expect diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/simple_cases/tricky_unicode_symbols.py b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/tricky_unicode_symbols.py similarity index 100% rename from crates/ruff_python_formatter/resources/test/fixtures/black/simple_cases/tricky_unicode_symbols.py rename to crates/ruff_python_formatter/resources/test/fixtures/black/cases/tricky_unicode_symbols.py diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/simple_cases/tricky_unicode_symbols.py.expect b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/tricky_unicode_symbols.py.expect similarity index 100% rename from crates/ruff_python_formatter/resources/test/fixtures/black/simple_cases/tricky_unicode_symbols.py.expect rename to crates/ruff_python_formatter/resources/test/fixtures/black/cases/tricky_unicode_symbols.py.expect diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/simple_cases/tupleassign.py b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/tupleassign.py similarity index 100% rename from crates/ruff_python_formatter/resources/test/fixtures/black/simple_cases/tupleassign.py rename to crates/ruff_python_formatter/resources/test/fixtures/black/cases/tupleassign.py diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/simple_cases/tupleassign.py.expect b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/tupleassign.py.expect similarity index 100% rename from crates/ruff_python_formatter/resources/test/fixtures/black/simple_cases/tupleassign.py.expect rename to crates/ruff_python_formatter/resources/test/fixtures/black/cases/tupleassign.py.expect diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/type_comments/type_comment_syntax_error.py b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/type_comment_syntax_error.py similarity index 100% rename from crates/ruff_python_formatter/resources/test/fixtures/black/type_comments/type_comment_syntax_error.py rename to crates/ruff_python_formatter/resources/test/fixtures/black/cases/type_comment_syntax_error.py diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/type_comments/type_comment_syntax_error.py.expect b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/type_comment_syntax_error.py.expect similarity index 100% rename from crates/ruff_python_formatter/resources/test/fixtures/black/type_comments/type_comment_syntax_error.py.expect rename to crates/ruff_python_formatter/resources/test/fixtures/black/cases/type_comment_syntax_error.py.expect diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/py_312/type_params.py b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/type_params.py similarity index 100% rename from crates/ruff_python_formatter/resources/test/fixtures/black/py_312/type_params.py rename to crates/ruff_python_formatter/resources/test/fixtures/black/cases/type_params.py diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/py_312/type_params.py.expect b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/type_params.py.expect similarity index 100% rename from crates/ruff_python_formatter/resources/test/fixtures/black/py_312/type_params.py.expect rename to crates/ruff_python_formatter/resources/test/fixtures/black/cases/type_params.py.expect diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/simple_cases/whitespace.py b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/whitespace.py similarity index 100% rename from crates/ruff_python_formatter/resources/test/fixtures/black/simple_cases/whitespace.py rename to crates/ruff_python_formatter/resources/test/fixtures/black/cases/whitespace.py diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/simple_cases/whitespace.py.expect b/crates/ruff_python_formatter/resources/test/fixtures/black/cases/whitespace.py.expect similarity index 100% rename from crates/ruff_python_formatter/resources/test/fixtures/black/simple_cases/whitespace.py.expect rename to crates/ruff_python_formatter/resources/test/fixtures/black/cases/whitespace.py.expect diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/miscellaneous/decorators.py b/crates/ruff_python_formatter/resources/test/fixtures/black/miscellaneous/decorators.py deleted file mode 100644 index 46e37f69ed..0000000000 --- a/crates/ruff_python_formatter/resources/test/fixtures/black/miscellaneous/decorators.py +++ /dev/null @@ -1,150 +0,0 @@ -# This file doesn't use the standard decomposition. -# Decorator syntax test cases are separated by double # comments. -# Those before the 'output' comment are valid under the old syntax. -# Those after the 'ouput' comment require PEP614 relaxed syntax. -# Do not remove the double # separator before the first test case, it allows -# the comment before the test case to be ignored. - -## - -@decorator -def f(): - ... - -## - -@decorator() -def f(): - ... - -## - -@decorator(arg) -def f(): - ... - -## - -@decorator(kwarg=0) -def f(): - ... - -## - -@decorator(*args) -def f(): - ... - -## - -@decorator(**kwargs) -def f(): - ... - -## - -@decorator(*args, **kwargs) -def f(): - ... - -## - -@decorator(*args, **kwargs,) -def f(): - ... - -## - -@dotted.decorator -def f(): - ... - -## - -@dotted.decorator(arg) -def f(): - ... - -## - -@dotted.decorator(kwarg=0) -def f(): - ... - -## - -@dotted.decorator(*args) -def f(): - ... - -## - -@dotted.decorator(**kwargs) -def f(): - ... - -## - -@dotted.decorator(*args, **kwargs) -def f(): - ... - -## - -@dotted.decorator(*args, **kwargs,) -def f(): - ... - -## - -@double.dotted.decorator -def f(): - ... - -## - -@double.dotted.decorator(arg) -def f(): - ... - -## - -@double.dotted.decorator(kwarg=0) -def f(): - ... - -## - -@double.dotted.decorator(*args) -def f(): - ... - -## - -@double.dotted.decorator(**kwargs) -def f(): - ... - -## - -@double.dotted.decorator(*args, **kwargs) -def f(): - ... - -## - -@double.dotted.decorator(*args, **kwargs,) -def f(): - ... - -## - -@_(sequence["decorator"]) -def f(): - ... - -## - -@eval("sequence['decorator']") -def f(): - ... diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/miscellaneous/decorators.py.expect b/crates/ruff_python_formatter/resources/test/fixtures/black/miscellaneous/decorators.py.expect deleted file mode 100644 index df17e1e749..0000000000 --- a/crates/ruff_python_formatter/resources/test/fixtures/black/miscellaneous/decorators.py.expect +++ /dev/null @@ -1,29 +0,0 @@ -## - -@decorator()() -def f(): - ... - -## - -@(decorator) -def f(): - ... - -## - -@sequence["decorator"] -def f(): - ... - -## - -@decorator[List[str]] -def f(): - ... - -## - -@var := decorator -def f(): - ... diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/miscellaneous/force_pyi.py b/crates/ruff_python_formatter/resources/test/fixtures/black/miscellaneous/force_pyi.py index edc7495e66..9c8c40cc96 100644 --- a/crates/ruff_python_formatter/resources/test/fixtures/black/miscellaneous/force_pyi.py +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/miscellaneous/force_pyi.py @@ -1,4 +1,3 @@ -# flags: --pyi from typing import Union @bird diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/miscellaneous/force_pyi.pyi b/crates/ruff_python_formatter/resources/test/fixtures/black/miscellaneous/force_pyi.pyi new file mode 100644 index 0000000000..9c8c40cc96 --- /dev/null +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/miscellaneous/force_pyi.pyi @@ -0,0 +1,30 @@ +from typing import Union + +@bird +def zoo(): ... + +class A: ... +@bar +class B: + def BMethod(self) -> None: ... + @overload + def BMethod(self, arg : List[str]) -> None: ... + +class C: ... +@hmm +class D: ... +class E: ... + +@baz +def foo() -> None: + ... + +class F (A , C): ... +def spam() -> None: ... + +@overload +def spam(arg: str) -> str: ... + +var : int = 1 + +def eggs() -> Union[str, int]: ... diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/miscellaneous/force_pyi.pyi.expect b/crates/ruff_python_formatter/resources/test/fixtures/black/miscellaneous/force_pyi.pyi.expect new file mode 100644 index 0000000000..4349ba0a53 --- /dev/null +++ b/crates/ruff_python_formatter/resources/test/fixtures/black/miscellaneous/force_pyi.pyi.expect @@ -0,0 +1,32 @@ +from typing import Union + +@bird +def zoo(): ... + +class A: ... + +@bar +class B: + def BMethod(self) -> None: ... + @overload + def BMethod(self, arg: List[str]) -> None: ... + +class C: ... + +@hmm +class D: ... + +class E: ... + +@baz +def foo() -> None: ... + +class F(A, C): ... + +def spam() -> None: ... +@overload +def spam(arg: str) -> str: ... + +var: int = 1 + +def eggs() -> Union[str, int]: ... diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/py_312/type_aliases.py b/crates/ruff_python_formatter/resources/test/fixtures/black/py_312/type_aliases.py deleted file mode 100644 index cdac8402f4..0000000000 --- a/crates/ruff_python_formatter/resources/test/fixtures/black/py_312/type_aliases.py +++ /dev/null @@ -1,5 +0,0 @@ -type A=int -type Gen[T]=list[T] - -type = aliased -print(type(42)) diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/py_312/type_aliases.py.expect b/crates/ruff_python_formatter/resources/test/fixtures/black/py_312/type_aliases.py.expect deleted file mode 100644 index 308a164e19..0000000000 --- a/crates/ruff_python_formatter/resources/test/fixtures/black/py_312/type_aliases.py.expect +++ /dev/null @@ -1,5 +0,0 @@ -type A = int -type Gen[T] = list[T] - -type = aliased -print(type(42)) diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/simple_cases/docstring.options.json b/crates/ruff_python_formatter/resources/test/fixtures/black/simple_cases/docstring.options.json deleted file mode 100644 index f709c8d714..0000000000 --- a/crates/ruff_python_formatter/resources/test/fixtures/black/simple_cases/docstring.options.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "indent_width": 4 -} diff --git a/crates/ruff_python_formatter/resources/test/fixtures/black/simple_cases/skip_magic_trailing_comma.options.json b/crates/ruff_python_formatter/resources/test/fixtures/black/simple_cases/skip_magic_trailing_comma.options.json deleted file mode 100644 index e01e786cb6..0000000000 --- a/crates/ruff_python_formatter/resources/test/fixtures/black/simple_cases/skip_magic_trailing_comma.options.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "magic_trailing_comma": "ignore" -} \ No newline at end of file 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 ad45c01784..31d1515aef 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 @@ -3,6 +3,8 @@ from __future__ import annotations import argparse +import json +import os from pathlib import Path @@ -14,22 +16,18 @@ def import_fixture(fixture: Path, fixture_set: str): output_directory = Path(__file__).parent.joinpath("black").joinpath(fixture_set) output_directory.mkdir(parents=True, exist_ok=True) - fixture_path = output_directory.joinpath(fixture.name) - expect_path = fixture_path.with_suffix(".py.expect") - - with ( - fixture.open("r") as black_file, - fixture_path.open("w") as fixture_file, - expect_path.open("w") as expect_file - ): + with fixture.open("r") as black_file: lines = iter(black_file) expected = [] input = [] + flags = None for line in lines: if line.rstrip() == "# output": expected = list(lines) break + elif not input and line.startswith("# flags:"): + flags = line else: input.append(line) @@ -37,24 +35,46 @@ def import_fixture(fixture: Path, fixture_set: str): # If there's no output marker, tread the whole file as already pre-formatted expected = input - fixture_file.write("".join(input).strip() + "\n") - expect_file.write("".join(expected).strip() + "\n") + options = {} + extension = "py" + + if flags: + if "--preview" in flags: + options["preview"] = "enabled" + + if "--pyi" in flags: + extension = "pyi" + + if "--line-length=" in flags: + [_, length_and_rest] = flags.split("--line-length=", 1) + length = length_and_rest.split(" ", 1)[0] + options["line_length"] = int(length) + + if "--skip-magic-trailing-comma" in flags: + options["magic_trailing_comma"] = "ignore" + + fixture_path = output_directory.joinpath(fixture.name).with_suffix(f".{extension}") + expect_path = fixture_path.with_suffix(f".{extension}.expect") + options_path = fixture_path.with_suffix(".options.json") + + if len(options) > 0: + with options_path.open("w") as options_file: + json.dump(options, options_file) + elif os.path.exists(options_path): + os.remove(options_path) + + with ( + fixture_path.open("w") as fixture_file, + expect_path.open("w") as expect_file + ): + fixture_file.write("".join(input).strip() + "\n") + expect_file.write("".join(expected).strip() + "\n") # The name of the folders in the `data` for which the tests should be imported FIXTURE_SETS = [ - "fast", - "py_36", - "py_37", - "py_38", - "py_39", - "py_310", - "py_311", - "py_312", - "simple_cases", + "cases", "miscellaneous", - ".", - "type_comments" ] # Tests that ruff doesn't fully support yet and, therefore, should not be imported @@ -63,9 +83,16 @@ IGNORE_LIST = [ "async_as_identifier.py", "invalid_header.py", "pattern_matching_invalid.py", + "pep_572_do_not_remove_parens.py", # Python 2 - "python2_detection.py" + "python2_detection.py", + + # Uses a different output format + "decorators.py", + + # Ruff fails to parse because of a parser bug + "type_aliases.py" # #8900 #8899 ] diff --git a/crates/ruff_python_formatter/src/expression/string/docstring.rs b/crates/ruff_python_formatter/src/expression/string/docstring.rs index 21057e4645..3155fa1a0f 100644 --- a/crates/ruff_python_formatter/src/expression/string/docstring.rs +++ b/crates/ruff_python_formatter/src/expression/string/docstring.rs @@ -1,5 +1,6 @@ use std::borrow::Cow; +use ruff_python_trivia::PythonWhitespace; use { ruff_formatter::{write, Printed}, ruff_source_file::Locator, @@ -99,7 +100,7 @@ pub(super) fn format(normalized: &NormalizedString, f: &mut PyFormatter) -> Form let docstring = &normalized.text; // Black doesn't change the indentation of docstrings that contain an escaped newline - if docstring.contains("\\\n") { + if contains_unescaped_newline(docstring) { return normalized.fmt(f); } @@ -200,6 +201,20 @@ pub(super) fn format(normalized: &NormalizedString, f: &mut PyFormatter) -> Form write!(f, [source_position(normalized.end()), normalized.quotes]) } +fn contains_unescaped_newline(haystack: &str) -> bool { + let mut rest = haystack; + + while let Some(index) = memchr::memchr(b'\\', rest.as_bytes()) { + rest = &rest[index + 1..].trim_whitespace_start(); + + if rest.starts_with('\n') { + return true; + } + } + + false +} + /// An abstraction for printing each line of a docstring. struct DocstringLinePrinter<'ast, 'buf, 'fmt, 'src> { f: &'fmt mut PyFormatter<'ast, 'buf>, diff --git a/crates/ruff_python_formatter/tests/fixtures.rs b/crates/ruff_python_formatter/tests/fixtures.rs index 1b949c7b73..3c48027d98 100644 --- a/crates/ruff_python_formatter/tests/fixtures.rs +++ b/crates/ruff_python_formatter/tests/fixtures.rs @@ -20,9 +20,10 @@ fn black_compatibility() { let options_path = input_path.with_extension("options.json"); - let options: PyFormatOptions = if let Ok(options_file) = fs::File::open(options_path) { + let options: PyFormatOptions = if let Ok(options_file) = fs::File::open(&options_path) { let reader = BufReader::new(options_file); - serde_json::from_reader(reader).expect("Options to be a valid Json file") + serde_json::from_reader(reader) + .unwrap_or_else(|_| panic!("Option file {options_path:?} to be a valid Json file")) } else { PyFormatOptions::from_extension(input_path) }; @@ -34,7 +35,11 @@ fn black_compatibility() { ) }); - let expected_path = input_path.with_extension("py.expect"); + let extension = input_path + .extension() + .expect("Test file to have py or pyi extension") + .to_string_lossy(); + let expected_path = input_path.with_extension(format!("{extension}.expect")); let expected_output = fs::read_to_string(&expected_path) .unwrap_or_else(|_| panic!("Expected Black output file '{expected_path:?}' to exist")); @@ -106,7 +111,11 @@ fn black_compatibility() { } }; - insta::glob!("../resources", "test/fixtures/black/**/*.py", test_file); + insta::glob!( + "../resources", + "test/fixtures/black/**/*.{py,pyi}", + test_file + ); } #[test] diff --git a/crates/ruff_python_formatter/tests/snapshots/black_compatibility@simple_cases__comment_after_escaped_newline.py.snap b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__comment_after_escaped_newline.py.snap similarity index 93% rename from crates/ruff_python_formatter/tests/snapshots/black_compatibility@simple_cases__comment_after_escaped_newline.py.snap rename to crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__comment_after_escaped_newline.py.snap index 9656ed1e16..d936affef1 100644 --- a/crates/ruff_python_formatter/tests/snapshots/black_compatibility@simple_cases__comment_after_escaped_newline.py.snap +++ b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__comment_after_escaped_newline.py.snap @@ -1,6 +1,6 @@ --- source: crates/ruff_python_formatter/tests/fixtures.rs -input_file: crates/ruff_python_formatter/resources/test/fixtures/black/simple_cases/comment_after_escaped_newline.py +input_file: crates/ruff_python_formatter/resources/test/fixtures/black/cases/comment_after_escaped_newline.py --- ## Input diff --git a/crates/ruff_python_formatter/tests/snapshots/black_compatibility@simple_cases__comments2.py.snap b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__comments2.py.snap similarity index 99% rename from crates/ruff_python_formatter/tests/snapshots/black_compatibility@simple_cases__comments2.py.snap rename to crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__comments2.py.snap index 9c0622bce3..d9273e2993 100644 --- a/crates/ruff_python_formatter/tests/snapshots/black_compatibility@simple_cases__comments2.py.snap +++ b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__comments2.py.snap @@ -1,6 +1,6 @@ --- source: crates/ruff_python_formatter/tests/fixtures.rs -input_file: crates/ruff_python_formatter/resources/test/fixtures/black/simple_cases/comments2.py +input_file: crates/ruff_python_formatter/resources/test/fixtures/black/cases/comments2.py --- ## Input diff --git a/crates/ruff_python_formatter/tests/snapshots/black_compatibility@simple_cases__comments6.py.snap b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__comments6.py.snap similarity index 99% rename from crates/ruff_python_formatter/tests/snapshots/black_compatibility@simple_cases__comments6.py.snap rename to crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__comments6.py.snap index 45ab60a21a..f1bc3b6173 100644 --- a/crates/ruff_python_formatter/tests/snapshots/black_compatibility@simple_cases__comments6.py.snap +++ b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__comments6.py.snap @@ -1,6 +1,6 @@ --- source: crates/ruff_python_formatter/tests/fixtures.rs -input_file: crates/ruff_python_formatter/resources/test/fixtures/black/simple_cases/comments6.py +input_file: crates/ruff_python_formatter/resources/test/fixtures/black/cases/comments6.py --- ## Input diff --git a/crates/ruff_python_formatter/tests/snapshots/black_compatibility@simple_cases__comments9.py.snap b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__comments9.py.snap similarity index 99% rename from crates/ruff_python_formatter/tests/snapshots/black_compatibility@simple_cases__comments9.py.snap rename to crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__comments9.py.snap index 743906edda..5e25b161c9 100644 --- a/crates/ruff_python_formatter/tests/snapshots/black_compatibility@simple_cases__comments9.py.snap +++ b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__comments9.py.snap @@ -1,6 +1,6 @@ --- source: crates/ruff_python_formatter/tests/fixtures.rs -input_file: crates/ruff_python_formatter/resources/test/fixtures/black/simple_cases/comments9.py +input_file: crates/ruff_python_formatter/resources/test/fixtures/black/cases/comments9.py --- ## Input diff --git a/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__comments_in_blocks.py.snap b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__comments_in_blocks.py.snap new file mode 100644 index 0000000000..7524d7c7a7 --- /dev/null +++ b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__comments_in_blocks.py.snap @@ -0,0 +1,382 @@ +--- +source: crates/ruff_python_formatter/tests/fixtures.rs +input_file: crates/ruff_python_formatter/resources/test/fixtures/black/cases/comments_in_blocks.py +--- +## Input + +```python +# Test cases from: +# - https://github.com/psf/black/issues/1798 +# - https://github.com/psf/black/issues/1499 +# - https://github.com/psf/black/issues/1211 +# - https://github.com/psf/black/issues/563 + +( + lambda + # a comment + : None +) + +( + lambda: + # b comment + None +) + +( + lambda + # a comment + : + # b comment + None +) + +[ + x + # Let's do this + for + # OK? + x + # Some comment + # And another + in + # One more + y +] + +return [ + (offers[offer_index], 1.0) + for offer_index, _ + # avoid returning any offers that don't match the grammar so + # that the return values here are consistent with what would be + # returned in AcceptValidHeader + in self._parse_and_normalize_offers(offers) +] + +from foo import ( + bar, + # qux +) + + +def convert(collection): + # replace all variables by integers + replacement_dict = { + variable: f"{index}" + for index, variable + # 0 is reserved as line terminator + in enumerate(collection.variables(), start=1) + } + + +{ + i: i + for i + # a comment + in range(5) +} + + +def get_subtree_proof_nodes( + chunk_index_groups: Sequence[Tuple[int, ...], ...], +) -> Tuple[int, ...]: + subtree_node_paths = ( + # We take a candidate element from each group and shift it to + # remove the bits that are not common to other group members, then + # we convert it to a tree path that all elements from this group + # have in common. + chunk_index + for chunk_index, bits_to_truncate + # Each group will contain an even "power-of-two" number of# elements. + # This tells us how many tailing bits each element has# which need to + # be truncated to get the group's common prefix. + in ((group[0], (len(group) - 1).bit_length()) for group in chunk_index_groups) + ) + return subtree_node_paths + + +if ( + # comment1 + a + # comment2 + or ( + # comment3 + ( + # comment4 + b + ) + # comment5 + and + # comment6 + c + or ( + # comment7 + d + ) + ) +): + print("Foo") +``` + +## Black Differences + +```diff +--- Black ++++ Ruff +@@ -5,9 +5,9 @@ + # - https://github.com/psf/black/issues/563 + + ( +- lambda ++ lambda: + # a comment +- : None ++ None + ) + + ( +@@ -17,9 +17,8 @@ + ) + + ( +- lambda ++ lambda: + # a comment +- : + # b comment + None + ) +``` + +## Ruff Output + +```python +# Test cases from: +# - https://github.com/psf/black/issues/1798 +# - https://github.com/psf/black/issues/1499 +# - https://github.com/psf/black/issues/1211 +# - https://github.com/psf/black/issues/563 + +( + lambda: + # a comment + None +) + +( + lambda: + # b comment + None +) + +( + lambda: + # a comment + # b comment + None +) + +[ + x + # Let's do this + for + # OK? + x + # Some comment + # And another + in + # One more + y +] + +return [ + (offers[offer_index], 1.0) + for offer_index, _ + # avoid returning any offers that don't match the grammar so + # that the return values here are consistent with what would be + # returned in AcceptValidHeader + in self._parse_and_normalize_offers(offers) +] + +from foo import ( + bar, + # qux +) + + +def convert(collection): + # replace all variables by integers + replacement_dict = { + variable: f"{index}" + for index, variable + # 0 is reserved as line terminator + in enumerate(collection.variables(), start=1) + } + + +{ + i: i + for i + # a comment + in range(5) +} + + +def get_subtree_proof_nodes( + chunk_index_groups: Sequence[Tuple[int, ...], ...], +) -> Tuple[int, ...]: + subtree_node_paths = ( + # We take a candidate element from each group and shift it to + # remove the bits that are not common to other group members, then + # we convert it to a tree path that all elements from this group + # have in common. + chunk_index + for chunk_index, bits_to_truncate + # Each group will contain an even "power-of-two" number of# elements. + # This tells us how many tailing bits each element has# which need to + # be truncated to get the group's common prefix. + in ((group[0], (len(group) - 1).bit_length()) for group in chunk_index_groups) + ) + return subtree_node_paths + + +if ( + # comment1 + a + # comment2 + or ( + # comment3 + ( + # comment4 + b + ) + # comment5 + and + # comment6 + c + or ( + # comment7 + d + ) + ) +): + print("Foo") +``` + +## Black Output + +```python +# Test cases from: +# - https://github.com/psf/black/issues/1798 +# - https://github.com/psf/black/issues/1499 +# - https://github.com/psf/black/issues/1211 +# - https://github.com/psf/black/issues/563 + +( + lambda + # a comment + : None +) + +( + lambda: + # b comment + None +) + +( + lambda + # a comment + : + # b comment + None +) + +[ + x + # Let's do this + for + # OK? + x + # Some comment + # And another + in + # One more + y +] + +return [ + (offers[offer_index], 1.0) + for offer_index, _ + # avoid returning any offers that don't match the grammar so + # that the return values here are consistent with what would be + # returned in AcceptValidHeader + in self._parse_and_normalize_offers(offers) +] + +from foo import ( + bar, + # qux +) + + +def convert(collection): + # replace all variables by integers + replacement_dict = { + variable: f"{index}" + for index, variable + # 0 is reserved as line terminator + in enumerate(collection.variables(), start=1) + } + + +{ + i: i + for i + # a comment + in range(5) +} + + +def get_subtree_proof_nodes( + chunk_index_groups: Sequence[Tuple[int, ...], ...], +) -> Tuple[int, ...]: + subtree_node_paths = ( + # We take a candidate element from each group and shift it to + # remove the bits that are not common to other group members, then + # we convert it to a tree path that all elements from this group + # have in common. + chunk_index + for chunk_index, bits_to_truncate + # Each group will contain an even "power-of-two" number of# elements. + # This tells us how many tailing bits each element has# which need to + # be truncated to get the group's common prefix. + in ((group[0], (len(group) - 1).bit_length()) for group in chunk_index_groups) + ) + return subtree_node_paths + + +if ( + # comment1 + a + # comment2 + or ( + # comment3 + ( + # comment4 + b + ) + # comment5 + and + # comment6 + c + or ( + # comment7 + d + ) + ) +): + print("Foo") +``` + + diff --git a/crates/ruff_python_formatter/tests/snapshots/black_compatibility@simple_cases__composition.py.snap b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__composition.py.snap similarity index 99% rename from crates/ruff_python_formatter/tests/snapshots/black_compatibility@simple_cases__composition.py.snap rename to crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__composition.py.snap index 756ef81dd3..c1c42e11bb 100644 --- a/crates/ruff_python_formatter/tests/snapshots/black_compatibility@simple_cases__composition.py.snap +++ b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__composition.py.snap @@ -1,6 +1,6 @@ --- source: crates/ruff_python_formatter/tests/fixtures.rs -input_file: crates/ruff_python_formatter/resources/test/fixtures/black/simple_cases/composition.py +input_file: crates/ruff_python_formatter/resources/test/fixtures/black/cases/composition.py --- ## Input diff --git a/crates/ruff_python_formatter/tests/snapshots/black_compatibility@simple_cases__composition_no_trailing_comma.py.snap b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__composition_no_trailing_comma.py.snap similarity index 99% rename from crates/ruff_python_formatter/tests/snapshots/black_compatibility@simple_cases__composition_no_trailing_comma.py.snap rename to crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__composition_no_trailing_comma.py.snap index 89664dc7c1..4888f9a617 100644 --- a/crates/ruff_python_formatter/tests/snapshots/black_compatibility@simple_cases__composition_no_trailing_comma.py.snap +++ b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__composition_no_trailing_comma.py.snap @@ -1,6 +1,6 @@ --- source: crates/ruff_python_formatter/tests/fixtures.rs -input_file: crates/ruff_python_formatter/resources/test/fixtures/black/simple_cases/composition_no_trailing_comma.py +input_file: crates/ruff_python_formatter/resources/test/fixtures/black/cases/composition_no_trailing_comma.py --- ## Input diff --git a/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__conditional_expression.py.snap b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__conditional_expression.py.snap new file mode 100644 index 0000000000..55b4f12034 --- /dev/null +++ b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__conditional_expression.py.snap @@ -0,0 +1,334 @@ +--- +source: crates/ruff_python_formatter/tests/fixtures.rs +input_file: crates/ruff_python_formatter/resources/test/fixtures/black/cases/conditional_expression.py +--- +## Input + +```python +long_kwargs_single_line = my_function( + foo="test, this is a sample value", + bar=some_long_value_name_foo_bar_baz if some_boolean_variable else some_fallback_value_foo_bar_baz, + baz="hello, this is a another value", +) + +multiline_kwargs_indented = my_function( + foo="test, this is a sample value", + bar=some_long_value_name_foo_bar_baz + if some_boolean_variable + else some_fallback_value_foo_bar_baz, + baz="hello, this is a another value", +) + +imploding_kwargs = my_function( + foo="test, this is a sample value", + bar=a + if foo + else b, + baz="hello, this is a another value", +) + +imploding_line = ( + 1 + if 1 + 1 == 2 + else 0 +) + +exploding_line = "hello this is a slightly long string" if some_long_value_name_foo_bar_baz else "this one is a little shorter" + +positional_argument_test(some_long_value_name_foo_bar_baz if some_boolean_variable else some_fallback_value_foo_bar_baz) + +def weird_default_argument(x=some_long_value_name_foo_bar_baz + if SOME_CONSTANT + else some_fallback_value_foo_bar_baz): + pass + +nested = "hello this is a slightly long string" if (some_long_value_name_foo_bar_baz if + nesting_test_expressions else some_fallback_value_foo_bar_baz) \ + else "this one is a little shorter" + +generator_expression = ( + some_long_value_name_foo_bar_baz if some_boolean_variable else some_fallback_value_foo_bar_baz for some_boolean_variable in some_iterable +) + + +def limit_offset_sql(self, low_mark, high_mark): + """Return LIMIT/OFFSET SQL clause.""" + limit, offset = self._get_limit_offset_params(low_mark, high_mark) + return " ".join( + sql + for sql in ( + "LIMIT %d" % limit if limit else None, + ("OFFSET %d" % offset) if offset else None, + ) + if sql + ) + + +def something(): + clone._iterable_class = ( + NamedValuesListIterable + if named + else FlatValuesListIterable + if flat + else ValuesListIterable + ) +``` + +## Black Differences + +```diff +--- Black ++++ Ruff +@@ -1,20 +1,16 @@ + long_kwargs_single_line = my_function( + foo="test, this is a sample value", +- bar=( +- some_long_value_name_foo_bar_baz +- if some_boolean_variable +- else some_fallback_value_foo_bar_baz +- ), ++ bar=some_long_value_name_foo_bar_baz ++ if some_boolean_variable ++ else some_fallback_value_foo_bar_baz, + baz="hello, this is a another value", + ) + + multiline_kwargs_indented = my_function( + foo="test, this is a sample value", +- bar=( +- some_long_value_name_foo_bar_baz +- if some_boolean_variable +- else some_fallback_value_foo_bar_baz +- ), ++ bar=some_long_value_name_foo_bar_baz ++ if some_boolean_variable ++ else some_fallback_value_foo_bar_baz, + baz="hello, this is a another value", + ) + +@@ -40,11 +36,9 @@ + + + def weird_default_argument( +- x=( +- some_long_value_name_foo_bar_baz +- if SOME_CONSTANT +- else some_fallback_value_foo_bar_baz +- ), ++ x=some_long_value_name_foo_bar_baz ++ if SOME_CONSTANT ++ else some_fallback_value_foo_bar_baz, + ): + pass + +@@ -60,11 +54,9 @@ + ) + + generator_expression = ( +- ( +- some_long_value_name_foo_bar_baz +- if some_boolean_variable +- else some_fallback_value_foo_bar_baz +- ) ++ some_long_value_name_foo_bar_baz ++ if some_boolean_variable ++ else some_fallback_value_foo_bar_baz + for some_boolean_variable in some_iterable + ) + +@@ -86,5 +78,7 @@ + clone._iterable_class = ( + NamedValuesListIterable + if named +- else FlatValuesListIterable if flat else ValuesListIterable ++ else FlatValuesListIterable ++ if flat ++ else ValuesListIterable + ) +``` + +## Ruff Output + +```python +long_kwargs_single_line = my_function( + foo="test, this is a sample value", + bar=some_long_value_name_foo_bar_baz + if some_boolean_variable + else some_fallback_value_foo_bar_baz, + baz="hello, this is a another value", +) + +multiline_kwargs_indented = my_function( + foo="test, this is a sample value", + bar=some_long_value_name_foo_bar_baz + if some_boolean_variable + else some_fallback_value_foo_bar_baz, + baz="hello, this is a another value", +) + +imploding_kwargs = my_function( + foo="test, this is a sample value", + bar=a if foo else b, + baz="hello, this is a another value", +) + +imploding_line = 1 if 1 + 1 == 2 else 0 + +exploding_line = ( + "hello this is a slightly long string" + if some_long_value_name_foo_bar_baz + else "this one is a little shorter" +) + +positional_argument_test( + some_long_value_name_foo_bar_baz + if some_boolean_variable + else some_fallback_value_foo_bar_baz +) + + +def weird_default_argument( + x=some_long_value_name_foo_bar_baz + if SOME_CONSTANT + else some_fallback_value_foo_bar_baz, +): + pass + + +nested = ( + "hello this is a slightly long string" + if ( + some_long_value_name_foo_bar_baz + if nesting_test_expressions + else some_fallback_value_foo_bar_baz + ) + else "this one is a little shorter" +) + +generator_expression = ( + some_long_value_name_foo_bar_baz + if some_boolean_variable + else some_fallback_value_foo_bar_baz + for some_boolean_variable in some_iterable +) + + +def limit_offset_sql(self, low_mark, high_mark): + """Return LIMIT/OFFSET SQL clause.""" + limit, offset = self._get_limit_offset_params(low_mark, high_mark) + return " ".join( + sql + for sql in ( + "LIMIT %d" % limit if limit else None, + ("OFFSET %d" % offset) if offset else None, + ) + if sql + ) + + +def something(): + clone._iterable_class = ( + NamedValuesListIterable + if named + else FlatValuesListIterable + if flat + else ValuesListIterable + ) +``` + +## Black Output + +```python +long_kwargs_single_line = my_function( + foo="test, this is a sample value", + bar=( + some_long_value_name_foo_bar_baz + if some_boolean_variable + else some_fallback_value_foo_bar_baz + ), + baz="hello, this is a another value", +) + +multiline_kwargs_indented = my_function( + foo="test, this is a sample value", + bar=( + some_long_value_name_foo_bar_baz + if some_boolean_variable + else some_fallback_value_foo_bar_baz + ), + baz="hello, this is a another value", +) + +imploding_kwargs = my_function( + foo="test, this is a sample value", + bar=a if foo else b, + baz="hello, this is a another value", +) + +imploding_line = 1 if 1 + 1 == 2 else 0 + +exploding_line = ( + "hello this is a slightly long string" + if some_long_value_name_foo_bar_baz + else "this one is a little shorter" +) + +positional_argument_test( + some_long_value_name_foo_bar_baz + if some_boolean_variable + else some_fallback_value_foo_bar_baz +) + + +def weird_default_argument( + x=( + some_long_value_name_foo_bar_baz + if SOME_CONSTANT + else some_fallback_value_foo_bar_baz + ), +): + pass + + +nested = ( + "hello this is a slightly long string" + if ( + some_long_value_name_foo_bar_baz + if nesting_test_expressions + else some_fallback_value_foo_bar_baz + ) + else "this one is a little shorter" +) + +generator_expression = ( + ( + some_long_value_name_foo_bar_baz + if some_boolean_variable + else some_fallback_value_foo_bar_baz + ) + for some_boolean_variable in some_iterable +) + + +def limit_offset_sql(self, low_mark, high_mark): + """Return LIMIT/OFFSET SQL clause.""" + limit, offset = self._get_limit_offset_params(low_mark, high_mark) + return " ".join( + sql + for sql in ( + "LIMIT %d" % limit if limit else None, + ("OFFSET %d" % offset) if offset else None, + ) + if sql + ) + + +def something(): + clone._iterable_class = ( + NamedValuesListIterable + if named + else FlatValuesListIterable if flat else ValuesListIterable + ) +``` + + diff --git a/crates/ruff_python_formatter/tests/snapshots/black_compatibility@miscellaneous__docstring_no_string_normalization.py.snap b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__docstring_no_string_normalization.py.snap similarity index 99% rename from crates/ruff_python_formatter/tests/snapshots/black_compatibility@miscellaneous__docstring_no_string_normalization.py.snap rename to crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__docstring_no_string_normalization.py.snap index bdc68fe77b..17915fd54d 100644 --- a/crates/ruff_python_formatter/tests/snapshots/black_compatibility@miscellaneous__docstring_no_string_normalization.py.snap +++ b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__docstring_no_string_normalization.py.snap @@ -1,6 +1,6 @@ --- source: crates/ruff_python_formatter/tests/fixtures.rs -input_file: crates/ruff_python_formatter/resources/test/fixtures/black/miscellaneous/docstring_no_string_normalization.py +input_file: crates/ruff_python_formatter/resources/test/fixtures/black/cases/docstring_no_string_normalization.py --- ## Input diff --git a/crates/ruff_python_formatter/tests/snapshots/black_compatibility@simple_cases__expression.py.snap b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__expression.py.snap similarity index 99% rename from crates/ruff_python_formatter/tests/snapshots/black_compatibility@simple_cases__expression.py.snap rename to crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__expression.py.snap index ea14186d29..5b60337c94 100644 --- a/crates/ruff_python_formatter/tests/snapshots/black_compatibility@simple_cases__expression.py.snap +++ b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__expression.py.snap @@ -1,6 +1,6 @@ --- source: crates/ruff_python_formatter/tests/fixtures.rs -input_file: crates/ruff_python_formatter/resources/test/fixtures/black/simple_cases/expression.py +input_file: crates/ruff_python_formatter/resources/test/fixtures/black/cases/expression.py --- ## Input diff --git a/crates/ruff_python_formatter/tests/snapshots/black_compatibility@simple_cases__fmtonoff.py.snap b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__fmtonoff.py.snap similarity index 99% rename from crates/ruff_python_formatter/tests/snapshots/black_compatibility@simple_cases__fmtonoff.py.snap rename to crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__fmtonoff.py.snap index 2df0b531f8..44fe937154 100644 --- a/crates/ruff_python_formatter/tests/snapshots/black_compatibility@simple_cases__fmtonoff.py.snap +++ b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__fmtonoff.py.snap @@ -1,6 +1,6 @@ --- source: crates/ruff_python_formatter/tests/fixtures.rs -input_file: crates/ruff_python_formatter/resources/test/fixtures/black/simple_cases/fmtonoff.py +input_file: crates/ruff_python_formatter/resources/test/fixtures/black/cases/fmtonoff.py --- ## Input diff --git a/crates/ruff_python_formatter/tests/snapshots/black_compatibility@simple_cases__fmtonoff4.py.snap b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__fmtonoff4.py.snap similarity index 96% rename from crates/ruff_python_formatter/tests/snapshots/black_compatibility@simple_cases__fmtonoff4.py.snap rename to crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__fmtonoff4.py.snap index 529e50d32c..9162f85921 100644 --- a/crates/ruff_python_formatter/tests/snapshots/black_compatibility@simple_cases__fmtonoff4.py.snap +++ b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__fmtonoff4.py.snap @@ -1,6 +1,6 @@ --- source: crates/ruff_python_formatter/tests/fixtures.rs -input_file: crates/ruff_python_formatter/resources/test/fixtures/black/simple_cases/fmtonoff4.py +input_file: crates/ruff_python_formatter/resources/test/fixtures/black/cases/fmtonoff4.py --- ## Input diff --git a/crates/ruff_python_formatter/tests/snapshots/black_compatibility@simple_cases__fmtonoff5.py.snap b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__fmtonoff5.py.snap similarity index 99% rename from crates/ruff_python_formatter/tests/snapshots/black_compatibility@simple_cases__fmtonoff5.py.snap rename to crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__fmtonoff5.py.snap index a531a87c05..1346480b80 100644 --- a/crates/ruff_python_formatter/tests/snapshots/black_compatibility@simple_cases__fmtonoff5.py.snap +++ b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__fmtonoff5.py.snap @@ -1,6 +1,6 @@ --- source: crates/ruff_python_formatter/tests/fixtures.rs -input_file: crates/ruff_python_formatter/resources/test/fixtures/black/simple_cases/fmtonoff5.py +input_file: crates/ruff_python_formatter/resources/test/fixtures/black/cases/fmtonoff5.py --- ## Input diff --git a/crates/ruff_python_formatter/tests/snapshots/black_compatibility@simple_cases__fmtpass_imports.py.snap b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__fmtpass_imports.py.snap similarity index 96% rename from crates/ruff_python_formatter/tests/snapshots/black_compatibility@simple_cases__fmtpass_imports.py.snap rename to crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__fmtpass_imports.py.snap index 8dceca43bc..6a8042f10d 100644 --- a/crates/ruff_python_formatter/tests/snapshots/black_compatibility@simple_cases__fmtpass_imports.py.snap +++ b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__fmtpass_imports.py.snap @@ -1,6 +1,6 @@ --- source: crates/ruff_python_formatter/tests/fixtures.rs -input_file: crates/ruff_python_formatter/resources/test/fixtures/black/simple_cases/fmtpass_imports.py +input_file: crates/ruff_python_formatter/resources/test/fixtures/black/cases/fmtpass_imports.py --- ## Input diff --git a/crates/ruff_python_formatter/tests/snapshots/black_compatibility@simple_cases__fmtskip5.py.snap b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__fmtskip5.py.snap similarity index 96% rename from crates/ruff_python_formatter/tests/snapshots/black_compatibility@simple_cases__fmtskip5.py.snap rename to crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__fmtskip5.py.snap index 597ccdeeba..325b315501 100644 --- a/crates/ruff_python_formatter/tests/snapshots/black_compatibility@simple_cases__fmtskip5.py.snap +++ b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__fmtskip5.py.snap @@ -1,6 +1,6 @@ --- source: crates/ruff_python_formatter/tests/fixtures.rs -input_file: crates/ruff_python_formatter/resources/test/fixtures/black/simple_cases/fmtskip5.py +input_file: crates/ruff_python_formatter/resources/test/fixtures/black/cases/fmtskip5.py --- ## Input diff --git a/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__funcdef_return_type_trailing_comma.py.snap b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__funcdef_return_type_trailing_comma.py.snap new file mode 100644 index 0000000000..a10744872c --- /dev/null +++ b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__funcdef_return_type_trailing_comma.py.snap @@ -0,0 +1,608 @@ +--- +source: crates/ruff_python_formatter/tests/fixtures.rs +input_file: crates/ruff_python_formatter/resources/test/fixtures/black/cases/funcdef_return_type_trailing_comma.py +--- +## Input + +```python +# normal, short, function definition +def foo(a, b) -> tuple[int, float]: ... + + +# normal, short, function definition w/o return type +def foo(a, b): ... + + +# no splitting +def foo(a: A, b: B) -> list[p, q]: + pass + + +# magic trailing comma in param list +def foo(a, b,): ... + + +# magic trailing comma in nested params in param list +def foo(a, b: tuple[int, float,]): ... + + +# magic trailing comma in return type, no params +def a() -> tuple[ + a, + b, +]: ... + + +# magic trailing comma in return type, params +def foo(a: A, b: B) -> list[ + p, + q, +]: + pass + + +# magic trailing comma in param list and in return type +def foo( + a: a, + b: b, +) -> list[ + a, + a, +]: + pass + + +# long function definition, param list is longer +def aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa( + bbbbbbbbbbbbbbbbbb, +) -> cccccccccccccccccccccccccccccc: ... + + +# long function definition, return type is longer +# this should maybe split on rhs? +def aaaaaaaaaaaaaaaaa(bbbbbbbbbbbbbbbbbb) -> list[ + Ccccccccccccccccccccccccccccccccccccccccccccccccccc, Dddddd +]: ... + + +# long return type, no param list +def foo() -> list[ + Loooooooooooooooooooooooooooooooooooong, + Loooooooooooooooooooong, + Looooooooooooong, +]: ... + + +# long function name, no param list, no return value +def thiiiiiiiiiiiiiiiiiis_iiiiiiiiiiiiiiiiiiiiiiiiiiiiiis_veeeeeeeeeeeeeeeeeeeeeeery_looooooong(): + pass + + +# long function name, no param list +def thiiiiiiiiiiiiiiiiiis_iiiiiiiiiiiiiiiiiiiiiiiiiiiiiis_veeeeeeeeeeeeeeeeeeeeeeery_looooooong() -> ( + list[int, float] +): ... + + +# long function name, no return value +def thiiiiiiiiiiiiiiiiiis_iiiiiiiiiiiiiiiiiiiiiiiiiiiiiis_veeeeeeeeeeeeeeeeeeeeeeery_looooooong( + a, b +): ... + + +# unskippable type hint (??) +def foo(a) -> list[aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa, aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa]: # type: ignore + pass + + +def foo(a) -> list[ + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa, aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +]: # abpedeifnore + pass + +def foo(a, b: list[Bad],): ... # type: ignore + +# don't lose any comments (no magic) +def foo( # 1 + a, # 2 + b) -> list[ # 3 + a, # 4 + b]: # 5 + ... # 6 + + +# don't lose any comments (param list magic) +def foo( # 1 + a, # 2 + b,) -> list[ # 3 + a, # 4 + b]: # 5 + ... # 6 + + +# don't lose any comments (return type magic) +def foo( # 1 + a, # 2 + b) -> list[ # 3 + a, # 4 + b,]: # 5 + ... # 6 + + +# don't lose any comments (both magic) +def foo( # 1 + a, # 2 + b,) -> list[ # 3 + a, # 4 + b,]: # 5 + ... # 6 + +# real life example +def SimplePyFn( + context: hl.GeneratorContext, + buffer_input: Buffer[UInt8, 2], + func_input: Buffer[Int32, 2], + float_arg: Scalar[Float32], + offset: int = 0, +) -> tuple[ + Buffer[UInt8, 2], + Buffer[UInt8, 2], +]: ... +``` + +## Black Differences + +```diff +--- Black ++++ Ruff +@@ -29,14 +29,18 @@ + + + # magic trailing comma in return type, no params +-def a() -> tuple[ +- a, +- b, +-]: ... ++def a() -> ( ++ tuple[ ++ a, ++ b, ++ ] ++): ... + + + # magic trailing comma in return type, params +-def foo(a: A, b: B) -> list[ ++def foo( ++ a: A, b: B ++) -> list[ + p, + q, + ]: +@@ -63,16 +67,18 @@ + # long function definition, return type is longer + # this should maybe split on rhs? + def aaaaaaaaaaaaaaaaa( +- bbbbbbbbbbbbbbbbbb, ++ bbbbbbbbbbbbbbbbbb + ) -> list[Ccccccccccccccccccccccccccccccccccccccccccccccccccc, Dddddd]: ... + + + # long return type, no param list +-def foo() -> list[ +- Loooooooooooooooooooooooooooooooooooong, +- Loooooooooooooooooooong, +- Looooooooooooong, +-]: ... ++def foo() -> ( ++ list[ ++ Loooooooooooooooooooooooooooooooooooong, ++ Loooooooooooooooooooong, ++ Looooooooooooong, ++ ] ++): ... + + + # long function name, no param list, no return value +@@ -93,12 +99,16 @@ + + + # unskippable type hint (??) +-def foo(a) -> list[aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa, aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa]: # type: ignore ++def foo( ++ a ++) -> list[ ++ aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa, aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa ++]: # type: ignore + pass + + + def foo( +- a, ++ a + ) -> list[ + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa, aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa + ]: # abpedeifnore +@@ -112,7 +122,13 @@ + + + # don't lose any comments (no magic) +-def foo(a, b) -> list[a, b]: # 1 # 2 # 3 # 4 # 5 ++def foo( # 1 ++ a, # 2 ++ b, ++) -> list[ # 3 ++ a, # 4 ++ b, ++]: # 5 + ... # 6 + + +@@ -120,12 +136,18 @@ + def foo( # 1 + a, # 2 + b, +-) -> list[a, b]: # 3 # 4 # 5 ++) -> list[ # 3 ++ a, # 4 ++ b, ++]: # 5 + ... # 6 + + + # don't lose any comments (return type magic) +-def foo(a, b) -> list[ # 1 # 2 # 3 ++def foo( # 1 ++ a, # 2 ++ b, ++) -> list[ # 3 + a, # 4 + b, + ]: # 5 +``` + +## Ruff Output + +```python +# normal, short, function definition +def foo(a, b) -> tuple[int, float]: ... + + +# normal, short, function definition w/o return type +def foo(a, b): ... + + +# no splitting +def foo(a: A, b: B) -> list[p, q]: + pass + + +# magic trailing comma in param list +def foo( + a, + b, +): ... + + +# magic trailing comma in nested params in param list +def foo( + a, + b: tuple[ + int, + float, + ], +): ... + + +# magic trailing comma in return type, no params +def a() -> ( + tuple[ + a, + b, + ] +): ... + + +# magic trailing comma in return type, params +def foo( + a: A, b: B +) -> list[ + p, + q, +]: + pass + + +# magic trailing comma in param list and in return type +def foo( + a: a, + b: b, +) -> list[ + a, + a, +]: + pass + + +# long function definition, param list is longer +def aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa( + bbbbbbbbbbbbbbbbbb, +) -> cccccccccccccccccccccccccccccc: ... + + +# long function definition, return type is longer +# this should maybe split on rhs? +def aaaaaaaaaaaaaaaaa( + bbbbbbbbbbbbbbbbbb +) -> list[Ccccccccccccccccccccccccccccccccccccccccccccccccccc, Dddddd]: ... + + +# long return type, no param list +def foo() -> ( + list[ + Loooooooooooooooooooooooooooooooooooong, + Loooooooooooooooooooong, + Looooooooooooong, + ] +): ... + + +# long function name, no param list, no return value +def thiiiiiiiiiiiiiiiiiis_iiiiiiiiiiiiiiiiiiiiiiiiiiiiiis_veeeeeeeeeeeeeeeeeeeeeeery_looooooong(): + pass + + +# long function name, no param list +def thiiiiiiiiiiiiiiiiiis_iiiiiiiiiiiiiiiiiiiiiiiiiiiiiis_veeeeeeeeeeeeeeeeeeeeeeery_looooooong() -> ( + list[int, float] +): ... + + +# long function name, no return value +def thiiiiiiiiiiiiiiiiiis_iiiiiiiiiiiiiiiiiiiiiiiiiiiiiis_veeeeeeeeeeeeeeeeeeeeeeery_looooooong( + a, b +): ... + + +# unskippable type hint (??) +def foo( + a +) -> list[ + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa, aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +]: # type: ignore + pass + + +def foo( + a +) -> list[ + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa, aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +]: # abpedeifnore + pass + + +def foo( + a, + b: list[Bad], +): ... # type: ignore + + +# don't lose any comments (no magic) +def foo( # 1 + a, # 2 + b, +) -> list[ # 3 + a, # 4 + b, +]: # 5 + ... # 6 + + +# don't lose any comments (param list magic) +def foo( # 1 + a, # 2 + b, +) -> list[ # 3 + a, # 4 + b, +]: # 5 + ... # 6 + + +# don't lose any comments (return type magic) +def foo( # 1 + a, # 2 + b, +) -> list[ # 3 + a, # 4 + b, +]: # 5 + ... # 6 + + +# don't lose any comments (both magic) +def foo( # 1 + a, # 2 + b, +) -> list[ # 3 + a, # 4 + b, +]: # 5 + ... # 6 + + +# real life example +def SimplePyFn( + context: hl.GeneratorContext, + buffer_input: Buffer[UInt8, 2], + func_input: Buffer[Int32, 2], + float_arg: Scalar[Float32], + offset: int = 0, +) -> tuple[ + Buffer[UInt8, 2], + Buffer[UInt8, 2], +]: ... +``` + +## Black Output + +```python +# normal, short, function definition +def foo(a, b) -> tuple[int, float]: ... + + +# normal, short, function definition w/o return type +def foo(a, b): ... + + +# no splitting +def foo(a: A, b: B) -> list[p, q]: + pass + + +# magic trailing comma in param list +def foo( + a, + b, +): ... + + +# magic trailing comma in nested params in param list +def foo( + a, + b: tuple[ + int, + float, + ], +): ... + + +# magic trailing comma in return type, no params +def a() -> tuple[ + a, + b, +]: ... + + +# magic trailing comma in return type, params +def foo(a: A, b: B) -> list[ + p, + q, +]: + pass + + +# magic trailing comma in param list and in return type +def foo( + a: a, + b: b, +) -> list[ + a, + a, +]: + pass + + +# long function definition, param list is longer +def aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa( + bbbbbbbbbbbbbbbbbb, +) -> cccccccccccccccccccccccccccccc: ... + + +# long function definition, return type is longer +# this should maybe split on rhs? +def aaaaaaaaaaaaaaaaa( + bbbbbbbbbbbbbbbbbb, +) -> list[Ccccccccccccccccccccccccccccccccccccccccccccccccccc, Dddddd]: ... + + +# long return type, no param list +def foo() -> list[ + Loooooooooooooooooooooooooooooooooooong, + Loooooooooooooooooooong, + Looooooooooooong, +]: ... + + +# long function name, no param list, no return value +def thiiiiiiiiiiiiiiiiiis_iiiiiiiiiiiiiiiiiiiiiiiiiiiiiis_veeeeeeeeeeeeeeeeeeeeeeery_looooooong(): + pass + + +# long function name, no param list +def thiiiiiiiiiiiiiiiiiis_iiiiiiiiiiiiiiiiiiiiiiiiiiiiiis_veeeeeeeeeeeeeeeeeeeeeeery_looooooong() -> ( + list[int, float] +): ... + + +# long function name, no return value +def thiiiiiiiiiiiiiiiiiis_iiiiiiiiiiiiiiiiiiiiiiiiiiiiiis_veeeeeeeeeeeeeeeeeeeeeeery_looooooong( + a, b +): ... + + +# unskippable type hint (??) +def foo(a) -> list[aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa, aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa]: # type: ignore + pass + + +def foo( + a, +) -> list[ + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa, aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +]: # abpedeifnore + pass + + +def foo( + a, + b: list[Bad], +): ... # type: ignore + + +# don't lose any comments (no magic) +def foo(a, b) -> list[a, b]: # 1 # 2 # 3 # 4 # 5 + ... # 6 + + +# don't lose any comments (param list magic) +def foo( # 1 + a, # 2 + b, +) -> list[a, b]: # 3 # 4 # 5 + ... # 6 + + +# don't lose any comments (return type magic) +def foo(a, b) -> list[ # 1 # 2 # 3 + a, # 4 + b, +]: # 5 + ... # 6 + + +# don't lose any comments (both magic) +def foo( # 1 + a, # 2 + b, +) -> list[ # 3 + a, # 4 + b, +]: # 5 + ... # 6 + + +# real life example +def SimplePyFn( + context: hl.GeneratorContext, + buffer_input: Buffer[UInt8, 2], + func_input: Buffer[Int32, 2], + float_arg: Scalar[Float32], + offset: int = 0, +) -> tuple[ + Buffer[UInt8, 2], + Buffer[UInt8, 2], +]: ... +``` + + diff --git a/crates/ruff_python_formatter/tests/snapshots/black_compatibility@simple_cases__function.py.snap b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__function.py.snap similarity index 99% rename from crates/ruff_python_formatter/tests/snapshots/black_compatibility@simple_cases__function.py.snap rename to crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__function.py.snap index 317d6c6a18..df6fda9a85 100644 --- a/crates/ruff_python_formatter/tests/snapshots/black_compatibility@simple_cases__function.py.snap +++ b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__function.py.snap @@ -1,6 +1,6 @@ --- source: crates/ruff_python_formatter/tests/fixtures.rs -input_file: crates/ruff_python_formatter/resources/test/fixtures/black/simple_cases/function.py +input_file: crates/ruff_python_formatter/resources/test/fixtures/black/cases/function.py --- ## Input diff --git a/crates/ruff_python_formatter/tests/snapshots/black_compatibility@simple_cases__function2.py.snap b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__function2.py.snap similarity index 99% rename from crates/ruff_python_formatter/tests/snapshots/black_compatibility@simple_cases__function2.py.snap rename to crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__function2.py.snap index 2fc0696b9b..f85dbd5fef 100644 --- a/crates/ruff_python_formatter/tests/snapshots/black_compatibility@simple_cases__function2.py.snap +++ b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__function2.py.snap @@ -1,6 +1,6 @@ --- source: crates/ruff_python_formatter/tests/fixtures.rs -input_file: crates/ruff_python_formatter/resources/test/fixtures/black/simple_cases/function2.py +input_file: crates/ruff_python_formatter/resources/test/fixtures/black/cases/function2.py --- ## Input diff --git a/crates/ruff_python_formatter/tests/snapshots/black_compatibility@simple_cases__ignore_pyi.py.snap b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__ignore_pyi.pyi.snap similarity index 71% rename from crates/ruff_python_formatter/tests/snapshots/black_compatibility@simple_cases__ignore_pyi.py.snap rename to crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__ignore_pyi.pyi.snap index 9027c47633..ed9ee11309 100644 --- a/crates/ruff_python_formatter/tests/snapshots/black_compatibility@simple_cases__ignore_pyi.py.snap +++ b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__ignore_pyi.pyi.snap @@ -1,6 +1,6 @@ --- source: crates/ruff_python_formatter/tests/fixtures.rs -input_file: crates/ruff_python_formatter/resources/test/fixtures/black/simple_cases/ignore_pyi.py +input_file: crates/ruff_python_formatter/resources/test/fixtures/black/cases/ignore_pyi.pyi --- ## Input @@ -22,9 +22,10 @@ def g(): # hi ... -def h(): - ... - # bye +# FIXME(#8905): Uncomment, leads to unstable formatting +# def h(): +# ... +# # bye ``` ## Black Differences @@ -32,34 +33,25 @@ def h(): ```diff --- Black +++ Ruff -@@ -1,18 +1,25 @@ - def f(): # type: ignore - ... +@@ -3,7 +3,6 @@ -+ class x: # some comment ... +- + class y: ... # comment --class y: ... # comment - -+class y: -+ ... # comment -+ -+ # whitespace doesn't matter (note the next line has a trailing space and tab) --class z: ... -+class z: -+ ... -+ - - def g(): +@@ -13,6 +12,7 @@ # hi ... -+ - def h(): - ... - # bye +-def h(): +- ... +- # bye ++# FIXME(#8905): Uncomment, leads to unstable formatting ++# def h(): ++# ... ++# # bye ``` ## Ruff Output @@ -68,28 +60,21 @@ def h(): def f(): # type: ignore ... - class x: # some comment ... - - -class y: - ... # comment - +class y: ... # comment # whitespace doesn't matter (note the next line has a trailing space and tab) -class z: - ... - +class z: ... def g(): # hi ... - -def h(): - ... - # bye +# FIXME(#8905): Uncomment, leads to unstable formatting +# def h(): +# ... +# # bye ``` ## Black Output diff --git a/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__line_ranges_basic.py.snap b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__line_ranges_basic.py.snap new file mode 100644 index 0000000000..a37d5fec5a --- /dev/null +++ b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__line_ranges_basic.py.snap @@ -0,0 +1,317 @@ +--- +source: crates/ruff_python_formatter/tests/fixtures.rs +input_file: crates/ruff_python_formatter/resources/test/fixtures/black/cases/line_ranges_basic.py +--- +## Input + +```python +# NOTE: If you need to modify this file, pay special attention to the --line-ranges= +# flag above as it's formatting specifically these lines. +def foo1(parameter_1, parameter_2, parameter_3, parameter_4, parameter_5, parameter_6, parameter_7): pass +def foo2(parameter_1, parameter_2, parameter_3, parameter_4, parameter_5, parameter_6, parameter_7): pass +def foo3(parameter_1, parameter_2, parameter_3, parameter_4, parameter_5, parameter_6, parameter_7): pass +def foo4(parameter_1, parameter_2, parameter_3, parameter_4, parameter_5, parameter_6, parameter_7): pass + +# Adding some unformated code covering a wide range of syntaxes. + +if True: + # Incorrectly indented prefix comments. + pass + +import typing +from typing import ( + Any , + ) +class MyClass( object): # Trailing comment with extra leading space. + #NOTE: The following indentation is incorrect: + @decor( 1 * 3 ) + def my_func( arg): + pass + +try: # Trailing comment with extra leading space. + for i in range(10): # Trailing comment with extra leading space. + while condition: + if something: + then_something( ) + elif something_else: + then_something_else( ) +except ValueError as e: + unformatted( ) +finally: + unformatted( ) + +async def test_async_unformatted( ): # Trailing comment with extra leading space. + async for i in some_iter( unformatted ): # Trailing comment with extra leading space. + await asyncio.sleep( 1 ) + async with some_context( unformatted ): + print( "unformatted" ) +``` + +## Black Differences + +```diff +--- Black ++++ Ruff +@@ -1,7 +1,17 @@ +-# flags: --line-ranges=5-6 + # NOTE: If you need to modify this file, pay special attention to the --line-ranges= + # flag above as it's formatting specifically these lines. +-def foo1(parameter_1, parameter_2, parameter_3, parameter_4, parameter_5, parameter_6, parameter_7): pass ++def foo1( ++ parameter_1, ++ parameter_2, ++ parameter_3, ++ parameter_4, ++ parameter_5, ++ parameter_6, ++ parameter_7, ++): ++ pass ++ ++ + def foo2( + parameter_1, + parameter_2, +@@ -26,38 +36,52 @@ + pass + + +-def foo4(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 unformated code covering a wide range of syntaxes. + + if True: +- # Incorrectly indented prefix comments. +- pass ++ # Incorrectly indented prefix comments. ++ pass ++ ++import typing ++from typing import ( ++ Any, ++) ++ ++ ++class MyClass(object): # Trailing comment with extra leading space. ++ # NOTE: The following indentation is incorrect: ++ @decor(1 * 3) ++ def my_func(arg): ++ pass + +-import typing +-from typing import ( +- Any , +- ) +-class MyClass( object): # Trailing comment with extra leading space. +- #NOTE: The following indentation is incorrect: +- @decor( 1 * 3 ) +- def my_func( arg): +- pass + +-try: # Trailing comment with extra leading space. +- for i in range(10): # Trailing comment with extra leading space. +- while condition: +- if something: +- then_something( ) +- elif something_else: +- then_something_else( ) +-except ValueError as e: +- unformatted( ) ++try: # Trailing comment with extra leading space. ++ for i in range(10): # Trailing comment with extra leading space. ++ while condition: ++ if something: ++ then_something() ++ elif something_else: ++ then_something_else() ++except ValueError as e: ++ unformatted() + finally: +- unformatted( ) ++ unformatted() ++ + +-async def test_async_unformatted( ): # Trailing comment with extra leading space. +- async for i in some_iter( unformatted ): # Trailing comment with extra leading space. +- await asyncio.sleep( 1 ) +- async with some_context( unformatted ): +- print( "unformatted" ) ++async def test_async_unformatted(): # Trailing comment with extra leading space. ++ async for i in some_iter(unformatted): # Trailing comment with extra leading space. ++ await asyncio.sleep(1) ++ async with some_context(unformatted): ++ print("unformatted") +``` + +## Ruff Output + +```python +# NOTE: If you need to modify this file, pay special attention to the --line-ranges= +# flag above as it's formatting specifically these lines. +def foo1( + parameter_1, + parameter_2, + parameter_3, + parameter_4, + parameter_5, + parameter_6, + parameter_7, +): + pass + + +def foo2( + parameter_1, + parameter_2, + parameter_3, + parameter_4, + parameter_5, + parameter_6, + parameter_7, +): + pass + + +def foo3( + parameter_1, + parameter_2, + parameter_3, + parameter_4, + parameter_5, + parameter_6, + parameter_7, +): + pass + + +def foo4( + parameter_1, + parameter_2, + parameter_3, + parameter_4, + parameter_5, + parameter_6, + parameter_7, +): + pass + + +# Adding some unformated code covering a wide range of syntaxes. + +if True: + # Incorrectly indented prefix comments. + pass + +import typing +from typing import ( + Any, +) + + +class MyClass(object): # Trailing comment with extra leading space. + # NOTE: The following indentation is incorrect: + @decor(1 * 3) + def my_func(arg): + pass + + +try: # Trailing comment with extra leading space. + for i in range(10): # Trailing comment with extra leading space. + while condition: + if something: + then_something() + elif something_else: + then_something_else() +except ValueError as e: + unformatted() +finally: + unformatted() + + +async def test_async_unformatted(): # Trailing comment with extra leading space. + async for i in some_iter(unformatted): # Trailing comment with extra leading space. + await asyncio.sleep(1) + async with some_context(unformatted): + print("unformatted") +``` + +## Black Output + +```python +# flags: --line-ranges=5-6 +# NOTE: If you need to modify this file, pay special attention to the --line-ranges= +# flag above as it's formatting specifically these lines. +def foo1(parameter_1, parameter_2, parameter_3, parameter_4, parameter_5, parameter_6, parameter_7): pass +def foo2( + parameter_1, + parameter_2, + parameter_3, + parameter_4, + parameter_5, + parameter_6, + parameter_7, +): + pass + + +def foo3( + parameter_1, + parameter_2, + parameter_3, + parameter_4, + parameter_5, + parameter_6, + parameter_7, +): + pass + + +def foo4(parameter_1, parameter_2, parameter_3, parameter_4, parameter_5, parameter_6, parameter_7): pass + +# Adding some unformated code covering a wide range of syntaxes. + +if True: + # Incorrectly indented prefix comments. + pass + +import typing +from typing import ( + Any , + ) +class MyClass( object): # Trailing comment with extra leading space. + #NOTE: The following indentation is incorrect: + @decor( 1 * 3 ) + def my_func( arg): + pass + +try: # Trailing comment with extra leading space. + for i in range(10): # Trailing comment with extra leading space. + while condition: + if something: + then_something( ) + elif something_else: + then_something_else( ) +except ValueError as e: + unformatted( ) +finally: + unformatted( ) + +async def test_async_unformatted( ): # Trailing comment with extra leading space. + async for i in some_iter( unformatted ): # Trailing comment with extra leading space. + await asyncio.sleep( 1 ) + async with some_context( unformatted ): + print( "unformatted" ) +``` + + diff --git a/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__line_ranges_diff_edge_case.py.snap b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__line_ranges_diff_edge_case.py.snap new file mode 100644 index 0000000000..ee8fb11d45 --- /dev/null +++ b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__line_ranges_diff_edge_case.py.snap @@ -0,0 +1,79 @@ +--- +source: crates/ruff_python_formatter/tests/fixtures.rs +input_file: crates/ruff_python_formatter/resources/test/fixtures/black/cases/line_ranges_diff_edge_case.py +--- +## Input + +```python +# NOTE: If you need to modify this file, pay special attention to the --line-ranges= +# flag above as it's formatting specifically these lines. + +# Reproducible example for https://github.com/psf/black/issues/4033. +# This can be fixed in the future if we use a better diffing algorithm, or make Black +# perform formatting in a single pass. + +print ( "format me" ) +print ( "format me" ) +print ( "format me" ) +print ( "format me" ) +print ( "format me" ) +``` + +## Black Differences + +```diff +--- Black ++++ Ruff +@@ -1,4 +1,3 @@ +-# flags: --line-ranges=10-11 + # NOTE: If you need to modify this file, pay special attention to the --line-ranges= + # flag above as it's formatting specifically these lines. + +@@ -6,8 +5,8 @@ + # This can be fixed in the future if we use a better diffing algorithm, or make Black + # perform formatting in a single pass. + +-print ( "format me" ) + print("format me") + print("format me") + print("format me") + print("format me") ++print("format me") +``` + +## Ruff Output + +```python +# NOTE: If you need to modify this file, pay special attention to the --line-ranges= +# flag above as it's formatting specifically these lines. + +# Reproducible example for https://github.com/psf/black/issues/4033. +# This can be fixed in the future if we use a better diffing algorithm, or make Black +# perform formatting in a single pass. + +print("format me") +print("format me") +print("format me") +print("format me") +print("format me") +``` + +## Black Output + +```python +# flags: --line-ranges=10-11 +# NOTE: If you need to modify this file, pay special attention to the --line-ranges= +# flag above as it's formatting specifically these lines. + +# Reproducible example for https://github.com/psf/black/issues/4033. +# This can be fixed in the future if we use a better diffing algorithm, or make Black +# perform formatting in a single pass. + +print ( "format me" ) +print("format me") +print("format me") +print("format me") +print("format me") +``` + + diff --git a/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__line_ranges_fmt_off.py.snap b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__line_ranges_fmt_off.py.snap new file mode 100644 index 0000000000..119237fb5d --- /dev/null +++ b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__line_ranges_fmt_off.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/line_ranges_fmt_off.py +--- +## Input + +```python +# NOTE: If you need to modify this file, pay special attention to the --line-ranges= +# flag above as it's formatting specifically these lines. + +# fmt: off +import os +def myfunc( ): # Intentionally unformatted. + pass +# fmt: on + + +def myfunc( ): # This will not be reformatted. + print( {"also won't be reformatted"} ) +# fmt: off +def myfunc( ): # This will not be reformatted. + print( {"also won't be reformatted"} ) +def myfunc( ): # This will not be reformatted. + print( {"also won't be reformatted"} ) +# fmt: on + + +def myfunc( ): # This will be reformatted. + print( {"this will be reformatted"} ) +``` + +## Black Differences + +```diff +--- Black ++++ Ruff +@@ -1,4 +1,3 @@ +-# flags: --line-ranges=7-7 --line-ranges=17-23 + # NOTE: If you need to modify this file, pay special attention to the --line-ranges= + # flag above as it's formatting specifically these lines. + +@@ -9,8 +8,10 @@ + # fmt: on + + +-def myfunc( ): # This will not be reformatted. +- print( {"also won't be reformatted"} ) ++def myfunc(): # This will not be reformatted. ++ print({"also won't be reformatted"}) ++ ++ + # fmt: off + def myfunc( ): # This will not be reformatted. + print( {"also won't be reformatted"} ) +``` + +## Ruff Output + +```python +# NOTE: If you need to modify this file, pay special attention to the --line-ranges= +# flag above as it's formatting specifically these lines. + +# fmt: off +import os +def myfunc( ): # Intentionally unformatted. + pass +# fmt: on + + +def myfunc(): # This will not be reformatted. + print({"also won't be reformatted"}) + + +# fmt: off +def myfunc( ): # This will not be reformatted. + print( {"also won't be reformatted"} ) +def myfunc( ): # This will not be reformatted. + print( {"also won't be reformatted"} ) +# fmt: on + + +def myfunc(): # This will be reformatted. + print({"this will be reformatted"}) +``` + +## Black Output + +```python +# flags: --line-ranges=7-7 --line-ranges=17-23 +# NOTE: If you need to modify this file, pay special attention to the --line-ranges= +# flag above as it's formatting specifically these lines. + +# fmt: off +import os +def myfunc( ): # Intentionally unformatted. + pass +# fmt: on + + +def myfunc( ): # This will not be reformatted. + print( {"also won't be reformatted"} ) +# fmt: off +def myfunc( ): # This will not be reformatted. + print( {"also won't be reformatted"} ) +def myfunc( ): # This will not be reformatted. + print( {"also won't be reformatted"} ) +# fmt: on + + +def myfunc(): # This will be reformatted. + print({"this will be reformatted"}) +``` + + diff --git a/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__line_ranges_fmt_off_decorator.py.snap b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__line_ranges_fmt_off_decorator.py.snap new file mode 100644 index 0000000000..4ac42448f3 --- /dev/null +++ b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__line_ranges_fmt_off_decorator.py.snap @@ -0,0 +1,74 @@ +--- +source: crates/ruff_python_formatter/tests/fixtures.rs +input_file: crates/ruff_python_formatter/resources/test/fixtures/black/cases/line_ranges_fmt_off_decorator.py +--- +## Input + +```python +# NOTE: If you need to modify this file, pay special attention to the --line-ranges= +# flag above as it's formatting specifically these lines. + +# Regression test for an edge case involving decorators and fmt: off/on. +class MyClass: + + # fmt: off + @decorator ( ) + # fmt: on + def method(): + print ( "str" ) +``` + +## Black Differences + +```diff +--- Black ++++ Ruff +@@ -1,12 +1,10 @@ +-# flags: --line-ranges=12-12 + # NOTE: If you need to modify this file, pay special attention to the --line-ranges= + # flag above as it's formatting specifically these lines. + + # Regression test for an edge case involving decorators and fmt: off/on. + class MyClass: +- + # fmt: off + @decorator ( ) + # fmt: on + def method(): +- print("str") ++ print ( "str" ) +``` + +## Ruff Output + +```python +# NOTE: If you need to modify this file, pay special attention to the --line-ranges= +# flag above as it's formatting specifically these lines. + +# Regression test for an edge case involving decorators and fmt: off/on. +class MyClass: + # fmt: off + @decorator ( ) + # fmt: on + def method(): + print ( "str" ) +``` + +## Black Output + +```python +# flags: --line-ranges=12-12 +# NOTE: If you need to modify this file, pay special attention to the --line-ranges= +# flag above as it's formatting specifically these lines. + +# Regression test for an edge case involving decorators and fmt: off/on. +class MyClass: + + # fmt: off + @decorator ( ) + # fmt: on + def method(): + print("str") +``` + + diff --git a/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__line_ranges_fmt_off_overlap.py.snap b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__line_ranges_fmt_off_overlap.py.snap new file mode 100644 index 0000000000..5130df3f7c --- /dev/null +++ b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__line_ranges_fmt_off_overlap.py.snap @@ -0,0 +1,93 @@ +--- +source: crates/ruff_python_formatter/tests/fixtures.rs +input_file: crates/ruff_python_formatter/resources/test/fixtures/black/cases/line_ranges_fmt_off_overlap.py +--- +## Input + +```python +# NOTE: If you need to modify this file, pay special attention to the --line-ranges= +# flag above as it's formatting specifically these lines. + + +def myfunc( ): # This will not be reformatted. + print( {"also won't be reformatted"} ) +# fmt: off +def myfunc( ): # This will not be reformatted. + print( {"also won't be reformatted"} ) +def myfunc( ): # This will not be reformatted. + print( {"also won't be reformatted"} ) +# fmt: on + + +def myfunc( ): # This will be reformatted. + print( {"this will be reformatted"} ) +``` + +## Black Differences + +```diff +--- Black ++++ Ruff +@@ -1,10 +1,11 @@ +-# flags: --line-ranges=11-17 + # NOTE: If you need to modify this file, pay special attention to the --line-ranges= + # flag above as it's formatting specifically these lines. + + +-def myfunc( ): # This will not be reformatted. +- print( {"also won't be reformatted"} ) ++def myfunc(): # This will not be reformatted. ++ print({"also won't be reformatted"}) ++ ++ + # fmt: off + def myfunc( ): # This will not be reformatted. + print( {"also won't be reformatted"} ) +``` + +## Ruff Output + +```python +# NOTE: If you need to modify this file, pay special attention to the --line-ranges= +# flag above as it's formatting specifically these lines. + + +def myfunc(): # This will not be reformatted. + print({"also won't be reformatted"}) + + +# fmt: off +def myfunc( ): # This will not be reformatted. + print( {"also won't be reformatted"} ) +def myfunc( ): # This will not be reformatted. + print( {"also won't be reformatted"} ) +# fmt: on + + +def myfunc(): # This will be reformatted. + print({"this will be reformatted"}) +``` + +## Black Output + +```python +# flags: --line-ranges=11-17 +# NOTE: If you need to modify this file, pay special attention to the --line-ranges= +# flag above as it's formatting specifically these lines. + + +def myfunc( ): # This will not be reformatted. + print( {"also won't be reformatted"} ) +# fmt: off +def myfunc( ): # This will not be reformatted. + print( {"also won't be reformatted"} ) +def myfunc( ): # This will not be reformatted. + print( {"also won't be reformatted"} ) +# fmt: on + + +def myfunc(): # This will be reformatted. + print({"this will be reformatted"}) +``` + + diff --git a/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__line_ranges_indentation.py.snap b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__line_ranges_indentation.py.snap new file mode 100644 index 0000000000..53fac25155 --- /dev/null +++ b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__line_ranges_indentation.py.snap @@ -0,0 +1,72 @@ +--- +source: crates/ruff_python_formatter/tests/fixtures.rs +input_file: crates/ruff_python_formatter/resources/test/fixtures/black/cases/line_ranges_indentation.py +--- +## Input + +```python +# NOTE: If you need to modify this file, pay special attention to the --line-ranges= +# flag above as it's formatting specifically these lines. +if cond1: + print("first") + if cond2: + print("second") + else: + print("else") + +if another_cond: + print("will not be changed") +``` + +## Black Differences + +```diff +--- Black ++++ Ruff +@@ -1,4 +1,3 @@ +-# flags: --line-ranges=5-5 + # NOTE: If you need to modify this file, pay special attention to the --line-ranges= + # flag above as it's formatting specifically these lines. + if cond1: +@@ -9,4 +8,4 @@ + print("else") + + if another_cond: +- print("will not be changed") ++ print("will not be changed") +``` + +## Ruff Output + +```python +# NOTE: If you need to modify this file, pay special attention to the --line-ranges= +# flag above as it's formatting specifically these lines. +if cond1: + print("first") + if cond2: + print("second") + else: + print("else") + +if another_cond: + print("will not be changed") +``` + +## Black Output + +```python +# flags: --line-ranges=5-5 +# NOTE: If you need to modify this file, pay special attention to the --line-ranges= +# flag above as it's formatting specifically these lines. +if cond1: + print("first") + if cond2: + print("second") + else: + print("else") + +if another_cond: + print("will not be changed") +``` + + diff --git a/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__line_ranges_two_passes.py.snap b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__line_ranges_two_passes.py.snap new file mode 100644 index 0000000000..b12db81720 --- /dev/null +++ b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__line_ranges_two_passes.py.snap @@ -0,0 +1,77 @@ +--- +source: crates/ruff_python_formatter/tests/fixtures.rs +input_file: crates/ruff_python_formatter/resources/test/fixtures/black/cases/line_ranges_two_passes.py +--- +## Input + +```python +# NOTE: If you need to modify this file, pay special attention to the --line-ranges= +# flag above as it's formatting specifically these lines. + +# This is a specific case for Black's two-pass formatting behavior in `format_str`. +# The second pass must respect the line ranges before the first pass. + + +def restrict_to_this_line(arg1, + arg2, + arg3): + print ( "This should not be formatted." ) + print ( "Note that in the second pass, the original line range 9-11 will cover these print lines.") +``` + +## Black Differences + +```diff +--- Black ++++ Ruff +@@ -1,4 +1,3 @@ +-# flags: --line-ranges=9-11 + # NOTE: If you need to modify this file, pay special attention to the --line-ranges= + # flag above as it's formatting specifically these lines. + +@@ -7,5 +6,7 @@ + + + def restrict_to_this_line(arg1, arg2, arg3): +- print ( "This should not be formatted." ) +- print ( "Note that in the second pass, the original line range 9-11 will cover these print lines.") ++ print("This should not be formatted.") ++ print( ++ "Note that in the second pass, the original line range 9-11 will cover these print lines." ++ ) +``` + +## Ruff Output + +```python +# NOTE: If you need to modify this file, pay special attention to the --line-ranges= +# flag above as it's formatting specifically these lines. + +# This is a specific case for Black's two-pass formatting behavior in `format_str`. +# The second pass must respect the line ranges before the first pass. + + +def restrict_to_this_line(arg1, arg2, arg3): + print("This should not be formatted.") + print( + "Note that in the second pass, the original line range 9-11 will cover these print lines." + ) +``` + +## Black Output + +```python +# flags: --line-ranges=9-11 +# NOTE: If you need to modify this file, pay special attention to the --line-ranges= +# flag above as it's formatting specifically these lines. + +# This is a specific case for Black's two-pass formatting behavior in `format_str`. +# The second pass must respect the line ranges before the first pass. + + +def restrict_to_this_line(arg1, arg2, arg3): + print ( "This should not be formatted." ) + print ( "Note that in the second pass, the original line range 9-11 will cover these print lines.") +``` + + diff --git a/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__line_ranges_unwrapping.py.snap b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__line_ranges_unwrapping.py.snap new file mode 100644 index 0000000000..02466fbfa2 --- /dev/null +++ b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__line_ranges_unwrapping.py.snap @@ -0,0 +1,60 @@ +--- +source: crates/ruff_python_formatter/tests/fixtures.rs +input_file: crates/ruff_python_formatter/resources/test/fixtures/black/cases/line_ranges_unwrapping.py +--- +## Input + +```python +# NOTE: If you need to modify this file, pay special attention to the --line-ranges= +# flag above as it's formatting specifically these lines. +alist = [ + 1, 2 +] + +adict = { + "key" : "value" +} + +func_call ( + arg = value +) +``` + +## Black Differences + +```diff +--- Black ++++ Ruff +@@ -1,4 +1,3 @@ +-# flags: --line-ranges=5-5 --line-ranges=9-9 --line-ranges=13-13 + # NOTE: If you need to modify this file, pay special attention to the --line-ranges= + # flag above as it's formatting specifically these lines. + alist = [1, 2] +``` + +## Ruff Output + +```python +# NOTE: If you need to modify this file, pay special attention to the --line-ranges= +# flag above as it's formatting specifically these lines. +alist = [1, 2] + +adict = {"key": "value"} + +func_call(arg=value) +``` + +## Black Output + +```python +# flags: --line-ranges=5-5 --line-ranges=9-9 --line-ranges=13-13 +# NOTE: If you need to modify this file, pay special attention to the --line-ranges= +# flag above as it's formatting specifically these lines. +alist = [1, 2] + +adict = {"key": "value"} + +func_call(arg=value) +``` + + diff --git a/crates/ruff_python_formatter/tests/snapshots/black_compatibility@miscellaneous__long_strings_flag_disabled.py.snap b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__long_strings_flag_disabled.py.snap similarity index 99% rename from crates/ruff_python_formatter/tests/snapshots/black_compatibility@miscellaneous__long_strings_flag_disabled.py.snap rename to crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__long_strings_flag_disabled.py.snap index 554b9b0fb3..47c71cf9a2 100644 --- a/crates/ruff_python_formatter/tests/snapshots/black_compatibility@miscellaneous__long_strings_flag_disabled.py.snap +++ b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__long_strings_flag_disabled.py.snap @@ -1,6 +1,6 @@ --- source: crates/ruff_python_formatter/tests/fixtures.rs -input_file: crates/ruff_python_formatter/resources/test/fixtures/black/miscellaneous/long_strings_flag_disabled.py +input_file: crates/ruff_python_formatter/resources/test/fixtures/black/cases/long_strings_flag_disabled.py --- ## Input diff --git a/crates/ruff_python_formatter/tests/snapshots/black_compatibility@simple_cases__multiline_consecutive_open_parentheses_ignore.py.snap b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__multiline_consecutive_open_parentheses_ignore.py.snap similarity index 95% rename from crates/ruff_python_formatter/tests/snapshots/black_compatibility@simple_cases__multiline_consecutive_open_parentheses_ignore.py.snap rename to crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__multiline_consecutive_open_parentheses_ignore.py.snap index 66ce78e5b1..45a41abfe2 100644 --- a/crates/ruff_python_formatter/tests/snapshots/black_compatibility@simple_cases__multiline_consecutive_open_parentheses_ignore.py.snap +++ b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__multiline_consecutive_open_parentheses_ignore.py.snap @@ -1,6 +1,6 @@ --- source: crates/ruff_python_formatter/tests/fixtures.rs -input_file: crates/ruff_python_formatter/resources/test/fixtures/black/simple_cases/multiline_consecutive_open_parentheses_ignore.py +input_file: crates/ruff_python_formatter/resources/test/fixtures/black/cases/multiline_consecutive_open_parentheses_ignore.py --- ## Input diff --git a/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__nested_stub.pyi.snap b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__nested_stub.pyi.snap new file mode 100644 index 0000000000..9ebdd53453 --- /dev/null +++ b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__nested_stub.pyi.snap @@ -0,0 +1,126 @@ +--- +source: crates/ruff_python_formatter/tests/fixtures.rs +input_file: crates/ruff_python_formatter/resources/test/fixtures/black/cases/nested_stub.pyi +--- +## Input + +```python +import sys + +class Outer: + class InnerStub: ... + outer_attr_after_inner_stub: int + class Inner: + inner_attr: int + outer_attr: int + +if sys.version_info > (3, 7): + if sys.platform == "win32": + assignment = 1 + def function_definition(self): ... + def f1(self) -> str: ... + if sys.platform != "win32": + def function_definition(self): ... + assignment = 1 + def f2(self) -> str: ... +``` + +## Black Differences + +```diff +--- Black ++++ Ruff +@@ -1,7 +1,9 @@ + import sys + ++ + class Outer: + class InnerStub: ... ++ + outer_attr_after_inner_stub: int + + class Inner: +@@ -9,14 +11,19 @@ + + outer_attr: int + ++ + if sys.version_info > (3, 7): + if sys.platform == "win32": + assignment = 1 ++ + def function_definition(self): ... + + def f1(self) -> str: ... ++ + if sys.platform != "win32": ++ + def function_definition(self): ... ++ + assignment = 1 + + def f2(self) -> str: ... +``` + +## Ruff Output + +```python +import sys + + +class Outer: + class InnerStub: ... + + outer_attr_after_inner_stub: int + + class Inner: + inner_attr: int + + outer_attr: int + + +if sys.version_info > (3, 7): + if sys.platform == "win32": + assignment = 1 + + def function_definition(self): ... + + def f1(self) -> str: ... + + if sys.platform != "win32": + + def function_definition(self): ... + + assignment = 1 + + def f2(self) -> str: ... +``` + +## Black Output + +```python +import sys + +class Outer: + class InnerStub: ... + outer_attr_after_inner_stub: int + + class Inner: + inner_attr: int + + outer_attr: int + +if sys.version_info > (3, 7): + if sys.platform == "win32": + assignment = 1 + def function_definition(self): ... + + def f1(self) -> str: ... + if sys.platform != "win32": + def function_definition(self): ... + assignment = 1 + + def f2(self) -> str: ... +``` + + diff --git a/crates/ruff_python_formatter/tests/snapshots/black_compatibility@py_310__pattern_matching_style.py.snap b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__pattern_matching_style.py.snap similarity index 98% rename from crates/ruff_python_formatter/tests/snapshots/black_compatibility@py_310__pattern_matching_style.py.snap rename to crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__pattern_matching_style.py.snap index 51669107f8..06422b3718 100644 --- a/crates/ruff_python_formatter/tests/snapshots/black_compatibility@py_310__pattern_matching_style.py.snap +++ b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__pattern_matching_style.py.snap @@ -1,6 +1,6 @@ --- source: crates/ruff_python_formatter/tests/fixtures.rs -input_file: crates/ruff_python_formatter/resources/test/fixtures/black/py_310/pattern_matching_style.py +input_file: crates/ruff_python_formatter/resources/test/fixtures/black/cases/pattern_matching_style.py --- ## Input 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 new file mode 100644 index 0000000000..278a20e4a6 --- /dev/null +++ b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__pep604_union_types_line_breaks.py.snap @@ -0,0 +1,370 @@ +--- +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 +--- +## Input + +```python +# This has always worked +z= Loooooooooooooooooooooooong | Loooooooooooooooooooooooong | Loooooooooooooooooooooooong | Loooooooooooooooooooooooong + +# "AnnAssign"s now also work +z: Loooooooooooooooooooooooong | Loooooooooooooooooooooooong | Loooooooooooooooooooooooong | Loooooooooooooooooooooooong +z: (Short + | Short2 + | Short3 + | Short4) +z: (int) +z: ((int)) + + +z: Loooooooooooooooooooooooong | Loooooooooooooooooooooooong | Loooooooooooooooooooooooong | Loooooooooooooooooooooooong = 7 +z: (Short + | Short2 + | Short3 + | Short4) = 8 +z: (int) = 2.3 +z: ((int)) = foo() + +# In case I go for not enforcing parantheses, this might get improved at the same time +x = ( + z + == 9999999999999999999999999999999999999999 + | 9999999999999999999999999999999999999999 + | 9999999999999999999999999999999999999999 + | 9999999999999999999999999999999999999999, + y + == 9999999999999999999999999999999999999999 + + 9999999999999999999999999999999999999999 + + 9999999999999999999999999999999999999999 + + 9999999999999999999999999999999999999999, +) + +x = ( + z == (9999999999999999999999999999999999999999 + | 9999999999999999999999999999999999999999 + | 9999999999999999999999999999999999999999 + | 9999999999999999999999999999999999999999), + y == (9999999999999999999999999999999999999999 + + 9999999999999999999999999999999999999999 + + 9999999999999999999999999999999999999999 + + 9999999999999999999999999999999999999999), +) + +# handle formatting of "tname"s in parameter list + +# remove unnecessary paren +def foo(i: (int)) -> None: ... + + +# this is a syntax error in the type annotation according to mypy, but it's not invalid *python* code, so make sure we don't mess with it and make it so. +def foo(i: (int,)) -> None: ... + +def foo( + i: int, + x: Loooooooooooooooooooooooong + | Looooooooooooooooong + | Looooooooooooooooooooong + | Looooooong, + *, + s: str, +) -> None: + pass + + +@app.get("/path/") +async def foo( + q: str + | None = Query(None, title="Some long title", description="Some long description") +): + pass + + +def f( + max_jobs: int + | None = Option( + None, help="Maximum number of jobs to launch. And some additional text." + ), + another_option: bool = False + ): + ... +``` + +## Black Differences + +```diff +--- Black ++++ Ruff +@@ -7,26 +7,16 @@ + ) + + # "AnnAssign"s now also work +-z: ( +- Loooooooooooooooooooooooong +- | Loooooooooooooooooooooooong +- | Loooooooooooooooooooooooong +- | Loooooooooooooooooooooooong +-) +-z: Short | Short2 | Short3 | Short4 +-z: int +-z: int ++z: Loooooooooooooooooooooooong | Loooooooooooooooooooooooong | Loooooooooooooooooooooooong | Loooooooooooooooooooooooong ++z: (Short | Short2 | Short3 | Short4) ++z: (int) ++z: (int) + + +-z: ( +- Loooooooooooooooooooooooong +- | Loooooooooooooooooooooooong +- | Loooooooooooooooooooooooong +- | Loooooooooooooooooooooooong +-) = 7 +-z: Short | Short2 | Short3 | Short4 = 8 +-z: int = 2.3 +-z: int = foo() ++z: Loooooooooooooooooooooooong | Loooooooooooooooooooooooong | Loooooooooooooooooooooooong | Loooooooooooooooooooooooong = 7 ++z: (Short | Short2 | Short3 | Short4) = 8 ++z: (int) = 2.3 ++z: (int) = foo() + + # In case I go for not enforcing parantheses, this might get improved at the same time + x = ( +@@ -63,7 +53,7 @@ + + + # remove unnecessary paren +-def foo(i: int) -> None: ... ++def foo(i: (int)) -> None: ... + + + # this is a syntax error in the type annotation according to mypy, but it's not invalid *python* code, so make sure we don't mess with it and make it so. +@@ -72,12 +62,10 @@ + + def foo( + i: int, +- x: ( +- Loooooooooooooooooooooooong +- | Looooooooooooooooong +- | Looooooooooooooooooooong +- | Looooooong +- ), ++ x: Loooooooooooooooooooooooong ++ | Looooooooooooooooong ++ | Looooooooooooooooooooong ++ | Looooooong, + *, + s: str, + ) -> None: +@@ -88,7 +76,7 @@ + async def foo( + q: str | None = Query( + None, title="Some long title", description="Some long description" +- ) ++ ), + ): + pass + +``` + +## Ruff Output + +```python +# This has always worked +z = ( + Loooooooooooooooooooooooong + | Loooooooooooooooooooooooong + | Loooooooooooooooooooooooong + | Loooooooooooooooooooooooong +) + +# "AnnAssign"s now also work +z: Loooooooooooooooooooooooong | Loooooooooooooooooooooooong | Loooooooooooooooooooooooong | Loooooooooooooooooooooooong +z: (Short | Short2 | Short3 | Short4) +z: (int) +z: (int) + + +z: Loooooooooooooooooooooooong | Loooooooooooooooooooooooong | Loooooooooooooooooooooooong | Loooooooooooooooooooooooong = 7 +z: (Short | Short2 | Short3 | Short4) = 8 +z: (int) = 2.3 +z: (int) = foo() + +# In case I go for not enforcing parantheses, this might get improved at the same time +x = ( + z + == 9999999999999999999999999999999999999999 + | 9999999999999999999999999999999999999999 + | 9999999999999999999999999999999999999999 + | 9999999999999999999999999999999999999999, + y + == 9999999999999999999999999999999999999999 + + 9999999999999999999999999999999999999999 + + 9999999999999999999999999999999999999999 + + 9999999999999999999999999999999999999999, +) + +x = ( + z + == ( + 9999999999999999999999999999999999999999 + | 9999999999999999999999999999999999999999 + | 9999999999999999999999999999999999999999 + | 9999999999999999999999999999999999999999 + ), + y + == ( + 9999999999999999999999999999999999999999 + + 9999999999999999999999999999999999999999 + + 9999999999999999999999999999999999999999 + + 9999999999999999999999999999999999999999 + ), +) + +# handle formatting of "tname"s in parameter list + + +# remove unnecessary paren +def foo(i: (int)) -> None: ... + + +# this is a syntax error in the type annotation according to mypy, but it's not invalid *python* code, so make sure we don't mess with it and make it so. +def foo(i: (int,)) -> None: ... + + +def foo( + i: int, + x: Loooooooooooooooooooooooong + | Looooooooooooooooong + | Looooooooooooooooooooong + | Looooooong, + *, + s: str, +) -> None: + pass + + +@app.get("/path/") +async def foo( + q: str | None = Query( + None, title="Some long title", description="Some long description" + ), +): + pass + + +def f( + max_jobs: int | None = Option( + None, help="Maximum number of jobs to launch. And some additional text." + ), + another_option: bool = False, +): ... +``` + +## Black Output + +```python +# This has always worked +z = ( + Loooooooooooooooooooooooong + | Loooooooooooooooooooooooong + | Loooooooooooooooooooooooong + | Loooooooooooooooooooooooong +) + +# "AnnAssign"s now also work +z: ( + Loooooooooooooooooooooooong + | Loooooooooooooooooooooooong + | Loooooooooooooooooooooooong + | Loooooooooooooooooooooooong +) +z: Short | Short2 | Short3 | Short4 +z: int +z: int + + +z: ( + Loooooooooooooooooooooooong + | Loooooooooooooooooooooooong + | Loooooooooooooooooooooooong + | Loooooooooooooooooooooooong +) = 7 +z: Short | Short2 | Short3 | Short4 = 8 +z: int = 2.3 +z: int = foo() + +# In case I go for not enforcing parantheses, this might get improved at the same time +x = ( + z + == 9999999999999999999999999999999999999999 + | 9999999999999999999999999999999999999999 + | 9999999999999999999999999999999999999999 + | 9999999999999999999999999999999999999999, + y + == 9999999999999999999999999999999999999999 + + 9999999999999999999999999999999999999999 + + 9999999999999999999999999999999999999999 + + 9999999999999999999999999999999999999999, +) + +x = ( + z + == ( + 9999999999999999999999999999999999999999 + | 9999999999999999999999999999999999999999 + | 9999999999999999999999999999999999999999 + | 9999999999999999999999999999999999999999 + ), + y + == ( + 9999999999999999999999999999999999999999 + + 9999999999999999999999999999999999999999 + + 9999999999999999999999999999999999999999 + + 9999999999999999999999999999999999999999 + ), +) + +# handle formatting of "tname"s in parameter list + + +# remove unnecessary paren +def foo(i: int) -> None: ... + + +# this is a syntax error in the type annotation according to mypy, but it's not invalid *python* code, so make sure we don't mess with it and make it so. +def foo(i: (int,)) -> None: ... + + +def foo( + i: int, + x: ( + Loooooooooooooooooooooooong + | Looooooooooooooooong + | Looooooooooooooooooooong + | Looooooong + ), + *, + s: str, +) -> None: + pass + + +@app.get("/path/") +async def foo( + q: str | None = Query( + None, title="Some long title", description="Some long description" + ) +): + pass + + +def f( + max_jobs: int | None = Option( + None, help="Maximum number of jobs to launch. And some additional text." + ), + another_option: bool = False, +): ... +``` + + diff --git a/crates/ruff_python_formatter/tests/snapshots/black_compatibility@py_310__pep_572_py310.py.snap b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__pep_572_py310.py.snap similarity index 98% rename from crates/ruff_python_formatter/tests/snapshots/black_compatibility@py_310__pep_572_py310.py.snap rename to crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__pep_572_py310.py.snap index b694f46cfd..a49397f429 100644 --- a/crates/ruff_python_formatter/tests/snapshots/black_compatibility@py_310__pep_572_py310.py.snap +++ b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__pep_572_py310.py.snap @@ -1,6 +1,6 @@ --- source: crates/ruff_python_formatter/tests/fixtures.rs -input_file: crates/ruff_python_formatter/resources/test/fixtures/black/py_310/pep_572_py310.py +input_file: crates/ruff_python_formatter/resources/test/fixtures/black/cases/pep_572_py310.py --- ## Input diff --git a/crates/ruff_python_formatter/tests/snapshots/black_compatibility@py_38__pep_572_remove_parens.py.snap b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__pep_572_remove_parens.py.snap similarity index 98% rename from crates/ruff_python_formatter/tests/snapshots/black_compatibility@py_38__pep_572_remove_parens.py.snap rename to crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__pep_572_remove_parens.py.snap index 49c78e6559..ac78124cef 100644 --- a/crates/ruff_python_formatter/tests/snapshots/black_compatibility@py_38__pep_572_remove_parens.py.snap +++ b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__pep_572_remove_parens.py.snap @@ -1,6 +1,6 @@ --- source: crates/ruff_python_formatter/tests/fixtures.rs -input_file: crates/ruff_python_formatter/resources/test/fixtures/black/py_38/pep_572_remove_parens.py +input_file: crates/ruff_python_formatter/resources/test/fixtures/black/cases/pep_572_remove_parens.py --- ## Input diff --git a/crates/ruff_python_formatter/tests/snapshots/black_compatibility@miscellaneous__power_op_newline.py.snap b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__power_op_newline.py.snap similarity index 91% rename from crates/ruff_python_formatter/tests/snapshots/black_compatibility@miscellaneous__power_op_newline.py.snap rename to crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__power_op_newline.py.snap index b9ff9703eb..1796017091 100644 --- a/crates/ruff_python_formatter/tests/snapshots/black_compatibility@miscellaneous__power_op_newline.py.snap +++ b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__power_op_newline.py.snap @@ -1,6 +1,6 @@ --- source: crates/ruff_python_formatter/tests/fixtures.rs -input_file: crates/ruff_python_formatter/resources/test/fixtures/black/miscellaneous/power_op_newline.py +input_file: crates/ruff_python_formatter/resources/test/fixtures/black/cases/power_op_newline.py --- ## Input diff --git a/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__preview_allow_empty_first_line_in_special_cases.py.snap b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__preview_allow_empty_first_line_in_special_cases.py.snap new file mode 100644 index 0000000000..2dce9bc3af --- /dev/null +++ b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__preview_allow_empty_first_line_in_special_cases.py.snap @@ -0,0 +1,218 @@ +--- +source: crates/ruff_python_formatter/tests/fixtures.rs +input_file: crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_allow_empty_first_line_in_special_cases.py +--- +## Input + +```python +def foo(): + """ + Docstring + """ + + # Here we go + if x: + + # This is also now fine + a = 123 + + else: + # But not necessary + a = 123 + + if y: + + while True: + + """ + Long comment here + """ + a = 123 + + if z: + + for _ in range(100): + a = 123 + else: + + try: + + # this should be ok + a = 123 + except: + + """also this""" + a = 123 + + +def bar(): + + if x: + a = 123 + + +def baz(): + + # OK + if x: + a = 123 +``` + +## Black Differences + +```diff +--- Black ++++ Ruff +@@ -5,7 +5,6 @@ + + # Here we go + if x: +- + # This is also now fine + a = 123 + +@@ -14,38 +13,30 @@ + a = 123 + + if y: +- + while True: +- + """ + Long comment here + """ + a = 123 + + if z: +- + for _ in range(100): + a = 123 + else: +- + try: +- + # this should be ok + a = 123 + except: +- + """also this""" + a = 123 + + + def bar(): +- + if x: + a = 123 + + + def baz(): +- + # OK + if x: + a = 123 +``` + +## Ruff Output + +```python +def foo(): + """ + Docstring + """ + + # Here we go + if x: + # This is also now fine + a = 123 + + else: + # But not necessary + a = 123 + + if y: + while True: + """ + Long comment here + """ + a = 123 + + if z: + for _ in range(100): + a = 123 + else: + try: + # this should be ok + a = 123 + except: + """also this""" + a = 123 + + +def bar(): + if x: + a = 123 + + +def baz(): + # OK + if x: + a = 123 +``` + +## Black Output + +```python +def foo(): + """ + Docstring + """ + + # Here we go + if x: + + # This is also now fine + a = 123 + + else: + # But not necessary + a = 123 + + if y: + + while True: + + """ + Long comment here + """ + a = 123 + + if z: + + for _ in range(100): + a = 123 + else: + + try: + + # this should be ok + a = 123 + except: + + """also this""" + a = 123 + + +def bar(): + + if x: + a = 123 + + +def baz(): + + # OK + if x: + a = 123 +``` + + diff --git a/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__preview_cantfit.py.snap b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__preview_cantfit.py.snap new file mode 100644 index 0000000000..c22c4b6ea4 --- /dev/null +++ b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__preview_cantfit.py.snap @@ -0,0 +1,219 @@ +--- +source: crates/ruff_python_formatter/tests/fixtures.rs +input_file: crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_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 +) +# long arguments +normal_name = normal_function_name( + "but with super long string arguments that on their own exceed the line limit so there's no way it can ever fit", + "eggs with spam and eggs and spam with eggs with spam and eggs and spam with eggs with spam and eggs and spam with eggs", + this_is_a_ridiculously_long_name_and_nobody_in_their_right_mind_would_use_one_like_it=0, +) +string_variable_name = ( + "a string that is waaaaaaaayyyyyyyy too long, even in parens, there's nothing you can do" # noqa +) +for key in """ + hostname + 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 +``` + +## 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 + ) +@@ -35,10 +29,8 @@ + ) + # long arguments + normal_name = normal_function_name( +- "but with super long string arguments that on their own exceed the line limit so" +- " there's no way it can ever fit", +- "eggs with spam and eggs and spam with eggs with spam and eggs and spam with eggs" +- " with spam and eggs and spam with eggs", ++ "but with super long string arguments that on their own exceed the line limit so there's no way it can ever fit", ++ "eggs with spam and eggs and spam with eggs with spam and eggs and spam with eggs with spam and eggs and spam with eggs", + this_is_a_ridiculously_long_name_and_nobody_in_their_right_mind_would_use_one_like_it=0, + ) + string_variable_name = "a string that is waaaaaaaayyyyyyyy too long, even in parens, there's nothing you can do" # noqa +``` + +## Ruff Output + +```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 + ) +) +# long arguments +normal_name = normal_function_name( + "but with super long string arguments that on their own exceed the line limit so there's no way it can ever fit", + "eggs with spam and eggs and spam with eggs with spam and eggs and spam with eggs with spam and eggs and spam with eggs", + this_is_a_ridiculously_long_name_and_nobody_in_their_right_mind_would_use_one_like_it=0, +) +string_variable_name = "a string that is waaaaaaaayyyyyyyy too long, even in parens, there's nothing you can do" # noqa +for key in """ + hostname + 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, +) +``` + +## 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 + ) +) +# long arguments +normal_name = normal_function_name( + "but with super long string arguments that on their own exceed the line limit so" + " there's no way it can ever fit", + "eggs with spam and eggs and spam with eggs with spam and eggs and spam with eggs" + " with spam and eggs and spam with eggs", + this_is_a_ridiculously_long_name_and_nobody_in_their_right_mind_would_use_one_like_it=0, +) +string_variable_name = "a string that is waaaaaaaayyyyyyyy too long, even in parens, there's nothing you can do" # noqa +for key in """ + hostname + 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, +) +``` + + 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 new file mode 100644 index 0000000000..337c78698d --- /dev/null +++ b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__preview_comments7.py.snap @@ -0,0 +1,600 @@ +--- +source: crates/ruff_python_formatter/tests/fixtures.rs +input_file: crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_comments7.py +--- +## Input + +```python +from .config import ( + Any, + Bool, + ConfigType, + ConfigTypeAttributes, + Int, + Path, + # String, + # resolve_to_config_type, + # DEFAULT_TYPE_ATTRIBUTES, +) + + +from .config import ( + Any, + Bool, + ConfigType, + ConfigTypeAttributes, + Int, + no_comma_here_yet + # and some comments, + # resolve_to_config_type, + # DEFAULT_TYPE_ATTRIBUTES, +) +from com.my_lovely_company.my_lovely_team.my_lovely_project.my_lovely_component import ( + MyLovelyCompanyTeamProjectComponent # NOT DRY +) +from com.my_lovely_company.my_lovely_team.my_lovely_project.my_lovely_component import ( + MyLovelyCompanyTeamProjectComponent as component # DRY +) + + +result = 1 # look ma, no comment migration xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx + +result = ( + 1 # look ma, no comment migration xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +) + +result = ( + "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" # aaa +) + +result = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" # aaa + + +def func(): + c = call( + 0.0123, + 0.0456, + 0.0789, + 0.0123, + 0.0789, + a[-1], # type: ignore + ) + c = call( + 0.0123, + 0.0456, + 0.0789, + 0.0123, + 0.0789, + a[-1] # type: ignore + ) + c = call( + 0.0123, + 0.0456, + 0.0789, + 0.0123, + 0.0456, + 0.0789, + 0.0123, + 0.0456, + 0.0789, + a[-1] # type: ignore + ) + + # The type: ignore exception only applies to line length, not + # other types of formatting. + c = call( + "aaaaaaaa", "aaaaaaaa", "aaaaaaaa", "aaaaaaaa", "aaaaaaaa", "aaaaaaaa", # type: ignore + "aaaaaaaa", "aaaaaaaa", "aaaaaaaa", "aaaaaaaa", "aaaaaaaa", "aaaaaaaa" + ) + + +class C: + @pytest.mark.parametrize( + ("post_data", "message"), + [ + # metadata_version errors. + ( + {}, + "None is an invalid value for Metadata-Version. Error: This field is" + " required. see" + " https://packaging.python.org/specifications/core-metadata" + ), + ( + {"metadata_version": "-1"}, + "'-1' is an invalid value for Metadata-Version. Error: Unknown Metadata" + " Version see" + " https://packaging.python.org/specifications/core-metadata" + ), + # name errors. + ( + {"metadata_version": "1.2"}, + "'' is an invalid value for Name. Error: This field is required. see" + " https://packaging.python.org/specifications/core-metadata" + ), + ( + {"metadata_version": "1.2", "name": "foo-"}, + "'foo-' is an invalid value for Name. Error: Must start and end with a" + " letter or numeral and contain only ascii numeric and '.', '_' and" + " '-'. see https://packaging.python.org/specifications/core-metadata" + ), + # version errors. + ( + {"metadata_version": "1.2", "name": "example"}, + "'' is an invalid value for Version. Error: This field is required. see" + " https://packaging.python.org/specifications/core-metadata" + ), + ( + {"metadata_version": "1.2", "name": "example", "version": "dog"}, + "'dog' is an invalid value for Version. Error: Must start and end with" + " a letter or numeral and contain only ascii numeric and '.', '_' and" + " '-'. see https://packaging.python.org/specifications/core-metadata" + ) + ] + ) + def test_fails_invalid_post_data( + self, pyramid_config, db_request, post_data, message + ): + ... + +square = Square(4) # type: Optional[Square] + +# Regression test for https://github.com/psf/black/issues/3756. +[ + ( + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" # aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa + ), +] +[ + ( # aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" # aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa + ), +] +``` + +## Black Differences + +```diff +--- Black ++++ Ruff +@@ -34,13 +34,9 @@ + + result = 1 # look ma, no comment migration xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx + +-result = ( # aaa +- "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" +-) ++result = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" # aaa + +-result = ( # aaa +- "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" +-) ++result = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" # aaa + + + def func(): +@@ -52,12 +48,19 @@ + 0.0789, + a[-1], # type: ignore + ) +- c = call(0.0123, 0.0456, 0.0789, 0.0123, 0.0789, a[-1]) # type: ignore + c = call( + 0.0123, + 0.0456, + 0.0789, + 0.0123, ++ 0.0789, ++ a[-1], # type: ignore ++ ) ++ c = call( ++ 0.0123, ++ 0.0456, ++ 0.0789, ++ 0.0123, + 0.0456, + 0.0789, + 0.0123, +@@ -91,53 +94,39 @@ + # metadata_version errors. + ( + {}, +- ( +- "None is an invalid value for Metadata-Version. Error: This field" +- " is required. see" +- " https://packaging.python.org/specifications/core-metadata" +- ), ++ "None is an invalid value for Metadata-Version. Error: This field is" ++ " required. see" ++ " https://packaging.python.org/specifications/core-metadata", + ), + ( + {"metadata_version": "-1"}, +- ( +- "'-1' is an invalid value for Metadata-Version. Error: Unknown" +- " Metadata Version see" +- " https://packaging.python.org/specifications/core-metadata" +- ), ++ "'-1' is an invalid value for Metadata-Version. Error: Unknown Metadata" ++ " Version see" ++ " https://packaging.python.org/specifications/core-metadata", + ), + # name errors. + ( + {"metadata_version": "1.2"}, +- ( +- "'' is an invalid value for Name. Error: This field is required." +- " see https://packaging.python.org/specifications/core-metadata" +- ), ++ "'' is an invalid value for Name. Error: This field is required. see" ++ " https://packaging.python.org/specifications/core-metadata", + ), + ( + {"metadata_version": "1.2", "name": "foo-"}, +- ( +- "'foo-' is an invalid value for Name. Error: Must start and end" +- " with a letter or numeral and contain only ascii numeric and '.'," +- " '_' and '-'. see" +- " https://packaging.python.org/specifications/core-metadata" +- ), ++ "'foo-' is an invalid value for Name. Error: Must start and end with a" ++ " letter or numeral and contain only ascii numeric and '.', '_' and" ++ " '-'. see https://packaging.python.org/specifications/core-metadata", + ), + # version errors. + ( + {"metadata_version": "1.2", "name": "example"}, +- ( +- "'' is an invalid value for Version. Error: This field is required." +- " see https://packaging.python.org/specifications/core-metadata" +- ), ++ "'' is an invalid value for Version. Error: This field is required. see" ++ " https://packaging.python.org/specifications/core-metadata", + ), + ( + {"metadata_version": "1.2", "name": "example", "version": "dog"}, +- ( +- "'dog' is an invalid value for Version. Error: Must start and end" +- " with a letter or numeral and contain only ascii numeric and '.'," +- " '_' and '-'. see" +- " https://packaging.python.org/specifications/core-metadata" +- ), ++ "'dog' is an invalid value for Version. Error: Must start and end with" ++ " a letter or numeral and contain only ascii numeric and '.', '_' and" ++ " '-'. see https://packaging.python.org/specifications/core-metadata", + ), + ], + ) +@@ -150,8 +139,8 @@ + + # Regression test for https://github.com/psf/black/issues/3756. + [ +- ( # aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +- "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" ++ ( ++ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" # aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa + ), + ] + [ +``` + +## Ruff Output + +```python +from .config import ( + Any, + Bool, + ConfigType, + ConfigTypeAttributes, + Int, + Path, + # String, + # resolve_to_config_type, + # DEFAULT_TYPE_ATTRIBUTES, +) + + +from .config import ( + Any, + Bool, + ConfigType, + ConfigTypeAttributes, + Int, + no_comma_here_yet, + # and some comments, + # resolve_to_config_type, + # DEFAULT_TYPE_ATTRIBUTES, +) +from com.my_lovely_company.my_lovely_team.my_lovely_project.my_lovely_component import ( + MyLovelyCompanyTeamProjectComponent, # NOT DRY +) +from com.my_lovely_company.my_lovely_team.my_lovely_project.my_lovely_component import ( + MyLovelyCompanyTeamProjectComponent as component, # DRY +) + + +result = 1 # look ma, no comment migration xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx + +result = 1 # look ma, no comment migration xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx + +result = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" # aaa + +result = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" # aaa + + +def func(): + c = call( + 0.0123, + 0.0456, + 0.0789, + 0.0123, + 0.0789, + a[-1], # type: ignore + ) + c = call( + 0.0123, + 0.0456, + 0.0789, + 0.0123, + 0.0789, + a[-1], # type: ignore + ) + c = call( + 0.0123, + 0.0456, + 0.0789, + 0.0123, + 0.0456, + 0.0789, + 0.0123, + 0.0456, + 0.0789, + a[-1], # type: ignore + ) + + # The type: ignore exception only applies to line length, not + # other types of formatting. + c = call( + "aaaaaaaa", + "aaaaaaaa", + "aaaaaaaa", + "aaaaaaaa", + "aaaaaaaa", + "aaaaaaaa", # type: ignore + "aaaaaaaa", + "aaaaaaaa", + "aaaaaaaa", + "aaaaaaaa", + "aaaaaaaa", + "aaaaaaaa", + ) + + +class C: + @pytest.mark.parametrize( + ("post_data", "message"), + [ + # metadata_version errors. + ( + {}, + "None is an invalid value for Metadata-Version. Error: This field is" + " required. see" + " https://packaging.python.org/specifications/core-metadata", + ), + ( + {"metadata_version": "-1"}, + "'-1' is an invalid value for Metadata-Version. Error: Unknown Metadata" + " Version see" + " https://packaging.python.org/specifications/core-metadata", + ), + # name errors. + ( + {"metadata_version": "1.2"}, + "'' is an invalid value for Name. Error: This field is required. see" + " https://packaging.python.org/specifications/core-metadata", + ), + ( + {"metadata_version": "1.2", "name": "foo-"}, + "'foo-' is an invalid value for Name. Error: Must start and end with a" + " letter or numeral and contain only ascii numeric and '.', '_' and" + " '-'. see https://packaging.python.org/specifications/core-metadata", + ), + # version errors. + ( + {"metadata_version": "1.2", "name": "example"}, + "'' is an invalid value for Version. Error: This field is required. see" + " https://packaging.python.org/specifications/core-metadata", + ), + ( + {"metadata_version": "1.2", "name": "example", "version": "dog"}, + "'dog' is an invalid value for Version. Error: Must start and end with" + " a letter or numeral and contain only ascii numeric and '.', '_' and" + " '-'. see https://packaging.python.org/specifications/core-metadata", + ), + ], + ) + def test_fails_invalid_post_data( + self, pyramid_config, db_request, post_data, message + ): ... + + +square = Square(4) # type: Optional[Square] + +# Regression test for https://github.com/psf/black/issues/3756. +[ + ( + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" # aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa + ), +] +[ + ( # aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" # aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa + ), +] +``` + +## Black Output + +```python +from .config import ( + Any, + Bool, + ConfigType, + ConfigTypeAttributes, + Int, + Path, + # String, + # resolve_to_config_type, + # DEFAULT_TYPE_ATTRIBUTES, +) + + +from .config import ( + Any, + Bool, + ConfigType, + ConfigTypeAttributes, + Int, + no_comma_here_yet, + # and some comments, + # resolve_to_config_type, + # DEFAULT_TYPE_ATTRIBUTES, +) +from com.my_lovely_company.my_lovely_team.my_lovely_project.my_lovely_component import ( + MyLovelyCompanyTeamProjectComponent, # NOT DRY +) +from com.my_lovely_company.my_lovely_team.my_lovely_project.my_lovely_component import ( + MyLovelyCompanyTeamProjectComponent as component, # DRY +) + + +result = 1 # look ma, no comment migration xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx + +result = 1 # look ma, no comment migration xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx + +result = ( # aaa + "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" +) + +result = ( # aaa + "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" +) + + +def func(): + c = call( + 0.0123, + 0.0456, + 0.0789, + 0.0123, + 0.0789, + a[-1], # type: ignore + ) + c = call(0.0123, 0.0456, 0.0789, 0.0123, 0.0789, a[-1]) # type: ignore + c = call( + 0.0123, + 0.0456, + 0.0789, + 0.0123, + 0.0456, + 0.0789, + 0.0123, + 0.0456, + 0.0789, + a[-1], # type: ignore + ) + + # The type: ignore exception only applies to line length, not + # other types of formatting. + c = call( + "aaaaaaaa", + "aaaaaaaa", + "aaaaaaaa", + "aaaaaaaa", + "aaaaaaaa", + "aaaaaaaa", # type: ignore + "aaaaaaaa", + "aaaaaaaa", + "aaaaaaaa", + "aaaaaaaa", + "aaaaaaaa", + "aaaaaaaa", + ) + + +class C: + @pytest.mark.parametrize( + ("post_data", "message"), + [ + # metadata_version errors. + ( + {}, + ( + "None is an invalid value for Metadata-Version. Error: This field" + " is required. see" + " https://packaging.python.org/specifications/core-metadata" + ), + ), + ( + {"metadata_version": "-1"}, + ( + "'-1' is an invalid value for Metadata-Version. Error: Unknown" + " Metadata Version see" + " https://packaging.python.org/specifications/core-metadata" + ), + ), + # name errors. + ( + {"metadata_version": "1.2"}, + ( + "'' is an invalid value for Name. Error: This field is required." + " see https://packaging.python.org/specifications/core-metadata" + ), + ), + ( + {"metadata_version": "1.2", "name": "foo-"}, + ( + "'foo-' is an invalid value for Name. Error: Must start and end" + " with a letter or numeral and contain only ascii numeric and '.'," + " '_' and '-'. see" + " https://packaging.python.org/specifications/core-metadata" + ), + ), + # version errors. + ( + {"metadata_version": "1.2", "name": "example"}, + ( + "'' is an invalid value for Version. Error: This field is required." + " see https://packaging.python.org/specifications/core-metadata" + ), + ), + ( + {"metadata_version": "1.2", "name": "example", "version": "dog"}, + ( + "'dog' is an invalid value for Version. Error: Must start and end" + " with a letter or numeral and contain only ascii numeric and '.'," + " '_' and '-'. see" + " https://packaging.python.org/specifications/core-metadata" + ), + ), + ], + ) + def test_fails_invalid_post_data( + self, pyramid_config, db_request, post_data, message + ): ... + + +square = Square(4) # type: Optional[Square] + +# Regression test for https://github.com/psf/black/issues/3756. +[ + ( # aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + ), +] +[ + ( # aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" # aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa + ), +] +``` + + diff --git a/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__preview_context_managers_39.py.snap b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__preview_context_managers_39.py.snap new file mode 100644 index 0000000000..ed87cfaba1 --- /dev/null +++ b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__preview_context_managers_39.py.snap @@ -0,0 +1,342 @@ +--- +source: crates/ruff_python_formatter/tests/fixtures.rs +input_file: crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_context_managers_39.py +--- +## Input + +```python +with \ + make_context_manager1() as cm1, \ + make_context_manager2() as cm2, \ + make_context_manager3() as cm3, \ + make_context_manager4() as cm4 \ +: + pass + + +# Leading comment +with \ + make_context_manager1() as cm1, \ + make_context_manager2(), \ + make_context_manager3() as cm3, \ + make_context_manager4() \ +: + pass + + +with \ + new_new_new1() as cm1, \ + new_new_new2() \ +: + pass + + +with ( + new_new_new1() as cm1, + new_new_new2() +): + pass + + +# Leading comment. +with ( + # First comment. + new_new_new1() as cm1, + # Second comment. + new_new_new2() + # Last comment. +): + pass + + +with \ + this_is_a_very_long_call(looong_arg1=looong_value1, looong_arg2=looong_value2) as cm1, \ + this_is_a_very_long_call(looong_arg1=looong_value1, looong_arg2=looong_value2, looong_arg3=looong_value3, looong_arg4=looong_value4) as cm2 \ +: + pass + + +with mock.patch.object( + self.my_runner, "first_method", autospec=True +) as mock_run_adb, mock.patch.object( + self.my_runner, "second_method", autospec=True, return_value="foo" +): + pass + + +with xxxxxxxx.some_kind_of_method( + some_argument=[ + "first", + "second", + "third", + ] +).another_method() as cmd: + pass + + +async def func(): + async with \ + make_context_manager1() as cm1, \ + make_context_manager2() as cm2, \ + make_context_manager3() as cm3, \ + make_context_manager4() as cm4 \ + : + pass + + async with some_function( + argument1, argument2, argument3="some_value" + ) as some_cm, some_other_function( + argument1, argument2, argument3="some_value" + ): + pass +``` + +## Black Differences + +```diff +--- Black ++++ Ruff +@@ -1,19 +1,9 @@ +-with ( +- make_context_manager1() as cm1, +- make_context_manager2() as cm2, +- make_context_manager3() as cm3, +- make_context_manager4() as cm4, +-): ++with make_context_manager1() as cm1, make_context_manager2() as cm2, make_context_manager3() as cm3, make_context_manager4() as cm4: + pass + + + # Leading comment +-with ( +- make_context_manager1() as cm1, +- make_context_manager2(), +- make_context_manager3() as cm3, +- make_context_manager4(), +-): ++with make_context_manager1() as cm1, make_context_manager2(), make_context_manager3() as cm3, make_context_manager4(): + pass + + +@@ -36,25 +26,21 @@ + pass + + +-with ( +- this_is_a_very_long_call( +- looong_arg1=looong_value1, looong_arg2=looong_value2 +- ) as cm1, +- this_is_a_very_long_call( +- looong_arg1=looong_value1, +- looong_arg2=looong_value2, +- looong_arg3=looong_value3, +- looong_arg4=looong_value4, +- ) as cm2, +-): ++with this_is_a_very_long_call( ++ looong_arg1=looong_value1, looong_arg2=looong_value2 ++) as cm1, this_is_a_very_long_call( ++ looong_arg1=looong_value1, ++ looong_arg2=looong_value2, ++ looong_arg3=looong_value3, ++ looong_arg4=looong_value4, ++) as cm2: + pass + + +-with ( +- mock.patch.object(self.my_runner, "first_method", autospec=True) as mock_run_adb, +- mock.patch.object( +- self.my_runner, "second_method", autospec=True, return_value="foo" +- ), ++with mock.patch.object( ++ self.my_runner, "first_method", autospec=True ++) as mock_run_adb, mock.patch.object( ++ self.my_runner, "second_method", autospec=True, return_value="foo" + ): + pass + +@@ -70,16 +56,10 @@ + + + async def func(): +- async with ( +- make_context_manager1() as cm1, +- make_context_manager2() as cm2, +- make_context_manager3() as cm3, +- make_context_manager4() as cm4, +- ): ++ async with make_context_manager1() as cm1, make_context_manager2() as cm2, make_context_manager3() as cm3, make_context_manager4() as cm4: + pass + +- async with ( +- some_function(argument1, argument2, argument3="some_value") as some_cm, +- some_other_function(argument1, argument2, argument3="some_value"), +- ): ++ async with some_function( ++ argument1, argument2, argument3="some_value" ++ ) as some_cm, some_other_function(argument1, argument2, argument3="some_value"): + pass +``` + +## Ruff Output + +```python +with make_context_manager1() as cm1, make_context_manager2() as cm2, make_context_manager3() as cm3, make_context_manager4() as cm4: + pass + + +# Leading comment +with make_context_manager1() as cm1, make_context_manager2(), make_context_manager3() as cm3, make_context_manager4(): + pass + + +with new_new_new1() as cm1, new_new_new2(): + pass + + +with new_new_new1() as cm1, new_new_new2(): + pass + + +# Leading comment. +with ( + # First comment. + new_new_new1() as cm1, + # Second comment. + new_new_new2(), + # Last comment. +): + pass + + +with this_is_a_very_long_call( + looong_arg1=looong_value1, looong_arg2=looong_value2 +) as cm1, this_is_a_very_long_call( + looong_arg1=looong_value1, + looong_arg2=looong_value2, + looong_arg3=looong_value3, + looong_arg4=looong_value4, +) as cm2: + pass + + +with mock.patch.object( + self.my_runner, "first_method", autospec=True +) as mock_run_adb, mock.patch.object( + self.my_runner, "second_method", autospec=True, return_value="foo" +): + pass + + +with xxxxxxxx.some_kind_of_method( + some_argument=[ + "first", + "second", + "third", + ] +).another_method() as cmd: + pass + + +async def func(): + async with make_context_manager1() as cm1, make_context_manager2() as cm2, make_context_manager3() as cm3, make_context_manager4() as cm4: + pass + + async with some_function( + argument1, argument2, argument3="some_value" + ) as some_cm, some_other_function(argument1, argument2, argument3="some_value"): + pass +``` + +## Black Output + +```python +with ( + make_context_manager1() as cm1, + make_context_manager2() as cm2, + make_context_manager3() as cm3, + make_context_manager4() as cm4, +): + pass + + +# Leading comment +with ( + make_context_manager1() as cm1, + make_context_manager2(), + make_context_manager3() as cm3, + make_context_manager4(), +): + pass + + +with new_new_new1() as cm1, new_new_new2(): + pass + + +with new_new_new1() as cm1, new_new_new2(): + pass + + +# Leading comment. +with ( + # First comment. + new_new_new1() as cm1, + # Second comment. + new_new_new2(), + # Last comment. +): + pass + + +with ( + this_is_a_very_long_call( + looong_arg1=looong_value1, looong_arg2=looong_value2 + ) as cm1, + this_is_a_very_long_call( + looong_arg1=looong_value1, + looong_arg2=looong_value2, + looong_arg3=looong_value3, + looong_arg4=looong_value4, + ) as cm2, +): + pass + + +with ( + mock.patch.object(self.my_runner, "first_method", autospec=True) as mock_run_adb, + mock.patch.object( + self.my_runner, "second_method", autospec=True, return_value="foo" + ), +): + pass + + +with xxxxxxxx.some_kind_of_method( + some_argument=[ + "first", + "second", + "third", + ] +).another_method() as cmd: + pass + + +async def func(): + async with ( + make_context_manager1() as cm1, + make_context_manager2() as cm2, + make_context_manager3() as cm3, + make_context_manager4() as cm4, + ): + pass + + async with ( + some_function(argument1, argument2, argument3="some_value") as some_cm, + some_other_function(argument1, argument2, argument3="some_value"), + ): + pass +``` + + diff --git a/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__preview_context_managers_autodetect_310.py.snap b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__preview_context_managers_autodetect_310.py.snap new file mode 100644 index 0000000000..abee1610d4 --- /dev/null +++ b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__preview_context_managers_autodetect_310.py.snap @@ -0,0 +1,79 @@ +--- +source: crates/ruff_python_formatter/tests/fixtures.rs +input_file: crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_context_managers_autodetect_310.py +--- +## Input + +```python +# This file uses pattern matching introduced in Python 3.10. + + +match http_code: + case 404: + print("Not found") + + +with \ + make_context_manager1() as cm1, \ + make_context_manager2() as cm2, \ + make_context_manager3() as cm3, \ + make_context_manager4() as cm4 \ +: + pass +``` + +## Black Differences + +```diff +--- Black ++++ Ruff +@@ -6,10 +6,5 @@ + print("Not found") + + +-with ( +- make_context_manager1() as cm1, +- make_context_manager2() as cm2, +- make_context_manager3() as cm3, +- make_context_manager4() as cm4, +-): ++with make_context_manager1() as cm1, make_context_manager2() as cm2, make_context_manager3() as cm3, make_context_manager4() as cm4: + pass +``` + +## Ruff Output + +```python +# This file uses pattern matching introduced in Python 3.10. + + +match http_code: + case 404: + print("Not found") + + +with make_context_manager1() as cm1, make_context_manager2() as cm2, make_context_manager3() as cm3, make_context_manager4() as cm4: + pass +``` + +## Black Output + +```python +# This file uses pattern matching introduced in Python 3.10. + + +match http_code: + case 404: + print("Not found") + + +with ( + make_context_manager1() as cm1, + make_context_manager2() as cm2, + make_context_manager3() as cm3, + make_context_manager4() as cm4, +): + pass +``` + + diff --git a/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__preview_context_managers_autodetect_311.py.snap b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__preview_context_managers_autodetect_311.py.snap new file mode 100644 index 0000000000..71002c8d5d --- /dev/null +++ b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__preview_context_managers_autodetect_311.py.snap @@ -0,0 +1,82 @@ +--- +source: crates/ruff_python_formatter/tests/fixtures.rs +input_file: crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_context_managers_autodetect_311.py +--- +## Input + +```python +# This file uses except* clause in Python 3.11. + + +try: + some_call() +except* Error as e: + pass + + +with \ + make_context_manager1() as cm1, \ + make_context_manager2() as cm2, \ + make_context_manager3() as cm3, \ + make_context_manager4() as cm4 \ +: + pass +``` + +## Black Differences + +```diff +--- Black ++++ Ruff +@@ -7,10 +7,5 @@ + pass + + +-with ( +- make_context_manager1() as cm1, +- make_context_manager2() as cm2, +- make_context_manager3() as cm3, +- make_context_manager4() as cm4, +-): ++with make_context_manager1() as cm1, make_context_manager2() as cm2, make_context_manager3() as cm3, make_context_manager4() as cm4: + pass +``` + +## Ruff Output + +```python +# This file uses except* clause in Python 3.11. + + +try: + some_call() +except* Error as e: + pass + + +with make_context_manager1() as cm1, make_context_manager2() as cm2, make_context_manager3() as cm3, make_context_manager4() as cm4: + pass +``` + +## Black Output + +```python +# This file uses except* clause in Python 3.11. + + +try: + some_call() +except* Error as e: + pass + + +with ( + make_context_manager1() as cm1, + make_context_manager2() as cm2, + make_context_manager3() as cm3, + make_context_manager4() as cm4, +): + pass +``` + + diff --git a/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__preview_context_managers_autodetect_39.py.snap b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__preview_context_managers_autodetect_39.py.snap new file mode 100644 index 0000000000..e1aeaa9faf --- /dev/null +++ b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__preview_context_managers_autodetect_39.py.snap @@ -0,0 +1,81 @@ +--- +source: crates/ruff_python_formatter/tests/fixtures.rs +input_file: crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_context_managers_autodetect_39.py +--- +## Input + +```python +# This file uses parenthesized context managers introduced in Python 3.9. + + +with \ + make_context_manager1() as cm1, \ + make_context_manager2() as cm2, \ + make_context_manager3() as cm3, \ + make_context_manager4() as cm4 \ +: + pass + + +with ( + new_new_new1() as cm1, + new_new_new2() +): + pass +``` + +## Black Differences + +```diff +--- Black ++++ Ruff +@@ -1,12 +1,7 @@ + # This file uses parenthesized context managers introduced in Python 3.9. + + +-with ( +- make_context_manager1() as cm1, +- make_context_manager2() as cm2, +- make_context_manager3() as cm3, +- make_context_manager4() as cm4, +-): ++with make_context_manager1() as cm1, make_context_manager2() as cm2, make_context_manager3() as cm3, make_context_manager4() as cm4: + pass + + +``` + +## Ruff Output + +```python +# This file uses parenthesized context managers introduced in Python 3.9. + + +with make_context_manager1() as cm1, make_context_manager2() as cm2, make_context_manager3() as cm3, make_context_manager4() as cm4: + pass + + +with new_new_new1() as cm1, new_new_new2(): + pass +``` + +## Black Output + +```python +# This file uses parenthesized context managers introduced in Python 3.9. + + +with ( + make_context_manager1() as cm1, + make_context_manager2() as cm2, + make_context_manager3() as cm3, + make_context_manager4() as cm4, +): + pass + + +with new_new_new1() as cm1, new_new_new2(): + pass +``` + + diff --git a/crates/ruff_python_formatter/tests/snapshots/black_compatibility@miscellaneous__docstring_preview_no_string_normalization.py.snap b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__preview_docstring_no_string_normalization.py.snap similarity index 96% rename from crates/ruff_python_formatter/tests/snapshots/black_compatibility@miscellaneous__docstring_preview_no_string_normalization.py.snap rename to crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__preview_docstring_no_string_normalization.py.snap index a2969f3e40..70b323e493 100644 --- a/crates/ruff_python_formatter/tests/snapshots/black_compatibility@miscellaneous__docstring_preview_no_string_normalization.py.snap +++ b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__preview_docstring_no_string_normalization.py.snap @@ -1,6 +1,6 @@ --- source: crates/ruff_python_formatter/tests/fixtures.rs -input_file: crates/ruff_python_formatter/resources/test/fixtures/black/miscellaneous/docstring_preview_no_string_normalization.py +input_file: crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_docstring_no_string_normalization.py --- ## Input diff --git a/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__preview_dummy_implementations.py.snap b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__preview_dummy_implementations.py.snap new file mode 100644 index 0000000000..a5ec909765 --- /dev/null +++ b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__preview_dummy_implementations.py.snap @@ -0,0 +1,231 @@ +--- +source: crates/ruff_python_formatter/tests/fixtures.rs +input_file: crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_dummy_implementations.py +--- +## Input + +```python +from typing import NoReturn, Protocol, Union, overload + + +def dummy(a): ... +def other(b): ... + + +@overload +def a(arg: int) -> int: ... +@overload +def a(arg: str) -> str: ... +@overload +def a(arg: object) -> NoReturn: ... +def a(arg: Union[int, str, object]) -> Union[int, str]: + if not isinstance(arg, (int, str)): + raise TypeError + return arg + +class Proto(Protocol): + def foo(self, a: int) -> int: + ... + + def bar(self, b: str) -> str: ... + def baz(self, c: bytes) -> str: + ... + + +def dummy_two(): + ... +@dummy +def dummy_three(): + ... + +def dummy_four(): + ... + +@overload +def b(arg: int) -> int: ... + +@overload +def b(arg: str) -> str: ... +@overload +def b(arg: object) -> NoReturn: ... + +def b(arg: Union[int, str, object]) -> Union[int, str]: + if not isinstance(arg, (int, str)): + raise TypeError + return arg +``` + +## Black Differences + +```diff +--- Black ++++ Ruff +@@ -2,15 +2,23 @@ + + + def dummy(a): ... ++ ++ + def other(b): ... + + + @overload + def a(arg: int) -> int: ... ++ ++ + @overload + def a(arg: str) -> str: ... ++ ++ + @overload + def a(arg: object) -> NoReturn: ... ++ ++ + def a(arg: Union[int, str, object]) -> Union[int, str]: + if not isinstance(arg, (int, str)): + raise TypeError +@@ -21,10 +29,13 @@ + def foo(self, a: int) -> int: ... + + def bar(self, b: str) -> str: ... ++ + def baz(self, c: bytes) -> str: ... + + + def dummy_two(): ... ++ ++ + @dummy + def dummy_three(): ... + +@@ -38,6 +49,8 @@ + + @overload + def b(arg: str) -> str: ... ++ ++ + @overload + def b(arg: object) -> NoReturn: ... + +``` + +## Ruff Output + +```python +from typing import NoReturn, Protocol, Union, overload + + +def dummy(a): ... + + +def other(b): ... + + +@overload +def a(arg: int) -> int: ... + + +@overload +def a(arg: str) -> str: ... + + +@overload +def a(arg: object) -> NoReturn: ... + + +def a(arg: Union[int, str, object]) -> Union[int, str]: + if not isinstance(arg, (int, str)): + raise TypeError + return arg + + +class Proto(Protocol): + def foo(self, a: int) -> int: ... + + def bar(self, b: str) -> str: ... + + def baz(self, c: bytes) -> str: ... + + +def dummy_two(): ... + + +@dummy +def dummy_three(): ... + + +def dummy_four(): ... + + +@overload +def b(arg: int) -> int: ... + + +@overload +def b(arg: str) -> str: ... + + +@overload +def b(arg: object) -> NoReturn: ... + + +def b(arg: Union[int, str, object]) -> Union[int, str]: + if not isinstance(arg, (int, str)): + raise TypeError + return arg +``` + +## Black Output + +```python +from typing import NoReturn, Protocol, Union, overload + + +def dummy(a): ... +def other(b): ... + + +@overload +def a(arg: int) -> int: ... +@overload +def a(arg: str) -> str: ... +@overload +def a(arg: object) -> NoReturn: ... +def a(arg: Union[int, str, object]) -> Union[int, str]: + if not isinstance(arg, (int, str)): + raise TypeError + return arg + + +class Proto(Protocol): + def foo(self, a: int) -> int: ... + + def bar(self, b: str) -> str: ... + def baz(self, c: bytes) -> str: ... + + +def dummy_two(): ... +@dummy +def dummy_three(): ... + + +def dummy_four(): ... + + +@overload +def b(arg: int) -> int: ... + + +@overload +def b(arg: str) -> str: ... +@overload +def b(arg: object) -> NoReturn: ... + + +def b(arg: Union[int, str, object]) -> Union[int, str]: + if not isinstance(arg, (int, str)): + raise TypeError + return arg +``` + + diff --git a/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__preview_form_feeds.py.snap b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__preview_form_feeds.py.snap new file mode 100644 index 0000000000..ae94587cd4 --- /dev/null +++ b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__preview_form_feeds.py.snap @@ -0,0 +1,446 @@ +--- +source: crates/ruff_python_formatter/tests/fixtures.rs +input_file: crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_form_feeds.py +--- +## Input + +```python +# Warning! This file contains form feeds (ASCII 0x0C, often represented by \f or ^L). +# These may be invisible in your editor: ensure you can see them before making changes here. + +# There's one at the start that'll get stripped + +# Comment and statement processing is different enough that we'll test variations of both +# contexts here + +# + + +# + + +# + + + +# + + + +# + + + +# + + +# + + + +# + +# + +# + + \ +# +pass + +pass + + +pass + + +pass + + + +pass + + + +pass + + + +pass + + +pass + + + +pass + +pass + +pass + + +# form feed after a dedent +def foo(): + pass + +pass + + +# form feeds are prohibited inside blocks, or on a line with nonwhitespace + def bar( a = 1 ,b : bool = False ) : + + + pass + + +class Baz: + + def __init__(self): + pass + + + def something(self): + pass + + + +# +pass +pass # +a = 1 + # + pass + a = 1 + +a = [ + +] + +# as internal whitespace of a comment is allowed but why +"form feed literal in a string is okay " + +# form feeds at the very end get removed. +``` + +## Black Differences + +```diff +--- Black ++++ Ruff +@@ -5,62 +5,62 @@ + + # Comment and statement processing is different enough that we'll test variations of both + # contexts here +- ++ + # + +- ++ + # + +- ++ + # + +- ++ + # + +- ++ + # + +- ++ + # + +- ++ + # + +- ++ + # +- ++ + # +- ++ + # + + # + pass +- ++ + pass + +- ++ + pass + +- ++ + pass + +- ++ + pass + +- ++ + pass + +- ++ + pass + +- ++ + pass + +- ++ + pass +- ++ + pass +- ++ + pass + + +@@ -68,7 +68,7 @@ + def foo(): + pass + +- ++ + pass + + +@@ -84,7 +84,7 @@ + def something(self): + pass + +- ++ + # + pass + pass # +``` + +## Ruff Output + +```python +# Warning! This file contains form feeds (ASCII 0x0C, often represented by \f or ^L). +# These may be invisible in your editor: ensure you can see them before making changes here. + +# There's one at the start that'll get stripped + +# Comment and statement processing is different enough that we'll test variations of both +# contexts here + +# + + +# + + +# + + +# + + +# + + +# + + +# + + +# + +# + +# + +# +pass + +pass + + +pass + + +pass + + +pass + + +pass + + +pass + + +pass + + +pass + +pass + +pass + + +# form feed after a dedent +def foo(): + pass + + +pass + + +# form feeds are prohibited inside blocks, or on a line with nonwhitespace +def bar(a=1, b: bool = False): + pass + + +class Baz: + def __init__(self): + pass + + def something(self): + pass + + +# +pass +pass # +a = 1 +# +pass +a = 1 + +a = [] + +# as internal whitespace of a comment is allowed but why +"form feed literal in a string is okay " + +# form feeds at the very end get removed. +``` + +## Black Output + +```python +# Warning! This file contains form feeds (ASCII 0x0C, often represented by \f or ^L). +# These may be invisible in your editor: ensure you can see them before making changes here. + +# There's one at the start that'll get stripped + +# Comment and statement processing is different enough that we'll test variations of both +# contexts here + +# + + +# + + +# + + +# + + +# + + +# + + +# + + +# + +# + +# + +# +pass + +pass + + +pass + + +pass + + +pass + + +pass + + +pass + + +pass + + +pass + +pass + +pass + + +# form feed after a dedent +def foo(): + pass + + +pass + + +# form feeds are prohibited inside blocks, or on a line with nonwhitespace +def bar(a=1, b: bool = False): + pass + + +class Baz: + def __init__(self): + pass + + def something(self): + pass + + +# +pass +pass # +a = 1 +# +pass +a = 1 + +a = [] + +# as internal whitespace of a comment is allowed but why +"form feed literal in a string is okay " + +# form feeds at the very end get removed. +``` + + diff --git a/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__preview_format_unicode_escape_seq.py.snap b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__preview_format_unicode_escape_seq.py.snap new file mode 100644 index 0000000000..c523ecc557 --- /dev/null +++ b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__preview_format_unicode_escape_seq.py.snap @@ -0,0 +1,97 @@ +--- +source: crates/ruff_python_formatter/tests/fixtures.rs +input_file: crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_format_unicode_escape_seq.py +--- +## Input + +```python +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" +``` + +## Black Differences + +```diff +--- Black ++++ Ruff +@@ -1,15 +1,15 @@ +-x = "\x1f" ++x = "\x1F" + x = "\\x1B" +-x = "\\\x1b" +-x = "\U0001f60e" ++x = "\\\x1B" ++x = "\U0001F60E" + x = "\u0001F60E" + x = r"\u0001F60E" + x = "don't format me" +-x = "\xa3" ++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 = "\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" +``` + +## Ruff Output + +```python +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" +``` + +## Black Output + +```python +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/tests/snapshots/black_compatibility@cases__preview_hug_parens_with_braces_and_square_brackets.py.snap b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__preview_hug_parens_with_braces_and_square_brackets.py.snap new file mode 100644 index 0000000000..d9a87a05c5 --- /dev/null +++ b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__preview_hug_parens_with_braces_and_square_brackets.py.snap @@ -0,0 +1,1212 @@ +--- +source: crates/ruff_python_formatter/tests/fixtures.rs +input_file: crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_hug_parens_with_braces_and_square_brackets.py +--- +## Input + +```python +def foo_brackets(request): + return JsonResponse( + { + "var_1": foo, + "var_2": bar, + } + ) + +def foo_square_brackets(request): + return JsonResponse( + [ + "var_1", + "var_2", + ] + ) + +func({"a": 37, "b": 42, "c": 927, "aaaaaaaaaaaaaaaaaaaaaaaaa": 11111111111111111111111111111111111111111}) + +func(["random_string_number_one","random_string_number_two","random_string_number_three","random_string_number_four"]) + +func( + { + # expand me + 'a':37, + 'b':42, + 'c':927 + } +) + +func( + [ + 'a', + 'b', + 'c', + ] +) + +func( + [ + 'a', + 'b', + 'c', + ], +) + +func( # a + [ # b + "c", # c + "d", # d + "e", # e + ] # f +) # g + +func( # a + { # b + "c": 1, # c + "d": 2, # d + "e": 3, # e + } # f +) # g + +func( + # preserve me + [ + "c", + "d", + "e", + ] +) + +func( + [ # preserve me but hug brackets + "c", + "d", + "e", + ] +) + +func( + [ + # preserve me but hug brackets + "c", + "d", + "e", + ] +) + +func( + [ + "c", + # preserve me but hug brackets + "d", + "e", + ] +) + +func( + [ + "c", + "d", + "e", + # preserve me but hug brackets + ] +) + +func( + [ + "c", + "d", + "e", + ] # preserve me but hug brackets +) + +func( + [ + "c", + "d", + "e", + ] + # preserve me +) + +func([x for x in "short line"]) +func([x for x in "long line long line long line long line long line long line long line"]) +func([x for x in [x for x in "long line long line long line long line long line long line long line"]]) + +func({"short line"}) +func({"long line", "long long line", "long long long line", "long long long long line", "long long long long long line"}) +func({{"long line", "long long line", "long long long line", "long long long long line", "long long long long long line"}}) +func(("long line", "long long line", "long long long line", "long long long long line", "long long long long long line")) +func((("long line", "long long line", "long long long line", "long long long long line", "long long long long long line"))) +func([["long line", "long long line", "long long long line", "long long long long line", "long long long long long line"]]) + +# Do not hug if the argument fits on a single line. +func({"fit line", "fit line", "fit line", "fit line", "fit line", "fit line", "fit line"}) +func(("fit line", "fit line", "fit line", "fit line", "fit line", "fit line", "fit line")) +func(["fit line", "fit line", "fit line", "fit line", "fit line", "fit line", "fit line"]) +func(**{"fit line", "fit line", "fit line", "fit line", "fit line", "fit line", "fit---"}) +func(*("fit line", "fit line", "fit line", "fit line", "fit line", "fit line", "fit----")) +array = [{"fit line", "fit line", "fit line", "fit line", "fit line", "fit line", "fit line"}] +array = [("fit line", "fit line", "fit line", "fit line", "fit line", "fit line", "fit line")] +array = [["fit line", "fit line", "fit line", "fit line", "fit line", "fit line", "fit line"]] + +foooooooooooooooooooo( + [{c: n + 1 for c in range(256)} for n in range(100)] + [{}], {size} +) + +baaaaaaaaaaaaar( + [1, 2, 3, 4, 5, 6, 7, 8, 9, 10], {x}, "a string", [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] +) + +nested_mapping = {"key": [{"a very long key 1": "with a very long value", "a very long key 2": "with a very long value"}]} +nested_array = [[["long line", "long long line", "long long long line", "long long long long line", "long long long long long line"]]] +explicit_exploding = [[["short", "line",],],] +single_item_do_not_explode = Context({ + "version": get_docs_version(), +}) + +foo(*["long long long long long line", "long long long long long line", "long long long long long line"]) + +foo(*[str(i) for i in range(100000000000000000000000000000000000000000000000000000000000)]) + +foo( + **{ + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa": 1, + "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb": 2, + "ccccccccccccccccccccccccccccccccc": 3, + **other, + } +) + +foo(**{x: y for x, y in enumerate(["long long long long line","long long long long line"])}) + +# Edge case when deciding whether to hug the brackets without inner content. +very_very_very_long_variable = very_very_very_long_module.VeryVeryVeryVeryLongClassName([[]]) + +for foo in ["a", "b"]: + output.extend([ + individual + for + # Foobar + container in xs_by_y[foo] + # Foobar + for individual in container["nested"] + ]) +``` + +## Black Differences + +```diff +--- Black ++++ Ruff +@@ -1,43 +1,55 @@ + def foo_brackets(request): +- return JsonResponse({ +- "var_1": foo, +- "var_2": bar, +- }) ++ return JsonResponse( ++ { ++ "var_1": foo, ++ "var_2": bar, ++ } ++ ) + + + def foo_square_brackets(request): +- return JsonResponse([ +- "var_1", +- "var_2", +- ]) ++ return JsonResponse( ++ [ ++ "var_1", ++ "var_2", ++ ] ++ ) + + +-func({ +- "a": 37, +- "b": 42, +- "c": 927, +- "aaaaaaaaaaaaaaaaaaaaaaaaa": 11111111111111111111111111111111111111111, +-}) ++func( ++ { ++ "a": 37, ++ "b": 42, ++ "c": 927, ++ "aaaaaaaaaaaaaaaaaaaaaaaaa": 11111111111111111111111111111111111111111, ++ } ++) + +-func([ +- "random_string_number_one", +- "random_string_number_two", +- "random_string_number_three", +- "random_string_number_four", +-]) ++func( ++ [ ++ "random_string_number_one", ++ "random_string_number_two", ++ "random_string_number_three", ++ "random_string_number_four", ++ ] ++) + +-func({ +- # expand me +- "a": 37, +- "b": 42, +- "c": 927, +-}) ++func( ++ { ++ # expand me ++ "a": 37, ++ "b": 42, ++ "c": 927, ++ } ++) + +-func([ +- "a", +- "b", +- "c", +-]) ++func( ++ [ ++ "a", ++ "b", ++ "c", ++ ] ++) + + func( + [ +@@ -47,17 +59,21 @@ + ], + ) + +-func([ # a # b +- "c", # c +- "d", # d +- "e", # e +-]) # f # g ++func( # a ++ [ # b ++ "c", # c ++ "d", # d ++ "e", # e ++ ] # f ++) # g + +-func({ # a # b +- "c": 1, # c +- "d": 2, # d +- "e": 3, # e +-}) # f # g ++func( # a ++ { # b ++ "c": 1, # c ++ "d": 2, # d ++ "e": 3, # e ++ } # f ++) # g + + func( + # preserve me +@@ -68,38 +84,48 @@ + ] + ) + +-func([ # preserve me but hug brackets +- "c", +- "d", +- "e", +-]) ++func( ++ [ # preserve me but hug brackets ++ "c", ++ "d", ++ "e", ++ ] ++) + +-func([ +- # preserve me but hug brackets +- "c", +- "d", +- "e", +-]) ++func( ++ [ ++ # preserve me but hug brackets ++ "c", ++ "d", ++ "e", ++ ] ++) + +-func([ +- "c", +- # preserve me but hug brackets +- "d", +- "e", +-]) ++func( ++ [ ++ "c", ++ # preserve me but hug brackets ++ "d", ++ "e", ++ ] ++) + +-func([ +- "c", +- "d", +- "e", +- # preserve me but hug brackets +-]) ++func( ++ [ ++ "c", ++ "d", ++ "e", ++ # preserve me but hug brackets ++ ] ++) + +-func([ +- "c", +- "d", +- "e", +-]) # preserve me but hug brackets ++func( ++ [ ++ "c", ++ "d", ++ "e", ++ ] # preserve me but hug brackets ++) + + func( + [ +@@ -114,50 +140,68 @@ + func( + [x for x in "long line long line long line long line long line long line long line"] + ) +-func([ +- x +- for x in [ ++func( ++ [ + x +- for x in "long line long line long line long line long line long line long line" ++ for x in [ ++ x ++ for x in "long line long line long line long line long line long line long line" ++ ] + ] +-]) ++) + + func({"short line"}) +-func({ +- "long line", +- "long long line", +- "long long long line", +- "long long long long line", +- "long long long long long line", +-}) +-func({{ +- "long line", +- "long long line", +- "long long long line", +- "long long long long line", +- "long long long long long line", +-}}) +-func(( +- "long line", +- "long long line", +- "long long long line", +- "long long long long line", +- "long long long long long line", +-)) +-func((( +- "long line", +- "long long line", +- "long long long line", +- "long long long long line", +- "long long long long long line", +-))) +-func([[ +- "long line", +- "long long line", +- "long long long line", +- "long long long long line", +- "long long long long long line", +-]]) ++func( ++ { ++ "long line", ++ "long long line", ++ "long long long line", ++ "long long long long line", ++ "long long long long long line", ++ } ++) ++func( ++ { ++ { ++ "long line", ++ "long long line", ++ "long long long line", ++ "long long long long line", ++ "long long long long long line", ++ } ++ } ++) ++func( ++ ( ++ "long line", ++ "long long line", ++ "long long long line", ++ "long long long long line", ++ "long long long long long line", ++ ) ++) ++func( ++ ( ++ ( ++ "long line", ++ "long long line", ++ "long long long line", ++ "long long long long line", ++ "long long long long long line", ++ ) ++ ) ++) ++func( ++ [ ++ [ ++ "long line", ++ "long long line", ++ "long long long line", ++ "long long long long line", ++ "long long long long long line", ++ ] ++ ] ++) + + # Do not hug if the argument fits on a single line. + func( +@@ -194,18 +238,24 @@ + ) + + nested_mapping = { +- "key": [{ +- "a very long key 1": "with a very long value", +- "a very long key 2": "with a very long value", +- }] ++ "key": [ ++ { ++ "a very long key 1": "with a very long value", ++ "a very long key 2": "with a very long value", ++ } ++ ] + } +-nested_array = [[[ +- "long line", +- "long long line", +- "long long long line", +- "long long long long line", +- "long long long long long line", +-]]] ++nested_array = [ ++ [ ++ [ ++ "long line", ++ "long long line", ++ "long long long line", ++ "long long long long line", ++ "long long long long long line", ++ ] ++ ] ++] + explicit_exploding = [ + [ + [ +@@ -214,30 +264,42 @@ + ], + ], + ] +-single_item_do_not_explode = Context({ +- "version": get_docs_version(), +-}) ++single_item_do_not_explode = Context( ++ { ++ "version": get_docs_version(), ++ } ++) + +-foo(*[ +- "long long long long long line", +- "long long long long long line", +- "long long long long long line", +-]) ++foo( ++ *[ ++ "long long long long long line", ++ "long long long long long line", ++ "long long long long long line", ++ ] ++) + +-foo(*[ +- str(i) for i in range(100000000000000000000000000000000000000000000000000000000000) +-]) ++foo( ++ *[ ++ str(i) ++ for i in range(100000000000000000000000000000000000000000000000000000000000) ++ ] ++) + +-foo(**{ +- "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa": 1, +- "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb": 2, +- "ccccccccccccccccccccccccccccccccc": 3, +- **other, +-}) ++foo( ++ **{ ++ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa": 1, ++ "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb": 2, ++ "ccccccccccccccccccccccccccccccccc": 3, ++ **other, ++ } ++) + +-foo(**{ +- x: y for x, y in enumerate(["long long long long line", "long long long long line"]) +-}) ++foo( ++ **{ ++ x: y ++ for x, y in enumerate(["long long long long line", "long long long long line"]) ++ } ++) + + # Edge case when deciding whether to hug the brackets without inner content. + very_very_very_long_variable = very_very_very_long_module.VeryVeryVeryVeryLongClassName( +@@ -245,11 +307,13 @@ + ) + + for foo in ["a", "b"]: +- output.extend([ +- individual +- for +- # Foobar +- container in xs_by_y[foo] +- # Foobar +- for individual in container["nested"] +- ]) ++ output.extend( ++ [ ++ individual ++ for ++ # Foobar ++ container in xs_by_y[foo] ++ # Foobar ++ for individual in container["nested"] ++ ] ++ ) +``` + +## Ruff Output + +```python +def foo_brackets(request): + return JsonResponse( + { + "var_1": foo, + "var_2": bar, + } + ) + + +def foo_square_brackets(request): + return JsonResponse( + [ + "var_1", + "var_2", + ] + ) + + +func( + { + "a": 37, + "b": 42, + "c": 927, + "aaaaaaaaaaaaaaaaaaaaaaaaa": 11111111111111111111111111111111111111111, + } +) + +func( + [ + "random_string_number_one", + "random_string_number_two", + "random_string_number_three", + "random_string_number_four", + ] +) + +func( + { + # expand me + "a": 37, + "b": 42, + "c": 927, + } +) + +func( + [ + "a", + "b", + "c", + ] +) + +func( + [ + "a", + "b", + "c", + ], +) + +func( # a + [ # b + "c", # c + "d", # d + "e", # e + ] # f +) # g + +func( # a + { # b + "c": 1, # c + "d": 2, # d + "e": 3, # e + } # f +) # g + +func( + # preserve me + [ + "c", + "d", + "e", + ] +) + +func( + [ # preserve me but hug brackets + "c", + "d", + "e", + ] +) + +func( + [ + # preserve me but hug brackets + "c", + "d", + "e", + ] +) + +func( + [ + "c", + # preserve me but hug brackets + "d", + "e", + ] +) + +func( + [ + "c", + "d", + "e", + # preserve me but hug brackets + ] +) + +func( + [ + "c", + "d", + "e", + ] # preserve me but hug brackets +) + +func( + [ + "c", + "d", + "e", + ] + # preserve me +) + +func([x for x in "short line"]) +func( + [x for x in "long line long line long line long line long line long line long line"] +) +func( + [ + x + for x in [ + x + for x in "long line long line long line long line long line long line long line" + ] + ] +) + +func({"short line"}) +func( + { + "long line", + "long long line", + "long long long line", + "long long long long line", + "long long long long long line", + } +) +func( + { + { + "long line", + "long long line", + "long long long line", + "long long long long line", + "long long long long long line", + } + } +) +func( + ( + "long line", + "long long line", + "long long long line", + "long long long long line", + "long long long long long line", + ) +) +func( + ( + ( + "long line", + "long long line", + "long long long line", + "long long long long line", + "long long long long long line", + ) + ) +) +func( + [ + [ + "long line", + "long long line", + "long long long line", + "long long long long line", + "long long long long long line", + ] + ] +) + +# Do not hug if the argument fits on a single line. +func( + {"fit line", "fit line", "fit line", "fit line", "fit line", "fit line", "fit line"} +) +func( + ("fit line", "fit line", "fit line", "fit line", "fit line", "fit line", "fit line") +) +func( + ["fit line", "fit line", "fit line", "fit line", "fit line", "fit line", "fit line"] +) +func( + **{"fit line", "fit line", "fit line", "fit line", "fit line", "fit line", "fit---"} +) +func( + *("fit line", "fit line", "fit line", "fit line", "fit line", "fit line", "fit----") +) +array = [ + {"fit line", "fit line", "fit line", "fit line", "fit line", "fit line", "fit line"} +] +array = [ + ("fit line", "fit line", "fit line", "fit line", "fit line", "fit line", "fit line") +] +array = [ + ["fit line", "fit line", "fit line", "fit line", "fit line", "fit line", "fit line"] +] + +foooooooooooooooooooo( + [{c: n + 1 for c in range(256)} for n in range(100)] + [{}], {size} +) + +baaaaaaaaaaaaar( + [1, 2, 3, 4, 5, 6, 7, 8, 9, 10], {x}, "a string", [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] +) + +nested_mapping = { + "key": [ + { + "a very long key 1": "with a very long value", + "a very long key 2": "with a very long value", + } + ] +} +nested_array = [ + [ + [ + "long line", + "long long line", + "long long long line", + "long long long long line", + "long long long long long line", + ] + ] +] +explicit_exploding = [ + [ + [ + "short", + "line", + ], + ], +] +single_item_do_not_explode = Context( + { + "version": get_docs_version(), + } +) + +foo( + *[ + "long long long long long line", + "long long long long long line", + "long long long long long line", + ] +) + +foo( + *[ + str(i) + for i in range(100000000000000000000000000000000000000000000000000000000000) + ] +) + +foo( + **{ + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa": 1, + "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb": 2, + "ccccccccccccccccccccccccccccccccc": 3, + **other, + } +) + +foo( + **{ + x: y + for x, y in enumerate(["long long long long line", "long long long long line"]) + } +) + +# Edge case when deciding whether to hug the brackets without inner content. +very_very_very_long_variable = very_very_very_long_module.VeryVeryVeryVeryLongClassName( + [[]] +) + +for foo in ["a", "b"]: + output.extend( + [ + individual + for + # Foobar + container in xs_by_y[foo] + # Foobar + for individual in container["nested"] + ] + ) +``` + +## Black Output + +```python +def foo_brackets(request): + return JsonResponse({ + "var_1": foo, + "var_2": bar, + }) + + +def foo_square_brackets(request): + return JsonResponse([ + "var_1", + "var_2", + ]) + + +func({ + "a": 37, + "b": 42, + "c": 927, + "aaaaaaaaaaaaaaaaaaaaaaaaa": 11111111111111111111111111111111111111111, +}) + +func([ + "random_string_number_one", + "random_string_number_two", + "random_string_number_three", + "random_string_number_four", +]) + +func({ + # expand me + "a": 37, + "b": 42, + "c": 927, +}) + +func([ + "a", + "b", + "c", +]) + +func( + [ + "a", + "b", + "c", + ], +) + +func([ # a # b + "c", # c + "d", # d + "e", # e +]) # f # g + +func({ # a # b + "c": 1, # c + "d": 2, # d + "e": 3, # e +}) # f # g + +func( + # preserve me + [ + "c", + "d", + "e", + ] +) + +func([ # preserve me but hug brackets + "c", + "d", + "e", +]) + +func([ + # preserve me but hug brackets + "c", + "d", + "e", +]) + +func([ + "c", + # preserve me but hug brackets + "d", + "e", +]) + +func([ + "c", + "d", + "e", + # preserve me but hug brackets +]) + +func([ + "c", + "d", + "e", +]) # preserve me but hug brackets + +func( + [ + "c", + "d", + "e", + ] + # preserve me +) + +func([x for x in "short line"]) +func( + [x for x in "long line long line long line long line long line long line long line"] +) +func([ + x + for x in [ + x + for x in "long line long line long line long line long line long line long line" + ] +]) + +func({"short line"}) +func({ + "long line", + "long long line", + "long long long line", + "long long long long line", + "long long long long long line", +}) +func({{ + "long line", + "long long line", + "long long long line", + "long long long long line", + "long long long long long line", +}}) +func(( + "long line", + "long long line", + "long long long line", + "long long long long line", + "long long long long long line", +)) +func((( + "long line", + "long long line", + "long long long line", + "long long long long line", + "long long long long long line", +))) +func([[ + "long line", + "long long line", + "long long long line", + "long long long long line", + "long long long long long line", +]]) + +# Do not hug if the argument fits on a single line. +func( + {"fit line", "fit line", "fit line", "fit line", "fit line", "fit line", "fit line"} +) +func( + ("fit line", "fit line", "fit line", "fit line", "fit line", "fit line", "fit line") +) +func( + ["fit line", "fit line", "fit line", "fit line", "fit line", "fit line", "fit line"] +) +func( + **{"fit line", "fit line", "fit line", "fit line", "fit line", "fit line", "fit---"} +) +func( + *("fit line", "fit line", "fit line", "fit line", "fit line", "fit line", "fit----") +) +array = [ + {"fit line", "fit line", "fit line", "fit line", "fit line", "fit line", "fit line"} +] +array = [ + ("fit line", "fit line", "fit line", "fit line", "fit line", "fit line", "fit line") +] +array = [ + ["fit line", "fit line", "fit line", "fit line", "fit line", "fit line", "fit line"] +] + +foooooooooooooooooooo( + [{c: n + 1 for c in range(256)} for n in range(100)] + [{}], {size} +) + +baaaaaaaaaaaaar( + [1, 2, 3, 4, 5, 6, 7, 8, 9, 10], {x}, "a string", [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] +) + +nested_mapping = { + "key": [{ + "a very long key 1": "with a very long value", + "a very long key 2": "with a very long value", + }] +} +nested_array = [[[ + "long line", + "long long line", + "long long long line", + "long long long long line", + "long long long long long line", +]]] +explicit_exploding = [ + [ + [ + "short", + "line", + ], + ], +] +single_item_do_not_explode = Context({ + "version": get_docs_version(), +}) + +foo(*[ + "long long long long long line", + "long long long long long line", + "long long long long long line", +]) + +foo(*[ + str(i) for i in range(100000000000000000000000000000000000000000000000000000000000) +]) + +foo(**{ + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa": 1, + "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb": 2, + "ccccccccccccccccccccccccccccccccc": 3, + **other, +}) + +foo(**{ + x: y for x, y in enumerate(["long long long long line", "long long long long line"]) +}) + +# Edge case when deciding whether to hug the brackets without inner content. +very_very_very_long_variable = very_very_very_long_module.VeryVeryVeryVeryLongClassName( + [[]] +) + +for foo in ["a", "b"]: + output.extend([ + individual + for + # Foobar + container in xs_by_y[foo] + # Foobar + for individual in container["nested"] + ]) +``` + + 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 new file mode 100644 index 0000000000..d60d394b85 --- /dev/null +++ b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__preview_long_dict_values.py.snap @@ -0,0 +1,201 @@ +--- +source: crates/ruff_python_formatter/tests/fixtures.rs +input_file: crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_long_dict_values.py +--- +## Input + +```python +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", +} + +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': + xxxxxxxxxxxxxxxxxxx.xxxxxxxxxxxxxx( + xxxxxxxxxxxxxx={ + 'x': + xxxxxxxxxxxxxxxxxxxxxxxxxx.xxxxxxxxxxxxxxxxxxxxxxxxxxxxx( + xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx=( + xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx + .xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx( + xxxxxxxxxxxxx=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx + .xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx( + xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx={ + 'x': x.xx, + 'x': x.x, + })))) + }), +} +``` + +## Black Differences + +```diff +--- Black ++++ Ruff +@@ -1,32 +1,26 @@ + 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" +- ), ++ "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", + } + + my_dict = { +- "a key in my dict": ( +- a_very_long_variable * and_a_very_long_function_call() / 100000.0 +- ) ++ "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 +- ) ++ "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") +- ) ++ "a key in my dict": MyClass.some_attribute.first_call() ++ .second_call() ++ .third_call(some_args="some value") + } + + { +``` + +## Ruff Output + +```python +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", +} + +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": xxxxxxxxxxxxxxxxxxx.xxxxxxxxxxxxxx( + xxxxxxxxxxxxxx={ + "x": xxxxxxxxxxxxxxxxxxxxxxxxxx.xxxxxxxxxxxxxxxxxxxxxxxxxxxxx( + xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx=( + xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx.xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx( + xxxxxxxxxxxxx=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx.xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx( + xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx={ + "x": x.xx, + "x": x.x, + } + ) + ) + ) + ) + } + ), +} +``` + +## Black Output + +```python +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" + ), +} + +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": xxxxxxxxxxxxxxxxxxx.xxxxxxxxxxxxxx( + xxxxxxxxxxxxxx={ + "x": xxxxxxxxxxxxxxxxxxxxxxxxxx.xxxxxxxxxxxxxxxxxxxxxxxxxxxxx( + xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx=( + xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx.xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx( + xxxxxxxxxxxxx=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx.xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx( + xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx={ + "x": x.xx, + "x": x.x, + } + ) + ) + ) + ) + } + ), +} +``` + + 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 new file mode 100644 index 0000000000..b3a03820fa --- /dev/null +++ b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__preview_long_strings.py.snap @@ -0,0 +1,2038 @@ +--- +source: crates/ruff_python_formatter/tests/fixtures.rs +input_file: crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_long_strings.py +--- +## Input + +```python +x = "This is a really long string that can't possibly be expected to fit all together on one line. In fact it may even take up three or more lines... like four or five... but probably just three." + +x += "This is a really long string that can't possibly be expected to fit all together on one line. In fact it may even take up three or more lines... like four or five... but probably just three." + +y = ( + 'Short string' +) + +print('This is a really long string inside of a print statement with extra arguments attached at the end of it.', x, y, z) + +print("This is a really long string inside of a print statement with no extra arguments attached at the end of it.") + +D1 = {"The First": "This is a really long string that can't possibly be expected to fit all together on one line. Also it is inside a dictionary, so formatting is more difficult.", "The Second": "This is another really really (not really) long string that also can't be expected to fit on one line and is, like the other string, inside a dictionary."} + +D2 = {1.0: "This is a really long string that can't possibly be expected to fit all together on one line. Also it is inside a dictionary, so formatting is more difficult.", 2.0: "This is another really really (not really) long string that also can't be expected to fit on one line and is, like the other string, inside a dictionary."} + +D3 = {x: "This is a really long string that can't possibly be expected to fit all together on one line. Also it is inside a dictionary, so formatting is more difficult.", y: "This is another really really (not really) long string that also can't be expected to fit on one line and is, like the other string, inside a dictionary."} + +D4 = {"A long and ridiculous {}".format(string_key): "This is a really really really long string that has to go i,side of a dictionary. It is soooo bad.", some_func("calling", "some", "stuff"): "This is a really really really long string that has to go inside of a dictionary. It is {soooo} bad (#{x}).".format(sooo="soooo", x=2), "A %s %s" % ("formatted", "string"): "This is a really really really long string that has to go inside of a dictionary. It is %s bad (#%d)." % ("soooo", 2)} + +D5 = { # Test for https://github.com/psf/black/issues/3261 + ("This is a really long string that can't be expected to fit in one line and is used as a nested dict's key"): {"inner": "value"}, +} + +D6 = { # Test for https://github.com/psf/black/issues/3261 + ("This is a really long string that can't be expected to fit in one line and is used as a dict's key"): ["value1", "value2"], +} + +L1 = ["The is a short string", "This is a really long string that can't possibly be expected to fit all together on one line. Also it is inside a list literal, so it's expected to be wrapped in parens when splitting to avoid implicit str concatenation.", short_call("arg", {"key": "value"}), "This is another really really (not really) long string that also can't be expected to fit on one line and is, like the other string, inside a list literal.", ("parens should be stripped for short string in list")] + +L2 = ["This is a really long string that can't be expected to fit in one line and is the only child of a list literal."] + +S1 = {"The is a short string", "This is a really long string that can't possibly be expected to fit all together on one line. Also it is inside a set literal, so it's expected to be wrapped in parens when splitting to avoid implicit str concatenation.", short_call("arg", {"key": "value"}), "This is another really really (not really) long string that also can't be expected to fit on one line and is, like the other string, inside a set literal.", ("parens should be stripped for short string in set")} + +S2 = {"This is a really long string that can't be expected to fit in one line and is the only child of a set literal."} + +T1 = ("The is a short string", "This is a really long string that can't possibly be expected to fit all together on one line. Also it is inside a tuple literal, so it's expected to be wrapped in parens when splitting to avoid implicit str concatenation.", short_call("arg", {"key": "value"}), "This is another really really (not really) long string that also can't be expected to fit on one line and is, like the other string, inside a tuple literal.", ("parens should be stripped for short string in list")) + +T2 = ("This is a really long string that can't be expected to fit in one line and is the only child of a tuple literal.",) + +func_with_keywords(my_arg, my_kwarg="Long keyword strings also need to be wrapped, but they will probably need to be handled a little bit differently.") + +bad_split1 = ( + 'But what should happen when code has already been formatted but in the wrong way? Like' + " with a space at the end instead of the beginning. Or what about when it is split too soon?" +) + +bad_split2 = "But what should happen when code has already " \ + "been formatted but in the wrong way? Like " \ + "with a space at the end instead of the " \ + "beginning. Or what about when it is split too " \ + "soon? In the case of a split that is too " \ + "short, black will try to honer the custom " \ + "split." + +bad_split3 = ( + "What if we have inline comments on " # First Comment + "each line of a bad split? In that " # Second Comment + "case, we should just leave it alone." # Third Comment +) + +bad_split_func1( + "But what should happen when code has already " + "been formatted but in the wrong way? Like " + "with a space at the end instead of the " + "beginning. Or what about when it is split too " + "soon? In the case of a split that is too " + "short, black will try to honer the custom " + "split.", + xxx, yyy, zzz +) + +bad_split_func2( + xxx, yyy, zzz, + long_string_kwarg="But what should happen when code has already been formatted but in the wrong way? Like " + "with a space at the end instead of the beginning. Or what about when it is split too " + "soon?", +) + +bad_split_func3( + ( + "But what should happen when code has already " + r"been formatted but in the wrong way? Like " + "with a space at the end instead of the " + r"beginning. Or what about when it is split too " + r"soon? In the case of a split that is too " + "short, black will try to honer the custom " + "split." + ), + xxx, + yyy, + zzz, +) + +inline_comments_func1( + "if there are inline " + "comments in the middle " + # Here is the standard alone comment. + "of the implicitly concatenated " + "string, we should handle " + "them correctly", + xxx, +) + +inline_comments_func2( + "what if the string is very very very very very very very very very very long and this part does " + "not fit into a single line? " + # Here is the standard alone comment. + "then the string should still be properly handled by merging and splitting " + "it into parts that fit in line length.", + xxx, +) + +raw_string = r"This is a long raw string. When re-formatting this string, black needs to make sure it prepends the 'r' onto the new string." + +fmt_string1 = "We also need to be sure to preserve any and all {} which may or may not be attached to the string in question.".format("method calls") + +fmt_string2 = "But what about when the string is {} but {}".format("short", "the method call is really really really really really really really really long?") + +old_fmt_string1 = "While we are on the topic of %s, we should also note that old-style formatting must also be preserved, since some %s still uses it." % ("formatting", "code") + +old_fmt_string2 = "This is a %s %s %s %s" % ("really really really really really", "old", "way to format strings!", "Use f-strings instead!") + +old_fmt_string3 = "Whereas only the strings after the percent sign were long in the last example, this example uses a long initial string as well. This is another %s %s %s %s" % ("really really really really really", "old", "way to format strings!", "Use f-strings instead!") + +fstring = f"f-strings definitely make things more {difficult} than they need to be for {{black}}. But boy they sure are handy. The problem is that some lines will need to have the 'f' whereas others do not. This {line}, for example, needs one." + +fstring_with_no_fexprs = f"Some regular string that needs to get split certainly but is NOT an fstring by any means whatsoever." + +comment_string = "Long lines with inline comments should have their comments appended to the reformatted string's enclosing right parentheses." # This comment gets thrown to the top. + +arg_comment_string = print("Long lines with inline comments which are apart of (and not the only member of) an argument list should have their comments appended to the reformatted string's enclosing left parentheses.", # This comment gets thrown to the top. + "Arg #2", "Arg #3", "Arg #4", "Arg #5") + +pragma_comment_string1 = "Lines which end with an inline pragma comment of the form `# : <...>` should be left alone." # noqa: E501 + +pragma_comment_string2 = "Lines which end with an inline pragma comment of the form `# : <...>` should be left alone." # noqa + +"""This is a really really really long triple quote string and it should not be touched.""" + +triple_quote_string = """This is a really really really long triple quote string assignment and it should not be touched.""" + +assert some_type_of_boolean_expression, "Followed by a really really really long string that is used to provide context to the AssertionError exception." + +assert some_type_of_boolean_expression, "Followed by a really really really long string that is used to provide context to the AssertionError exception, which uses dynamic string {}.".format("formatting") + +assert some_type_of_boolean_expression, "Followed by a really really really long string that is used to provide context to the AssertionError exception, which uses dynamic string %s." % "formatting" + +assert some_type_of_boolean_expression, "Followed by a really really really long string that is used to provide context to the AssertionError exception, which uses dynamic %s %s." % ("string", "formatting") + +some_function_call("With a reallly generic name and with a really really long string that is, at some point down the line, " + added + " to a variable and then added to another string.") + +some_function_call("With a reallly generic name and with a really really long string that is, at some point down the line, " + added + " to a variable and then added to another string. But then what happens when the final string is also supppppperrrrr long?! Well then that second (realllllllly long) string should be split too.", "and a second argument", and_a_third) + +return "A really really really really really really really really really really really really really long {} {}".format("return", "value") + +func_with_bad_comma( + "This is a really long string argument to a function that has a trailing comma which should NOT be there.", +) + +func_with_bad_comma( + "This is a really long string argument to a function that has a trailing comma which should NOT be there.", # comment after comma +) + +func_with_bad_comma( + ( + "This is a really long string argument to a function that has a trailing comma" + " which should NOT be there." + ), +) + +func_with_bad_comma( + ( + "This is a really long string argument to a function that has a trailing comma" + " which should NOT be there." + ), # comment after comma +) + +func_with_bad_parens_that_wont_fit_in_one_line( + ("short string that should have parens stripped"), + x, + y, + z +) + +func_with_bad_parens_that_wont_fit_in_one_line( + x, + y, + ("short string that should have parens stripped"), + z +) + +func_with_bad_parens( + ("short string that should have parens stripped"), + x, + y, + z, +) + +func_with_bad_parens( + x, + y, + ("short string that should have parens stripped"), + z, +) + +annotated_variable: Final = "This is a large " + STRING + " that has been " + CONCATENATED + "using the '+' operator." +annotated_variable: Final = "This is a large string that has a type annotation attached to it. A type annotation should NOT stop a long string from being wrapped." +annotated_variable: Literal["fakse_literal"] = "This is a large string that has a type annotation attached to it. A type annotation should NOT stop a long string from being wrapped." + +backslashes = "This is a really long string with \"embedded\" double quotes and 'single' quotes that also handles checking for an even number of backslashes \\" +backslashes = "This is a really long string with \"embedded\" double quotes and 'single' quotes that also handles checking for an even number of backslashes \\\\" +backslashes = "This is a really 'long' string with \"embedded double quotes\" and 'single' quotes that also handles checking for an odd number of backslashes \\\", like this...\\\\\\" + +short_string = ( + "Hi" + " there." +) + +func_call( + short_string=( + "Hi" + " there." + ) +) + +raw_strings = r"Don't" " get" r" merged" " unless they are all raw." + +def foo(): + yield "This is a really long string that can't possibly be expected to fit all together on one line. In fact it may even take up three or more lines... like four or five... but probably just three." + +x = f"This is a {{really}} long string that needs to be split without a doubt (i.e. most definitely). In short, this {string} that can't possibly be {{expected}} to fit all together on one line. In {fact} it may even take up three or more lines... like four or five... but probably just four." + +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 + " of it." +) + +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" # noqa + " of it." +) + +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" # pylint: disable=some-pylint-check + " of it." +) + +string_with_nameescape = ( + "........................................................................ \N{LAO KO LA}" +) + +string_with_nameescape = ( + "........................................................................... \N{LAO KO LA}" +) + +string_with_nameescape = ( + "............................................................................ \N{LAO KO LA}" +) + +string_with_nameescape_and_escaped_backslash = ( + "...................................................................... \\\N{LAO KO LA}" +) + +string_with_nameescape_and_escaped_backslash = ( + "......................................................................... \\\N{LAO KO LA}" +) + +string_with_nameescape_and_escaped_backslash = ( + ".......................................................................... \\\N{LAO KO LA}" +) + +string_with_escaped_nameescape = ( + "........................................................................ \\N{LAO KO LA}" +) + +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" + +dict_with_lambda_values = { + "join": lambda j: ( + f"{j.__class__.__name__}({some_function_call(j.left)}, " + f"{some_function_call(j.right)})" + ), +} + +# Complex string concatenations with a method call in the middle. +code = ( + (" return [\n") + + ( + ", \n".join( + " (%r, self.%s, visitor.%s)" + % (attrname, attrname, visit_name) + for attrname, visit_name in names + ) + ) + + ("\n ]\n") +) + + +# Test case of an outer string' parens enclose an inner string's parens. +call(body=("%s %s" % ((",".join(items)), suffix))) + +log.info(f'Skipping: {desc["db_id"]=} {desc["ms_name"]} {money=} {dte=} {pos_share=} {desc["status"]=} {desc["exposure_max"]=}') + +log.info(f"Skipping: {desc['db_id']=} {desc['ms_name']} {money=} {dte=} {pos_share=} {desc['status']=} {desc['exposure_max']=}") + +log.info(f'Skipping: {desc["db_id"]} {foo("bar",x=123)} {"foo" != "bar"} {(x := "abc=")} {pos_share=} {desc["status"]} {desc["exposure_max"]}') + +log.info(f'Skipping: {desc["db_id"]} {desc["ms_name"]} {money=} {(x := "abc=")=} {pos_share=} {desc["status"]} {desc["exposure_max"]}') + +log.info(f'Skipping: {desc["db_id"]} {foo("bar",x=123)=} {money=} {dte=} {pos_share=} {desc["status"]} {desc["exposure_max"]}') + +log.info(f'Skipping: {foo("asdf")=} {desc["ms_name"]} {money=} {dte=} {pos_share=} {desc["status"]} {desc["exposure_max"]}') + +log.info(f'Skipping: {"a" == "b" == "c" == "d"} {desc["ms_name"]} {money=} {dte=} {pos_share=} {desc["status"]} {desc["exposure_max"]}') + +log.info(f'Skipping: {"a" == "b" == "c" == "d"=} {desc["ms_name"]} {money=} {dte=} {pos_share=} {desc["status"]} {desc["exposure_max"]}') + +log.info(f'Skipping: { longer_longer_longer_longer_longer_longer_name [ "db_id" ] [ "another_key" ] = : .3f }') + +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"]}''') + +log.info(f"""Skipping: {'a' == 'b'} {desc['ms_name']} {money=} {dte=} {pos_share=} {desc['status']} {desc['exposure_max']}""") +``` + +## Black Differences + +```diff +--- Black ++++ Ruff +@@ -1,175 +1,108 @@ +-x = ( +- "This is a really long string that can't possibly be expected to fit all together" +- " on one line. In fact it may even take up three or more lines... like four or" +- " five... but probably just three." +-) ++x = "This is a really long string that can't possibly be expected to fit all together on one line. In fact it may even take up three or more lines... like four or five... but probably just three." + +-x += ( +- "This is a really long string that can't possibly be expected to fit all together" +- " on one line. In fact it may even take up three or more lines... like four or" +- " five... but probably just three." +-) ++x += "This is a really long string that can't possibly be expected to fit all together on one line. In fact it may even take up three or more lines... like four or five... but probably just three." + + y = "Short string" + + print( +- "This is a really long string inside of a print statement with extra arguments" +- " attached at the end of it.", ++ "This is a really long string inside of a print statement with extra arguments attached at the end of it.", + x, + y, + z, + ) + + print( +- "This is a really long string inside of a print statement with no extra arguments" +- " attached at the end of it." ++ "This is a really long string inside of a print statement with no extra arguments attached at the end of it." + ) + + D1 = { +- "The First": ( +- "This is a really long string that can't possibly be expected to fit all" +- " together on one line. Also it is inside a dictionary, so formatting is more" +- " difficult." +- ), +- "The Second": ( +- "This is another really really (not really) long string that also can't be" +- " expected to fit on one line and is, like the other string, inside a" +- " dictionary." +- ), ++ "The First": "This is a really long string that can't possibly be expected to fit all together on one line. Also it is inside a dictionary, so formatting is more difficult.", ++ "The Second": "This is another really really (not really) long string that also can't be expected to fit on one line and is, like the other string, inside a dictionary.", + } + + D2 = { +- 1.0: ( +- "This is a really long string that can't possibly be expected to fit all" +- " together on one line. Also it is inside a dictionary, so formatting is more" +- " difficult." +- ), +- 2.0: ( +- "This is another really really (not really) long string that also can't be" +- " expected to fit on one line and is, like the other string, inside a" +- " dictionary." +- ), ++ 1.0: "This is a really long string that can't possibly be expected to fit all together on one line. Also it is inside a dictionary, so formatting is more difficult.", ++ 2.0: "This is another really really (not really) long string that also can't be expected to fit on one line and is, like the other string, inside a dictionary.", + } + + D3 = { +- x: ( +- "This is a really long string that can't possibly be expected to fit all" +- " together on one line. Also it is inside a dictionary, so formatting is more" +- " difficult." +- ), +- y: ( +- "This is another really really (not really) long string that also can't be" +- " expected to fit on one line and is, like the other string, inside a" +- " dictionary." +- ), ++ x: "This is a really long string that can't possibly be expected to fit all together on one line. Also it is inside a dictionary, so formatting is more difficult.", ++ y: "This is another really really (not really) long string that also can't be expected to fit on one line and is, like the other string, inside a dictionary.", + } + + D4 = { +- "A long and ridiculous {}".format(string_key): ( +- "This is a really really really long string that has to go i,side of a" +- " dictionary. It is soooo bad." +- ), +- some_func("calling", "some", "stuff"): ( +- "This is a really really really long string that has to go inside of a" +- " dictionary. It is {soooo} bad (#{x}).".format(sooo="soooo", x=2) ++ "A long and ridiculous {}".format( ++ string_key ++ ): "This is a really really really long string that has to go i,side of a dictionary. It is soooo bad.", ++ some_func( ++ "calling", "some", "stuff" ++ ): "This is a really really really long string that has to go inside of a dictionary. It is {soooo} bad (#{x}).".format( ++ sooo="soooo", x=2 + ), + "A %s %s" +- % ("formatted", "string"): ( +- "This is a really really really long string that has to go inside of a" +- " dictionary. It is %s bad (#%d)." % ("soooo", 2) +- ), ++ % ( ++ "formatted", ++ "string", ++ ): "This is a really really really long string that has to go inside of a dictionary. It is %s bad (#%d)." ++ % ("soooo", 2), + } + + D5 = { # Test for https://github.com/psf/black/issues/3261 +- "This is a really long string that can't be expected to fit in one line and is used as a nested dict's key": { +- "inner": "value" +- }, ++ ( ++ "This is a really long string that can't be expected to fit in one line and is used as a nested dict's key" ++ ): {"inner": "value"}, + } + + D6 = { # Test for https://github.com/psf/black/issues/3261 +- "This is a really long string that can't be expected to fit in one line and is used as a dict's key": [ +- "value1", +- "value2", +- ], ++ ( ++ "This is a really long string that can't be expected to fit in one line and is used as a dict's key" ++ ): ["value1", "value2"], + } + + L1 = [ + "The is a short string", +- ( +- "This is a really long string that can't possibly be expected to fit all" +- " together on one line. Also it is inside a list literal, so it's expected to" +- " be wrapped in parens when splitting to avoid implicit str concatenation." +- ), ++ "This is a really long string that can't possibly be expected to fit all together on one line. Also it is inside a list literal, so it's expected to be wrapped in parens when splitting to avoid implicit str concatenation.", + short_call("arg", {"key": "value"}), +- ( +- "This is another really really (not really) long string that also can't be" +- " expected to fit on one line and is, like the other string, inside a list" +- " literal." +- ), +- "parens should be stripped for short string in list", ++ "This is another really really (not really) long string that also can't be expected to fit on one line and is, like the other string, inside a list literal.", ++ ("parens should be stripped for short string in list"), + ] + + L2 = [ +- "This is a really long string that can't be expected to fit in one line and is the" +- " only child of a list literal." ++ "This is a really long string that can't be expected to fit in one line and is the only child of a list literal." + ] + + S1 = { + "The is a short string", +- ( +- "This is a really long string that can't possibly be expected to fit all" +- " together on one line. Also it is inside a set literal, so it's expected to be" +- " wrapped in parens when splitting to avoid implicit str concatenation." +- ), ++ "This is a really long string that can't possibly be expected to fit all together on one line. Also it is inside a set literal, so it's expected to be wrapped in parens when splitting to avoid implicit str concatenation.", + short_call("arg", {"key": "value"}), +- ( +- "This is another really really (not really) long string that also can't be" +- " expected to fit on one line and is, like the other string, inside a set" +- " literal." +- ), +- "parens should be stripped for short string in set", ++ "This is another really really (not really) long string that also can't be expected to fit on one line and is, like the other string, inside a set literal.", ++ ("parens should be stripped for short string in set"), + } + + S2 = { +- "This is a really long string that can't be expected to fit in one line and is the" +- " only child of a set literal." ++ "This is a really long string that can't be expected to fit in one line and is the only child of a set literal." + } + + T1 = ( + "The is a short string", +- ( +- "This is a really long string that can't possibly be expected to fit all" +- " together on one line. Also it is inside a tuple literal, so it's expected to" +- " be wrapped in parens when splitting to avoid implicit str concatenation." +- ), ++ "This is a really long string that can't possibly be expected to fit all together on one line. Also it is inside a tuple literal, so it's expected to be wrapped in parens when splitting to avoid implicit str concatenation.", + short_call("arg", {"key": "value"}), +- ( +- "This is another really really (not really) long string that also can't be" +- " expected to fit on one line and is, like the other string, inside a tuple" +- " literal." +- ), +- "parens should be stripped for short string in list", ++ "This is another really really (not really) long string that also can't be expected to fit on one line and is, like the other string, inside a tuple literal.", ++ ("parens should be stripped for short string in list"), + ) + + T2 = ( +- ( +- "This is a really long string that can't be expected to fit in one line and is" +- " the only child of a tuple literal." +- ), ++ "This is a really long string that can't be expected to fit in one line and is the only child of a tuple literal.", + ) + + func_with_keywords( + my_arg, +- my_kwarg=( +- "Long keyword strings also need to be wrapped, but they will probably need to" +- " be handled a little bit differently." +- ), ++ my_kwarg="Long keyword strings also need to be wrapped, but they will probably need to be handled a little bit differently.", + ) + + bad_split1 = ( +- "But what should happen when code has already been formatted but in the wrong way?" +- " Like with a space at the end instead of the beginning. Or what about when it is" +- " split too soon?" ++ "But what should happen when code has already been formatted but in the wrong way? Like" ++ " with a space at the end instead of the beginning. Or what about when it is split too soon?" + ) + + bad_split2 = ( +@@ -205,11 +138,9 @@ + xxx, + yyy, + zzz, +- long_string_kwarg=( +- "But what should happen when code has already been formatted but in the wrong" +- " way? Like with a space at the end instead of the beginning. Or what about" +- " when it is split too soon?" +- ), ++ long_string_kwarg="But what should happen when code has already been formatted but in the wrong way? Like " ++ "with a space at the end instead of the beginning. Or what about when it is split too " ++ "soon?", + ) + + bad_split_func3( +@@ -228,29 +159,28 @@ + ) + + inline_comments_func1( +- "if there are inline comments in the middle " ++ "if there are inline " ++ "comments in the middle " + # Here is the standard alone comment. +- "of the implicitly concatenated string, we should handle them correctly", ++ "of the implicitly concatenated " ++ "string, we should handle " ++ "them correctly", + xxx, + ) + + inline_comments_func2( +- "what if the string is very very very very very very very very very very long and" +- " this part does not fit into a single line? " ++ "what if the string is very very very very very very very very very very long and this part does " ++ "not fit into a single line? " + # Here is the standard alone comment. + "then the string should still be properly handled by merging and splitting " + "it into parts that fit in line length.", + xxx, + ) + +-raw_string = ( +- r"This is a long raw string. When re-formatting this string, black needs to make" +- r" sure it prepends the 'r' onto the new string." +-) ++raw_string = r"This is a long raw string. When re-formatting this string, black needs to make sure it prepends the 'r' onto the new string." + +-fmt_string1 = ( +- "We also need to be sure to preserve any and all {} which may or may not be" +- " attached to the string in question.".format("method calls") ++fmt_string1 = "We also need to be sure to preserve any and all {} which may or may not be attached to the string in question.".format( ++ "method calls" + ) + + fmt_string2 = "But what about when the string is {} but {}".format( +@@ -259,8 +189,8 @@ + ) + + old_fmt_string1 = ( +- "While we are on the topic of %s, we should also note that old-style formatting" +- " must also be preserved, since some %s still uses it." % ("formatting", "code") ++ "While we are on the topic of %s, we should also note that old-style formatting must also be preserved, since some %s still uses it." ++ % ("formatting", "code") + ) + + old_fmt_string2 = "This is a %s %s %s %s" % ( +@@ -271,8 +201,7 @@ + ) + + old_fmt_string3 = ( +- "Whereas only the strings after the percent sign were long in the last example," +- " this example uses a long initial string as well. This is another %s %s %s %s" ++ "Whereas only the strings after the percent sign were long in the last example, this example uses a long initial string as well. This is another %s %s %s %s" + % ( + "really really really really really", + "old", +@@ -281,26 +210,14 @@ + ) + ) + +-fstring = ( +- f"f-strings definitely make things more {difficult} than they need to be for" +- " {black}. But boy they sure are handy. The problem is that some lines will need" +- f" to have the 'f' whereas others do not. This {line}, for example, needs one." +-) ++fstring = f"f-strings definitely make things more {difficult} than they need to be for {{black}}. But boy they sure are handy. The problem is that some lines will need to have the 'f' whereas others do not. This {line}, for example, needs one." + +-fstring_with_no_fexprs = ( +- f"Some regular string that needs to get split certainly but is NOT an fstring by" +- f" any means whatsoever." +-) ++fstring_with_no_fexprs = f"Some regular string that needs to get split certainly but is NOT an fstring by any means whatsoever." + +-comment_string = ( # This comment gets thrown to the top. +- "Long lines with inline comments should have their comments appended to the" +- " reformatted string's enclosing right parentheses." +-) ++comment_string = "Long lines with inline comments should have their comments appended to the reformatted string's enclosing right parentheses." # This comment gets thrown to the top. + + arg_comment_string = print( +- "Long lines with inline comments which are apart of (and not the only member of) an" +- " argument list should have their comments appended to the reformatted string's" +- " enclosing left parentheses.", # This comment gets thrown to the top. ++ "Long lines with inline comments which are apart of (and not the only member of) an argument list should have their comments appended to the reformatted string's enclosing left parentheses.", # This comment gets thrown to the top. + "Arg #2", + "Arg #3", + "Arg #4", +@@ -315,80 +232,72 @@ + + triple_quote_string = """This is a really really really long triple quote string assignment and it should not be touched.""" + +-assert some_type_of_boolean_expression, ( +- "Followed by a really really really long string that is used to provide context to" +- " the AssertionError exception." +-) ++assert some_type_of_boolean_expression, "Followed by a really really really long string that is used to provide context to the AssertionError exception." + +-assert some_type_of_boolean_expression, ( +- "Followed by a really really really long string that is used to provide context to" +- " the AssertionError exception, which uses dynamic string {}.".format("formatting") ++assert some_type_of_boolean_expression, "Followed by a really really really long string that is used to provide context to the AssertionError exception, which uses dynamic string {}.".format( ++ "formatting" + ) + + assert some_type_of_boolean_expression, ( +- "Followed by a really really really long string that is used to provide context to" +- " the AssertionError exception, which uses dynamic string %s." % "formatting" ++ "Followed by a really really really long string that is used to provide context to the AssertionError exception, which uses dynamic string %s." ++ % "formatting" + ) + + assert some_type_of_boolean_expression, ( +- "Followed by a really really really long string that is used to provide context to" +- " the AssertionError exception, which uses dynamic %s %s." ++ "Followed by a really really really long string that is used to provide context to the AssertionError exception, which uses dynamic %s %s." + % ("string", "formatting") + ) + + some_function_call( +- "With a reallly generic name and with a really really long string that is, at some" +- " point down the line, " ++ "With a reallly generic name and with a really really long string that is, at some point down the line, " + + added + + " to a variable and then added to another string." + ) + + some_function_call( +- "With a reallly generic name and with a really really long string that is, at some" +- " point down the line, " ++ "With a reallly generic name and with a really really long string that is, at some point down the line, " + + added +- + " to a variable and then added to another string. But then what happens when the" +- " final string is also supppppperrrrr long?! Well then that second (realllllllly" +- " long) string should be split too.", ++ + " to a variable and then added to another string. But then what happens when the final string is also supppppperrrrr long?! Well then that second (realllllllly long) string should be split too.", + "and a second argument", + and_a_third, + ) + +-return ( +- "A really really really really really really really really really really really" +- " really really long {} {}".format("return", "value") ++return "A really really really really really really really really really really really really really long {} {}".format( ++ "return", "value" + ) + + func_with_bad_comma( +- "This is a really long string argument to a function that has a trailing comma" +- " which should NOT be there.", ++ "This is a really long string argument to a function that has a trailing comma which should NOT be there.", + ) + + func_with_bad_comma( +- "This is a really long string argument to a function that has a trailing comma" +- " which should NOT be there.", # comment after comma ++ "This is a really long string argument to a function that has a trailing comma which should NOT be there.", # comment after comma + ) + + func_with_bad_comma( +- "This is a really long string argument to a function that has a trailing comma" +- " which should NOT be there.", ++ ( ++ "This is a really long string argument to a function that has a trailing comma" ++ " which should NOT be there." ++ ), + ) + + func_with_bad_comma( +- "This is a really long string argument to a function that has a trailing comma" +- " which should NOT be there.", # comment after comma ++ ( ++ "This is a really long string argument to a function that has a trailing comma" ++ " which should NOT be there." ++ ), # comment after comma + ) + + func_with_bad_parens_that_wont_fit_in_one_line( +- "short string that should have parens stripped", x, y, z ++ ("short string that should have parens stripped"), x, y, z + ) + + func_with_bad_parens_that_wont_fit_in_one_line( +- x, y, "short string that should have parens stripped", z ++ x, y, ("short string that should have parens stripped"), z + ) + + func_with_bad_parens( +- "short string that should have parens stripped", ++ ("short string that should have parens stripped"), + x, + y, + z, +@@ -397,7 +306,7 @@ + func_with_bad_parens( + x, + y, +- "short string that should have parens stripped", ++ ("short string that should have parens stripped"), + z, + ) + +@@ -408,50 +317,27 @@ + + CONCATENATED + + "using the '+' operator." + ) +-annotated_variable: Final = ( +- "This is a large string that has a type annotation attached to it. A type" +- " annotation should NOT stop a long string from being wrapped." +-) +-annotated_variable: Literal["fakse_literal"] = ( +- "This is a large string that has a type annotation attached to it. A type" +- " annotation should NOT stop a long string from being wrapped." +-) ++annotated_variable: Final = "This is a large string that has a type annotation attached to it. A type annotation should NOT stop a long string from being wrapped." ++annotated_variable: Literal[ ++ "fakse_literal" ++] = "This is a large string that has a type annotation attached to it. A type annotation should NOT stop a long string from being wrapped." + +-backslashes = ( +- "This is a really long string with \"embedded\" double quotes and 'single' quotes" +- " that also handles checking for an even number of backslashes \\" +-) +-backslashes = ( +- "This is a really long string with \"embedded\" double quotes and 'single' quotes" +- " that also handles checking for an even number of backslashes \\\\" +-) +-backslashes = ( +- "This is a really 'long' string with \"embedded double quotes\" and 'single' quotes" +- ' that also handles checking for an odd number of backslashes \\", like' +- " this...\\\\\\" +-) ++backslashes = "This is a really long string with \"embedded\" double quotes and 'single' quotes that also handles checking for an even number of backslashes \\" ++backslashes = "This is a really long string with \"embedded\" double quotes and 'single' quotes that also handles checking for an even number of backslashes \\\\" ++backslashes = "This is a really 'long' string with \"embedded double quotes\" and 'single' quotes that also handles checking for an odd number of backslashes \\\", like this...\\\\\\" + +-short_string = "Hi there." ++short_string = "Hi" " there." + +-func_call(short_string="Hi there.") ++func_call(short_string=("Hi" " there.")) + + raw_strings = r"Don't" " get" r" merged" " unless they are all raw." + + + def foo(): +- yield ( +- "This is a really long string that can't possibly be expected to fit all" +- " together on one line. In fact it may even take up three or more lines... like" +- " four or five... but probably just three." +- ) ++ yield "This is a really long string that can't possibly be expected to fit all together on one line. In fact it may even take up three or more lines... like four or five... but probably just three." + + +-x = ( +- "This is a {really} long string that needs to be split without a doubt (i.e." +- f" most definitely). In short, this {string} that can't possibly be {{expected}} to" +- f" fit all together on one line. In {fact} it may even take up three or more" +- " lines... like four or five... but probably just four." +-) ++x = f"This is a {{really}} long string that needs to be split without a doubt (i.e. most definitely). In short, this {string} that can't possibly be {{expected}} to fit all together on one line. In {fact} it may even take up three or more lines... like four or five... but probably just four." + + 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 +354,24 @@ + " of it." + ) + +-string_with_nameescape = ( +- "........................................................................" +- " \N{LAO KO LA}" +-) ++string_with_nameescape = "........................................................................ \N{LAO KO LA}" + +-string_with_nameescape = ( +- "..........................................................................." +- " \N{LAO KO LA}" +-) ++string_with_nameescape = "........................................................................... \N{LAO KO LA}" + +-string_with_nameescape = ( +- "............................................................................" +- " \N{LAO KO LA}" +-) ++string_with_nameescape = "............................................................................ \N{LAO KO LA}" + +-string_with_nameescape_and_escaped_backslash = ( +- "......................................................................" +- " \\\N{LAO KO LA}" +-) ++string_with_nameescape_and_escaped_backslash = "...................................................................... \\\N{LAO KO LA}" + +-string_with_nameescape_and_escaped_backslash = ( +- "........................................................................." +- " \\\N{LAO KO LA}" +-) ++string_with_nameescape_and_escaped_backslash = "......................................................................... \\\N{LAO KO LA}" + +-string_with_nameescape_and_escaped_backslash = ( +- ".........................................................................." +- " \\\N{LAO KO LA}" +-) ++string_with_nameescape_and_escaped_backslash = ".......................................................................... \\\N{LAO KO LA}" + +-string_with_escaped_nameescape = ( +- "........................................................................ \\N{LAO" +- " KO LA}" +-) ++string_with_escaped_nameescape = "........................................................................ \\N{LAO KO LA}" + +-string_with_escaped_nameescape = ( +- "..........................................................................." +- " \\N{LAO KO LA}" +-) ++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" + ) + + dict_with_lambda_values = { +@@ -524,61 +383,54 @@ + + # Complex string concatenations with a method call in the middle. + code = ( +- " return [\n" +- + ", \n".join( +- " (%r, self.%s, visitor.%s)" % (attrname, attrname, visit_name) +- for attrname, visit_name in names ++ (" return [\n") ++ + ( ++ ", \n".join( ++ " (%r, self.%s, visitor.%s)" % (attrname, attrname, visit_name) ++ for attrname, visit_name in names ++ ) + ) +- + "\n ]\n" ++ + ("\n ]\n") + ) + + + # Test case of an outer string' parens enclose an inner string's parens. +-call(body="%s %s" % (",".join(items), suffix)) ++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=} {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=} {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=")} {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=")=} {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=} {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=} {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=} {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=} {pos_share=} {desc["status"]} {desc["exposure_max"]}' + ) + + log.info( +- "Skipping:" +- f' { longer_longer_longer_longer_longer_longer_name [ "db_id" ] [ "another_key" ] = : .3f }' ++ f'Skipping: { longer_longer_longer_longer_longer_longer_name [ "db_id" ] [ "another_key" ] = : .3f }' + ) + + log.info( +``` + +## Ruff Output + +```python +x = "This is a really long string that can't possibly be expected to fit all together on one line. In fact it may even take up three or more lines... like four or five... but probably just three." + +x += "This is a really long string that can't possibly be expected to fit all together on one line. In fact it may even take up three or more lines... like four or five... but probably just three." + +y = "Short string" + +print( + "This is a really long string inside of a print statement with extra arguments attached at the end of it.", + x, + y, + z, +) + +print( + "This is a really long string inside of a print statement with no extra arguments attached at the end of it." +) + +D1 = { + "The First": "This is a really long string that can't possibly be expected to fit all together on one line. Also it is inside a dictionary, so formatting is more difficult.", + "The Second": "This is another really really (not really) long string that also can't be expected to fit on one line and is, like the other string, inside a dictionary.", +} + +D2 = { + 1.0: "This is a really long string that can't possibly be expected to fit all together on one line. Also it is inside a dictionary, so formatting is more difficult.", + 2.0: "This is another really really (not really) long string that also can't be expected to fit on one line and is, like the other string, inside a dictionary.", +} + +D3 = { + x: "This is a really long string that can't possibly be expected to fit all together on one line. Also it is inside a dictionary, so formatting is more difficult.", + y: "This is another really really (not really) long string that also can't be expected to fit on one line and is, like the other string, inside a dictionary.", +} + +D4 = { + "A long and ridiculous {}".format( + string_key + ): "This is a really really really long string that has to go i,side of a dictionary. It is soooo bad.", + some_func( + "calling", "some", "stuff" + ): "This is a really really really long string that has to go inside of a dictionary. It is {soooo} bad (#{x}).".format( + sooo="soooo", x=2 + ), + "A %s %s" + % ( + "formatted", + "string", + ): "This is a really really really long string that has to go inside of a dictionary. It is %s bad (#%d)." + % ("soooo", 2), +} + +D5 = { # Test for https://github.com/psf/black/issues/3261 + ( + "This is a really long string that can't be expected to fit in one line and is used as a nested dict's key" + ): {"inner": "value"}, +} + +D6 = { # Test for https://github.com/psf/black/issues/3261 + ( + "This is a really long string that can't be expected to fit in one line and is used as a dict's key" + ): ["value1", "value2"], +} + +L1 = [ + "The is a short string", + "This is a really long string that can't possibly be expected to fit all together on one line. Also it is inside a list literal, so it's expected to be wrapped in parens when splitting to avoid implicit str concatenation.", + short_call("arg", {"key": "value"}), + "This is another really really (not really) long string that also can't be expected to fit on one line and is, like the other string, inside a list literal.", + ("parens should be stripped for short string in list"), +] + +L2 = [ + "This is a really long string that can't be expected to fit in one line and is the only child of a list literal." +] + +S1 = { + "The is a short string", + "This is a really long string that can't possibly be expected to fit all together on one line. Also it is inside a set literal, so it's expected to be wrapped in parens when splitting to avoid implicit str concatenation.", + short_call("arg", {"key": "value"}), + "This is another really really (not really) long string that also can't be expected to fit on one line and is, like the other string, inside a set literal.", + ("parens should be stripped for short string in set"), +} + +S2 = { + "This is a really long string that can't be expected to fit in one line and is the only child of a set literal." +} + +T1 = ( + "The is a short string", + "This is a really long string that can't possibly be expected to fit all together on one line. Also it is inside a tuple literal, so it's expected to be wrapped in parens when splitting to avoid implicit str concatenation.", + short_call("arg", {"key": "value"}), + "This is another really really (not really) long string that also can't be expected to fit on one line and is, like the other string, inside a tuple literal.", + ("parens should be stripped for short string in list"), +) + +T2 = ( + "This is a really long string that can't be expected to fit in one line and is the only child of a tuple literal.", +) + +func_with_keywords( + my_arg, + my_kwarg="Long keyword strings also need to be wrapped, but they will probably need to be handled a little bit differently.", +) + +bad_split1 = ( + "But what should happen when code has already been formatted but in the wrong way? Like" + " with a space at the end instead of the beginning. Or what about when it is split too soon?" +) + +bad_split2 = ( + "But what should happen when code has already " + "been formatted but in the wrong way? Like " + "with a space at the end instead of the " + "beginning. Or what about when it is split too " + "soon? In the case of a split that is too " + "short, black will try to honer the custom " + "split." +) + +bad_split3 = ( + "What if we have inline comments on " # First Comment + "each line of a bad split? In that " # Second Comment + "case, we should just leave it alone." # Third Comment +) + +bad_split_func1( + "But what should happen when code has already " + "been formatted but in the wrong way? Like " + "with a space at the end instead of the " + "beginning. Or what about when it is split too " + "soon? In the case of a split that is too " + "short, black will try to honer the custom " + "split.", + xxx, + yyy, + zzz, +) + +bad_split_func2( + xxx, + yyy, + zzz, + long_string_kwarg="But what should happen when code has already been formatted but in the wrong way? Like " + "with a space at the end instead of the beginning. Or what about when it is split too " + "soon?", +) + +bad_split_func3( + ( + "But what should happen when code has already " + r"been formatted but in the wrong way? Like " + "with a space at the end instead of the " + r"beginning. Or what about when it is split too " + r"soon? In the case of a split that is too " + "short, black will try to honer the custom " + "split." + ), + xxx, + yyy, + zzz, +) + +inline_comments_func1( + "if there are inline " + "comments in the middle " + # Here is the standard alone comment. + "of the implicitly concatenated " + "string, we should handle " + "them correctly", + xxx, +) + +inline_comments_func2( + "what if the string is very very very very very very very very very very long and this part does " + "not fit into a single line? " + # Here is the standard alone comment. + "then the string should still be properly handled by merging and splitting " + "it into parts that fit in line length.", + xxx, +) + +raw_string = r"This is a long raw string. When re-formatting this string, black needs to make sure it prepends the 'r' onto the new string." + +fmt_string1 = "We also need to be sure to preserve any and all {} which may or may not be attached to the string in question.".format( + "method calls" +) + +fmt_string2 = "But what about when the string is {} but {}".format( + "short", + "the method call is really really really really really really really really long?", +) + +old_fmt_string1 = ( + "While we are on the topic of %s, we should also note that old-style formatting must also be preserved, since some %s still uses it." + % ("formatting", "code") +) + +old_fmt_string2 = "This is a %s %s %s %s" % ( + "really really really really really", + "old", + "way to format strings!", + "Use f-strings instead!", +) + +old_fmt_string3 = ( + "Whereas only the strings after the percent sign were long in the last example, this example uses a long initial string as well. This is another %s %s %s %s" + % ( + "really really really really really", + "old", + "way to format strings!", + "Use f-strings instead!", + ) +) + +fstring = f"f-strings definitely make things more {difficult} than they need to be for {{black}}. But boy they sure are handy. The problem is that some lines will need to have the 'f' whereas others do not. This {line}, for example, needs one." + +fstring_with_no_fexprs = f"Some regular string that needs to get split certainly but is NOT an fstring by any means whatsoever." + +comment_string = "Long lines with inline comments should have their comments appended to the reformatted string's enclosing right parentheses." # This comment gets thrown to the top. + +arg_comment_string = print( + "Long lines with inline comments which are apart of (and not the only member of) an argument list should have their comments appended to the reformatted string's enclosing left parentheses.", # This comment gets thrown to the top. + "Arg #2", + "Arg #3", + "Arg #4", + "Arg #5", +) + +pragma_comment_string1 = "Lines which end with an inline pragma comment of the form `# : <...>` should be left alone." # noqa: E501 + +pragma_comment_string2 = "Lines which end with an inline pragma comment of the form `# : <...>` should be left alone." # noqa + +"""This is a really really really long triple quote string and it should not be touched.""" + +triple_quote_string = """This is a really really really long triple quote string assignment and it should not be touched.""" + +assert some_type_of_boolean_expression, "Followed by a really really really long string that is used to provide context to the AssertionError exception." + +assert some_type_of_boolean_expression, "Followed by a really really really long string that is used to provide context to the AssertionError exception, which uses dynamic string {}.".format( + "formatting" +) + +assert some_type_of_boolean_expression, ( + "Followed by a really really really long string that is used to provide context to the AssertionError exception, which uses dynamic string %s." + % "formatting" +) + +assert some_type_of_boolean_expression, ( + "Followed by a really really really long string that is used to provide context to the AssertionError exception, which uses dynamic %s %s." + % ("string", "formatting") +) + +some_function_call( + "With a reallly generic name and with a really really long string that is, at some point down the line, " + + added + + " to a variable and then added to another string." +) + +some_function_call( + "With a reallly generic name and with a really really long string that is, at some point down the line, " + + added + + " to a variable and then added to another string. But then what happens when the final string is also supppppperrrrr long?! Well then that second (realllllllly long) string should be split too.", + "and a second argument", + and_a_third, +) + +return "A really really really really really really really really really really really really really long {} {}".format( + "return", "value" +) + +func_with_bad_comma( + "This is a really long string argument to a function that has a trailing comma which should NOT be there.", +) + +func_with_bad_comma( + "This is a really long string argument to a function that has a trailing comma which should NOT be there.", # comment after comma +) + +func_with_bad_comma( + ( + "This is a really long string argument to a function that has a trailing comma" + " which should NOT be there." + ), +) + +func_with_bad_comma( + ( + "This is a really long string argument to a function that has a trailing comma" + " which should NOT be there." + ), # comment after comma +) + +func_with_bad_parens_that_wont_fit_in_one_line( + ("short string that should have parens stripped"), x, y, z +) + +func_with_bad_parens_that_wont_fit_in_one_line( + x, y, ("short string that should have parens stripped"), z +) + +func_with_bad_parens( + ("short string that should have parens stripped"), + x, + y, + z, +) + +func_with_bad_parens( + x, + y, + ("short string that should have parens stripped"), + z, +) + +annotated_variable: Final = ( + "This is a large " + + STRING + + " that has been " + + CONCATENATED + + "using the '+' operator." +) +annotated_variable: Final = "This is a large string that has a type annotation attached to it. A type annotation should NOT stop a long string from being wrapped." +annotated_variable: Literal[ + "fakse_literal" +] = "This is a large string that has a type annotation attached to it. A type annotation should NOT stop a long string from being wrapped." + +backslashes = "This is a really long string with \"embedded\" double quotes and 'single' quotes that also handles checking for an even number of backslashes \\" +backslashes = "This is a really long string with \"embedded\" double quotes and 'single' quotes that also handles checking for an even number of backslashes \\\\" +backslashes = "This is a really 'long' string with \"embedded double quotes\" and 'single' quotes that also handles checking for an odd number of backslashes \\\", like this...\\\\\\" + +short_string = "Hi" " there." + +func_call(short_string=("Hi" " there.")) + +raw_strings = r"Don't" " get" r" merged" " unless they are all raw." + + +def foo(): + yield "This is a really long string that can't possibly be expected to fit all together on one line. In fact it may even take up three or more lines... like four or five... but probably just three." + + +x = f"This is a {{really}} long string that needs to be split without a doubt (i.e. most definitely). In short, this {string} that can't possibly be {{expected}} to fit all together on one line. In {fact} it may even take up three or more lines... like four or five... but probably just four." + +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 + " of it." +) + +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" # noqa + " of it." +) + +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" # pylint: disable=some-pylint-check + " of it." +) + +string_with_nameescape = "........................................................................ \N{LAO KO LA}" + +string_with_nameescape = "........................................................................... \N{LAO KO LA}" + +string_with_nameescape = "............................................................................ \N{LAO KO LA}" + +string_with_nameescape_and_escaped_backslash = "...................................................................... \\\N{LAO KO LA}" + +string_with_nameescape_and_escaped_backslash = "......................................................................... \\\N{LAO KO LA}" + +string_with_nameescape_and_escaped_backslash = ".......................................................................... \\\N{LAO KO LA}" + +string_with_escaped_nameescape = "........................................................................ \\N{LAO KO LA}" + +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" +) + +dict_with_lambda_values = { + "join": lambda j: ( + f"{j.__class__.__name__}({some_function_call(j.left)}, " + f"{some_function_call(j.right)})" + ), +} + +# Complex string concatenations with a method call in the middle. +code = ( + (" return [\n") + + ( + ", \n".join( + " (%r, self.%s, visitor.%s)" % (attrname, attrname, visit_name) + for attrname, visit_name in names + ) + ) + + ("\n ]\n") +) + + +# Test case of an outer string' parens enclose an inner string's parens. +call(body=("%s %s" % ((",".join(items)), suffix))) + +log.info( + f'Skipping: {desc["db_id"]=} {desc["ms_name"]} {money=} {dte=} {pos_share=} {desc["status"]=} {desc["exposure_max"]=}' +) + +log.info( + f"Skipping: {desc['db_id']=} {desc['ms_name']} {money=} {dte=} {pos_share=} {desc['status']=} {desc['exposure_max']=}" +) + +log.info( + f'Skipping: {desc["db_id"]} {foo("bar",x=123)} {"foo" != "bar"} {(x := "abc=")} {pos_share=} {desc["status"]} {desc["exposure_max"]}' +) + +log.info( + f'Skipping: {desc["db_id"]} {desc["ms_name"]} {money=} {(x := "abc=")=} {pos_share=} {desc["status"]} {desc["exposure_max"]}' +) + +log.info( + f'Skipping: {desc["db_id"]} {foo("bar",x=123)=} {money=} {dte=} {pos_share=} {desc["status"]} {desc["exposure_max"]}' +) + +log.info( + f'Skipping: {foo("asdf")=} {desc["ms_name"]} {money=} {dte=} {pos_share=} {desc["status"]} {desc["exposure_max"]}' +) + +log.info( + f'Skipping: {"a" == "b" == "c" == "d"} {desc["ms_name"]} {money=} {dte=} {pos_share=} {desc["status"]} {desc["exposure_max"]}' +) + +log.info( + f'Skipping: {"a" == "b" == "c" == "d"=} {desc["ms_name"]} {money=} {dte=} {pos_share=} {desc["status"]} {desc["exposure_max"]}' +) + +log.info( + f'Skipping: { longer_longer_longer_longer_longer_longer_name [ "db_id" ] [ "another_key" ] = : .3f }' +) + +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"]}""" +) + +log.info( + f"""Skipping: {'a' == 'b'} {desc['ms_name']} {money=} {dte=} {pos_share=} {desc['status']} {desc['exposure_max']}""" +) +``` + +## Black Output + +```python +x = ( + "This is a really long string that can't possibly be expected to fit all together" + " on one line. In fact it may even take up three or more lines... like four or" + " five... but probably just three." +) + +x += ( + "This is a really long string that can't possibly be expected to fit all together" + " on one line. In fact it may even take up three or more lines... like four or" + " five... but probably just three." +) + +y = "Short string" + +print( + "This is a really long string inside of a print statement with extra arguments" + " attached at the end of it.", + x, + y, + z, +) + +print( + "This is a really long string inside of a print statement with no extra arguments" + " attached at the end of it." +) + +D1 = { + "The First": ( + "This is a really long string that can't possibly be expected to fit all" + " together on one line. Also it is inside a dictionary, so formatting is more" + " difficult." + ), + "The Second": ( + "This is another really really (not really) long string that also can't be" + " expected to fit on one line and is, like the other string, inside a" + " dictionary." + ), +} + +D2 = { + 1.0: ( + "This is a really long string that can't possibly be expected to fit all" + " together on one line. Also it is inside a dictionary, so formatting is more" + " difficult." + ), + 2.0: ( + "This is another really really (not really) long string that also can't be" + " expected to fit on one line and is, like the other string, inside a" + " dictionary." + ), +} + +D3 = { + x: ( + "This is a really long string that can't possibly be expected to fit all" + " together on one line. Also it is inside a dictionary, so formatting is more" + " difficult." + ), + y: ( + "This is another really really (not really) long string that also can't be" + " expected to fit on one line and is, like the other string, inside a" + " dictionary." + ), +} + +D4 = { + "A long and ridiculous {}".format(string_key): ( + "This is a really really really long string that has to go i,side of a" + " dictionary. It is soooo bad." + ), + some_func("calling", "some", "stuff"): ( + "This is a really really really long string that has to go inside of a" + " dictionary. It is {soooo} bad (#{x}).".format(sooo="soooo", x=2) + ), + "A %s %s" + % ("formatted", "string"): ( + "This is a really really really long string that has to go inside of a" + " dictionary. It is %s bad (#%d)." % ("soooo", 2) + ), +} + +D5 = { # Test for https://github.com/psf/black/issues/3261 + "This is a really long string that can't be expected to fit in one line and is used as a nested dict's key": { + "inner": "value" + }, +} + +D6 = { # Test for https://github.com/psf/black/issues/3261 + "This is a really long string that can't be expected to fit in one line and is used as a dict's key": [ + "value1", + "value2", + ], +} + +L1 = [ + "The is a short string", + ( + "This is a really long string that can't possibly be expected to fit all" + " together on one line. Also it is inside a list literal, so it's expected to" + " be wrapped in parens when splitting to avoid implicit str concatenation." + ), + short_call("arg", {"key": "value"}), + ( + "This is another really really (not really) long string that also can't be" + " expected to fit on one line and is, like the other string, inside a list" + " literal." + ), + "parens should be stripped for short string in list", +] + +L2 = [ + "This is a really long string that can't be expected to fit in one line and is the" + " only child of a list literal." +] + +S1 = { + "The is a short string", + ( + "This is a really long string that can't possibly be expected to fit all" + " together on one line. Also it is inside a set literal, so it's expected to be" + " wrapped in parens when splitting to avoid implicit str concatenation." + ), + short_call("arg", {"key": "value"}), + ( + "This is another really really (not really) long string that also can't be" + " expected to fit on one line and is, like the other string, inside a set" + " literal." + ), + "parens should be stripped for short string in set", +} + +S2 = { + "This is a really long string that can't be expected to fit in one line and is the" + " only child of a set literal." +} + +T1 = ( + "The is a short string", + ( + "This is a really long string that can't possibly be expected to fit all" + " together on one line. Also it is inside a tuple literal, so it's expected to" + " be wrapped in parens when splitting to avoid implicit str concatenation." + ), + short_call("arg", {"key": "value"}), + ( + "This is another really really (not really) long string that also can't be" + " expected to fit on one line and is, like the other string, inside a tuple" + " literal." + ), + "parens should be stripped for short string in list", +) + +T2 = ( + ( + "This is a really long string that can't be expected to fit in one line and is" + " the only child of a tuple literal." + ), +) + +func_with_keywords( + my_arg, + my_kwarg=( + "Long keyword strings also need to be wrapped, but they will probably need to" + " be handled a little bit differently." + ), +) + +bad_split1 = ( + "But what should happen when code has already been formatted but in the wrong way?" + " Like with a space at the end instead of the beginning. Or what about when it is" + " split too soon?" +) + +bad_split2 = ( + "But what should happen when code has already " + "been formatted but in the wrong way? Like " + "with a space at the end instead of the " + "beginning. Or what about when it is split too " + "soon? In the case of a split that is too " + "short, black will try to honer the custom " + "split." +) + +bad_split3 = ( + "What if we have inline comments on " # First Comment + "each line of a bad split? In that " # Second Comment + "case, we should just leave it alone." # Third Comment +) + +bad_split_func1( + "But what should happen when code has already " + "been formatted but in the wrong way? Like " + "with a space at the end instead of the " + "beginning. Or what about when it is split too " + "soon? In the case of a split that is too " + "short, black will try to honer the custom " + "split.", + xxx, + yyy, + zzz, +) + +bad_split_func2( + xxx, + yyy, + zzz, + long_string_kwarg=( + "But what should happen when code has already been formatted but in the wrong" + " way? Like with a space at the end instead of the beginning. Or what about" + " when it is split too soon?" + ), +) + +bad_split_func3( + ( + "But what should happen when code has already " + r"been formatted but in the wrong way? Like " + "with a space at the end instead of the " + r"beginning. Or what about when it is split too " + r"soon? In the case of a split that is too " + "short, black will try to honer the custom " + "split." + ), + xxx, + yyy, + zzz, +) + +inline_comments_func1( + "if there are inline comments in the middle " + # Here is the standard alone comment. + "of the implicitly concatenated string, we should handle them correctly", + xxx, +) + +inline_comments_func2( + "what if the string is very very very very very very very very very very long and" + " this part does not fit into a single line? " + # Here is the standard alone comment. + "then the string should still be properly handled by merging and splitting " + "it into parts that fit in line length.", + xxx, +) + +raw_string = ( + r"This is a long raw string. When re-formatting this string, black needs to make" + r" sure it prepends the 'r' onto the new string." +) + +fmt_string1 = ( + "We also need to be sure to preserve any and all {} which may or may not be" + " attached to the string in question.".format("method calls") +) + +fmt_string2 = "But what about when the string is {} but {}".format( + "short", + "the method call is really really really really really really really really long?", +) + +old_fmt_string1 = ( + "While we are on the topic of %s, we should also note that old-style formatting" + " must also be preserved, since some %s still uses it." % ("formatting", "code") +) + +old_fmt_string2 = "This is a %s %s %s %s" % ( + "really really really really really", + "old", + "way to format strings!", + "Use f-strings instead!", +) + +old_fmt_string3 = ( + "Whereas only the strings after the percent sign were long in the last example," + " this example uses a long initial string as well. This is another %s %s %s %s" + % ( + "really really really really really", + "old", + "way to format strings!", + "Use f-strings instead!", + ) +) + +fstring = ( + f"f-strings definitely make things more {difficult} than they need to be for" + " {black}. But boy they sure are handy. The problem is that some lines will need" + f" to have the 'f' whereas others do not. This {line}, for example, needs one." +) + +fstring_with_no_fexprs = ( + f"Some regular string that needs to get split certainly but is NOT an fstring by" + f" any means whatsoever." +) + +comment_string = ( # This comment gets thrown to the top. + "Long lines with inline comments should have their comments appended to the" + " reformatted string's enclosing right parentheses." +) + +arg_comment_string = print( + "Long lines with inline comments which are apart of (and not the only member of) an" + " argument list should have their comments appended to the reformatted string's" + " enclosing left parentheses.", # This comment gets thrown to the top. + "Arg #2", + "Arg #3", + "Arg #4", + "Arg #5", +) + +pragma_comment_string1 = "Lines which end with an inline pragma comment of the form `# : <...>` should be left alone." # noqa: E501 + +pragma_comment_string2 = "Lines which end with an inline pragma comment of the form `# : <...>` should be left alone." # noqa + +"""This is a really really really long triple quote string and it should not be touched.""" + +triple_quote_string = """This is a really really really long triple quote string assignment and it should not be touched.""" + +assert some_type_of_boolean_expression, ( + "Followed by a really really really long string that is used to provide context to" + " the AssertionError exception." +) + +assert some_type_of_boolean_expression, ( + "Followed by a really really really long string that is used to provide context to" + " the AssertionError exception, which uses dynamic string {}.".format("formatting") +) + +assert some_type_of_boolean_expression, ( + "Followed by a really really really long string that is used to provide context to" + " the AssertionError exception, which uses dynamic string %s." % "formatting" +) + +assert some_type_of_boolean_expression, ( + "Followed by a really really really long string that is used to provide context to" + " the AssertionError exception, which uses dynamic %s %s." + % ("string", "formatting") +) + +some_function_call( + "With a reallly generic name and with a really really long string that is, at some" + " point down the line, " + + added + + " to a variable and then added to another string." +) + +some_function_call( + "With a reallly generic name and with a really really long string that is, at some" + " point down the line, " + + added + + " to a variable and then added to another string. But then what happens when the" + " final string is also supppppperrrrr long?! Well then that second (realllllllly" + " long) string should be split too.", + "and a second argument", + and_a_third, +) + +return ( + "A really really really really really really really really really really really" + " really really long {} {}".format("return", "value") +) + +func_with_bad_comma( + "This is a really long string argument to a function that has a trailing comma" + " which should NOT be there.", +) + +func_with_bad_comma( + "This is a really long string argument to a function that has a trailing comma" + " which should NOT be there.", # comment after comma +) + +func_with_bad_comma( + "This is a really long string argument to a function that has a trailing comma" + " which should NOT be there.", +) + +func_with_bad_comma( + "This is a really long string argument to a function that has a trailing comma" + " which should NOT be there.", # comment after comma +) + +func_with_bad_parens_that_wont_fit_in_one_line( + "short string that should have parens stripped", x, y, z +) + +func_with_bad_parens_that_wont_fit_in_one_line( + x, y, "short string that should have parens stripped", z +) + +func_with_bad_parens( + "short string that should have parens stripped", + x, + y, + z, +) + +func_with_bad_parens( + x, + y, + "short string that should have parens stripped", + z, +) + +annotated_variable: Final = ( + "This is a large " + + STRING + + " that has been " + + CONCATENATED + + "using the '+' operator." +) +annotated_variable: Final = ( + "This is a large string that has a type annotation attached to it. A type" + " annotation should NOT stop a long string from being wrapped." +) +annotated_variable: Literal["fakse_literal"] = ( + "This is a large string that has a type annotation attached to it. A type" + " annotation should NOT stop a long string from being wrapped." +) + +backslashes = ( + "This is a really long string with \"embedded\" double quotes and 'single' quotes" + " that also handles checking for an even number of backslashes \\" +) +backslashes = ( + "This is a really long string with \"embedded\" double quotes and 'single' quotes" + " that also handles checking for an even number of backslashes \\\\" +) +backslashes = ( + "This is a really 'long' string with \"embedded double quotes\" and 'single' quotes" + ' that also handles checking for an odd number of backslashes \\", like' + " this...\\\\\\" +) + +short_string = "Hi there." + +func_call(short_string="Hi there.") + +raw_strings = r"Don't" " get" r" merged" " unless they are all raw." + + +def foo(): + yield ( + "This is a really long string that can't possibly be expected to fit all" + " together on one line. In fact it may even take up three or more lines... like" + " four or five... but probably just three." + ) + + +x = ( + "This is a {really} long string that needs to be split without a doubt (i.e." + f" most definitely). In short, this {string} that can't possibly be {{expected}} to" + f" fit all together on one line. In {fact} it may even take up three or more" + " lines... like four or five... but probably just four." +) + +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 + " of it." +) + +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" # noqa + " of it." +) + +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" # pylint: disable=some-pylint-check + " of it." +) + +string_with_nameescape = ( + "........................................................................" + " \N{LAO KO LA}" +) + +string_with_nameescape = ( + "..........................................................................." + " \N{LAO KO LA}" +) + +string_with_nameescape = ( + "............................................................................" + " \N{LAO KO LA}" +) + +string_with_nameescape_and_escaped_backslash = ( + "......................................................................" + " \\\N{LAO KO LA}" +) + +string_with_nameescape_and_escaped_backslash = ( + "........................................................................." + " \\\N{LAO KO LA}" +) + +string_with_nameescape_and_escaped_backslash = ( + ".........................................................................." + " \\\N{LAO KO LA}" +) + +string_with_escaped_nameescape = ( + "........................................................................ \\N{LAO" + " KO LA}" +) + +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" + ) +) + +dict_with_lambda_values = { + "join": lambda j: ( + f"{j.__class__.__name__}({some_function_call(j.left)}, " + f"{some_function_call(j.right)})" + ), +} + +# Complex string concatenations with a method call in the middle. +code = ( + " return [\n" + + ", \n".join( + " (%r, self.%s, visitor.%s)" % (attrname, attrname, visit_name) + for attrname, visit_name in names + ) + + "\n ]\n" +) + + +# Test case of an outer string' parens enclose an inner string's parens. +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"]=}' +) + +log.info( + "Skipping:" + f" {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']}" +) + +log.info( + "Skipping:" + f' {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"]}' +) + +log.info( + "Skipping:" + f' {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']}" +) + +log.info( + "Skipping:" + f' {"a" == "b" == "c" == "d"=} {desc["ms_name"]} {money=} {dte=} {pos_share=} {desc["status"]} {desc["exposure_max"]}' +) + +log.info( + "Skipping:" + f' { longer_longer_longer_longer_longer_longer_name [ "db_id" ] [ "another_key" ] = : .3f }' +) + +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"]}""" +) + +log.info( + f"""Skipping: {'a' == 'b'} {desc['ms_name']} {money=} {dte=} {pos_share=} {desc['status']} {desc['exposure_max']}""" +) +``` + + diff --git a/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__preview_long_strings__east_asian_width.py.snap b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__preview_long_strings__east_asian_width.py.snap new file mode 100644 index 0000000000..7ba6778674 --- /dev/null +++ b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__preview_long_strings__east_asian_width.py.snap @@ -0,0 +1,75 @@ +--- +source: crates/ruff_python_formatter/tests/fixtures.rs +input_file: crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_long_strings__east_asian_width.py +--- +## Input + +```python +# The following strings do not have not-so-many chars, but are long enough +# when these are rendered in a monospace font (if the renderer respects +# Unicode East Asian Width properties). +hangul = '코드포인트 수는 적으나 실제 터미널이나 에디터에서 렌더링될 땐 너무 길어서 줄바꿈이 필요한 문자열' +hanzi = '中文測試:代碼點數量少,但在真正的終端模擬器或編輯器中呈現時太長,因此需要換行的字符串。' +japanese = 'コードポイントの数は少ないが、実際の端末エミュレータやエディタでレンダリングされる時は長すぎる為、改行が要る文字列' +``` + +## Black Differences + +```diff +--- Black ++++ Ruff +@@ -1,16 +1,6 @@ + # The following strings do not have not-so-many chars, but are long enough + # when these are rendered in a monospace font (if the renderer respects + # Unicode East Asian Width properties). +-hangul = ( +- "코드포인트 수는 적으나 실제 터미널이나 에디터에서 렌더링될 땐 너무 길어서 줄바꿈이" +- " 필요한 문자열" +-) +-hanzi = ( +- "中文測試:代碼點數量少,但在真正的終端模擬器或編輯器中呈現時太長," +- "因此需要換行的字符串。" +-) +-japanese = ( +- "コードポイントの数は少ないが、" +- "実際の端末エミュレータやエディタでレンダリングされる時は長すぎる為、" +- "改行が要る文字列" +-) ++hangul = "코드포인트 수는 적으나 실제 터미널이나 에디터에서 렌더링될 땐 너무 길어서 줄바꿈이 필요한 문자열" ++hanzi = "中文測試:代碼點數量少,但在真正的終端模擬器或編輯器中呈現時太長,因此需要換行的字符串。" ++japanese = "コードポイントの数は少ないが、実際の端末エミュレータやエディタでレンダリングされる時は長すぎる為、改行が要る文字列" +``` + +## Ruff Output + +```python +# The following strings do not have not-so-many chars, but are long enough +# when these are rendered in a monospace font (if the renderer respects +# Unicode East Asian Width properties). +hangul = "코드포인트 수는 적으나 실제 터미널이나 에디터에서 렌더링될 땐 너무 길어서 줄바꿈이 필요한 문자열" +hanzi = "中文測試:代碼點數量少,但在真正的終端模擬器或編輯器中呈現時太長,因此需要換行的字符串。" +japanese = "コードポイントの数は少ないが、実際の端末エミュレータやエディタでレンダリングされる時は長すぎる為、改行が要る文字列" +``` + +## Black Output + +```python +# The following strings do not have not-so-many chars, but are long enough +# when these are rendered in a monospace font (if the renderer respects +# Unicode East Asian Width properties). +hangul = ( + "코드포인트 수는 적으나 실제 터미널이나 에디터에서 렌더링될 땐 너무 길어서 줄바꿈이" + " 필요한 문자열" +) +hanzi = ( + "中文測試:代碼點數量少,但在真正的終端模擬器或編輯器中呈現時太長," + "因此需要換行的字符串。" +) +japanese = ( + "コードポイントの数は少ないが、" + "実際の端末エミュレータやエディタでレンダリングされる時は長すぎる為、" + "改行が要る文字列" +) +``` + + diff --git a/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__preview_long_strings__edge_case.py.snap b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__preview_long_strings__edge_case.py.snap new file mode 100644 index 0000000000..4db3fa039c --- /dev/null +++ b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__preview_long_strings__edge_case.py.snap @@ -0,0 +1,340 @@ +--- +source: crates/ruff_python_formatter/tests/fixtures.rs +input_file: crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_long_strings__edge_case.py +--- +## Input + +```python +some_variable = "This string is long but not so long that it needs to be split just yet" +some_variable = 'This string is long but not so long that it needs to be split just yet' +some_variable = "This string is long, just long enough that it needs to be split, u get?" +some_variable = 'This string is long, just long enough that it needs to be split, u get?' +some_variable = "This string is long, just long enough that it needs to be split, u get? So we stay" +some_variable = 'This string is long, just long enough that it needs to be split, u get? So we stay' +some_variable = "This string is long, just long enough that it needs to be split, u get? So we split" +some_variable = 'This string is long, just long enough that it needs to be split, u get? So we split' +some_variable = "This string is long but not so long that it needs hahahah toooooo be so greatttt {} that I just can't think of any more good words to say about it at alll".format("ha") +some_variable = "This string is long but not so long that it needs hahahah toooooo be so greatttt {} that I just can't think of any more good words to say about it at allll".format("ha") +some_variable = "This string is long but not so long that it needs hahahah toooooo be so greatttt {} that I just can't think of any more good words to say about it at alllllllllll".format("ha") +some_variable = "This string is long but not so long that it needs hahahah toooooo be so greatttt {} that I just can't think of any more good words to say about it at allllllllllll".format("ha") +some_variable = "This is a long string that will end with a method that is not calleddd".format +addition_inside_tuple = ( + some_string_inside_a_variable + + "Some string that is just long enough to cause a split to take place.............", + xyz, + "Some really long string that needs to get split eventually but I'm running out of things to say" + some_string_inside_a_variable +) +addition_inside_tuple = ( + some_string_inside_a_variable + + "Some string that is just long enough to cause a split to take place.............." +) +return "Hi there. This is areally really reallllly long string that needs to be split!!!" +ternary_expression = ( + "Short String" + if some_condition + else "This is a really long string that will eventually need to be split right here." +) +return f'{x}/b/c/d/d/d/dadfjsadjsaidoaisjdsfjaofjdfijaidfjaodfjaoifjodjafojdoajaaaaaaaaaaa' +return f'{x}/b/c/d/d/d/dadfjsadjsaidoaisjdsfjaofjdfijaidfjaodfjaoifjodjafojdoajaaaaaaaaaaaa' +assert str(result) == "This long string should be split at some point right close to or around hereeeeeee" +assert str(result) < "This long string should be split at some point right close to or around hereeeeee" +assert "A format string: %s" % "This long string should be split at some point right close to or around hereeeeeee" != result +msg += "This long string should be wrapped in parens at some point right around hereeeee" +msg += "This long string should be split at some point right close to or around hereeeeeeee" +msg += "This long string should not be split at any point ever since it is just righttt" +``` + +## Black Differences + +```diff +--- Black ++++ Ruff +@@ -12,51 +12,33 @@ + some_variable = ( + "This string is long, just long enough that it needs to be split, u get? So we stay" + ) +-some_variable = ( +- "This string is long, just long enough that it needs to be split, u get? So we" +- " split" +-) +-some_variable = ( +- "This string is long, just long enough that it needs to be split, u get? So we" +- " split" +-) +-some_variable = ( +- "This string is long but not so long that it needs hahahah toooooo be so greatttt" +- " {} that I just can't think of any more good words to say about it at alll".format( +- "ha" +- ) ++some_variable = "This string is long, just long enough that it needs to be split, u get? So we split" ++some_variable = "This string is long, just long enough that it needs to be split, u get? So we split" ++some_variable = "This string is long but not so long that it needs hahahah toooooo be so greatttt {} that I just can't think of any more good words to say about it at alll".format( ++ "ha" + ) +-some_variable = ( +- "This string is long but not so long that it needs hahahah toooooo be so greatttt" +- " {} that I just can't think of any more good words to say about it at allll" +- .format("ha") ++some_variable = "This string is long but not so long that it needs hahahah toooooo be so greatttt {} that I just can't think of any more good words to say about it at allll".format( ++ "ha" + ) +-some_variable = ( +- "This string is long but not so long that it needs hahahah toooooo be so greatttt" +- " {} that I just can't think of any more good words to say about it at alllllllllll" +- .format("ha") ++some_variable = "This string is long but not so long that it needs hahahah toooooo be so greatttt {} that I just can't think of any more good words to say about it at alllllllllll".format( ++ "ha" + ) +-some_variable = ( +- "This string is long but not so long that it needs hahahah toooooo be so greatttt" +- " {} that I just can't think of any more good words to say about it at" +- " allllllllllll".format("ha") ++some_variable = "This string is long but not so long that it needs hahahah toooooo be so greatttt {} that I just can't think of any more good words to say about it at allllllllllll".format( ++ "ha" + ) + some_variable = ( + "This is a long string that will end with a method that is not calleddd".format + ) + addition_inside_tuple = ( + some_string_inside_a_variable +- + "Some string that is just long enough to cause a split to take" +- " place.............", ++ + "Some string that is just long enough to cause a split to take place.............", + xyz, +- "Some really long string that needs to get split eventually but I'm running out of" +- " things to say" ++ "Some really long string that needs to get split eventually but I'm running out of things to say" + + some_string_inside_a_variable, + ) + addition_inside_tuple = ( + some_string_inside_a_variable +- + "Some string that is just long enough to cause a split to take" +- " place.............." ++ + "Some string that is just long enough to cause a split to take place.............." + ) + return ( + "Hi there. This is areally really reallllly long string that needs to be split!!!" +@@ -64,9 +46,7 @@ + ternary_expression = ( + "Short String" + if some_condition +- else ( +- "This is a really long string that will eventually need to be split right here." +- ) ++ else "This is a really long string that will eventually need to be split right here." + ) + return ( + f"{x}/b/c/d/d/d/dadfjsadjsaidoaisjdsfjaofjdfijaidfjaodfjaoifjodjafojdoajaaaaaaaaaaa" +@@ -74,25 +54,19 @@ + return f"{x}/b/c/d/d/d/dadfjsadjsaidoaisjdsfjaofjdfijaidfjaodfjaoifjodjafojdoajaaaaaaaaaaaa" + assert ( + str(result) +- == "This long string should be split at some point right close to or around" +- " hereeeeeee" ++ == "This long string should be split at some point right close to or around hereeeeeee" + ) + assert ( + str(result) +- < "This long string should be split at some point right close to or around" +- " hereeeeee" ++ < "This long string should be split at some point right close to or around hereeeeee" + ) + assert ( + "A format string: %s" +- % "This long string should be split at some point right close to or around" +- " hereeeeeee" ++ % "This long string should be split at some point right close to or around hereeeeeee" + != result + ) + msg += ( + "This long string should be wrapped in parens at some point right around hereeeee" + ) +-msg += ( +- "This long string should be split at some point right close to or around" +- " hereeeeeeee" +-) ++msg += "This long string should be split at some point right close to or around hereeeeeeee" + msg += "This long string should not be split at any point ever since it is just righttt" +``` + +## Ruff Output + +```python +some_variable = "This string is long but not so long that it needs to be split just yet" +some_variable = "This string is long but not so long that it needs to be split just yet" +some_variable = ( + "This string is long, just long enough that it needs to be split, u get?" +) +some_variable = ( + "This string is long, just long enough that it needs to be split, u get?" +) +some_variable = ( + "This string is long, just long enough that it needs to be split, u get? So we stay" +) +some_variable = ( + "This string is long, just long enough that it needs to be split, u get? So we stay" +) +some_variable = "This string is long, just long enough that it needs to be split, u get? So we split" +some_variable = "This string is long, just long enough that it needs to be split, u get? So we split" +some_variable = "This string is long but not so long that it needs hahahah toooooo be so greatttt {} that I just can't think of any more good words to say about it at alll".format( + "ha" +) +some_variable = "This string is long but not so long that it needs hahahah toooooo be so greatttt {} that I just can't think of any more good words to say about it at allll".format( + "ha" +) +some_variable = "This string is long but not so long that it needs hahahah toooooo be so greatttt {} that I just can't think of any more good words to say about it at alllllllllll".format( + "ha" +) +some_variable = "This string is long but not so long that it needs hahahah toooooo be so greatttt {} that I just can't think of any more good words to say about it at allllllllllll".format( + "ha" +) +some_variable = ( + "This is a long string that will end with a method that is not calleddd".format +) +addition_inside_tuple = ( + some_string_inside_a_variable + + "Some string that is just long enough to cause a split to take place.............", + xyz, + "Some really long string that needs to get split eventually but I'm running out of things to say" + + some_string_inside_a_variable, +) +addition_inside_tuple = ( + some_string_inside_a_variable + + "Some string that is just long enough to cause a split to take place.............." +) +return ( + "Hi there. This is areally really reallllly long string that needs to be split!!!" +) +ternary_expression = ( + "Short String" + if some_condition + else "This is a really long string that will eventually need to be split right here." +) +return ( + f"{x}/b/c/d/d/d/dadfjsadjsaidoaisjdsfjaofjdfijaidfjaodfjaoifjodjafojdoajaaaaaaaaaaa" +) +return f"{x}/b/c/d/d/d/dadfjsadjsaidoaisjdsfjaofjdfijaidfjaodfjaoifjodjafojdoajaaaaaaaaaaaa" +assert ( + str(result) + == "This long string should be split at some point right close to or around hereeeeeee" +) +assert ( + str(result) + < "This long string should be split at some point right close to or around hereeeeee" +) +assert ( + "A format string: %s" + % "This long string should be split at some point right close to or around hereeeeeee" + != result +) +msg += ( + "This long string should be wrapped in parens at some point right around hereeeee" +) +msg += "This long string should be split at some point right close to or around hereeeeeeee" +msg += "This long string should not be split at any point ever since it is just righttt" +``` + +## Black Output + +```python +some_variable = "This string is long but not so long that it needs to be split just yet" +some_variable = "This string is long but not so long that it needs to be split just yet" +some_variable = ( + "This string is long, just long enough that it needs to be split, u get?" +) +some_variable = ( + "This string is long, just long enough that it needs to be split, u get?" +) +some_variable = ( + "This string is long, just long enough that it needs to be split, u get? So we stay" +) +some_variable = ( + "This string is long, just long enough that it needs to be split, u get? So we stay" +) +some_variable = ( + "This string is long, just long enough that it needs to be split, u get? So we" + " split" +) +some_variable = ( + "This string is long, just long enough that it needs to be split, u get? So we" + " split" +) +some_variable = ( + "This string is long but not so long that it needs hahahah toooooo be so greatttt" + " {} that I just can't think of any more good words to say about it at alll".format( + "ha" + ) +) +some_variable = ( + "This string is long but not so long that it needs hahahah toooooo be so greatttt" + " {} that I just can't think of any more good words to say about it at allll" + .format("ha") +) +some_variable = ( + "This string is long but not so long that it needs hahahah toooooo be so greatttt" + " {} that I just can't think of any more good words to say about it at alllllllllll" + .format("ha") +) +some_variable = ( + "This string is long but not so long that it needs hahahah toooooo be so greatttt" + " {} that I just can't think of any more good words to say about it at" + " allllllllllll".format("ha") +) +some_variable = ( + "This is a long string that will end with a method that is not calleddd".format +) +addition_inside_tuple = ( + some_string_inside_a_variable + + "Some string that is just long enough to cause a split to take" + " place.............", + xyz, + "Some really long string that needs to get split eventually but I'm running out of" + " things to say" + + some_string_inside_a_variable, +) +addition_inside_tuple = ( + some_string_inside_a_variable + + "Some string that is just long enough to cause a split to take" + " place.............." +) +return ( + "Hi there. This is areally really reallllly long string that needs to be split!!!" +) +ternary_expression = ( + "Short String" + if some_condition + else ( + "This is a really long string that will eventually need to be split right here." + ) +) +return ( + f"{x}/b/c/d/d/d/dadfjsadjsaidoaisjdsfjaofjdfijaidfjaodfjaoifjodjafojdoajaaaaaaaaaaa" +) +return f"{x}/b/c/d/d/d/dadfjsadjsaidoaisjdsfjaofjdfijaidfjaodfjaoifjodjafojdoajaaaaaaaaaaaa" +assert ( + str(result) + == "This long string should be split at some point right close to or around" + " hereeeeeee" +) +assert ( + str(result) + < "This long string should be split at some point right close to or around" + " hereeeeee" +) +assert ( + "A format string: %s" + % "This long string should be split at some point right close to or around" + " hereeeeeee" + != result +) +msg += ( + "This long string should be wrapped in parens at some point right around hereeeee" +) +msg += ( + "This long string should be split at some point right close to or around" + " hereeeeeeee" +) +msg += "This long string should not be split at any point ever since it is just righttt" +``` + + 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 new file mode 100644 index 0000000000..e05940e360 --- /dev/null +++ b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__preview_long_strings__regression.py.snap @@ -0,0 +1,2565 @@ +--- +source: crates/ruff_python_formatter/tests/fixtures.rs +input_file: crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_long_strings__regression.py +--- +## Input + +```python +class A: + def foo(): + result = type(message)("") + + +# Don't merge multiline (e.g. triple-quoted) strings. +def foo(): + query = ( + """SELECT xxxxxxxxxxxxxxxxxxxx(xxx)""" + """ FROM xxxxxxxxxxxxxxxx WHERE xxxxxxxxxx AND xxx <> xxxxxxxxxxxxxx()""") + +# There was a bug where tuples were being identified as long strings. +long_tuple = ('Apple', 'Berry', 'Cherry', 'Dill', 'Evergreen', 'Fig', + 'Grape', 'Harry', 'Iglu', 'Jaguar') + +stupid_format_method_bug = "Some really long string that just so happens to be the {} {} to force the 'format' method to hang over the line length boundary. This is pretty annoying.".format("perfect", "length") + +class A: + def foo(): + os.system("This is a regression test. xxx xxx xxx xxx xxx xxx xxx xxx xxx xxx xxx xxx xxx xxx xxx xxx xxx xxx xxx xxx xxx xxx xxx xxx xxx xxx xxx xxx xxx xxx xxxx.".format("xxxxxxxxxx", "xxxxxx", "xxxxxxxxxx")) + + +class A: + def foo(): + XXXXXXXXXXXX.append( + ( + "xxx_xxxxxxxxxx(xxxxx={}, xxxx={}, xxxxx, xxxx_xxxx_xxxxxxxxxx={})".format( + xxxxx, xxxx, xxxx_xxxx_xxxxxxxxxx + ), + my_var, + my_other_var, + ) + ) + +class A: + class B: + def foo(): + bar( + ( + "[{}]: xxx_xxxxxxxxxx(xxxxx={}, xxxx={}, xxxxx={}" + " xxxx_xxxx_xxxxxxxxxx={}, xxxx={})" + .format(xxxx._xxxxxxxxxxxxxx, xxxxx, xxxx, xxxx_xxxx_xxxxxxxxxx, xxxxxxx) + ), + varX, + varY, + varZ, + ) + +def foo(xxxx): + for (xxx_xxxx, _xxx_xxx, _xxx_xxxxx, xxx_xxxx) in xxxx: + for xxx in xxx_xxxx: + assert ("x" in xxx) or ( + xxx in xxx_xxx_xxxxx + ), "{0} xxxxxxx xx {1}, xxx {1} xx xxx xx xxxx xx xxx xxxx: xxx xxxx {2}".format( + xxx_xxxx, xxx, xxxxxx.xxxxxxx(xxx_xxx_xxxxx) + ) + +class A: + def disappearing_comment(): + return ( + ( # xx -x xxxxxxx xx xxx xxxxxxx. + '{{xxx_xxxxxxxxxx_xxxxxxxx}} xxx xxxx' + ' {} {{xxxx}} >&2' + .format( + "{xxxx} {xxxxxx}" + if xxxxx.xx_xxxxxxxxxx + else ( # Disappearing Comment + "--xxxxxxx --xxxxxx=x --xxxxxx-xxxxx=xxxxxx" + " --xxxxxx-xxxx=xxxxxxxxxxx.xxx" + ) + ) + ), + (x, y, z), + ) + +class A: + class B: + def foo(): + xxxxx_xxxx( + xx, "\t" + "@xxxxxx '{xxxx_xxx}\t' > {xxxxxx_xxxx}.xxxxxxx;" + "{xxxx_xxx} >> {xxxxxx_xxxx}.xxxxxxx 2>&1; xx=$$?;" + "xxxx $$xx" + .format(xxxx_xxx=xxxx_xxxxxxx, xxxxxx_xxxx=xxxxxxx + "/" + xxxx_xxx_xxxx, x=xxx_xxxxx_xxxxx_xxx), + x, + y, + z, + ) + +func_call_where_string_arg_has_method_call_and_bad_parens( + ( + "A long string with {}. This string is so long that it is ridiculous. It can't fit on one line at alllll.".format("formatting") + ), +) + +func_call_where_string_arg_has_old_fmt_and_bad_parens( + ( + "A long string with {}. This string is so long that it is ridiculous. It can't fit on one line at alllll." % "formatting" + ), +) + +func_call_where_string_arg_has_old_fmt_and_bad_parens( + ( + "A long string with {}. This {} is so long that it is ridiculous. It can't fit on one line at alllll." % ("formatting", "string") + ), +) + +class A: + def append(self): + if True: + xxxx.xxxxxxx.xxxxx( ('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' + % (xxxx.xxxxxxxxx, + xxxx.xxxxxxxxxxxxxx(xxxx.xxxxxxxxx), + x, + xxxx.xxxxxxxxxxxxxx( xx) + ))) + +class A: + def foo(): + some_func_call( + 'xxxxxxxxxx', + ( + "xx {xxxxxxxxxxx}/xxxxxxxxxxx.xxx xxxx.xxx && xxxxxx -x " + "\"xxxx xxxxxxx xxxxxx xxxx; xxxx xxxxxx_xxxxx xxxxxx xxxx; " + "xxxx.xxxx_xxxxxx(['xxxx.xxx'], xxxx.xxxxxxx().xxxxxxxxxx)\" " + ), + None, + ('xxxxxxxxxxx',), + ), + +class A: + def foo(): + some_func_call( + ( + "xx {xxxxxxxxxxx}/xxxxxxxxxxx.xxx xxxx.xxx && xxxxxx -x " + "xxxx, ('xxxxxxx xxxxxx xxxx, xxxx') xxxxxx_xxxxx xxxxxx xxxx; " + "xxxx.xxxx_xxxxxx(['xxxx.xxx'], xxxx.xxxxxxx().xxxxxxxxxx)\" " + ), + None, + ('xxxxxxxxxxx',), + ), + +xxxxxxx = { 'xx' : 'xxxx xxxxxxx xxxxxxxxx -x xxx -x /xxx/{0} -x xxx,xxx -xx {1} \ +-xx {1} -xx xxx=xxx_xxxx,xxx_xx,xxx_xxx,xxx_xxxx,xxx_xx,xxx_xxx |\ + xxxxxx -x xxxxxxxx -x xxxxxxxx -x', + 'xx' : 'xxxx xxxxxxx xxxxxxxxx -x xxx -x /xxx/{0} -x xxx,xxx -xx {1} \ +-xx {1} -xx xxx=xxx_xxxx_xxx_xxxx,xxx_xx_xxx_xxxx,xxx_xxxx_xxx_xxxx,\ +xxx_xx_xxxx_xxxx,xxx_xxx_xxxx,xxx_xxx_xxxx xxxx=xxx | xxxxxx -x xxxxxxxx -x xxxxxxxx -x' +} + +class A: + def foo(self): + if True: + xxxxx_xxxxxxxxxxxx('xxx xxxxxx xxx xxxxxxxxx.xx xx xxxxxxxx. xxx xxxxxxxxxxxxx.xx xxxxxxx ' + + 'xx xxxxxx xxxxxx xxxxxx xx xxxxxxx xxx xxx ${0} xx x xxxxxxxx xxxxx'.xxxxxx(xxxxxx_xxxxxx_xxx)) + +class A: + class B: + def foo(): + row = { + 'xxxxxxxxxxxxxxx' : xxxxxx_xxxxx_xxxx, + # 'xxxxxxxxxxxxxxxxxxxxxxx' + # 'xxxxxxxxxxxxxxxxxxxxxx' + # 'xxxxxxxxxxxxxxxxxx' + # 'xxxxxxxxxxxxxxxxx' + 'xxxxxxxxxx' : xxxxx_xxxxx, + } + +class A: + def xxxx_xxx_xx_xxxxxxxxxx_xxxx_xxxxxxxxx(xxxx): + xxxxxxxx = [ + xxxxxxxxxxxxxxxx( + 'xxxx', + xxxxxxxxxxx={ + 'xxxx' : 1.0, + }, + xxxxxx={'xxxxxx 1' : xxxxxx(xxxx='xxxxxx 1', xxxxxx=600.0)}, + xxxxxxxx_xxxxxxx=0.0, + ), + xxxxxxxxxxxxxxxx( + 'xxxxxxx', + xxxxxxxxxxx={ + 'xxxx' : 1.0, + }, + xxxxxx={'xxxxxx 1' : xxxxxx(xxxx='xxxxxx 1', xxxxxx=200.0)}, + xxxxxxxx_xxxxxxx=0.0, + ), + xxxxxxxxxxxxxxxx( + 'xxxx', + ), + ] + +some_dictionary = { + 'xxxxx006': ['xxx-xxx xxxxx3xxxx1xx2xxxxxxxxxxxxxx0xx6xxxxxxxxxx2xxxxxx9xxxxxxxxxx0xxxxx1xxx2x/xx9xx6+x+xxxxxxxxxxxxxx4xxxxxxxxxxxxxxxxxxxxx43xxx2xx2x4x++xxx6xxxxxxxxx+xxxxx/xx9x+xxxxxxxxxxxxxx8x15xxxxxxxxxxxxxxxxx82xx/xxxxxxxxxxxxxx/x5xxxxxxxxxxxxxx6xxxxxx74x4/xxx4x+xxxxxxxxx2xxxxxxxx87xxxxx4xxxxxxxx3xx0xxxxx4xxx1xx9xx5xxxxxxx/xxxxx5xx6xx4xxxx1x/x2xxxxxxxxxxxx64xxxxxxx1x0xx5xxxxxxxxxxxxxx== xxxxx000 xxxxxxxxxx\n', + 'xxx-xxx xxxxx3xxxx1xx2xxxxxxxxxxxxxx6xxxxxxxxxxxxxx9xxxxxxxxxxxxx3xxx9xxxxxxxxxxxxxxxx0xxxxxxxxxxxxxxxxx2xxxx2xxx6xxxxx/xx54xxxxxxxxx4xxx3xxxxxx9xx3xxxxx39xxxxxxxxx5xx91xxxx7xxxxxx8xxxxxxxxxxxxxxxx9xxx93xxxxxxxxxxxxxxxxx7xxx8xx8xx4/x1xxxxx1x3xxxxxxxxxxxxx3xxxxxx9xx4xx4x7xxxxxxxxxxxxx1xxxxxxxxx7xxxxxxxxxxxxxx4xx6xxxxxxxxx9xxx7xxxx2xxxxxxxxxxxxxxxxxxxxxx8xxxxxxxxxxxxxxxxxxxx6xx== xxxxx010 xxxxxxxxxx\n'], + 'xxxxx016': ['xxx-xxx xxxxx3xxxx1xx2xxxxxxxxxxxxxx0xx6xxxxxxxxxx2xxxxxx9xxxxxxxxxx0xxxxx1xxx2x/xx9xx6+x+xxxxxxxxxxxxxx4xxxxxxxxxxxxxxxxxxxxx43xxx2xx2x4x++xxx6xxxxxxxxx+xxxxx/xx9x+xxxxxxxxxxxxxx8x15xxxxxxxxxxxxxxxxx82xx/xxxxxxxxxxxxxx/x5xxxxxxxxxxxxxx6xxxxxx74x4/xxx4x+xxxxxxxxx2xxxxxxxx87xxxxx4xxxxxxxx3xx0xxxxx4xxx1xx9xx5xxxxxxx/xxxxx5xx6xx4xxxx1x/x2xxxxxxxxxxxx64xxxxxxx1x0xx5xxxxxxxxxxxxxx== xxxxx000 xxxxxxxxxx\n', + 'xxx-xxx xxxxx3xxxx1xx2xxxxxxxxxxxxxx6xxxxxxxxxxxxxx9xxxxxxxxxxxxx3xxx9xxxxxxxxxxxxxxxx0xxxxxxxxxxxxxxxxx2xxxx2xxx6xxxxx/xx54xxxxxxxxx4xxx3xxxxxx9xx3xxxxx39xxxxxxxxx5xx91xxxx7xxxxxx8xxxxxxxxxxxxxxxx9xxx93xxxxxxxxxxxxxxxxx7xxx8xx8xx4/x1xxxxx1x3xxxxxxxxxxxxx3xxxxxx9xx4xx4x7xxxxxxxxxxxxx1xxxxxxxxx7xxxxxxxxxxxxxx4xx6xxxxxxxxx9xxx7xxxx2xxxxxxxxxxxxxxxxxxxxxx8xxxxxxxxxxxxxxxxxxxx6xx== xxxxx010 xxxxxxxxxx\n'] +} + +def foo(): + xxx_xxx = ( + 'xxxx xxx xxxxxxxx_xxxx xx "xxxxxxxxxx".' + '\n xxx: xxxxxx xxxxxxxx_xxxx=xxxxxxxxxx' + ) # xxxx xxxxxxxxxx xxxx xx xxxx xx xxx xxxxxxxx xxxxxx xxxxx. + +some_tuple = ("some string", "some string" " which should be joined") + +some_commented_string = ( # This comment stays at the top. + "This string is long but not so long that it needs hahahah toooooo be so greatttt" + " {} that I just can't think of any more good words to say about it at" + " allllllllllll".format("ha") # comments here are fine +) + +some_commented_string = ( + "This string is long but not so long that it needs hahahah toooooo be so greatttt" # But these + " {} that I just can't think of any more good words to say about it at" # comments will stay + " allllllllllll".format("ha") # comments here are fine +) + +lpar_and_rpar_have_comments = func_call( # LPAR Comment + "Long really ridiculous type of string that shouldn't really even exist at all. I mean commmme onnn!!!", # Comma Comment +) # RPAR Comment + +cmd_fstring = ( + f"sudo -E deluge-console info --detailed --sort-reverse=time_added " + f"{'' if ID is None else ID} | perl -nE 'print if /^{field}:/'" +) + +cmd_fstring = f"sudo -E deluge-console info --detailed --sort-reverse=time_added {'' if ID is None else ID} | perl -nE 'print if /^{field}:/'" + +cmd_fstring = f"sudo -E deluge-console info --detailed --sort-reverse=time_added {'{{}}' if ID is None else ID} | perl -nE 'print if /^{field}:/'" + +cmd_fstring = f"sudo -E deluge-console info --detailed --sort-reverse=time_added {{'' if ID is None else ID}} | perl -nE 'print if /^{field}:/'" + +fstring = f"This string really doesn't need to be an {{{{fstring}}}}, but this one most certainly, absolutely {does}." + +fstring = ( + f"We have to remember to escape {braces}." + " Like {these}." + f" But not {this}." +) + +class A: + class B: + def foo(): + st_error = STError( + f"This string ({string_leaf.value}) appears to be pointless (i.e. has" + " no parent)." + ) + +def foo(): + user_regex = _lazy_re_compile( + r"(^[-!#$%&'*+/=?^_`{}|~0-9A-Z]+(\.[-!#$%&'*+/=?^_`{}|~0-9A-Z]+)*\Z" # dot-atom + r'|^"([\001-\010\013\014\016-\037!#-\[\]-\177]|\\[\001-\011\013\014\016-\177])*"\Z)', # quoted-string + re.IGNORECASE) + +def foo(): + user_regex = _lazy_re_compile( + "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" # dot-atom + 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx', # quoted-string + xyz + ) + +def foo(): + user_regex = _lazy_re_compile( + "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" # dot-atom + 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx', # quoted-string + xyz + ) + +class A: + class B: + def foo(): + if not hasattr(module, name): + raise ValueError( + "Could not find object %s in %s.\n" + "Please note that you cannot serialize things like inner " + "classes. Please move the object into the main module " + "body to use migrations.\n" + "For more information, see " + "https://docs.djangoproject.com/en/%s/topics/migrations/#serializing-values" + % (name, module_name, get_docs_version())) + +class A: + class B: + def foo(): + if not hasattr(module, name): + raise ValueError( + "Could not find object %s in %s.\nPlease note that you cannot serialize things like inner classes. Please move the object into the main module body to use migrations.\nFor more information, see https://docs.djangoproject.com/en/%s/topics/migrations/#serializing-values" + % (name, module_name, get_docs_version())) + +x = ( + "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" + "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" + "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" + "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" +) + +class Step(StepBase): + def who(self): + self.cmd = 'SR AAAA-CORRECT NAME IS {last_name} {first_name}{middle_name} {title}/P{passenger_association}'.format( + last_name=last_name, + first_name=first_name, + middle_name=middle_name, + title=title, + passenger_association=passenger_association, + ) + +xxxxxxx_xxxxxx_xxxxxxx = xxx( + [ + xxxxxxxxxxxx( + xxxxxx_xxxxxxx=( + '((x.aaaaaaaaa = "xxxxxx.xxxxxxxxxxxxxxxxxxxxx") || (x.xxxxxxxxx = "xxxxxxxxxxxx")) && ' + # xxxxx xxxxxxxxxxxx xxxx xxx (xxxxxxxxxxxxxxxx) xx x xxxxxxxxx xx xxxxxx. + "(x.bbbbbbbbbbbb.xxx != " + '"xxx:xxx:xxx::cccccccccccc:xxxxxxx-xxxx/xxxxxxxxxxx/xxxxxxxxxxxxxxxxx") && ' + ) + ) + ] +) + +if __name__ == "__main__": + for i in range(4, 8): + cmd = ( + r"for pid in $(ps aux | grep paster | grep -v grep | grep '\-%d' | awk '{print $2}'); do kill $pid; done" + % (i) + ) + +def A(): + def B(): + def C(): + def D(): + def E(): + def F(): + def G(): + assert ( + c_float(val[0][0] / val[0][1]).value + == c_float(value[0][0] / value[0][1]).value + ), "%s didn't roundtrip" % tag + +class xxxxxxxxxxxxxxxxxxxxx(xxxx.xxxxxxxxxxxxx): + def xxxxxxx_xxxxxx(xxxx): + assert xxxxxxx_xxxx in [ + x.xxxxx.xxxxxx.xxxxx.xxxxxx, + x.xxxxx.xxxxxx.xxxxx.xxxx, + ], ("xxxxxxxxxxx xxxxxxx xxxx (xxxxxx xxxx) %x xxx xxxxx" % xxxxxxx_xxxx) + +value.__dict__[ + key +] = "test" # set some Thrift field to non-None in the struct aa bb cc dd ee + +RE_ONE_BACKSLASH = { + "asdf_hjkl_jkl": re.compile( + r"(?>\n" +) + +assert str(suffix_arr) == ( + "['$', 'angaroo$', 'angrykangaroo$', 'aroo$', 'garoo$', " + "'grykangaroo$', 'kangaroo$', 'ngaroo$', 'ngrykangaroo$', " + "'o$', 'oo$', 'roo$', 'rykangaroo$', 'ykangaroo$']" +) +assert str(suffix_arr) != ( + "['$', 'angaroo$', 'angrykangaroo$', 'aroo$', 'garoo$', " + "'grykangaroo$', 'kangaroo$', 'ngaroo$', 'ngrykangaroo$', " + "'o$', 'oo$', 'roo$', 'rykangaroo$', 'ykangaroo$']" +) +assert str(suffix_arr) <= ( + "['$', 'angaroo$', 'angrykangaroo$', 'aroo$', 'garoo$', " + "'grykangaroo$', 'kangaroo$', 'ngaroo$', 'ngrykangaroo$', " + "'o$', 'oo$', 'roo$', 'rykangaroo$', 'ykangaroo$']" +) +assert str(suffix_arr) >= ( + "['$', 'angaroo$', 'angrykangaroo$', 'aroo$', 'garoo$', " + "'grykangaroo$', 'kangaroo$', 'ngaroo$', 'ngrykangaroo$', " + "'o$', 'oo$', 'roo$', 'rykangaroo$', 'ykangaroo$']" +) +assert str(suffix_arr) < ( + "['$', 'angaroo$', 'angrykangaroo$', 'aroo$', 'garoo$', " + "'grykangaroo$', 'kangaroo$', 'ngaroo$', 'ngrykangaroo$', " + "'o$', 'oo$', 'roo$', 'rykangaroo$', 'ykangaroo$']" +) +assert str(suffix_arr) > ( + "['$', 'angaroo$', 'angrykangaroo$', 'aroo$', 'garoo$', " + "'grykangaroo$', 'kangaroo$', 'ngaroo$', 'ngrykangaroo$', " + "'o$', 'oo$', 'roo$', 'rykangaroo$', 'ykangaroo$']" +) +assert str(suffix_arr) in "['$', 'angaroo$', 'angrykangaroo$', 'aroo$', 'garoo$', 'grykangaroo$', 'kangaroo$', 'ngaroo$', 'ngrykangaroo$', 'o$', 'oo$', 'roo$', 'rykangaroo$', 'ykangaroo$']" +assert str(suffix_arr) not in "['$', 'angaroo$', 'angrykangaroo$', 'aroo$', 'garoo$', 'grykangaroo$', 'kangaroo$', 'ngaroo$', 'ngrykangaroo$', 'o$', 'oo$', 'roo$', 'rykangaroo$', 'ykangaroo$']" +message = ( + f"1. Go to Google Developers Console and log in with your Google account." + "(https://console.developers.google.com/)" + "2. You should be prompted to create a new project (name does not matter)." + "3. Click on Enable APIs and Services at the top." + "4. In the list of APIs choose or search for YouTube Data API v3 and " + "click on it. Choose Enable." + "5. Click on Credentials on the left navigation bar." + "6. Click on Create Credential at the top." + '7. At the top click the link for "API key".' + "8. No application restrictions are needed. Click Create at the bottom." + "9. You now have a key to add to `{prefix}set api youtube api_key`" +) +message = ( + f"1. Go to Google Developers Console and log in with your Google account." + "(https://console.developers.google.com/)" + "2. You should be prompted to create a new project (name does not matter)." + f"3. Click on Enable APIs and Services at the top." + "4. In the list of APIs choose or search for YouTube Data API v3 and " + "click on it. Choose Enable." + f"5. Click on Credentials on the left navigation bar." + "6. Click on Create Credential at the top." + '7. At the top click the link for "API key".' + "8. No application restrictions are needed. Click Create at the bottom." + "9. You now have a key to add to `{prefix}set api youtube api_key`" +) +message = ( + f"1. Go to Google Developers Console and log in with your Google account." + "(https://console.developers.google.com/)" + "2. You should be prompted to create a new project (name does not matter)." + f"3. Click on Enable APIs and Services at the top." + "4. In the list of APIs choose or search for YouTube Data API v3 and " + "click on it. Choose Enable." + f"5. Click on Credentials on the left navigation bar." + "6. Click on Create Credential at the top." + '7. At the top click the link for "API key".' + "8. No application restrictions are needed. Click Create at the bottom." + f"9. You now have a key to add to `{prefix}set api youtube api_key`" +) + +# It shouldn't matter if the string prefixes are capitalized. +temp_msg = ( + F"{F'{humanize_number(pos)}.': <{pound_len+2}} " + F"{balance: <{bal_len + 5}} " + F"<<{author.display_name}>>\n" +) + +fstring = ( + F"We have to remember to escape {braces}." + " Like {these}." + F" But not {this}." +) + +welcome_to_programming = R"hello," R" world!" + +fstring = F"f-strings definitely make things more {difficult} than they need to be for {{black}}. But boy they sure are handy. The problem is that some lines will need to have the 'f' whereas others do not. This {line}, for example, needs one." + +x = F"This is a long string which contains an f-expr that should not split {{{[i for i in range(5)]}}}." + +x = ( + "\N{BLACK RIGHT-POINTING TRIANGLE WITH DOUBLE VERTICAL BAR}\N{VARIATION SELECTOR-16}" +) + +xxxxxx_xxx_xxxx_xx_xxxxx_xxxxxxxx_xxxxxxxx_xxxxxxxxxx_xxxx_xxxx_xxxxx = xxxx.xxxxxx.xxxxxxxxx.xxxxxxxxxxxxxxxxxxxx( + xx_xxxxxx={ + "x3_xxxxxxxx": "xxx3_xxxxx_xxxxxxxx_xxxxxxxx_xxxxxxxxxx_xxxxxxxx_xxxxxx_xxxxxxx", + }, +) + +# Regression test for https://github.com/psf/black/issues/3117. +some_dict = { + "something_something": + r"Lorem ipsum dolor sit amet, an sed convenire eloquentiam \t" + r"signiferumque, duo ea vocibus consetetur scriptorem. Facer \t", +} + +# Regression test for https://github.com/psf/black/issues/3459. +xxxx( + empty_str_as_first_split='' + f'xxxxxxx {xxxxxxxxxx} xxx xxxxxxxxxx xxxxx xxx xxx xx ' + 'xxxxx xxxxxxxxx xxxxxxx, xxx xxxxxxxxxxx xxx xxxxx. ' + f'xxxxxxxxxxxxx xxxx xx xxxxxxxxxx. xxxxx: {x.xxx}', + empty_u_str_as_first_split=u'' + f'xxxxxxx {xxxxxxxxxx} xxx xxxxxxxxxx xxxxx xxx xxx xx ' + 'xxxxx xxxxxxxxx xxxxxxx, xxx xxxxxxxxxxx xxx xxxxx. ' + f'xxxxxxxxxxxxx xxxx xx xxxxxxxxxx. xxxxx: {x.xxx}', +) + +# Regression test for https://github.com/psf/black/issues/3455. +a_dict = { + "/this/is/a/very/very/very/very/very/very/very/very/very/very/long/key/without/spaces": + # And there is a comment before the value + ("item1", "item2", "item3"), +} + +# Regression test for https://github.com/psf/black/issues/3506. +s = ( + "With single quote: ' " + f" {my_dict['foo']}" + ' With double quote: " ' + f' {my_dict["bar"]}' +) + +s = f'Lorem Ipsum is simply dummy text of the printing and typesetting industry:\'{my_dict["foo"]}\'' +``` + +## Black Differences + +```diff +--- Black ++++ Ruff +@@ -25,41 +25,42 @@ + "Jaguar", + ) + +-stupid_format_method_bug = ( +- "Some really long string that just so happens to be the {} {} to force the 'format'" +- " method to hang over the line length boundary. This is pretty annoying.".format( +- "perfect", "length" +- ) ++stupid_format_method_bug = "Some really long string that just so happens to be the {} {} to force the 'format' method to hang over the line length boundary. This is pretty annoying.".format( ++ "perfect", "length" + ) + + + class A: + def foo(): + os.system( +- "This is a regression test. xxx xxx xxx xxx xxx xxx xxx xxx xxx xxx xxx xxx" +- " xxx xxx xxx xxx xxx xxx xxx xxx xxx xxx xxx xxx xxx xxx xxx xxx xxx xxx" +- " xxxx.".format("xxxxxxxxxx", "xxxxxx", "xxxxxxxxxx") ++ "This is a regression test. xxx xxx xxx xxx xxx xxx xxx xxx xxx xxx xxx xxx xxx xxx xxx xxx xxx xxx xxx xxx xxx xxx xxx xxx xxx xxx xxx xxx xxx xxx xxxx.".format( ++ "xxxxxxxxxx", "xxxxxx", "xxxxxxxxxx" ++ ) + ) + + + class A: + def foo(): +- XXXXXXXXXXXX.append(( +- "xxx_xxxxxxxxxx(xxxxx={}, xxxx={}, xxxxx, xxxx_xxxx_xxxxxxxxxx={})".format( +- xxxxx, xxxx, xxxx_xxxx_xxxxxxxxxx +- ), +- my_var, +- my_other_var, +- )) ++ XXXXXXXXXXXX.append( ++ ( ++ "xxx_xxxxxxxxxx(xxxxx={}, xxxx={}, xxxxx, xxxx_xxxx_xxxxxxxxxx={})".format( ++ xxxxx, xxxx, xxxx_xxxx_xxxxxxxxxx ++ ), ++ my_var, ++ my_other_var, ++ ) ++ ) + + + class A: + class B: + def foo(): + bar( +- "[{}]: xxx_xxxxxxxxxx(xxxxx={}, xxxx={}, xxxxx={}" +- " xxxx_xxxx_xxxxxxxxxx={}, xxxx={})".format( +- xxxx._xxxxxxxxxxxxxx, xxxxx, xxxx, xxxx_xxxx_xxxxxxxxxx, xxxxxxx ++ ( ++ "[{}]: xxx_xxxxxxxxxx(xxxxx={}, xxxx={}, xxxxx={}" ++ " xxxx_xxxx_xxxxxxxxxx={}, xxxx={})".format( ++ xxxx._xxxxxxxxxxxxxx, xxxxx, xxxx, xxxx_xxxx_xxxxxxxxxx, xxxxxxx ++ ) + ), + varX, + varY, +@@ -70,9 +71,10 @@ + def foo(xxxx): + for xxx_xxxx, _xxx_xxx, _xxx_xxxxx, xxx_xxxx in xxxx: + for xxx in xxx_xxxx: +- assert ("x" in xxx) or (xxx in xxx_xxx_xxxxx), ( +- "{0} xxxxxxx xx {1}, xxx {1} xx xxx xx xxxx xx xxx xxxx: xxx xxxx {2}" +- .format(xxx_xxxx, xxx, xxxxxx.xxxxxxx(xxx_xxx_xxxxx)) ++ assert ( ++ ("x" in xxx) or (xxx in xxx_xxx_xxxxx) ++ ), "{0} xxxxxxx xx {1}, xxx {1} xx xxx xx xxxx xx xxx xxxx: xxx xxxx {2}".format( ++ xxx_xxxx, xxx, xxxxxx.xxxxxxx(xxx_xxx_xxxxx) + ) + + +@@ -80,10 +82,11 @@ + def disappearing_comment(): + return ( + ( # xx -x xxxxxxx xx xxx xxxxxxx. +- "{{xxx_xxxxxxxxxx_xxxxxxxx}} xxx xxxx {} {{xxxx}} >&2".format( ++ "{{xxx_xxxxxxxxxx_xxxxxxxx}} xxx xxxx" " {} {{xxxx}} >&2".format( + "{xxxx} {xxxxxx}" + if xxxxx.xx_xxxxxxxxxx +- else ( # Disappearing Comment ++ # Disappearing Comment ++ else ( + "--xxxxxxx --xxxxxx=x --xxxxxx-xxxxx=xxxxxx" + " --xxxxxx-xxxx=xxxxxxxxxxx.xxx" + ) +@@ -113,18 +116,25 @@ + + + func_call_where_string_arg_has_method_call_and_bad_parens( +- "A long string with {}. This string is so long that it is ridiculous. It can't fit" +- " on one line at alllll.".format("formatting"), ++ ( ++ "A long string with {}. This string is so long that it is ridiculous. It can't fit on one line at alllll.".format( ++ "formatting" ++ ) ++ ), + ) + + func_call_where_string_arg_has_old_fmt_and_bad_parens( +- "A long string with {}. This string is so long that it is ridiculous. It can't fit" +- " on one line at alllll." % "formatting", ++ ( ++ "A long string with {}. This string is so long that it is ridiculous. It can't fit on one line at alllll." ++ % "formatting" ++ ), + ) + + func_call_where_string_arg_has_old_fmt_and_bad_parens( +- "A long string with {}. This {} is so long that it is ridiculous. It can't fit on" +- " one line at alllll." % ("formatting", "string"), ++ ( ++ "A long string with {}. This {} is so long that it is ridiculous. It can't fit on one line at alllll." ++ % ("formatting", "string") ++ ), + ) + + +@@ -132,52 +142,60 @@ + def append(self): + if True: + xxxx.xxxxxxx.xxxxx( +- "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" +- % ( +- xxxx.xxxxxxxxx, +- xxxx.xxxxxxxxxxxxxx(xxxx.xxxxxxxxx), +- x, +- xxxx.xxxxxxxxxxxxxx(xx), ++ ( ++ "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" ++ % ( ++ xxxx.xxxxxxxxx, ++ xxxx.xxxxxxxxxxxxxx(xxxx.xxxxxxxxx), ++ x, ++ xxxx.xxxxxxxxxxxxxx(xx), ++ ) + ) + ) + + + class A: + def foo(): +- some_func_call( +- "xxxxxxxxxx", +- "xx {xxxxxxxxxxx}/xxxxxxxxxxx.xxx xxxx.xxx && xxxxxx -x " +- '"xxxx xxxxxxx xxxxxx xxxx; xxxx xxxxxx_xxxxx xxxxxx xxxx; ' +- "xxxx.xxxx_xxxxxx(['xxxx.xxx'], xxxx.xxxxxxx().xxxxxxxxxx)\" ", +- None, +- ("xxxxxxxxxxx",), +- ), ++ ( ++ some_func_call( ++ "xxxxxxxxxx", ++ ( ++ "xx {xxxxxxxxxxx}/xxxxxxxxxxx.xxx xxxx.xxx && xxxxxx -x " ++ '"xxxx xxxxxxx xxxxxx xxxx; xxxx xxxxxx_xxxxx xxxxxx xxxx; ' ++ "xxxx.xxxx_xxxxxx(['xxxx.xxx'], xxxx.xxxxxxx().xxxxxxxxxx)\" " ++ ), ++ None, ++ ("xxxxxxxxxxx",), ++ ), ++ ) + + + class A: + def foo(): +- some_func_call( +- "xx {xxxxxxxxxxx}/xxxxxxxxxxx.xxx xxxx.xxx && xxxxxx -x " +- "xxxx, ('xxxxxxx xxxxxx xxxx, xxxx') xxxxxx_xxxxx xxxxxx xxxx; " +- "xxxx.xxxx_xxxxxx(['xxxx.xxx'], xxxx.xxxxxxx().xxxxxxxxxx)\" ", +- None, +- ("xxxxxxxxxxx",), +- ), ++ ( ++ some_func_call( ++ ( ++ "xx {xxxxxxxxxxx}/xxxxxxxxxxx.xxx xxxx.xxx && xxxxxx -x " ++ "xxxx, ('xxxxxxx xxxxxx xxxx, xxxx') xxxxxx_xxxxx xxxxxx xxxx; " ++ "xxxx.xxxx_xxxxxx(['xxxx.xxx'], xxxx.xxxxxxx().xxxxxxxxxx)\" " ++ ), ++ None, ++ ("xxxxxxxxxxx",), ++ ), ++ ) + + + xxxxxxx = { +- "xx": ( +- "xxxx xxxxxxx xxxxxxxxx -x xxx -x /xxx/{0} -x xxx,xxx -xx {1} -xx {1} -xx" +- " xxx=xxx_xxxx,xxx_xx,xxx_xxx,xxx_xxxx,xxx_xx,xxx_xxx | xxxxxx -x xxxxxxxx -x" +- " xxxxxxxx -x" +- ), +- "xx": ( +- "xxxx xxxxxxx xxxxxxxxx -x xxx -x /xxx/{0} -x xxx,xxx -xx {1} -xx {1} -xx" +- " xxx=xxx_xxxx_xxx_xxxx,xxx_xx_xxx_xxxx,xxx_xxxx_xxx_xxxx,xxx_xx_xxxx_xxxx,xxx_xxx_xxxx,xxx_xxx_xxxx" +- " xxxx=xxx | xxxxxx -x xxxxxxxx -x xxxxxxxx -x" +- ), ++ "xx": "xxxx xxxxxxx xxxxxxxxx -x xxx -x /xxx/{0} -x xxx,xxx -xx {1} \ ++-xx {1} -xx xxx=xxx_xxxx,xxx_xx,xxx_xxx,xxx_xxxx,xxx_xx,xxx_xxx |\ ++ xxxxxx -x xxxxxxxx -x xxxxxxxx -x", ++ "xx": "xxxx xxxxxxx xxxxxxxxx -x xxx -x /xxx/{0} -x xxx,xxx -xx {1} \ ++-xx {1} -xx xxx=xxx_xxxx_xxx_xxxx,xxx_xx_xxx_xxxx,xxx_xxxx_xxx_xxxx,\ ++xxx_xx_xxxx_xxxx,xxx_xxx_xxxx,xxx_xxx_xxxx xxxx=xxx | xxxxxx -x xxxxxxxx -x xxxxxxxx -x", + } + + +@@ -185,10 +203,10 @@ + def foo(self): + if True: + xxxxx_xxxxxxxxxxxx( +- "xxx xxxxxx xxx xxxxxxxxx.xx xx xxxxxxxx. xxx xxxxxxxxxxxxx.xx" +- " xxxxxxx " +- + "xx xxxxxx xxxxxx xxxxxx xx xxxxxxx xxx xxx ${0} xx x xxxxxxxx xxxxx" +- .xxxxxx(xxxxxx_xxxxxx_xxx) ++ "xxx xxxxxx xxx xxxxxxxxx.xx xx xxxxxxxx. xxx xxxxxxxxxxxxx.xx xxxxxxx " ++ + "xx xxxxxx xxxxxx xxxxxx xx xxxxxxx xxx xxx ${0} xx x xxxxxxxx xxxxx".xxxxxx( ++ xxxxxx_xxxxxx_xxx ++ ) + ) + + +@@ -232,39 +250,24 @@ + + some_dictionary = { + "xxxxx006": [ +- ( +- "xxx-xxx" +- " xxxxx3xxxx1xx2xxxxxxxxxxxxxx0xx6xxxxxxxxxx2xxxxxx9xxxxxxxxxx0xxxxx1xxx2x/xx9xx6+x+xxxxxxxxxxxxxx4xxxxxxxxxxxxxxxxxxxxx43xxx2xx2x4x++xxx6xxxxxxxxx+xxxxx/xx9x+xxxxxxxxxxxxxx8x15xxxxxxxxxxxxxxxxx82xx/xxxxxxxxxxxxxx/x5xxxxxxxxxxxxxx6xxxxxx74x4/xxx4x+xxxxxxxxx2xxxxxxxx87xxxxx4xxxxxxxx3xx0xxxxx4xxx1xx9xx5xxxxxxx/xxxxx5xx6xx4xxxx1x/x2xxxxxxxxxxxx64xxxxxxx1x0xx5xxxxxxxxxxxxxx==" +- " xxxxx000 xxxxxxxxxx\n" +- ), +- ( +- "xxx-xxx" +- " xxxxx3xxxx1xx2xxxxxxxxxxxxxx6xxxxxxxxxxxxxx9xxxxxxxxxxxxx3xxx9xxxxxxxxxxxxxxxx0xxxxxxxxxxxxxxxxx2xxxx2xxx6xxxxx/xx54xxxxxxxxx4xxx3xxxxxx9xx3xxxxx39xxxxxxxxx5xx91xxxx7xxxxxx8xxxxxxxxxxxxxxxx9xxx93xxxxxxxxxxxxxxxxx7xxx8xx8xx4/x1xxxxx1x3xxxxxxxxxxxxx3xxxxxx9xx4xx4x7xxxxxxxxxxxxx1xxxxxxxxx7xxxxxxxxxxxxxx4xx6xxxxxxxxx9xxx7xxxx2xxxxxxxxxxxxxxxxxxxxxx8xxxxxxxxxxxxxxxxxxxx6xx==" +- " xxxxx010 xxxxxxxxxx\n" +- ), ++ "xxx-xxx xxxxx3xxxx1xx2xxxxxxxxxxxxxx0xx6xxxxxxxxxx2xxxxxx9xxxxxxxxxx0xxxxx1xxx2x/xx9xx6+x+xxxxxxxxxxxxxx4xxxxxxxxxxxxxxxxxxxxx43xxx2xx2x4x++xxx6xxxxxxxxx+xxxxx/xx9x+xxxxxxxxxxxxxx8x15xxxxxxxxxxxxxxxxx82xx/xxxxxxxxxxxxxx/x5xxxxxxxxxxxxxx6xxxxxx74x4/xxx4x+xxxxxxxxx2xxxxxxxx87xxxxx4xxxxxxxx3xx0xxxxx4xxx1xx9xx5xxxxxxx/xxxxx5xx6xx4xxxx1x/x2xxxxxxxxxxxx64xxxxxxx1x0xx5xxxxxxxxxxxxxx== xxxxx000 xxxxxxxxxx\n", ++ "xxx-xxx xxxxx3xxxx1xx2xxxxxxxxxxxxxx6xxxxxxxxxxxxxx9xxxxxxxxxxxxx3xxx9xxxxxxxxxxxxxxxx0xxxxxxxxxxxxxxxxx2xxxx2xxx6xxxxx/xx54xxxxxxxxx4xxx3xxxxxx9xx3xxxxx39xxxxxxxxx5xx91xxxx7xxxxxx8xxxxxxxxxxxxxxxx9xxx93xxxxxxxxxxxxxxxxx7xxx8xx8xx4/x1xxxxx1x3xxxxxxxxxxxxx3xxxxxx9xx4xx4x7xxxxxxxxxxxxx1xxxxxxxxx7xxxxxxxxxxxxxx4xx6xxxxxxxxx9xxx7xxxx2xxxxxxxxxxxxxxxxxxxxxx8xxxxxxxxxxxxxxxxxxxx6xx== xxxxx010 xxxxxxxxxx\n", + ], + "xxxxx016": [ +- ( +- "xxx-xxx" +- " xxxxx3xxxx1xx2xxxxxxxxxxxxxx0xx6xxxxxxxxxx2xxxxxx9xxxxxxxxxx0xxxxx1xxx2x/xx9xx6+x+xxxxxxxxxxxxxx4xxxxxxxxxxxxxxxxxxxxx43xxx2xx2x4x++xxx6xxxxxxxxx+xxxxx/xx9x+xxxxxxxxxxxxxx8x15xxxxxxxxxxxxxxxxx82xx/xxxxxxxxxxxxxx/x5xxxxxxxxxxxxxx6xxxxxx74x4/xxx4x+xxxxxxxxx2xxxxxxxx87xxxxx4xxxxxxxx3xx0xxxxx4xxx1xx9xx5xxxxxxx/xxxxx5xx6xx4xxxx1x/x2xxxxxxxxxxxx64xxxxxxx1x0xx5xxxxxxxxxxxxxx==" +- " xxxxx000 xxxxxxxxxx\n" +- ), +- ( +- "xxx-xxx" +- " xxxxx3xxxx1xx2xxxxxxxxxxxxxx6xxxxxxxxxxxxxx9xxxxxxxxxxxxx3xxx9xxxxxxxxxxxxxxxx0xxxxxxxxxxxxxxxxx2xxxx2xxx6xxxxx/xx54xxxxxxxxx4xxx3xxxxxx9xx3xxxxx39xxxxxxxxx5xx91xxxx7xxxxxx8xxxxxxxxxxxxxxxx9xxx93xxxxxxxxxxxxxxxxx7xxx8xx8xx4/x1xxxxx1x3xxxxxxxxxxxxx3xxxxxx9xx4xx4x7xxxxxxxxxxxxx1xxxxxxxxx7xxxxxxxxxxxxxx4xx6xxxxxxxxx9xxx7xxxx2xxxxxxxxxxxxxxxxxxxxxx8xxxxxxxxxxxxxxxxxxxx6xx==" +- " xxxxx010 xxxxxxxxxx\n" +- ), ++ "xxx-xxx xxxxx3xxxx1xx2xxxxxxxxxxxxxx0xx6xxxxxxxxxx2xxxxxx9xxxxxxxxxx0xxxxx1xxx2x/xx9xx6+x+xxxxxxxxxxxxxx4xxxxxxxxxxxxxxxxxxxxx43xxx2xx2x4x++xxx6xxxxxxxxx+xxxxx/xx9x+xxxxxxxxxxxxxx8x15xxxxxxxxxxxxxxxxx82xx/xxxxxxxxxxxxxx/x5xxxxxxxxxxxxxx6xxxxxx74x4/xxx4x+xxxxxxxxx2xxxxxxxx87xxxxx4xxxxxxxx3xx0xxxxx4xxx1xx9xx5xxxxxxx/xxxxx5xx6xx4xxxx1x/x2xxxxxxxxxxxx64xxxxxxx1x0xx5xxxxxxxxxxxxxx== xxxxx000 xxxxxxxxxx\n", ++ "xxx-xxx xxxxx3xxxx1xx2xxxxxxxxxxxxxx6xxxxxxxxxxxxxx9xxxxxxxxxxxxx3xxx9xxxxxxxxxxxxxxxx0xxxxxxxxxxxxxxxxx2xxxx2xxx6xxxxx/xx54xxxxxxxxx4xxx3xxxxxx9xx3xxxxx39xxxxxxxxx5xx91xxxx7xxxxxx8xxxxxxxxxxxxxxxx9xxx93xxxxxxxxxxxxxxxxx7xxx8xx8xx4/x1xxxxx1x3xxxxxxxxxxxxx3xxxxxx9xx4xx4x7xxxxxxxxxxxxx1xxxxxxxxx7xxxxxxxxxxxxxx4xx6xxxxxxxxx9xxx7xxxx2xxxxxxxxxxxxxxxxxxxxxx8xxxxxxxxxxxxxxxxxxxx6xx== xxxxx010 xxxxxxxxxx\n", + ], + } + + + def foo(): +- xxx_xxx = ( # xxxx xxxxxxxxxx xxxx xx xxxx xx xxx xxxxxxxx xxxxxx xxxxx. +- 'xxxx xxx xxxxxxxx_xxxx xx "xxxxxxxxxx".\n xxx: xxxxxx xxxxxxxx_xxxx=xxxxxxxxxx' +- ) ++ xxx_xxx = ( ++ 'xxxx xxx xxxxxxxx_xxxx xx "xxxxxxxxxx".' ++ "\n xxx: xxxxxx xxxxxxxx_xxxx=xxxxxxxxxx" ++ ) # xxxx xxxxxxxxxx xxxx xx xxxx xx xxx xxxxxxxx xxxxxx xxxxx. + + +-some_tuple = ("some string", "some string which should be joined") ++some_tuple = ("some string", "some string" " which should be joined") + + some_commented_string = ( # This comment stays at the top. + "This string is long but not so long that it needs hahahah toooooo be so greatttt" +@@ -279,36 +282,25 @@ + ) + + lpar_and_rpar_have_comments = func_call( # LPAR Comment +- "Long really ridiculous type of string that shouldn't really even exist at all. I" +- " mean commmme onnn!!!", # Comma Comment ++ "Long really ridiculous type of string that shouldn't really even exist at all. I mean commmme onnn!!!", # Comma Comment + ) # RPAR Comment + + cmd_fstring = ( +- "sudo -E deluge-console info --detailed --sort-reverse=time_added " ++ f"sudo -E deluge-console info --detailed --sort-reverse=time_added " + f"{'' if ID is None else ID} | perl -nE 'print if /^{field}:/'" + ) + +-cmd_fstring = ( +- "sudo -E deluge-console info --detailed --sort-reverse=time_added" +- f" {'' if ID is None else ID} | perl -nE 'print if /^{field}:/'" +-) ++cmd_fstring = f"sudo -E deluge-console info --detailed --sort-reverse=time_added {'' if ID is None else ID} | perl -nE 'print if /^{field}:/'" ++ ++cmd_fstring = f"sudo -E deluge-console info --detailed --sort-reverse=time_added {'{{}}' if ID is None else ID} | perl -nE 'print if /^{field}:/'" + +-cmd_fstring = ( +- "sudo -E deluge-console info --detailed --sort-reverse=time_added" +- f" {'{{}}' if ID is None else ID} | perl -nE 'print if /^{field}:/'" +-) ++cmd_fstring = f"sudo -E deluge-console info --detailed --sort-reverse=time_added {{'' if ID is None else ID}} | perl -nE 'print if /^{field}:/'" + +-cmd_fstring = ( +- "sudo -E deluge-console info --detailed --sort-reverse=time_added {'' if ID is" +- f" None else ID}} | perl -nE 'print if /^{field}:/'" +-) ++fstring = f"This string really doesn't need to be an {{{{fstring}}}}, but this one most certainly, absolutely {does}." + + fstring = ( +- "This string really doesn't need to be an {{fstring}}, but this one most" +- f" certainly, absolutely {does}." ++ f"We have to remember to escape {braces}." " Like {these}." f" But not {this}." + ) +- +-fstring = f"We have to remember to escape {braces}. Like {{these}}. But not {this}." + + + class A: +@@ -364,10 +356,7 @@ + def foo(): + if not hasattr(module, name): + raise ValueError( +- "Could not find object %s in %s.\nPlease note that you cannot" +- " serialize things like inner classes. Please move the object into" +- " the main module body to use migrations.\nFor more information," +- " see https://docs.djangoproject.com/en/%s/topics/migrations/#serializing-values" ++ "Could not find object %s in %s.\nPlease note that you cannot serialize things like inner classes. Please move the object into the main module body to use migrations.\nFor more information, see https://docs.djangoproject.com/en/%s/topics/migrations/#serializing-values" + % (name, module_name, get_docs_version()) + ) + +@@ -382,35 +371,33 @@ + + class Step(StepBase): + def who(self): +- self.cmd = ( +- "SR AAAA-CORRECT NAME IS {last_name} {first_name}{middle_name}" +- " {title}/P{passenger_association}".format( +- last_name=last_name, +- first_name=first_name, +- middle_name=middle_name, +- title=title, +- passenger_association=passenger_association, +- ) ++ self.cmd = "SR AAAA-CORRECT NAME IS {last_name} {first_name}{middle_name} {title}/P{passenger_association}".format( ++ last_name=last_name, ++ first_name=first_name, ++ middle_name=middle_name, ++ title=title, ++ passenger_association=passenger_association, + ) + + +-xxxxxxx_xxxxxx_xxxxxxx = xxx([ +- xxxxxxxxxxxx( +- xxxxxx_xxxxxxx=( +- '((x.aaaaaaaaa = "xxxxxx.xxxxxxxxxxxxxxxxxxxxx") || (x.xxxxxxxxx =' +- ' "xxxxxxxxxxxx")) && ' +- # xxxxx xxxxxxxxxxxx xxxx xxx (xxxxxxxxxxxxxxxx) xx x xxxxxxxxx xx xxxxxx. +- "(x.bbbbbbbbbbbb.xxx != " +- '"xxx:xxx:xxx::cccccccccccc:xxxxxxx-xxxx/xxxxxxxxxxx/xxxxxxxxxxxxxxxxx") && ' ++xxxxxxx_xxxxxx_xxxxxxx = xxx( ++ [ ++ xxxxxxxxxxxx( ++ xxxxxx_xxxxxxx=( ++ '((x.aaaaaaaaa = "xxxxxx.xxxxxxxxxxxxxxxxxxxxx") || (x.xxxxxxxxx = "xxxxxxxxxxxx")) && ' ++ # xxxxx xxxxxxxxxxxx xxxx xxx (xxxxxxxxxxxxxxxx) xx x xxxxxxxxx xx xxxxxx. ++ "(x.bbbbbbbbbbbb.xxx != " ++ '"xxx:xxx:xxx::cccccccccccc:xxxxxxx-xxxx/xxxxxxxxxxx/xxxxxxxxxxxxxxxxx") && ' ++ ) + ) +- ) +-]) ++ ] ++) + + if __name__ == "__main__": + for i in range(4, 8): + cmd = ( +- r"for pid in $(ps aux | grep paster | grep -v grep | grep '\-%d' | awk" +- r" '{print $2}'); do kill $pid; done" % (i) ++ r"for pid in $(ps aux | grep paster | grep -v grep | grep '\-%d' | awk '{print $2}'); do kill $pid; done" ++ % (i) + ) + + +@@ -432,14 +419,12 @@ + assert xxxxxxx_xxxx in [ + x.xxxxx.xxxxxx.xxxxx.xxxxxx, + x.xxxxx.xxxxxx.xxxxx.xxxx, +- ], ( +- "xxxxxxxxxxx xxxxxxx xxxx (xxxxxx xxxx) %x xxx xxxxx" % xxxxxxx_xxxx +- ) ++ ], "xxxxxxxxxxx xxxxxxx xxxx (xxxxxx xxxx) %x xxx xxxxx" % xxxxxxx_xxxx + + +-value.__dict__[key] = ( +- "test" # set some Thrift field to non-None in the struct aa bb cc dd ee +-) ++value.__dict__[ ++ key ++] = "test" # set some Thrift field to non-None in the struct aa bb cc dd ee + + RE_ONE_BACKSLASH = { + "asdf_hjkl_jkl": re.compile( +@@ -449,8 +434,7 @@ + + RE_TWO_BACKSLASHES = { + "asdf_hjkl_jkl": re.compile( +- r"(?>\n" + ) + +-assert ( +- str(suffix_arr) +- == "['$', 'angaroo$', 'angrykangaroo$', 'aroo$', 'garoo$', " ++assert str(suffix_arr) == ( ++ "['$', 'angaroo$', 'angrykangaroo$', 'aroo$', 'garoo$', " + "'grykangaroo$', 'kangaroo$', 'ngaroo$', 'ngrykangaroo$', " + "'o$', 'oo$', 'roo$', 'rykangaroo$', 'ykangaroo$']" + ) +-assert ( +- str(suffix_arr) +- != "['$', 'angaroo$', 'angrykangaroo$', 'aroo$', 'garoo$', " ++assert str(suffix_arr) != ( ++ "['$', 'angaroo$', 'angrykangaroo$', 'aroo$', 'garoo$', " + "'grykangaroo$', 'kangaroo$', 'ngaroo$', 'ngrykangaroo$', " + "'o$', 'oo$', 'roo$', 'rykangaroo$', 'ykangaroo$']" + ) +-assert ( +- str(suffix_arr) +- <= "['$', 'angaroo$', 'angrykangaroo$', 'aroo$', 'garoo$', " ++assert str(suffix_arr) <= ( ++ "['$', 'angaroo$', 'angrykangaroo$', 'aroo$', 'garoo$', " + "'grykangaroo$', 'kangaroo$', 'ngaroo$', 'ngrykangaroo$', " + "'o$', 'oo$', 'roo$', 'rykangaroo$', 'ykangaroo$']" + ) +-assert ( +- str(suffix_arr) +- >= "['$', 'angaroo$', 'angrykangaroo$', 'aroo$', 'garoo$', " ++assert str(suffix_arr) >= ( ++ "['$', 'angaroo$', 'angrykangaroo$', 'aroo$', 'garoo$', " + "'grykangaroo$', 'kangaroo$', 'ngaroo$', 'ngrykangaroo$', " + "'o$', 'oo$', 'roo$', 'rykangaroo$', 'ykangaroo$']" + ) +-assert ( +- str(suffix_arr) +- < "['$', 'angaroo$', 'angrykangaroo$', 'aroo$', 'garoo$', " ++assert str(suffix_arr) < ( ++ "['$', 'angaroo$', 'angrykangaroo$', 'aroo$', 'garoo$', " + "'grykangaroo$', 'kangaroo$', 'ngaroo$', 'ngrykangaroo$', " + "'o$', 'oo$', 'roo$', 'rykangaroo$', 'ykangaroo$']" + ) +-assert ( +- str(suffix_arr) +- > "['$', 'angaroo$', 'angrykangaroo$', 'aroo$', 'garoo$', " ++assert str(suffix_arr) > ( ++ "['$', 'angaroo$', 'angrykangaroo$', 'aroo$', 'garoo$', " + "'grykangaroo$', 'kangaroo$', 'ngaroo$', 'ngrykangaroo$', " + "'o$', 'oo$', 'roo$', 'rykangaroo$', 'ykangaroo$']" + ) + assert ( + str(suffix_arr) +- in "['$', 'angaroo$', 'angrykangaroo$', 'aroo$', 'garoo$', 'grykangaroo$'," +- " 'kangaroo$', 'ngaroo$', 'ngrykangaroo$', 'o$', 'oo$', 'roo$', 'rykangaroo$'," +- " 'ykangaroo$']" ++ in "['$', 'angaroo$', 'angrykangaroo$', 'aroo$', 'garoo$', 'grykangaroo$', 'kangaroo$', 'ngaroo$', 'ngrykangaroo$', 'o$', 'oo$', 'roo$', 'rykangaroo$', 'ykangaroo$']" + ) + assert ( + str(suffix_arr) +- not in "['$', 'angaroo$', 'angrykangaroo$', 'aroo$', 'garoo$', 'grykangaroo$'," +- " 'kangaroo$', 'ngaroo$', 'ngrykangaroo$', 'o$', 'oo$', 'roo$'," +- " 'rykangaroo$', 'ykangaroo$']" ++ not in "['$', 'angaroo$', 'angrykangaroo$', 'aroo$', 'garoo$', 'grykangaroo$', 'kangaroo$', 'ngaroo$', 'ngrykangaroo$', 'o$', 'oo$', 'roo$', 'rykangaroo$', 'ykangaroo$']" + ) + message = ( + f"1. Go to Google Developers Console and log in with your Google account." +- f"(https://console.developers.google.com/)" +- f"2. You should be prompted to create a new project (name does not matter)." +- f"3. Click on Enable APIs and Services at the top." +- f"4. In the list of APIs choose or search for YouTube Data API v3 and " +- f"click on it. Choose Enable." +- f"5. Click on Credentials on the left navigation bar." +- f"6. Click on Create Credential at the top." +- f'7. At the top click the link for "API key".' +- f"8. No application restrictions are needed. Click Create at the bottom." +- f"9. You now have a key to add to `{{prefix}}set api youtube api_key`" ++ "(https://console.developers.google.com/)" ++ "2. You should be prompted to create a new project (name does not matter)." ++ "3. Click on Enable APIs and Services at the top." ++ "4. In the list of APIs choose or search for YouTube Data API v3 and " ++ "click on it. Choose Enable." ++ "5. Click on Credentials on the left navigation bar." ++ "6. Click on Create Credential at the top." ++ '7. At the top click the link for "API key".' ++ "8. No application restrictions are needed. Click Create at the bottom." ++ "9. You now have a key to add to `{prefix}set api youtube api_key`" + ) + message = ( + f"1. Go to Google Developers Console and log in with your Google account." +- f"(https://console.developers.google.com/)" +- f"2. You should be prompted to create a new project (name does not matter)." ++ "(https://console.developers.google.com/)" ++ "2. You should be prompted to create a new project (name does not matter)." + f"3. Click on Enable APIs and Services at the top." +- f"4. In the list of APIs choose or search for YouTube Data API v3 and " +- f"click on it. Choose Enable." ++ "4. In the list of APIs choose or search for YouTube Data API v3 and " ++ "click on it. Choose Enable." + f"5. Click on Credentials on the left navigation bar." +- f"6. Click on Create Credential at the top." +- f'7. At the top click the link for "API key".' +- f"8. No application restrictions are needed. Click Create at the bottom." +- f"9. You now have a key to add to `{{prefix}}set api youtube api_key`" ++ "6. Click on Create Credential at the top." ++ '7. At the top click the link for "API key".' ++ "8. No application restrictions are needed. Click Create at the bottom." ++ "9. You now have a key to add to `{prefix}set api youtube api_key`" + ) + message = ( +- "1. Go to Google Developers Console and log in with your Google account." ++ f"1. Go to Google Developers Console and log in with your Google account." + "(https://console.developers.google.com/)" + "2. You should be prompted to create a new project (name does not matter)." +- "3. Click on Enable APIs and Services at the top." ++ f"3. Click on Enable APIs and Services at the top." + "4. In the list of APIs choose or search for YouTube Data API v3 and " + "click on it. Choose Enable." +- "5. Click on Credentials on the left navigation bar." ++ f"5. Click on Credentials on the left navigation bar." + "6. Click on Create Credential at the top." + '7. At the top click the link for "API key".' + "8. No application restrictions are needed. Click Create at the bottom." +@@ -613,55 +583,40 @@ + f"<<{author.display_name}>>\n" + ) + +-fstring = f"We have to remember to escape {braces}. Like {{these}}. But not {this}." ++fstring = ( ++ f"We have to remember to escape {braces}." " Like {these}." f" But not {this}." ++) + + welcome_to_programming = R"hello," R" world!" + +-fstring = ( +- f"f-strings definitely make things more {difficult} than they need to be for" +- " {black}. But boy they sure are handy. The problem is that some lines will need" +- f" to have the 'f' whereas others do not. This {line}, for example, needs one." +-) ++fstring = f"f-strings definitely make things more {difficult} than they need to be for {{black}}. But boy they sure are handy. The problem is that some lines will need to have the 'f' whereas others do not. This {line}, for example, needs one." + +-x = ( +- "This is a long string which contains an f-expr that should not split" +- f" {{{[i for i in range(5)]}}}." +-) ++x = f"This is a long string which contains an f-expr that should not split {{{[i for i in range(5)]}}}." + +-x = ( +- "\N{BLACK RIGHT-POINTING TRIANGLE WITH DOUBLE VERTICAL BAR}\N{VARIATION SELECTOR-16}" +-) ++x = "\N{BLACK RIGHT-POINTING TRIANGLE WITH DOUBLE VERTICAL BAR}\N{VARIATION SELECTOR-16}" + + xxxxxx_xxx_xxxx_xx_xxxxx_xxxxxxxx_xxxxxxxx_xxxxxxxxxx_xxxx_xxxx_xxxxx = xxxx.xxxxxx.xxxxxxxxx.xxxxxxxxxxxxxxxxxxxx( + xx_xxxxxx={ +- "x3_xxxxxxxx": ( +- "xxx3_xxxxx_xxxxxxxx_xxxxxxxx_xxxxxxxxxx_xxxxxxxx_xxxxxx_xxxxxxx" +- ), ++ "x3_xxxxxxxx": "xxx3_xxxxx_xxxxxxxx_xxxxxxxx_xxxxxxxxxx_xxxxxxxx_xxxxxx_xxxxxxx", + }, + ) + + # Regression test for https://github.com/psf/black/issues/3117. + some_dict = { +- "something_something": ( +- r"Lorem ipsum dolor sit amet, an sed convenire eloquentiam \t" +- r"signiferumque, duo ea vocibus consetetur scriptorem. Facer \t" +- ), ++ "something_something": r"Lorem ipsum dolor sit amet, an sed convenire eloquentiam \t" ++ r"signiferumque, duo ea vocibus consetetur scriptorem. Facer \t", + } + + # Regression test for https://github.com/psf/black/issues/3459. + xxxx( +- empty_str_as_first_split=( +- "" +- f"xxxxxxx {xxxxxxxxxx} xxx xxxxxxxxxx xxxxx xxx xxx xx " +- "xxxxx xxxxxxxxx xxxxxxx, xxx xxxxxxxxxxx xxx xxxxx. " +- f"xxxxxxxxxxxxx xxxx xx xxxxxxxxxx. xxxxx: {x.xxx}" +- ), +- empty_u_str_as_first_split=( +- "" +- f"xxxxxxx {xxxxxxxxxx} xxx xxxxxxxxxx xxxxx xxx xxx xx " +- "xxxxx xxxxxxxxx xxxxxxx, xxx xxxxxxxxxxx xxx xxxxx. " +- f"xxxxxxxxxxxxx xxxx xx xxxxxxxxxx. xxxxx: {x.xxx}" +- ), ++ empty_str_as_first_split="" ++ f"xxxxxxx {xxxxxxxxxx} xxx xxxxxxxxxx xxxxx xxx xxx xx " ++ "xxxxx xxxxxxxxx xxxxxxx, xxx xxxxxxxxxxx xxx xxxxx. " ++ f"xxxxxxxxxxxxx xxxx xx xxxxxxxxxx. xxxxx: {x.xxx}", ++ empty_u_str_as_first_split="" ++ f"xxxxxxx {xxxxxxxxxx} xxx xxxxxxxxxx xxxxx xxx xxx xx " ++ "xxxxx xxxxxxxxx xxxxxxx, xxx xxxxxxxxxxx xxx xxxxx. " ++ f"xxxxxxxxxxxxx xxxx xx xxxxxxxxxx. xxxxx: {x.xxx}", + ) + + # Regression test for https://github.com/psf/black/issues/3455. +@@ -672,9 +627,11 @@ + } + + # Regression test for https://github.com/psf/black/issues/3506. +-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']}'" ++ "With single quote: ' " ++ f" {my_dict['foo']}" ++ ' With double quote: " ' ++ f' {my_dict["bar"]}' + ) ++ ++s = f'Lorem Ipsum is simply dummy text of the printing and typesetting industry:\'{my_dict["foo"]}\'' +``` + +## Ruff Output + +```python +class A: + def foo(): + result = type(message)("") + + +# Don't merge multiline (e.g. triple-quoted) strings. +def foo(): + query = ( + """SELECT xxxxxxxxxxxxxxxxxxxx(xxx)""" + """ FROM xxxxxxxxxxxxxxxx WHERE xxxxxxxxxx AND xxx <> xxxxxxxxxxxxxx()""" + ) + + +# There was a bug where tuples were being identified as long strings. +long_tuple = ( + "Apple", + "Berry", + "Cherry", + "Dill", + "Evergreen", + "Fig", + "Grape", + "Harry", + "Iglu", + "Jaguar", +) + +stupid_format_method_bug = "Some really long string that just so happens to be the {} {} to force the 'format' method to hang over the line length boundary. This is pretty annoying.".format( + "perfect", "length" +) + + +class A: + def foo(): + os.system( + "This is a regression test. xxx xxx xxx xxx xxx xxx xxx xxx xxx xxx xxx xxx xxx xxx xxx xxx xxx xxx xxx xxx xxx xxx xxx xxx xxx xxx xxx xxx xxx xxx xxxx.".format( + "xxxxxxxxxx", "xxxxxx", "xxxxxxxxxx" + ) + ) + + +class A: + def foo(): + XXXXXXXXXXXX.append( + ( + "xxx_xxxxxxxxxx(xxxxx={}, xxxx={}, xxxxx, xxxx_xxxx_xxxxxxxxxx={})".format( + xxxxx, xxxx, xxxx_xxxx_xxxxxxxxxx + ), + my_var, + my_other_var, + ) + ) + + +class A: + class B: + def foo(): + bar( + ( + "[{}]: xxx_xxxxxxxxxx(xxxxx={}, xxxx={}, xxxxx={}" + " xxxx_xxxx_xxxxxxxxxx={}, xxxx={})".format( + xxxx._xxxxxxxxxxxxxx, xxxxx, xxxx, xxxx_xxxx_xxxxxxxxxx, xxxxxxx + ) + ), + varX, + varY, + varZ, + ) + + +def foo(xxxx): + for xxx_xxxx, _xxx_xxx, _xxx_xxxxx, xxx_xxxx in xxxx: + for xxx in xxx_xxxx: + assert ( + ("x" in xxx) or (xxx in xxx_xxx_xxxxx) + ), "{0} xxxxxxx xx {1}, xxx {1} xx xxx xx xxxx xx xxx xxxx: xxx xxxx {2}".format( + xxx_xxxx, xxx, xxxxxx.xxxxxxx(xxx_xxx_xxxxx) + ) + + +class A: + def disappearing_comment(): + return ( + ( # xx -x xxxxxxx xx xxx xxxxxxx. + "{{xxx_xxxxxxxxxx_xxxxxxxx}} xxx xxxx" " {} {{xxxx}} >&2".format( + "{xxxx} {xxxxxx}" + if xxxxx.xx_xxxxxxxxxx + # Disappearing Comment + else ( + "--xxxxxxx --xxxxxx=x --xxxxxx-xxxxx=xxxxxx" + " --xxxxxx-xxxx=xxxxxxxxxxx.xxx" + ) + ) + ), + (x, y, z), + ) + + +class A: + class B: + def foo(): + xxxxx_xxxx( + xx, + "\t" + "@xxxxxx '{xxxx_xxx}\t' > {xxxxxx_xxxx}.xxxxxxx;" + "{xxxx_xxx} >> {xxxxxx_xxxx}.xxxxxxx 2>&1; xx=$$?;" + "xxxx $$xx".format( + xxxx_xxx=xxxx_xxxxxxx, + xxxxxx_xxxx=xxxxxxx + "/" + xxxx_xxx_xxxx, + x=xxx_xxxxx_xxxxx_xxx, + ), + x, + y, + z, + ) + + +func_call_where_string_arg_has_method_call_and_bad_parens( + ( + "A long string with {}. This string is so long that it is ridiculous. It can't fit on one line at alllll.".format( + "formatting" + ) + ), +) + +func_call_where_string_arg_has_old_fmt_and_bad_parens( + ( + "A long string with {}. This string is so long that it is ridiculous. It can't fit on one line at alllll." + % "formatting" + ), +) + +func_call_where_string_arg_has_old_fmt_and_bad_parens( + ( + "A long string with {}. This {} is so long that it is ridiculous. It can't fit on one line at alllll." + % ("formatting", "string") + ), +) + + +class A: + def append(self): + if True: + xxxx.xxxxxxx.xxxxx( + ( + "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" + % ( + xxxx.xxxxxxxxx, + xxxx.xxxxxxxxxxxxxx(xxxx.xxxxxxxxx), + x, + xxxx.xxxxxxxxxxxxxx(xx), + ) + ) + ) + + +class A: + def foo(): + ( + some_func_call( + "xxxxxxxxxx", + ( + "xx {xxxxxxxxxxx}/xxxxxxxxxxx.xxx xxxx.xxx && xxxxxx -x " + '"xxxx xxxxxxx xxxxxx xxxx; xxxx xxxxxx_xxxxx xxxxxx xxxx; ' + "xxxx.xxxx_xxxxxx(['xxxx.xxx'], xxxx.xxxxxxx().xxxxxxxxxx)\" " + ), + None, + ("xxxxxxxxxxx",), + ), + ) + + +class A: + def foo(): + ( + some_func_call( + ( + "xx {xxxxxxxxxxx}/xxxxxxxxxxx.xxx xxxx.xxx && xxxxxx -x " + "xxxx, ('xxxxxxx xxxxxx xxxx, xxxx') xxxxxx_xxxxx xxxxxx xxxx; " + "xxxx.xxxx_xxxxxx(['xxxx.xxx'], xxxx.xxxxxxx().xxxxxxxxxx)\" " + ), + None, + ("xxxxxxxxxxx",), + ), + ) + + +xxxxxxx = { + "xx": "xxxx xxxxxxx xxxxxxxxx -x xxx -x /xxx/{0} -x xxx,xxx -xx {1} \ +-xx {1} -xx xxx=xxx_xxxx,xxx_xx,xxx_xxx,xxx_xxxx,xxx_xx,xxx_xxx |\ + xxxxxx -x xxxxxxxx -x xxxxxxxx -x", + "xx": "xxxx xxxxxxx xxxxxxxxx -x xxx -x /xxx/{0} -x xxx,xxx -xx {1} \ +-xx {1} -xx xxx=xxx_xxxx_xxx_xxxx,xxx_xx_xxx_xxxx,xxx_xxxx_xxx_xxxx,\ +xxx_xx_xxxx_xxxx,xxx_xxx_xxxx,xxx_xxx_xxxx xxxx=xxx | xxxxxx -x xxxxxxxx -x xxxxxxxx -x", +} + + +class A: + def foo(self): + if True: + xxxxx_xxxxxxxxxxxx( + "xxx xxxxxx xxx xxxxxxxxx.xx xx xxxxxxxx. xxx xxxxxxxxxxxxx.xx xxxxxxx " + + "xx xxxxxx xxxxxx xxxxxx xx xxxxxxx xxx xxx ${0} xx x xxxxxxxx xxxxx".xxxxxx( + xxxxxx_xxxxxx_xxx + ) + ) + + +class A: + class B: + def foo(): + row = { + "xxxxxxxxxxxxxxx": xxxxxx_xxxxx_xxxx, + # 'xxxxxxxxxxxxxxxxxxxxxxx' + # 'xxxxxxxxxxxxxxxxxxxxxx' + # 'xxxxxxxxxxxxxxxxxx' + # 'xxxxxxxxxxxxxxxxx' + "xxxxxxxxxx": xxxxx_xxxxx, + } + + +class A: + def xxxx_xxx_xx_xxxxxxxxxx_xxxx_xxxxxxxxx(xxxx): + xxxxxxxx = [ + xxxxxxxxxxxxxxxx( + "xxxx", + xxxxxxxxxxx={ + "xxxx": 1.0, + }, + xxxxxx={"xxxxxx 1": xxxxxx(xxxx="xxxxxx 1", xxxxxx=600.0)}, + xxxxxxxx_xxxxxxx=0.0, + ), + xxxxxxxxxxxxxxxx( + "xxxxxxx", + xxxxxxxxxxx={ + "xxxx": 1.0, + }, + xxxxxx={"xxxxxx 1": xxxxxx(xxxx="xxxxxx 1", xxxxxx=200.0)}, + xxxxxxxx_xxxxxxx=0.0, + ), + xxxxxxxxxxxxxxxx( + "xxxx", + ), + ] + + +some_dictionary = { + "xxxxx006": [ + "xxx-xxx xxxxx3xxxx1xx2xxxxxxxxxxxxxx0xx6xxxxxxxxxx2xxxxxx9xxxxxxxxxx0xxxxx1xxx2x/xx9xx6+x+xxxxxxxxxxxxxx4xxxxxxxxxxxxxxxxxxxxx43xxx2xx2x4x++xxx6xxxxxxxxx+xxxxx/xx9x+xxxxxxxxxxxxxx8x15xxxxxxxxxxxxxxxxx82xx/xxxxxxxxxxxxxx/x5xxxxxxxxxxxxxx6xxxxxx74x4/xxx4x+xxxxxxxxx2xxxxxxxx87xxxxx4xxxxxxxx3xx0xxxxx4xxx1xx9xx5xxxxxxx/xxxxx5xx6xx4xxxx1x/x2xxxxxxxxxxxx64xxxxxxx1x0xx5xxxxxxxxxxxxxx== xxxxx000 xxxxxxxxxx\n", + "xxx-xxx xxxxx3xxxx1xx2xxxxxxxxxxxxxx6xxxxxxxxxxxxxx9xxxxxxxxxxxxx3xxx9xxxxxxxxxxxxxxxx0xxxxxxxxxxxxxxxxx2xxxx2xxx6xxxxx/xx54xxxxxxxxx4xxx3xxxxxx9xx3xxxxx39xxxxxxxxx5xx91xxxx7xxxxxx8xxxxxxxxxxxxxxxx9xxx93xxxxxxxxxxxxxxxxx7xxx8xx8xx4/x1xxxxx1x3xxxxxxxxxxxxx3xxxxxx9xx4xx4x7xxxxxxxxxxxxx1xxxxxxxxx7xxxxxxxxxxxxxx4xx6xxxxxxxxx9xxx7xxxx2xxxxxxxxxxxxxxxxxxxxxx8xxxxxxxxxxxxxxxxxxxx6xx== xxxxx010 xxxxxxxxxx\n", + ], + "xxxxx016": [ + "xxx-xxx xxxxx3xxxx1xx2xxxxxxxxxxxxxx0xx6xxxxxxxxxx2xxxxxx9xxxxxxxxxx0xxxxx1xxx2x/xx9xx6+x+xxxxxxxxxxxxxx4xxxxxxxxxxxxxxxxxxxxx43xxx2xx2x4x++xxx6xxxxxxxxx+xxxxx/xx9x+xxxxxxxxxxxxxx8x15xxxxxxxxxxxxxxxxx82xx/xxxxxxxxxxxxxx/x5xxxxxxxxxxxxxx6xxxxxx74x4/xxx4x+xxxxxxxxx2xxxxxxxx87xxxxx4xxxxxxxx3xx0xxxxx4xxx1xx9xx5xxxxxxx/xxxxx5xx6xx4xxxx1x/x2xxxxxxxxxxxx64xxxxxxx1x0xx5xxxxxxxxxxxxxx== xxxxx000 xxxxxxxxxx\n", + "xxx-xxx xxxxx3xxxx1xx2xxxxxxxxxxxxxx6xxxxxxxxxxxxxx9xxxxxxxxxxxxx3xxx9xxxxxxxxxxxxxxxx0xxxxxxxxxxxxxxxxx2xxxx2xxx6xxxxx/xx54xxxxxxxxx4xxx3xxxxxx9xx3xxxxx39xxxxxxxxx5xx91xxxx7xxxxxx8xxxxxxxxxxxxxxxx9xxx93xxxxxxxxxxxxxxxxx7xxx8xx8xx4/x1xxxxx1x3xxxxxxxxxxxxx3xxxxxx9xx4xx4x7xxxxxxxxxxxxx1xxxxxxxxx7xxxxxxxxxxxxxx4xx6xxxxxxxxx9xxx7xxxx2xxxxxxxxxxxxxxxxxxxxxx8xxxxxxxxxxxxxxxxxxxx6xx== xxxxx010 xxxxxxxxxx\n", + ], +} + + +def foo(): + xxx_xxx = ( + 'xxxx xxx xxxxxxxx_xxxx xx "xxxxxxxxxx".' + "\n xxx: xxxxxx xxxxxxxx_xxxx=xxxxxxxxxx" + ) # xxxx xxxxxxxxxx xxxx xx xxxx xx xxx xxxxxxxx xxxxxx xxxxx. + + +some_tuple = ("some string", "some string" " which should be joined") + +some_commented_string = ( # This comment stays at the top. + "This string is long but not so long that it needs hahahah toooooo be so greatttt" + " {} that I just can't think of any more good words to say about it at" + " allllllllllll".format("ha") # comments here are fine +) + +some_commented_string = ( + "This string is long but not so long that it needs hahahah toooooo be so greatttt" # But these + " {} that I just can't think of any more good words to say about it at" # comments will stay + " allllllllllll".format("ha") # comments here are fine +) + +lpar_and_rpar_have_comments = func_call( # LPAR Comment + "Long really ridiculous type of string that shouldn't really even exist at all. I mean commmme onnn!!!", # Comma Comment +) # RPAR Comment + +cmd_fstring = ( + f"sudo -E deluge-console info --detailed --sort-reverse=time_added " + f"{'' if ID is None else ID} | perl -nE 'print if /^{field}:/'" +) + +cmd_fstring = f"sudo -E deluge-console info --detailed --sort-reverse=time_added {'' if ID is None else ID} | perl -nE 'print if /^{field}:/'" + +cmd_fstring = f"sudo -E deluge-console info --detailed --sort-reverse=time_added {'{{}}' if ID is None else ID} | perl -nE 'print if /^{field}:/'" + +cmd_fstring = f"sudo -E deluge-console info --detailed --sort-reverse=time_added {{'' if ID is None else ID}} | perl -nE 'print if /^{field}:/'" + +fstring = f"This string really doesn't need to be an {{{{fstring}}}}, but this one most certainly, absolutely {does}." + +fstring = ( + f"We have to remember to escape {braces}." " Like {these}." f" But not {this}." +) + + +class A: + class B: + def foo(): + st_error = STError( + f"This string ({string_leaf.value}) appears to be pointless (i.e. has" + " no parent)." + ) + + +def foo(): + user_regex = _lazy_re_compile( + r"(^[-!#$%&'*+/=?^_`{}|~0-9A-Z]+(\.[-!#$%&'*+/=?^_`{}|~0-9A-Z]+)*\Z" # dot-atom + r'|^"([\001-\010\013\014\016-\037!#-\[\]-\177]|\\[\001-\011\013\014\016-\177])*"\Z)', # quoted-string + re.IGNORECASE, + ) + + +def foo(): + user_regex = _lazy_re_compile( + "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" # dot-atom + "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", # quoted-string + xyz, + ) + + +def foo(): + user_regex = _lazy_re_compile( + "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" # dot-atom + "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", # quoted-string + xyz, + ) + + +class A: + class B: + def foo(): + if not hasattr(module, name): + raise ValueError( + "Could not find object %s in %s.\n" + "Please note that you cannot serialize things like inner " + "classes. Please move the object into the main module " + "body to use migrations.\n" + "For more information, see " + "https://docs.djangoproject.com/en/%s/topics/migrations/#serializing-values" + % (name, module_name, get_docs_version()) + ) + + +class A: + class B: + def foo(): + if not hasattr(module, name): + raise ValueError( + "Could not find object %s in %s.\nPlease note that you cannot serialize things like inner classes. Please move the object into the main module body to use migrations.\nFor more information, see https://docs.djangoproject.com/en/%s/topics/migrations/#serializing-values" + % (name, module_name, get_docs_version()) + ) + + +x = ( + "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" + "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" + "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" + "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" +) + + +class Step(StepBase): + def who(self): + self.cmd = "SR AAAA-CORRECT NAME IS {last_name} {first_name}{middle_name} {title}/P{passenger_association}".format( + last_name=last_name, + first_name=first_name, + middle_name=middle_name, + title=title, + passenger_association=passenger_association, + ) + + +xxxxxxx_xxxxxx_xxxxxxx = xxx( + [ + xxxxxxxxxxxx( + xxxxxx_xxxxxxx=( + '((x.aaaaaaaaa = "xxxxxx.xxxxxxxxxxxxxxxxxxxxx") || (x.xxxxxxxxx = "xxxxxxxxxxxx")) && ' + # xxxxx xxxxxxxxxxxx xxxx xxx (xxxxxxxxxxxxxxxx) xx x xxxxxxxxx xx xxxxxx. + "(x.bbbbbbbbbbbb.xxx != " + '"xxx:xxx:xxx::cccccccccccc:xxxxxxx-xxxx/xxxxxxxxxxx/xxxxxxxxxxxxxxxxx") && ' + ) + ) + ] +) + +if __name__ == "__main__": + for i in range(4, 8): + cmd = ( + r"for pid in $(ps aux | grep paster | grep -v grep | grep '\-%d' | awk '{print $2}'); do kill $pid; done" + % (i) + ) + + +def A(): + def B(): + def C(): + def D(): + def E(): + def F(): + def G(): + assert ( + c_float(val[0][0] / val[0][1]).value + == c_float(value[0][0] / value[0][1]).value + ), "%s didn't roundtrip" % tag + + +class xxxxxxxxxxxxxxxxxxxxx(xxxx.xxxxxxxxxxxxx): + def xxxxxxx_xxxxxx(xxxx): + assert xxxxxxx_xxxx in [ + x.xxxxx.xxxxxx.xxxxx.xxxxxx, + x.xxxxx.xxxxxx.xxxxx.xxxx, + ], "xxxxxxxxxxx xxxxxxx xxxx (xxxxxx xxxx) %x xxx xxxxx" % xxxxxxx_xxxx + + +value.__dict__[ + key +] = "test" # set some Thrift field to non-None in the struct aa bb cc dd ee + +RE_ONE_BACKSLASH = { + "asdf_hjkl_jkl": re.compile( + r"(?>\n" +) + +assert str(suffix_arr) == ( + "['$', 'angaroo$', 'angrykangaroo$', 'aroo$', 'garoo$', " + "'grykangaroo$', 'kangaroo$', 'ngaroo$', 'ngrykangaroo$', " + "'o$', 'oo$', 'roo$', 'rykangaroo$', 'ykangaroo$']" +) +assert str(suffix_arr) != ( + "['$', 'angaroo$', 'angrykangaroo$', 'aroo$', 'garoo$', " + "'grykangaroo$', 'kangaroo$', 'ngaroo$', 'ngrykangaroo$', " + "'o$', 'oo$', 'roo$', 'rykangaroo$', 'ykangaroo$']" +) +assert str(suffix_arr) <= ( + "['$', 'angaroo$', 'angrykangaroo$', 'aroo$', 'garoo$', " + "'grykangaroo$', 'kangaroo$', 'ngaroo$', 'ngrykangaroo$', " + "'o$', 'oo$', 'roo$', 'rykangaroo$', 'ykangaroo$']" +) +assert str(suffix_arr) >= ( + "['$', 'angaroo$', 'angrykangaroo$', 'aroo$', 'garoo$', " + "'grykangaroo$', 'kangaroo$', 'ngaroo$', 'ngrykangaroo$', " + "'o$', 'oo$', 'roo$', 'rykangaroo$', 'ykangaroo$']" +) +assert str(suffix_arr) < ( + "['$', 'angaroo$', 'angrykangaroo$', 'aroo$', 'garoo$', " + "'grykangaroo$', 'kangaroo$', 'ngaroo$', 'ngrykangaroo$', " + "'o$', 'oo$', 'roo$', 'rykangaroo$', 'ykangaroo$']" +) +assert str(suffix_arr) > ( + "['$', 'angaroo$', 'angrykangaroo$', 'aroo$', 'garoo$', " + "'grykangaroo$', 'kangaroo$', 'ngaroo$', 'ngrykangaroo$', " + "'o$', 'oo$', 'roo$', 'rykangaroo$', 'ykangaroo$']" +) +assert ( + str(suffix_arr) + in "['$', 'angaroo$', 'angrykangaroo$', 'aroo$', 'garoo$', 'grykangaroo$', 'kangaroo$', 'ngaroo$', 'ngrykangaroo$', 'o$', 'oo$', 'roo$', 'rykangaroo$', 'ykangaroo$']" +) +assert ( + str(suffix_arr) + not in "['$', 'angaroo$', 'angrykangaroo$', 'aroo$', 'garoo$', 'grykangaroo$', 'kangaroo$', 'ngaroo$', 'ngrykangaroo$', 'o$', 'oo$', 'roo$', 'rykangaroo$', 'ykangaroo$']" +) +message = ( + f"1. Go to Google Developers Console and log in with your Google account." + "(https://console.developers.google.com/)" + "2. You should be prompted to create a new project (name does not matter)." + "3. Click on Enable APIs and Services at the top." + "4. In the list of APIs choose or search for YouTube Data API v3 and " + "click on it. Choose Enable." + "5. Click on Credentials on the left navigation bar." + "6. Click on Create Credential at the top." + '7. At the top click the link for "API key".' + "8. No application restrictions are needed. Click Create at the bottom." + "9. You now have a key to add to `{prefix}set api youtube api_key`" +) +message = ( + f"1. Go to Google Developers Console and log in with your Google account." + "(https://console.developers.google.com/)" + "2. You should be prompted to create a new project (name does not matter)." + f"3. Click on Enable APIs and Services at the top." + "4. In the list of APIs choose or search for YouTube Data API v3 and " + "click on it. Choose Enable." + f"5. Click on Credentials on the left navigation bar." + "6. Click on Create Credential at the top." + '7. At the top click the link for "API key".' + "8. No application restrictions are needed. Click Create at the bottom." + "9. You now have a key to add to `{prefix}set api youtube api_key`" +) +message = ( + f"1. Go to Google Developers Console and log in with your Google account." + "(https://console.developers.google.com/)" + "2. You should be prompted to create a new project (name does not matter)." + f"3. Click on Enable APIs and Services at the top." + "4. In the list of APIs choose or search for YouTube Data API v3 and " + "click on it. Choose Enable." + f"5. Click on Credentials on the left navigation bar." + "6. Click on Create Credential at the top." + '7. At the top click the link for "API key".' + "8. No application restrictions are needed. Click Create at the bottom." + f"9. You now have a key to add to `{prefix}set api youtube api_key`" +) + +# It shouldn't matter if the string prefixes are capitalized. +temp_msg = ( + f"{F'{humanize_number(pos)}.': <{pound_len+2}} " + f"{balance: <{bal_len + 5}} " + f"<<{author.display_name}>>\n" +) + +fstring = ( + f"We have to remember to escape {braces}." " Like {these}." f" But not {this}." +) + +welcome_to_programming = R"hello," R" world!" + +fstring = f"f-strings definitely make things more {difficult} than they need to be for {{black}}. But boy they sure are handy. The problem is that some lines will need to have the 'f' whereas others do not. This {line}, for example, needs one." + +x = f"This is a long string which contains an f-expr that should not split {{{[i for i in range(5)]}}}." + +x = "\N{BLACK RIGHT-POINTING TRIANGLE WITH DOUBLE VERTICAL BAR}\N{VARIATION SELECTOR-16}" + +xxxxxx_xxx_xxxx_xx_xxxxx_xxxxxxxx_xxxxxxxx_xxxxxxxxxx_xxxx_xxxx_xxxxx = xxxx.xxxxxx.xxxxxxxxx.xxxxxxxxxxxxxxxxxxxx( + xx_xxxxxx={ + "x3_xxxxxxxx": "xxx3_xxxxx_xxxxxxxx_xxxxxxxx_xxxxxxxxxx_xxxxxxxx_xxxxxx_xxxxxxx", + }, +) + +# Regression test for https://github.com/psf/black/issues/3117. +some_dict = { + "something_something": r"Lorem ipsum dolor sit amet, an sed convenire eloquentiam \t" + r"signiferumque, duo ea vocibus consetetur scriptorem. Facer \t", +} + +# Regression test for https://github.com/psf/black/issues/3459. +xxxx( + empty_str_as_first_split="" + f"xxxxxxx {xxxxxxxxxx} xxx xxxxxxxxxx xxxxx xxx xxx xx " + "xxxxx xxxxxxxxx xxxxxxx, xxx xxxxxxxxxxx xxx xxxxx. " + f"xxxxxxxxxxxxx xxxx xx xxxxxxxxxx. xxxxx: {x.xxx}", + empty_u_str_as_first_split="" + f"xxxxxxx {xxxxxxxxxx} xxx xxxxxxxxxx xxxxx xxx xxx xx " + "xxxxx xxxxxxxxx xxxxxxx, xxx xxxxxxxxxxx xxx xxxxx. " + f"xxxxxxxxxxxxx xxxx xx xxxxxxxxxx. xxxxx: {x.xxx}", +) + +# Regression test for https://github.com/psf/black/issues/3455. +a_dict = { + "/this/is/a/very/very/very/very/very/very/very/very/very/very/long/key/without/spaces": + # And there is a comment before the value + ("item1", "item2", "item3"), +} + +# Regression test for https://github.com/psf/black/issues/3506. +s = ( + "With single quote: ' " + f" {my_dict['foo']}" + ' With double quote: " ' + f' {my_dict["bar"]}' +) + +s = f'Lorem Ipsum is simply dummy text of the printing and typesetting industry:\'{my_dict["foo"]}\'' +``` + +## Black Output + +```python +class A: + def foo(): + result = type(message)("") + + +# Don't merge multiline (e.g. triple-quoted) strings. +def foo(): + query = ( + """SELECT xxxxxxxxxxxxxxxxxxxx(xxx)""" + """ FROM xxxxxxxxxxxxxxxx WHERE xxxxxxxxxx AND xxx <> xxxxxxxxxxxxxx()""" + ) + + +# There was a bug where tuples were being identified as long strings. +long_tuple = ( + "Apple", + "Berry", + "Cherry", + "Dill", + "Evergreen", + "Fig", + "Grape", + "Harry", + "Iglu", + "Jaguar", +) + +stupid_format_method_bug = ( + "Some really long string that just so happens to be the {} {} to force the 'format'" + " method to hang over the line length boundary. This is pretty annoying.".format( + "perfect", "length" + ) +) + + +class A: + def foo(): + os.system( + "This is a regression test. xxx xxx xxx xxx xxx xxx xxx xxx xxx xxx xxx xxx" + " xxx xxx xxx xxx xxx xxx xxx xxx xxx xxx xxx xxx xxx xxx xxx xxx xxx xxx" + " xxxx.".format("xxxxxxxxxx", "xxxxxx", "xxxxxxxxxx") + ) + + +class A: + def foo(): + XXXXXXXXXXXX.append(( + "xxx_xxxxxxxxxx(xxxxx={}, xxxx={}, xxxxx, xxxx_xxxx_xxxxxxxxxx={})".format( + xxxxx, xxxx, xxxx_xxxx_xxxxxxxxxx + ), + my_var, + my_other_var, + )) + + +class A: + class B: + def foo(): + bar( + "[{}]: xxx_xxxxxxxxxx(xxxxx={}, xxxx={}, xxxxx={}" + " xxxx_xxxx_xxxxxxxxxx={}, xxxx={})".format( + xxxx._xxxxxxxxxxxxxx, xxxxx, xxxx, xxxx_xxxx_xxxxxxxxxx, xxxxxxx + ), + varX, + varY, + varZ, + ) + + +def foo(xxxx): + for xxx_xxxx, _xxx_xxx, _xxx_xxxxx, xxx_xxxx in xxxx: + for xxx in xxx_xxxx: + assert ("x" in xxx) or (xxx in xxx_xxx_xxxxx), ( + "{0} xxxxxxx xx {1}, xxx {1} xx xxx xx xxxx xx xxx xxxx: xxx xxxx {2}" + .format(xxx_xxxx, xxx, xxxxxx.xxxxxxx(xxx_xxx_xxxxx)) + ) + + +class A: + def disappearing_comment(): + return ( + ( # xx -x xxxxxxx xx xxx xxxxxxx. + "{{xxx_xxxxxxxxxx_xxxxxxxx}} xxx xxxx {} {{xxxx}} >&2".format( + "{xxxx} {xxxxxx}" + if xxxxx.xx_xxxxxxxxxx + else ( # Disappearing Comment + "--xxxxxxx --xxxxxx=x --xxxxxx-xxxxx=xxxxxx" + " --xxxxxx-xxxx=xxxxxxxxxxx.xxx" + ) + ) + ), + (x, y, z), + ) + + +class A: + class B: + def foo(): + xxxxx_xxxx( + xx, + "\t" + "@xxxxxx '{xxxx_xxx}\t' > {xxxxxx_xxxx}.xxxxxxx;" + "{xxxx_xxx} >> {xxxxxx_xxxx}.xxxxxxx 2>&1; xx=$$?;" + "xxxx $$xx".format( + xxxx_xxx=xxxx_xxxxxxx, + xxxxxx_xxxx=xxxxxxx + "/" + xxxx_xxx_xxxx, + x=xxx_xxxxx_xxxxx_xxx, + ), + x, + y, + z, + ) + + +func_call_where_string_arg_has_method_call_and_bad_parens( + "A long string with {}. This string is so long that it is ridiculous. It can't fit" + " on one line at alllll.".format("formatting"), +) + +func_call_where_string_arg_has_old_fmt_and_bad_parens( + "A long string with {}. This string is so long that it is ridiculous. It can't fit" + " on one line at alllll." % "formatting", +) + +func_call_where_string_arg_has_old_fmt_and_bad_parens( + "A long string with {}. This {} is so long that it is ridiculous. It can't fit on" + " one line at alllll." % ("formatting", "string"), +) + + +class A: + def append(self): + if True: + xxxx.xxxxxxx.xxxxx( + "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" + % ( + xxxx.xxxxxxxxx, + xxxx.xxxxxxxxxxxxxx(xxxx.xxxxxxxxx), + x, + xxxx.xxxxxxxxxxxxxx(xx), + ) + ) + + +class A: + def foo(): + some_func_call( + "xxxxxxxxxx", + "xx {xxxxxxxxxxx}/xxxxxxxxxxx.xxx xxxx.xxx && xxxxxx -x " + '"xxxx xxxxxxx xxxxxx xxxx; xxxx xxxxxx_xxxxx xxxxxx xxxx; ' + "xxxx.xxxx_xxxxxx(['xxxx.xxx'], xxxx.xxxxxxx().xxxxxxxxxx)\" ", + None, + ("xxxxxxxxxxx",), + ), + + +class A: + def foo(): + some_func_call( + "xx {xxxxxxxxxxx}/xxxxxxxxxxx.xxx xxxx.xxx && xxxxxx -x " + "xxxx, ('xxxxxxx xxxxxx xxxx, xxxx') xxxxxx_xxxxx xxxxxx xxxx; " + "xxxx.xxxx_xxxxxx(['xxxx.xxx'], xxxx.xxxxxxx().xxxxxxxxxx)\" ", + None, + ("xxxxxxxxxxx",), + ), + + +xxxxxxx = { + "xx": ( + "xxxx xxxxxxx xxxxxxxxx -x xxx -x /xxx/{0} -x xxx,xxx -xx {1} -xx {1} -xx" + " xxx=xxx_xxxx,xxx_xx,xxx_xxx,xxx_xxxx,xxx_xx,xxx_xxx | xxxxxx -x xxxxxxxx -x" + " xxxxxxxx -x" + ), + "xx": ( + "xxxx xxxxxxx xxxxxxxxx -x xxx -x /xxx/{0} -x xxx,xxx -xx {1} -xx {1} -xx" + " xxx=xxx_xxxx_xxx_xxxx,xxx_xx_xxx_xxxx,xxx_xxxx_xxx_xxxx,xxx_xx_xxxx_xxxx,xxx_xxx_xxxx,xxx_xxx_xxxx" + " xxxx=xxx | xxxxxx -x xxxxxxxx -x xxxxxxxx -x" + ), +} + + +class A: + def foo(self): + if True: + xxxxx_xxxxxxxxxxxx( + "xxx xxxxxx xxx xxxxxxxxx.xx xx xxxxxxxx. xxx xxxxxxxxxxxxx.xx" + " xxxxxxx " + + "xx xxxxxx xxxxxx xxxxxx xx xxxxxxx xxx xxx ${0} xx x xxxxxxxx xxxxx" + .xxxxxx(xxxxxx_xxxxxx_xxx) + ) + + +class A: + class B: + def foo(): + row = { + "xxxxxxxxxxxxxxx": xxxxxx_xxxxx_xxxx, + # 'xxxxxxxxxxxxxxxxxxxxxxx' + # 'xxxxxxxxxxxxxxxxxxxxxx' + # 'xxxxxxxxxxxxxxxxxx' + # 'xxxxxxxxxxxxxxxxx' + "xxxxxxxxxx": xxxxx_xxxxx, + } + + +class A: + def xxxx_xxx_xx_xxxxxxxxxx_xxxx_xxxxxxxxx(xxxx): + xxxxxxxx = [ + xxxxxxxxxxxxxxxx( + "xxxx", + xxxxxxxxxxx={ + "xxxx": 1.0, + }, + xxxxxx={"xxxxxx 1": xxxxxx(xxxx="xxxxxx 1", xxxxxx=600.0)}, + xxxxxxxx_xxxxxxx=0.0, + ), + xxxxxxxxxxxxxxxx( + "xxxxxxx", + xxxxxxxxxxx={ + "xxxx": 1.0, + }, + xxxxxx={"xxxxxx 1": xxxxxx(xxxx="xxxxxx 1", xxxxxx=200.0)}, + xxxxxxxx_xxxxxxx=0.0, + ), + xxxxxxxxxxxxxxxx( + "xxxx", + ), + ] + + +some_dictionary = { + "xxxxx006": [ + ( + "xxx-xxx" + " xxxxx3xxxx1xx2xxxxxxxxxxxxxx0xx6xxxxxxxxxx2xxxxxx9xxxxxxxxxx0xxxxx1xxx2x/xx9xx6+x+xxxxxxxxxxxxxx4xxxxxxxxxxxxxxxxxxxxx43xxx2xx2x4x++xxx6xxxxxxxxx+xxxxx/xx9x+xxxxxxxxxxxxxx8x15xxxxxxxxxxxxxxxxx82xx/xxxxxxxxxxxxxx/x5xxxxxxxxxxxxxx6xxxxxx74x4/xxx4x+xxxxxxxxx2xxxxxxxx87xxxxx4xxxxxxxx3xx0xxxxx4xxx1xx9xx5xxxxxxx/xxxxx5xx6xx4xxxx1x/x2xxxxxxxxxxxx64xxxxxxx1x0xx5xxxxxxxxxxxxxx==" + " xxxxx000 xxxxxxxxxx\n" + ), + ( + "xxx-xxx" + " xxxxx3xxxx1xx2xxxxxxxxxxxxxx6xxxxxxxxxxxxxx9xxxxxxxxxxxxx3xxx9xxxxxxxxxxxxxxxx0xxxxxxxxxxxxxxxxx2xxxx2xxx6xxxxx/xx54xxxxxxxxx4xxx3xxxxxx9xx3xxxxx39xxxxxxxxx5xx91xxxx7xxxxxx8xxxxxxxxxxxxxxxx9xxx93xxxxxxxxxxxxxxxxx7xxx8xx8xx4/x1xxxxx1x3xxxxxxxxxxxxx3xxxxxx9xx4xx4x7xxxxxxxxxxxxx1xxxxxxxxx7xxxxxxxxxxxxxx4xx6xxxxxxxxx9xxx7xxxx2xxxxxxxxxxxxxxxxxxxxxx8xxxxxxxxxxxxxxxxxxxx6xx==" + " xxxxx010 xxxxxxxxxx\n" + ), + ], + "xxxxx016": [ + ( + "xxx-xxx" + " xxxxx3xxxx1xx2xxxxxxxxxxxxxx0xx6xxxxxxxxxx2xxxxxx9xxxxxxxxxx0xxxxx1xxx2x/xx9xx6+x+xxxxxxxxxxxxxx4xxxxxxxxxxxxxxxxxxxxx43xxx2xx2x4x++xxx6xxxxxxxxx+xxxxx/xx9x+xxxxxxxxxxxxxx8x15xxxxxxxxxxxxxxxxx82xx/xxxxxxxxxxxxxx/x5xxxxxxxxxxxxxx6xxxxxx74x4/xxx4x+xxxxxxxxx2xxxxxxxx87xxxxx4xxxxxxxx3xx0xxxxx4xxx1xx9xx5xxxxxxx/xxxxx5xx6xx4xxxx1x/x2xxxxxxxxxxxx64xxxxxxx1x0xx5xxxxxxxxxxxxxx==" + " xxxxx000 xxxxxxxxxx\n" + ), + ( + "xxx-xxx" + " xxxxx3xxxx1xx2xxxxxxxxxxxxxx6xxxxxxxxxxxxxx9xxxxxxxxxxxxx3xxx9xxxxxxxxxxxxxxxx0xxxxxxxxxxxxxxxxx2xxxx2xxx6xxxxx/xx54xxxxxxxxx4xxx3xxxxxx9xx3xxxxx39xxxxxxxxx5xx91xxxx7xxxxxx8xxxxxxxxxxxxxxxx9xxx93xxxxxxxxxxxxxxxxx7xxx8xx8xx4/x1xxxxx1x3xxxxxxxxxxxxx3xxxxxx9xx4xx4x7xxxxxxxxxxxxx1xxxxxxxxx7xxxxxxxxxxxxxx4xx6xxxxxxxxx9xxx7xxxx2xxxxxxxxxxxxxxxxxxxxxx8xxxxxxxxxxxxxxxxxxxx6xx==" + " xxxxx010 xxxxxxxxxx\n" + ), + ], +} + + +def foo(): + xxx_xxx = ( # xxxx xxxxxxxxxx xxxx xx xxxx xx xxx xxxxxxxx xxxxxx xxxxx. + 'xxxx xxx xxxxxxxx_xxxx xx "xxxxxxxxxx".\n xxx: xxxxxx xxxxxxxx_xxxx=xxxxxxxxxx' + ) + + +some_tuple = ("some string", "some string which should be joined") + +some_commented_string = ( # This comment stays at the top. + "This string is long but not so long that it needs hahahah toooooo be so greatttt" + " {} that I just can't think of any more good words to say about it at" + " allllllllllll".format("ha") # comments here are fine +) + +some_commented_string = ( + "This string is long but not so long that it needs hahahah toooooo be so greatttt" # But these + " {} that I just can't think of any more good words to say about it at" # comments will stay + " allllllllllll".format("ha") # comments here are fine +) + +lpar_and_rpar_have_comments = func_call( # LPAR Comment + "Long really ridiculous type of string that shouldn't really even exist at all. I" + " mean commmme onnn!!!", # Comma Comment +) # RPAR Comment + +cmd_fstring = ( + "sudo -E deluge-console info --detailed --sort-reverse=time_added " + f"{'' if ID is None else ID} | perl -nE 'print if /^{field}:/'" +) + +cmd_fstring = ( + "sudo -E deluge-console info --detailed --sort-reverse=time_added" + f" {'' if ID is None else ID} | perl -nE 'print if /^{field}:/'" +) + +cmd_fstring = ( + "sudo -E deluge-console info --detailed --sort-reverse=time_added" + f" {'{{}}' if ID is None else ID} | perl -nE 'print if /^{field}:/'" +) + +cmd_fstring = ( + "sudo -E deluge-console info --detailed --sort-reverse=time_added {'' if ID is" + f" None else ID}} | perl -nE 'print if /^{field}:/'" +) + +fstring = ( + "This string really doesn't need to be an {{fstring}}, but this one most" + f" certainly, absolutely {does}." +) + +fstring = f"We have to remember to escape {braces}. Like {{these}}. But not {this}." + + +class A: + class B: + def foo(): + st_error = STError( + f"This string ({string_leaf.value}) appears to be pointless (i.e. has" + " no parent)." + ) + + +def foo(): + user_regex = _lazy_re_compile( + r"(^[-!#$%&'*+/=?^_`{}|~0-9A-Z]+(\.[-!#$%&'*+/=?^_`{}|~0-9A-Z]+)*\Z" # dot-atom + r'|^"([\001-\010\013\014\016-\037!#-\[\]-\177]|\\[\001-\011\013\014\016-\177])*"\Z)', # quoted-string + re.IGNORECASE, + ) + + +def foo(): + user_regex = _lazy_re_compile( + "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" # dot-atom + "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", # quoted-string + xyz, + ) + + +def foo(): + user_regex = _lazy_re_compile( + "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" # dot-atom + "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", # quoted-string + xyz, + ) + + +class A: + class B: + def foo(): + if not hasattr(module, name): + raise ValueError( + "Could not find object %s in %s.\n" + "Please note that you cannot serialize things like inner " + "classes. Please move the object into the main module " + "body to use migrations.\n" + "For more information, see " + "https://docs.djangoproject.com/en/%s/topics/migrations/#serializing-values" + % (name, module_name, get_docs_version()) + ) + + +class A: + class B: + def foo(): + if not hasattr(module, name): + raise ValueError( + "Could not find object %s in %s.\nPlease note that you cannot" + " serialize things like inner classes. Please move the object into" + " the main module body to use migrations.\nFor more information," + " see https://docs.djangoproject.com/en/%s/topics/migrations/#serializing-values" + % (name, module_name, get_docs_version()) + ) + + +x = ( + "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" + "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" + "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" + "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" +) + + +class Step(StepBase): + def who(self): + self.cmd = ( + "SR AAAA-CORRECT NAME IS {last_name} {first_name}{middle_name}" + " {title}/P{passenger_association}".format( + last_name=last_name, + first_name=first_name, + middle_name=middle_name, + title=title, + passenger_association=passenger_association, + ) + ) + + +xxxxxxx_xxxxxx_xxxxxxx = xxx([ + xxxxxxxxxxxx( + xxxxxx_xxxxxxx=( + '((x.aaaaaaaaa = "xxxxxx.xxxxxxxxxxxxxxxxxxxxx") || (x.xxxxxxxxx =' + ' "xxxxxxxxxxxx")) && ' + # xxxxx xxxxxxxxxxxx xxxx xxx (xxxxxxxxxxxxxxxx) xx x xxxxxxxxx xx xxxxxx. + "(x.bbbbbbbbbbbb.xxx != " + '"xxx:xxx:xxx::cccccccccccc:xxxxxxx-xxxx/xxxxxxxxxxx/xxxxxxxxxxxxxxxxx") && ' + ) + ) +]) + +if __name__ == "__main__": + for i in range(4, 8): + cmd = ( + r"for pid in $(ps aux | grep paster | grep -v grep | grep '\-%d' | awk" + r" '{print $2}'); do kill $pid; done" % (i) + ) + + +def A(): + def B(): + def C(): + def D(): + def E(): + def F(): + def G(): + assert ( + c_float(val[0][0] / val[0][1]).value + == c_float(value[0][0] / value[0][1]).value + ), "%s didn't roundtrip" % tag + + +class xxxxxxxxxxxxxxxxxxxxx(xxxx.xxxxxxxxxxxxx): + def xxxxxxx_xxxxxx(xxxx): + assert xxxxxxx_xxxx in [ + x.xxxxx.xxxxxx.xxxxx.xxxxxx, + x.xxxxx.xxxxxx.xxxxx.xxxx, + ], ( + "xxxxxxxxxxx xxxxxxx xxxx (xxxxxx xxxx) %x xxx xxxxx" % xxxxxxx_xxxx + ) + + +value.__dict__[key] = ( + "test" # set some Thrift field to non-None in the struct aa bb cc dd ee +) + +RE_ONE_BACKSLASH = { + "asdf_hjkl_jkl": re.compile( + r"(?>\n" +) + +assert ( + str(suffix_arr) + == "['$', 'angaroo$', 'angrykangaroo$', 'aroo$', 'garoo$', " + "'grykangaroo$', 'kangaroo$', 'ngaroo$', 'ngrykangaroo$', " + "'o$', 'oo$', 'roo$', 'rykangaroo$', 'ykangaroo$']" +) +assert ( + str(suffix_arr) + != "['$', 'angaroo$', 'angrykangaroo$', 'aroo$', 'garoo$', " + "'grykangaroo$', 'kangaroo$', 'ngaroo$', 'ngrykangaroo$', " + "'o$', 'oo$', 'roo$', 'rykangaroo$', 'ykangaroo$']" +) +assert ( + str(suffix_arr) + <= "['$', 'angaroo$', 'angrykangaroo$', 'aroo$', 'garoo$', " + "'grykangaroo$', 'kangaroo$', 'ngaroo$', 'ngrykangaroo$', " + "'o$', 'oo$', 'roo$', 'rykangaroo$', 'ykangaroo$']" +) +assert ( + str(suffix_arr) + >= "['$', 'angaroo$', 'angrykangaroo$', 'aroo$', 'garoo$', " + "'grykangaroo$', 'kangaroo$', 'ngaroo$', 'ngrykangaroo$', " + "'o$', 'oo$', 'roo$', 'rykangaroo$', 'ykangaroo$']" +) +assert ( + str(suffix_arr) + < "['$', 'angaroo$', 'angrykangaroo$', 'aroo$', 'garoo$', " + "'grykangaroo$', 'kangaroo$', 'ngaroo$', 'ngrykangaroo$', " + "'o$', 'oo$', 'roo$', 'rykangaroo$', 'ykangaroo$']" +) +assert ( + str(suffix_arr) + > "['$', 'angaroo$', 'angrykangaroo$', 'aroo$', 'garoo$', " + "'grykangaroo$', 'kangaroo$', 'ngaroo$', 'ngrykangaroo$', " + "'o$', 'oo$', 'roo$', 'rykangaroo$', 'ykangaroo$']" +) +assert ( + str(suffix_arr) + in "['$', 'angaroo$', 'angrykangaroo$', 'aroo$', 'garoo$', 'grykangaroo$'," + " 'kangaroo$', 'ngaroo$', 'ngrykangaroo$', 'o$', 'oo$', 'roo$', 'rykangaroo$'," + " 'ykangaroo$']" +) +assert ( + str(suffix_arr) + not in "['$', 'angaroo$', 'angrykangaroo$', 'aroo$', 'garoo$', 'grykangaroo$'," + " 'kangaroo$', 'ngaroo$', 'ngrykangaroo$', 'o$', 'oo$', 'roo$'," + " 'rykangaroo$', 'ykangaroo$']" +) +message = ( + f"1. Go to Google Developers Console and log in with your Google account." + f"(https://console.developers.google.com/)" + f"2. You should be prompted to create a new project (name does not matter)." + f"3. Click on Enable APIs and Services at the top." + f"4. In the list of APIs choose or search for YouTube Data API v3 and " + f"click on it. Choose Enable." + f"5. Click on Credentials on the left navigation bar." + f"6. Click on Create Credential at the top." + f'7. At the top click the link for "API key".' + f"8. No application restrictions are needed. Click Create at the bottom." + f"9. You now have a key to add to `{{prefix}}set api youtube api_key`" +) +message = ( + f"1. Go to Google Developers Console and log in with your Google account." + f"(https://console.developers.google.com/)" + f"2. You should be prompted to create a new project (name does not matter)." + f"3. Click on Enable APIs and Services at the top." + f"4. In the list of APIs choose or search for YouTube Data API v3 and " + f"click on it. Choose Enable." + f"5. Click on Credentials on the left navigation bar." + f"6. Click on Create Credential at the top." + f'7. At the top click the link for "API key".' + f"8. No application restrictions are needed. Click Create at the bottom." + f"9. You now have a key to add to `{{prefix}}set api youtube api_key`" +) +message = ( + "1. Go to Google Developers Console and log in with your Google account." + "(https://console.developers.google.com/)" + "2. You should be prompted to create a new project (name does not matter)." + "3. Click on Enable APIs and Services at the top." + "4. In the list of APIs choose or search for YouTube Data API v3 and " + "click on it. Choose Enable." + "5. Click on Credentials on the left navigation bar." + "6. Click on Create Credential at the top." + '7. At the top click the link for "API key".' + "8. No application restrictions are needed. Click Create at the bottom." + f"9. You now have a key to add to `{prefix}set api youtube api_key`" +) + +# It shouldn't matter if the string prefixes are capitalized. +temp_msg = ( + f"{F'{humanize_number(pos)}.': <{pound_len+2}} " + f"{balance: <{bal_len + 5}} " + f"<<{author.display_name}>>\n" +) + +fstring = f"We have to remember to escape {braces}. Like {{these}}. But not {this}." + +welcome_to_programming = R"hello," R" world!" + +fstring = ( + f"f-strings definitely make things more {difficult} than they need to be for" + " {black}. But boy they sure are handy. The problem is that some lines will need" + f" to have the 'f' whereas others do not. This {line}, for example, needs one." +) + +x = ( + "This is a long string which contains an f-expr that should not split" + f" {{{[i for i in range(5)]}}}." +) + +x = ( + "\N{BLACK RIGHT-POINTING TRIANGLE WITH DOUBLE VERTICAL BAR}\N{VARIATION SELECTOR-16}" +) + +xxxxxx_xxx_xxxx_xx_xxxxx_xxxxxxxx_xxxxxxxx_xxxxxxxxxx_xxxx_xxxx_xxxxx = xxxx.xxxxxx.xxxxxxxxx.xxxxxxxxxxxxxxxxxxxx( + xx_xxxxxx={ + "x3_xxxxxxxx": ( + "xxx3_xxxxx_xxxxxxxx_xxxxxxxx_xxxxxxxxxx_xxxxxxxx_xxxxxx_xxxxxxx" + ), + }, +) + +# Regression test for https://github.com/psf/black/issues/3117. +some_dict = { + "something_something": ( + r"Lorem ipsum dolor sit amet, an sed convenire eloquentiam \t" + r"signiferumque, duo ea vocibus consetetur scriptorem. Facer \t" + ), +} + +# Regression test for https://github.com/psf/black/issues/3459. +xxxx( + empty_str_as_first_split=( + "" + f"xxxxxxx {xxxxxxxxxx} xxx xxxxxxxxxx xxxxx xxx xxx xx " + "xxxxx xxxxxxxxx xxxxxxx, xxx xxxxxxxxxxx xxx xxxxx. " + f"xxxxxxxxxxxxx xxxx xx xxxxxxxxxx. xxxxx: {x.xxx}" + ), + empty_u_str_as_first_split=( + "" + f"xxxxxxx {xxxxxxxxxx} xxx xxxxxxxxxx xxxxx xxx xxx xx " + "xxxxx xxxxxxxxx xxxxxxx, xxx xxxxxxxxxxx xxx xxxxx. " + f"xxxxxxxxxxxxx xxxx xx xxxxxxxxxx. xxxxx: {x.xxx}" + ), +) + +# Regression test for https://github.com/psf/black/issues/3455. +a_dict = { + "/this/is/a/very/very/very/very/very/very/very/very/very/very/long/key/without/spaces": + # And there is a comment before the value + ("item1", "item2", "item3"), +} + +# Regression test for https://github.com/psf/black/issues/3506. +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']}'" +) +``` + + diff --git a/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__preview_long_strings__type_annotations.py.snap b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__preview_long_strings__type_annotations.py.snap new file mode 100644 index 0000000000..8b8220f9c4 --- /dev/null +++ b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__preview_long_strings__type_annotations.py.snap @@ -0,0 +1,115 @@ +--- +source: crates/ruff_python_formatter/tests/fixtures.rs +input_file: crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_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", ++ 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__preview_multiline_strings.py.snap b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__preview_multiline_strings.py.snap new file mode 100644 index 0000000000..9e841c8b21 --- /dev/null +++ b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__preview_multiline_strings.py.snap @@ -0,0 +1,923 @@ +--- +source: crates/ruff_python_formatter/tests/fixtures.rs +input_file: crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_multiline_strings.py +--- +## Input + +```python +"""cow +say""", +call(3, "dogsay", textwrap.dedent("""dove + coo""" % "cowabunga")) +call(3, "dogsay", textwrap.dedent("""dove +coo""" % "cowabunga")) +call(3, textwrap.dedent("""cow + moo""" % "cowabunga"), "dogsay") +call(3, "dogsay", textwrap.dedent("""crow + caw""" % "cowabunga"),) +call(3, textwrap.dedent("""cat + meow""" % "cowabunga"), {"dog", "say"}) +call(3, {"dog", "say"}, textwrap.dedent("""horse + neigh""" % "cowabunga")) +call(3, {"dog", "say"}, textwrap.dedent("""pig + oink""" % "cowabunga"),) +textwrap.dedent("""A one-line triple-quoted string.""") +textwrap.dedent("""A two-line triple-quoted string +since it goes to the next line.""") +textwrap.dedent("""A three-line triple-quoted string +that not only goes to the next line +but also goes one line beyond.""") +textwrap.dedent("""\ + A triple-quoted string + actually leveraging the textwrap.dedent functionality + that ends in a trailing newline, + representing e.g. file contents. +""") +path.write_text(textwrap.dedent("""\ + A triple-quoted string + actually leveraging the textwrap.dedent functionality + that ends in a trailing newline, + representing e.g. file contents. +""")) +path.write_text(textwrap.dedent("""\ + A triple-quoted string + actually leveraging the textwrap.dedent functionality + that ends in a trailing newline, + representing e.g. {config_filename} file contents. +""".format("config_filename", config_filename))) +# Another use case +data = yaml.load("""\ +a: 1 +b: 2 +""") +data = yaml.load("""\ +a: 1 +b: 2 +""",) +data = yaml.load( + """\ + a: 1 + b: 2 +""" +) + +MULTILINE = """ +foo +""".replace("\n", "") +generated_readme = lambda project_name: """ +{} + + +""".strip().format(project_name) +parser.usage += """ +Custom extra help summary. + +Extra test: +- with +- bullets +""" + + +def get_stuff(cr, value): + # original + cr.execute(""" + SELECT whatever + FROM some_table t + WHERE id = %s + """, [value]) + return cr.fetchone() + + +def get_stuff(cr, value): + # preferred + cr.execute( + """ + SELECT whatever + FROM some_table t + WHERE id = %s + """, + [value], + ) + return cr.fetchone() + + +call(arg1, arg2, """ +short +""", arg3=True) +test_vectors = [ + "one-liner\n", + "two\nliner\n", + """expressed +as a three line +mulitline string""", +] + +_wat = re.compile( + r""" + regex + """, + re.MULTILINE | re.VERBOSE, +) +dis_c_instance_method = """\ +%3d 0 LOAD_FAST 1 (x) + 2 LOAD_CONST 1 (1) + 4 COMPARE_OP 2 (==) + 6 LOAD_FAST 0 (self) + 8 STORE_ATTR 0 (x) + 10 LOAD_CONST 0 (None) + 12 RETURN_VALUE +""" % (_C.__init__.__code__.co_firstlineno + 1,) +path.write_text(textwrap.dedent("""\ + A triple-quoted string + actually {verb} the textwrap.dedent functionality + that ends in a trailing newline, + representing e.g. {file_type} file contents. +""".format(verb="using", file_type="test"))) +{"""cow +moos"""} +["""cow +moos"""] +["""cow +moos""", """dog +woofs +and +barks"""] +def dastardly_default_value( + cow: String = json.loads("""this +is +quite +the +dastadardly +value!"""), + **kwargs, +): + pass + +print(f""" + This {animal} + moos and barks +{animal} say +""") +msg = f"""The arguments {bad_arguments} were passed in. +Please use `--build-option` instead, +`--global-option` is reserved to flags like `--verbose` or `--quiet`. +""" + +this_will_become_one_line = ( + "a" + "b" + "c" +) + +this_will_stay_on_three_lines = ( + "a" # comment + "b" + "c" +) + +this_will_also_become_one_line = ( # comment + "a" + "b" + "c" +) +``` + +## Black Differences + +```diff +--- Black ++++ Ruff +@@ -1,95 +1,138 @@ +-"""cow ++( ++ """cow + say""", ++) + call( + 3, + "dogsay", +- textwrap.dedent("""dove +- coo""" % "cowabunga"), ++ textwrap.dedent( ++ """dove ++ coo""" ++ % "cowabunga" ++ ), + ) + call( + 3, + "dogsay", +- textwrap.dedent("""dove +-coo""" % "cowabunga"), ++ textwrap.dedent( ++ """dove ++coo""" ++ % "cowabunga" ++ ), + ) + call( + 3, +- textwrap.dedent("""cow +- moo""" % "cowabunga"), ++ textwrap.dedent( ++ """cow ++ moo""" ++ % "cowabunga" ++ ), + "dogsay", + ) + call( + 3, + "dogsay", +- textwrap.dedent("""crow +- caw""" % "cowabunga"), ++ textwrap.dedent( ++ """crow ++ caw""" ++ % "cowabunga" ++ ), + ) + call( + 3, +- textwrap.dedent("""cat +- meow""" % "cowabunga"), ++ textwrap.dedent( ++ """cat ++ meow""" ++ % "cowabunga" ++ ), + {"dog", "say"}, + ) + call( + 3, + {"dog", "say"}, +- textwrap.dedent("""horse +- neigh""" % "cowabunga"), ++ textwrap.dedent( ++ """horse ++ neigh""" ++ % "cowabunga" ++ ), + ) + call( + 3, + {"dog", "say"}, +- textwrap.dedent("""pig +- oink""" % "cowabunga"), ++ textwrap.dedent( ++ """pig ++ oink""" ++ % "cowabunga" ++ ), + ) + textwrap.dedent("""A one-line triple-quoted string.""") +-textwrap.dedent("""A two-line triple-quoted string +-since it goes to the next line.""") +-textwrap.dedent("""A three-line triple-quoted string ++textwrap.dedent( ++ """A two-line triple-quoted string ++since it goes to the next line.""" ++) ++textwrap.dedent( ++ """A three-line triple-quoted string + that not only goes to the next line +-but also goes one line beyond.""") +-textwrap.dedent("""\ ++but also goes one line beyond.""" ++) ++textwrap.dedent( ++ """\ + A triple-quoted string + actually leveraging the textwrap.dedent functionality + that ends in a trailing newline, + representing e.g. file contents. +-""") +-path.write_text(textwrap.dedent("""\ ++""" ++) ++path.write_text( ++ textwrap.dedent( ++ """\ + A triple-quoted string + actually leveraging the textwrap.dedent functionality + that ends in a trailing newline, + representing e.g. file contents. +-""")) +-path.write_text(textwrap.dedent("""\ ++""" ++ ) ++) ++path.write_text( ++ textwrap.dedent( ++ """\ + A triple-quoted string + actually leveraging the textwrap.dedent functionality + that ends in a trailing newline, + representing e.g. {config_filename} file contents. +-""".format("config_filename", config_filename))) ++""".format("config_filename", config_filename) ++ ) ++) + # Another use case +-data = yaml.load("""\ ++data = yaml.load( ++ """\ + a: 1 + b: 2 +-""") ++""" ++) + data = yaml.load( + """\ + a: 1 + b: 2 + """, + ) +-data = yaml.load("""\ ++data = yaml.load( ++ """\ + a: 1 + b: 2 +-""") ++""" ++) + + MULTILINE = """ + foo + """.replace("\n", "") +-generated_readme = lambda project_name: """ ++generated_readme = ( ++ lambda project_name: """ + {} + + + """.strip().format(project_name) ++) + parser.usage += """ + Custom extra help summary. + +@@ -156,16 +199,24 @@ + 10 LOAD_CONST 0 (None) + 12 RETURN_VALUE + """ % (_C.__init__.__code__.co_firstlineno + 1,) +-path.write_text(textwrap.dedent("""\ ++path.write_text( ++ textwrap.dedent( ++ """\ + A triple-quoted string + actually {verb} the textwrap.dedent functionality + that ends in a trailing newline, + representing e.g. {file_type} file contents. +-""".format(verb="using", file_type="test"))) +-{"""cow +-moos"""} +-["""cow +-moos"""] ++""".format(verb="using", file_type="test") ++ ) ++) ++{ ++ """cow ++moos""" ++} ++[ ++ """cow ++moos""" ++] + [ + """cow + moos""", +@@ -177,28 +228,32 @@ + + + def dastardly_default_value( +- cow: String = json.loads("""this ++ cow: String = json.loads( ++ """this + is + quite + the + dastadardly +-value!"""), ++value!""" ++ ), + **kwargs, + ): + pass + + +-print(f""" ++print( ++ f""" + This {animal} + moos and barks + {animal} say +-""") ++""" ++) + msg = f"""The arguments {bad_arguments} were passed in. + Please use `--build-option` instead, + `--global-option` is reserved to flags like `--verbose` or `--quiet`. + """ + +-this_will_become_one_line = "abc" ++this_will_become_one_line = "a" "b" "c" + + this_will_stay_on_three_lines = ( + "a" # comment +@@ -206,4 +261,6 @@ + "c" + ) + +-this_will_also_become_one_line = "abc" # comment ++this_will_also_become_one_line = ( # comment ++ "a" "b" "c" ++) +``` + +## Ruff Output + +```python +( + """cow +say""", +) +call( + 3, + "dogsay", + textwrap.dedent( + """dove + coo""" + % "cowabunga" + ), +) +call( + 3, + "dogsay", + textwrap.dedent( + """dove +coo""" + % "cowabunga" + ), +) +call( + 3, + textwrap.dedent( + """cow + moo""" + % "cowabunga" + ), + "dogsay", +) +call( + 3, + "dogsay", + textwrap.dedent( + """crow + caw""" + % "cowabunga" + ), +) +call( + 3, + textwrap.dedent( + """cat + meow""" + % "cowabunga" + ), + {"dog", "say"}, +) +call( + 3, + {"dog", "say"}, + textwrap.dedent( + """horse + neigh""" + % "cowabunga" + ), +) +call( + 3, + {"dog", "say"}, + textwrap.dedent( + """pig + oink""" + % "cowabunga" + ), +) +textwrap.dedent("""A one-line triple-quoted string.""") +textwrap.dedent( + """A two-line triple-quoted string +since it goes to the next line.""" +) +textwrap.dedent( + """A three-line triple-quoted string +that not only goes to the next line +but also goes one line beyond.""" +) +textwrap.dedent( + """\ + A triple-quoted string + actually leveraging the textwrap.dedent functionality + that ends in a trailing newline, + representing e.g. file contents. +""" +) +path.write_text( + textwrap.dedent( + """\ + A triple-quoted string + actually leveraging the textwrap.dedent functionality + that ends in a trailing newline, + representing e.g. file contents. +""" + ) +) +path.write_text( + textwrap.dedent( + """\ + A triple-quoted string + actually leveraging the textwrap.dedent functionality + that ends in a trailing newline, + representing e.g. {config_filename} file contents. +""".format("config_filename", config_filename) + ) +) +# Another use case +data = yaml.load( + """\ +a: 1 +b: 2 +""" +) +data = yaml.load( + """\ +a: 1 +b: 2 +""", +) +data = yaml.load( + """\ + a: 1 + b: 2 +""" +) + +MULTILINE = """ +foo +""".replace("\n", "") +generated_readme = ( + lambda project_name: """ +{} + + +""".strip().format(project_name) +) +parser.usage += """ +Custom extra help summary. + +Extra test: +- with +- bullets +""" + + +def get_stuff(cr, value): + # original + cr.execute( + """ + SELECT whatever + FROM some_table t + WHERE id = %s + """, + [value], + ) + return cr.fetchone() + + +def get_stuff(cr, value): + # preferred + cr.execute( + """ + SELECT whatever + FROM some_table t + WHERE id = %s + """, + [value], + ) + return cr.fetchone() + + +call( + arg1, + arg2, + """ +short +""", + arg3=True, +) +test_vectors = [ + "one-liner\n", + "two\nliner\n", + """expressed +as a three line +mulitline string""", +] + +_wat = re.compile( + r""" + regex + """, + re.MULTILINE | re.VERBOSE, +) +dis_c_instance_method = """\ +%3d 0 LOAD_FAST 1 (x) + 2 LOAD_CONST 1 (1) + 4 COMPARE_OP 2 (==) + 6 LOAD_FAST 0 (self) + 8 STORE_ATTR 0 (x) + 10 LOAD_CONST 0 (None) + 12 RETURN_VALUE +""" % (_C.__init__.__code__.co_firstlineno + 1,) +path.write_text( + textwrap.dedent( + """\ + A triple-quoted string + actually {verb} the textwrap.dedent functionality + that ends in a trailing newline, + representing e.g. {file_type} file contents. +""".format(verb="using", file_type="test") + ) +) +{ + """cow +moos""" +} +[ + """cow +moos""" +] +[ + """cow +moos""", + """dog +woofs +and +barks""", +] + + +def dastardly_default_value( + cow: String = json.loads( + """this +is +quite +the +dastadardly +value!""" + ), + **kwargs, +): + pass + + +print( + f""" + This {animal} + moos and barks +{animal} say +""" +) +msg = f"""The arguments {bad_arguments} were passed in. +Please use `--build-option` instead, +`--global-option` is reserved to flags like `--verbose` or `--quiet`. +""" + +this_will_become_one_line = "a" "b" "c" + +this_will_stay_on_three_lines = ( + "a" # comment + "b" + "c" +) + +this_will_also_become_one_line = ( # comment + "a" "b" "c" +) +``` + +## Black Output + +```python +"""cow +say""", +call( + 3, + "dogsay", + textwrap.dedent("""dove + coo""" % "cowabunga"), +) +call( + 3, + "dogsay", + textwrap.dedent("""dove +coo""" % "cowabunga"), +) +call( + 3, + textwrap.dedent("""cow + moo""" % "cowabunga"), + "dogsay", +) +call( + 3, + "dogsay", + textwrap.dedent("""crow + caw""" % "cowabunga"), +) +call( + 3, + textwrap.dedent("""cat + meow""" % "cowabunga"), + {"dog", "say"}, +) +call( + 3, + {"dog", "say"}, + textwrap.dedent("""horse + neigh""" % "cowabunga"), +) +call( + 3, + {"dog", "say"}, + textwrap.dedent("""pig + oink""" % "cowabunga"), +) +textwrap.dedent("""A one-line triple-quoted string.""") +textwrap.dedent("""A two-line triple-quoted string +since it goes to the next line.""") +textwrap.dedent("""A three-line triple-quoted string +that not only goes to the next line +but also goes one line beyond.""") +textwrap.dedent("""\ + A triple-quoted string + actually leveraging the textwrap.dedent functionality + that ends in a trailing newline, + representing e.g. file contents. +""") +path.write_text(textwrap.dedent("""\ + A triple-quoted string + actually leveraging the textwrap.dedent functionality + that ends in a trailing newline, + representing e.g. file contents. +""")) +path.write_text(textwrap.dedent("""\ + A triple-quoted string + actually leveraging the textwrap.dedent functionality + that ends in a trailing newline, + representing e.g. {config_filename} file contents. +""".format("config_filename", config_filename))) +# Another use case +data = yaml.load("""\ +a: 1 +b: 2 +""") +data = yaml.load( + """\ +a: 1 +b: 2 +""", +) +data = yaml.load("""\ + a: 1 + b: 2 +""") + +MULTILINE = """ +foo +""".replace("\n", "") +generated_readme = lambda project_name: """ +{} + + +""".strip().format(project_name) +parser.usage += """ +Custom extra help summary. + +Extra test: +- with +- bullets +""" + + +def get_stuff(cr, value): + # original + cr.execute( + """ + SELECT whatever + FROM some_table t + WHERE id = %s + """, + [value], + ) + return cr.fetchone() + + +def get_stuff(cr, value): + # preferred + cr.execute( + """ + SELECT whatever + FROM some_table t + WHERE id = %s + """, + [value], + ) + return cr.fetchone() + + +call( + arg1, + arg2, + """ +short +""", + arg3=True, +) +test_vectors = [ + "one-liner\n", + "two\nliner\n", + """expressed +as a three line +mulitline string""", +] + +_wat = re.compile( + r""" + regex + """, + re.MULTILINE | re.VERBOSE, +) +dis_c_instance_method = """\ +%3d 0 LOAD_FAST 1 (x) + 2 LOAD_CONST 1 (1) + 4 COMPARE_OP 2 (==) + 6 LOAD_FAST 0 (self) + 8 STORE_ATTR 0 (x) + 10 LOAD_CONST 0 (None) + 12 RETURN_VALUE +""" % (_C.__init__.__code__.co_firstlineno + 1,) +path.write_text(textwrap.dedent("""\ + A triple-quoted string + actually {verb} the textwrap.dedent functionality + that ends in a trailing newline, + representing e.g. {file_type} file contents. +""".format(verb="using", file_type="test"))) +{"""cow +moos"""} +["""cow +moos"""] +[ + """cow +moos""", + """dog +woofs +and +barks""", +] + + +def dastardly_default_value( + cow: String = json.loads("""this +is +quite +the +dastadardly +value!"""), + **kwargs, +): + pass + + +print(f""" + This {animal} + moos and barks +{animal} say +""") +msg = f"""The arguments {bad_arguments} were passed in. +Please use `--build-option` instead, +`--global-option` is reserved to flags like `--verbose` or `--quiet`. +""" + +this_will_become_one_line = "abc" + +this_will_stay_on_three_lines = ( + "a" # comment + "b" + "c" +) + +this_will_also_become_one_line = "abc" # comment +``` + + diff --git a/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__preview_no_blank_line_before_docstring.py.snap b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__preview_no_blank_line_before_docstring.py.snap new file mode 100644 index 0000000000..bd93e24292 --- /dev/null +++ b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__preview_no_blank_line_before_docstring.py.snap @@ -0,0 +1,134 @@ +--- +source: crates/ruff_python_formatter/tests/fixtures.rs +input_file: crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_no_blank_line_before_docstring.py +--- +## Input + +```python +def line_before_docstring(): + + """Please move me up""" + + +class LineBeforeDocstring: + + """Please move me up""" + + +class EvenIfThereIsAMethodAfter: + + """I'm the docstring""" + def method(self): + pass + + +class TwoLinesBeforeDocstring: + + + """I want to be treated the same as if I were closer""" + + +class MultilineDocstringsAsWell: + + """I'm so far + + and on so many lines... + """ +``` + +## Black Differences + +```diff +--- Black ++++ Ruff +@@ -3,10 +3,12 @@ + + + class LineBeforeDocstring: ++ + """Please move me up""" + + + class EvenIfThereIsAMethodAfter: ++ + """I'm the docstring""" + + def method(self): +@@ -14,10 +16,12 @@ + + + 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... +``` + +## 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... + """ +``` + +## 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... + """ +``` + + diff --git a/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__preview_pattern_matching_trailing_comma.py.snap b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__preview_pattern_matching_trailing_comma.py.snap new file mode 100644 index 0000000000..257d3e63f6 --- /dev/null +++ b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__preview_pattern_matching_trailing_comma.py.snap @@ -0,0 +1,104 @@ +--- +source: crates/ruff_python_formatter/tests/fixtures.rs +input_file: crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_pattern_matching_trailing_comma.py +--- +## Input + +```python +match maybe, multiple: + case perhaps, 5: + pass + case perhaps, 6,: + pass + + +match more := (than, one), indeed,: + case _, (5, 6): + pass + case [[5], (6)], [7],: + pass + case _: + pass +``` + +## Black Differences + +```diff +--- Black ++++ Ruff +@@ -8,13 +8,16 @@ + pass + + +-match more := (than, one), indeed,: ++match ( ++ more := (than, one), ++ indeed, ++): + case _, (5, 6): + pass +- case ( ++ case [ + [[5], (6)], + [7], +- ): ++ ]: + pass + case _: + pass +``` + +## Ruff Output + +```python +match maybe, multiple: + case perhaps, 5: + pass + case ( + perhaps, + 6, + ): + pass + + +match ( + more := (than, one), + indeed, +): + case _, (5, 6): + pass + case [ + [[5], (6)], + [7], + ]: + pass + case _: + pass +``` + +## Black Output + +```python +match maybe, multiple: + case perhaps, 5: + pass + case ( + perhaps, + 6, + ): + pass + + +match more := (than, one), indeed,: + case _, (5, 6): + pass + case ( + [[5], (6)], + [7], + ): + pass + case _: + pass +``` + + diff --git a/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__preview_pep_572.py.snap b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__preview_pep_572.py.snap new file mode 100644 index 0000000000..03f27f16c2 --- /dev/null +++ b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__preview_pep_572.py.snap @@ -0,0 +1,38 @@ +--- +source: crates/ruff_python_formatter/tests/fixtures.rs +input_file: crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_pep_572.py +--- +## Input + +```python +x[(a:=0):] +x[:(a:=0)] +``` + +## Black Differences + +```diff +--- Black ++++ Ruff +@@ -1,2 +1,2 @@ +-x[(a := 0):] +-x[:(a := 0)] ++x[(a := 0) :] ++x[: (a := 0)] +``` + +## Ruff Output + +```python +x[(a := 0) :] +x[: (a := 0)] +``` + +## Black Output + +```python +x[(a := 0):] +x[:(a := 0)] +``` + + diff --git a/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__preview_percent_precedence.py.snap b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__preview_percent_precedence.py.snap new file mode 100644 index 0000000000..01f401e99a --- /dev/null +++ b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__preview_percent_precedence.py.snap @@ -0,0 +1,99 @@ +--- +source: crates/ruff_python_formatter/tests/fixtures.rs +input_file: crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_percent_precedence.py +--- +## Input + +```python +("" % a) ** 2 +("" % a)[0] +("" % a)() +("" % a).b + +2 * ("" % a) +2 @ ("" % a) +2 / ("" % a) +2 // ("" % a) +2 % ("" % a) ++("" % a) +b + ("" % a) +-("" % a) +b - ("" % a) +b + -("" % a) +~("" % a) +2 ** ("" % a) +await ("" % a) +b[("" % a)] +b(("" % a)) +``` + +## Black Differences + +```diff +--- Black ++++ Ruff +@@ -9,9 +9,9 @@ + 2 // ("" % a) + 2 % ("" % a) + +("" % a) +-b + "" % a ++b + ("" % a) + -("" % a) +-b - "" % a ++b - ("" % a) + b + -("" % a) + ~("" % a) + 2 ** ("" % a) +``` + +## Ruff Output + +```python +("" % a) ** 2 +("" % a)[0] +("" % a)() +("" % a).b + +2 * ("" % a) +2 @ ("" % a) +2 / ("" % a) +2 // ("" % a) +2 % ("" % a) ++("" % a) +b + ("" % a) +-("" % a) +b - ("" % a) +b + -("" % a) +~("" % a) +2 ** ("" % a) +await ("" % a) +b[("" % a)] +b(("" % a)) +``` + +## Black Output + +```python +("" % a) ** 2 +("" % a)[0] +("" % a)() +("" % a).b + +2 * ("" % a) +2 @ ("" % a) +2 / ("" % a) +2 // ("" % a) +2 % ("" % a) ++("" % a) +b + "" % a +-("" % a) +b - "" % a +b + -("" % a) +~("" % a) +2 ** ("" % a) +await ("" % a) +b[("" % a)] +b(("" % a)) +``` + + diff --git a/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__preview_power_op_spacing.py.snap b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__preview_power_op_spacing.py.snap new file mode 100644 index 0000000000..f7cb0914c0 --- /dev/null +++ b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__preview_power_op_spacing.py.snap @@ -0,0 +1,354 @@ +--- +source: crates/ruff_python_formatter/tests/fixtures.rs +input_file: crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_power_op_spacing.py +--- +## Input + +```python +a = 1**1**1**1**1**1**1**1**1**1**1**1**1**1**1**1**1**1**1**1**1**1**1**1**1**1**1**1 +b = 1**1**1**1**1**1**1**1**1**1**1**1**1**1**1**1**1**1**1**1**1**1**1**1**1**1**1**1**1 +c = 1 ** 1 ** 1 ** 1 ** 1 ** 1 ** 1 ** 1 ** 1 ** 1 ** 1 ** 1 ** 1 ** 1 ** 1 ** 1 ** 1 ** 1 ** 1 ** 1 ** 1 ** 1 ** 1 ** 1 ** 1 ** 1 ** 1 ** 1 +d = 1**1 ** 1**1 ** 1**1 ** 1**1 ** 1**1**1 ** 1 ** 1**1 ** 1**1**1**1**1 ** 1 ** 1**1**1 **1**1** 1 ** 1 ** 1 +e = 𨉟**𨉟**𨉟**𨉟**𨉟**𨉟**𨉟**𨉟**𨉟**𨉟**𨉟**𨉟**𨉟**𨉟**𨉟**𨉟**𨉟**𨉟**𨉟**𨉟**𨉟 +f = 𨉟**𨉟**𨉟**𨉟**𨉟**𨉟**𨉟**𨉟**𨉟**𨉟**𨉟**𨉟**𨉟**𨉟**𨉟**𨉟**𨉟**𨉟**𨉟**𨉟**𨉟**𨉟 + +a = 1.0**1.0**1.0**1.0**1.0**1.0**1.0**1.0**1.0**1.0**1.0**1.0**1.0**1.0**1.0**1.0**1.0 +b = 1.0**1.0**1.0**1.0**1.0**1.0**1.0**1.0**1.0**1.0**1.0**1.0**1.0**1.0**1.0**1.0**1.0**1.0 +c = 1.0 ** 1.0 ** 1.0 ** 1.0 ** 1.0 ** 1.0 ** 1.0 ** 1.0 ** 1.0 ** 1.0 ** 1.0 ** 1.0 ** 1.0 ** 1.0 ** 1.0 ** 1.0 ** 1.0 +d = 1.0**1.0 ** 1.0**1.0 ** 1.0**1.0 ** 1.0**1.0 ** 1.0**1.0**1.0 ** 1.0 ** 1.0**1.0 ** 1.0**1.0**1.0 +``` + +## Black Differences + +```diff +--- Black ++++ Ruff +@@ -1,83 +1,83 @@ + a = 1**1**1**1**1**1**1**1**1**1**1**1**1**1**1**1**1**1**1**1**1**1**1**1**1**1**1**1 + b = ( + 1 +- ** 1 +- ** 1 +- ** 1 +- ** 1 +- ** 1 +- ** 1 +- ** 1 +- ** 1 +- ** 1 +- ** 1 +- ** 1 +- ** 1 +- ** 1 +- ** 1 +- ** 1 +- ** 1 +- ** 1 +- ** 1 +- ** 1 +- ** 1 +- ** 1 +- ** 1 +- ** 1 +- ** 1 +- ** 1 +- ** 1 +- ** 1 +- ** 1 ++ **1 ++ **1 ++ **1 ++ **1 ++ **1 ++ **1 ++ **1 ++ **1 ++ **1 ++ **1 ++ **1 ++ **1 ++ **1 ++ **1 ++ **1 ++ **1 ++ **1 ++ **1 ++ **1 ++ **1 ++ **1 ++ **1 ++ **1 ++ **1 ++ **1 ++ **1 ++ **1 ++ **1 + ) + c = 1**1**1**1**1**1**1**1**1**1**1**1**1**1**1**1**1**1**1**1**1**1**1**1**1**1**1**1 + d = 1**1**1**1**1**1**1**1**1**1**1**1**1**1**1**1**1**1**1**1**1**1**1**1**1**1**1**1 + e = 𨉟**𨉟**𨉟**𨉟**𨉟**𨉟**𨉟**𨉟**𨉟**𨉟**𨉟**𨉟**𨉟**𨉟**𨉟**𨉟**𨉟**𨉟**𨉟**𨉟**𨉟 + f = ( + 𨉟 +- ** 𨉟 +- ** 𨉟 +- ** 𨉟 +- ** 𨉟 +- ** 𨉟 +- ** 𨉟 +- ** 𨉟 +- ** 𨉟 +- ** 𨉟 +- ** 𨉟 +- ** 𨉟 +- ** 𨉟 +- ** 𨉟 +- ** 𨉟 +- ** 𨉟 +- ** 𨉟 +- ** 𨉟 +- ** 𨉟 +- ** 𨉟 +- ** 𨉟 +- ** 𨉟 ++ **𨉟 ++ **𨉟 ++ **𨉟 ++ **𨉟 ++ **𨉟 ++ **𨉟 ++ **𨉟 ++ **𨉟 ++ **𨉟 ++ **𨉟 ++ **𨉟 ++ **𨉟 ++ **𨉟 ++ **𨉟 ++ **𨉟 ++ **𨉟 ++ **𨉟 ++ **𨉟 ++ **𨉟 ++ **𨉟 ++ **𨉟 + ) + + a = 1.0**1.0**1.0**1.0**1.0**1.0**1.0**1.0**1.0**1.0**1.0**1.0**1.0**1.0**1.0**1.0**1.0 + b = ( + 1.0 +- ** 1.0 +- ** 1.0 +- ** 1.0 +- ** 1.0 +- ** 1.0 +- ** 1.0 +- ** 1.0 +- ** 1.0 +- ** 1.0 +- ** 1.0 +- ** 1.0 +- ** 1.0 +- ** 1.0 +- ** 1.0 +- ** 1.0 +- ** 1.0 +- ** 1.0 ++ **1.0 ++ **1.0 ++ **1.0 ++ **1.0 ++ **1.0 ++ **1.0 ++ **1.0 ++ **1.0 ++ **1.0 ++ **1.0 ++ **1.0 ++ **1.0 ++ **1.0 ++ **1.0 ++ **1.0 ++ **1.0 ++ **1.0 + ) + c = 1.0**1.0**1.0**1.0**1.0**1.0**1.0**1.0**1.0**1.0**1.0**1.0**1.0**1.0**1.0**1.0**1.0 + d = 1.0**1.0**1.0**1.0**1.0**1.0**1.0**1.0**1.0**1.0**1.0**1.0**1.0**1.0**1.0**1.0**1.0 +``` + +## Ruff Output + +```python +a = 1**1**1**1**1**1**1**1**1**1**1**1**1**1**1**1**1**1**1**1**1**1**1**1**1**1**1**1 +b = ( + 1 + **1 + **1 + **1 + **1 + **1 + **1 + **1 + **1 + **1 + **1 + **1 + **1 + **1 + **1 + **1 + **1 + **1 + **1 + **1 + **1 + **1 + **1 + **1 + **1 + **1 + **1 + **1 + **1 +) +c = 1**1**1**1**1**1**1**1**1**1**1**1**1**1**1**1**1**1**1**1**1**1**1**1**1**1**1**1 +d = 1**1**1**1**1**1**1**1**1**1**1**1**1**1**1**1**1**1**1**1**1**1**1**1**1**1**1**1 +e = 𨉟**𨉟**𨉟**𨉟**𨉟**𨉟**𨉟**𨉟**𨉟**𨉟**𨉟**𨉟**𨉟**𨉟**𨉟**𨉟**𨉟**𨉟**𨉟**𨉟**𨉟 +f = ( + 𨉟 + **𨉟 + **𨉟 + **𨉟 + **𨉟 + **𨉟 + **𨉟 + **𨉟 + **𨉟 + **𨉟 + **𨉟 + **𨉟 + **𨉟 + **𨉟 + **𨉟 + **𨉟 + **𨉟 + **𨉟 + **𨉟 + **𨉟 + **𨉟 + **𨉟 +) + +a = 1.0**1.0**1.0**1.0**1.0**1.0**1.0**1.0**1.0**1.0**1.0**1.0**1.0**1.0**1.0**1.0**1.0 +b = ( + 1.0 + **1.0 + **1.0 + **1.0 + **1.0 + **1.0 + **1.0 + **1.0 + **1.0 + **1.0 + **1.0 + **1.0 + **1.0 + **1.0 + **1.0 + **1.0 + **1.0 + **1.0 +) +c = 1.0**1.0**1.0**1.0**1.0**1.0**1.0**1.0**1.0**1.0**1.0**1.0**1.0**1.0**1.0**1.0**1.0 +d = 1.0**1.0**1.0**1.0**1.0**1.0**1.0**1.0**1.0**1.0**1.0**1.0**1.0**1.0**1.0**1.0**1.0 +``` + +## Black Output + +```python +a = 1**1**1**1**1**1**1**1**1**1**1**1**1**1**1**1**1**1**1**1**1**1**1**1**1**1**1**1 +b = ( + 1 + ** 1 + ** 1 + ** 1 + ** 1 + ** 1 + ** 1 + ** 1 + ** 1 + ** 1 + ** 1 + ** 1 + ** 1 + ** 1 + ** 1 + ** 1 + ** 1 + ** 1 + ** 1 + ** 1 + ** 1 + ** 1 + ** 1 + ** 1 + ** 1 + ** 1 + ** 1 + ** 1 + ** 1 +) +c = 1**1**1**1**1**1**1**1**1**1**1**1**1**1**1**1**1**1**1**1**1**1**1**1**1**1**1**1 +d = 1**1**1**1**1**1**1**1**1**1**1**1**1**1**1**1**1**1**1**1**1**1**1**1**1**1**1**1 +e = 𨉟**𨉟**𨉟**𨉟**𨉟**𨉟**𨉟**𨉟**𨉟**𨉟**𨉟**𨉟**𨉟**𨉟**𨉟**𨉟**𨉟**𨉟**𨉟**𨉟**𨉟 +f = ( + 𨉟 + ** 𨉟 + ** 𨉟 + ** 𨉟 + ** 𨉟 + ** 𨉟 + ** 𨉟 + ** 𨉟 + ** 𨉟 + ** 𨉟 + ** 𨉟 + ** 𨉟 + ** 𨉟 + ** 𨉟 + ** 𨉟 + ** 𨉟 + ** 𨉟 + ** 𨉟 + ** 𨉟 + ** 𨉟 + ** 𨉟 + ** 𨉟 +) + +a = 1.0**1.0**1.0**1.0**1.0**1.0**1.0**1.0**1.0**1.0**1.0**1.0**1.0**1.0**1.0**1.0**1.0 +b = ( + 1.0 + ** 1.0 + ** 1.0 + ** 1.0 + ** 1.0 + ** 1.0 + ** 1.0 + ** 1.0 + ** 1.0 + ** 1.0 + ** 1.0 + ** 1.0 + ** 1.0 + ** 1.0 + ** 1.0 + ** 1.0 + ** 1.0 + ** 1.0 +) +c = 1.0**1.0**1.0**1.0**1.0**1.0**1.0**1.0**1.0**1.0**1.0**1.0**1.0**1.0**1.0**1.0**1.0 +d = 1.0**1.0**1.0**1.0**1.0**1.0**1.0**1.0**1.0**1.0**1.0**1.0**1.0**1.0**1.0**1.0**1.0 +``` + + diff --git a/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__preview_prefer_rhs_split.py.snap b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__preview_prefer_rhs_split.py.snap new file mode 100644 index 0000000000..18b9fa9a06 --- /dev/null +++ b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__preview_prefer_rhs_split.py.snap @@ -0,0 +1,457 @@ +--- +source: crates/ruff_python_formatter/tests/fixtures.rs +input_file: crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_prefer_rhs_split.py +--- +## Input + +```python +first_item, second_item = ( + some_looooooooong_module.some_looooooooooooooong_function_name( + first_argument, second_argument, third_argument + ) +) + +some_dict["with_a_long_key"] = ( + some_looooooooong_module.some_looooooooooooooong_function_name( + first_argument, second_argument, third_argument + ) +) + +# Make sure it works when the RHS only has one pair of (optional) parens. +first_item, second_item = ( + some_looooooooong_module.SomeClass.some_looooooooooooooong_variable_name +) + +some_dict["with_a_long_key"] = ( + some_looooooooong_module.SomeClass.some_looooooooooooooong_variable_name +) + +# Make sure chaining assignments work. +first_item, second_item, third_item, forth_item = m["everything"] = ( + some_looooooooong_module.some_looooooooooooooong_function_name( + first_argument, second_argument, third_argument + ) +) + +# Make sure when the RHS's first split at the non-optional paren fits, +# we split there instead of the outer RHS optional paren. +first_item, second_item = some_looooooooong_module.some_loooooog_function_name( + first_argument, second_argument, third_argument +) + +( + first_item, + second_item, + third_item, + forth_item, + fifth_item, + last_item_very_loooooong, +) = some_looooooooong_module.some_looooooooooooooong_function_name( + first_argument, second_argument, third_argument +) + +( + first_item, + second_item, + third_item, + forth_item, + fifth_item, + last_item_very_loooooong, +) = everything = some_looooong_function_name( + first_argument, second_argument, third_argument +) + + +# Make sure unsplittable type ignore won't be moved. +some_kind_of_table[some_key] = util.some_function( # type: ignore # noqa: E501 + some_arg +).intersection(pk_cols) + +some_kind_of_table[ + some_key +] = lambda obj: obj.some_long_named_method() # type: ignore # noqa: E501 + +some_kind_of_table[ + some_key # type: ignore # noqa: E501 +] = lambda obj: obj.some_long_named_method() + + +# Make when when the left side of assignment plus the opening paren "... = (" is +# exactly line length limit + 1, it won't be split like that. +xxxxxxxxx_yyy_zzzzzzzz[ + xx.xxxxxx(x_yyy_zzzzzz.xxxxx[0]), x_yyy_zzzzzz.xxxxxx(xxxx=1) +] = 1 + + +# Right side of assignment contains un-nested pairs of inner parens. +some_kind_of_instance.some_kind_of_map[a_key] = ( + isinstance(some_var, SomeClass) + and table.something_and_something != table.something_else +) or ( + isinstance(some_other_var, BaseClass) and table.something != table.some_other_thing +) + +# Multiple targets +a = b = ( + ccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc +) + +a = b = c = d = e = f = g = ( + hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh +) = i = j = ( + kkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkk +) + +a = ( + bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb +) = c + +a = ( + bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb +) = ( + cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc +) = ddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd +``` + +## Black Differences + +```diff +--- Black ++++ Ruff +@@ -1,29 +1,31 @@ +-first_item, second_item = ( +- some_looooooooong_module.some_looooooooooooooong_function_name( +- first_argument, second_argument, third_argument +- ) ++( ++ first_item, ++ second_item, ++) = some_looooooooong_module.some_looooooooooooooong_function_name( ++ first_argument, second_argument, third_argument + ) + +-some_dict["with_a_long_key"] = ( +- some_looooooooong_module.some_looooooooooooooong_function_name( +- first_argument, second_argument, third_argument +- ) ++some_dict[ ++ "with_a_long_key" ++] = some_looooooooong_module.some_looooooooooooooong_function_name( ++ first_argument, second_argument, third_argument + ) + + # Make sure it works when the RHS only has one pair of (optional) parens. +-first_item, second_item = ( +- some_looooooooong_module.SomeClass.some_looooooooooooooong_variable_name +-) ++( ++ first_item, ++ second_item, ++) = some_looooooooong_module.SomeClass.some_looooooooooooooong_variable_name + +-some_dict["with_a_long_key"] = ( +- some_looooooooong_module.SomeClass.some_looooooooooooooong_variable_name +-) ++some_dict[ ++ "with_a_long_key" ++] = some_looooooooong_module.SomeClass.some_looooooooooooooong_variable_name + + # Make sure chaining assignments work. +-first_item, second_item, third_item, forth_item = m["everything"] = ( +- some_looooooooong_module.some_looooooooooooooong_function_name( +- first_argument, second_argument, third_argument +- ) ++first_item, second_item, third_item, forth_item = m[ ++ "everything" ++] = some_looooooooong_module.some_looooooooooooooong_function_name( ++ first_argument, second_argument, third_argument + ) + + # Make sure when the RHS's first split at the non-optional paren fits, +@@ -60,9 +62,7 @@ + some_arg + ).intersection(pk_cols) + +-some_kind_of_table[ +- some_key +-] = lambda obj: obj.some_long_named_method() # type: ignore # noqa: E501 ++some_kind_of_table[some_key] = lambda obj: obj.some_long_named_method() # type: ignore # noqa: E501 + + some_kind_of_table[ + some_key # type: ignore # noqa: E501 +@@ -85,15 +85,29 @@ + ) + + # Multiple targets +-a = b = ( +- ccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc +-) ++a = ( ++ b ++) = ccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc + +-a = b = c = d = e = f = g = ( ++a = ( ++ b ++) = ( ++ c ++) = ( ++ d ++) = ( ++ e ++) = ( ++ f ++) = ( ++ g ++) = ( + hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh +-) = i = j = ( +- kkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkk +-) ++) = ( ++ i ++) = ( ++ j ++) = kkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkk + + a = ( + bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb +``` + +## Ruff Output + +```python +( + first_item, + second_item, +) = some_looooooooong_module.some_looooooooooooooong_function_name( + first_argument, second_argument, third_argument +) + +some_dict[ + "with_a_long_key" +] = some_looooooooong_module.some_looooooooooooooong_function_name( + first_argument, second_argument, third_argument +) + +# Make sure it works when the RHS only has one pair of (optional) parens. +( + first_item, + second_item, +) = some_looooooooong_module.SomeClass.some_looooooooooooooong_variable_name + +some_dict[ + "with_a_long_key" +] = some_looooooooong_module.SomeClass.some_looooooooooooooong_variable_name + +# Make sure chaining assignments work. +first_item, second_item, third_item, forth_item = m[ + "everything" +] = some_looooooooong_module.some_looooooooooooooong_function_name( + first_argument, second_argument, third_argument +) + +# Make sure when the RHS's first split at the non-optional paren fits, +# we split there instead of the outer RHS optional paren. +first_item, second_item = some_looooooooong_module.some_loooooog_function_name( + first_argument, second_argument, third_argument +) + +( + first_item, + second_item, + third_item, + forth_item, + fifth_item, + last_item_very_loooooong, +) = some_looooooooong_module.some_looooooooooooooong_function_name( + first_argument, second_argument, third_argument +) + +( + first_item, + second_item, + third_item, + forth_item, + fifth_item, + last_item_very_loooooong, +) = everything = some_looooong_function_name( + first_argument, second_argument, third_argument +) + + +# Make sure unsplittable type ignore won't be moved. +some_kind_of_table[some_key] = util.some_function( # type: ignore # noqa: E501 + some_arg +).intersection(pk_cols) + +some_kind_of_table[some_key] = lambda obj: obj.some_long_named_method() # type: ignore # noqa: E501 + +some_kind_of_table[ + some_key # type: ignore # noqa: E501 +] = lambda obj: obj.some_long_named_method() + + +# Make when when the left side of assignment plus the opening paren "... = (" is +# exactly line length limit + 1, it won't be split like that. +xxxxxxxxx_yyy_zzzzzzzz[ + xx.xxxxxx(x_yyy_zzzzzz.xxxxx[0]), x_yyy_zzzzzz.xxxxxx(xxxx=1) +] = 1 + + +# Right side of assignment contains un-nested pairs of inner parens. +some_kind_of_instance.some_kind_of_map[a_key] = ( + isinstance(some_var, SomeClass) + and table.something_and_something != table.something_else +) or ( + isinstance(some_other_var, BaseClass) and table.something != table.some_other_thing +) + +# Multiple targets +a = ( + b +) = ccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc + +a = ( + b +) = ( + c +) = ( + d +) = ( + e +) = ( + f +) = ( + g +) = ( + hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh +) = ( + i +) = ( + j +) = kkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkk + +a = ( + bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb +) = c + +a = ( + bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb +) = ( + cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc +) = ddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd +``` + +## Black Output + +```python +first_item, second_item = ( + some_looooooooong_module.some_looooooooooooooong_function_name( + first_argument, second_argument, third_argument + ) +) + +some_dict["with_a_long_key"] = ( + some_looooooooong_module.some_looooooooooooooong_function_name( + first_argument, second_argument, third_argument + ) +) + +# Make sure it works when the RHS only has one pair of (optional) parens. +first_item, second_item = ( + some_looooooooong_module.SomeClass.some_looooooooooooooong_variable_name +) + +some_dict["with_a_long_key"] = ( + some_looooooooong_module.SomeClass.some_looooooooooooooong_variable_name +) + +# Make sure chaining assignments work. +first_item, second_item, third_item, forth_item = m["everything"] = ( + some_looooooooong_module.some_looooooooooooooong_function_name( + first_argument, second_argument, third_argument + ) +) + +# Make sure when the RHS's first split at the non-optional paren fits, +# we split there instead of the outer RHS optional paren. +first_item, second_item = some_looooooooong_module.some_loooooog_function_name( + first_argument, second_argument, third_argument +) + +( + first_item, + second_item, + third_item, + forth_item, + fifth_item, + last_item_very_loooooong, +) = some_looooooooong_module.some_looooooooooooooong_function_name( + first_argument, second_argument, third_argument +) + +( + first_item, + second_item, + third_item, + forth_item, + fifth_item, + last_item_very_loooooong, +) = everything = some_looooong_function_name( + first_argument, second_argument, third_argument +) + + +# Make sure unsplittable type ignore won't be moved. +some_kind_of_table[some_key] = util.some_function( # type: ignore # noqa: E501 + some_arg +).intersection(pk_cols) + +some_kind_of_table[ + some_key +] = lambda obj: obj.some_long_named_method() # type: ignore # noqa: E501 + +some_kind_of_table[ + some_key # type: ignore # noqa: E501 +] = lambda obj: obj.some_long_named_method() + + +# Make when when the left side of assignment plus the opening paren "... = (" is +# exactly line length limit + 1, it won't be split like that. +xxxxxxxxx_yyy_zzzzzzzz[ + xx.xxxxxx(x_yyy_zzzzzz.xxxxx[0]), x_yyy_zzzzzz.xxxxxx(xxxx=1) +] = 1 + + +# Right side of assignment contains un-nested pairs of inner parens. +some_kind_of_instance.some_kind_of_map[a_key] = ( + isinstance(some_var, SomeClass) + and table.something_and_something != table.something_else +) or ( + isinstance(some_other_var, BaseClass) and table.something != table.some_other_thing +) + +# Multiple targets +a = b = ( + ccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc +) + +a = b = c = d = e = f = g = ( + hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh +) = i = j = ( + kkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkk +) + +a = ( + bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb +) = c + +a = ( + bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb +) = ( + cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc +) = ddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd +``` + + diff --git a/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__preview_return_annotation_brackets_string.py.snap b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__preview_return_annotation_brackets_string.py.snap new file mode 100644 index 0000000000..268fcf0eb8 --- /dev/null +++ b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__preview_return_annotation_brackets_string.py.snap @@ -0,0 +1,75 @@ +--- +source: crates/ruff_python_formatter/tests/fixtures.rs +input_file: crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_return_annotation_brackets_string.py +--- +## Input + +```python +# Long string example +def frobnicate() -> "ThisIsTrulyUnreasonablyExtremelyLongClassName | list[ThisIsTrulyUnreasonablyExtremelyLongClassName]": + pass + +# splitting the string breaks if there's any parameters +def frobnicate(a) -> "ThisIsTrulyUnreasonablyExtremelyLongClassName | list[ThisIsTrulyUnreasonablyExtremelyLongClassName]": + pass +``` + +## Black Differences + +```diff +--- Black ++++ Ruff +@@ -1,13 +1,12 @@ + # Long string example + def frobnicate() -> ( +- "ThisIsTrulyUnreasonablyExtremelyLongClassName |" +- " list[ThisIsTrulyUnreasonablyExtremelyLongClassName]" ++ "ThisIsTrulyUnreasonablyExtremelyLongClassName | list[ThisIsTrulyUnreasonablyExtremelyLongClassName]" + ): + pass + + + # splitting the string breaks if there's any parameters + def frobnicate( +- a, ++ a + ) -> "ThisIsTrulyUnreasonablyExtremelyLongClassName | list[ThisIsTrulyUnreasonablyExtremelyLongClassName]": + pass +``` + +## Ruff Output + +```python +# Long string example +def frobnicate() -> ( + "ThisIsTrulyUnreasonablyExtremelyLongClassName | list[ThisIsTrulyUnreasonablyExtremelyLongClassName]" +): + pass + + +# splitting the string breaks if there's any parameters +def frobnicate( + a +) -> "ThisIsTrulyUnreasonablyExtremelyLongClassName | list[ThisIsTrulyUnreasonablyExtremelyLongClassName]": + pass +``` + +## Black Output + +```python +# Long string example +def frobnicate() -> ( + "ThisIsTrulyUnreasonablyExtremelyLongClassName |" + " list[ThisIsTrulyUnreasonablyExtremelyLongClassName]" +): + pass + + +# splitting the string breaks if there's any parameters +def frobnicate( + a, +) -> "ThisIsTrulyUnreasonablyExtremelyLongClassName | list[ThisIsTrulyUnreasonablyExtremelyLongClassName]": + pass +``` + + diff --git a/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__preview_single_line_format_skip_with_multiple_comments.py.snap b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__preview_single_line_format_skip_with_multiple_comments.py.snap new file mode 100644 index 0000000000..78dc5bad0d --- /dev/null +++ b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__preview_single_line_format_skip_with_multiple_comments.py.snap @@ -0,0 +1,64 @@ +--- +source: crates/ruff_python_formatter/tests/fixtures.rs +input_file: crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_single_line_format_skip_with_multiple_comments.py +--- +## Input + +```python +foo = 123 # fmt: skip # noqa: E501 # pylint +bar = ( + 123 , + ( 1 + 5 ) # pylint # fmt:skip +) +baz = "a" + "b" # pylint; fmt: skip; noqa: E501 +skip_will_not_work = "a" + "b" # pylint fmt:skip +skip_will_not_work2 = "a" + "b" # some text; fmt:skip happens to be part of it +``` + +## Black Differences + +```diff +--- Black ++++ Ruff +@@ -1,8 +1,8 @@ +-foo = 123 # fmt: skip # noqa: E501 # pylint ++foo = 123 # fmt: skip # noqa: E501 # pylint + bar = ( +- 123 , +- ( 1 + 5 ) # pylint # fmt:skip ++ 123, ++ (1 + 5), # pylint # fmt:skip + ) +-baz = "a" + "b" # pylint; fmt: skip; noqa: E501 ++baz = "a" + "b" # pylint; fmt: skip; noqa: E501 + skip_will_not_work = "a" + "b" # pylint fmt:skip + skip_will_not_work2 = "a" + "b" # some text; fmt:skip happens to be part of it +``` + +## Ruff Output + +```python +foo = 123 # fmt: skip # noqa: E501 # pylint +bar = ( + 123, + (1 + 5), # pylint # fmt:skip +) +baz = "a" + "b" # pylint; fmt: skip; noqa: E501 +skip_will_not_work = "a" + "b" # pylint fmt:skip +skip_will_not_work2 = "a" + "b" # some text; fmt:skip happens to be part of it +``` + +## Black Output + +```python +foo = 123 # fmt: skip # noqa: E501 # pylint +bar = ( + 123 , + ( 1 + 5 ) # pylint # fmt:skip +) +baz = "a" + "b" # pylint; fmt: skip; noqa: E501 +skip_will_not_work = "a" + "b" # pylint fmt:skip +skip_will_not_work2 = "a" + "b" # some text; fmt:skip happens to be part of it +``` + + diff --git a/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__raw_docstring.py.snap b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__raw_docstring.py.snap new file mode 100644 index 0000000000..8b27585265 --- /dev/null +++ b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__raw_docstring.py.snap @@ -0,0 +1,89 @@ +--- +source: crates/ruff_python_formatter/tests/fixtures.rs +input_file: crates/ruff_python_formatter/resources/test/fixtures/black/cases/raw_docstring.py +--- +## Input + +```python +class C: + + r"""Raw""" + +def f(): + + r"""Raw""" + +class SingleQuotes: + + + r'''Raw''' + +class UpperCaseR: + R"""Raw""" +``` + +## Black Differences + +```diff +--- Black ++++ Ruff +@@ -1,4 +1,5 @@ + class C: ++ + r"""Raw""" + + +@@ -7,8 +8,9 @@ + + + class SingleQuotes: +- r'''Raw''' + ++ r"""Raw""" ++ + + class UpperCaseR: + R"""Raw""" +``` + +## Ruff Output + +```python +class C: + + r"""Raw""" + + +def f(): + r"""Raw""" + + +class SingleQuotes: + + r"""Raw""" + + +class UpperCaseR: + R"""Raw""" +``` + +## Black Output + +```python +class C: + r"""Raw""" + + +def f(): + r"""Raw""" + + +class SingleQuotes: + r'''Raw''' + + +class UpperCaseR: + R"""Raw""" +``` + + diff --git a/crates/ruff_python_formatter/tests/snapshots/black_compatibility@simple_cases__remove_await_parens.py.snap b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__remove_await_parens.py.snap similarity index 90% rename from crates/ruff_python_formatter/tests/snapshots/black_compatibility@simple_cases__remove_await_parens.py.snap rename to crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__remove_await_parens.py.snap index 33eabc6fa9..284e85189e 100644 --- a/crates/ruff_python_formatter/tests/snapshots/black_compatibility@simple_cases__remove_await_parens.py.snap +++ b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__remove_await_parens.py.snap @@ -1,6 +1,6 @@ --- source: crates/ruff_python_formatter/tests/fixtures.rs -input_file: crates/ruff_python_formatter/resources/test/fixtures/black/simple_cases/remove_await_parens.py +input_file: crates/ruff_python_formatter/resources/test/fixtures/black/cases/remove_await_parens.py --- ## Input @@ -86,6 +86,15 @@ async def main(): async def main(): await (yield) + +async def main(): + await (a ** b) + await (a[b] ** c) + await (a ** b[c]) + await ((a + b) ** (c + d)) + await (a + b) + await (a[b]) + await (a[b ** c]) ``` ## Black Differences @@ -213,6 +222,16 @@ async def main(): async def main(): await (yield) + + +async def main(): + await (a**b) + await (a[b] ** c) + await (a ** b[c]) + await ((a + b) ** (c + d)) + await (a + b) + await a[b] + await a[b**c] ``` ## Black Output @@ -311,6 +330,16 @@ async def main(): async def main(): await (yield) + + +async def main(): + await (a**b) + await (a[b] ** c) + await (a ** b[c]) + await ((a + b) ** (c + d)) + await (a + b) + await a[b] + await a[b**c] ``` diff --git a/crates/ruff_python_formatter/tests/snapshots/black_compatibility@simple_cases__remove_except_parens.py.snap b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__remove_except_parens.py.snap similarity index 98% rename from crates/ruff_python_formatter/tests/snapshots/black_compatibility@simple_cases__remove_except_parens.py.snap rename to crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__remove_except_parens.py.snap index a0459420e5..55f1e95384 100644 --- a/crates/ruff_python_formatter/tests/snapshots/black_compatibility@simple_cases__remove_except_parens.py.snap +++ b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__remove_except_parens.py.snap @@ -1,6 +1,6 @@ --- source: crates/ruff_python_formatter/tests/fixtures.rs -input_file: crates/ruff_python_formatter/resources/test/fixtures/black/simple_cases/remove_except_parens.py +input_file: crates/ruff_python_formatter/resources/test/fixtures/black/cases/remove_except_parens.py --- ## Input diff --git a/crates/ruff_python_formatter/tests/snapshots/black_compatibility@simple_cases__remove_for_brackets.py.snap b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__remove_for_brackets.py.snap similarity index 98% rename from crates/ruff_python_formatter/tests/snapshots/black_compatibility@simple_cases__remove_for_brackets.py.snap rename to crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__remove_for_brackets.py.snap index 3c2eb7eab6..5b98ee4d7f 100644 --- a/crates/ruff_python_formatter/tests/snapshots/black_compatibility@simple_cases__remove_for_brackets.py.snap +++ b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__remove_for_brackets.py.snap @@ -1,6 +1,6 @@ --- source: crates/ruff_python_formatter/tests/fixtures.rs -input_file: crates/ruff_python_formatter/resources/test/fixtures/black/simple_cases/remove_for_brackets.py +input_file: crates/ruff_python_formatter/resources/test/fixtures/black/cases/remove_for_brackets.py --- ## Input diff --git a/crates/ruff_python_formatter/tests/snapshots/black_compatibility@simple_cases__return_annotation_brackets.py.snap b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__return_annotation_brackets.py.snap similarity index 90% rename from crates/ruff_python_formatter/tests/snapshots/black_compatibility@simple_cases__return_annotation_brackets.py.snap rename to crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__return_annotation_brackets.py.snap index fd67777f13..415daad90c 100644 --- a/crates/ruff_python_formatter/tests/snapshots/black_compatibility@simple_cases__return_annotation_brackets.py.snap +++ b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__return_annotation_brackets.py.snap @@ -1,6 +1,6 @@ --- source: crates/ruff_python_formatter/tests/fixtures.rs -input_file: crates/ruff_python_formatter/resources/test/fixtures/black/simple_cases/return_annotation_brackets.py +input_file: crates/ruff_python_formatter/resources/test/fixtures/black/cases/return_annotation_brackets.py --- ## Input @@ -93,6 +93,11 @@ def foo() -> tuple[loooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo # Magic trailing comma example def foo() -> tuple[int, int, int,]: return 2 + +# Magic trailing comma example, with params +# this is broken - the trailing comma is transferred to the param list. Fixed in preview +def foo(a,b) -> tuple[int, int, int,]: + return 2 ``` ## Black Differences @@ -122,6 +127,17 @@ def foo() -> tuple[int, int, int,]: return 2 * a +@@ -124,5 +132,9 @@ + # this is broken - the trailing comma is transferred to the param list. Fixed in preview + def foo( + a, b +-) -> tuple[int, int, int,]: ++) -> tuple[ ++ int, ++ int, ++ int, ++]: + return 2 ``` ## Ruff Output @@ -255,6 +271,18 @@ def foo() -> ( ] ): return 2 + + +# Magic trailing comma example, with params +# this is broken - the trailing comma is transferred to the param list. Fixed in preview +def foo( + a, b +) -> tuple[ + int, + int, + int, +]: + return 2 ``` ## Black Output @@ -380,6 +408,14 @@ def foo() -> ( ] ): return 2 + + +# Magic trailing comma example, with params +# this is broken - the trailing comma is transferred to the param list. Fixed in preview +def foo( + a, b +) -> tuple[int, int, int,]: + return 2 ``` diff --git a/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__stub.pyi.snap b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__stub.pyi.snap new file mode 100644 index 0000000000..14b894cda7 --- /dev/null +++ b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__stub.pyi.snap @@ -0,0 +1,288 @@ +--- +source: crates/ruff_python_formatter/tests/fixtures.rs +input_file: crates/ruff_python_formatter/resources/test/fixtures/black/cases/stub.pyi +--- +## Input + +```python +X: int + +def f(): ... + + +class D: + ... + + +class C: + ... + +class B: + this_lack_of_newline_should_be_kept: int + def b(self) -> None: ... + + but_this_newline_should_also_be_kept: int + +class A: + attr: int + attr2: str + + def f(self) -> int: + ... + + def g(self) -> str: ... + + + +def g(): + ... + +def h(): ... + +if sys.version_info >= (3, 8): + class E: + def f(self): ... + class F: + + def f(self): ... + class G: ... + class H: ... +else: + class I: ... + class J: ... + def f(): ... + + class K: + def f(self): ... + def f(): ... + +class Nested: + class dirty: ... + class little: ... + class secret: + def who_has_to_know(self): ... + def verse(self): ... + +class Conditional: + def f(self): ... + if sys.version_info >= (3, 8): + def g(self): ... + else: + def g(self): ... + def h(self): ... + def i(self): ... + if sys.version_info >= (3, 8): + def j(self): ... + def k(self): ... + if sys.version_info >= (3, 8): + class A: ... + class B: ... + class C: + def l(self): ... + def m(self): ... +``` + +## Black Differences + +```diff +--- Black ++++ Ruff +@@ -24,31 +24,24 @@ + if sys.version_info >= (3, 8): + class E: + def f(self): ... +- + class F: + def f(self): ... +- + class G: ... + class H: ... +- + else: + class I: ... + class J: ... +- + def f(): ... + + class K: + def f(self): ... +- + def f(): ... + + class Nested: + class dirty: ... + class little: ... +- + class secret: + def who_has_to_know(self): ... +- + def verse(self): ... + + class Conditional: +@@ -57,17 +50,14 @@ + def g(self): ... + else: + def g(self): ... +- + def h(self): ... + def i(self): ... + if sys.version_info >= (3, 8): + def j(self): ... +- + def k(self): ... + if sys.version_info >= (3, 8): + class A: ... + class B: ... +- + class C: + def l(self): ... + def m(self): ... +``` + +## Ruff Output + +```python +X: int + +def f(): ... + +class D: ... +class C: ... + +class B: + this_lack_of_newline_should_be_kept: int + def b(self) -> None: ... + + but_this_newline_should_also_be_kept: int + +class A: + attr: int + attr2: str + + def f(self) -> int: ... + def g(self) -> str: ... + +def g(): ... +def h(): ... + +if sys.version_info >= (3, 8): + class E: + def f(self): ... + class F: + def f(self): ... + class G: ... + class H: ... +else: + class I: ... + class J: ... + def f(): ... + + class K: + def f(self): ... + def f(): ... + +class Nested: + class dirty: ... + class little: ... + class secret: + def who_has_to_know(self): ... + def verse(self): ... + +class Conditional: + def f(self): ... + if sys.version_info >= (3, 8): + def g(self): ... + else: + def g(self): ... + def h(self): ... + def i(self): ... + if sys.version_info >= (3, 8): + def j(self): ... + def k(self): ... + if sys.version_info >= (3, 8): + class A: ... + class B: ... + class C: + def l(self): ... + def m(self): ... +``` + +## Black Output + +```python +X: int + +def f(): ... + +class D: ... +class C: ... + +class B: + this_lack_of_newline_should_be_kept: int + def b(self) -> None: ... + + but_this_newline_should_also_be_kept: int + +class A: + attr: int + attr2: str + + def f(self) -> int: ... + def g(self) -> str: ... + +def g(): ... +def h(): ... + +if sys.version_info >= (3, 8): + class E: + def f(self): ... + + class F: + def f(self): ... + + class G: ... + class H: ... + +else: + class I: ... + class J: ... + + def f(): ... + + class K: + def f(self): ... + + def f(): ... + +class Nested: + class dirty: ... + class little: ... + + class secret: + def who_has_to_know(self): ... + + def verse(self): ... + +class Conditional: + def f(self): ... + if sys.version_info >= (3, 8): + def g(self): ... + else: + def g(self): ... + + def h(self): ... + def i(self): ... + if sys.version_info >= (3, 8): + def j(self): ... + + def k(self): ... + if sys.version_info >= (3, 8): + class A: ... + class B: ... + + class C: + def l(self): ... + def m(self): ... +``` + + diff --git a/crates/ruff_python_formatter/tests/snapshots/black_compatibility@simple_cases__torture.py.snap b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__torture.py.snap similarity index 99% rename from crates/ruff_python_formatter/tests/snapshots/black_compatibility@simple_cases__torture.py.snap rename to crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__torture.py.snap index 6b7ddaa378..f8e78b1ac0 100644 --- a/crates/ruff_python_formatter/tests/snapshots/black_compatibility@simple_cases__torture.py.snap +++ b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__torture.py.snap @@ -1,6 +1,6 @@ --- source: crates/ruff_python_formatter/tests/fixtures.rs -input_file: crates/ruff_python_formatter/resources/test/fixtures/black/simple_cases/torture.py +input_file: crates/ruff_python_formatter/resources/test/fixtures/black/cases/torture.py --- ## Input diff --git a/crates/ruff_python_formatter/tests/snapshots/black_compatibility@simple_cases__trailing_commas_in_leading_parts.py.snap b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__trailing_commas_in_leading_parts.py.snap similarity index 98% rename from crates/ruff_python_formatter/tests/snapshots/black_compatibility@simple_cases__trailing_commas_in_leading_parts.py.snap rename to crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__trailing_commas_in_leading_parts.py.snap index 8ad0695b04..fd4942632e 100644 --- a/crates/ruff_python_formatter/tests/snapshots/black_compatibility@simple_cases__trailing_commas_in_leading_parts.py.snap +++ b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__trailing_commas_in_leading_parts.py.snap @@ -1,6 +1,6 @@ --- source: crates/ruff_python_formatter/tests/fixtures.rs -input_file: crates/ruff_python_formatter/resources/test/fixtures/black/simple_cases/trailing_commas_in_leading_parts.py +input_file: crates/ruff_python_formatter/resources/test/fixtures/black/cases/trailing_commas_in_leading_parts.py --- ## Input diff --git a/crates/ruff_python_formatter/tests/snapshots/black_compatibility@simple_cases__tupleassign.py.snap b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__tupleassign.py.snap similarity index 97% rename from crates/ruff_python_formatter/tests/snapshots/black_compatibility@simple_cases__tupleassign.py.snap rename to crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__tupleassign.py.snap index 16f034ed48..654b488efe 100644 --- a/crates/ruff_python_formatter/tests/snapshots/black_compatibility@simple_cases__tupleassign.py.snap +++ b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@cases__tupleassign.py.snap @@ -1,6 +1,6 @@ --- source: crates/ruff_python_formatter/tests/fixtures.rs -input_file: crates/ruff_python_formatter/resources/test/fixtures/black/simple_cases/tupleassign.py +input_file: crates/ruff_python_formatter/resources/test/fixtures/black/cases/tupleassign.py --- ## Input diff --git a/crates/ruff_python_formatter/tests/snapshots/black_compatibility@miscellaneous__decorators.py.snap b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@miscellaneous__decorators.py.snap deleted file mode 100644 index 9eaba761d4..0000000000 --- a/crates/ruff_python_formatter/tests/snapshots/black_compatibility@miscellaneous__decorators.py.snap +++ /dev/null @@ -1,624 +0,0 @@ ---- -source: crates/ruff_python_formatter/tests/fixtures.rs -input_file: crates/ruff_python_formatter/resources/test/fixtures/black/miscellaneous/decorators.py ---- -## Input - -```python -# This file doesn't use the standard decomposition. -# Decorator syntax test cases are separated by double # comments. -# Those before the 'output' comment are valid under the old syntax. -# Those after the 'ouput' comment require PEP614 relaxed syntax. -# Do not remove the double # separator before the first test case, it allows -# the comment before the test case to be ignored. - -## - -@decorator -def f(): - ... - -## - -@decorator() -def f(): - ... - -## - -@decorator(arg) -def f(): - ... - -## - -@decorator(kwarg=0) -def f(): - ... - -## - -@decorator(*args) -def f(): - ... - -## - -@decorator(**kwargs) -def f(): - ... - -## - -@decorator(*args, **kwargs) -def f(): - ... - -## - -@decorator(*args, **kwargs,) -def f(): - ... - -## - -@dotted.decorator -def f(): - ... - -## - -@dotted.decorator(arg) -def f(): - ... - -## - -@dotted.decorator(kwarg=0) -def f(): - ... - -## - -@dotted.decorator(*args) -def f(): - ... - -## - -@dotted.decorator(**kwargs) -def f(): - ... - -## - -@dotted.decorator(*args, **kwargs) -def f(): - ... - -## - -@dotted.decorator(*args, **kwargs,) -def f(): - ... - -## - -@double.dotted.decorator -def f(): - ... - -## - -@double.dotted.decorator(arg) -def f(): - ... - -## - -@double.dotted.decorator(kwarg=0) -def f(): - ... - -## - -@double.dotted.decorator(*args) -def f(): - ... - -## - -@double.dotted.decorator(**kwargs) -def f(): - ... - -## - -@double.dotted.decorator(*args, **kwargs) -def f(): - ... - -## - -@double.dotted.decorator(*args, **kwargs,) -def f(): - ... - -## - -@_(sequence["decorator"]) -def f(): - ... - -## - -@eval("sequence['decorator']") -def f(): - ... -``` - -## Black Differences - -```diff ---- Black -+++ Ruff -@@ -1,29 +1,206 @@ -+# This file doesn't use the standard decomposition. -+# Decorator syntax test cases are separated by double # comments. -+# Those before the 'output' comment are valid under the old syntax. -+# Those after the 'ouput' comment require PEP614 relaxed syntax. -+# Do not remove the double # separator before the first test case, it allows -+# the comment before the test case to be ignored. -+ -+## -+ -+ -+@decorator -+def f(): -+ ... -+ -+ -+## -+ -+ -+@decorator() -+def f(): -+ ... -+ -+ -+## -+ -+ -+@decorator(arg) -+def f(): -+ ... -+ -+ -+## -+ -+ -+@decorator(kwarg=0) -+def f(): -+ ... -+ -+ -+## -+ -+ -+@decorator(*args) -+def f(): -+ ... -+ -+ - ## - --@decorator()() -+ -+@decorator(**kwargs) - def f(): - ... - -+ - ## - --@(decorator) -+ -+@decorator(*args, **kwargs) - def f(): - ... - -+ - ## - --@sequence["decorator"] -+ -+@decorator( -+ *args, -+ **kwargs, -+) - def f(): - ... - -+ - ## - --@decorator[List[str]] -+ -+@dotted.decorator - def f(): - ... - -+ - ## - --@var := decorator -+ -+@dotted.decorator(arg) -+def f(): -+ ... -+ -+ -+## -+ -+ -+@dotted.decorator(kwarg=0) -+def f(): -+ ... -+ -+ -+## -+ -+ -+@dotted.decorator(*args) -+def f(): -+ ... -+ -+ -+## -+ -+ -+@dotted.decorator(**kwargs) -+def f(): -+ ... -+ -+ -+## -+ -+ -+@dotted.decorator(*args, **kwargs) -+def f(): -+ ... -+ -+ -+## -+ -+ -+@dotted.decorator( -+ *args, -+ **kwargs, -+) -+def f(): -+ ... -+ -+ -+## -+ -+ -+@double.dotted.decorator -+def f(): -+ ... -+ -+ -+## -+ -+ -+@double.dotted.decorator(arg) -+def f(): -+ ... -+ -+ -+## -+ -+ -+@double.dotted.decorator(kwarg=0) -+def f(): -+ ... -+ -+ -+## -+ -+ -+@double.dotted.decorator(*args) -+def f(): -+ ... -+ -+ -+## -+ -+ -+@double.dotted.decorator(**kwargs) -+def f(): -+ ... -+ -+ -+## -+ -+ -+@double.dotted.decorator(*args, **kwargs) -+def f(): -+ ... -+ -+ -+## -+ -+ -+@double.dotted.decorator( -+ *args, -+ **kwargs, -+) -+def f(): -+ ... -+ -+ -+## -+ -+ -+@_(sequence["decorator"]) -+def f(): -+ ... -+ -+ -+## -+ -+ -+@eval("sequence['decorator']") - def f(): - ... -``` - -## Ruff Output - -```python -# This file doesn't use the standard decomposition. -# Decorator syntax test cases are separated by double # comments. -# Those before the 'output' comment are valid under the old syntax. -# Those after the 'ouput' comment require PEP614 relaxed syntax. -# Do not remove the double # separator before the first test case, it allows -# the comment before the test case to be ignored. - -## - - -@decorator -def f(): - ... - - -## - - -@decorator() -def f(): - ... - - -## - - -@decorator(arg) -def f(): - ... - - -## - - -@decorator(kwarg=0) -def f(): - ... - - -## - - -@decorator(*args) -def f(): - ... - - -## - - -@decorator(**kwargs) -def f(): - ... - - -## - - -@decorator(*args, **kwargs) -def f(): - ... - - -## - - -@decorator( - *args, - **kwargs, -) -def f(): - ... - - -## - - -@dotted.decorator -def f(): - ... - - -## - - -@dotted.decorator(arg) -def f(): - ... - - -## - - -@dotted.decorator(kwarg=0) -def f(): - ... - - -## - - -@dotted.decorator(*args) -def f(): - ... - - -## - - -@dotted.decorator(**kwargs) -def f(): - ... - - -## - - -@dotted.decorator(*args, **kwargs) -def f(): - ... - - -## - - -@dotted.decorator( - *args, - **kwargs, -) -def f(): - ... - - -## - - -@double.dotted.decorator -def f(): - ... - - -## - - -@double.dotted.decorator(arg) -def f(): - ... - - -## - - -@double.dotted.decorator(kwarg=0) -def f(): - ... - - -## - - -@double.dotted.decorator(*args) -def f(): - ... - - -## - - -@double.dotted.decorator(**kwargs) -def f(): - ... - - -## - - -@double.dotted.decorator(*args, **kwargs) -def f(): - ... - - -## - - -@double.dotted.decorator( - *args, - **kwargs, -) -def f(): - ... - - -## - - -@_(sequence["decorator"]) -def f(): - ... - - -## - - -@eval("sequence['decorator']") -def f(): - ... -``` - -## Black Output - -```python -## - -@decorator()() -def f(): - ... - -## - -@(decorator) -def f(): - ... - -## - -@sequence["decorator"] -def f(): - ... - -## - -@decorator[List[str]] -def f(): - ... - -## - -@var := decorator -def f(): - ... -``` - - diff --git a/crates/ruff_python_formatter/tests/snapshots/black_compatibility@miscellaneous__force_pyi.py.snap b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@miscellaneous__force_pyi.py.snap index 4eeadc7a82..c2d79524f4 100644 --- a/crates/ruff_python_formatter/tests/snapshots/black_compatibility@miscellaneous__force_pyi.py.snap +++ b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@miscellaneous__force_pyi.py.snap @@ -5,7 +5,6 @@ input_file: crates/ruff_python_formatter/resources/test/fixtures/black/miscellan ## Input ```python -# flags: --pyi from typing import Union @bird @@ -43,8 +42,7 @@ def eggs() -> Union[str, int]: ... ```diff --- Black +++ Ruff -@@ -1,32 +1,59 @@ -+# flags: --pyi +@@ -1,32 +1,58 @@ from typing import Union + @@ -69,13 +67,13 @@ def eggs() -> Union[str, int]: ... - def BMethod(self, arg: List[str]) -> None: ... + def BMethod(self, arg: List[str]) -> None: + ... ++ ++ ++class C: ++ ... -class C: ... -+class C: -+ ... -+ -+ @hmm -class D: ... +class D: @@ -120,7 +118,6 @@ def eggs() -> Union[str, int]: ... ## Ruff Output ```python -# flags: --pyi from typing import Union diff --git a/crates/ruff_python_formatter/tests/snapshots/black_compatibility@miscellaneous__force_pyi.pyi.snap b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@miscellaneous__force_pyi.pyi.snap new file mode 100644 index 0000000000..2eb67a391b --- /dev/null +++ b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@miscellaneous__force_pyi.pyi.snap @@ -0,0 +1,128 @@ +--- +source: crates/ruff_python_formatter/tests/fixtures.rs +input_file: crates/ruff_python_formatter/resources/test/fixtures/black/miscellaneous/force_pyi.pyi +--- +## Input + +```python +from typing import Union + +@bird +def zoo(): ... + +class A: ... +@bar +class B: + def BMethod(self) -> None: ... + @overload + def BMethod(self, arg : List[str]) -> None: ... + +class C: ... +@hmm +class D: ... +class E: ... + +@baz +def foo() -> None: + ... + +class F (A , C): ... +def spam() -> None: ... + +@overload +def spam(arg: str) -> str: ... + +var : int = 1 + +def eggs() -> Union[str, int]: ... +``` + +## Black Differences + +```diff +--- Black ++++ Ruff +@@ -15,7 +15,6 @@ + + @hmm + class D: ... +- + class E: ... + + @baz +``` + +## Ruff Output + +```python +from typing import Union + +@bird +def zoo(): ... + +class A: ... + +@bar +class B: + def BMethod(self) -> None: ... + @overload + def BMethod(self, arg: List[str]) -> None: ... + +class C: ... + +@hmm +class D: ... +class E: ... + +@baz +def foo() -> None: ... + +class F(A, C): ... + +def spam() -> None: ... +@overload +def spam(arg: str) -> str: ... + +var: int = 1 + +def eggs() -> Union[str, int]: ... +``` + +## Black Output + +```python +from typing import Union + +@bird +def zoo(): ... + +class A: ... + +@bar +class B: + def BMethod(self) -> None: ... + @overload + def BMethod(self, arg: List[str]) -> None: ... + +class C: ... + +@hmm +class D: ... + +class E: ... + +@baz +def foo() -> None: ... + +class F(A, C): ... + +def spam() -> None: ... +@overload +def spam(arg: str) -> str: ... + +var: int = 1 + +def eggs() -> Union[str, int]: ... +``` + + diff --git a/crates/ruff_python_formatter/tests/snapshots/black_compatibility@py_310__pattern_matching_extras.py.snap b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@py_310__pattern_matching_extras.py.snap deleted file mode 100644 index 865bb18e44..0000000000 --- a/crates/ruff_python_formatter/tests/snapshots/black_compatibility@py_310__pattern_matching_extras.py.snap +++ /dev/null @@ -1,420 +0,0 @@ ---- -source: crates/ruff_python_formatter/tests/fixtures.rs -input_file: crates/ruff_python_formatter/resources/test/fixtures/black/py_310/pattern_matching_extras.py ---- -## Input - -```python -import match - -match something: - case [a as b]: - print(b) - case [a as b, c, d, e as f]: - print(f) - case Point(a as b): - print(b) - case Point(int() as x, int() as y): - print(x, y) - - -match = 1 -case: int = re.match(something) - -match re.match(case): - case type("match", match): - pass - case match: - pass - - -def func(match: case, case: match) -> case: - match Something(): - case func(match, case): - ... - case another: - ... - - -match maybe, multiple: - case perhaps, 5: - pass - case perhaps, 6,: - pass - - -match more := (than, one), indeed,: - case _, (5, 6): - pass - case [[5], (6)], [7],: - pass - case _: - pass - - -match a, *b, c: - case [*_]: - assert "seq" == _ - case {}: - assert "map" == b - - -match match( - case, - match( - match, case, match, looooooooooooooooooooooooooooooooooooong, match, case, match - ), - case, -): - case case( - match=case, - case=re.match( - loooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong - ), - ): - pass - - case [a as match]: - pass - - case case: - pass - - -match match: - case case: - pass - - -match a, *b(), c: - case d, *f, g: - pass - - -match something: - case { - "key": key as key_1, - "password": PASS.ONE | PASS.TWO | PASS.THREE as password, - }: - pass - case {"maybe": something(complicated as this) as that}: - pass - - -match something: - case 1 as a: - pass - - case 2 as b, 3 as c: - pass - - case 4 as d, (5 as e), (6 | 7 as g), *h: - pass - - -match bar1: - case Foo(aa=Callable() as aa, bb=int()): - print(bar1.aa, bar1.bb) - case _: - print("no match", "\n") - - -match bar1: - case Foo( - normal=x, perhaps=[list, {"x": d, "y": 1.0}] as y, otherwise=something, q=t as u - ): - pass -``` - -## Black Differences - -```diff ---- Black -+++ Ruff -@@ -32,14 +32,23 @@ - match maybe, multiple: - case perhaps, 5: - pass -- case perhaps, 6,: -+ case ( -+ perhaps, -+ 6, -+ ): - pass - - --match more := (than, one), indeed,: -+match ( -+ more := (than, one), -+ indeed, -+): - case _, (5, 6): - pass -- case [[5], (6)], [7],: -+ case [ -+ [[5], (6)], -+ [7], -+ ]: - pass - case _: - pass -``` - -## Ruff Output - -```python -import match - -match something: - case [a as b]: - print(b) - case [a as b, c, d, e as f]: - print(f) - case Point(a as b): - print(b) - case Point(int() as x, int() as y): - print(x, y) - - -match = 1 -case: int = re.match(something) - -match re.match(case): - case type("match", match): - pass - case match: - pass - - -def func(match: case, case: match) -> case: - match Something(): - case func(match, case): - ... - case another: - ... - - -match maybe, multiple: - case perhaps, 5: - pass - case ( - perhaps, - 6, - ): - pass - - -match ( - more := (than, one), - indeed, -): - case _, (5, 6): - pass - case [ - [[5], (6)], - [7], - ]: - pass - case _: - pass - - -match a, *b, c: - case [*_]: - assert "seq" == _ - case {}: - assert "map" == b - - -match match( - case, - match( - match, case, match, looooooooooooooooooooooooooooooooooooong, match, case, match - ), - case, -): - case case( - match=case, - case=re.match( - loooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong - ), - ): - pass - - case [a as match]: - pass - - case case: - pass - - -match match: - case case: - pass - - -match a, *b(), c: - case d, *f, g: - pass - - -match something: - case { - "key": key as key_1, - "password": PASS.ONE | PASS.TWO | PASS.THREE as password, - }: - pass - case {"maybe": something(complicated as this) as that}: - pass - - -match something: - case 1 as a: - pass - - case 2 as b, 3 as c: - pass - - case 4 as d, (5 as e), (6 | 7 as g), *h: - pass - - -match bar1: - case Foo(aa=Callable() as aa, bb=int()): - print(bar1.aa, bar1.bb) - case _: - print("no match", "\n") - - -match bar1: - case Foo( - normal=x, perhaps=[list, {"x": d, "y": 1.0}] as y, otherwise=something, q=t as u - ): - pass -``` - -## Black Output - -```python -import match - -match something: - case [a as b]: - print(b) - case [a as b, c, d, e as f]: - print(f) - case Point(a as b): - print(b) - case Point(int() as x, int() as y): - print(x, y) - - -match = 1 -case: int = re.match(something) - -match re.match(case): - case type("match", match): - pass - case match: - pass - - -def func(match: case, case: match) -> case: - match Something(): - case func(match, case): - ... - case another: - ... - - -match maybe, multiple: - case perhaps, 5: - pass - case perhaps, 6,: - pass - - -match more := (than, one), indeed,: - case _, (5, 6): - pass - case [[5], (6)], [7],: - pass - case _: - pass - - -match a, *b, c: - case [*_]: - assert "seq" == _ - case {}: - assert "map" == b - - -match match( - case, - match( - match, case, match, looooooooooooooooooooooooooooooooooooong, match, case, match - ), - case, -): - case case( - match=case, - case=re.match( - loooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong - ), - ): - pass - - case [a as match]: - pass - - case case: - pass - - -match match: - case case: - pass - - -match a, *b(), c: - case d, *f, g: - pass - - -match something: - case { - "key": key as key_1, - "password": PASS.ONE | PASS.TWO | PASS.THREE as password, - }: - pass - case {"maybe": something(complicated as this) as that}: - pass - - -match something: - case 1 as a: - pass - - case 2 as b, 3 as c: - pass - - case 4 as d, (5 as e), (6 | 7 as g), *h: - pass - - -match bar1: - case Foo(aa=Callable() as aa, bb=int()): - print(bar1.aa, bar1.bb) - case _: - print("no match", "\n") - - -match bar1: - case Foo( - normal=x, perhaps=[list, {"x": d, "y": 1.0}] as y, otherwise=something, q=t as u - ): - pass -``` - - diff --git a/crates/ruff_python_formatter/tests/snapshots/black_compatibility@simple_cases__docstring_preview.py.snap b/crates/ruff_python_formatter/tests/snapshots/black_compatibility@simple_cases__docstring_preview.py.snap deleted file mode 100644 index afb73efb11..0000000000 --- a/crates/ruff_python_formatter/tests/snapshots/black_compatibility@simple_cases__docstring_preview.py.snap +++ /dev/null @@ -1,184 +0,0 @@ ---- -source: crates/ruff_python_formatter/tests/fixtures.rs -input_file: crates/ruff_python_formatter/resources/test/fixtures/black/simple_cases/docstring_preview.py ---- -## Input - -```python -def docstring_almost_at_line_limit(): - """long docstring................................................................. - """ - - -def docstring_almost_at_line_limit_with_prefix(): - f"""long docstring................................................................ - """ - - -def mulitline_docstring_almost_at_line_limit(): - """long docstring................................................................. - - .................................................................................. - """ - - -def mulitline_docstring_almost_at_line_limit_with_prefix(): - f"""long docstring................................................................ - - .................................................................................. - """ - - -def docstring_at_line_limit(): - """long docstring................................................................""" - - -def docstring_at_line_limit_with_prefix(): - f"""long docstring...............................................................""" - - -def multiline_docstring_at_line_limit(): - """first line----------------------------------------------------------------------- - - second line----------------------------------------------------------------------""" - - -def multiline_docstring_at_line_limit_with_prefix(): - f"""first line---------------------------------------------------------------------- - - second line----------------------------------------------------------------------""" - - -def single_quote_docstring_over_line_limit(): - "We do not want to put the closing quote on a new line as that is invalid (see GH-3141)." - - -def single_quote_docstring_over_line_limit2(): - 'We do not want to put the closing quote on a new line as that is invalid (see GH-3141).' -``` - -## Black Differences - -```diff ---- Black -+++ Ruff -@@ -3,7 +3,8 @@ - - - def docstring_almost_at_line_limit_with_prefix(): -- f"""long docstring................................................................""" -+ f"""long docstring................................................................ -+ """ - - - def mulitline_docstring_almost_at_line_limit(): -``` - -## Ruff Output - -```python -def docstring_almost_at_line_limit(): - """long docstring.................................................................""" - - -def docstring_almost_at_line_limit_with_prefix(): - f"""long docstring................................................................ - """ - - -def mulitline_docstring_almost_at_line_limit(): - """long docstring................................................................. - - .................................................................................. - """ - - -def mulitline_docstring_almost_at_line_limit_with_prefix(): - f"""long docstring................................................................ - - .................................................................................. - """ - - -def docstring_at_line_limit(): - """long docstring................................................................""" - - -def docstring_at_line_limit_with_prefix(): - f"""long docstring...............................................................""" - - -def multiline_docstring_at_line_limit(): - """first line----------------------------------------------------------------------- - - second line----------------------------------------------------------------------""" - - -def multiline_docstring_at_line_limit_with_prefix(): - f"""first line---------------------------------------------------------------------- - - second line----------------------------------------------------------------------""" - - -def single_quote_docstring_over_line_limit(): - "We do not want to put the closing quote on a new line as that is invalid (see GH-3141)." - - -def single_quote_docstring_over_line_limit2(): - "We do not want to put the closing quote on a new line as that is invalid (see GH-3141)." -``` - -## Black Output - -```python -def docstring_almost_at_line_limit(): - """long docstring.................................................................""" - - -def docstring_almost_at_line_limit_with_prefix(): - f"""long docstring................................................................""" - - -def mulitline_docstring_almost_at_line_limit(): - """long docstring................................................................. - - .................................................................................. - """ - - -def mulitline_docstring_almost_at_line_limit_with_prefix(): - f"""long docstring................................................................ - - .................................................................................. - """ - - -def docstring_at_line_limit(): - """long docstring................................................................""" - - -def docstring_at_line_limit_with_prefix(): - f"""long docstring...............................................................""" - - -def multiline_docstring_at_line_limit(): - """first line----------------------------------------------------------------------- - - second line----------------------------------------------------------------------""" - - -def multiline_docstring_at_line_limit_with_prefix(): - f"""first line---------------------------------------------------------------------- - - second line----------------------------------------------------------------------""" - - -def single_quote_docstring_over_line_limit(): - "We do not want to put the closing quote on a new line as that is invalid (see GH-3141)." - - -def single_quote_docstring_over_line_limit2(): - "We do not want to put the closing quote on a new line as that is invalid (see GH-3141)." -``` - -