Skip to content

Commit 6e9f1e4

Browse files
Refactor: utilize standard CallExpr validation by aborting translation in semanal.py
1 parent 718d32c commit 6e9f1e4

3 files changed

Lines changed: 15 additions & 22 deletions

File tree

mypy/checkexpr.py

Lines changed: 0 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -5432,17 +5432,6 @@ def visit_dict_expr(self, e: DictExpr) -> Type:
54325432
expected_types: list[Type] = []
54335433
for key, value in e.items:
54345434
if key is None:
5435-
# This is a **expr unpacking.
5436-
# If this DictExpr came from a dict() call, we need to check that
5437-
# the expression has string keys (since dict() uses keyword args).
5438-
# For plain dict literals like {**mapping}, non-string keys are valid.
5439-
if e.from_dict_call:
5440-
value_type = get_proper_type(self.accept(value))
5441-
if not self.is_valid_keyword_var_arg(value_type):
5442-
is_mapping = is_subtype(
5443-
value_type, self.chk.named_type("_typeshed.SupportsKeysAndGetItem")
5444-
)
5445-
self.msg.invalid_keyword_var_arg(value_type, is_mapping, value)
54465435
args.append(value)
54475436
expected_types.append(
54485437
self.chk.named_generic_type("_typeshed.SupportsKeysAndGetItem", [kt, vt])

mypy/nodes.py

Lines changed: 2 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2672,22 +2672,15 @@ def accept(self, visitor: ExpressionVisitor[T]) -> T:
26722672
class DictExpr(Expression):
26732673
"""Dictionary literal expression {key: value, ...}."""
26742674

2675-
__slots__ = ("items", "from_dict_call")
2675+
__slots__ = ("items",)
26762676

26772677
__match_args__ = ("items",)
26782678

26792679
items: list[tuple[Expression | None, Expression]]
2680-
# True if this DictExpr was created from a dict() call (e.g., dict(a=1, **x))
2681-
from_dict_call: bool
26822680

2683-
def __init__(
2684-
self,
2685-
items: list[tuple[Expression | None, Expression]],
2686-
from_dict_call: bool = False,
2687-
) -> None:
2681+
def __init__(self, items: list[tuple[Expression | None, Expression]]) -> None:
26882682
super().__init__()
26892683
self.items = items
2690-
self.from_dict_call = from_dict_call
26912684

26922685
def accept(self, visitor: ExpressionVisitor[T]) -> T:
26932686
return visitor.visit_dict_expr(self)

mypy/semanal.py

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6051,12 +6051,23 @@ def translate_dict_call(self, call: CallExpr) -> DictExpr | None:
60516051
for a in call.args:
60526052
a.accept(self)
60536053
return None
6054+
# Check if any **kwargs argument is a dict literal with non-string keys.
6055+
# In that case, don't translate so that normal function call type checking
6056+
# will catch the "keywords must be strings" error.
6057+
for kind, arg in zip(call.arg_kinds, call.args):
6058+
if kind == ARG_STAR2 and isinstance(arg, DictExpr):
6059+
# Check if all keys in the dict literal are strings
6060+
for key, _ in arg.items:
6061+
if key is not None and not isinstance(key, (StrExpr, BytesExpr)):
6062+
# Non-string key found, don't translate
6063+
for a in call.args:
6064+
a.accept(self)
6065+
return None
60546066
expr = DictExpr(
60556067
[
60566068
(StrExpr(key) if key is not None else None, value)
60576069
for key, value in zip(call.arg_names, call.args)
6058-
],
6059-
from_dict_call=True,
6070+
]
60606071
)
60616072
expr.set_line(call)
60626073
expr.accept(self)

0 commit comments

Comments
 (0)