Repository navigation
Conversation
…able Fixes python#22342. When a list literal is checked against a union such as list[bool] | Hashable | MutableSequence[str], union simplification dropped MutableSequence[str] (a proper subtype of Hashable), and the list template could not match the remaining Hashable member (list is unhashable), so inference committed to list[bool] and reported the items against bool. Constraint inference now recovers options for union items lost to simplification when the template can match the dropped item but none of the items that absorbed it can. I used an AI coding assistant while working on this fix, and verified the change and the tests myself.
for more information, see https://pre-commit.ci
This comment has been minimized.
This comment has been minimized.
The recovery in _options_lost_by_simplification added a constraint even when no simplified union item produced one, which invented a T = Hashable constraint for [names] against Hashable | Sequence[Hashable] | dict[Hashable, Hashable] and turned the previous assignment error into a new list-item error (pandas/core/indexes/base.py:2143, reported by mypy_primer on python#22381). Recovery exists to offer an alternative to a (possibly wrong) commitment, not to invent constraints where the fallback already applies, so skip recovery when every simplified option is None. The python#22342 case still recovers (list[bool] yields T = bool).
|
Thanks for the primer run — it caught a real regression, and I have fixed it. The pandas case ( I have narrowed the recovery so it only runs when at least one surviving union item already produced a constraint — recovery exists to offer an alternative to a (possibly wrong) commitment, and here there was no commitment to correct, so the literal fallback handles it as before. The #22342 case is unaffected (its Added a regression test for the pandas-shaped case ( |
for more information, see https://pre-commit.ci
|
According to mypy_primer, this change doesn't affect type check results on a corpus of open source code. ✅ |
Fix false positive
list-itemfor list literal against union withHashableFixes #22342.
Pyright, pyrefly and ty accept the call; mypy reported both items against
bool.Root cause
Union simplification in
mypy/constraints.pydroppedMutableSequence[str]fromthe context union before inferring
list[T]— it is a proper subtype ofHashable(typeshed's
MutableSequencedoesn't set__hash__ = None). Butlistdoes set__hash__ = None, so thelist[T]template cannot match the remainingHashablemember, leaving
list[bool]as the only option and forcingT = bool.Subtyping is not transitive here:
MutableSequence[str] <: Hashableandlist[str] <: MutableSequence[str], butlist[str]is not a subtype ofHashable.Fix
_infer_constraintsnow keeps the unsimplified union items around, and in theSUBTYPE_OF+ union-context branch it recovers constraint options for itemsdropped by simplification — but only when the template can match the dropped item
while every item that absorbed it cannot. That is exactly the "subtyping wasn't
transitive here" case, so normal simplification behavior is unchanged (e.g.
list[int] | Sequence[int]still simplifies, andlist[bool] | Hashablestillreports
list-itemfor["a"]).How this differs from #22366
#22366 bundled this same constraint-inference recovery together with a second,
unrelated fix (deferred validation of member-access base classes for #22337).
The mypy_primer regressions on that PR — new
valid-typeerrors inoperatorand
scrapyaroundself._charm_cls/dict_class/spider_classused as baseclasses — all came from the member-base change, not from the constraints change.
This PR contains only the #22342 fix, with no semanal/checker changes, so those
regressions do not apply. The constraint-recovery design follows the analysis
posted by hamarshehmhmd in the issue (who tested the same approach on master).
Tests
New regression test
testListLiteralContextWithUnhashableListAndHashableSequencein
test-data/unit/check-inference-context.testwith a new fixturetest-data/unit/fixtures/list_hashable.pyi(builtins stub withlist.__hash__as
ClassVar[None], since the default fixtures don't modellist.__hash__).It failed on unmodified code with the reported
list-itemerrors and passeswith the fix. It also covers
f([True])(passes) andf([1.5]), which nowerrors as
arg-typeagainst the whole union with the__hash__conflict noteinstead of
list-itemvsbool.Test runs (all with the repo venv):
pytest mypy/test/testcheck.py mypy/test/testtypes.py mypy/test/testsolve.py mypy/test/testinfer.py: 8486 passed, 29 skipped, 7 xfailedg(["a"])withlist[bool] | Hashablestill errors withlist-itemas beforemypy/constraints.py: no new errors (same pre-existingoutput as clean master, modulo line shifts); black clean
Note: I used an AI coding assistant while working on this fix, and verified
the change and the tests myself.