Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Format StmtFor #5163

Merged
merged 3 commits into from
Jun 21, 2023
Merged

Format StmtFor #5163

merged 3 commits into from
Jun 21, 2023

Conversation

davidszotten
Copy link
Contributor

Summary

format StmtFor

still trying to learn how to help out with the formatter. trying something slightly more advanced than break

mostly copied form StmtWhile

Test Plan

snapshots

@davidszotten davidszotten changed the title basic impl copied from StmtWhile StmtFor: basic impl copied from StmtWhile Jun 17, 2023
@github-actions
Copy link
Contributor

github-actions bot commented Jun 17, 2023

PR Check Results

Ecosystem

✅ ecosystem check detected no changes.

Benchmark

Linux

group                                      main                                   pr
-----                                      ----                                   --
formatter/large/dataset.py                 1.00      6.9±0.02ms     5.9 MB/sec    1.01      6.9±0.01ms     5.9 MB/sec
formatter/numpy/ctypeslib.py               1.00   1378.2±2.69µs    12.1 MB/sec    1.04   1433.1±4.34µs    11.6 MB/sec
formatter/numpy/globals.py                 1.00    133.6±0.27µs    22.1 MB/sec    1.00    133.2±0.20µs    22.1 MB/sec
formatter/pydantic/types.py                1.00      2.9±0.01ms     8.9 MB/sec    1.01      2.9±0.00ms     8.9 MB/sec
linter/all-rules/large/dataset.py          1.00     13.7±0.04ms     3.0 MB/sec    1.02     13.9±0.05ms     2.9 MB/sec
linter/all-rules/numpy/ctypeslib.py        1.00      3.4±0.00ms     4.9 MB/sec    1.01      3.5±0.01ms     4.8 MB/sec
linter/all-rules/numpy/globals.py          1.00    363.6±3.56µs     8.1 MB/sec    1.00    365.3±5.50µs     8.1 MB/sec
linter/all-rules/pydantic/types.py         1.00      6.0±0.01ms     4.2 MB/sec    1.01      6.1±0.01ms     4.2 MB/sec
linter/default-rules/large/dataset.py      1.00      7.0±0.01ms     5.8 MB/sec    1.03      7.2±0.01ms     5.6 MB/sec
linter/default-rules/numpy/ctypeslib.py    1.00   1461.4±5.73µs    11.4 MB/sec    1.02   1497.3±3.42µs    11.1 MB/sec
linter/default-rules/numpy/globals.py      1.00    154.4±0.14µs    19.1 MB/sec    1.02    157.0±0.69µs    18.8 MB/sec
linter/default-rules/pydantic/types.py     1.00      3.2±0.01ms     8.0 MB/sec    1.02      3.3±0.01ms     7.8 MB/sec

Windows

group                                      main                                   pr
-----                                      ----                                   --
formatter/large/dataset.py                 1.01      9.8±0.34ms     4.2 MB/sec    1.00      9.6±0.37ms     4.2 MB/sec
formatter/numpy/ctypeslib.py               1.00  1997.8±75.16µs     8.3 MB/sec    1.01      2.0±0.12ms     8.3 MB/sec
formatter/numpy/globals.py                 1.03   199.0±13.21µs    14.8 MB/sec    1.00   192.7±12.89µs    15.3 MB/sec
formatter/pydantic/types.py                1.02      4.1±0.20ms     6.2 MB/sec    1.00      4.0±0.17ms     6.3 MB/sec
linter/all-rules/large/dataset.py          1.02     19.4±0.57ms     2.1 MB/sec    1.00     19.0±0.64ms     2.1 MB/sec
linter/all-rules/numpy/ctypeslib.py        1.02      5.1±0.18ms     3.3 MB/sec    1.00      5.0±0.25ms     3.3 MB/sec
linter/all-rules/numpy/globals.py          1.00   621.7±30.06µs     4.7 MB/sec    1.01   625.6±33.97µs     4.7 MB/sec
linter/all-rules/pydantic/types.py         1.00      8.7±0.34ms     2.9 MB/sec    1.03      8.9±0.46ms     2.9 MB/sec
linter/default-rules/large/dataset.py      1.00      9.9±0.31ms     4.1 MB/sec    1.00      9.9±0.32ms     4.1 MB/sec
linter/default-rules/numpy/ctypeslib.py    1.00      2.2±0.10ms     7.7 MB/sec    1.00      2.2±0.13ms     7.7 MB/sec
linter/default-rules/numpy/globals.py      1.05   252.1±12.58µs    11.7 MB/sec    1.00   239.2±12.27µs    12.3 MB/sec
linter/default-rules/pydantic/types.py     1.02      4.5±0.17ms     5.6 MB/sec    1.00      4.4±0.19ms     5.7 MB/sec

