Uh oh!
There was an error while loading. Please reload this page.
Fix Type Comparison Edge Cases - #20277
Conversation
This comment has been minimized.
This comment has been minimized.
George-Ogden
commented
Nov 20, 2025
I don't quite understand what I should do with |
A5rocks
left a comment
There was a problem hiding this comment.
I'm not familiar with this part of mypy (this is the first time I saw TypeRange!), so my comments may be wrong. Initial pass through though.
I'm not sure about the bigger picture here, it feels somewhat hacky to rely on type(x) == Y logic to handle type(x) == type(y). I don't have any better ideas though.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
This comment has been minimized.
This comment has been minimized.
Uh oh!
There was an error while loading. Please reload this page.
tyralla
commented
Nov 21, 2025
Thanks for the PR! Also, just a short remark/question without any deep knowledge of the matter: If I understand #20275 correctly, calling |
George-Ogden
commented
Nov 21, 2025
@tyralla: Let me know if this makes it clear or if any of that doesn't make sense. |
Huh, then couldn't we get rid of any special casing for looking for specifically I imagine it's a bit strange (e.g. (I guess the flaw is actually that we need to know the relevant name to know what to narrow. Maybe that's why we don't do that) |
tyralla
commented
Nov 22, 2025
I never really thought about type narrowing with If I look at this example: classX: x=1classY: y=1classZ(X, Y): ...
z=Z()
x: X=zy: Y=zifxisy:
x.y+y.x# error: "X" has no attribute "y"# error: "Y" has no attribute "xI ask myself if Mypy should not handle it like: ifisinstance(x, type(y)) andisinstance(y, type(x)):
x.y+y.xWhich could eventually easily be implemented with the help of |
George-Ogden
commented
Nov 22, 2025
I had a look at this and I've modified the implementation to allow this kind of checking. It just needs a bit of cleaning up. |
George-Ogden
commented
Nov 23, 2025
This cannot be directly handled here because classX: x=1classY: y=1classZ(X, Y): ...
z=Z()
x: X=zy: Y=ziftype(x) istype(y): # instead of x is yx.y+y.x |
|
|
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
Diff from mypy_primer, showing the effect of this PR on open source code: pandas (https://github.com/pandas-dev/pandas)
+ pandas/core/arrays/base.py:1502: error: Redundant cast to "ExtensionArray" [redundant-cast]
ibis (https://github.com/ibis-project/ibis)
- ibis/common/patterns.py:658: error: "Pattern" has no attribute "type" [attr-defined]- ibis/common/patterns.py:934: error: "Pattern" has no attribute "patterns" [attr-defined]
jax (https://github.com/google/jax)
+ jax/_src/interpreters/partial_eval.py:96: error: Unused "type: ignore" comment [unused-ignore]+ jax/_src/interpreters/partial_eval.py:99: error: No overload variant of "get" of "dict" matches argument types "int | DArray | Tracer | Var | DBIdx | InDBIdx | OutDBIdx", "int | DArray | Tracer | Var | DBIdx | InDBIdx | OutDBIdx" [call-overload]+ jax/_src/interpreters/partial_eval.py:99: note: Possible overload variants:+ jax/_src/interpreters/partial_eval.py:99: note: def get(self, Name, None = ..., /) -> DBIdx | None+ jax/_src/interpreters/partial_eval.py:99: note: def get(self, Name, DBIdx, /) -> DBIdx+ jax/_src/interpreters/partial_eval.py:99: note: def [_T] get(self, Name, _T, /) -> DBIdx | _T+ jax/_src/interpreters/batching.py:231: error: Unused "type: ignore" comment [unused-ignore]+ jax/_src/interpreters/batching.py:232: error: Unused "type: ignore" comment [unused-ignore]+ jax/_src/interpreters/batching.py:234: error: No overload variant of "get" of "dict" matches argument types "int | DArray | Tracer | Var | DBIdx | InDBIdx | OutDBIdx", "int | DArray | Tracer | Var | DBIdx | InDBIdx | OutDBIdx" [call-overload]+ jax/_src/interpreters/batching.py:234: note: Possible overload variants:+ jax/_src/interpreters/batching.py:234: note: def get(self, Name, None = ..., /) -> DBIdx | None+ jax/_src/interpreters/batching.py:234: note: def get(self, Name, DBIdx, /) -> DBIdx+ jax/_src/interpreters/batching.py:234: note: def [_T] get(self, Name, _T, /) -> DBIdx | _T+ jax/_src/interpreters/batching.py:258: error: Unused "type: ignore" comment [unused-ignore]
rotki (https://github.com/rotki/rotki)
+ rotkehlchen/history/events/structures/base.py:161: error: Unused "type: ignore" comment [unused-ignore]+ rotkehlchen/history/events/structures/base.py:162: error: Unused "type: ignore" comment [unused-ignore]+ rotkehlchen/history/events/structures/base.py:163: error: Unused "type: ignore" comment [unused-ignore]+ rotkehlchen/history/events/structures/base.py:164: error: Unused "type: ignore" comment [unused-ignore]+ rotkehlchen/history/events/structures/base.py:165: error: Unused "type: ignore" comment [unused-ignore]+ rotkehlchen/history/events/structures/base.py:166: error: Unused "type: ignore" comment [unused-ignore]+ rotkehlchen/history/events/structures/base.py:167: error: Unused "type: ignore" comment [unused-ignore]+ rotkehlchen/history/events/structures/base.py:168: error: Unused "type: ignore" comment [unused-ignore]+ rotkehlchen/history/events/structures/base.py:169: error: Unused "type: ignore" comment [unused-ignore]+ rotkehlchen/history/events/structures/base.py:170: error: Unused "type: ignore" comment [unused-ignore]+ rotkehlchen/history/events/structures/base.py:171: error: Unused "type: ignore" comment [unused-ignore]+ rotkehlchen/history/events/structures/base.py:172: error: Unused "type: ignore" comment [unused-ignore]
pip (https://github.com/pypa/pip)
+ src/pip/_internal/utils/misc.py:551: error: Redundant cast to "HiddenText" [redundant-cast] |
Test cases added from #20277 (initial PR) --------- Co-authored-by: hauntsaninja <hauntsaninja@gmail.com>
hauntsaninja
commented
Feb 2, 2026
Thanks! I think all the test cases you'd talked about are handled on master. Do let me know if there is something on master that isn't working as intended! :-) |
Fixes#20275 and #20041
This code snippet fails to type-check before this PR:
But type-checks afterwards.
This is done by finding a least type as the upper bound when the equality succeeds.