Skip to content

Commit cef4154

Browse files
fix UnionType.__or__ and add UnionType.__getitem__ (#14687)
Co-authored-by: Alex Waygood <[email protected]>
1 parent 2cc9c16 commit cef4154

File tree

2 files changed

+24
-3
lines changed

2 files changed

+24
-3
lines changed

stdlib/@tests/test_cases/check_types.py

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,11 @@
33
import sys
44
import types
55
from collections import UserDict
6-
from typing import Union
6+
from typing import Any, Literal, TypeVar, Union
77
from typing_extensions import assert_type
88

9+
_T = TypeVar("_T")
10+
911
# test `types.SimpleNamespace`
1012

1113
# Valid:
@@ -58,3 +60,13 @@ def foo(self, value: int) -> None:
5860
@foo.deleter
5961
def foo(self) -> None:
6062
self._value = None
63+
64+
65+
if sys.version_info > (3, 10):
66+
union_type = int | list[_T]
67+
68+
# ideally this would be `_SpecialForm` (Union)
69+
assert_type(union_type | Literal[1], types.UnionType | Any)
70+
# Both mypy and pyright special-case this operation,
71+
# but in different ways, so we just check that no error is emitted:
72+
_ = union_type[int]

stdlib/types.pyi

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -717,10 +717,19 @@ if sys.version_info >= (3, 10):
717717
def __args__(self) -> tuple[Any, ...]: ...
718718
@property
719719
def __parameters__(self) -> tuple[Any, ...]: ...
720-
def __or__(self, value: Any, /) -> UnionType: ...
721-
def __ror__(self, value: Any, /) -> UnionType: ...
720+
# `(int | str) | Literal["foo"]` returns a generic alias to an instance of `_SpecialForm` (`Union`).
721+
# Normally we'd express this using the return type of `_SpecialForm.__ror__`,
722+
# but because `UnionType.__or__` accepts `Any`, type checkers will use
723+
# the return type of `UnionType.__or__` to infer the result of this operation
724+
# rather than `_SpecialForm.__ror__`. To mitigate this, we use `| Any`
725+
# in the return type of `UnionType.__(r)or__`.
726+
def __or__(self, value: Any, /) -> UnionType | Any: ...
727+
def __ror__(self, value: Any, /) -> UnionType | Any: ...
722728
def __eq__(self, value: object, /) -> bool: ...
723729
def __hash__(self) -> int: ...
730+
# you can only subscript a `UnionType` instance if at least one of the elements
731+
# in the union is a generic alias instance that has a non-empty `__parameters__`
732+
def __getitem__(self, parameters: Any) -> object: ...
724733

725734
if sys.version_info >= (3, 13):
726735
@final

0 commit comments

Comments
 (0)