Copy link
Member

@konstin konstin left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You picked a tough node but this looks good barring ExprTuple always keeping its parentheses (because i missed that case in ExprTuple)

# trailing else body comment


for (
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

black doesn't break this for statement, but i think this actually makes more sense

Copy link
Contributor Author

@davidszotten davidszotten Jun 18, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hm. interesting (how did you notice the difference?)

at this point, do we have a policy/guidelines for matching black (to help people migrate) vs improving formatting? maybe we raise an issue or otherwise to see if the black maintainers also prefer this formatting?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Our goal is to match black except in edge cases, but we haven't formalized what exactly this means. I'll discuss with @MichaReiser tomorrow what we want do about this

how did you notice the difference?

copied the code and ran black over it ;)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

copied the code and ran black over it ;)

is it only the tests from black's test suite that automatically compare to black's output?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

at the moment, yes

text("for"),
space(),
// TODO: the `IfBreaks` is currently ignored by
// https://github.com/astral-sh/ruff/blob/4b9b6829dccabdd4faf6efa6a118b4868347a701/crates/ruff_python_formatter/src/expression/expr_tuple.rs#L78
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We might need a new setting that actually removes expression parentheses if not used, i'll experiment with this

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

do we want to keep this open pending that or merge as is for now?

@davidszotten
Copy link
Contributor Author

You picked a though node

Impl is mostly copied from StmtWhile which made it easier. Still trying to learn my way around

@konstin konstin added the formatter Related to the formatter label Jun 18, 2023
konstin added a commit that referenced this pull request Jun 21, 2023
## Motivation

While black keeps parentheses nearly everywhere, with the notable exception of in the body of for loops:
```python
for (a, b) in x:
    pass
```
becomes
```python
for a, b in x:
    pass
```

This currently blocks #5163, which this PR should unblock.

## Solution

This changes the `ExprTuple` formatting option to include one additional option that removes the parentheses when not using magic trailing comma and not breaking. It is supposed to be used through
```
#[derive(Debug)]
struct ExprTupleWithoutParentheses<'a>(&'a Expr);

impl Format<PyFormatContext<'_>> for ExprTupleWithoutParentheses<'_> {
    fn fmt(&self, f: &mut Formatter<PyFormatContext<'_>>) -> FormatResult<()> {
        match self.0 {
            Expr::Tuple(expr_tuple) => expr_tuple
                .format()
                .with_options(TupleParentheses::StripInsideForLoop)
                .fmt(f),
            other => other.format().with_options(Parenthesize::IfBreaks).fmt(f),
        }
    }
}
```

## Testing

The for loop formatting isn't merged due to missing this (and i didn't want to create more git weirdness across two people), but I've confirmed that when applying this to while loops instead of for loops, then
```rust
        write!(
            f,
            [
                text("while"),
                space(),
                ExprTupleWithoutParentheses(test.as_ref()),
                text(":"),
                trailing_comments(trailing_condition_comments),
                block_indent(&body.format())
            ]
        )?;
```
makes
```python
while (a, b):
    pass

while (
    ajssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssa,
    b,
):
    pass

while (a,b,):
    pass
```
formatted as
```python
while a, b:
    pass

while (
    ajssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssa,
    b,
):
    pass

while (
    a,
    b,
):
    pass
```
@konstin
Copy link
Member

konstin commented Jun 21, 2023

Finally got around to it: #5175

This should hopefully unblock you, using

#[derive(Debug)]
struct ExprTupleWithoutParentheses<'a>(&'a Expr);

impl Format<PyFormatContext<'_>> for ExprTupleWithoutParentheses<'_> {
    fn fmt(&self, f: &mut Formatter<PyFormatContext<'_>>) -> FormatResult<()> {
        match self.0 {
            Expr::Tuple(expr_tuple) => expr_tuple
                .format()
                .with_options(TupleParentheses::StripInsideForLoop)
                .fmt(f),
            other => other.format().with_options(Parenthesize::IfBreaks).fmt(f),
        }
    }
}

