-
-
Notifications
You must be signed in to change notification settings - Fork 2.9k
Regression with covariant type through built-in function #16476
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Comments
Here's a self-contained repro that doesn't depend on typeshed's (complicated!) stubs for the builtin from typing import Any, Callable, Iterable, Protocol, TypeVar, overload
from typing_extensions import TypeAlias
T = TypeVar("T")
T_contra = TypeVar("T_contra", contravariant=True)
class SupportsDunderLT(Protocol[T_contra]):
def __lt__(self, __other: T_contra) -> bool: ...
class SupportsDunderGT(Protocol[T_contra]):
def __gt__(self, __other: T_contra) -> bool: ...
SupportsRichComparison: TypeAlias = SupportsDunderLT[Any] | SupportsDunderGT[Any]
SupportsRichComparisonT = TypeVar("SupportsRichComparisonT", bound=SupportsRichComparison)
@overload
def sorted(iterable: Iterable[SupportsRichComparisonT], *, key: None = None) -> list[SupportsRichComparisonT]: ...
@overload
def sorted(iterable: Iterable[T], *, key: Callable[[T], SupportsRichComparison]) -> list[T]: ...
def sorted(iterable: Iterable[object], *, key: Callable[[Any], Any] | None = None) -> list[Any]:
return list(iterable)
def foo(a: list[int] | list[str]) -> list[int] | list[str]:
return sorted(a) Mypy 1.6: passes. Mypy 1.7:
The following snippet, however, is simplified even further, and seems to be improved on mypy 1.7: from typing import Any, Iterable, Protocol, TypeVar
from typing_extensions import TypeAlias
T = TypeVar("T")
T_contra = TypeVar("T_contra", contravariant=True)
class SupportsDunderLT(Protocol[T_contra]):
def __lt__(self, __other: T_contra) -> bool: ...
class SupportsDunderGT(Protocol[T_contra]):
def __gt__(self, __other: T_contra) -> bool: ...
SupportsRichComparison: TypeAlias = SupportsDunderLT[Any] | SupportsDunderGT[Any]
SupportsRichComparisonT = TypeVar("SupportsRichComparisonT", bound=SupportsRichComparison)
def sorted(iterable: Iterable[SupportsRichComparisonT]) -> list[SupportsRichComparisonT]:
return list(iterable)
def foo(a: list[int] | list[str]) -> list[int] | list[str]:
return sorted(a) Mypy 1.6:
Mypy 1.7:
|
The change in behaviour bisects to 5f6961b (cc. @ilevkivskyi) |
OK, this one is non-trivial. Mypy has this thing called overload union math that infers good types when an overload is called on union type(s). But it is always used as a fallback, i.e. it is not used if a single overload matched. Now mypy is smart enough to infer a value for type variable that will make first overload match the whole union. So there are two options:
I am not sure what to do here. Any opinions? cc @JukkaL |
Random thought: What about always using union math if the type context has a union type? This would be a smaller change than always using union math. |
Bug Report
Regression from 1.6.1 to 1.7: when passing a simple covariant type through a builtin (like sorted) the return type becomes a wider generic based type.
To Reproduce
Expected Behavior
running mypy on the simple code above produces no error in 1.6.1.
Actual Behavior
Your Environment
Regression from 1.6.1 to 1.7.0. Seen on multiple python versions (3.10, 3.11)
The text was updated successfully, but these errors were encountered: