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 --> <!-- 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 ### Stable style
<!-- Changes that affect Black's stable style --> <!-- Changes that affect Black's stable style -->
### Preview style Several bug fixes were made in features that are moved to the stable style in this
release:
<!-- Changes that affect Black's preview style -->
- Fix comment handling when parenthesising conditional expressions (#4134) - 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, - Fix bug where spaces were not added around parenthesized walruses in subscripts,
unlike other binary operators (#4109) unlike other binary operators (#4109)
- Remove empty lines before docstrings in async functions (#4132) - 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 - Address a missing case in the change to allow empty lines at the beginning of all
blocks, except immediately before a docstring (#4130) blocks, except immediately before a docstring (#4130)
- For stubs, fix logic to enforce empty line after nested classes with bodies (#4141) - 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 crash when using a walrus in a dictionary (#4155)
- Fix unnecessary parentheses when wrapping long dicts (#4135) - Fix unnecessary parentheses when wrapping long dicts (#4135)

View File

@ -3,7 +3,7 @@
from functools import lru_cache from functools import lru_cache
from typing import Collection, Final, Iterator, List, Optional, Tuple, Union 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 ( from black.nodes import (
CLOSING_BRACKETS, CLOSING_BRACKETS,
STANDALONE_COMMENT, 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) # noqa:XXX # fmt:skip # a nice line <-- multiple comments (Preview)
# pylint:XXX; fmt:skip <-- list of comments (; separated, Preview) # pylint:XXX; fmt:skip <-- list of comments (; separated, Preview)
""" """
semantic_comment_blocks = ( semantic_comment_blocks = [
[ comment_line,
comment_line, *[
*[ _COMMENT_PREFIX + comment.strip()
_COMMENT_PREFIX + comment.strip() for comment in comment_line.split(_COMMENT_PREFIX)[1:]
for comment in comment_line.split(_COMMENT_PREFIX)[1:] ],
], *[
*[ _COMMENT_PREFIX + comment.strip()
_COMMENT_PREFIX + comment.strip() for comment in comment_line.strip(_COMMENT_PREFIX).split(
for comment in comment_line.strip(_COMMENT_PREFIX).split( _COMMENT_LIST_SEPARATOR
_COMMENT_LIST_SEPARATOR )
) ],
], ]
]
if Preview.single_line_format_skip_with_multiple_comments in mode
else [comment_line]
)
return any(comment in FMT_SKIP for comment in semantic_comment_blocks) 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 self.current_line.depth += indent
return # Line is empty, don't emit. Creating a new one unnecessary. return # Line is empty, don't emit. Creating a new one unnecessary.
if ( if len(self.current_line.leaves) == 1 and is_async_stmt_or_funcdef(
Preview.improved_async_statements_handling in self.mode self.current_line.leaves[0]
and 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` # Special case for async def/for/with statements. `visit_async_stmt`
# adds an `ASYNC` leaf then visits the child def/for/with statement # 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]: def visit_test(self, node: Node) -> Iterator[Line]:
"""Visit an `x if y else z` test""" """Visit an `x if y else z` test"""
if Preview.parenthesize_conditional_expressions in self.mode: already_parenthesized = (
already_parenthesized = ( node.prev_sibling and node.prev_sibling.type == token.LPAR
node.prev_sibling and node.prev_sibling.type == token.LPAR )
)
if not already_parenthesized: if not already_parenthesized:
# Similar to logic in wrap_in_parentheses # Similar to logic in wrap_in_parentheses
lpar = Leaf(token.LPAR, "") lpar = Leaf(token.LPAR, "")
rpar = Leaf(token.RPAR, "") rpar = Leaf(token.RPAR, "")
prefix = node.prefix prefix = node.prefix
node.prefix = "" node.prefix = ""
lpar.prefix = prefix lpar.prefix = prefix
node.insert_child(0, lpar) node.insert_child(0, lpar)
node.append_child(rpar) node.append_child(rpar)
yield from self.visit_default(node) 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]: def visit_suite(self, node: Node) -> Iterator[Line]:
"""Visit a suite.""" """Visit a suite."""
if ( if is_stub_suite(node):
self.mode.is_pyi or Preview.dummy_implementations in self.mode
) and is_stub_suite(node, self.mode):
yield from self.visit(node.children[2]) yield from self.visit(node.children[2])
else: else:
yield from self.visit_default(node) yield from self.visit_default(node)
@ -308,11 +303,7 @@ def visit_simple_stmt(self, node: Node) -> Iterator[Line]:
prev_type = child.type prev_type = child.type
if node.parent and node.parent.type in STATEMENT: if node.parent and node.parent.type in STATEMENT:
if Preview.dummy_implementations in self.mode: if is_parent_function_or_class(node) and is_stub_body(node):
condition = is_parent_function_or_class(node)
else:
condition = self.mode.is_pyi
if condition and is_stub_body(node):
yield from self.visit_default(node) yield from self.visit_default(node)
else: else:
yield from self.line(+1) yield from self.line(+1)
@ -320,11 +311,7 @@ def visit_simple_stmt(self, node: Node) -> Iterator[Line]:
yield from self.line(-1) yield from self.line(-1)
else: else:
if ( if not node.parent or not is_stub_suite(node.parent):
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)
):
yield from self.line() yield from self.line()
yield from self.visit_default(node) yield from self.visit_default(node)
@ -342,11 +329,7 @@ def visit_async_stmt(self, node: Node) -> Iterator[Line]:
break break
internal_stmt = next(children) internal_stmt = next(children)
if Preview.improved_async_statements_handling in self.mode: yield from self.visit(internal_stmt)
yield from self.visit(internal_stmt)
else:
for child in internal_stmt.children:
yield from self.visit(child)
def visit_decorators(self, node: Node) -> Iterator[Line]: def visit_decorators(self, node: Node) -> Iterator[Line]:
"""Visit decorators.""" """Visit decorators."""
@ -420,10 +403,9 @@ def foo(a: int, b: float = 7): ...
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
assert len(node.children) == 3 if maybe_make_parens_invisible_in_atom(node.children[2], parent=node):
if maybe_make_parens_invisible_in_atom(node.children[2], parent=node): wrap_in_parentheses(node, node.children[2], visible=False)
wrap_in_parentheses(node, node.children[2], visible=False)
yield from self.visit_default(node) 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_with_stmt = partial(v, keywords={"with"}, parens={"with"})
self.visit_classdef = partial(v, keywords={"class"}, parens=Ø) self.visit_classdef = partial(v, keywords={"class"}, parens=Ø)
# When this is moved out of preview, add ":" directly to ASSIGNMENTS in nodes.py self.visit_expr_stmt = partial(v, keywords=Ø, parens=ASSIGNMENTS)
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_return_stmt = partial(v, keywords={"return"}, parens={"return"}) self.visit_return_stmt = partial(v, keywords={"return"}, parens={"return"})
self.visit_import_from = partial(v, keywords=Ø, parens={"import"}) self.visit_import_from = partial(v, keywords=Ø, parens={"import"})
self.visit_del_stmt = partial(v, keywords=Ø, parens={"del"}) 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 # 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. # split the line. Default to line_str, if no power operator are present on the line.
line_str_hugging_power_ops = ( line_str_hugging_power_ops = (
(_hugging_power_ops_line_to_string(line, features, mode) or line_str) _hugging_power_ops_line_to_string(line, features, mode) or line_str
if Preview.fix_power_op_line_length in mode
else line_str
) )
ll = mode.line_length 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 """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. 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] = [] return_type_leaves: List[Leaf] = []
in_return_type = False in_return_type = False
@ -919,9 +890,6 @@ def _maybe_split_omitting_optional_parens(
try: try:
# The RHSResult Omitting Optional Parens. # The RHSResult Omitting Optional Parens.
rhs_oop = _first_right_hand_split(line, omit=omit) 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 = ( is_split_right_after_equal = (
len(rhs.head.leaves) >= 2 and rhs.head.leaves[-2].type == token.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 ( if (
not ( not (
prefer_splitting_rhs_mode is_split_right_after_equal
and is_split_right_after_equal
and rhs_head_contains_brackets and rhs_head_contains_brackets
and rhs_head_short_enough and rhs_head_short_enough
and rhs_head_explode_blocked_by_magic_trailing_comma 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 trailing_comma_safe and Feature.TRAILING_COMMA_IN_CALL in features
) )
if ( if last_leaf.type == STANDALONE_COMMENT and leaf_idx == last_non_comment_leaf:
Preview.add_trailing_comma_consistently in mode
and last_leaf.type == STANDALONE_COMMENT
and leaf_idx == last_non_comment_leaf
):
current_line = _safe_add_trailing_comma( current_line = _safe_add_trailing_comma(
trailing_comma_safe, delimiter_priority, current_line 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 # Fixes a bug where invisible parens are not properly wrapped around
# case blocks. # case blocks.
if ( if isinstance(child, Node) and child.type == syms.case_block:
isinstance(child, Node)
and child.type == syms.case_block
and Preview.long_case_block_line_splitting in mode
):
normalize_invisible_parens( normalize_invisible_parens(
child, parens_after={"case"}, mode=mode, features=features 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 is not None
and child.next_sibling.type == token.COLON and child.next_sibling.type == token.COLON
and child.value == "case" and child.value == "case"
and Preview.long_case_block_line_splitting in mode
): ):
# A special patch for "case case:" scenario, the second occurrence # A special patch for "case case:" scenario, the second occurrence
# of case will be not parsed as a Python keyword. # of case will be not parsed as a Python keyword.
@ -1448,7 +1406,6 @@ def _maybe_wrap_cms_in_parens(
""" """
if ( if (
Feature.PARENTHESIZED_CONTEXT_MANAGERS not in features Feature.PARENTHESIZED_CONTEXT_MANAGERS not in features
or Preview.wrap_multiple_context_managers_in_parens not in mode
or len(node.children) <= 2 or len(node.children) <= 2
# If it's an atom, it's already wrapped in parens. # If it's an atom, it's already wrapped in parens.
or node.children[1].type == syms.atom 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 value = self.leaves[0].value
if value.startswith(('"""', "'''")): if value.startswith(('"""', "'''")):
return True return True
if Preview.accept_raw_docstrings in self.mode and value.startswith( if value.startswith(("r'''", 'r"""', "R'''", 'R"""')):
("r'''", 'r"""', "R'''", 'R"""')
):
return True return True
return False return False
@ -450,14 +448,8 @@ def is_complex_subscript(self, leaf: Leaf) -> bool:
if subscript_start.type == syms.subscriptlist: if subscript_start.type == syms.subscriptlist:
subscript_start = child_towards(subscript_start, leaf) 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( 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( def enumerate_with_length(
@ -567,8 +559,7 @@ def maybe_empty_lines(self, current_line: Line) -> LinesBlock:
lines (two on module-level). lines (two on module-level).
""" """
form_feed = ( form_feed = (
Preview.allow_form_feeds in self.mode current_line.depth == 0
and current_line.depth == 0
and bool(current_line.leaves) and bool(current_line.leaves)
and "\f\n" in current_line.leaves[0].prefix 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 else before - previous_after
) )
if ( if (
Preview.module_docstring_newlines in current_line.mode self.previous_block
and self.previous_block
and self.previous_block.previous_block is None and self.previous_block.previous_block is None
and len(self.previous_block.original_line.leaves) == 1 and len(self.previous_block.original_line.leaves) == 1
and self.previous_block.original_line.is_docstring 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: if previous_def is not None:
assert self.previous_line is not None assert self.previous_line is not None
if self.mode.is_pyi: if self.mode.is_pyi:
if ( if previous_def.is_class and not previous_def.is_stub_class:
Preview.blank_line_after_nested_stub_class in self.mode
and previous_def.is_class
and not previous_def.is_stub_class
):
before = 1 before = 1
elif depth and not current_line.is_def and self.previous_line.is_def: elif depth and not current_line.is_def and self.previous_line.is_def:
# Empty lines between attributes and methods should be preserved. # 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 self.previous_line.is_class
and current_line.is_docstring and current_line.is_docstring
): ):
if Preview.no_blank_line_before_class_docstring in current_line.mode: return 0, 1
return 0, 1
return before, 1
# In preview mode, always allow blank lines, except right before a function # In preview mode, always allow blank lines, except right before a function
# docstring # docstring
is_empty_first_line_ok = ( is_empty_first_line_ok = not current_line.is_docstring or (
Preview.allow_empty_first_line_in_block in current_line.mode self.previous_line and not self.previous_line.is_def
and (
not current_line.is_docstring
or (self.previous_line and not self.previous_line.is_def)
)
) )
if ( if (
@ -736,7 +716,7 @@ def _maybe_empty_lines_for_class_or_def( # noqa: C901
if self.previous_line.depth < current_line.depth and ( if self.previous_line.depth < current_line.depth and (
self.previous_line.is_class or self.previous_line.is_def 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 return 0, 0
else: else:
return 1 if user_had_newline else 0, 0 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 # 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 # statement in the same level, we always want a blank line if there's
# something with a body preceding. # something with a body preceding.
elif ( elif self.previous_line.depth > current_line.depth:
Preview.blank_line_between_nested_and_def_stub_file in current_line.mode
and self.previous_line.depth > current_line.depth
):
newlines = 1 newlines = 1
elif ( elif (
current_line.is_def or current_line.is_decorator 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 newlines = 1 if current_line.depth else 2
# If a user has left no space after a dummy implementation, don't insert # 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. # new lines. This is useful for instance for @overload or Protocols.
if ( if self.previous_line.is_stub_def and not user_had_newline:
Preview.dummy_implementations in self.mode
and self.previous_line.is_stub_def
and not user_had_newline
):
newlines = 0 newlines = 0
if comment_to_add_newlines is not None: if comment_to_add_newlines is not None:
previous_block = comment_to_add_newlines.previous_block previous_block = comment_to_add_newlines.previous_block
@ -859,11 +832,9 @@ def is_line_short_enough( # noqa: C901
if not line_str: if not line_str:
line_str = line_to_string(line) 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: if Preview.multiline_string_handling not in mode:
return ( return (
width(line_str) <= mode.line_length str_width(line_str) <= mode.line_length
and "\n" not in line_str # multiline strings and "\n" not in line_str # multiline strings
and not line.contains_standalone_comments() and not line.contains_standalone_comments()
) )
@ -872,10 +843,10 @@ def is_line_short_enough( # noqa: C901
return False return False
if "\n" not in line_str: if "\n" not in line_str:
# No multiline strings (MLS) present # 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") 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 return False
# Traverse the AST to examine the context of the multiline string (MLS), # Traverse the AST to examine the context of the multiline string (MLS),
@ -1015,11 +986,7 @@ def can_omit_invisible_parens(
return False return False
if delimiter_count == 1: if delimiter_count == 1:
if ( if max_priority == COMMA_PRIORITY and rhs.head.is_with_or_async_with_stmt:
Preview.wrap_multiple_context_managers_in_parens in line.mode
and max_priority == COMMA_PRIORITY
and rhs.head.is_with_or_async_with_stmt
):
# For two context manager with statements, the optional parentheses read # For two context manager with statements, the optional parentheses read
# better. In this case, `rhs.body` is the context managers part of # better. In this case, `rhs.body` is the context managers part of
# the with statement. `rhs.head` is the `with (` part on the previous # 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): class Preview(Enum):
"""Individual preview style features.""" """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() 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 # NOTE: string_processing requires wrap_long_dict_values_in_parens
# for https://github.com/psf/black/issues/3117 to be fixed. # for https://github.com/psf/black/issues/3117 to be fixed.
string_processing = auto() 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() 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() unify_docstring_detection = auto()
respect_east_asian_width = auto() wrap_long_dict_values_in_parens = auto()
multiline_string_handling = auto()
class Deprecated(UserWarning): class Deprecated(UserWarning):

View File

@ -104,6 +104,7 @@
syms.trailer, syms.trailer,
syms.term, syms.term,
syms.power, syms.power,
syms.namedexpr_test,
} }
TYPED_NAMES: Final = {syms.tname, syms.tname_star} TYPED_NAMES: Final = {syms.tname, syms.tname_star}
ASSIGNMENTS: Final = { ASSIGNMENTS: Final = {
@ -121,6 +122,7 @@
">>=", ">>=",
"**=", "**=",
"//=", "//=",
":",
} }
IMPLICIT_TUPLE: Final = {syms.testlist, syms.testlist_star_expr, syms.exprlist} 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 return NO
elif Preview.walrus_subscript in mode and ( elif t == token.COLONEQUAL or prev.type == token.COLONEQUAL:
t == token.COLONEQUAL or prev.type == token.COLONEQUAL
):
return SPACE return SPACE
elif not complex_subscript: 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} 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.""" """Return True if `node` is a suite with a stub body."""
if ( if node.parent is not None and not is_parent_function_or_class(node):
node.parent is not None
and Preview.dummy_implementations in mode
and not is_parent_function_or_class(node)
):
return False return False
# If there is a comment, we want to keep it. # If there is a comment, we want to keep it.

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,4 +1,4 @@
# flags: --preview --minimum-version=3.9 # flags: --minimum-version=3.9
with \ with \
make_context_manager1() as cm1, \ make_context_manager1() as cm1, \
make_context_manager2() as cm2, \ 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. # 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. # 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. # 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. # 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 from typing import NoReturn, Protocol, Union, overload
class Empty: class Empty:

View File

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

View File

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

View File

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

View File

@ -1,4 +1,3 @@
# flags: --preview
# Warning! This file contains form feeds (ASCII 0x0C, often represented by \f or ^L). # 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, g: int = 1 if False else 2,
h: str = "", h: str = "",
i: str = r"", i: str = r"",
): ): ...
...
def spaces2(result=_core.Value(None)): def spaces2(result=_core.Value(None)):
assert fut is self._read_fut, (fut, self._read_fut) assert fut is self._read_fut, (fut, self._read_fut)

View File

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

View File

@ -43,8 +43,10 @@
% ( % (
"formatted", "formatted",
"string", "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( func_with_keywords(
@ -254,10 +256,12 @@
+ CONCATENATED + CONCATENATED
+ "using the '+' operator." + "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: Final = (
annotated_variable: 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."
"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: 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 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.""" """Single line module-level docstring should be followed by single newline."""

View File

@ -1,7 +1,7 @@
# flags: --preview # flags: --preview
"""I am a very helpful module docstring. """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, Lorem ipsum dolor sit amet, consectetur adipiscing elit,
sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
Ut enim ad minim veniam, Ut enim ad minim veniam,
@ -39,7 +39,7 @@
# output # output
"""I am a very helpful module docstring. """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, Lorem ipsum dolor sit amet, consectetur adipiscing elit,
sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
Ut enim ad minim veniam, Ut enim ad minim veniam,

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,4 +1,3 @@
# flags: --preview
def line_before_docstring(): def line_before_docstring():
"""Please move me up""" """Please move me up"""
@ -63,4 +62,5 @@ class MultilineDocstringsAsWell:
class SingleQuotedDocstring: class SingleQuotedDocstring:
"I'm a docstring but I don't even get triple quotes." "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: match x:
case "abcd" | "abcd" | "abcd" : case "abcd" | "abcd" | "abcd" :
pass pass

View File

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

View File

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

View File

@ -1,8 +1,8 @@
# flags: --minimum-version=3.10 # flags: --minimum-version=3.10
# Unparenthesized walruses are now allowed in indices since Python 3.10. # Unparenthesized walruses are now allowed in indices since Python 3.10.
x[a:=0] x[a := 0]
x[a:=0, b:=1] x[a := 0, b := 1]
x[5, b:=0] x[5, b := 0]
# Walruses are allowed inside generator expressions on function calls since 3.10. # Walruses are allowed inside generator expressions on function calls since 3.10.
if any(match := pattern_error.match(s) for s in buffer): 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))) 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) lambda: (x := 1)
a[(x := 12)] 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 # we don't touch expressions in f-strings but if we do one day, don't break 'em
f"{(x:=10)}" f"{(x:=10)}"

View File

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

View File

@ -1,4 +1,3 @@
# flags: --preview
("" % a) ** 2 ("" % a) ** 2
("" % a)[0] ("" % a)[0]
("" % a)() ("" % a)()
@ -31,9 +30,9 @@
2 // ("" % a) 2 // ("" % a)
2 % ("" % a) 2 % ("" % a)
+("" % a) +("" % a)
b + "" % a b + ("" % a)
-("" % a) -("" % a)
b - "" % a b - ("" % a)
b + -("" % a) b + -("" % a)
~("" % a) ~("" % a)
2 ** ("" % 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 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 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 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 = ( first_item, second_item = (
some_looooooooong_module.some_looooooooooooooong_function_name( some_looooooooong_module.some_looooooooooooooong_function_name(
first_argument, second_argument, third_argument 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] x[a := 0]
x[a := 0, b := 1] x[a := 0, b := 1]

View File

@ -15,19 +15,16 @@ def f():
# output # output
@relaxed_decorator[0] @relaxed_decorator[0]
def f(): def f(): ...
...
@relaxed_decorator[ @relaxed_decorator[
extremely_long_name_that_definitely_will_not_fit_on_one_line_of_standard_length 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( @extremely_long_variable_name_that_doesnt_fit := complex.expression(
with_long="arguments_value_that_wont_fit_at_the_end_of_the_line" 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: class C:
r"""Raw""" r"""Raw"""

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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