Omit constraints referencing `dict` if a conflicting `TypedDict` constraint exists
Fixes #19201.
When inferring callable constraints, excludes constraints with dict target when other constraints have TypedDict type. The right way to do this would be to allow backtracking in meet and join (so that dict can be promoted to TypedDict if possible), but that seems to be way more difficult and needs significant refactoring first.
This should be safe as we never rely solely on solutions, such inference is followed by actually checking all arguments, so any incompatibilities will still be reported.
Diff from mypy_primer, showing the effect of this PR on open source code:
antidote (https://github.com/Finistere/antidote): 1.50x slower (62.0s -> 93.0s in single noisy sample)
materialize (https://github.com/MaterializeInc/materialize): 1.10x slower (130.4s -> 143.2s in single noisy sample)
core (https://github.com/home-assistant/core)
+ homeassistant/core.py:2342: error: Unused "type: ignore" comment [unused-ignore]
+ homeassistant/scripts/benchmark/__init__.py:144: error: Unused "type: ignore" comment [unused-ignore]
+ homeassistant/scripts/benchmark/__init__.py:144: error: Argument 2 to "async_fire" of "EventBus" has incompatible type "dict[str, object]"; expected "EventStateChangedData | None" [arg-type]
+ homeassistant/scripts/benchmark/__init__.py:144: note: Error code "arg-type" not covered by "type: ignore" comment
+ homeassistant/scripts/benchmark/__init__.py:177: error: Unused "type: ignore" comment [unused-ignore]
+ homeassistant/scripts/benchmark/__init__.py:177: error: Argument 2 to "async_fire" of "EventBus" has incompatible type "dict[str, object]"; expected "EventStateChangedData | None" [arg-type]
+ homeassistant/scripts/benchmark/__init__.py:177: note: Error code "arg-type" not covered by "type: ignore" comment
+ homeassistant/scripts/benchmark/__init__.py:215: error: Unused "type: ignore" comment [unused-ignore]
+ homeassistant/scripts/benchmark/__init__.py:215: error: Argument 2 to "async_fire" of "EventBus" has incompatible type "dict[str, object]"; expected "EventStateChangedData | None" [arg-type]
+ homeassistant/scripts/benchmark/__init__.py:215: note: Error code "arg-type" not covered by "type: ignore" comment
hydra-zen (https://github.com/mit-ll-responsible-ai/hydra-zen): 1.13x slower (77.5s -> 87.6s in single noisy sample)
discord.py (https://github.com/Rapptz/discord.py): 1.18x slower (268.5s -> 315.7s in single noisy sample)
Diff from mypy_primer, showing the effect of this PR on open source code:
core (https://github.com/home-assistant/core)
+ homeassistant/core.py:2342: error: Unused "type: ignore" comment [unused-ignore]
+ homeassistant/scripts/benchmark/__init__.py:144: error: Unused "type: ignore" comment [unused-ignore]
+ homeassistant/scripts/benchmark/__init__.py:144: error: Argument 2 to "async_fire" of "EventBus" has incompatible type "dict[str, object]"; expected "EventStateChangedData | None" [arg-type]
+ homeassistant/scripts/benchmark/__init__.py:144: note: Error code "arg-type" not covered by "type: ignore" comment
+ homeassistant/scripts/benchmark/__init__.py:177: error: Unused "type: ignore" comment [unused-ignore]
+ homeassistant/scripts/benchmark/__init__.py:177: error: Argument 2 to "async_fire" of "EventBus" has incompatible type "dict[str, object]"; expected "EventStateChangedData | None" [arg-type]
+ homeassistant/scripts/benchmark/__init__.py:177: note: Error code "arg-type" not covered by "type: ignore" comment
+ homeassistant/scripts/benchmark/__init__.py:215: error: Unused "type: ignore" comment [unused-ignore]
+ homeassistant/scripts/benchmark/__init__.py:215: error: Argument 2 to "async_fire" of "EventBus" has incompatible type "dict[str, object]"; expected "EventStateChangedData | None" [arg-type]
+ homeassistant/scripts/benchmark/__init__.py:215: note: Error code "arg-type" not covered by "type: ignore" comment
The right way to do this would be to allow backtracking in meet and join (so that
dictcan be promoted toTypedDictif possible),
Do you think there's any neat way to handle prioritization in such a general way that it solves this too? (i.e. marking constraints as higher quality than others)
I did some basic version of that in https://github.com/python/mypy/pull/18958 and I would be happy if there's a method that generalizes to solve both this issue and that issue.
Hm, I don't see any straightforward solution that could set constraints priorities reliably. But your Literal suggestion sounds interesting, I'll try to expand this PR tomorrow to cover this case
OK, Literal constraints are significantly different, nothing prevents their precise inference - we already keep track of last_known_value. That should be handled in join.py instead and does not belong to this PR. That would be something like
diff --git a/mypy/join.py b/mypy/join.py
index a012a633d..099df0268 100644
--- a/mypy/join.py
+++ b/mypy/join.py
@@ -625,6 +625,8 @@ class TypeJoinVisitor(TypeVisitor[ProperType]):
if self.s.fallback.type.is_enum and t.fallback.type.is_enum:
return mypy.typeops.make_simplified_union([self.s, t])
return join_types(self.s.fallback, t.fallback)
+ elif isinstance(self.s, Instance) and self.s.last_known_value == t:
+ return t
else:
return join_types(self.s, t.fallback)
This triggers another suggestion: should we record similar last_known_value for dict literals, at least simple ones? That isn't trivial either, but should be doable.
This triggers another suggestion: should we record similar last_known_value for dict literals, at least simple ones?
Well, that would be pretty nice! I assume that would solve this PR too, as well as some commonly requested stuff?