mypy
mypy copied to clipboard
false positive: "Overloaded function signatures ... overlap with incompatible return types"
Bug Report
Code of a certain form causes a false positive error:
error: Overloaded function signatures 1 and 2 overlap with incompatible return types [misc]
To Reproduce
Gist: mypy-play.net
from typing import (
Literal,
overload,
Sequence,
TypeVar,
Union,
)
T1 = TypeVar('T1')
T2 = TypeVar('T2')
@overload
def func(a1: Literal[True], a2: T1) -> Union[T1, Sequence[T2]]: ...
# error: Overloaded function signatures 1 and 2 overlap with incompatible return types [misc]
@overload
def func(a1: bool, a2: T1) -> Union[T1, Sequence[T2], T2]: ...
def func( # type: ignore[empty-body]
a1: bool, a2: T1) -> Union[T1, Sequence[T2], T2]:
...
Expected Behavior
There should be no errors.
- the second signature's arguments are clearly broader than the first's (
bool
vs.Literal[True]
) - the second signature's return type is clearly broader than the first's (
Union[...]
vs.Union[..., T2]
)
Actual Behavior
An error is issued:
error: Overloaded function signatures 1 and 2 overlap with incompatible return types [misc]
Additional notes
This example is fairly minimal:
- removing the
a2
argument from all three declarations causes the problem to disappear - removing the
Sequence[T2]
clause from the returnUnion
of all three declarations causes the problem to disappear
Your Environment
- Mypy version used: 0.910, 1.8.0
- Mypy command-line flags: (none)
- Mypy configuration options from
mypy.ini
(and other config files): (none) - Python version used: 3.8, 3.12
The following example may be related. It is something we are trying to get working with respect to pandas stubs supplied by Microsoft with VS Code. Full discussion https://github.com/microsoft/python-type-stubs/issues/87
Goal is to have two parameters i1
that is bool
and cs
that is Optional[int]
that may or may not be specified. Issue with mypy is that it is saying that None
and int
overlap. Goal of the code below is to have results corresponding to the table below:
i1 |
cs |
Return Type |
---|---|---|
Not specified | Not specified | int |
False |
Not specified | int |
False |
None |
int |
False |
An integer | str |
True |
Not specified | str |
True |
None |
str |
True |
An integer | str |
Not specified | None |
int |
Not specified | An integer | str |
@overload
def myfun(fake: str, i1: Literal[True], cs: Union[int, None] = ...) -> str:
...
@overload
def myfun(fake: str, i1: Literal[False], cs: int) -> str:
...
@overload
def myfun(fake: str, i1: Literal[False] = ..., cs: None = ...) -> int:
...
@overload
def myfun(fake: str, i1: bool = ..., cs: int = ...) -> str:
...
def myfun(fake: str, i1: bool = False, cs: Optional[int] = None) -> Union[str, int]:
print(f"i1 is {i1} cs is {cs} result is ", end="")
if i1:
if cs is not None:
return "TextFileReader"
else:
return "TextFileReader"
else:
if cs is not None:
return "TextFileReader"
else:
return -1
res1: int = myfun("meh")
res2: int = myfun("meh", i1=False)
res3: int = myfun("meh", i1=False, cs=None)
res4: str = myfun("meh", i1=False, cs=23)
res5: str = myfun("meh", i1=True)
res6: str = myfun("meh", i1=True, cs=None)
res7: str = myfun("meh", i1=True, cs=23)
res8: int = myfun("meh", cs=None)
res9: str = myfun("meh", cs=23)
mypy
version 0.910 complains as follows:
error: Overloaded function signatures 3 and 4 overlap with incompatible return types
The pyright
type checker does not have this complaint.
I think what's confusing mypy is the fact that cs
has a default argument in overloads 3 and 4. We can eliminate that requirement if i1
and cs
are keyword-only parameters by making the following change. This works fine in both mypy and pyright.
@overload
def myfun(fake: str, *, i1: Literal[True], cs: int | None = ...) -> str:
...
@overload
def myfun(fake: str, *, i1: Literal[False], cs: int) -> str:
...
@overload
def myfun(fake: str, *, i1: Literal[False] = ..., cs: None = ...) -> int:
...
@overload
def myfun(fake: str, *, i1: bool = ..., cs: int) -> str:
...
def myfun(fake: str, i1: bool = False, cs: Optional[int] = None) -> Union[str, int]:
print(f"i1 is {i1} cs is {cs} result is ", end="")
if i1:
if cs is not None:
return "TextFileReader"
else:
return "TextFileReader"
else:
if cs is not None:
return "TextFileReader"
else:
return -1
res1: int = myfun("meh")
res2: int = myfun("meh", i1=False)
res3: int = myfun("meh", i1=False, cs=None)
res4: str = myfun("meh", i1=False, cs=23)
res5: str = myfun("meh", i1=True)
res6: str = myfun("meh", i1=True, cs=None)
res7: str = myfun("meh", i1=True, cs=23)
res8: int = myfun("meh", cs=None)
res9: str = myfun("meh", cs=23)
- verified to affect mypy 1.8.0 on Python 3.12
- refreshed reproduction case to account for an unrelated issue (fixed), new warning (
empty-body
) - added mypy-play[.]net gist link to bug description
Possibly related: https://github.com/python/mypy/issues/6580
I think this is another example: