1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87
|
from dataclasses import dataclass
from mypy.nodes import (
CallExpr,
DictionaryComprehension,
ForStmt,
GeneratorExpr,
MemberExpr,
NameExpr,
Node,
TupleExpr,
Var,
)
from refurb.checks.common import check_for_loop_like, is_name_unused_in_contexts
from refurb.error import Error
@dataclass
class ErrorInfo(Error):
"""
Don't use `.items()` on a `dict` if you only care about the keys or the
values, but not both:
Bad:
```
books = {"Frank Herbert": "Dune"}
for author, _ in books.items():
print(author)
for _, book in books.items():
print(book)
```
Good:
```
books = {"Frank Herbert": "Dune"}
for author in books:
print(author)
for book in books.values():
print(book)
```
"""
name = "no-ignored-dict-items"
code = 135
categories = ("dict",)
def check(
node: ForStmt | GeneratorExpr | DictionaryComprehension,
errors: list[Error],
) -> None:
check_for_loop_like(check_dict_items_call, node, errors)
def check_dict_items_call(
index: Node, expr: Node, contexts: list[Node], errors: list[Error]
) -> None:
match index, expr:
case (
TupleExpr(items=[NameExpr() as key, NameExpr() as value]),
CallExpr(
callee=MemberExpr(
expr=NameExpr(node=Var(type=ty)),
name="items",
)
),
) if str(ty).startswith("builtins.dict["):
check_unused_key_or_value(key, value, contexts, errors)
def check_unused_key_or_value(
key: NameExpr, value: NameExpr, contexts: list[Node], errors: list[Error]
) -> None:
if is_name_unused_in_contexts(key, contexts):
errors.append(
ErrorInfo.from_node(key, "Key is unused, use `for value in d.values()` instead")
)
if is_name_unused_in_contexts(value, contexts):
errors.append(ErrorInfo.from_node(value, "Value is unused, use `for key in d` instead"))
|