Fix crash when a tuple is used as a ContextManager (#4646)

This commit is contained in:
Pedro Mezacasa Muller 2025-04-09 01:42:17 -03:00 committed by GitHub
parent a41dc89f1f
commit d0ff3bd6cb
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 65 additions and 0 deletions

View File

@ -15,6 +15,7 @@
- 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)
- Fix crash when tuple is used as a context manager inside a `with` statement (#4646)
### Preview style

View File

@ -40,6 +40,7 @@
ensure_visible,
fstring_to_string,
get_annotation_type,
has_sibling_with_type,
is_arith_like,
is_async_stmt_or_funcdef,
is_atom_with_invisible_parens,
@ -1628,6 +1629,11 @@ def maybe_make_parens_invisible_in_atom(
or is_empty_tuple(node)
or is_one_tuple(node)
or (is_tuple(node) and parent.type == syms.asexpr_test)
or (
is_tuple(node)
and parent.type == syms.with_stmt
and has_sibling_with_type(node, token.COMMA)
)
or (is_yield(node) and parent.type != syms.expr_stmt)
or (
# This condition tries to prevent removing non-optional brackets

View File

@ -1058,3 +1058,21 @@ def furthest_ancestor_with_last_leaf(leaf: Leaf) -> LN:
while node.parent and node.parent.children and node is node.parent.children[-1]:
node = node.parent
return node
def has_sibling_with_type(node: LN, type: int) -> bool:
# Check previous siblings
sibling = node.prev_sibling
while sibling is not None:
if sibling.type == type:
return True
sibling = sibling.prev_sibling
# Check next siblings
sibling = node.next_sibling
while sibling is not None:
if sibling.type == type:
return True
sibling = sibling.next_sibling
return False

View File

@ -89,6 +89,26 @@ async def func():
with (x, y) as z:
pass
# don't remove the brackets here, it changes the meaning of the code.
# even though the code will always trigger a runtime error
with (name_5, name_4), name_5:
pass
def test_tuple_as_contextmanager():
from contextlib import nullcontext
try:
with (nullcontext(),nullcontext()),nullcontext():
pass
except TypeError:
# test passed
pass
else:
# this should be a type error
assert False
# output
@ -182,3 +202,23 @@ async def func():
# don't remove the brackets here, it changes the meaning of the code.
with (x, y) as z:
pass
# don't remove the brackets here, it changes the meaning of the code.
# even though the code will always trigger a runtime error
with (name_5, name_4), name_5:
pass
def test_tuple_as_contextmanager():
from contextlib import nullcontext
try:
with (nullcontext(), nullcontext()), nullcontext():
pass
except TypeError:
# test passed
pass
else:
# this should be a type error
assert False