Skip to content

Commit

Permalink
Fix new style union syntax in type aliases
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
JukkaL committed Nov 4, 2022
1 parent 796068d commit 5856657
Show file tree
Hide file tree
Showing 4 changed files with 36 additions and 4 deletions.
7 changes: 3 additions & 4 deletions mypy/checkexpr.py
Original file line number Diff line number Diff line change
Expand Up @@ -3869,9 +3869,8 @@ class LongName(Generic[T]): ...
else:
if alias_definition:
return AnyType(TypeOfAny.special_form)
# This type is invalid in most runtime contexts, give it an 'object' type.
# TODO: Use typing._SpecialForm instead?
return self.named_type("builtins.object")
# This type is valid in some runtime contexts (e.g. it may have __or__).
return self.named_type("typing._SpecialForm")

def apply_type_arguments_to_callable(
self, tp: Type, args: Sequence[Type], ctx: Context
Expand Down Expand Up @@ -4741,7 +4740,7 @@ def has_member(self, typ: Type, member: str) -> bool:
typ = typ.fallback
if isinstance(typ, Instance):
return typ.type.has_readable_member(member)
if isinstance(typ, CallableType) and typ.is_type_obj():
if isinstance(typ, FunctionLike) and typ.is_type_obj():
return typ.fallback.type.has_readable_member(member)
elif isinstance(typ, AnyType):
return True
Expand Down
16 changes: 16 additions & 0 deletions test-data/unit/check-python310.test
Original file line number Diff line number Diff line change
Expand Up @@ -1788,3 +1788,19 @@ def f6(a: object) -> None:
case _ if y is not None: # E: Name "y" may be undefined
pass
[builtins fixtures/tuple.pyi]

[case testTypeAliasWithNewUnionSyntaxAndNoneLeftOperand]
from typing import overload
class C:
@overload
def __init__(self) -> None: pass
@overload
def __init__(self, x: int) -> None: pass
def __init__(self, x=0):
pass

class D: pass

X = None | C
Y = None | D
[builtins fixtures/type.pyi]
1 change: 1 addition & 0 deletions test-data/unit/fixtures/type.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ class list(Generic[T]): pass
class type(Generic[T]):
__name__: str
def __or__(self, other: Union[type, None]) -> type: pass
def __ror__(self, other: Union[type, None]) -> type: pass
def mro(self) -> List['type']: pass

class tuple(Generic[T]): pass
Expand Down
16 changes: 16 additions & 0 deletions test-data/unit/pythoneval.test
Original file line number Diff line number Diff line change
Expand Up @@ -1660,3 +1660,19 @@ _testNarrowTypeForDictKeys.py:7: note: Revealed type is "builtins.str"
_testNarrowTypeForDictKeys.py:9: note: Revealed type is "Union[builtins.str, None]"
_testNarrowTypeForDictKeys.py:14: note: Revealed type is "builtins.str"
_testNarrowTypeForDictKeys.py:16: note: Revealed type is "Union[builtins.str, None]"

[case testTypeAliasWithNewStyleUnion]
# flags: --python-version 3.10
from typing import Literal

Foo = Literal[1, 2]
reveal_type(Foo)
Bar = Foo | Literal[3]

U1 = int | str
U2 = U1 | bytes

Opt1 = None | int
Opt2 = None | float
[out]
_testTypeAliasWithNewStyleUnion.py:5: note: Revealed type is "typing._SpecialForm"

0 comments on commit 5856657

Please sign in to comment.