-
Notifications
You must be signed in to change notification settings - Fork 1.5k
Description
Black is planning to stabilize a subset of the preview style as stable style for 2024 (psf/black#4042). Ruff implements a subset of the current preview style, and subsequently a subset of the planned stable style.
For each of the unchecked items below, we need to port the test case to ruff, implement the changed behavior under the preview flag and tick off the box below. For two of the changes, we will need to introduce target version support to the formatter (same as in the linter).
Shipped in Ruff Stable
These are mainly bug fixes to Black that we didn't inherit or shipping them right away simplified the implementation and are uncontroversial.
-
respect_magic_trailing_comma_in_return_type
-
add_trailing_comma_consistently
-
skip_magic_trailing_comma_in_subscript
: Requires--skip-magic-trailing-comma
-
blank_line_between_nested_and_def_stub_file
: Only applies to stub files (.pyi
) -
accept_raw_docstrings
-
long_case_block_line_splitting
, although our formatting may not be a 100% correct. See Can omit parentheses layout for patternsΒ #6933 - Formatter:
walrus_subscript
preview styleΒ #8887
Likely to be stabilized
Styles that with a high likelihood to be promoted to Black stable and are part of the 2024 alpha release.
- Formatter:
no_blank_line_before_class_docstring
preview styleΒ #8888 - Formatter:
wrap_multiple_context_managers_in_parens
preview styleΒ #8889 - Formatter:
improved_async_statements_handling
preview styleΒ #8890 - Formatter:
blank_line_after_nested_stub_class
preview styleΒ #8891 - Formatter:
single_line_format_skip_with_multiple_comments
preview styleΒ #8892 - Formatter:
dummy_implementations
preview styleΒ #8357 - Formatter:
prefer_splitting_right_hand_side_of_assignments
preview styleΒ #6975 - Formatter:
allow_empty_first_line_before_new_block_or_comment
preview styleΒ #8893 - Formatter:
parenthesize_long_type_hints
preview styleΒ #8894 - Add newline after module docstrings (
module_docstring_newlines
)Β #7995 - Formatter:
allow_form_feeds
preview styleΒ #8895 - Other changes (discovered in test files)Β #8904
-
fix_power_op_line_length
preview styleΒ #8938
Not uncontroversial preview features that are likely be stabilized
- Formatter:
multiline_string_handling
preview styleΒ #8896 - Formatter:
wrap_long_dict_values_in_parens
preview styleΒ #8897 - Formatter:
parenthesize_conditional_expressions
preview styleΒ #8438
Unlikely to be stabilized in 2024
- Formatter:
string_processing
preview styleΒ #6936 -
hex_codes_in_unicode_sequences
(PR). More string normalization @MichaReiser - Formatter:
hug_parens_with_braces_and_square_brackets
preview styleΒ #8279
Gather feedback
Notes
We might have to remove some preview styles from ruff, but removing the preview clause is enough that we can delay until black's changes are finalized
Reference test file
# ERR dummy_implementations (https://github.com/psf/black/pull/3796)
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:
...
# ERR prefer_splitting_right_hand_side_of_assignments (https://github.com/psf/black/pull/3368)
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
)
)
# ERR(https://github.com/psf/black/issues/3568) hex_codes_in_unicode_sequences (https://github.com/psf/black/pull/2916)
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"
# ERR allow_empty_first_line_before_new_block_or_comment (https://github.com/psf/black/pull/3967)
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
# ERR parenthesize_long_type_hints (https://github.com/psf/black/pull/3899)
# 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()
# OK module_docstring_newlines (https://github.com/psf/black/pull/3932)
# [Can't do this here since it needs to be the first item in a file, but it's done: https://github.com/astral-sh/ruff/issues/7995]
# OK: respect_magic_trailing_comma_in_return_type (https://github.com/psf/black/pull/3916)
def foo(a,b) -> tuple[int, int, int,]:
return 2
# ERR: no_blank_line_before_class_docstring (https://github.com/psf/black/pull/3692)
class LineBeforeDocstring:
"""Please move me up"""
# ERR(--target-version py39, see also improved_async_statements_handling): wrap_multiple_context_managers_in_parens (https://github.com/psf/black/pull/3489)
with \
make_context_manager1() as cm1, \
make_context_manager2() as cm2, \
make_context_manager3() as cm3, \
make_context_manager4() as cm4 \
:
pass
# OK: fix_power_op_line_length (https://github.com/psf/black/pull/3942)
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
# OK: add_trailing_comma_consistently (https://github.com/psf/black/pull/3393)
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
}
# OK(--skip-magic-trailing-comma) skip_magic_trailing_comma_in_subscript (https://github.com/psf/black/pull/3209)
# We should not remove the trailing comma in a single-element subscript.
a: tuple[int,]
b = tuple[int,]
# But commas in multiple element subscripts should be removed.
c: tuple[int, int,]
d = tuple[int, int,]
# Remove commas for non-subscripts.
small_list = [1,]
list_of_types = [tuple[int,],]
small_set = {1,}
set_of_types = {tuple[int,],}
# Except single element tuples
small_tuple = (1,)
# ERR(.pyi) blank_line_after_nested_stub_class (https://github.com/psf/black/pull/3564)
class Outer:
class InnerStub: ...
outer_attr_after_inner_stub: int
class Inner:
inner_attr: int
outer_attr: int
# OK blank_line_between_nested_and_def_stub_file (https://github.com/psf/black/pull/3862)
if sys.version_info > (3, 7):
class Nested1:
assignment = 1
def function_definition(self): ...
def f1(self) -> str: ...
class Nested2:
def function_definition(self): ...
assignment = 1
def f2(self) -> str: ...
if sys.version_info > (3, 7):
def nested1():
assignment = 1
def function_definition(self): ...
def f1(self) -> str: ...
def nested2():
def function_definition(self): ...
assignment = 1
def f2(self) -> str: ...
# OK accept_raw_docstrings (https://github.com/psf/black/pull/3947)
class C:
r"""Raw"""
# OK long_case_block_line_splitting (https://github.com/psf/black/pull/4024)
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
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
# ERR(i think we're correct here though) walrus_subscript (https://github.com/psf/black/pull/3823)
x[(a:=0):]
x[:(a:=0)]
x[a:=0]
x[a := 0]
x[a := 0, b := 1]
x[5, b := 0]
x[a:=0,b:=1]
# ERR(--target-version py39, same as wrap_multiple_context_managers_in_parens effectively) improved_async_statements_handling (https://github.com/psf/black/pull/3609)
async def func() -> (int):
return 0
@decorated
async def func() -> (int):
return 0
async for (item) in async_iter:
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
# ERR(we have different format) single_line_format_skip_with_multiple_comments (https://github.com/psf/black/pull/3959)
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
# ERR parenthesize_conditional_expressions (https://github.com/psf/black/pull/2278)
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
)
# ERR wrap_long_dict_values_in_parens (https://github.com/psf/black/pull/3440)
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")
}
# ERR multiline_string_handling (https://github.com/psf/black/pull/1879)
textwrap.dedent(
"""\
This is a
multiline string
"""
)