8000 Relax overload checks to allow return types to be regular subtypes (#… · python/mypy@74371df · GitHub
[go: up one dir, main page]

Skip to content

Commit 74371df

Browse files
Michael0x2ailevkivskyi
authored andcommitted
Relax overload checks to allow return types to be regular subtypes (#5064)
This pull request relaxes the overlapping overload checks so that if the parameter types of an alternative are a proper subtype of another, the return type needs to only be a regular subtype, not a proper subtype. The net effect is that the return type of the first alternative is allowed to be 'Any'. This *does* make overloads slightly less safe, but 'Any' was always meant to be an escape hatch, so I'm figuring this is fine. **Rationale**: My [proposed changes on overhauling overloads][0] require a few changes to typeshed -- [relevant pull request here][1]. Rather then trying to change both at the same time, I want to get typeshed working first. This was pretty easy to do, apart from the attrs stubs. The issue basically boils down to this: # Alternative 1 @overload def attrib(x: Optional[T]) -> T: ... # Alternative 2 @overload def attrib(x: None = None) -> Any: ... This code typechecks under the current system because alternative 1 completely shadows alternative 2. It fails to typecheck under the new system for the exact same reason. If we swap the two alternatives, it fails under the current system because 'Any' is not a proper subtype of 'T'. It *is*, however, regular subtype of 'T' -- hence this change. [0]: python/typing#253 (comment) [1]: python/typeshed#2138
1 parent 8f8d136 commit 74371df

File tree

3 files changed

+29
-15
lines changed

3 files changed

+29
-15
lines changed

mypy/checker.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3557,7 +3557,7 @@ def is_unsafe_overlapping_signatures(signature: Type, other: Type) -> bool:
35573557
# Special case: all args are subtypes, and returns are subtypes
35583558
if (all(is_proper_subtype(s, o)
35593559
for (s, o) in zip(signature.arg_types, other.arg_types)) and
3560-
is_proper_subtype(signature.ret_type, other.ret_type)):
3560+
is_subtype(signature.ret_type, other.ret_type)):
35613561
return False
35623562
return not is_more_precise_signature(signature, other)
35633563
return True

test-data/unit/check-overloading.test

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1490,3 +1490,17 @@ class Child4(ParentWithDynamicImpl):
14901490

14911491
[builtins fixtures/tuple.pyi]
14921492

1493+
[case testOverloadAnyIsConsideredValidReturnSubtype]
1494+
from typing import Any, overload, Optional
1495+
1496+
@overload
1497+
def foo(x: None) -> Any: ...
1498+
@overload
1499+
def foo(x: Optional[str]) -> str: ...
1500+
def foo(x): pass
1501+
1502+
@overload
1503+
def bar(x: None) -> object: ... # E: Overloaded function signatures 1 and 2 overlap with incompatible return types
1504+
@overload
1505+
def bar(x: Optional[str]) -> str: ...
1506+
def bar(x): pass

test-data/unit/lib-stub/attr.pyi

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,20 @@ _ConverterType = Callable[[Any], _T]
88
_FilterType = Callable[[Any, Any], bool]
99
_ValidatorArgType = Union[_ValidatorType[_T], Sequence[_ValidatorType[_T]]]
1010

11+
# This form catches explicit None or no default but with no other arguments returns Any.
12+
@overload
13+
def attrib(default: None = ...,
14+
validator: None = ...,
15+
repr: bool = ...,
16+
cmp: bool = ...,
17+
hash: Optional[bool] = ...,
18+
init: bool = ...,
19+
convert: None = ...,
20+
metadata: Optional[Mapping[Any, Any]] = ...,
21+
type: None = ...,
22+
converter: None = ...,
23+
factory: None = ...,
24+
) -> Any: ...
1125
# This form catches an explicit None or no default and infers the type from the other arguments.
1226
@overload
1327
def attrib(default: None = ...,
@@ -36,20 +50,6 @@ def attrib(default: _T,
3650
converter: Optional[_ConverterType[_T]] = ...,
3751
factory: Optional[Callable[[], _T]] = ...,
3852
) -> _T: ...
39-
# This form catches explicit None or no default but with no other arguments returns Any.
40-
@overload
41-
def attrib(default: None = ...,
42-
validator: None = ...,
43-
repr: bool = ...,
44-
cmp: bool = ...,
45-
hash: Optional[bool] = ...,
46-
init: bool = ...,
47-
convert: None = ...,
48-
metadata: Optional[Mapping[Any, Any]] = ...,
49-
type: None = ...,
50-
converter: None = ...,
51-
factory: None = ...,
52-
) -> Any: ...
5353
# This form covers type=non-Type: e.g. forward references (str), Any
5454
@overload
5555
def attrib(default: Optional[_T] = ...,

0 commit comments

Comments
 (0)
0