Create the 2024 stable style (#4106)

This commit is contained in:
Jelle Zijlstra 2024-01-24 17:06:14 -08:00 committed by GitHub
parent 8fe602b1fa
commit 59b9d858a3
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
50 changed files with 222 additions and 294 deletions

View File

@ -6,22 +6,54 @@
<!-- Include any especially major or disruptive changes here -->
This release introduces the new 2024 stable style (#4106), stabilizing the following
changes:
- Add parentheses around `if`-`else` expressions (#2278)
- Dummy class and function implementations consisting only of `...` are formatted more
compactly (#3796)
- If an assignment statement is too long, we now prefer splitting on the right-hand side
(#3368)
- Hex codes in Unicode escape sequences are now standardized to lowercase (#2916)
- Allow empty first lines at the beginning of most blocks (#3967, #4061)
- Add parentheses around long type annotations (#3899)
- Standardize on a single newline after module docstrings (#3932)
- Fix incorrect magic trailing comma handling in return types (#3916)
- Remove blank lines before class docstrings (#3692)
- Wrap multiple context managers in parentheses if combined in a single `with` statement
(#3489)
- Fix bug in line length calculations for power operations (#3942)
- Add trailing commas to collection literals even if there's a comment after the last
entry (#3393)
- When using `--skip-magic-trailing-comma` or `-C`, trailing commas are stripped from
subscript expressions with more than 1 element (#3209)
- Add extra blank lines in stubs in a few cases (#3564, #3862)
- Accept raw strings as docstrings (#3947)
- Split long lines in case blocks (#4024)
- Stop removing spaces from walrus operators within subscripts (#3823)
- Fix incorrect formatting of certain async statements (#3609)
- Allow combining `# fmt: skip` with other comments (#3959)
### Stable style
<!-- Changes that affect Black's stable style -->
### Preview style
<!-- Changes that affect Black's preview style -->
Several bug fixes were made in features that are moved to the stable style in this
release:
- Fix comment handling when parenthesising conditional expressions (#4134)
- Format module docstrings the same as class and function docstrings (#4095)
- Fix bug where spaces were not added around parenthesized walruses in subscripts,
unlike other binary operators (#4109)
- Remove empty lines before docstrings in async functions (#4132)
- Address a missing case in the change to allow empty lines at the beginning of all
blocks, except immediately before a docstring (#4130)
- For stubs, fix logic to enforce empty line after nested classes with bodies (#4141)
### Preview style
<!-- Changes that affect Black's preview style -->
- Format module docstrings the same as class and function docstrings (#4095)
- Fix crash when using a walrus in a dictionary (#4155)
- Fix unnecessary parentheses when wrapping long dicts (#4135)

View File

@ -3,7 +3,7 @@
from functools import lru_cache
from typing import Collection, Final, Iterator, List, Optional, Tuple, Union
from black.mode import Mode, Preview
from black.mode import Mode
from black.nodes import (
CLOSING_BRACKETS,
STANDALONE_COMMENT,
@ -390,22 +390,18 @@ def _contains_fmt_skip_comment(comment_line: str, mode: Mode) -> bool:
# noqa:XXX # fmt:skip # a nice line <-- multiple comments (Preview)
# pylint:XXX; fmt:skip <-- list of comments (; separated, Preview)
"""
semantic_comment_blocks = (
[
comment_line,
*[
_COMMENT_PREFIX + comment.strip()
for comment in comment_line.split(_COMMENT_PREFIX)[1:]
],
*[
_COMMENT_PREFIX + comment.strip()
for comment in comment_line.strip(_COMMENT_PREFIX).split(
_COMMENT_LIST_SEPARATOR
)
],
]
if Preview.single_line_format_skip_with_multiple_comments in mode
else [comment_line]
)
semantic_comment_blocks = [
comment_line,
*[
_COMMENT_PREFIX + comment.strip()
for comment in comment_line.split(_COMMENT_PREFIX)[1:]
],
*[
_COMMENT_PREFIX + comment.strip()
for comment in comment_line.strip(_COMMENT_PREFIX).split(
_COMMENT_LIST_SEPARATOR
)
],
]
return any(comment in FMT_SKIP for comment in semantic_comment_blocks)

View File

@ -115,10 +115,8 @@ def line(self, indent: int = 0) -> Iterator[Line]:
self.current_line.depth += indent
return # Line is empty, don't emit. Creating a new one unnecessary.
if (
Preview.improved_async_statements_handling in self.mode
and len(self.current_line.leaves) == 1
and is_async_stmt_or_funcdef(self.current_line.leaves[0])
if len(self.current_line.leaves) == 1 and is_async_stmt_or_funcdef(
self.current_line.leaves[0]
):
# Special case for async def/for/with statements. `visit_async_stmt`
# adds an `ASYNC` leaf then visits the child def/for/with statement
@ -164,20 +162,19 @@ def visit_default(self, node: LN) -> Iterator[Line]:
def visit_test(self, node: Node) -> Iterator[Line]:
"""Visit an `x if y else z` test"""
if Preview.parenthesize_conditional_expressions in self.mode:
already_parenthesized = (
node.prev_sibling and node.prev_sibling.type == token.LPAR
)
already_parenthesized = (
node.prev_sibling and node.prev_sibling.type == token.LPAR
)
if not already_parenthesized:
# Similar to logic in wrap_in_parentheses
lpar = Leaf(token.LPAR, "")
rpar = Leaf(token.RPAR, "")
prefix = node.prefix
node.prefix = ""
lpar.prefix = prefix
node.insert_child(0, lpar)
node.append_child(rpar)
if not already_parenthesized:
# Similar to logic in wrap_in_parentheses
lpar = Leaf(token.LPAR, "")
rpar = Leaf(token.RPAR, "")
prefix = node.prefix
node.prefix = ""
lpar.prefix = prefix
node.insert_child(0, lpar)
node.append_child(rpar)
yield from self.visit_default(node)
@ -292,9 +289,7 @@ def visit_match_case(self, node: Node) -> Iterator[Line]:
def visit_suite(self, node: Node) -> Iterator[Line]:
"""Visit a suite."""
if (
self.mode.is_pyi or Preview.dummy_implementations in self.mode
) and is_stub_suite(node, self.mode):
if is_stub_suite(node):
yield from self.visit(node.children[2])
else:
yield from self.visit_default(node)
@ -308,11 +303,7 @@ def visit_simple_stmt(self, node: Node) -> Iterator[Line]:
prev_type = child.type
if node.parent and node.parent.type in STATEMENT:
if Preview.dummy_implementations in self.mode:
condition = is_parent_function_or_class(node)
else:
condition = self.mode.is_pyi
if condition and is_stub_body(node):
if is_parent_function_or_class(node) and is_stub_body(node):
yield from self.visit_default(node)
else:
yield from self.line(+1)
@ -320,11 +311,7 @@ def visit_simple_stmt(self, node: Node) -> Iterator[Line]:
yield from self.line(-1)
else:
if (
not (self.mode.is_pyi or Preview.dummy_implementations in self.mode)
or not node.parent
or not is_stub_suite(node.parent, self.mode)
):
if not node.parent or not is_stub_suite(node.parent):
yield from self.line()
yield from self.visit_default(node)
@ -342,11 +329,7 @@ def visit_async_stmt(self, node: Node) -> Iterator[Line]:
break
internal_stmt = next(children)
if Preview.improved_async_statements_handling in self.mode:
yield from self.visit(internal_stmt)
else:
for child in internal_stmt.children:
yield from self.visit(child)
yield from self.visit(internal_stmt)
def visit_decorators(self, node: Node) -> Iterator[Line]:
"""Visit decorators."""
@ -420,10 +403,9 @@ def foo(a: int, b: float = 7): ...
def foo(a: (int), b: (float) = 7): ...
"""
if Preview.parenthesize_long_type_hints in self.mode:
assert len(node.children) == 3
if maybe_make_parens_invisible_in_atom(node.children[2], parent=node):
wrap_in_parentheses(node, node.children[2], visible=False)
assert len(node.children) == 3
if maybe_make_parens_invisible_in_atom(node.children[2], parent=node):
wrap_in_parentheses(node, node.children[2], visible=False)
yield from self.visit_default(node)
@ -529,13 +511,7 @@ def __post_init__(self) -> None:
self.visit_with_stmt = partial(v, keywords={"with"}, parens={"with"})
self.visit_classdef = partial(v, keywords={"class"}, parens=Ø)
# When this is moved out of preview, add ":" directly to ASSIGNMENTS in nodes.py
if Preview.parenthesize_long_type_hints in self.mode:
assignments = ASSIGNMENTS | {":"}
else:
assignments = ASSIGNMENTS
self.visit_expr_stmt = partial(v, keywords=Ø, parens=assignments)
self.visit_expr_stmt = partial(v, keywords=Ø, parens=ASSIGNMENTS)
self.visit_return_stmt = partial(v, keywords={"return"}, parens={"return"})
self.visit_import_from = partial(v, keywords=Ø, parens={"import"})
self.visit_del_stmt = partial(v, keywords=Ø, parens={"del"})
@ -576,9 +552,7 @@ def transform_line(
# We need the line string when power operators are hugging to determine if we should
# split the line. Default to line_str, if no power operator are present on the line.
line_str_hugging_power_ops = (
(_hugging_power_ops_line_to_string(line, features, mode) or line_str)
if Preview.fix_power_op_line_length in mode
else line_str
_hugging_power_ops_line_to_string(line, features, mode) or line_str
)
ll = mode.line_length
@ -688,9 +662,6 @@ def should_split_funcdef_with_rhs(line: Line, mode: Mode) -> bool:
"""If a funcdef has a magic trailing comma in the return type, then we should first
split the line with rhs to respect the comma.
"""
if Preview.respect_magic_trailing_comma_in_return_type not in mode:
return False
return_type_leaves: List[Leaf] = []
in_return_type = False
@ -919,9 +890,6 @@ def _maybe_split_omitting_optional_parens(
try:
# The RHSResult Omitting Optional Parens.
rhs_oop = _first_right_hand_split(line, omit=omit)
prefer_splitting_rhs_mode = (
Preview.prefer_splitting_right_hand_side_of_assignments in line.mode
)
is_split_right_after_equal = (
len(rhs.head.leaves) >= 2 and rhs.head.leaves[-2].type == token.EQUAL
)
@ -937,8 +905,7 @@ def _maybe_split_omitting_optional_parens(
)
if (
not (
prefer_splitting_rhs_mode
and is_split_right_after_equal
is_split_right_after_equal
and rhs_head_contains_brackets
and rhs_head_short_enough
and rhs_head_explode_blocked_by_magic_trailing_comma
@ -1224,11 +1191,7 @@ def append_to_line(leaf: Leaf) -> Iterator[Line]:
trailing_comma_safe and Feature.TRAILING_COMMA_IN_CALL in features
)
if (
Preview.add_trailing_comma_consistently in mode
and last_leaf.type == STANDALONE_COMMENT
and leaf_idx == last_non_comment_leaf
):
if last_leaf.type == STANDALONE_COMMENT and leaf_idx == last_non_comment_leaf:
current_line = _safe_add_trailing_comma(
trailing_comma_safe, delimiter_priority, current_line
)
@ -1315,11 +1278,7 @@ def normalize_invisible_parens( # noqa: C901
# Fixes a bug where invisible parens are not properly wrapped around
# case blocks.
if (
isinstance(child, Node)
and child.type == syms.case_block
and Preview.long_case_block_line_splitting in mode
):
if isinstance(child, Node) and child.type == syms.case_block:
normalize_invisible_parens(
child, parens_after={"case"}, mode=mode, features=features
)
@ -1374,7 +1333,6 @@ def normalize_invisible_parens( # noqa: C901
and child.next_sibling is not None
and child.next_sibling.type == token.COLON
and child.value == "case"
and Preview.long_case_block_line_splitting in mode
):
# A special patch for "case case:" scenario, the second occurrence
# of case will be not parsed as a Python keyword.
@ -1448,7 +1406,6 @@ def _maybe_wrap_cms_in_parens(
"""
if (
Feature.PARENTHESIZED_CONTEXT_MANAGERS not in features
or Preview.wrap_multiple_context_managers_in_parens not in mode
or len(node.children) <= 2
# If it's an atom, it's already wrapped in parens.
or node.children[1].type == syms.atom

View File

@ -202,9 +202,7 @@ def _is_triple_quoted_string(self) -> bool:
value = self.leaves[0].value
if value.startswith(('"""', "'''")):
return True
if Preview.accept_raw_docstrings in self.mode and value.startswith(
("r'''", 'r"""', "R'''", 'R"""')
):
if value.startswith(("r'''", 'r"""', "R'''", 'R"""')):
return True
return False
@ -450,14 +448,8 @@ def is_complex_subscript(self, leaf: Leaf) -> bool:
if subscript_start.type == syms.subscriptlist:
subscript_start = child_towards(subscript_start, leaf)
# When this is moved out of preview, add syms.namedexpr_test directly to
# TEST_DESCENDANTS in nodes.py
if Preview.walrus_subscript in self.mode:
test_decendants = TEST_DESCENDANTS | {syms.namedexpr_test}
else:
test_decendants = TEST_DESCENDANTS
return subscript_start is not None and any(
n.type in test_decendants for n in subscript_start.pre_order()
n.type in TEST_DESCENDANTS for n in subscript_start.pre_order()
)
def enumerate_with_length(
@ -567,8 +559,7 @@ def maybe_empty_lines(self, current_line: Line) -> LinesBlock:
lines (two on module-level).
"""
form_feed = (
Preview.allow_form_feeds in self.mode
and current_line.depth == 0
current_line.depth == 0
and bool(current_line.leaves)
and "\f\n" in current_line.leaves[0].prefix
)
@ -582,8 +573,7 @@ def maybe_empty_lines(self, current_line: Line) -> LinesBlock:
else before - previous_after
)
if (
Preview.module_docstring_newlines in current_line.mode
and self.previous_block
self.previous_block
and self.previous_block.previous_block is None
and len(self.previous_block.original_line.leaves) == 1
and self.previous_block.original_line.is_docstring
@ -640,11 +630,7 @@ def _maybe_empty_lines(self, current_line: Line) -> Tuple[int, int]:
if previous_def is not None:
assert self.previous_line is not None
if self.mode.is_pyi:
if (
Preview.blank_line_after_nested_stub_class in self.mode
and previous_def.is_class
and not previous_def.is_stub_class
):
if previous_def.is_class and not previous_def.is_stub_class:
before = 1
elif depth and not current_line.is_def and self.previous_line.is_def:
# Empty lines between attributes and methods should be preserved.
@ -695,18 +681,12 @@ def _maybe_empty_lines(self, current_line: Line) -> Tuple[int, int]:
and self.previous_line.is_class
and current_line.is_docstring
):
if Preview.no_blank_line_before_class_docstring in current_line.mode:
return 0, 1
return before, 1
return 0, 1
# In preview mode, always allow blank lines, except right before a function
# docstring
is_empty_first_line_ok = (
Preview.allow_empty_first_line_in_block in current_line.mode
and (
not current_line.is_docstring
or (self.previous_line and not self.previous_line.is_def)
)
is_empty_first_line_ok = not current_line.is_docstring or (
self.previous_line and not self.previous_line.is_def
)
if (
@ -736,7 +716,7 @@ def _maybe_empty_lines_for_class_or_def( # noqa: C901
if self.previous_line.depth < current_line.depth and (
self.previous_line.is_class or self.previous_line.is_def
):
if self.mode.is_pyi or not Preview.allow_empty_first_line_in_block:
if self.mode.is_pyi:
return 0, 0
else:
return 1 if user_had_newline else 0, 0
@ -776,10 +756,7 @@ def _maybe_empty_lines_for_class_or_def( # noqa: C901
# Don't inspect the previous line if it's part of the body of the previous
# statement in the same level, we always want a blank line if there's
# something with a body preceding.
elif (
Preview.blank_line_between_nested_and_def_stub_file in current_line.mode
and self.previous_line.depth > current_line.depth
):
elif self.previous_line.depth > current_line.depth:
newlines = 1
elif (
current_line.is_def or current_line.is_decorator
@ -800,11 +777,7 @@ def _maybe_empty_lines_for_class_or_def( # noqa: C901
newlines = 1 if current_line.depth else 2
# If a user has left no space after a dummy implementation, don't insert
# new lines. This is useful for instance for @overload or Protocols.
if (
Preview.dummy_implementations in self.mode
and self.previous_line.is_stub_def
and not user_had_newline
):
if self.previous_line.is_stub_def and not user_had_newline:
newlines = 0
if comment_to_add_newlines is not None:
previous_block = comment_to_add_newlines.previous_block
@ -859,11 +832,9 @@ def is_line_short_enough( # noqa: C901
if not line_str:
line_str = line_to_string(line)
width = str_width if Preview.respect_east_asian_width in mode else len
if Preview.multiline_string_handling not in mode:
return (
width(line_str) <= mode.line_length
str_width(line_str) <= mode.line_length
and "\n" not in line_str # multiline strings
and not line.contains_standalone_comments()
)
@ -872,10 +843,10 @@ def is_line_short_enough( # noqa: C901
return False
if "\n" not in line_str:
# No multiline strings (MLS) present
return width(line_str) <= mode.line_length
return str_width(line_str) <= mode.line_length
first, *_, last = line_str.split("\n")
if width(first) > mode.line_length or width(last) > mode.line_length:
if str_width(first) > mode.line_length or str_width(last) > mode.line_length:
return False
# Traverse the AST to examine the context of the multiline string (MLS),
@ -1015,11 +986,7 @@ def can_omit_invisible_parens(
return False
if delimiter_count == 1:
if (
Preview.wrap_multiple_context_managers_in_parens in line.mode
and max_priority == COMMA_PRIORITY
and rhs.head.is_with_or_async_with_stmt
):
if max_priority == COMMA_PRIORITY and rhs.head.is_with_or_async_with_stmt:
# For two context manager with statements, the optional parentheses read
# better. In this case, `rhs.body` is the context managers part of
# the with statement. `rhs.head` is the `with (` part on the previous

View File

@ -168,35 +168,14 @@ def supports_feature(target_versions: Set[TargetVersion], feature: Feature) -> b
class Preview(Enum):
"""Individual preview style features."""
add_trailing_comma_consistently = auto()
blank_line_after_nested_stub_class = auto()
blank_line_between_nested_and_def_stub_file = auto()
hex_codes_in_unicode_sequences = auto()
improved_async_statements_handling = auto()
multiline_string_handling = auto()
no_blank_line_before_class_docstring = auto()
prefer_splitting_right_hand_side_of_assignments = auto()
# NOTE: string_processing requires wrap_long_dict_values_in_parens
# for https://github.com/psf/black/issues/3117 to be fixed.
string_processing = auto()
parenthesize_conditional_expressions = auto()
parenthesize_long_type_hints = auto()
respect_magic_trailing_comma_in_return_type = auto()
skip_magic_trailing_comma_in_subscript = auto()
wrap_long_dict_values_in_parens = auto()
wrap_multiple_context_managers_in_parens = auto()
dummy_implementations = auto()
walrus_subscript = auto()
module_docstring_newlines = auto()
accept_raw_docstrings = auto()
fix_power_op_line_length = auto()
hug_parens_with_braces_and_square_brackets = auto()
allow_empty_first_line_in_block = auto()
single_line_format_skip_with_multiple_comments = auto()
long_case_block_line_splitting = auto()
allow_form_feeds = auto()
unify_docstring_detection = auto()
respect_east_asian_width = auto()
wrap_long_dict_values_in_parens = auto()
multiline_string_handling = auto()
class Deprecated(UserWarning):

View File

@ -104,6 +104,7 @@
syms.trailer,
syms.term,
syms.power,
syms.namedexpr_test,
}
TYPED_NAMES: Final = {syms.tname, syms.tname_star}
ASSIGNMENTS: Final = {
@ -121,6 +122,7 @@
">>=",
"**=",
"//=",
":",
}
IMPLICIT_TUPLE: Final = {syms.testlist, syms.testlist_star_expr, syms.exprlist}
@ -346,9 +348,7 @@ def whitespace(leaf: Leaf, *, complex_subscript: bool, mode: Mode) -> str: # no
return NO
elif Preview.walrus_subscript in mode and (
t == token.COLONEQUAL or prev.type == token.COLONEQUAL
):
elif t == token.COLONEQUAL or prev.type == token.COLONEQUAL:
return SPACE
elif not complex_subscript:
@ -753,13 +753,9 @@ def is_function_or_class(node: Node) -> bool:
return node.type in {syms.funcdef, syms.classdef, syms.async_funcdef}
def is_stub_suite(node: Node, mode: Mode) -> bool:
def is_stub_suite(node: Node) -> bool:
"""Return True if `node` is a suite with a stub body."""
if (
node.parent is not None
and Preview.dummy_implementations in mode
and not is_parent_function_or_class(node)
):
if node.parent is not None and not is_parent_function_or_class(node):
return False
# If there is a comment, we want to keep it.

View File

@ -1,4 +1,3 @@
# flags: --preview
def foo():
"""
Docstring

View File

@ -1,4 +1,3 @@
# flags: --preview
async def func() -> (int):
return 0

View File

@ -45,8 +45,7 @@ def wat():
@deco2(with_args=True)
# leading 3
@deco3
def decorated1():
...
def decorated1(): ...
# leading 1
@ -54,8 +53,7 @@ def decorated1():
# leading 2
@deco2(with_args=True)
# leading function comment
def decorated1():
...
def decorated1(): ...
# Note: this is fixed in
@ -65,8 +63,7 @@ def decorated1():
# This comment should be split from `some_instruction` by two lines but isn't.
def g():
...
def g(): ...
if __name__ == "__main__":

View File

@ -1,4 +1,3 @@
# flags: --preview
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,
@ -197,7 +196,9 @@ def foo(wait: bool = True):
time.sleep(1) if wait else None
a = "".join((
"", # comment
"" if True else "",
))
a = "".join(
(
"", # comment
"" if True else "",
)
)

View File

@ -1,4 +1,4 @@
# flags: --preview --minimum-version=3.8
# flags: --minimum-version=3.8
with \
make_context_manager1() as cm1, \
make_context_manager2() as cm2, \

View File

@ -1,4 +1,4 @@
# flags: --preview --minimum-version=3.9
# flags: --minimum-version=3.9
with \
make_context_manager1() as cm1, \
make_context_manager2() as cm2, \

View File

@ -1,4 +1,4 @@
# flags: --preview --minimum-version=3.10
# flags: --minimum-version=3.10
# This file uses pattern matching introduced in Python 3.10.

View File

@ -1,4 +1,4 @@
# flags: --preview --minimum-version=3.11
# flags: --minimum-version=3.11
# This file uses except* clause in Python 3.11.

View File

@ -1,4 +1,3 @@
# flags: --preview
# This file doesn't use any Python 3.9+ only grammars.

View File

@ -1,4 +1,4 @@
# flags: --preview --minimum-version=3.9
# flags: --minimum-version=3.9
# This file uses parenthesized context managers introduced in Python 3.9.

View File

@ -1,4 +1,3 @@
# flags: --preview
from typing import NoReturn, Protocol, Union, overload
class Empty:

View File

@ -119,6 +119,7 @@ def f():
if not prev:
prevp = preceding_leaf(p)
if not prevp or prevp.type in OPENING_BRACKETS:
return NO
if prevp.type == token.EQUAL:

View File

@ -243,12 +243,8 @@ def spaces_types(
g: int = 1 if False else 2,
h: str = "",
i: str = r"",
):
...
def spaces2(result=_core.Value(None)):
...
): ...
def spaces2(result=_core.Value(None)): ...
something = {

View File

@ -161,8 +161,7 @@ def this_wont_be_formatted ( self ) -> str: ...
class Factory(t.Protocol):
def this_will_be_formatted(self, **kwargs) -> Named:
...
def this_will_be_formatted(self, **kwargs) -> Named: ...
# fmt: on

View File

@ -1,4 +1,3 @@
# flags: --preview
# Warning! This file contains form feeds (ASCII 0x0C, often represented by \f or ^L).

View File

@ -158,10 +158,7 @@ def spaces_types(
g: int = 1 if False else 2,
h: str = "",
i: str = r"",
):
...
): ...
def spaces2(result=_core.Value(None)):
assert fut is self._read_fut, (fut, self._read_fut)

View File

@ -21,15 +21,21 @@ def http_status(status):
# output
def http_status(status):
match status:
case 400:
return "Bad request"
case 401:
return "Unauthorized"
case 403:
return "Forbidden"
case 404:
return "Not found"

View File

@ -43,8 +43,10 @@
% (
"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),
): (
"This is a really really really long string that has to go inside of a dictionary. It is %s bad (#%d)."
% ("soooo", 2)
),
}
func_with_keywords(
@ -254,10 +256,12 @@
+ 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 \\\\"

View File

@ -1,4 +1,3 @@
# flags: --preview
"""Single line module-level docstring should be followed by single newline."""

View File

@ -1,7 +1,7 @@
# flags: --preview
"""I am a very helpful module docstring.
With trailing spaces:
With trailing spaces (only removed with unify_docstring_detection on):
Lorem ipsum dolor sit amet, consectetur adipiscing elit,
sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
Ut enim ad minim veniam,
@ -39,7 +39,7 @@
# output
"""I am a very helpful module docstring.
With trailing spaces:
With trailing spaces (only removed with unify_docstring_detection on):
Lorem ipsum dolor sit amet, consectetur adipiscing elit,
sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
Ut enim ad minim veniam,

View File

@ -1,4 +1,3 @@
# flags: --preview
"""Single line module-level docstring should be followed by single newline."""
a = 1

View File

@ -1,4 +1,3 @@
# flags: --preview
"""Single line module-level docstring should be followed by single newline."""
a = 1

View File

@ -1,4 +1,3 @@
# flags: --preview
"""Two blank lines between module docstring and a class."""
class MyClass:
pass

View File

@ -1,4 +1,3 @@
# flags: --preview
"""Two blank lines between module docstring and a function def."""
def function():
pass

View File

@ -1,4 +1,4 @@
# flags: --pyi --preview
# flags: --pyi
import sys
class Outer:

View File

@ -1,4 +1,3 @@
# flags: --preview
def line_before_docstring():
"""Please move me up"""
@ -63,4 +62,5 @@ class MultilineDocstringsAsWell:
class SingleQuotedDocstring:
"I'm a docstring but I don't even get triple quotes."

View File

@ -1,4 +1,4 @@
# flags: --preview --minimum-version=3.10
# flags: --minimum-version=3.10
match x:
case "abcd" | "abcd" | "abcd" :
pass

View File

@ -1,4 +1,4 @@
# flags: --preview --minimum-version=3.10
# flags: --minimum-version=3.10
match maybe, multiple:
case perhaps, 5:
pass

View File

@ -1,4 +1,4 @@
# flags: --preview --minimum-version=3.10
# flags: --minimum-version=3.10
# This has always worked
z= Loooooooooooooooooooooooong | Loooooooooooooooooooooooong | Loooooooooooooooooooooooong | Loooooooooooooooooooooooong

View File

@ -1,8 +1,8 @@
# flags: --minimum-version=3.10
# Unparenthesized walruses are now allowed in indices since Python 3.10.
x[a:=0]
x[a:=0, b:=1]
x[5, b:=0]
x[a := 0]
x[a := 0, b := 1]
x[5, b := 0]
# Walruses are allowed inside generator expressions on function calls since 3.10.
if any(match := pattern_error.match(s) for s in buffer):

View File

@ -96,18 +96,16 @@ async def await_the_walrus():
foo(x=(y := f(x)))
def foo(answer=(p := 42)):
...
def foo(answer=(p := 42)): ...
def foo2(answer: (p := 42) = 5):
...
def foo2(answer: (p := 42) = 5): ...
lambda: (x := 1)
a[(x := 12)]
a[:(x := 13)]
a[: (x := 13)]
# we don't touch expressions in f-strings but if we do one day, don't break 'em
f"{(x:=10)}"

View File

@ -1,4 +1,3 @@
# flags: --preview
x[(a:=0):]
x[:(a:=0)]

View File

@ -1,4 +1,3 @@
# flags: --preview
("" % a) ** 2
("" % a)[0]
("" % a)()
@ -31,9 +30,9 @@
2 // ("" % a)
2 % ("" % a)
+("" % a)
b + "" % a
b + ("" % a)
-("" % a)
b - "" % a
b - ("" % a)
b + -("" % a)
~("" % a)
2 ** ("" % a)

View File

@ -1,4 +1,3 @@
# flags: --preview
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

View File

@ -1,4 +1,3 @@
# flags: --preview
first_item, second_item = (
some_looooooooong_module.some_looooooooooooooong_function_name(
first_argument, second_argument, third_argument

View File

@ -1,4 +1,4 @@
# flags: --preview --minimum-version=3.10
# flags: --minimum-version=3.10
x[a:=0]
x[a := 0]
x[a := 0, b := 1]

View File

@ -15,19 +15,16 @@ def f():
# output
@relaxed_decorator[0]
def f():
...
def f(): ...
@relaxed_decorator[
extremely_long_name_that_definitely_will_not_fit_on_one_line_of_standard_length
]
def f():
...
def f(): ...
@extremely_long_variable_name_that_doesnt_fit := complex.expression(
with_long="arguments_value_that_wont_fit_at_the_end_of_the_line"
)
def f():
...
def f(): ...

View File

@ -1,4 +1,4 @@
# flags: --preview --skip-string-normalization
# flags: --skip-string-normalization
class C:
r"""Raw"""

View File

@ -1,4 +1,4 @@
# flags: --preview --skip-string-normalization
# flags: --skip-string-normalization
def do_not_touch_this_prefix():
R"""There was a bug where docstring prefixes would be normalized even with -S."""

View File

@ -3,14 +3,14 @@
def foo1():
print("The newline above me should be deleted!")
print("The newline above me should be kept!")
def foo2():
print("All the newlines above me should be deleted!")
print("All the newlines above me should be kept!")
def foo3():
@ -30,31 +30,31 @@ def foo4():
class Foo:
def bar(self):
print("The newline above me should be deleted!")
print("The newline above me should be kept!")
for i in range(5):
print(f"{i}) The line above me should be removed!")
print(f"{i}) The line above me should be kept!")
for i in range(5):
print(f"{i}) The lines above me should be removed!")
print(f"{i}) The lines above me should be kept!")
for i in range(5):
for j in range(7):
print(f"{i}) The lines above me should be removed!")
print(f"{i}) The lines above me should be kept!")
if random.randint(0, 3) == 0:
print("The new line above me is about to be removed!")
print("The new line above me will be kept!")
if random.randint(0, 3) == 0:
@ -62,43 +62,45 @@ def bar(self):
print("The new lines above me is about to be removed!")
print("The new lines above me will be kept!")
if random.randint(0, 3) == 0:
if random.uniform(0, 1) > 0.5:
print("Two lines above me are about to be removed!")
print("Two lines above me will be kept!")
while True:
print("The newline above me should be deleted!")
print("The newline above me should be kept!")
while True:
print("The newlines above me should be deleted!")
print("The newlines above me should be kept!")
while True:
while False:
print("The newlines above me should be deleted!")
print("The newlines above me should be kept!")
with open("/path/to/file.txt", mode="w") as file:
file.write("The new line above me is about to be removed!")
file.write("The new line above me will be kept!")
with open("/path/to/file.txt", mode="w") as file:
file.write("The new lines above me is about to be removed!")
file.write("The new lines above me will be kept!")
with open("/path/to/file.txt", mode="r") as read_file:
@ -113,20 +115,24 @@ def bar(self):
def foo1():
print("The newline above me should be deleted!")
print("The newline above me should be kept!")
def foo2():
print("All the newlines above me should be deleted!")
print("All the newlines above me should be kept!")
def foo3():
print("No newline above me!")
print("There is a newline above me, and that's OK!")
def foo4():
# There is a comment here
print("The newline above me should not be deleted!")
@ -134,56 +140,73 @@ def foo4():
class Foo:
def bar(self):
print("The newline above me should be deleted!")
print("The newline above me should be kept!")
for i in range(5):
print(f"{i}) The line above me should be removed!")
print(f"{i}) The line above me should be kept!")
for i in range(5):
print(f"{i}) The lines above me should be removed!")
print(f"{i}) The lines above me should be kept!")
for i in range(5):
for j in range(7):
print(f"{i}) The lines above me should be removed!")
print(f"{i}) The lines above me should be kept!")
if random.randint(0, 3) == 0:
print("The new line above me is about to be removed!")
print("The new line above me will be kept!")
if random.randint(0, 3) == 0:
print("The new lines above me is about to be removed!")
print("The new lines above me will be kept!")
if random.randint(0, 3) == 0:
if random.uniform(0, 1) > 0.5:
print("Two lines above me are about to be removed!")
print("Two lines above me will be kept!")
while True:
print("The newline above me should be deleted!")
print("The newline above me should be kept!")
while True:
print("The newlines above me should be deleted!")
print("The newlines above me should be kept!")
while True:
while False:
print("The newlines above me should be deleted!")
print("The newlines above me should be kept!")
with open("/path/to/file.txt", mode="w") as file:
file.write("The new line above me is about to be removed!")
file.write("The new line above me will be kept!")
with open("/path/to/file.txt", mode="w") as file:
file.write("The new lines above me is about to be removed!")
file.write("The new lines above me will be kept!")
with open("/path/to/file.txt", mode="r") as read_file:
with open("/path/to/output_file.txt", mode="w") as write_file:
write_file.writelines(read_file.readlines())

View File

@ -88,7 +88,6 @@ 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
@ -194,30 +193,27 @@ def foo() -> tuple[int, int, int]:
return 2
def foo() -> (
tuple[
loooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong,
loooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong,
loooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong,
]
):
def foo() -> tuple[
loooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong,
loooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong,
loooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong,
]:
return 2
# Magic trailing comma example
def foo() -> (
tuple[
int,
int,
int,
]
):
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,]:
def foo(a, b) -> tuple[
int,
int,
int,
]:
return 2

View File

@ -1,4 +1,3 @@
# flags: --preview
foo = 123 # fmt: skip # noqa: E501 # pylint
bar = (
123 ,

View File

@ -1,4 +1,3 @@
# flags: --preview
e = {
"a": fun(msg, "ts"),
"longggggggggggggggid": ...,

View File

@ -1,7 +1,9 @@
# flags: --preview
# This is testing an issue that is specific to the preview style
{
"is_update": (up := commit.hash in update_hashes)
}
# output
# This is testing an issue that is specific to the preview style
{"is_update": (up := commit.hash in update_hashes)}