Skip to content

Commit

Permalink
Expand C416 to dict comprehensions (#490)
Browse files Browse the repository at this point in the history
Co-authored-by: Adam Johnson <me@adamj.eu>
  • Loading branch information
Skylion007 and adamchainz committed Mar 18, 2023
1 parent c95c753 commit 5016baf
Show file tree
Hide file tree
Showing 4 changed files with 45 additions and 8 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.rst
Expand Up @@ -2,6 +2,10 @@
Changelog
=========

* Expand C416 to ``dict`` comprehensions.

Thanks to Aaron Gokaslan in `PR #490 <https://github.com/adamchainz/flake8-comprehensions/pull/490>`__.

3.10.1 (2022-10-29)
-------------------

Expand Down
9 changes: 5 additions & 4 deletions README.rst
Expand Up @@ -150,13 +150,14 @@ For example:
* Rewrite ``sorted(iterable)[::-1]`` as ``sorted(iterable, reverse=True)``
* Rewrite ``reversed(iterable[::-1])`` as ``iterable``

C416: Unnecessary ``<list/set>`` comprehension - rewrite using ``<list/set>``\().
---------------------------------------------------------------------------------
C416: Unnecessary ``<dict/list/set>`` comprehension - rewrite using ``<dict/list/set>``\().
-------------------------------------------------------------------------------------------

It's unnecessary to use a list comprehension if the elements are unchanged.
The iterable should be wrapped in ``list()`` or ``set()`` instead.
It's unnecessary to use a dict/list/set comprehension to build a data structure if the elements are unchanged.
Wrap the iterable with ``dict()``, ``list()``, or ``set()`` instead.
For example:

* Rewrite ``{a: b for a, b in iterable}`` as ``dict(iterable)``
* Rewrite ``[x for x in iterable]`` as ``list(iterable)``
* Rewrite ``{x for x in iterable}`` as ``set(iterable)``

Expand Down
22 changes: 18 additions & 4 deletions src/flake8_comprehensions/__init__.py
Expand Up @@ -295,15 +295,29 @@ def run(self) -> Generator[tuple[int, int, str, type[Any]], None, None]:
type(self),
)

elif isinstance(node, (ast.ListComp, ast.SetComp)):
elif isinstance(node, (ast.DictComp, ast.ListComp, ast.SetComp)):
if (
len(node.generators) == 1
and not node.generators[0].ifs
and not node.generators[0].is_async
and (
isinstance(node.elt, ast.Name)
and isinstance(node.generators[0].target, ast.Name)
and node.elt.id == node.generators[0].target.id
(
isinstance(node, (ast.ListComp, ast.SetComp))
and isinstance(node.elt, ast.Name)
and isinstance(node.generators[0].target, ast.Name)
and node.elt.id == node.generators[0].target.id
)
or (
isinstance(node, ast.DictComp)
and isinstance(node.key, ast.Name)
and isinstance(node.value, ast.Name)
and isinstance(node.generators[0].target, ast.Tuple)
and len(node.generators[0].target.elts) == 2
and isinstance(node.generators[0].target.elts[0], ast.Name)
and node.generators[0].target.elts[0].id == node.key.id
and isinstance(node.generators[0].target.elts[1], ast.Name)
and node.generators[0].target.elts[1].id == node.value.id
)
)
):
yield (
Expand Down
18 changes: 18 additions & 0 deletions tests/test_flake8_comprehensions.py
Expand Up @@ -757,6 +757,10 @@ def test_C415_fail(code, failures, flake8_path):
@pytest.mark.parametrize(
"code",
[
"{x, y for x, y, z in zip('abc', '123', 'def')}",
"{y: x for x, y in zip('abc', '123')}",
"{x: y for x, (y,) in zip('a', ('1',))}",
"{x: z for x, (y,), z in zip('a', ('1',), 'b')}",
"[str(x) for x in range(5)]",
"[x + 1 for x in range(5)]",
"[x for x in range(5) if x % 2]",
Expand Down Expand Up @@ -796,6 +800,20 @@ def test_C416_pass(code, flake8_path):
@pytest.mark.parametrize(
"code,failures",
[
(
"{x: y for x, y in zip(range(5), range(5))}",
[
"./example.py:1:1: C416 Unnecessary dict comprehension - "
+ "rewrite using dict().",
],
),
(
"{x: y for (x, y) in zip(range(5), range(5))}",
[
"./example.py:1:1: C416 Unnecessary dict comprehension - "
+ "rewrite using dict().",
],
),
(
"[x for x in range(5)]",
[
Expand Down

0 comments on commit 5016baf

Please sign in to comment.