- Add trailing commas to collection literals even if there's a comment after the last
entry (#3393)
+- `async def`, `async for`, and `async with` statements are now formatted consistently
+ compared to their non-async version. (#3609)
- `with` statements that contain two context managers will be consistently wrapped in
parentheses (#3589)
Visitor,
ensure_visible,
is_arith_like,
+ is_async_stmt_or_funcdef,
is_atom_with_invisible_parens,
is_docstring,
is_empty_tuple,
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])
+ ):
+ # Special case for async def/for/with statements. `visit_async_stmt`
+ # adds an `ASYNC` leaf then visits the child def/for/with statement
+ # nodes. Line yields from those nodes shouldn't treat the former
+ # `ASYNC` leaf as a complete line.
+ return
+
complete_line = self.current_line
self.current_line = Line(mode=self.mode, depth=complete_line.depth + indent)
yield complete_line
break
internal_stmt = next(children)
- for child in internal_stmt.children:
- yield from self.visit(child)
+ 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)
def visit_decorators(self, node: Node) -> Iterator[Line]:
"""Visit decorators."""
is_multiline_string,
is_one_sequence_between,
is_type_comment,
- is_with_stmt,
+ is_with_or_async_with_stmt,
replace_child,
syms,
whitespace,
return bool(self) and is_import(self.leaves[0])
@property
- def is_with_stmt(self) -> bool:
+ def is_with_or_async_with_stmt(self) -> bool:
"""Is this a with_stmt line?"""
- return bool(self) and is_with_stmt(self.leaves[0])
+ return bool(self) and is_with_or_async_with_stmt(self.leaves[0])
@property
def is_class(self) -> bool:
if (
Preview.wrap_multiple_context_managers_in_parens in line.mode
and max_priority == COMMA_PRIORITY
- and rhs.head.is_with_stmt
+ 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
add_trailing_comma_consistently = auto()
hex_codes_in_unicode_sequences = auto()
+ improved_async_statements_handling = auto()
multiline_string_handling = auto()
prefer_splitting_right_hand_side_of_assignments = auto()
# NOTE: string_processing requires wrap_long_dict_values_in_parens
)
-def is_with_stmt(leaf: Leaf) -> bool:
- """Return True if the given leaf starts a with statement."""
+def is_with_or_async_with_stmt(leaf: Leaf) -> bool:
+ """Return True if the given leaf starts a with or async with statement."""
return bool(
leaf.type == token.NAME
and leaf.value == "with"
and leaf.parent
and leaf.parent.type == syms.with_stmt
+ ) or bool(
+ leaf.type == token.ASYNC
+ and leaf.next_sibling
+ and leaf.next_sibling.type == syms.with_stmt
+ )
+
+
+def is_async_stmt_or_funcdef(leaf: Leaf) -> bool:
+ """Return True if the given leaf starts an async def/for/with statement.
+
+ Note that `async def` can be either an `async_stmt` or `async_funcdef`,
+ the latter is used when it has decorators.
+ """
+ return bool(
+ leaf.type == token.ASYNC
+ and leaf.parent
+ and leaf.parent.type in {syms.async_stmt, syms.async_funcdef}
)
--- /dev/null
+async def func() -> (int):
+ return 0
+
+
+@decorated
+async def func() -> (int):
+ return 0
+
+
+async for (item) in async_iter:
+ pass
+
+
+# output
+
+
+async def func() -> int:
+ return 0
+
+
+@decorated
+async def func() -> int:
+ return 0
+
+
+async for item in async_iter:
+ pass
pass
+async def func():
+ async with \
+ make_context_manager1() as cm1, \
+ make_context_manager2() as cm2, \
+ make_context_manager3() as cm3, \
+ make_context_manager4() as cm4 \
+ :
+ pass
+
+ async with some_function(
+ argument1, argument2, argument3="some_value"
+ ) as some_cm, some_other_function(
+ argument1, argument2, argument3="some_value"
+ ):
+ pass
+
+
# output
]
).another_method() as cmd:
pass
+
+
+async def func():
+ async with (
+ make_context_manager1() as cm1,
+ make_context_manager2() as cm2,
+ make_context_manager3() as cm3,
+ make_context_manager4() as cm4,
+ ):
+ pass
+
+ async with (
+ some_function(argument1, argument2, argument3="some_value") as some_cm,
+ some_other_function(argument1, argument2, argument3="some_value"),
+ ):
+ pass