diff --git a/crates/ruff_python_formatter/resources/test/fixtures/ruff/statement/with.py b/crates/ruff_python_formatter/resources/test/fixtures/ruff/statement/with.py index db4d7b77da1f0f..37948c5b30dd31 100644 --- a/crates/ruff_python_formatter/resources/test/fixtures/ruff/statement/with.py +++ b/crates/ruff_python_formatter/resources/test/fixtures/ruff/statement/with.py @@ -305,3 +305,13 @@ with Child(aaaaaaaaa, bbbbbbbbbbbbbbb, cccccc), Document(aaaaa, bbbbbbbbbb, ddddddddddddd): pass + +with aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa + bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb: + pass + +with aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa + bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb as b: + pass + +if True: + with anyio.CancelScope(shield=True) if get_running_loop() else contextlib.nullcontext() as b: + pass diff --git a/crates/ruff_python_formatter/src/other/with_item.rs b/crates/ruff_python_formatter/src/other/with_item.rs index e349fee895b5b5..bda6c79b85d3ba 100644 --- a/crates/ruff_python_formatter/src/other/with_item.rs +++ b/crates/ruff_python_formatter/src/other/with_item.rs @@ -7,6 +7,7 @@ use crate::expression::parentheses::{ is_expression_parenthesized, parenthesized, Parentheses, Parenthesize, }; use crate::prelude::*; +use crate::preview::is_with_single_item_pre_39_enabled; #[derive(Copy, Clone, Debug, Eq, PartialEq, Default)] pub enum WithItemLayout { @@ -23,7 +24,7 @@ pub enum WithItemLayout { SingleParenthesizedContextExpression, /// This layout is used when the target python version doesn't support parenthesized context managers. - Python38OrOlder, + Python38OrOlder { single: bool }, /// A with item where the `with` formatting adds parentheses around all context managers if necessary. /// @@ -108,8 +109,10 @@ impl FormatNodeRule for FormatWithItem { )?; } - WithItemLayout::Python38OrOlder => { - let parenthesize = if is_parenthesized { + WithItemLayout::Python38OrOlder { single } => { + let parenthesize = if (is_with_single_item_pre_39_enabled(f.context()) && single) + || is_parenthesized + { Parenthesize::IfBreaks } else { Parenthesize::IfRequired diff --git a/crates/ruff_python_formatter/src/preview.rs b/crates/ruff_python_formatter/src/preview.rs index 067a0af7e9e530..a403e4a8011d4b 100644 --- a/crates/ruff_python_formatter/src/preview.rs +++ b/crates/ruff_python_formatter/src/preview.rs @@ -18,3 +18,7 @@ pub(crate) const fn is_hug_parens_with_braces_and_square_brackets_enabled( pub(crate) fn is_f_string_formatting_enabled(context: &PyFormatContext) -> bool { context.is_preview() } + +pub(crate) fn is_with_single_item_pre_39_enabled(context: &PyFormatContext) -> bool { + context.is_preview() +} diff --git a/crates/ruff_python_formatter/src/statement/stmt_with.rs b/crates/ruff_python_formatter/src/statement/stmt_with.rs index 7543ab4798684f..9d0c91bbb6366e 100644 --- a/crates/ruff_python_formatter/src/statement/stmt_with.rs +++ b/crates/ruff_python_formatter/src/statement/stmt_with.rs @@ -99,7 +99,9 @@ impl FormatNodeRule for FormatStmtWith { WithItemsLayout::Python38OrOlder => f .join_with(format_args![token(","), space()]) .entries(with_stmt.items.iter().map(|item| { - item.format().with_options(WithItemLayout::Python38OrOlder) + item.format().with_options(WithItemLayout::Python38OrOlder { + single: with_stmt.items.len() == 1, + }) })) .finish(), diff --git a/crates/ruff_python_formatter/tests/snapshots/format@statement__with.py.snap b/crates/ruff_python_formatter/tests/snapshots/format@statement__with.py.snap index 312162387ad8f3..dfe1ef79137e63 100644 --- a/crates/ruff_python_formatter/tests/snapshots/format@statement__with.py.snap +++ b/crates/ruff_python_formatter/tests/snapshots/format@statement__with.py.snap @@ -311,6 +311,16 @@ if True: with Child(aaaaaaaaa, bbbbbbbbbbbbbbb, cccccc), Document(aaaaa, bbbbbbbbbb, ddddddddddddd): pass + +with aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa + bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb: + pass + +with aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa + bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb as b: + pass + +if True: + with anyio.CancelScope(shield=True) if get_running_loop() else contextlib.nullcontext() as b: + pass ``` ## Outputs @@ -664,6 +674,121 @@ with Child(aaaaaaaaa, bbbbbbbbbbbbbbb, cccccc), Document( aaaaa, bbbbbbbbbb, ddddddddddddd ): pass + +with aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa + bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb: + pass + +with aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa + bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb as b: + pass + +if True: + with anyio.CancelScope( + shield=True + ) if get_running_loop() else contextlib.nullcontext() as b: + pass +``` + + +#### Preview changes +```diff +--- Stable ++++ Preview +@@ -45,7 +45,9 @@ + with a: # should remove brackets + pass + +-with aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa + bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb as c: ++with ( ++ aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa + bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb ++) as c: + pass + + # currently unparsable by black: https://github.com/psf/black/issues/3678 +@@ -209,7 +211,9 @@ + pass + + # Breaking of with items. +-with test as ( # bar # foo ++with ( ++ test # bar ++) as ( # foo + # test + foo + ): +@@ -221,7 +225,9 @@ + ): + pass + +-with test as ( # bar # foo # baz ++with ( ++ test # bar ++) as ( # foo # baz + # test + foo + ): +@@ -274,7 +280,9 @@ + ) as b: + pass + +-with aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa + bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb as b: ++with ( ++ aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa + bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb ++) as b: + pass + + with ( +@@ -317,15 +325,19 @@ + pass + + if True: +- with anyio.CancelScope( +- shield=True +- ) if get_running_loop() else contextlib.nullcontext(): ++ with ( ++ anyio.CancelScope(shield=True) ++ if get_running_loop() ++ else contextlib.nullcontext() ++ ): + pass + + if True: +- with anyio.CancelScope( +- shield=True +- ) if get_running_loop() else contextlib.nullcontext() as c: ++ with ( ++ anyio.CancelScope(shield=True) ++ if get_running_loop() ++ else contextlib.nullcontext() ++ ) as c: + pass + + with Child(aaaaaaaaa, bbbbbbbbbbbbbbb, cccccc), Document( +@@ -333,14 +345,20 @@ + ): + pass + +-with aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa + bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb: ++with ( ++ aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa + bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb ++): + pass + +-with aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa + bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb as b: ++with ( ++ aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa + bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb ++) as b: + pass + + if True: +- with anyio.CancelScope( +- shield=True +- ) if get_running_loop() else contextlib.nullcontext() as b: ++ with ( ++ anyio.CancelScope(shield=True) ++ if get_running_loop() ++ else contextlib.nullcontext() ++ ) as b: + pass ``` @@ -1058,4 +1183,23 @@ with ( Document(aaaaa, bbbbbbbbbb, ddddddddddddd), ): pass + +with ( + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa + bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb +): + pass + +with ( + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa + + bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb as b +): + pass + +if True: + with ( + anyio.CancelScope(shield=True) + if get_running_loop() + else contextlib.nullcontext() as b + ): + pass ```