Skip to content

Commit 59b9d85

Browse files
Create the 2024 stable style (#4106)
1 parent 8fe602b commit 59b9d85

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

50 files changed

+222
-294
lines changed

CHANGES.md

+36-4
Original file line numberDiff line numberDiff line change
@@ -6,22 +6,54 @@
66

77
<!-- Include any especially major or disruptive changes here -->
88

9+
This release introduces the new 2024 stable style (#4106), stabilizing the following
10+
changes:
11+
12+
- Add parentheses around `if`-`else` expressions (#2278)
13+
- Dummy class and function implementations consisting only of `...` are formatted more
14+
compactly (#3796)
15+
- If an assignment statement is too long, we now prefer splitting on the right-hand side
16+
(#3368)
17+
- Hex codes in Unicode escape sequences are now standardized to lowercase (#2916)
18+
- Allow empty first lines at the beginning of most blocks (#3967, #4061)
19+
- Add parentheses around long type annotations (#3899)
20+
- Standardize on a single newline after module docstrings (#3932)
21+
- Fix incorrect magic trailing comma handling in return types (#3916)
22+
- Remove blank lines before class docstrings (#3692)
23+
- Wrap multiple context managers in parentheses if combined in a single `with` statement
24+
(#3489)
25+
- Fix bug in line length calculations for power operations (#3942)
26+
- Add trailing commas to collection literals even if there's a comment after the last
27+
entry (#3393)
28+
- When using `--skip-magic-trailing-comma` or `-C`, trailing commas are stripped from
29+
subscript expressions with more than 1 element (#3209)
30+
- Add extra blank lines in stubs in a few cases (#3564, #3862)
31+
- Accept raw strings as docstrings (#3947)
32+
- Split long lines in case blocks (#4024)
33+
- Stop removing spaces from walrus operators within subscripts (#3823)
34+
- Fix incorrect formatting of certain async statements (#3609)
35+
- Allow combining `# fmt: skip` with other comments (#3959)
36+
937
### Stable style
1038

1139
<!-- Changes that affect Black's stable style -->
1240

13-
### Preview style
14-
15-
<!-- Changes that affect Black's preview style -->
41+
Several bug fixes were made in features that are moved to the stable style in this
42+
release:
1643

1744
- Fix comment handling when parenthesising conditional expressions (#4134)
18-
- Format module docstrings the same as class and function docstrings (#4095)
1945
- Fix bug where spaces were not added around parenthesized walruses in subscripts,
2046
unlike other binary operators (#4109)
2147
- Remove empty lines before docstrings in async functions (#4132)
2248
- Address a missing case in the change to allow empty lines at the beginning of all
2349
blocks, except immediately before a docstring (#4130)
2450
- For stubs, fix logic to enforce empty line after nested classes with bodies (#4141)
51+
52+
### Preview style
53+
54+
<!-- Changes that affect Black's preview style -->
55+
56+
- Format module docstrings the same as class and function docstrings (#4095)
2557
- Fix crash when using a walrus in a dictionary (#4155)
2658
- Fix unnecessary parentheses when wrapping long dicts (#4135)
2759

src/black/comments.py

+14-18
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
from functools import lru_cache
44
from typing import Collection, Final, Iterator, List, Optional, Tuple, Union
55

6-
from black.mode import Mode, Preview
6+
from black.mode import Mode
77
from black.nodes import (
88
CLOSING_BRACKETS,
99
STANDALONE_COMMENT,
@@ -390,22 +390,18 @@ def _contains_fmt_skip_comment(comment_line: str, mode: Mode) -> bool:
390390
# noqa:XXX # fmt:skip # a nice line <-- multiple comments (Preview)
391391
# pylint:XXX; fmt:skip <-- list of comments (; separated, Preview)
392392
"""
393-
semantic_comment_blocks = (
394-
[
395-
comment_line,
396-
*[
397-
_COMMENT_PREFIX + comment.strip()
398-
for comment in comment_line.split(_COMMENT_PREFIX)[1:]
399-
],
400-
*[
401-
_COMMENT_PREFIX + comment.strip()
402-
for comment in comment_line.strip(_COMMENT_PREFIX).split(
403-
_COMMENT_LIST_SEPARATOR
404-
)
405-
],
406-
]
407-
if Preview.single_line_format_skip_with_multiple_comments in mode
408-
else [comment_line]
409-
)
393+
semantic_comment_blocks = [
394+
comment_line,
395+
*[
396+
_COMMENT_PREFIX + comment.strip()
397+
for comment in comment_line.split(_COMMENT_PREFIX)[1:]
398+
],
399+
*[
400+
_COMMENT_PREFIX + comment.strip()
401+
for comment in comment_line.strip(_COMMENT_PREFIX).split(
402+
_COMMENT_LIST_SEPARATOR
403+
)
404+
],
405+
]
410406

411407
return any(comment in FMT_SKIP for comment in semantic_comment_blocks)

src/black/linegen.py

+26-69
Original file line numberDiff line numberDiff line change
@@ -115,10 +115,8 @@ def line(self, indent: int = 0) -> Iterator[Line]:
115115
self.current_line.depth += indent
116116
return # Line is empty, don't emit. Creating a new one unnecessary.
117117

118-
if (
119-
Preview.improved_async_statements_handling in self.mode
120-
and len(self.current_line.leaves) == 1
121-
and is_async_stmt_or_funcdef(self.current_line.leaves[0])
118+
if len(self.current_line.leaves) == 1 and is_async_stmt_or_funcdef(
119+
self.current_line.leaves[0]
122120
):
123121
# Special case for async def/for/with statements. `visit_async_stmt`
124122
# adds an `ASYNC` leaf then visits the child def/for/with statement
@@ -164,20 +162,19 @@ def visit_default(self, node: LN) -> Iterator[Line]:
164162
def visit_test(self, node: Node) -> Iterator[Line]:
165163
"""Visit an `x if y else z` test"""
166164

167-
if Preview.parenthesize_conditional_expressions in self.mode:
168-
already_parenthesized = (
169-
node.prev_sibling and node.prev_sibling.type == token.LPAR
170-
)
165+
already_parenthesized = (
166+
node.prev_sibling and node.prev_sibling.type == token.LPAR
167+
)
171168

172-
if not already_parenthesized:
173-
# Similar to logic in wrap_in_parentheses
174-
lpar = Leaf(token.LPAR, "")
175-
rpar = Leaf(token.RPAR, "")
176-
prefix = node.prefix
177-
node.prefix = ""
178-
lpar.prefix = prefix
179-
node.insert_child(0, lpar)
180-
node.append_child(rpar)
169+
if not already_parenthesized:
170+
# Similar to logic in wrap_in_parentheses
171+
lpar = Leaf(token.LPAR, "")
172+
rpar = Leaf(token.RPAR, "")
173+
prefix = node.prefix
174+
node.prefix = ""
175+
lpar.prefix = prefix
176+
node.insert_child(0, lpar)
177+
node.append_child(rpar)
181178

182179
yield from self.visit_default(node)
183180

@@ -292,9 +289,7 @@ def visit_match_case(self, node: Node) -> Iterator[Line]:
292289

293290
def visit_suite(self, node: Node) -> Iterator[Line]:
294291
"""Visit a suite."""
295-
if (
296-
self.mode.is_pyi or Preview.dummy_implementations in self.mode
297-
) and is_stub_suite(node, self.mode):
292+
if is_stub_suite(node):
298293
yield from self.visit(node.children[2])
299294
else:
300295
yield from self.visit_default(node)
@@ -308,23 +303,15 @@ def visit_simple_stmt(self, node: Node) -> Iterator[Line]:
308303
prev_type = child.type
309304

310305
if node.parent and node.parent.type in STATEMENT:
311-
if Preview.dummy_implementations in self.mode:
312-
condition = is_parent_function_or_class(node)
313-
else:
314-
condition = self.mode.is_pyi
315-
if condition and is_stub_body(node):
306+
if is_parent_function_or_class(node) and is_stub_body(node):
316307
yield from self.visit_default(node)
317308
else:
318309
yield from self.line(+1)
319310
yield from self.visit_default(node)
320311
yield from self.line(-1)
321312

322313
else:
323-
if (
324-
not (self.mode.is_pyi or Preview.dummy_implementations in self.mode)
325-
or not node.parent
326-
or not is_stub_suite(node.parent, self.mode)
327-
):
314+
if not node.parent or not is_stub_suite(node.parent):
328315
yield from self.line()
329316
yield from self.visit_default(node)
330317

@@ -342,11 +329,7 @@ def visit_async_stmt(self, node: Node) -> Iterator[Line]:
342329
break
343330

344331
internal_stmt = next(children)
345-
if Preview.improved_async_statements_handling in self.mode:
346-
yield from self.visit(internal_stmt)
347-
else:
348-
for child in internal_stmt.children:
349-
yield from self.visit(child)
332+
yield from self.visit(internal_stmt)
350333

351334
def visit_decorators(self, node: Node) -> Iterator[Line]:
352335
"""Visit decorators."""
@@ -420,10 +403,9 @@ def foo(a: int, b: float = 7): ...
420403
421404
def foo(a: (int), b: (float) = 7): ...
422405
"""
423-
if Preview.parenthesize_long_type_hints in self.mode:
424-
assert len(node.children) == 3
425-
if maybe_make_parens_invisible_in_atom(node.children[2], parent=node):
426-
wrap_in_parentheses(node, node.children[2], visible=False)
406+
assert len(node.children) == 3
407+
if maybe_make_parens_invisible_in_atom(node.children[2], parent=node):
408+
wrap_in_parentheses(node, node.children[2], visible=False)
427409

428410
yield from self.visit_default(node)
429411

@@ -529,13 +511,7 @@ def __post_init__(self) -> None:
529511
self.visit_with_stmt = partial(v, keywords={"with"}, parens={"with"})
530512
self.visit_classdef = partial(v, keywords={"class"}, parens=Ø)
531513

532-
# When this is moved out of preview, add ":" directly to ASSIGNMENTS in nodes.py
533-
if Preview.parenthesize_long_type_hints in self.mode:
534-
assignments = ASSIGNMENTS | {":"}
535-
else:
536-
assignments = ASSIGNMENTS
537-
self.visit_expr_stmt = partial(v, keywords=Ø, parens=assignments)
538-
514+
self.visit_expr_stmt = partial(v, keywords=Ø, parens=ASSIGNMENTS)
539515
self.visit_return_stmt = partial(v, keywords={"return"}, parens={"return"})
540516
self.visit_import_from = partial(v, keywords=Ø, parens={"import"})
541517
self.visit_del_stmt = partial(v, keywords=Ø, parens={"del"})
@@ -576,9 +552,7 @@ def transform_line(
576552
# We need the line string when power operators are hugging to determine if we should
577553
# split the line. Default to line_str, if no power operator are present on the line.
578554
line_str_hugging_power_ops = (
579-
(_hugging_power_ops_line_to_string(line, features, mode) or line_str)
580-
if Preview.fix_power_op_line_length in mode
581-
else line_str
555+
_hugging_power_ops_line_to_string(line, features, mode) or line_str
582556
)
583557

584558
ll = mode.line_length
@@ -688,9 +662,6 @@ def should_split_funcdef_with_rhs(line: Line, mode: Mode) -> bool:
688662
"""If a funcdef has a magic trailing comma in the return type, then we should first
689663
split the line with rhs to respect the comma.
690664
"""
691-
if Preview.respect_magic_trailing_comma_in_return_type not in mode:
692-
return False
693-
694665
return_type_leaves: List[Leaf] = []
695666
in_return_type = False
696667

@@ -919,9 +890,6 @@ def _maybe_split_omitting_optional_parens(
919890
try:
920891
# The RHSResult Omitting Optional Parens.
921892
rhs_oop = _first_right_hand_split(line, omit=omit)
922-
prefer_splitting_rhs_mode = (
923-
Preview.prefer_splitting_right_hand_side_of_assignments in line.mode
924-
)
925893
is_split_right_after_equal = (
926894
len(rhs.head.leaves) >= 2 and rhs.head.leaves[-2].type == token.EQUAL
927895
)
@@ -937,8 +905,7 @@ def _maybe_split_omitting_optional_parens(
937905
)
938906
if (
939907
not (
940-
prefer_splitting_rhs_mode
941-
and is_split_right_after_equal
908+
is_split_right_after_equal
942909
and rhs_head_contains_brackets
943910
and rhs_head_short_enough
944911
and rhs_head_explode_blocked_by_magic_trailing_comma
@@ -1224,11 +1191,7 @@ def append_to_line(leaf: Leaf) -> Iterator[Line]:
12241191
trailing_comma_safe and Feature.TRAILING_COMMA_IN_CALL in features
12251192
)
12261193

1227-
if (
1228-
Preview.add_trailing_comma_consistently in mode
1229-
and last_leaf.type == STANDALONE_COMMENT
1230-
and leaf_idx == last_non_comment_leaf
1231-
):
1194+
if last_leaf.type == STANDALONE_COMMENT and leaf_idx == last_non_comment_leaf:
12321195
current_line = _safe_add_trailing_comma(
12331196
trailing_comma_safe, delimiter_priority, current_line
12341197
)
@@ -1315,11 +1278,7 @@ def normalize_invisible_parens( # noqa: C901
13151278

13161279
# Fixes a bug where invisible parens are not properly wrapped around
13171280
# case blocks.
1318-
if (
1319-
isinstance(child, Node)
1320-
and child.type == syms.case_block
1321-
and Preview.long_case_block_line_splitting in mode
1322-
):
1281+
if isinstance(child, Node) and child.type == syms.case_block:
13231282
normalize_invisible_parens(
13241283
child, parens_after={"case"}, mode=mode, features=features
13251284
)
@@ -1374,7 +1333,6 @@ def normalize_invisible_parens( # noqa: C901
13741333
and child.next_sibling is not None
13751334
and child.next_sibling.type == token.COLON
13761335
and child.value == "case"
1377-
and Preview.long_case_block_line_splitting in mode
13781336
):
13791337
# A special patch for "case case:" scenario, the second occurrence
13801338
# of case will be not parsed as a Python keyword.
@@ -1448,7 +1406,6 @@ def _maybe_wrap_cms_in_parens(
14481406
"""
14491407
if (
14501408
Feature.PARENTHESIZED_CONTEXT_MANAGERS not in features
1451-
or Preview.wrap_multiple_context_managers_in_parens not in mode
14521409
or len(node.children) <= 2
14531410
# If it's an atom, it's already wrapped in parens.
14541411
or node.children[1].type == syms.atom

0 commit comments

Comments
 (0)