Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

🏖️ Black 2024 Preview Style #8678

Closed
27 of 28 tasks
konstin opened this issue Nov 14, 2023 · 0 comments
Closed
27 of 28 tasks

🏖️ Black 2024 Preview Style #8678

konstin opened this issue Nov 14, 2023 · 0 comments
Labels
formatter Related to the formatter preview Related to preview mode features

Comments

@konstin
Copy link
Member

konstin commented Nov 14, 2023

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.

Likely to be stabilized

Styles that with a high likelihood to be promoted to Black stable and are part of the 2024 alpha release.

Not uncontroversial preview features that are likely be stabilized

Unlikely to be stabilized in 2024

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
"""
)
@konstin konstin added the formatter Related to the formatter label Nov 14, 2023
@MichaReiser MichaReiser added the preview Related to preview mode features label Nov 27, 2023
@MichaReiser MichaReiser changed the title Implement the planned 2024 black stable style Black 2024 Preview Style Nov 28, 2023
@MichaReiser MichaReiser changed the title Black 2024 Preview Style 🏖️ Black 2024 Preview Style Nov 29, 2023
@MichaReiser MichaReiser added this to the Formatter: Stable milestone Nov 29, 2023
@MichaReiser MichaReiser modified the milestones: Formatter: Stable, v0.4 Feb 14, 2024
@charliermarsh charliermarsh removed this from the v0.4.0 milestone Apr 12, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
formatter Related to the formatter preview Related to preview mode features
Projects
None yet
Development

No branches or pull requests

3 participants