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

fix: unpack unions inside tuples in except handlers #17762

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 11 additions & 2 deletions mypy/checker.py
Original file line number Diff line number Diff line change
Expand Up @@ -4858,16 +4858,25 @@ def get_types_from_except_handler(self, typ: Type, n: Expression) -> list[Type]:
"""Helper for check_except_handler_test to retrieve handler types."""
typ = get_proper_type(typ)
if isinstance(typ, TupleType):
return typ.items
# avoid recursion here, since we can accidentally unpack tuples inside
merged_type = make_simplified_union(typ.items)
if isinstance(merged_type, UnionType):
return merged_type.relevant_items()
return [merged_type]
elif isinstance(typ, UnionType):
# recursion is fine here for top-level Union
return [
union_typ
for item in typ.relevant_items()
for union_typ in self.get_types_from_except_handler(item, n)
]
elif is_named_instance(typ, "builtins.tuple"):
# variadic tuple
return [typ.args[0]]
# avoid recursion here too
merged_type = make_simplified_union((typ.args[0],))
if isinstance(merged_type, UnionType):
return merged_type.relevant_items()
return [merged_type]
else:
return [typ]

Expand Down
51 changes: 51 additions & 0 deletions test-data/unit/check-statements.test
Original file line number Diff line number Diff line change
Expand Up @@ -794,6 +794,57 @@ def error_in_variadic(exc: Tuple[int, ...]) -> None:

[builtins fixtures/tuple.pyi]

[case testExceptWithMultipleTypes5]
from typing import Tuple, Type, Union

class E1(BaseException): pass
class E2(BaseException): pass
class E3(BaseException): pass

def union_in_variadic(exc: Tuple[Union[Type[E1], Type[E2]], ...]) -> None:
try:
pass
except exc as e:
reveal_type(e) # N: Revealed type is "Union[__main__.E1, __main__.E2]"

def nested_union_in_variadic(exc: Tuple[Union[Type[E1], Union[Type[E2], Type[E3]]], ...]) -> None:
try:
pass
except exc as e:
reveal_type(e) # N: Revealed type is "Union[__main__.E1, __main__.E2, __main__.E3]"

def union_in_tuple(exc: Tuple[Union[Type[E1], Type[E2]], Type[E3]]) -> None:
try:
pass
except exc as e:
reveal_type(e) # N: Revealed type is "Union[__main__.E1, __main__.E2, __main__.E3]"

def error_in_variadic_union(exc: Tuple[Union[Type[E1], int], ...]) -> None:
try:
pass
except exc as e: # E: Exception type must be derived from BaseException (or be a tuple of exception classes)
pass

def error_in_variadic_nested_union(exc: Tuple[Union[Type[E1], Union[Type[E2], int]], ...]) -> None:
try:
pass
except exc as e: # E: Exception type must be derived from BaseException (or be a tuple of exception classes)
pass

def error_in_tuple_inside_variadic_nested_union(exc: Tuple[Union[Type[E1], Union[Type[E2], Tuple[Type[E3]]]], ...]) -> None:
try:
pass
except exc as e: # E: Exception type must be derived from BaseException (or be a tuple of exception classes)
pass

def error_in_tuple_union(exc: Tuple[Union[Type[E1], Type[E2]], Union[Type[E3], int]]) -> None:
try:
pass
except exc as e: # E: Exception type must be derived from BaseException (or be a tuple of exception classes)
pass

[builtins fixtures/tuple.pyi]

[case testExceptWithAnyTypes]
from typing import Any

Expand Down
Loading