Formatter: Improve single-with item formatting for Python 3.8 or older (#10276)

## Summary

This PR changes how we format `with` statements with a single with item
for Python 3.8 or older. This change is not compatible with Black.

This is how we format a single-item with statement today 

```python
def run(data_path, model_uri):
    with pyspark.sql.SparkSession.builder.config(
        key="spark.python.worker.reuse", value=True
    ).config(key="spark.ui.enabled", value=False).master(
        "local-cluster[2, 1, 1024]"
    ).getOrCreate():
        # ignore spark log output
        spark.sparkContext.setLogLevel("OFF")
        print(score_model(spark, data_path, model_uri))
```

This is different than how we would format the same expression if it is
inside any other clause header (`while`, `if`, ...):

```python
def run(data_path, model_uri):
    while (
        pyspark.sql.SparkSession.builder.config(
            key="spark.python.worker.reuse", value=True
        )
        .config(key="spark.ui.enabled", value=False)
        .master("local-cluster[2, 1, 1024]")
        .getOrCreate()
    ):
        # ignore spark log output
        spark.sparkContext.setLogLevel("OFF")
        print(score_model(spark, data_path, model_uri))

```

Which seems inconsistent to me. 

This PR changes the formatting of the single-item with Python 3.8 or
older to match that of other clause headers.

```python
def run(data_path, model_uri):
    with (
        pyspark.sql.SparkSession.builder.config(
            key="spark.python.worker.reuse", value=True
        )
        .config(key="spark.ui.enabled", value=False)
        .master("local-cluster[2, 1, 1024]")
        .getOrCreate()
    ):
        # ignore spark log output
        spark.sparkContext.setLogLevel("OFF")
        print(score_model(spark, data_path, model_uri))
```

According to our versioning policy, this style change is gated behind a
preview flag.

## Test Plan

See added tests.

Added
This commit is contained in:
Micha Reiser 2024-03-09 00:56:02 +01:00 committed by GitHub
parent 4bce801065
commit b64f2ea401
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 167 additions and 4 deletions

View file

@ -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 {
@ -49,7 +50,7 @@ pub enum WithItemLayout {
/// with a, b:
/// ...
/// ```
Python38OrOlder,
Python38OrOlder { single: bool },
/// A with item where the `with` formatting adds parentheses around all context managers if necessary.
///
@ -135,8 +136,10 @@ impl FormatNodeRule<WithItem> for FormatWithItem {
)?;
}
WithItemLayout::Python38OrOlder => {
let parenthesize = if is_parenthesized {
WithItemLayout::Python38OrOlder { single } => {
let parenthesize = if (single && is_with_single_item_pre_39_enabled(f.context()))
|| is_parenthesized
{
Parenthesize::IfBreaks
} else {
Parenthesize::IfRequired

View file

@ -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()
}

View file

@ -104,7 +104,9 @@ impl FormatNodeRule<StmtWith> 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(),