Skip to content

Commit b6c2b95

Browse files
authored
Add rule for unnecessary dict() calls (#500)
1 parent c7e7ffb commit b6c2b95

File tree

4 files changed

+78
-1
lines changed

4 files changed

+78
-1
lines changed

CHANGELOG.rst

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22
Changelog
33
=========
44

5+
* Add rule C418 to check for calls passing a dict literal or dict comprehension to ``dict()``.
6+
57
3.11.1 (2023-03-21)
68
-------------------
79

README.rst

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -113,7 +113,7 @@ C409-410: Unnecessary ``<list/tuple>`` passed to ``<list/tuple>``\() - ``<advice
113113
Rules:
114114

115115
* C409 Unnecessary ``<list/tuple>`` passed to tuple() - ``<advice>``.
116-
* C410 Unnecessary ``list passed to list() - ``<advice>``.
116+
* C410 Unnecessary list passed to list() - ``<advice>``.
117117

118118
Where ``<advice>`` is either:
119119

@@ -207,3 +207,12 @@ For example:
207207
* Rewrite ``list(map(lambda num: num * 2, nums))`` to ``[num * 2 for num in nums]``
208208
* Rewrite ``set(map(lambda num: num % 2 == 0, nums))`` to ``{num % 2 == 0 for num in nums}``
209209
* Rewrite ``dict(map(lambda v: (v, v ** 2), values))`` to ``{v : v ** 2 for v in values}``
210+
211+
C418: Unnecessary ``<dict/dict comprehension>`` passed to dict() - remove the outer call to dict()
212+
--------------------------------------------------------------------------------------------------
213+
214+
It's unnecessary to use a ``dict`` around a dict literal or dict comprehension, since either syntax already constructs a dict.
215+
For example:
216+
217+
* Rewrite ``dict({})`` as ``{}``
218+
* Rewrite ``dict({"a": 1})`` as ``{"a": 1}``

src/flake8_comprehensions/__init__.py

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,10 @@ def __init__(self, tree: ast.AST) -> None:
4343
"C415": "C415 Unnecessary subscript reversal of iterable within {func}().",
4444
"C416": "C416 Unnecessary {type} comprehension - rewrite using {type}().",
4545
"C417": "C417 Unnecessary use of map - use a {comp} instead.",
46+
"C418": (
47+
"C418 Unnecessary {type} passed to dict() - "
48+
+ "remove the outer call to dict()."
49+
),
4650
}
4751

4852
def run(self) -> Generator[tuple[int, int, str, type[Any]], None, None]:
@@ -117,6 +121,23 @@ def run(self) -> Generator[tuple[int, int, str, type[Any]], None, None]:
117121
type(self),
118122
)
119123

124+
elif (
125+
num_positional_args == 1
126+
and num_keyword_args == 0
127+
and isinstance(node.args[0], (ast.Dict, ast.DictComp))
128+
and node.func.id == "dict"
129+
):
130+
if isinstance(node.args[0], ast.Dict):
131+
type_ = "dict"
132+
else:
133+
type_ = "dict comprehension"
134+
yield (
135+
node.lineno,
136+
node.col_offset,
137+
self.messages["C418"].format(type=type_),
138+
type(self),
139+
)
140+
120141
elif (
121142
num_positional_args == 1
122143
and isinstance(node.args[0], (ast.Tuple, ast.List))

tests/test_flake8_comprehensions.py

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -893,3 +893,48 @@ def test_C417_fail(code, failures, flake8_path):
893893
(flake8_path / "example.py").write_text(dedent(code))
894894
result = flake8_path.run_flake8()
895895
assert result.out_lines == failures
896+
897+
898+
@pytest.mark.parametrize(
899+
"code",
900+
[
901+
"dict({}, a=1)",
902+
"dict({x: 1 for x in range(1)}, a=1)",
903+
],
904+
)
905+
def test_C418_pass(code, flake8_path):
906+
(flake8_path / "example.py").write_text(dedent(code))
907+
result = flake8_path.run_flake8()
908+
assert result.out_lines == []
909+
910+
911+
@pytest.mark.parametrize(
912+
"code,failures",
913+
[
914+
(
915+
"dict({})",
916+
[
917+
"./example.py:1:1: C418 Unnecessary dict passed to dict() - "
918+
+ "remove the outer call to dict()."
919+
],
920+
),
921+
(
922+
"dict({'a': 1})",
923+
[
924+
"./example.py:1:1: C418 Unnecessary dict passed to dict() - "
925+
+ "remove the outer call to dict()."
926+
],
927+
),
928+
(
929+
"dict({'x': 1 for x in range(10)})",
930+
[
931+
"./example.py:1:1: C418 Unnecessary dict comprehension passed "
932+
+ "to dict() - remove the outer call to dict()."
933+
],
934+
),
935+
],
936+
)
937+
def test_C418_fail(code, failures, flake8_path):
938+
(flake8_path / "example.py").write_text(dedent(code))
939+
result = flake8_path.run_flake8()
940+
assert result.out_lines == failures

0 commit comments

Comments
 (0)