From 950ec38c119916868833cb9896dab324c5d8eadd Mon Sep 17 00:00:00 2001 From: Tushar Sadhwani Date: Tue, 1 Apr 2025 20:19:37 +0530 Subject: [PATCH] Disallow unwrapping tuples in an `as` clause (#4634) --- CHANGES.md | 2 ++ src/black/linegen.py | 2 ++ src/black/nodes.py | 11 +++++++++++ tests/data/cases/context_managers_39.py | 10 ++++++++++ 4 files changed, 25 insertions(+) diff --git a/CHANGES.md b/CHANGES.md index 65bcab1..b7520f3 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -13,6 +13,8 @@ - Fix crash while formatting expressions using the walrus operator in complex `with` statements (#4630) - Handle `# fmt: skip` followed by a comment at the end of file (#4635) +- Fix crash when a tuple appears in the `as` clause of a `with` statement + (#4634) ### Preview style diff --git a/src/black/linegen.py b/src/black/linegen.py index 1ee9f6a..52ef2cf 100644 --- a/src/black/linegen.py +++ b/src/black/linegen.py @@ -56,6 +56,7 @@ is_rpar_token, is_stub_body, is_stub_suite, + is_tuple, is_tuple_containing_star, is_tuple_containing_walrus, is_type_ignore_comment_string, @@ -1626,6 +1627,7 @@ def maybe_make_parens_invisible_in_atom( node.type not in (syms.atom, syms.expr) or is_empty_tuple(node) or is_one_tuple(node) + or (is_tuple(node) and parent.type == syms.asexpr_test) or (is_yield(node) and parent.type != syms.expr_stmt) or ( # This condition tries to prevent removing non-optional brackets diff --git a/src/black/nodes.py b/src/black/nodes.py index 3b74e2d..665cb15 100644 --- a/src/black/nodes.py +++ b/src/black/nodes.py @@ -603,6 +603,17 @@ def is_one_tuple(node: LN) -> bool: ) +def is_tuple(node: LN) -> bool: + """Return True if `node` holds a tuple.""" + if node.type != syms.atom: + return False + gexp = unwrap_singleton_parenthesis(node) + if gexp is None or gexp.type != syms.testlist_gexp: + return False + + return True + + def is_tuple_containing_walrus(node: LN) -> bool: """Return True if `node` holds a tuple that contains a walrus operator.""" if node.type != syms.atom: diff --git a/tests/data/cases/context_managers_39.py b/tests/data/cases/context_managers_39.py index c9fcf9c..ff4289d 100644 --- a/tests/data/cases/context_managers_39.py +++ b/tests/data/cases/context_managers_39.py @@ -84,6 +84,11 @@ async def func(): pass + +# don't remove the brackets here, it changes the meaning of the code. +with (x, y) as z: + pass + # output @@ -172,3 +177,8 @@ async def func(): some_other_function(argument1, argument2, argument3="some_value"), ): pass + + +# don't remove the brackets here, it changes the meaning of the code. +with (x, y) as z: + pass