From ae3accba3e5948007cb6a3a46f710037fcae4fef Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Tue, 2 May 2017 14:48:43 +0100 Subject: [PATCH 1/2] Fix overload handling with union types in signatures Previously `None` was not considered as more precise than `Optional[x]`, which was obviously incorrect. Fixed the implementation of type precision checking to match the description, how that proper subtype checking works. Fixes the original example in #3295. --- mypy/subtypes.py | 2 +- mypy/test/testtypes.py | 4 ++-- test-data/unit/check-optional.test | 15 +++++++++++++++ 3 files changed, 18 insertions(+), 3 deletions(-) diff --git a/mypy/subtypes.py b/mypy/subtypes.py index 6535dfd4496a3..cfd236cc1efe5 100644 --- a/mypy/subtypes.py +++ b/mypy/subtypes.py @@ -720,4 +720,4 @@ def is_more_precise(t: Type, s: Type) -> bool: # Fall back to subclass check and ignore other properties of the callable. return is_proper_subtype(t.fallback, s) return is_proper_subtype(t, s) - return sametypes.is_same_type(t, s) + return is_proper_subtype(t, s) diff --git a/mypy/test/testtypes.py b/mypy/test/testtypes.py index 74a71f4e984bd..4302db7d8d46b 100644 --- a/mypy/test/testtypes.py +++ b/mypy/test/testtypes.py @@ -175,11 +175,11 @@ def test_is_more_precise(self) -> None: assert_true(is_more_precise(fx.b, fx.anyt)) assert_true(is_more_precise(self.tuple(fx.b, fx.a), self.tuple(fx.b, fx.a))) + assert_true(is_more_precise(self.tuple(fx.b, fx.b), + self.tuple(fx.b, fx.a))) assert_false(is_more_precise(fx.a, fx.b)) assert_false(is_more_precise(fx.anyt, fx.b)) - assert_false(is_more_precise(self.tuple(fx.b, fx.b), - self.tuple(fx.b, fx.a))) # is_proper_subtype diff --git a/test-data/unit/check-optional.test b/test-data/unit/check-optional.test index e4fb2a16025c3..a025ed5f78af8 100644 --- a/test-data/unit/check-optional.test +++ b/test-data/unit/check-optional.test @@ -596,3 +596,18 @@ x: Optional[Union[int, str]] reveal_type(x) # E: Revealed type is 'Union[builtins.int, builtins.str, builtins.None]' y: Optional[Union[int, None]] reveal_type(y) # E: Revealed type is 'Union[builtins.int, builtins.None]' + +[case testOverloadWithNoneAndOptional] +from typing import overload, Optional + +@overload +def f(x: int) -> str: ... +@overload +def f(x: Optional[int]) -> Optional[str]: ... +def f(x): return x + +reveal_type(f(1)) +reveal_type(f(None)) +[out] +main:9: error: Revealed type is 'builtins.str' +main:10: error: Revealed type is 'Union[builtins.str, builtins.None]' From a606f5dadadea3d38f7447d9930a0f1ccf4747bc Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Wed, 28 Jun 2017 17:23:56 +0100 Subject: [PATCH 2/2] Clean up --- mypy/subtypes.py | 19 +++++++------------ test-data/unit/check-optional.test | 9 ++++----- 2 files changed, 11 insertions(+), 17 deletions(-) diff --git a/mypy/subtypes.py b/mypy/subtypes.py index 1c0ad9dc8b4c5..9d250718030e5 100644 --- a/mypy/subtypes.py +++ b/mypy/subtypes.py @@ -717,19 +717,14 @@ def visit_type_type(self, left: TypeType) -> bool: return False -def is_more_precise(t: Type, s: Type) -> bool: - """Check if t is a more precise type than s. +def is_more_precise(left: Type, right: Type) -> bool: + """Check if left is a more precise type than right. - A t is a proper subtype of s, t is also more precise than s. Also, if - s is Any, t is more precise than s for any t. Finally, if t is the same - type as s, t is more precise than s. + A left is a proper subtype of right, left is also more precise than + right. Also, if right is Any, left is more precise than right, for + any left. """ # TODO Should List[int] be more precise than List[Any]? - if isinstance(s, AnyType): + if isinstance(right, AnyType): return True - if isinstance(s, Instance): - if isinstance(t, CallableType): - # Fall back to subclass check and ignore other properties of the callable. - return is_proper_subtype(t.fallback, s) - return is_proper_subtype(t, s) - return is_proper_subtype(t, s) + return is_proper_subtype(left, right) diff --git a/test-data/unit/check-optional.test b/test-data/unit/check-optional.test index 0a4c539fd69a3..b5d484517b025 100644 --- a/test-data/unit/check-optional.test +++ b/test-data/unit/check-optional.test @@ -604,11 +604,10 @@ def f(x: int) -> str: ... def f(x: Optional[int]) -> Optional[str]: ... def f(x): return x -reveal_type(f(1)) -reveal_type(f(None)) -[out] -main:9: error: Revealed type is 'builtins.str' -main:10: error: Revealed type is 'Union[builtins.str, builtins.None]' +reveal_type(f(1)) # E: Revealed type is 'builtins.str' +reveal_type(f(None)) # E: Revealed type is 'Union[builtins.str, builtins.None]' +x: Optional[int] +reveal_type(f(x)) # E: Revealed type is 'Union[builtins.str, builtins.None]' [case testUnionTruthinessTracking] from typing import Optional, Any