Skip to content

Commit

Permalink
SIM911: Avoid using zip(dict.keys(), dict.values()) (#183)
Browse files Browse the repository at this point in the history
Closes #161
  • Loading branch information
ScDor authored Sep 23, 2023
1 parent ce2b70d commit 9f7134b
Show file tree
Hide file tree
Showing 4 changed files with 99 additions and 0 deletions.
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ Simplifying usage of dictionaries:

* [`SIM401`](https://github.com/MartinThoma/flake8-simplify/issues/72): Use 'a_dict.get(key, "default_value")' instead of an if-block ([example](#SIM401))
* [`SIM118`](https://github.com/MartinThoma/flake8-simplify/issues/40): Use 'key in dict' instead of 'key in dict.keys()' ([example](#SIM118))
* `SIM119` Reserved for [SIM911](#sim911) once it's stable

General Code Style:

Expand Down Expand Up @@ -106,6 +107,7 @@ Current experimental rules:
* [`SIM908`](https://github.com/MartinThoma/flake8-simplify/issues/50): Use dict.get(key) ([example](#SIM908))
* [`SIM909`](https://github.com/MartinThoma/flake8-simplify/issues/114): Avoid reflexive assignments ([example](#SIM909))
* [`SIM910`](https://github.com/MartinThoma/flake8-simplify/issues/171): Avoid to use `dict.get(key, None)` ([example](#SIM910))
* [`SIM911`](https://github.com/MartinThoma/flake8-simplify/issues/161): Avoid using `zip(dict.keys(), dict.values())` ([example](#SIM911))

## Disabling Rules

Expand Down
2 changes: 2 additions & 0 deletions flake8_simplify/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
get_sim905,
get_sim906,
get_sim910,
get_sim911,
)
from flake8_simplify.rules.ast_classdef import get_sim120
from flake8_simplify.rules.ast_compare import get_sim118, get_sim300
Expand Down Expand Up @@ -76,6 +77,7 @@ def visit_Call(self, node: ast.Call) -> Any:
self.errors += get_sim905(node)
self.errors += get_sim906(node)
self.errors += get_sim910(Call(node))
self.errors += get_sim911(node)
self.generic_visit(node)

def visit_With(self, node: ast.With) -> Any:
Expand Down
64 changes: 64 additions & 0 deletions flake8_simplify/rules/ast_call.py
Original file line number Diff line number Diff line change
Expand Up @@ -233,3 +233,67 @@ def get_sim910(node: Call) -> List[Tuple[int, int, str]]:
)
)
return errors


def get_sim911(node: ast.AST) -> List[Tuple[int, int, str]]:
"""
Find nodes representing the expression "zip(_.keys(), _.values())".
Returns a list of tuples containing the line number and column offset
of each identified node.
Expr(
value=Call(
func=Name(id='zip', ctx=Load()),
args=[
Call(
func=Attribute(
value=Name(id='_', ctx=Load()),
attr='keys',
ctx=Load()),
args=[],
keywords=[]),
Call(
func=Attribute(
value=Name(id='_', ctx=Load()),
attr='values',
ctx=Load()),
args=[],
keywords=[])],
keywords=[
keyword(
arg='strict',
value=Constant(value=False))
]
)
)
"""
RULE = "SIM911 Use '{name}.items()' instead of 'zip({name}.keys(),{name}.values())'"
errors: List[Tuple[int, int, str]] = []

if isinstance(node, ast.Call):
if (
isinstance(node.func, ast.Name)
and node.func.id == "zip"
and len(node.args) == 2
):
first_arg, second_arg = node.args
if (
isinstance(first_arg, ast.Call)
and isinstance(first_arg.func, ast.Attribute)
and isinstance(first_arg.func.value, ast.Name)
and first_arg.func.attr == "keys"
and isinstance(second_arg, ast.Call)
and isinstance(second_arg.func, ast.Attribute)
and isinstance(second_arg.func.value, ast.Name)
and second_arg.func.attr == "values"
and first_arg.func.value.id == second_arg.func.value.id
):
errors.append(
(
node.lineno,
node.col_offset,
RULE.format(name=first_arg.func.value.id),
)
)
return errors
31 changes: 31 additions & 0 deletions tests/test_900_rules.py
Original file line number Diff line number Diff line change
Expand Up @@ -204,3 +204,34 @@ def test_sim910(s, msg):
expected = {msg} if msg is not None else set()
results = _results(s)
assert results == expected


@pytest.mark.parametrize(
("s", "expected"),
(
(
"zip(d.keys(), d.values())",
{
"1:0 SIM911 Use 'd.items()' instead of 'zip(d.keys(), d.values())'"
},
),
(
"zip(d.keys(), d.keys())",
None,
),
(
"zip(d1.keys(), d2.values())",
None,
),
(
"zip(d1.keys(), values)",
None,
),
(
"zip(keys, values)",
None,
),
),
)
def test_sim911(s, expected):
assert _results(s) == (expected or set())

0 comments on commit 9f7134b

Please sign in to comment.