Skip to content

Conversation

@shaleh
Copy link

@shaleh shaleh commented May 8, 2025

Context managers can be grouped in the same way as imports.

with foo:
    with bar:
        body

becomes

with (foo, bar):
    body

Resolves. #872

Python 3.10 allows for context managers to be grouped in the same was a
imports. Previous versions only worked with a hack:
with (foo as _, bar as _):
    body

This PR implements two fixes:
1. Fold nested with statements.

with foo:
    with bar:
        body

becomes

with (foo, bar):
    body

2. Removes the pre 3.10 hack

with (foo as _, bar as _):
    body

becomes

with (foo, bar):
    body
README.md Outdated
```

```diff
- with (foo as _, bar as _):
Copy link
Owner

Choose a reason for hiding this comment

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

I don't think this is in scope. there's no reason to write the original code with nonsense as

Copy link
Author

Choose a reason for hiding this comment

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

This was a real work around. I know, I maintain code with it. So I added code to remove it.

Copy link
Author

Choose a reason for hiding this comment

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

It is also useless assigning to underscore so also fits in with things like replacing set() with {} or similar which pyupgrade does.

Copy link
Owner

Choose a reason for hiding this comment

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

no it does not. nobody would write code like that because it doesn't help

Copy link
Author

Choose a reason for hiding this comment

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

https://docs.python.org/3.3/reference/compound_stmts.html#the-with-statement

You needed to use the as syntax to stack them in one with. But since you often did not need the name using _ was a real thing.

The options were:

with foo:
    with bar:

or

with foo as f, bar as b:

As you correctly point out, the underscore names are not helpful and with 3.10 there is nothing forcing the programmer to use a name. So it is ok to remove them. Technically.... we could do analysis of the body and following code to see if any of the names are used but that gets ugly fast so I stuck with only removing the useless underscore.

Copy link
Author

Choose a reason for hiding this comment

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

Disagree but understood. Will post a revised version shortly.

Copy link
Author

Choose a reason for hiding this comment

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

In case we misunderstood each other.

3.3 to 3.9 did require the use of as to nest context managers. with foo as f, bar as b: was the only way.
3.10 remove the requirement for the as naming. Now you can do with (foo, bar): and it follows the same pattern as import statements.

My original PR removed the now unneeded syntax of the as when migrating to 3.10 when the name was an underscore because that is was semantically safe. Any other name may have actually been used. I am moving this logic to another branch because I still want to upgrade code that I have to maintain elsewhere. I can post it as a separate PR for clarity. Or it can remain in a private repo. No worries.

Copy link
Owner

Choose a reason for hiding this comment

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

no it did not. I just tried it and it works fine without as

the only thing 3.9 added was trailing commas

Copy link
Author

@shaleh shaleh May 8, 2025

Choose a reason for hiding this comment

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

Hmm, our code was 3.7 at one point. I wonder if this was changed somewhere before 3.9. Because I know it was failing CI or we would not have added the ugly captures. Either way, it is out of the current PR. Folding the nesting is still valuable.

Copy link
Author

Choose a reason for hiding this comment

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

Ok, I did some digging. Looks like that weird underscore form came from a StackOverflow discovery back in like 3.3 or 3.5 days.... definitely worthless now.

This code below passes 3.9 but the parenthesis form at the end does not parse in 3.8 or older. The as syntax does not help.

Thanks for your patience. I have removed one more mislearned thing from my list.

from contextlib import suppress

try:
    with suppress(ValueError) as _, suppress(IndexError) as _:
        print("hello 1")
        raise ValueError("foo")
except ValueError:
    print("Not supressed")

try:
    with suppress(ValueError), suppress(IndexError):
        print("hello 2")
        raise ValueError("foo")
except ValueError:
    print("Not supressed")

try:
    with (
        suppress(ValueError),
        suppress(IndexError)
    ):
        print("hello 3")
        raise ValueError("foo")
except ValueError:
    print("Not supressed")

@shaleh
Copy link
Author

shaleh commented May 8, 2025

PR updated as requested. In doing so I found a case I had fixed and then somehow missed during subsequent debugging.

@shaleh
Copy link
Author

shaleh commented May 8, 2025

I would appreciate insight into why the 3.9 code coverage is failing. It passes for 3.10.

Drop second change.
Comment on lines +40 to +41
def flatten(xs: Iterable[Any]) -> list[Any]:
return list(itertools.chain.from_iterable(xs))
Copy link
Owner

Choose a reason for hiding this comment

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

this is not at all acceptable or typesafe



def _expand_item(indent: int, item: ast.AST) -> str:
return '{}{}'.format(' ' * indent, ast.unparse(item))
Copy link
Owner

Choose a reason for hiding this comment

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

ast.unparse does not roundtrip so it is not usable or acceptable

Choose a reason for hiding this comment

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

https://github.com/Instagram/LibCST is finally being maintained again so I guess @shaleh could use this. Alternatively, and likely recommended by @asottile, use his https://github.com/asottile/tokenize-rt

Copy link

@Kelleretoro Kelleretoro left a comment

Choose a reason for hiding this comment

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

ok

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants