mypy icon indicating copy to clipboard operation
mypy copied to clipboard

Omit constraints referencing `dict` if a conflicting `TypedDict` constraint exists

Open sterliakov opened this issue 7 months ago • 6 comments

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.

sterliakov avatar Jun 04 '25 02:06 sterliakov

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)

github-actions[bot] avatar Jun 04 '25 02:06 github-actions[bot]

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

github-actions[bot] avatar Jun 04 '25 12:06 github-actions[bot]

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),

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.

A5rocks avatar Jun 10 '25 00:06 A5rocks

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

sterliakov avatar Jun 11 '25 01:06 sterliakov

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.

sterliakov avatar Jun 12 '25 00:06 sterliakov

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?

A5rocks avatar Jun 12 '25 23:06 A5rocks