Skip to content

Commit 5856657

Browse files
committed
Fix new style union syntax in type aliases
Fix Python 3.10 `|` union syntax in type aliases, when one of the operands is a type alias or a type with an overloaded `__init__`. Fixes #12368. Fixes #12005. Fixes #11426.
1 parent 796068d commit 5856657

File tree

4 files changed

+36
-4
lines changed

4 files changed

+36
-4
lines changed

mypy/checkexpr.py

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3869,9 +3869,8 @@ class LongName(Generic[T]): ...
38693869
else:
38703870
if alias_definition:
38713871
return AnyType(TypeOfAny.special_form)
3872-
# This type is invalid in most runtime contexts, give it an 'object' type.
3873-
# TODO: Use typing._SpecialForm instead?
3874-
return self.named_type("builtins.object")
3872+
# This type is valid in some runtime contexts (e.g. it may have __or__).
3873+
return self.named_type("typing._SpecialForm")
38753874

38763875
def apply_type_arguments_to_callable(
38773876
self, tp: Type, args: Sequence[Type], ctx: Context
@@ -4741,7 +4740,7 @@ def has_member(self, typ: Type, member: str) -> bool:
47414740
typ = typ.fallback
47424741
if isinstance(typ, Instance):
47434742
return typ.type.has_readable_member(member)
4744-
if isinstance(typ, CallableType) and typ.is_type_obj():
4743+
if isinstance(typ, FunctionLike) and typ.is_type_obj():
47454744
return typ.fallback.type.has_readable_member(member)
47464745
elif isinstance(typ, AnyType):
47474746
return True

test-data/unit/check-python310.test

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1788,3 +1788,19 @@ def f6(a: object) -> None:
17881788
case _ if y is not None: # E: Name "y" may be undefined
17891789
pass
17901790
[builtins fixtures/tuple.pyi]
1791+
1792+
[case testTypeAliasWithNewUnionSyntaxAndNoneLeftOperand]
1793+
from typing import overload
1794+
class C:
1795+
@overload
1796+
def __init__(self) -> None: pass
1797+
@overload
1798+
def __init__(self, x: int) -> None: pass
1799+
def __init__(self, x=0):
1800+
pass
1801+
1802+
class D: pass
1803+
1804+
X = None | C
1805+
Y = None | D
1806+
[builtins fixtures/type.pyi]

test-data/unit/fixtures/type.pyi

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ class list(Generic[T]): pass
1313
class type(Generic[T]):
1414
__name__: str
1515
def __or__(self, other: Union[type, None]) -> type: pass
16+
def __ror__(self, other: Union[type, None]) -> type: pass
1617
def mro(self) -> List['type']: pass
1718

1819
class tuple(Generic[T]): pass

test-data/unit/pythoneval.test

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1660,3 +1660,19 @@ _testNarrowTypeForDictKeys.py:7: note: Revealed type is "builtins.str"
16601660
_testNarrowTypeForDictKeys.py:9: note: Revealed type is "Union[builtins.str, None]"
16611661
_testNarrowTypeForDictKeys.py:14: note: Revealed type is "builtins.str"
16621662
_testNarrowTypeForDictKeys.py:16: note: Revealed type is "Union[builtins.str, None]"
1663+
1664+
[case testTypeAliasWithNewStyleUnion]
1665+
# flags: --python-version 3.10
1666+
from typing import Literal
1667+
1668+
Foo = Literal[1, 2]
1669+
reveal_type(Foo)
1670+
Bar = Foo | Literal[3]
1671+
1672+
U1 = int | str
1673+
U2 = U1 | bytes
1674+
1675+
Opt1 = None | int
1676+
Opt2 = None | float
1677+
[out]
1678+
_testTypeAliasWithNewStyleUnion.py:5: note: Revealed type is "typing._SpecialForm"

0 commit comments

Comments
 (0)