and ExprTupleWithoutParentheses(target.as_ref()),

block_indent(&orelse.format())
]
)?;
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you add a debug_assert! here that in the orelse.is_empty() case or_else_comments is also empty?

konstin added a commit that referenced this pull request Jun 21, 2023
## Motivation

While black keeps parentheses nearly everywhere, with the notable exception of in the body of for loops:
```python
for (a, b) in x:
    pass
```
becomes
```python
for a, b in x:
    pass
```

This currently blocks #5163, which this PR should unblock.

## Solution

This changes the `ExprTuple` formatting option to include one additional option that removes the parentheses when not using magic trailing comma and not breaking. It is supposed to be used through
```
#[derive(Debug)]
struct ExprTupleWithoutParentheses<'a>(&'a Expr);

impl Format<PyFormatContext<'_>> for ExprTupleWithoutParentheses<'_> {
    fn fmt(&self, f: &mut Formatter<PyFormatContext<'_>>) -> FormatResult<()> {
        match self.0 {
            Expr::Tuple(expr_tuple) => expr_tuple
                .format()
                .with_options(TupleParentheses::StripInsideForLoop)
                .fmt(f),
            other => other.format().with_options(Parenthesize::IfBreaks).fmt(f),
        }
    }
}
```

## Testing

The for loop formatting isn't merged due to missing this (and i didn't want to create more git weirdness across two people), but I've confirmed that when applying this to while loops instead of for loops, then
```rust
        write!(
            f,
            [
                text("while"),
                space(),
                ExprTupleWithoutParentheses(test.as_ref()),
                text(":"),
                trailing_comments(trailing_condition_comments),
                block_indent(&body.format())
            ]
        )?;
```
makes
```python
while (a, b):
    pass

while (
    ajssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssa,
    b,
):
    pass

while (a,b,):
    pass
```
formatted as
```python
while a, b:
    pass

while (
    ajssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssa,
    b,
):
    pass

while (
    a,
    b,
):
    pass
```
konstin added a commit that referenced this pull request Jun 21, 2023
## Motivation

While black keeps parentheses nearly everywhere, the notable exception
is in the body of for loops:
```python
for (a, b) in x:
    pass
```
becomes
```python
for a, b in x:
    pass
```

This currently blocks #5163, which this PR should unblock.

## Solution

This changes the `ExprTuple` formatting option to include one additional
option that removes the parentheses when not using magic trailing comma
and not breaking. It is supposed to be used through
```rust
#[derive(Debug)]
struct ExprTupleWithoutParentheses<'a>(&'a Expr);

impl Format<PyFormatContext<'_>> for ExprTupleWithoutParentheses<'_> {
    fn fmt(&self, f: &mut Formatter<PyFormatContext<'_>>) -> FormatResult<()> {
        match self.0 {
            Expr::Tuple(expr_tuple) => expr_tuple
                .format()
                .with_options(TupleParentheses::StripInsideForLoop)
                .fmt(f),
            other => other.format().with_options(Parenthesize::IfBreaks).fmt(f),
        }
    }
}
```


## Testing

The for loop formatting isn't merged due to missing this (and i didn't
want to create more git weirdness across two people), but I've confirmed
that when applying this to while loops instead of for loops, then
```rust
        write!(
            f,
            [
                text("while"),
                space(),
                ExprTupleWithoutParentheses(test.as_ref()),
                text(":"),
                trailing_comments(trailing_condition_comments),
                block_indent(&body.format())
            ]
        )?;
```
makes
```python
while (a, b):
    pass

while (
    ajssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssa,
    b,
):
    pass

while (a,b,):
    pass
```
formatted as
```python
while a, b:
    pass

while (
    ajssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssa,
    b,
):
    pass

while (
    a,
    b,
):
    pass
```
Copy link
Member

@konstin konstin left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

thanks

@konstin konstin changed the title StmtFor: basic impl copied from StmtWhile Fromat StmtFor Jun 21, 2023
@konstin konstin changed the title Fromat StmtFor Format StmtFor Jun 21, 2023
@konstin konstin merged commit 1eccbbb into astral-sh:main Jun 21, 2023
16 checks passed
@MichaReiser MichaReiser linked an issue Jun 22, 2023 that may be closed by this pull request
2 tasks
@davidszotten davidszotten deleted the format-for branch July 7, 2023 20:48
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
formatter Related to the formatter
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Formatter: For / AsyncFor
2 participants