-
-
Notifications
You must be signed in to change notification settings - Fork 2.9k
Special Method Overload Resolution #6710
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
I think the root cause here is actually due to the So, when the subclass doesn't use the same signature (doesn't respect Liskov), you get inconsistencies like this. Unfortunately, I'm not sure if there's a good workaround for this: the signature for Probably there's a better way of handling this, but it's somewhat unclear to me what that way would be. For example, adjusting mypy to do this fallback behavior only when |
That's what I thought initially too, that the type ignore was causing something funny to happen with overload resolution where it would inherit a partial type signature from the base class. However, I did some testing and I think the example below makes it more clear that it's using the right operand's from typing import overload
class Expr: ...
class Foo:
@overload # type: ignore
def __eq__(self, other: 'Foo') -> 'Expr': ...
@overload
def __eq__(self, other: str) -> 'Expr': ...
class Bar:
def __eq__(self, other: object) -> int: ...
foo: Foo
bar: Bar
reveal_type(foo == bar) # Revealed type is 'builtins.int' Seems to me that these comparisons try overloads from the left and then try overloads from the right, rather than checking for the existence of the special method on the left and only looking at the right if the matching left signature returns NotImplemented. |
Now, I do understand there's a real problem here -- mypy and typeshed currently aren't built to support overloading I don't see an easy way out -- in particular because in general |
The reason it's doing this is because mypy is assuming that the left operand overrode |
I agree that this will not be an easy ticket to solve, and I expect it will take some time to figure out how to address this with a reasonable upgrade path. That said, I think it's important for a type checker to value correctness over brevity, and that's especially true in this case since it's not a self-contained false positive that can be hidden away by a Now before we go further, I want to make sure I have my understanding correct:
Is that a reasonable summary? |
For what it's worth, pyre and pyright both emit an error: pyre:
pyright:
|
I am with @Michael0x2a here. After an ignored error, we try to avoid further false negatives on the best effort basis (unlike subsequent false positives, these should be always avoided if the original error is ignored). I am therefore proposing to close this issue. |
I understand the current state of things (type ignore causing assumptions to be broken), and I can understand decreasing the issue priority since it's likely to take a bit of reworking to fix. However, I don't think this should be closed, since it's a real bug with a reliable repro and no solution. Maybe stick a help-wanted label on it, to indicate you guys arent actively tackling it, but it's still an unsolved issue? I can try to poke around. Don't want to be one of those people whole files issues but doesnt contribute PRs. The mypy codebase is a lot to take in though, so don't hold your breath. |
I have no opinion on whether this issue should be open or closed, but I do want to clarify that I don't think this is a bug: mypy detected the root error and your code samples silenced it. And as Ivan said, mypy operates on a best-effort principle once an error is silenced and doesn't guarantee it'll identify subsequent "downstream" errors. In that regard, everything is behaving as expected. Rather, I'd characterize this as in part a usability issue and in part a feature request:
Actually, I suppose there is one thing here I'd count as a bug: mypy doesn't enforce that operator methods correctly use NotImplemented. If mypy expects operator methods to follow Python's standard data model, I think it also makes sense to try and actually enforce this. |
Right, I think that's sort of the crux of the short-term issue: mypy always implies Longer-term, I agree this is pretty nontrivial feature request to broaden the special methods so they support a wider range of use cases. The docs say
so |
or a mock-up repro if the source is private. We would appreciate
if you try to simplify your case to a minimal repro.
Since
Foo.__eq__
exists, it will be called for all equality comparisons at runtime, and it will not fall back tofloat.__eq__
unless it returnsNotImplemented
. Since none of the overload variants do that, it should never happen and this comparison should be a type error.My current workaround is to include a
Any
overload that returnsNoReturn
, but that's not ideal since it defers the error at the call site to a "if I get lucky it will hit an error downstream" situation. Some of that could be addressed with more preciseNoReturn
semantics, but as it stands right now there are still some discussions/issues surrounding that, and it also feels clunky to have to explicitly include an overload for type errors.In any case, I think special method overload resolution needs some fine tuning, and it should not mix-and-match the left/right operand's methods unless an overload variant is allowed to return
type(NotImplemented)
(which, btw, needs some type tightening in typeshed, but I'll file that over there).0.710+dev.bb7dbd5afd84656a62311e5f69a1cef6d06466bc
yes
The text was updated successfully, but these errors were encountered: