mirror of
https://github.com/astral-sh/ruff.git
synced 2025-08-31 15:47:41 +00:00
Parenthesize multi-context managers (#9222)
This commit is contained in:
parent
fa2c37b411
commit
a06723da2b
13 changed files with 928 additions and 649 deletions
9
crates/ruff_python_formatter/resources/test/fixtures/ruff/statement/with.options.json
vendored
Normal file
9
crates/ruff_python_formatter/resources/test/fixtures/ruff/statement/with.options.json
vendored
Normal file
|
@ -0,0 +1,9 @@
|
|||
[
|
||||
{
|
||||
"target_version": "py38"
|
||||
},
|
||||
{
|
||||
"target_version": "py39",
|
||||
"preview": "enabled"
|
||||
}
|
||||
]
|
6
crates/ruff_python_formatter/resources/test/fixtures/ruff/statement/with_39.options.json
vendored
Normal file
6
crates/ruff_python_formatter/resources/test/fixtures/ruff/statement/with_39.options.json
vendored
Normal file
|
@ -0,0 +1,6 @@
|
|||
[
|
||||
{
|
||||
"target_version": "py39",
|
||||
"preview": "enabled"
|
||||
}
|
||||
]
|
88
crates/ruff_python_formatter/resources/test/fixtures/ruff/statement/with_39.py
vendored
Normal file
88
crates/ruff_python_formatter/resources/test/fixtures/ruff/statement/with_39.py
vendored
Normal file
|
@ -0,0 +1,88 @@
|
|||
if True:
|
||||
with (
|
||||
anyio.CancelScope(shield=True)
|
||||
if get_running_loop()
|
||||
else contextlib.nullcontext()
|
||||
):
|
||||
pass
|
||||
|
||||
|
||||
# Black avoids parenthesizing the with because it can make all with items fit by just breaking
|
||||
# around parentheses. We don't implement this optimisation because it makes it difficult to see where
|
||||
# the different context managers start and end.
|
||||
with cmd, xxxxxxxx.some_kind_of_method(
|
||||
some_argument=[
|
||||
"first",
|
||||
"second",
|
||||
"third",
|
||||
]
|
||||
) as cmd, another, and_more as x:
|
||||
pass
|
||||
|
||||
# Avoid parenthesizing single item context managers when splitting after the parentheses (can_omit_optional_parentheses)
|
||||
# is sufficient
|
||||
with xxxxxxxx.some_kind_of_method(
|
||||
some_argument=[
|
||||
"first",
|
||||
"second",
|
||||
"third",
|
||||
]
|
||||
).another_method(): pass
|
||||
|
||||
if True:
|
||||
with (
|
||||
anyio.CancelScope(shield=True)
|
||||
if get_running_loop()
|
||||
else contextlib.nullcontext()
|
||||
):
|
||||
pass
|
||||
|
||||
|
||||
# Black avoids parentheses here because it can make the entire with
|
||||
# header fit without requiring parentheses to do so.
|
||||
# We don't implement this optimisation because it very difficult to see where
|
||||
# the different context managers start or end.
|
||||
with cmd, xxxxxxxx.some_kind_of_method(
|
||||
some_argument=[
|
||||
"first",
|
||||
"second",
|
||||
"third",
|
||||
]
|
||||
) as cmd, another, and_more as x:
|
||||
pass
|
||||
|
||||
# Avoid parenthesizing single item context managers when splitting after the parentheses
|
||||
# is sufficient
|
||||
with xxxxxxxx.some_kind_of_method(
|
||||
some_argument=[
|
||||
"first",
|
||||
"second",
|
||||
"third",
|
||||
]
|
||||
).another_method(): pass
|
||||
|
||||
# Parenthesize the with items if it makes them fit. Breaking the binary expression isn't
|
||||
# necessary because the entire items fit just into the 88 character limit.
|
||||
with aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa + bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb as c:
|
||||
pass
|
||||
|
||||
|
||||
# Black parenthesizes this binary expression but also preserves the parentheses of the first with-item.
|
||||
# It does so because it prefers splitting already parenthesized context managers, even if it leads to more parentheses
|
||||
# like in this case.
|
||||
with (
|
||||
(
|
||||
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa + bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
|
||||
) as b,
|
||||
c as d,
|
||||
):
|
||||
pass
|
||||
|
||||
if True:
|
||||
with anyio.CancelScope(shield=True) if get_running_loop() else contextlib.nullcontext():
|
||||
pass
|
||||
|
||||
with (aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa + bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb as c):
|
||||
pass
|
||||
|
||||
|
|
@ -538,8 +538,8 @@ pub(crate) fn can_omit_optional_parentheses(expr: &Expr, context: &PyFormatConte
|
|||
false
|
||||
} else if visitor.max_precedence_count > 1 {
|
||||
false
|
||||
} else if visitor.max_precedence == OperatorPrecedence::None && expr.is_lambda_expr() {
|
||||
// Micha: This seems to exclusively apply for lambda expressions where the body ends in a subscript.
|
||||
} else if visitor.max_precedence == OperatorPrecedence::None {
|
||||
// Micha: This seems to apply for lambda expressions where the body ends in a subscript.
|
||||
// Subscripts are excluded by default because breaking them looks odd, but it seems to be fine for lambda expression.
|
||||
//
|
||||
// ```python
|
||||
|
@ -566,10 +566,19 @@ pub(crate) fn can_omit_optional_parentheses(expr: &Expr, context: &PyFormatConte
|
|||
// ]
|
||||
// )
|
||||
// ```
|
||||
//
|
||||
// Another case are method chains:
|
||||
// ```python
|
||||
// xxxxxxxx.some_kind_of_method(
|
||||
// some_argument=[
|
||||
// "first",
|
||||
// "second",
|
||||
// "third",
|
||||
// ]
|
||||
// ).another_method(a)
|
||||
// ```
|
||||
true
|
||||
} else if visitor.max_precedence == OperatorPrecedence::Attribute
|
||||
&& (expr.is_lambda_expr() || expr.is_named_expr_expr())
|
||||
{
|
||||
} else if visitor.max_precedence == OperatorPrecedence::Attribute {
|
||||
// A single method call inside a named expression (`:=`) or as the body of a lambda function:
|
||||
// ```python
|
||||
// kwargs["open_with"] = lambda path, _: fsspec.open(
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
use ruff_formatter::write;
|
||||
|
||||
use ruff_python_ast::WithItem;
|
||||
|
||||
use crate::comments::SourceComment;
|
||||
|
@ -8,6 +7,7 @@ use crate::expression::parentheses::{
|
|||
is_expression_parenthesized, parenthesized, Parentheses, Parenthesize,
|
||||
};
|
||||
use crate::prelude::*;
|
||||
use crate::preview::is_wrap_multiple_context_managers_in_parens_enabled;
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct FormatWithItem;
|
||||
|
@ -23,26 +23,49 @@ impl FormatNodeRule<WithItem> for FormatWithItem {
|
|||
let comments = f.context().comments().clone();
|
||||
let trailing_as_comments = comments.dangling(item);
|
||||
|
||||
// Prefer keeping parentheses for already parenthesized expressions over
|
||||
// parenthesizing other nodes.
|
||||
let parenthesize = if is_expression_parenthesized(
|
||||
let is_parenthesized = is_expression_parenthesized(
|
||||
context_expr.into(),
|
||||
f.context().comments().ranges(),
|
||||
f.context().source(),
|
||||
) {
|
||||
Parenthesize::IfBreaks
|
||||
} else {
|
||||
Parenthesize::IfRequired
|
||||
};
|
||||
);
|
||||
|
||||
write!(
|
||||
f,
|
||||
[maybe_parenthesize_expression(
|
||||
context_expr,
|
||||
item,
|
||||
parenthesize
|
||||
)]
|
||||
)?;
|
||||
// Remove the parentheses of the `with_items` if the with statement adds parentheses
|
||||
if f.context().node_level().is_parenthesized()
|
||||
&& is_wrap_multiple_context_managers_in_parens_enabled(f.context())
|
||||
{
|
||||
if is_parenthesized {
|
||||
// ...except if the with item is parenthesized, then use this with item as a preferred breaking point
|
||||
// or when it has comments, then parenthesize it to prevent comments from moving.
|
||||
maybe_parenthesize_expression(
|
||||
context_expr,
|
||||
item,
|
||||
Parenthesize::IfBreaksOrIfRequired,
|
||||
)
|
||||
.fmt(f)?;
|
||||
} else {
|
||||
context_expr
|
||||
.format()
|
||||
.with_options(Parentheses::Never)
|
||||
.fmt(f)?;
|
||||
}
|
||||
} else {
|
||||
// Prefer keeping parentheses for already parenthesized expressions over
|
||||
// parenthesizing other nodes.
|
||||
let parenthesize = if is_parenthesized {
|
||||
Parenthesize::IfBreaks
|
||||
} else {
|
||||
Parenthesize::IfRequired
|
||||
};
|
||||
|
||||
write!(
|
||||
f,
|
||||
[maybe_parenthesize_expression(
|
||||
context_expr,
|
||||
item,
|
||||
parenthesize
|
||||
)]
|
||||
)?;
|
||||
}
|
||||
|
||||
if let Some(optional_vars) = optional_vars {
|
||||
write!(f, [space(), token("as"), space()])?;
|
||||
|
|
|
@ -39,6 +39,15 @@ pub(crate) const fn is_no_blank_line_before_class_docstring_enabled(
|
|||
context.is_preview()
|
||||
}
|
||||
|
||||
/// Returns `true` if the [`wrap_multiple_context_managers_in_parens`](https://github.com/astral-sh/ruff/issues/8889) preview style is enabled.
|
||||
///
|
||||
/// Unlike Black, we re-use the same preview style feature flag for [`improved_async_statements_handling`](https://github.com/astral-sh/ruff/issues/8890)
|
||||
pub(crate) const fn is_wrap_multiple_context_managers_in_parens_enabled(
|
||||
context: &PyFormatContext,
|
||||
) -> bool {
|
||||
context.is_preview()
|
||||
}
|
||||
|
||||
/// Returns `true` if the [`module_docstring_newlines`](https://github.com/astral-sh/ruff/issues/7995) preview style is enabled.
|
||||
pub(crate) const fn is_module_docstring_newlines_enabled(context: &PyFormatContext) -> bool {
|
||||
context.is_preview()
|
||||
|
|
|
@ -6,17 +6,21 @@ use ruff_text_size::{Ranged, TextRange};
|
|||
|
||||
use crate::builders::parenthesize_if_expands;
|
||||
use crate::comments::SourceComment;
|
||||
use crate::expression::parentheses::parenthesized;
|
||||
use crate::expression::can_omit_optional_parentheses;
|
||||
use crate::expression::parentheses::{
|
||||
is_expression_parenthesized, optional_parentheses, parenthesized,
|
||||
};
|
||||
use crate::other::commas;
|
||||
use crate::prelude::*;
|
||||
use crate::preview::is_wrap_multiple_context_managers_in_parens_enabled;
|
||||
use crate::statement::clause::{clause_body, clause_header, ClauseHeader};
|
||||
use crate::PyFormatOptions;
|
||||
use crate::{PyFormatOptions, PythonVersion};
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct FormatStmtWith;
|
||||
|
||||
impl FormatNodeRule<StmtWith> for FormatStmtWith {
|
||||
fn fmt_fields(&self, item: &StmtWith, f: &mut PyFormatter) -> FormatResult<()> {
|
||||
fn fmt_fields(&self, with_stmt: &StmtWith, f: &mut PyFormatter) -> FormatResult<()> {
|
||||
// The `with` statement can have one dangling comment on the open parenthesis, like:
|
||||
// ```python
|
||||
// with ( # comment
|
||||
|
@ -31,9 +35,10 @@ impl FormatNodeRule<StmtWith> for FormatStmtWith {
|
|||
// ...
|
||||
// ```
|
||||
let comments = f.context().comments().clone();
|
||||
let dangling_comments = comments.dangling(item.as_any_node_ref());
|
||||
let dangling_comments = comments.dangling(with_stmt.as_any_node_ref());
|
||||
let partition_point = dangling_comments.partition_point(|comment| {
|
||||
item.items
|
||||
with_stmt
|
||||
.items
|
||||
.first()
|
||||
.is_some_and(|with_item| with_item.start() > comment.start())
|
||||
});
|
||||
|
@ -43,35 +48,26 @@ impl FormatNodeRule<StmtWith> for FormatStmtWith {
|
|||
f,
|
||||
[
|
||||
clause_header(
|
||||
ClauseHeader::With(item),
|
||||
ClauseHeader::With(with_stmt),
|
||||
colon_comments,
|
||||
&format_with(|f| {
|
||||
write!(
|
||||
f,
|
||||
[
|
||||
item.is_async
|
||||
with_stmt
|
||||
.is_async
|
||||
.then_some(format_args![token("async"), space()]),
|
||||
token("with"),
|
||||
space()
|
||||
]
|
||||
)?;
|
||||
|
||||
if !parenthesized_comments.is_empty() {
|
||||
let joined = format_with(|f: &mut PyFormatter| {
|
||||
f.join_comma_separated(item.body.first().unwrap().start())
|
||||
.nodes(&item.items)
|
||||
.finish()
|
||||
});
|
||||
|
||||
parenthesized("(", &joined, ")")
|
||||
.with_dangling_comments(parenthesized_comments)
|
||||
.fmt(f)?;
|
||||
} else if should_parenthesize(item, f.options(), f.context())? {
|
||||
parenthesize_if_expands(&format_with(|f| {
|
||||
if parenthesized_comments.is_empty() {
|
||||
let format_items = format_with(|f| {
|
||||
let mut joiner =
|
||||
f.join_comma_separated(item.body.first().unwrap().start());
|
||||
f.join_comma_separated(with_stmt.body.first().unwrap().start());
|
||||
|
||||
for item in &item.items {
|
||||
for item in &with_stmt.items {
|
||||
joiner.entry_with_line_separator(
|
||||
item,
|
||||
&item.format(),
|
||||
|
@ -79,26 +75,48 @@ impl FormatNodeRule<StmtWith> for FormatStmtWith {
|
|||
);
|
||||
}
|
||||
joiner.finish()
|
||||
}))
|
||||
.fmt(f)?;
|
||||
} else if let [item] = item.items.as_slice() {
|
||||
// This is similar to `maybe_parenthesize_expression`, but we're not
|
||||
// dealing with an expression here, it's a `WithItem`.
|
||||
if comments.has_leading(item) || comments.has_trailing(item) {
|
||||
parenthesized("(", &item.format(), ")").fmt(f)?;
|
||||
} else {
|
||||
item.format().fmt(f)?;
|
||||
});
|
||||
|
||||
match should_parenthesize(with_stmt, f.options(), f.context())? {
|
||||
ParenthesizeWith::Optional => {
|
||||
optional_parentheses(&format_items).fmt(f)?;
|
||||
}
|
||||
ParenthesizeWith::IfExpands => {
|
||||
parenthesize_if_expands(&format_items).fmt(f)?;
|
||||
}
|
||||
ParenthesizeWith::UnlessCommented => {
|
||||
if let [item] = with_stmt.items.as_slice() {
|
||||
// This is similar to `maybe_parenthesize_expression`, but we're not
|
||||
// dealing with an expression here, it's a `WithItem`.
|
||||
if comments.has_leading(item) || comments.has_trailing(item)
|
||||
{
|
||||
parenthesized("(", &item.format(), ")").fmt(f)?;
|
||||
} else {
|
||||
item.format().fmt(f)?;
|
||||
}
|
||||
} else {
|
||||
f.join_with(format_args![token(","), space()])
|
||||
.entries(with_stmt.items.iter().formatted())
|
||||
.finish()?;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
f.join_with(format_args![token(","), space()])
|
||||
.entries(item.items.iter().formatted())
|
||||
.finish()?;
|
||||
let joined = format_with(|f: &mut PyFormatter| {
|
||||
f.join_comma_separated(with_stmt.body.first().unwrap().start())
|
||||
.nodes(&with_stmt.items)
|
||||
.finish()
|
||||
});
|
||||
|
||||
parenthesized("(", &joined, ")")
|
||||
.with_dangling_comments(parenthesized_comments)
|
||||
.fmt(f)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
})
|
||||
),
|
||||
clause_body(&item.body, colon_comments)
|
||||
clause_body(&with_stmt.body, colon_comments)
|
||||
]
|
||||
)
|
||||
}
|
||||
|
@ -113,24 +131,79 @@ impl FormatNodeRule<StmtWith> for FormatStmtWith {
|
|||
}
|
||||
}
|
||||
|
||||
/// Returns `true` if the `with` items should be parenthesized, if at least one item expands.
|
||||
/// Determines whether the `with` items should be parenthesized (over parenthesizing each item),
|
||||
/// and if so, which parenthesizing layout to use.
|
||||
///
|
||||
/// Black parenthesizes `with` items if there's more than one item and they're already
|
||||
/// parenthesized, _or_ there's a single item with a trailing comma.
|
||||
/// Parenthesize `with` items if
|
||||
/// * The last item has a trailing comma (implying that the with items were parenthesized in the source)
|
||||
/// * There's more than one item and they're already parenthesized
|
||||
/// * There's more than one item, the [`wrap_multiple_context_managers_in_parens`](is_wrap_multiple_context_managers_in_parens) preview style is enabled,
|
||||
/// and the target python version is >= 3.9
|
||||
/// * There's a single non-parenthesized item. The function returns [`ParenthesizeWith::Optional`]
|
||||
/// if the parentheses can be omitted if breaking around parenthesized sub-expressions is sufficient
|
||||
/// to make the expression fit. It returns [`ParenthesizeWith::IfExpands`] otherwise.
|
||||
/// * The only item is parenthesized and has comments.
|
||||
fn should_parenthesize(
|
||||
with: &StmtWith,
|
||||
options: &PyFormatOptions,
|
||||
context: &PyFormatContext,
|
||||
) -> FormatResult<bool> {
|
||||
) -> FormatResult<ParenthesizeWith> {
|
||||
if has_magic_trailing_comma(with, options, context) {
|
||||
return Ok(true);
|
||||
return Ok(ParenthesizeWith::IfExpands);
|
||||
}
|
||||
|
||||
if are_with_items_parenthesized(with, context)? {
|
||||
return Ok(true);
|
||||
let can_parenthesize = (is_wrap_multiple_context_managers_in_parens_enabled(context)
|
||||
&& options.target_version() >= PythonVersion::Py39)
|
||||
|| are_with_items_parenthesized(with, context)?;
|
||||
|
||||
if !can_parenthesize {
|
||||
return Ok(ParenthesizeWith::UnlessCommented);
|
||||
}
|
||||
|
||||
Ok(false)
|
||||
if let [single] = with.items.as_slice() {
|
||||
return Ok(
|
||||
// If the with item itself has comments (not the context expression), then keep the parentheses
|
||||
if context.comments().has_leading(single) || context.comments().has_trailing(single) {
|
||||
ParenthesizeWith::IfExpands
|
||||
}
|
||||
// If it is the only expression and it has comments, then the with statement
|
||||
// as well as the with item add parentheses
|
||||
else if is_expression_parenthesized(
|
||||
(&single.context_expr).into(),
|
||||
context.comments().ranges(),
|
||||
context.source(),
|
||||
) {
|
||||
// Preserve the parentheses around the context expression instead of parenthesizing the entire
|
||||
// with items.
|
||||
ParenthesizeWith::UnlessCommented
|
||||
} else if is_wrap_multiple_context_managers_in_parens_enabled(context)
|
||||
&& can_omit_optional_parentheses(&single.context_expr, context)
|
||||
{
|
||||
ParenthesizeWith::Optional
|
||||
} else {
|
||||
ParenthesizeWith::IfExpands
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
// Always parenthesize multiple items
|
||||
Ok(ParenthesizeWith::IfExpands)
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||
enum ParenthesizeWith {
|
||||
/// Don't wrap the with items in parentheses except if it is a single item
|
||||
/// and it has leading or trailing comment.
|
||||
///
|
||||
/// This is required because `are_with_items_parenthesized` cannot determine if
|
||||
/// `with (expr)` is a parenthesized expression or a parenthesized with item.
|
||||
UnlessCommented,
|
||||
|
||||
/// Wrap the with items in optional parentheses
|
||||
Optional,
|
||||
|
||||
/// Wrap the with items in parentheses if they expand
|
||||
IfExpands,
|
||||
}
|
||||
|
||||
fn has_magic_trailing_comma(
|
||||
|
|
|
@ -1,342 +0,0 @@
|
|||
---
|
||||
source: crates/ruff_python_formatter/tests/fixtures.rs
|
||||
input_file: crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_context_managers_39.py
|
||||
---
|
||||
## Input
|
||||
|
||||
```python
|
||||
with \
|
||||
make_context_manager1() as cm1, \
|
||||
make_context_manager2() as cm2, \
|
||||
make_context_manager3() as cm3, \
|
||||
make_context_manager4() as cm4 \
|
||||
:
|
||||
pass
|
||||
|
||||
|
||||
# Leading comment
|
||||
with \
|
||||
make_context_manager1() as cm1, \
|
||||
make_context_manager2(), \
|
||||
make_context_manager3() as cm3, \
|
||||
make_context_manager4() \
|
||||
:
|
||||
pass
|
||||
|
||||
|
||||
with \
|
||||
new_new_new1() as cm1, \
|
||||
new_new_new2() \
|
||||
:
|
||||
pass
|
||||
|
||||
|
||||
with (
|
||||
new_new_new1() as cm1,
|
||||
new_new_new2()
|
||||
):
|
||||
pass
|
||||
|
||||
|
||||
# Leading comment.
|
||||
with (
|
||||
# First comment.
|
||||
new_new_new1() as cm1,
|
||||
# Second comment.
|
||||
new_new_new2()
|
||||
# Last comment.
|
||||
):
|
||||
pass
|
||||
|
||||
|
||||
with \
|
||||
this_is_a_very_long_call(looong_arg1=looong_value1, looong_arg2=looong_value2) as cm1, \
|
||||
this_is_a_very_long_call(looong_arg1=looong_value1, looong_arg2=looong_value2, looong_arg3=looong_value3, looong_arg4=looong_value4) as cm2 \
|
||||
:
|
||||
pass
|
||||
|
||||
|
||||
with mock.patch.object(
|
||||
self.my_runner, "first_method", autospec=True
|
||||
) as mock_run_adb, mock.patch.object(
|
||||
self.my_runner, "second_method", autospec=True, return_value="foo"
|
||||
):
|
||||
pass
|
||||
|
||||
|
||||
with xxxxxxxx.some_kind_of_method(
|
||||
some_argument=[
|
||||
"first",
|
||||
"second",
|
||||
"third",
|
||||
]
|
||||
).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
|
||||
```
|
||||
|
||||
## Black Differences
|
||||
|
||||
```diff
|
||||
--- Black
|
||||
+++ Ruff
|
||||
@@ -1,19 +1,9 @@
|
||||
-with (
|
||||
- make_context_manager1() as cm1,
|
||||
- make_context_manager2() as cm2,
|
||||
- make_context_manager3() as cm3,
|
||||
- make_context_manager4() as cm4,
|
||||
-):
|
||||
+with make_context_manager1() as cm1, make_context_manager2() as cm2, make_context_manager3() as cm3, make_context_manager4() as cm4:
|
||||
pass
|
||||
|
||||
|
||||
# Leading comment
|
||||
-with (
|
||||
- make_context_manager1() as cm1,
|
||||
- make_context_manager2(),
|
||||
- make_context_manager3() as cm3,
|
||||
- make_context_manager4(),
|
||||
-):
|
||||
+with make_context_manager1() as cm1, make_context_manager2(), make_context_manager3() as cm3, make_context_manager4():
|
||||
pass
|
||||
|
||||
|
||||
@@ -36,25 +26,21 @@
|
||||
pass
|
||||
|
||||
|
||||
-with (
|
||||
- this_is_a_very_long_call(
|
||||
- looong_arg1=looong_value1, looong_arg2=looong_value2
|
||||
- ) as cm1,
|
||||
- this_is_a_very_long_call(
|
||||
- looong_arg1=looong_value1,
|
||||
- looong_arg2=looong_value2,
|
||||
- looong_arg3=looong_value3,
|
||||
- looong_arg4=looong_value4,
|
||||
- ) as cm2,
|
||||
-):
|
||||
+with this_is_a_very_long_call(
|
||||
+ looong_arg1=looong_value1, looong_arg2=looong_value2
|
||||
+) as cm1, this_is_a_very_long_call(
|
||||
+ looong_arg1=looong_value1,
|
||||
+ looong_arg2=looong_value2,
|
||||
+ looong_arg3=looong_value3,
|
||||
+ looong_arg4=looong_value4,
|
||||
+) as cm2:
|
||||
pass
|
||||
|
||||
|
||||
-with (
|
||||
- mock.patch.object(self.my_runner, "first_method", autospec=True) as mock_run_adb,
|
||||
- mock.patch.object(
|
||||
- self.my_runner, "second_method", autospec=True, return_value="foo"
|
||||
- ),
|
||||
+with mock.patch.object(
|
||||
+ self.my_runner, "first_method", autospec=True
|
||||
+) as mock_run_adb, mock.patch.object(
|
||||
+ self.my_runner, "second_method", autospec=True, return_value="foo"
|
||||
):
|
||||
pass
|
||||
|
||||
@@ -70,16 +56,10 @@
|
||||
|
||||
|
||||
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,
|
||||
- ):
|
||||
+ 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"),
|
||||
- ):
|
||||
+ async with some_function(
|
||||
+ argument1, argument2, argument3="some_value"
|
||||
+ ) as some_cm, some_other_function(argument1, argument2, argument3="some_value"):
|
||||
pass
|
||||
```
|
||||
|
||||
## Ruff Output
|
||||
|
||||
```python
|
||||
with make_context_manager1() as cm1, make_context_manager2() as cm2, make_context_manager3() as cm3, make_context_manager4() as cm4:
|
||||
pass
|
||||
|
||||
|
||||
# Leading comment
|
||||
with make_context_manager1() as cm1, make_context_manager2(), make_context_manager3() as cm3, make_context_manager4():
|
||||
pass
|
||||
|
||||
|
||||
with new_new_new1() as cm1, new_new_new2():
|
||||
pass
|
||||
|
||||
|
||||
with new_new_new1() as cm1, new_new_new2():
|
||||
pass
|
||||
|
||||
|
||||
# Leading comment.
|
||||
with (
|
||||
# First comment.
|
||||
new_new_new1() as cm1,
|
||||
# Second comment.
|
||||
new_new_new2(),
|
||||
# Last comment.
|
||||
):
|
||||
pass
|
||||
|
||||
|
||||
with this_is_a_very_long_call(
|
||||
looong_arg1=looong_value1, looong_arg2=looong_value2
|
||||
) as cm1, this_is_a_very_long_call(
|
||||
looong_arg1=looong_value1,
|
||||
looong_arg2=looong_value2,
|
||||
looong_arg3=looong_value3,
|
||||
looong_arg4=looong_value4,
|
||||
) as cm2:
|
||||
pass
|
||||
|
||||
|
||||
with mock.patch.object(
|
||||
self.my_runner, "first_method", autospec=True
|
||||
) as mock_run_adb, mock.patch.object(
|
||||
self.my_runner, "second_method", autospec=True, return_value="foo"
|
||||
):
|
||||
pass
|
||||
|
||||
|
||||
with xxxxxxxx.some_kind_of_method(
|
||||
some_argument=[
|
||||
"first",
|
||||
"second",
|
||||
"third",
|
||||
]
|
||||
).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
|
||||
```
|
||||
|
||||
## Black Output
|
||||
|
||||
```python
|
||||
with (
|
||||
make_context_manager1() as cm1,
|
||||
make_context_manager2() as cm2,
|
||||
make_context_manager3() as cm3,
|
||||
make_context_manager4() as cm4,
|
||||
):
|
||||
pass
|
||||
|
||||
|
||||
# Leading comment
|
||||
with (
|
||||
make_context_manager1() as cm1,
|
||||
make_context_manager2(),
|
||||
make_context_manager3() as cm3,
|
||||
make_context_manager4(),
|
||||
):
|
||||
pass
|
||||
|
||||
|
||||
with new_new_new1() as cm1, new_new_new2():
|
||||
pass
|
||||
|
||||
|
||||
with new_new_new1() as cm1, new_new_new2():
|
||||
pass
|
||||
|
||||
|
||||
# Leading comment.
|
||||
with (
|
||||
# First comment.
|
||||
new_new_new1() as cm1,
|
||||
# Second comment.
|
||||
new_new_new2(),
|
||||
# Last comment.
|
||||
):
|
||||
pass
|
||||
|
||||
|
||||
with (
|
||||
this_is_a_very_long_call(
|
||||
looong_arg1=looong_value1, looong_arg2=looong_value2
|
||||
) as cm1,
|
||||
this_is_a_very_long_call(
|
||||
looong_arg1=looong_value1,
|
||||
looong_arg2=looong_value2,
|
||||
looong_arg3=looong_value3,
|
||||
looong_arg4=looong_value4,
|
||||
) as cm2,
|
||||
):
|
||||
pass
|
||||
|
||||
|
||||
with (
|
||||
mock.patch.object(self.my_runner, "first_method", autospec=True) as mock_run_adb,
|
||||
mock.patch.object(
|
||||
self.my_runner, "second_method", autospec=True, return_value="foo"
|
||||
),
|
||||
):
|
||||
pass
|
||||
|
||||
|
||||
with xxxxxxxx.some_kind_of_method(
|
||||
some_argument=[
|
||||
"first",
|
||||
"second",
|
||||
"third",
|
||||
]
|
||||
).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
|
||||
```
|
||||
|
||||
|
|
@ -1,79 +0,0 @@
|
|||
---
|
||||
source: crates/ruff_python_formatter/tests/fixtures.rs
|
||||
input_file: crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_context_managers_autodetect_310.py
|
||||
---
|
||||
## Input
|
||||
|
||||
```python
|
||||
# This file uses pattern matching introduced in Python 3.10.
|
||||
|
||||
|
||||
match http_code:
|
||||
case 404:
|
||||
print("Not found")
|
||||
|
||||
|
||||
with \
|
||||
make_context_manager1() as cm1, \
|
||||
make_context_manager2() as cm2, \
|
||||
make_context_manager3() as cm3, \
|
||||
make_context_manager4() as cm4 \
|
||||
:
|
||||
pass
|
||||
```
|
||||
|
||||
## Black Differences
|
||||
|
||||
```diff
|
||||
--- Black
|
||||
+++ Ruff
|
||||
@@ -6,10 +6,5 @@
|
||||
print("Not found")
|
||||
|
||||
|
||||
-with (
|
||||
- make_context_manager1() as cm1,
|
||||
- make_context_manager2() as cm2,
|
||||
- make_context_manager3() as cm3,
|
||||
- make_context_manager4() as cm4,
|
||||
-):
|
||||
+with make_context_manager1() as cm1, make_context_manager2() as cm2, make_context_manager3() as cm3, make_context_manager4() as cm4:
|
||||
pass
|
||||
```
|
||||
|
||||
## Ruff Output
|
||||
|
||||
```python
|
||||
# This file uses pattern matching introduced in Python 3.10.
|
||||
|
||||
|
||||
match http_code:
|
||||
case 404:
|
||||
print("Not found")
|
||||
|
||||
|
||||
with make_context_manager1() as cm1, make_context_manager2() as cm2, make_context_manager3() as cm3, make_context_manager4() as cm4:
|
||||
pass
|
||||
```
|
||||
|
||||
## Black Output
|
||||
|
||||
```python
|
||||
# This file uses pattern matching introduced in Python 3.10.
|
||||
|
||||
|
||||
match http_code:
|
||||
case 404:
|
||||
print("Not found")
|
||||
|
||||
|
||||
with (
|
||||
make_context_manager1() as cm1,
|
||||
make_context_manager2() as cm2,
|
||||
make_context_manager3() as cm3,
|
||||
make_context_manager4() as cm4,
|
||||
):
|
||||
pass
|
||||
```
|
||||
|
||||
|
|
@ -1,82 +0,0 @@
|
|||
---
|
||||
source: crates/ruff_python_formatter/tests/fixtures.rs
|
||||
input_file: crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_context_managers_autodetect_311.py
|
||||
---
|
||||
## Input
|
||||
|
||||
```python
|
||||
# This file uses except* clause in Python 3.11.
|
||||
|
||||
|
||||
try:
|
||||
some_call()
|
||||
except* Error as e:
|
||||
pass
|
||||
|
||||
|
||||
with \
|
||||
make_context_manager1() as cm1, \
|
||||
make_context_manager2() as cm2, \
|
||||
make_context_manager3() as cm3, \
|
||||
make_context_manager4() as cm4 \
|
||||
:
|
||||
pass
|
||||
```
|
||||
|
||||
## Black Differences
|
||||
|
||||
```diff
|
||||
--- Black
|
||||
+++ Ruff
|
||||
@@ -7,10 +7,5 @@
|
||||
pass
|
||||
|
||||
|
||||
-with (
|
||||
- make_context_manager1() as cm1,
|
||||
- make_context_manager2() as cm2,
|
||||
- make_context_manager3() as cm3,
|
||||
- make_context_manager4() as cm4,
|
||||
-):
|
||||
+with make_context_manager1() as cm1, make_context_manager2() as cm2, make_context_manager3() as cm3, make_context_manager4() as cm4:
|
||||
pass
|
||||
```
|
||||
|
||||
## Ruff Output
|
||||
|
||||
```python
|
||||
# This file uses except* clause in Python 3.11.
|
||||
|
||||
|
||||
try:
|
||||
some_call()
|
||||
except* Error as e:
|
||||
pass
|
||||
|
||||
|
||||
with make_context_manager1() as cm1, make_context_manager2() as cm2, make_context_manager3() as cm3, make_context_manager4() as cm4:
|
||||
pass
|
||||
```
|
||||
|
||||
## Black Output
|
||||
|
||||
```python
|
||||
# This file uses except* clause in Python 3.11.
|
||||
|
||||
|
||||
try:
|
||||
some_call()
|
||||
except* Error as e:
|
||||
pass
|
||||
|
||||
|
||||
with (
|
||||
make_context_manager1() as cm1,
|
||||
make_context_manager2() as cm2,
|
||||
make_context_manager3() as cm3,
|
||||
make_context_manager4() as cm4,
|
||||
):
|
||||
pass
|
||||
```
|
||||
|
||||
|
|
@ -1,81 +0,0 @@
|
|||
---
|
||||
source: crates/ruff_python_formatter/tests/fixtures.rs
|
||||
input_file: crates/ruff_python_formatter/resources/test/fixtures/black/cases/preview_context_managers_autodetect_39.py
|
||||
---
|
||||
## Input
|
||||
|
||||
```python
|
||||
# This file uses parenthesized context managers introduced in Python 3.9.
|
||||
|
||||
|
||||
with \
|
||||
make_context_manager1() as cm1, \
|
||||
make_context_manager2() as cm2, \
|
||||
make_context_manager3() as cm3, \
|
||||
make_context_manager4() as cm4 \
|
||||
:
|
||||
pass
|
||||
|
||||
|
||||
with (
|
||||
new_new_new1() as cm1,
|
||||
new_new_new2()
|
||||
):
|
||||
pass
|
||||
```
|
||||
|
||||
## Black Differences
|
||||
|
||||
```diff
|
||||
--- Black
|
||||
+++ Ruff
|
||||
@@ -1,12 +1,7 @@
|
||||
# This file uses parenthesized context managers introduced in Python 3.9.
|
||||
|
||||
|
||||
-with (
|
||||
- make_context_manager1() as cm1,
|
||||
- make_context_manager2() as cm2,
|
||||
- make_context_manager3() as cm3,
|
||||
- make_context_manager4() as cm4,
|
||||
-):
|
||||
+with make_context_manager1() as cm1, make_context_manager2() as cm2, make_context_manager3() as cm3, make_context_manager4() as cm4:
|
||||
pass
|
||||
|
||||
|
||||
```
|
||||
|
||||
## Ruff Output
|
||||
|
||||
```python
|
||||
# This file uses parenthesized context managers introduced in Python 3.9.
|
||||
|
||||
|
||||
with make_context_manager1() as cm1, make_context_manager2() as cm2, make_context_manager3() as cm3, make_context_manager4() as cm4:
|
||||
pass
|
||||
|
||||
|
||||
with new_new_new1() as cm1, new_new_new2():
|
||||
pass
|
||||
```
|
||||
|
||||
## Black Output
|
||||
|
||||
```python
|
||||
# This file uses parenthesized context managers introduced in Python 3.9.
|
||||
|
||||
|
||||
with (
|
||||
make_context_manager1() as cm1,
|
||||
make_context_manager2() as cm2,
|
||||
make_context_manager3() as cm3,
|
||||
make_context_manager4() as cm4,
|
||||
):
|
||||
pass
|
||||
|
||||
|
||||
with new_new_new1() as cm1, new_new_new2():
|
||||
pass
|
||||
```
|
||||
|
||||
|
|
@ -315,7 +315,21 @@ with Child(aaaaaaaaa, bbbbbbbbbbbbbbb, cccccc), Document(aaaaa, bbbbbbbbbb, dddd
|
|||
pass
|
||||
```
|
||||
|
||||
## Output
|
||||
## Outputs
|
||||
### Output 1
|
||||
```
|
||||
indent-style = space
|
||||
line-width = 88
|
||||
indent-width = 4
|
||||
quote-style = Double
|
||||
line-ending = LineFeed
|
||||
magic-trailing-comma = Respect
|
||||
docstring-code = Disabled
|
||||
docstring-code-line-width = "dynamic"
|
||||
preview = Disabled
|
||||
target_version = Py38
|
||||
```
|
||||
|
||||
```python
|
||||
with aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa, aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa:
|
||||
pass
|
||||
|
@ -653,4 +667,414 @@ with Child(aaaaaaaaa, bbbbbbbbbbbbbbb, cccccc), Document(
|
|||
```
|
||||
|
||||
|
||||
#### Preview changes
|
||||
```diff
|
||||
--- Stable
|
||||
+++ Preview
|
||||
@@ -295,8 +295,9 @@
|
||||
pass
|
||||
|
||||
with (
|
||||
- aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
|
||||
- + bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb as b,
|
||||
+ (
|
||||
+ aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa + bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
|
||||
+ ) as b,
|
||||
c as d,
|
||||
):
|
||||
pass
|
||||
```
|
||||
|
||||
|
||||
### Output 2
|
||||
```
|
||||
indent-style = space
|
||||
line-width = 88
|
||||
indent-width = 4
|
||||
quote-style = Double
|
||||
line-ending = LineFeed
|
||||
magic-trailing-comma = Respect
|
||||
docstring-code = Disabled
|
||||
docstring-code-line-width = "dynamic"
|
||||
preview = Enabled
|
||||
target_version = Py39
|
||||
```
|
||||
|
||||
```python
|
||||
with (
|
||||
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa,
|
||||
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa,
|
||||
):
|
||||
pass
|
||||
# trailing
|
||||
|
||||
with a, a: # after colon
|
||||
pass
|
||||
# trailing
|
||||
|
||||
with (
|
||||
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa,
|
||||
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa,
|
||||
):
|
||||
pass
|
||||
# trailing
|
||||
|
||||
|
||||
with (
|
||||
a, # a # comma
|
||||
b, # c
|
||||
): # colon
|
||||
pass
|
||||
|
||||
|
||||
with (
|
||||
a as ( # a # as
|
||||
# own line
|
||||
b
|
||||
), # b # comma
|
||||
c, # c
|
||||
): # colon
|
||||
pass # body
|
||||
# body trailing own
|
||||
|
||||
with (
|
||||
a as ( # a # as
|
||||
# own line
|
||||
bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
|
||||
) # b
|
||||
):
|
||||
pass
|
||||
|
||||
|
||||
with (
|
||||
a,
|
||||
): # magic trailing comma
|
||||
pass
|
||||
|
||||
|
||||
with a: # should remove brackets
|
||||
pass
|
||||
|
||||
with (
|
||||
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa + bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb as c
|
||||
):
|
||||
pass
|
||||
|
||||
|
||||
# currently unparsable by black: https://github.com/psf/black/issues/3678
|
||||
with (name_2 for name_0 in name_4):
|
||||
pass
|
||||
with (a, *b):
|
||||
pass
|
||||
|
||||
with (
|
||||
# leading comment
|
||||
a
|
||||
) as b:
|
||||
pass
|
||||
|
||||
with (
|
||||
# leading comment
|
||||
a as b
|
||||
):
|
||||
pass
|
||||
|
||||
with (
|
||||
a as b
|
||||
# trailing comment
|
||||
):
|
||||
pass
|
||||
|
||||
with (
|
||||
a as (
|
||||
# leading comment
|
||||
b
|
||||
)
|
||||
):
|
||||
pass
|
||||
|
||||
with (
|
||||
a as (
|
||||
b
|
||||
# trailing comment
|
||||
)
|
||||
):
|
||||
pass
|
||||
|
||||
with (
|
||||
a as b # trailing same line comment
|
||||
# trailing own line comment
|
||||
):
|
||||
pass
|
||||
|
||||
with (
|
||||
a # trailing same line comment
|
||||
# trailing own line comment
|
||||
) as b:
|
||||
pass
|
||||
|
||||
with (
|
||||
(
|
||||
a
|
||||
# trailing own line comment
|
||||
) as ( # trailing as same line comment
|
||||
b
|
||||
) # trailing b same line comment
|
||||
):
|
||||
pass
|
||||
|
||||
with (
|
||||
# comment
|
||||
a
|
||||
):
|
||||
pass
|
||||
|
||||
with (
|
||||
a # comment
|
||||
):
|
||||
pass
|
||||
|
||||
with (
|
||||
a
|
||||
# comment
|
||||
):
|
||||
pass
|
||||
|
||||
with (
|
||||
# comment
|
||||
a as b
|
||||
):
|
||||
pass
|
||||
|
||||
with (
|
||||
a as b # comment
|
||||
):
|
||||
pass
|
||||
|
||||
with (
|
||||
a as b
|
||||
# comment
|
||||
):
|
||||
pass
|
||||
|
||||
with (
|
||||
[
|
||||
"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
|
||||
"bbbbbbbbbb",
|
||||
"cccccccccccccccccccccccccccccccccccccccccc",
|
||||
dddddddddddddddddddddddddddddddd,
|
||||
] as example1,
|
||||
aaaaaaaaaaaaaaaaaaaaaaaaaa
|
||||
+ bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
|
||||
+ cccccccccccccccccccccccccccc
|
||||
+ ddddddddddddddddd as example2,
|
||||
CtxManager2() as example2,
|
||||
CtxManager2() as example2,
|
||||
CtxManager2() as example2,
|
||||
):
|
||||
pass
|
||||
|
||||
with (
|
||||
[
|
||||
"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
|
||||
"bbbbbbbbbb",
|
||||
"cccccccccccccccccccccccccccccccccccccccccc",
|
||||
dddddddddddddddddddddddddddddddd,
|
||||
] as example1,
|
||||
aaaaaaaaaaaaaaaaaaaaaaaaaa
|
||||
* bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
|
||||
* cccccccccccccccccccccccccccc
|
||||
+ ddddddddddddddddd as example2,
|
||||
CtxManager222222222222222() as example2,
|
||||
):
|
||||
pass
|
||||
|
||||
# Comments on open parentheses
|
||||
with ( # comment
|
||||
CtxManager1() as example1,
|
||||
CtxManager2() as example2,
|
||||
CtxManager3() as example3,
|
||||
):
|
||||
pass
|
||||
|
||||
with ( # outer comment
|
||||
( # inner comment
|
||||
CtxManager1()
|
||||
) as example1,
|
||||
CtxManager2() as example2,
|
||||
CtxManager3() as example3,
|
||||
):
|
||||
pass
|
||||
|
||||
with ( # outer comment
|
||||
CtxManager()
|
||||
) as example:
|
||||
pass
|
||||
|
||||
with (
|
||||
( # outer comment
|
||||
CtxManager()
|
||||
) as example,
|
||||
( # inner comment
|
||||
CtxManager2()
|
||||
) as example2,
|
||||
):
|
||||
pass
|
||||
|
||||
with ( # outer comment
|
||||
CtxManager1(),
|
||||
CtxManager2(),
|
||||
) as example:
|
||||
pass
|
||||
|
||||
with ( # outer comment
|
||||
( # inner comment
|
||||
CtxManager1()
|
||||
),
|
||||
CtxManager2(),
|
||||
) as example:
|
||||
pass
|
||||
|
||||
# Breaking of with items.
|
||||
with (
|
||||
test as ( # bar # foo
|
||||
# test
|
||||
foo
|
||||
)
|
||||
):
|
||||
pass
|
||||
|
||||
with (
|
||||
test as (
|
||||
# test
|
||||
foo
|
||||
)
|
||||
):
|
||||
pass
|
||||
|
||||
with (
|
||||
test as ( # bar # foo # baz
|
||||
# test
|
||||
foo
|
||||
)
|
||||
):
|
||||
pass
|
||||
|
||||
with a as b, c as d:
|
||||
pass
|
||||
|
||||
with (
|
||||
a as b,
|
||||
# foo
|
||||
c as d,
|
||||
):
|
||||
pass
|
||||
|
||||
with (
|
||||
a as ( # foo
|
||||
b
|
||||
)
|
||||
):
|
||||
pass
|
||||
|
||||
with f(
|
||||
a,
|
||||
) as b:
|
||||
pass
|
||||
|
||||
with (x := 1) as d:
|
||||
pass
|
||||
|
||||
with x[
|
||||
1,
|
||||
2,
|
||||
] as d:
|
||||
pass
|
||||
|
||||
with (
|
||||
f(
|
||||
a,
|
||||
) as b,
|
||||
c as d,
|
||||
):
|
||||
pass
|
||||
|
||||
with (
|
||||
f(
|
||||
a,
|
||||
) as b,
|
||||
c as d,
|
||||
):
|
||||
pass
|
||||
|
||||
with (
|
||||
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa + bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
|
||||
) as b:
|
||||
pass
|
||||
|
||||
with (
|
||||
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
|
||||
+ bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb as b
|
||||
):
|
||||
pass
|
||||
|
||||
with (
|
||||
(
|
||||
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa + bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
|
||||
) as b,
|
||||
c as d,
|
||||
):
|
||||
pass
|
||||
|
||||
with (
|
||||
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
|
||||
+ bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb as b,
|
||||
c as d,
|
||||
):
|
||||
pass
|
||||
|
||||
with (
|
||||
(
|
||||
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa + bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
|
||||
) as b,
|
||||
c as d,
|
||||
):
|
||||
pass
|
||||
|
||||
with foo() as bar, baz() as bop:
|
||||
pass
|
||||
|
||||
if True:
|
||||
with (
|
||||
anyio.CancelScope(shield=True)
|
||||
if get_running_loop()
|
||||
else contextlib.nullcontext()
|
||||
):
|
||||
pass
|
||||
|
||||
if True:
|
||||
with (
|
||||
anyio.CancelScope(shield=True)
|
||||
and B
|
||||
and [aaaaaaaa, bbbbbbbbbbbbb, cccccccccc, dddddddddddd, eeeeeeeeeeeee]
|
||||
):
|
||||
pass
|
||||
|
||||
if True:
|
||||
with (
|
||||
anyio.CancelScope(shield=True)
|
||||
if get_running_loop()
|
||||
else contextlib.nullcontext()
|
||||
):
|
||||
pass
|
||||
|
||||
|
||||
with (
|
||||
Child(aaaaaaaaa, bbbbbbbbbbbbbbb, cccccc),
|
||||
Document(aaaaa, bbbbbbbbbb, ddddddddddddd),
|
||||
):
|
||||
pass
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -0,0 +1,222 @@
|
|||
---
|
||||
source: crates/ruff_python_formatter/tests/fixtures.rs
|
||||
input_file: crates/ruff_python_formatter/resources/test/fixtures/ruff/statement/with_39.py
|
||||
---
|
||||
## Input
|
||||
```python
|
||||
if True:
|
||||
with (
|
||||
anyio.CancelScope(shield=True)
|
||||
if get_running_loop()
|
||||
else contextlib.nullcontext()
|
||||
):
|
||||
pass
|
||||
|
||||
|
||||
# Black avoids parenthesizing the with because it can make all with items fit by just breaking
|
||||
# around parentheses. We don't implement this optimisation because it makes it difficult to see where
|
||||
# the different context managers start and end.
|
||||
with cmd, xxxxxxxx.some_kind_of_method(
|
||||
some_argument=[
|
||||
"first",
|
||||
"second",
|
||||
"third",
|
||||
]
|
||||
) as cmd, another, and_more as x:
|
||||
pass
|
||||
|
||||
# Avoid parenthesizing single item context managers when splitting after the parentheses (can_omit_optional_parentheses)
|
||||
# is sufficient
|
||||
with xxxxxxxx.some_kind_of_method(
|
||||
some_argument=[
|
||||
"first",
|
||||
"second",
|
||||
"third",
|
||||
]
|
||||
).another_method(): pass
|
||||
|
||||
if True:
|
||||
with (
|
||||
anyio.CancelScope(shield=True)
|
||||
if get_running_loop()
|
||||
else contextlib.nullcontext()
|
||||
):
|
||||
pass
|
||||
|
||||
|
||||
# Black avoids parentheses here because it can make the entire with
|
||||
# header fit without requiring parentheses to do so.
|
||||
# We don't implement this optimisation because it very difficult to see where
|
||||
# the different context managers start or end.
|
||||
with cmd, xxxxxxxx.some_kind_of_method(
|
||||
some_argument=[
|
||||
"first",
|
||||
"second",
|
||||
"third",
|
||||
]
|
||||
) as cmd, another, and_more as x:
|
||||
pass
|
||||
|
||||
# Avoid parenthesizing single item context managers when splitting after the parentheses
|
||||
# is sufficient
|
||||
with xxxxxxxx.some_kind_of_method(
|
||||
some_argument=[
|
||||
"first",
|
||||
"second",
|
||||
"third",
|
||||
]
|
||||
).another_method(): pass
|
||||
|
||||
# Parenthesize the with items if it makes them fit. Breaking the binary expression isn't
|
||||
# necessary because the entire items fit just into the 88 character limit.
|
||||
with aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa + bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb as c:
|
||||
pass
|
||||
|
||||
|
||||
# Black parenthesizes this binary expression but also preserves the parentheses of the first with-item.
|
||||
# It does so because it prefers splitting already parenthesized context managers, even if it leads to more parentheses
|
||||
# like in this case.
|
||||
with (
|
||||
(
|
||||
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa + bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
|
||||
) as b,
|
||||
c as d,
|
||||
):
|
||||
pass
|
||||
|
||||
if True:
|
||||
with anyio.CancelScope(shield=True) if get_running_loop() else contextlib.nullcontext():
|
||||
pass
|
||||
|
||||
with (aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa + bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb as c):
|
||||
pass
|
||||
|
||||
|
||||
```
|
||||
|
||||
## Outputs
|
||||
### Output 1
|
||||
```
|
||||
indent-style = space
|
||||
line-width = 88
|
||||
indent-width = 4
|
||||
quote-style = Double
|
||||
line-ending = LineFeed
|
||||
magic-trailing-comma = Respect
|
||||
docstring-code = Disabled
|
||||
docstring-code-line-width = "dynamic"
|
||||
preview = Enabled
|
||||
target_version = Py39
|
||||
```
|
||||
|
||||
```python
|
||||
if True:
|
||||
with (
|
||||
anyio.CancelScope(shield=True)
|
||||
if get_running_loop()
|
||||
else contextlib.nullcontext()
|
||||
):
|
||||
pass
|
||||
|
||||
|
||||
# Black avoids parenthesizing the with because it can make all with items fit by just breaking
|
||||
# around parentheses. We don't implement this optimisation because it makes it difficult to see where
|
||||
# the different context managers start and end.
|
||||
with (
|
||||
cmd,
|
||||
xxxxxxxx.some_kind_of_method(
|
||||
some_argument=[
|
||||
"first",
|
||||
"second",
|
||||
"third",
|
||||
]
|
||||
) as cmd,
|
||||
another,
|
||||
and_more as x,
|
||||
):
|
||||
pass
|
||||
|
||||
# Avoid parenthesizing single item context managers when splitting after the parentheses (can_omit_optional_parentheses)
|
||||
# is sufficient
|
||||
with xxxxxxxx.some_kind_of_method(
|
||||
some_argument=[
|
||||
"first",
|
||||
"second",
|
||||
"third",
|
||||
]
|
||||
).another_method():
|
||||
pass
|
||||
|
||||
if True:
|
||||
with (
|
||||
anyio.CancelScope(shield=True)
|
||||
if get_running_loop()
|
||||
else contextlib.nullcontext()
|
||||
):
|
||||
pass
|
||||
|
||||
|
||||
# Black avoids parentheses here because it can make the entire with
|
||||
# header fit without requiring parentheses to do so.
|
||||
# We don't implement this optimisation because it very difficult to see where
|
||||
# the different context managers start or end.
|
||||
with (
|
||||
cmd,
|
||||
xxxxxxxx.some_kind_of_method(
|
||||
some_argument=[
|
||||
"first",
|
||||
"second",
|
||||
"third",
|
||||
]
|
||||
) as cmd,
|
||||
another,
|
||||
and_more as x,
|
||||
):
|
||||
pass
|
||||
|
||||
# Avoid parenthesizing single item context managers when splitting after the parentheses
|
||||
# is sufficient
|
||||
with xxxxxxxx.some_kind_of_method(
|
||||
some_argument=[
|
||||
"first",
|
||||
"second",
|
||||
"third",
|
||||
]
|
||||
).another_method():
|
||||
pass
|
||||
|
||||
# Parenthesize the with items if it makes them fit. Breaking the binary expression isn't
|
||||
# necessary because the entire items fit just into the 88 character limit.
|
||||
with (
|
||||
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa + bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb as c
|
||||
):
|
||||
pass
|
||||
|
||||
|
||||
# Black parenthesizes this binary expression but also preserves the parentheses of the first with-item.
|
||||
# It does so because it prefers splitting already parenthesized context managers, even if it leads to more parentheses
|
||||
# like in this case.
|
||||
with (
|
||||
(
|
||||
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa + bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
|
||||
) as b,
|
||||
c as d,
|
||||
):
|
||||
pass
|
||||
|
||||
if True:
|
||||
with (
|
||||
anyio.CancelScope(shield=True)
|
||||
if get_running_loop()
|
||||
else contextlib.nullcontext()
|
||||
):
|
||||
pass
|
||||
|
||||
with (
|
||||
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa + bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb as c
|
||||
):
|
||||
pass
|
||||
```
|
||||
|
||||
|
||||
|
Loading…
Add table
Add a link
Reference in a new issue