From 56da848f225ad0f9a72821f0627282d6c8764fa4 Mon Sep 17 00:00:00 2001 From: Steve Dignam Date: Thu, 14 May 2020 00:10:09 -0400 Subject: [PATCH 1/3] Add narrowing Union with boolean Literal with identity check Allows for narrowing `Union[str, Literal[False]]` via `is not False`. related: https://github.com/python/mypy/pull/8368 fixes: https://github.com/python/mypy/issues/8810 --- mypy/typeops.py | 3 ++- test-data/unit/check-narrowing.test | 30 +++++++++++++++++++++++++++++ 2 files changed, 32 insertions(+), 1 deletion(-) diff --git a/mypy/typeops.py b/mypy/typeops.py index 828791333f36..d091e4b23dca 100644 --- a/mypy/typeops.py +++ b/mypy/typeops.py @@ -607,7 +607,8 @@ def is_singleton_type(typ: Type) -> bool: # TODO: Also make this return True if the type is a bool LiteralType. # Also make this return True if the type corresponds to ... (ellipsis) or NotImplemented? return ( - isinstance(typ, NoneType) or (isinstance(typ, LiteralType) and typ.is_enum_literal()) + isinstance(typ, NoneType) + or (isinstance(typ, LiteralType) and (typ.is_enum_literal() or isinstance(typ.value, bool))) or (isinstance(typ, Instance) and typ.type.is_enum and len(get_enum_values(typ)) == 1) ) diff --git a/test-data/unit/check-narrowing.test b/test-data/unit/check-narrowing.test index 45d4a625f8c7..b2f2b558feeb 100644 --- a/test-data/unit/check-narrowing.test +++ b/test-data/unit/check-narrowing.test @@ -984,3 +984,33 @@ if true_or_false: else: reveal_type(true_or_false) # N: Revealed type is 'Literal[False]' [builtins fixtures/primitives.pyi] + +[case testNarrowingLiteralIdentityCheck] +from typing import Union +from typing_extensions import Literal + +str_or_false: Union[Literal[False], str] + +if str_or_false is not False: + reveal_type(str_or_false) # N: Revealed type is 'builtins.str' +else: + reveal_type(str_or_false) # N: Revealed type is 'Literal[False]' + +if str_or_false is False: + reveal_type(str_or_false) # N: Revealed type is 'Literal[False]' +else: + reveal_type(str_or_false) # N: Revealed type is 'builtins.str' + +str_or_true: Union[Literal[True], str] + +if str_or_true is True: + reveal_type(str_or_true) # N: Revealed type is 'Literal[True]' +else: + reveal_type(str_or_true) # N: Revealed type is 'builtins.str' + +if str_or_true is not True: + reveal_type(str_or_true) # N: Revealed type is 'builtins.str' +else: + reveal_type(str_or_true) # N: Revealed type is 'Literal[True]' + +[builtins fixtures/primitives.pyi] From 75e0cf97c3af68441c48c8c4e8077c7d5b4aa000 Mon Sep 17 00:00:00 2001 From: Steve Dignam Date: Thu, 14 May 2020 00:20:24 -0400 Subject: [PATCH 2/3] fix formatting for long line was 104 characters long --- mypy/typeops.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/mypy/typeops.py b/mypy/typeops.py index d091e4b23dca..ef8d871446d0 100644 --- a/mypy/typeops.py +++ b/mypy/typeops.py @@ -608,7 +608,8 @@ def is_singleton_type(typ: Type) -> bool: # Also make this return True if the type corresponds to ... (ellipsis) or NotImplemented? return ( isinstance(typ, NoneType) - or (isinstance(typ, LiteralType) and (typ.is_enum_literal() or isinstance(typ.value, bool))) + or (isinstance(typ, LiteralType) + and (typ.is_enum_literal() or isinstance(typ.value, bool))) or (isinstance(typ, Instance) and typ.type.is_enum and len(get_enum_values(typ)) == 1) ) From c6f3006c59eae21814b9fc08f2a1ee20f3eb9fe5 Mon Sep 17 00:00:00 2001 From: Steve Dignam Date: Thu, 14 May 2020 09:02:45 -0400 Subject: [PATCH 3/3] update comment, add another test case --- mypy/typeops.py | 2 +- test-data/unit/check-narrowing.test | 12 ++++++++++++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/mypy/typeops.py b/mypy/typeops.py index ef8d871446d0..0833b15a0bee 100644 --- a/mypy/typeops.py +++ b/mypy/typeops.py @@ -604,7 +604,7 @@ def is_singleton_type(typ: Type) -> bool: constructing two distinct instances of 100001. """ typ = get_proper_type(typ) - # TODO: Also make this return True if the type is a bool LiteralType. + # TODO: # Also make this return True if the type corresponds to ... (ellipsis) or NotImplemented? return ( isinstance(typ, NoneType) diff --git a/test-data/unit/check-narrowing.test b/test-data/unit/check-narrowing.test index b2f2b558feeb..a1d9685cc43d 100644 --- a/test-data/unit/check-narrowing.test +++ b/test-data/unit/check-narrowing.test @@ -1013,4 +1013,16 @@ if str_or_true is not True: else: reveal_type(str_or_true) # N: Revealed type is 'Literal[True]' +str_or_bool_literal: Union[Literal[False], Literal[True], str] + +if str_or_bool_literal is not True: + reveal_type(str_or_bool_literal) # N: Revealed type is 'Union[Literal[False], builtins.str]' +else: + reveal_type(str_or_bool_literal) # N: Revealed type is 'Literal[True]' + +if str_or_bool_literal is not True and str_or_bool_literal is not False: + reveal_type(str_or_bool_literal) # N: Revealed type is 'builtins.str' +else: + reveal_type(str_or_bool_literal) # N: Revealed type is 'Union[Literal[False], Literal[True]]' + [builtins fixtures/primitives.pyi]