Skip to content

Commit d10abad

Browse files
committed
Translate dict(x=y, ...) to {'x': y, ...} internally
Closes #239.
1 parent b0f6171 commit d10abad

File tree

5 files changed

+60
-3
lines changed

5 files changed

+60
-3
lines changed

mypy/nodes.py

+3-3
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,7 @@ def __str__(self) -> str:
8888
return repr(self)
8989
return ans
9090

91-
def set_line(self, target: Union[Token, int]) -> 'Node':
91+
def set_line(self, target: Union[Token, 'Node', int]) -> 'Node':
9292
if isinstance(target, int):
9393
self.line = target
9494
else:
@@ -282,7 +282,7 @@ def _initialization_statement(self) -> Optional['AssignmentStmt']:
282282
assign = AssignmentStmt([lvalue], rvalue)
283283
return assign
284284

285-
def set_line(self, target: Union[Token, int]) -> Node:
285+
def set_line(self, target: Union[Token, Node, int]) -> Node:
286286
super().set_line(target)
287287

288288
if self.initializer:
@@ -329,7 +329,7 @@ def __init__(self, arguments: List[Argument], body: 'Block',
329329
def max_fixed_argc(self) -> int:
330330
return self.max_pos
331331

332-
def set_line(self, target: Union[Token, int]) -> Node:
332+
def set_line(self, target: Union[Token, Node, int]) -> Node:
333333
super().set_line(target)
334334
for arg in self.arguments:
335335
arg.set_line(self.line)

mypy/semanal.py

+17
Original file line numberDiff line numberDiff line change
@@ -1647,11 +1647,28 @@ def visit_call_expr(self, expr: CallExpr) -> None:
16471647
expr.analyzed = PromoteExpr(target)
16481648
expr.analyzed.line = expr.line
16491649
expr.analyzed.accept(self)
1650+
elif refers_to_fullname(expr.callee, 'builtins.dict'):
1651+
expr.analyzed = self.translate_dict_call(expr)
16501652
else:
16511653
# Normal call expression.
16521654
for a in expr.args:
16531655
a.accept(self)
16541656

1657+
def translate_dict_call(self, call: CallExpr) -> Optional[DictExpr]:
1658+
"""Translate 'dict(x=y, ...)' to {'x': y, ...}.
1659+
1660+
For other variants of dict(...), return None.
1661+
"""
1662+
if not call.args:
1663+
return None
1664+
if not all(kind == ARG_NAMED for kind in call.arg_kinds):
1665+
return None
1666+
expr = DictExpr([(StrExpr(key), value)
1667+
for key, value in zip(call.arg_names, call.args)])
1668+
expr.set_line(call)
1669+
expr.accept(self)
1670+
return expr
1671+
16551672
def check_fixed_args(self, expr: CallExpr, numargs: int,
16561673
name: str) -> bool:
16571674
"""Verify that expr has specified number of positional args.

mypy/test/data/check-expressions.test

+15
Original file line numberDiff line numberDiff line change
@@ -1318,3 +1318,18 @@ def f(x: int) -> Iterator[None]:
13181318
(yield)
13191319
[builtins fixtures/for.py]
13201320
[out]
1321+
1322+
1323+
-- dict(x=y, ...)
1324+
-- --------------
1325+
1326+
1327+
[case testDictWithKeywordArgs]
1328+
from typing import Dict, Any
1329+
d1 = dict(a=1, b=2) # type: Dict[str, int]
1330+
d2 = dict(a=1, b='') # type: Dict[str, int] # E: Incompatible types in assignment (expression has type Dict[str, object], variable has type Dict[str, int])
1331+
d3 = dict(a=1) # type: Dict[int, int] # E: Incompatible types in assignment (expression has type Dict[str, int], variable has type Dict[int, int])
1332+
d4 = dict(a=1, b=1)
1333+
d4.xyz # E: Dict[str, int] has no attribute "xyz"
1334+
d5 = dict(a=1, b='') # type: Dict[str, Any]
1335+
[builtins fixtures/dict.py]

mypy/test/data/pythoneval.test

+11
Original file line numberDiff line numberDiff line change
@@ -1063,3 +1063,14 @@ t = n * ('',)
10631063
t + 1
10641064
[out]
10651065
_program.py:3: error: Unsupported operand types for + (Tuple[str, ...] and "int")
1066+
1067+
[case testDictWithKeywordArgs]
1068+
from typing import Dict, Any
1069+
d1 = dict(a=1, b=2) # type: Dict[str, int]
1070+
d2 = dict(a=1, b='') # type: Dict[str, int] # E
1071+
d3 = dict(a=1, b=1)
1072+
d3.xyz # E
1073+
d4 = dict(a=1, b='') # type: Dict[str, Any]
1074+
[out]
1075+
_program.py:3: error: Incompatible types in assignment (expression has type Dict[str, object], variable has type Dict[str, int])
1076+
_program.py:5: error: Dict[str, int] has no attribute "xyz"

mypy/test/data/semanal-expressions.test

+14
Original file line numberDiff line numberDiff line change
@@ -379,3 +379,17 @@ MypyFile:1(
379379
NameExpr(None [builtins.None]))
380380
NameExpr(int [builtins.int])
381381
NameExpr(str [builtins.str]))))
382+
383+
[case testDictWithKeywordArgs]
384+
dict(a=1, b=str())
385+
[builtins fixtures/dict.py]
386+
[out]
387+
MypyFile:1(
388+
ExpressionStmt:1(
389+
DictExpr:1(
390+
StrExpr(a)
391+
IntExpr(1)
392+
StrExpr(b)
393+
CallExpr:1(
394+
NameExpr(str [builtins.str])
395+
Args()))))

0 commit comments

Comments
 (0)