From ba9380d01dd79bb55dd42b50f8bdd855ce355a2f Mon Sep 17 00:00:00 2001 From: Vikash Kumar Date: Sun, 1 Feb 2026 13:21:32 +0530 Subject: [PATCH 1/3] Fix: ensure unpacked **kwargs have string-compatible keys (#20706) --- mypy/checkexpr.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/mypy/checkexpr.py b/mypy/checkexpr.py index 8288b676b52e..8329760f6dc3 100644 --- a/mypy/checkexpr.py +++ b/mypy/checkexpr.py @@ -5432,6 +5432,13 @@ def visit_dict_expr(self, e: DictExpr) -> Type: expected_types: list[Type] = [] for key, value in e.items: if key is None: + # This is a **expr unpacking. Check that the expression has string keys. + value_type = get_proper_type(self.accept(value)) + if not self.is_valid_keyword_var_arg(value_type): + is_mapping = is_subtype( + value_type, self.chk.named_type("_typeshed.SupportsKeysAndGetItem") + ) + self.msg.invalid_keyword_var_arg(value_type, is_mapping, value) args.append(value) expected_types.append( self.chk.named_generic_type("_typeshed.SupportsKeysAndGetItem", [kt, vt]) From 12db980393ad1d5d625e65c8d2186613dd5da3b2 Mon Sep 17 00:00:00 2001 From: Vikash Kumar Date: Sun, 1 Feb 2026 17:50:31 +0530 Subject: [PATCH 2/3] Refine: allow Any/Unbound types in dict unpacking to avoid false positives --- mypy/checkexpr.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/mypy/checkexpr.py b/mypy/checkexpr.py index 8329760f6dc3..0b7cf636399c 100644 --- a/mypy/checkexpr.py +++ b/mypy/checkexpr.py @@ -5434,7 +5434,10 @@ def visit_dict_expr(self, e: DictExpr) -> Type: if key is None: # This is a **expr unpacking. Check that the expression has string keys. value_type = get_proper_type(self.accept(value)) - if not self.is_valid_keyword_var_arg(value_type): + # NEW: Allow Any or Unbound types to reduce false positives in external libraries + if isinstance(value_type, (AnyType, UnboundType)): + pass + elif not self.is_valid_keyword_var_arg(value_type): is_mapping = is_subtype( value_type, self.chk.named_type("_typeshed.SupportsKeysAndGetItem") ) From 05366f5b891ab52b54d88f35b23135929941d533 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Sun, 1 Feb 2026 12:22:36 +0000 Subject: [PATCH 3/3] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- mypy/checkexpr.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mypy/checkexpr.py b/mypy/checkexpr.py index 0b7cf636399c..d8a861b8d59e 100644 --- a/mypy/checkexpr.py +++ b/mypy/checkexpr.py @@ -5434,7 +5434,7 @@ def visit_dict_expr(self, e: DictExpr) -> Type: if key is None: # This is a **expr unpacking. Check that the expression has string keys. value_type = get_proper_type(self.accept(value)) - # NEW: Allow Any or Unbound types to reduce false positives in external libraries + # NEW: Allow Any or Unbound types to reduce false positives in external libraries if isinstance(value_type, (AnyType, UnboundType)): pass elif not self.is_valid_keyword_var_arg(value_type):