Create the 2024 stable style (#4106)
This commit is contained in:
parent
8fe602b1fa
commit
59b9d858a3
40
CHANGES.md
40
CHANGES.md
@ -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)
|
||||
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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):
|
||||
|
@ -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.
|
||||
|
@ -1,4 +1,3 @@
|
||||
# flags: --preview
|
||||
def foo():
|
||||
"""
|
||||
Docstring
|
@ -1,4 +1,3 @@
|
||||
# flags: --preview
|
||||
async def func() -> (int):
|
||||
return 0
|
||||
|
@ -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__":
|
||||
|
@ -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 "",
|
||||
)
|
||||
)
|
||||
|
@ -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, \
|
@ -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, \
|
@ -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.
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
# flags: --preview --minimum-version=3.11
|
||||
# flags: --minimum-version=3.11
|
||||
# This file uses except* clause in Python 3.11.
|
||||
|
||||
|
@ -1,4 +1,3 @@
|
||||
# flags: --preview
|
||||
# This file doesn't use any Python 3.9+ only grammars.
|
||||
|
||||
|
@ -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.
|
||||
|
||||
|
@ -1,4 +1,3 @@
|
||||
# flags: --preview
|
||||
from typing import NoReturn, Protocol, Union, overload
|
||||
|
||||
class Empty:
|
@ -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:
|
||||
|
@ -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 = {
|
||||
|
@ -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
|
||||
|
||||
|
@ -1,4 +1,3 @@
|
||||
# flags: --preview
|
||||
|
||||
|
||||
# Warning! This file contains form feeds (ASCII 0x0C, often represented by \f or ^L).
|
@ -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)
|
||||
|
||||
|
@ -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"
|
@ -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 \\\\"
|
||||
|
@ -1,4 +1,3 @@
|
||||
# flags: --preview
|
||||
"""Single line module-level docstring should be followed by single newline."""
|
||||
|
||||
|
||||
|
@ -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,
|
||||
|
@ -1,4 +1,3 @@
|
||||
# flags: --preview
|
||||
"""Single line module-level docstring should be followed by single newline."""
|
||||
a = 1
|
||||
|
||||
|
@ -1,4 +1,3 @@
|
||||
# flags: --preview
|
||||
"""Single line module-level docstring should be followed by single newline."""
|
||||
|
||||
a = 1
|
||||
|
@ -1,4 +1,3 @@
|
||||
# flags: --preview
|
||||
"""Two blank lines between module docstring and a class."""
|
||||
class MyClass:
|
||||
pass
|
||||
|
@ -1,4 +1,3 @@
|
||||
# flags: --preview
|
||||
"""Two blank lines between module docstring and a function def."""
|
||||
def function():
|
||||
pass
|
||||
|
@ -1,4 +1,4 @@
|
||||
# flags: --pyi --preview
|
||||
# flags: --pyi
|
||||
import sys
|
||||
|
||||
class Outer:
|
||||
|
@ -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."
|
@ -1,4 +1,4 @@
|
||||
# flags: --preview --minimum-version=3.10
|
||||
# flags: --minimum-version=3.10
|
||||
match x:
|
||||
case "abcd" | "abcd" | "abcd" :
|
||||
pass
|
@ -1,4 +1,4 @@
|
||||
# flags: --preview --minimum-version=3.10
|
||||
# flags: --minimum-version=3.10
|
||||
match maybe, multiple:
|
||||
case perhaps, 5:
|
||||
pass
|
@ -1,4 +1,4 @@
|
||||
# flags: --preview --minimum-version=3.10
|
||||
# flags: --minimum-version=3.10
|
||||
# This has always worked
|
||||
z= Loooooooooooooooooooooooong | Loooooooooooooooooooooooong | Loooooooooooooooooooooooong | Loooooooooooooooooooooooong
|
||||
|
||||
|
@ -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):
|
||||
|
@ -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)}"
|
||||
|
@ -1,4 +1,3 @@
|
||||
# flags: --preview
|
||||
x[(a:=0):]
|
||||
x[:(a:=0)]
|
||||
|
@ -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)
|
@ -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
|
@ -1,4 +1,3 @@
|
||||
# flags: --preview
|
||||
first_item, second_item = (
|
||||
some_looooooooong_module.some_looooooooooooooong_function_name(
|
||||
first_argument, second_argument, third_argument
|
@ -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]
|
||||
|
@ -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(): ...
|
@ -1,4 +1,4 @@
|
||||
# flags: --preview --skip-string-normalization
|
||||
# flags: --skip-string-normalization
|
||||
class C:
|
||||
|
||||
r"""Raw"""
|
||||
|
@ -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."""
|
||||
|
@ -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())
|
||||
|
@ -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
|
||||
|
@ -1,4 +1,3 @@
|
||||
# flags: --preview
|
||||
foo = 123 # fmt: skip # noqa: E501 # pylint
|
||||
bar = (
|
||||
123 ,
|
@ -1,4 +1,3 @@
|
||||
# flags: --preview
|
||||
e = {
|
||||
"a": fun(msg, "ts"),
|
||||
"longggggggggggggggid": ...,
|
@ -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)}
|
||||
|
Loading…
Reference in New Issue
Block a user