Skip to content

Commit 6ea100d

Browse files
László Vaskócsernazs
László Vaskó
andcommitted
Union types: Support narrowing to Ellipsis (...) cases of Unions
After this change, narrowing to `Ellipsis` works similarly with regards to narrowing as `None` in `Optional`s It would be a good followup refactor to delegate some of the logic from `is_singleton_type` to the actual mypy types so they could decide for themselves if they are representing singleton objects Fixes: #13117 Co-authored-by: Zsolt Cserna <[email protected]>
1 parent b13a450 commit 6ea100d

File tree

3 files changed

+20
-5
lines changed

3 files changed

+20
-5
lines changed

mypy/typeops.py

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -732,8 +732,8 @@ def is_singleton_type(typ: Type) -> bool:
732732
'is_singleton_type(t)' returns True if and only if the expression 'a is b' is
733733
always true.
734734
735-
Currently, this returns True when given NoneTypes, enum LiteralTypes and
736-
enum types with a single value.
735+
Currently, this returns True when given NoneTypes, enum LiteralTypes,
736+
enum types with a single value and ... (Ellipses).
737737
738738
Note that other kinds of LiteralTypes cannot count as singleton types. For
739739
example, suppose we do 'a = 100000 + 1' and 'b = 100001'. It is not guaranteed
@@ -742,12 +742,14 @@ def is_singleton_type(typ: Type) -> bool:
742742
"""
743743
typ = get_proper_type(typ)
744744
# TODO:
745-
# Also make this return True if the type corresponds to ... (ellipsis) or NotImplemented?
745+
# Also make this return True if the type corresponds to NotImplemented?
746746
return (
747747
isinstance(typ, NoneType)
748748
or (isinstance(typ, LiteralType)
749749
and (typ.is_enum_literal() or isinstance(typ.value, bool)))
750-
or (isinstance(typ, Instance) and typ.type.is_enum and len(get_enum_values(typ)) == 1)
750+
or (isinstance(typ, Instance) and (
751+
typ.type.is_enum and len(get_enum_values(typ)) == 1
752+
or typ.type.fullname == 'builtins.ellipsis'))
751753
)
752754

753755

test-data/unit/check-unions.test

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,16 @@ def f() -> Union[int, None]: pass
137137
x = 1
138138
x = f()
139139

140+
[case testUnionWithEllipsis]
141+
from typing import Union
142+
def f(x: Union[int, EllipsisType]) -> int:
143+
if x is Ellipsis:
144+
reveal_type(x) # N: Revealed type is "builtins.ellipsis"
145+
x = 1
146+
reveal_type(x) # N: Revealed type is "builtins.int"
147+
return x
148+
[builtins fixtures/isinstancelist.pyi]
149+
140150
[case testOptional]
141151
from typing import Optional
142152
def f(x: Optional[int]) -> None: pass

test-data/unit/fixtures/isinstancelist.pyi

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,12 @@ class type:
1010
def __init__(self, x) -> None: pass
1111

1212
class function: pass
13-
class ellipsis: pass
1413
class classmethod: pass
1514

15+
class ellipsis: pass
16+
EllipsisType = ellipsis
17+
Ellipsis = ellipsis()
18+
1619
def isinstance(x: object, t: Union[type, Tuple]) -> bool: pass
1720
def issubclass(x: object, t: Union[type, Tuple]) -> bool: pass
1821

0 commit comments

Comments
 (0)