You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
If we make this change, we should probably also update similar functions such as min() and sorted(), which have similar issues. But, I know @srittau disagrees with me 🙂
Related: the mypy issue I opened here, following the conversation in #6342.
Ah, I missed the discussion in the original PR which covers this in detail.
If we'd support this in type checkers, this could result in false negatives if user code tries to call __lt__ or __gt__:
deff(x: SupportsDunderGt, other: Any) ->bool:
returnx.__gt__(other) # Doesn't do the right thing if __gt__ is missingf(Lt(5), Lt(6))
The main issue I have is that this would add "ad-hoc" complexity at the type system level, which seems unnecessary in this case. We could easily support this use case in typeshed once, and this would work as expected with every type checker, and without false negatives as far as I can see. If we expect type checkers to support some sort of __lt__/__gt__ equivalence, the implementation is likely to be more difficult to maintain, and it needs to be duplicated for every type checker, including current ones (at least mypy) and any possible future ones. And if some type checker doesn't implement the special case, this will result in false positives for the users of the type checker. It's also possible that different type checkers would implement the rules differently, since this type checking behavior isn't explicitly defined in any PEP, including PEP 544.
Also, I don't think that semantics of PyObject_RichCompareBool are relevant here (this was mentioned in #6342). The protocol class only says that __lt__ or __gt__ is present, and the protocol doesn't talk anything about what is done with __lt__ or __gt__ by the callee. Another function with a signature similar to max() might require __gt__ to be present, and wouldn't use PyObject_RichCompareBool or the equivalent at all.
For these reasons I'd prefer this to be handled in typeshed.
We need to reopen this, unfortunately. (EDIT: We don't, see below.) There was a logical error in my PR #6583, in that the following two types are not equivalent:
The former is incorrect, and is what is currently in the stub in e.g. overloads 2, 4 & 6 of max. The latter is what should be used in these situations.
I think we need another alias in _typeshed — what should it be called? CallableReturningComparable? RichComparisonCallable?
Example
In _typeshed we could have
fromtypingimportCallable, TypeVar, Union# SupportsDunderLT & SupportsDunderGT still defined as they are currently_T=TypeVar("_T")
CallableReturningComparable=Union[Callable[[_T], SupportsDunderLT], Callable[[_T], SupportsDunderGT]]
Have you seen issues caused by the new type? I believe that the union return type is fine, since the return type can vary covariantly (Callable[..., X] is valid if Callable[..., X | Y] is expected).
Activity
JukkaL commentedon Dec 13, 2021
Having
__ge__or__le__is not sufficient, however.AlexWaygood commentedon Dec 13, 2021
In #6342, I argued that the signature of
maxshould be the following due to the way comparison operators are desugared by the interpreter :If we make this change, we should probably also update similar functions such as
min()andsorted(), which have similar issues. But, I know @srittau disagrees with me 🙂Related: the mypy issue I opened here, following the conversation in #6342.
JukkaL commentedon Dec 13, 2021
Ah, I missed the discussion in the original PR which covers this in detail.
If we'd support this in type checkers, this could result in false negatives if user code tries to call
__lt__or__gt__:The main issue I have is that this would add "ad-hoc" complexity at the type system level, which seems unnecessary in this case. We could easily support this use case in typeshed once, and this would work as expected with every type checker, and without false negatives as far as I can see. If we expect type checkers to support some sort of
__lt__/__gt__equivalence, the implementation is likely to be more difficult to maintain, and it needs to be duplicated for every type checker, including current ones (at least mypy) and any possible future ones. And if some type checker doesn't implement the special case, this will result in false positives for the users of the type checker. It's also possible that different type checkers would implement the rules differently, since this type checking behavior isn't explicitly defined in any PEP, including PEP 544.Also, I don't think that semantics of
PyObject_RichCompareBoolare relevant here (this was mentioned in #6342). The protocol class only says that__lt__or__gt__is present, and the protocol doesn't talk anything about what is done with__lt__or__gt__by the callee. Another function with a signature similar tomax()might require__gt__to be present, and wouldn't usePyObject_RichCompareBoolor the equivalent at all.For these reasons I'd prefer this to be handled in typeshed.
srittau commentedon Dec 13, 2021
Would a custom "RichComparison" alias make sense here?
JukkaL commentedon Dec 13, 2021
If we'd use the definitions from #6575 (comment) above, I think this would make sense.
Maybe something like this?
AlexWaygood commentedon Dec 13, 2021
I'd be very interested in submitting a PR along these lines, if we have consensus?
JukkaL commentedon Dec 14, 2021
@AlexWaygood I think that we have enough thumbs ups to move forward.
Add `SupportsRichComparison` type to `_typeshed` (#6583)
AlexWaygood commentedon Dec 17, 2021
We need to reopen this, unfortunately. (EDIT: We don't, see below.) There was a logical error in my PR #6583, in that the following two types are not equivalent:is not the same as
The former is incorrect, and is what is currently in the stub in e.g. overloads 2, 4 & 6 of
max. The latter is what should be used in these situations.I think we need another alias in
_typeshed— what should it be called?CallableReturningComparable?RichComparisonCallable?Example
In
_typeshedwe could haveAnd then
maxcould be rewritten as:AlexWaygood commentedon Dec 17, 2021
JukkaL commentedon Dec 17, 2021
Have you seen issues caused by the new type? I believe that the union return type is fine, since the return type can vary covariantly (
Callable[..., X]is valid ifCallable[..., X | Y]is expected).AlexWaygood commentedon Dec 17, 2021
@JukkaL you're right, please ignore me — it seems to work fine.
Kache commentedon Oct 13, 2023
Is the following expected? Or is there an issue with either typeshed or the type checker?
https://mypy-play.net/?mypy=master&python=3.11&gist=2dcc0cc29ac7d0ee49e87b14a327b301
AlexWaygood commentedon Oct 13, 2023
@Kache please open a new issue rather than commenting on one that's been closed for two years :)
Kache commentedon Oct 13, 2023
Sorry, wasn't sure if it was an actual issue worth posting about.