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 88 89
|
from dataclasses import dataclass
from mypy.nodes import CallExpr, ListExpr, MemberExpr, NameExpr, StrExpr
from refurb.checks.common import stringify
from refurb.checks.string.use_fstring_fmt import CONVERSIONS as FURB_119_FUNCS
from refurb.error import Error
from refurb.visitor import TraverserVisitor
@dataclass
class ErrorInfo(Error):
"""
If you want to stringify a single value without concatenating anything, use
the `str()` function instead.
Bad:
```
nums = [123, 456]
num = f"{num[0]}")
```
Good:
```
nums = [123, 456]
num = str(num[0])
```
"""
name = "use-str-func"
code = 183
categories = ("readability",)
ignore = set[int]()
# TODO: add support for returning False from check to indicate it shouldnt prapogate
class NestedFstringIgnorer(TraverserVisitor):
def visit_call_expr(self, o: CallExpr) -> None:
ignore.add(id(o))
super().visit_call_expr(o)
def check(node: CallExpr, errors: list[Error]) -> None:
if id(node) in ignore:
return
match node:
case CallExpr(
callee=MemberExpr(
expr=StrExpr(value=""),
name="join",
),
args=[ListExpr(items=items)],
):
visitor = NestedFstringIgnorer()
for item in items:
visitor.accept(item)
case CallExpr(
callee=MemberExpr(
expr=StrExpr(value="{:{}}"),
name="format",
),
args=[arg, StrExpr(value="")],
):
match arg:
case CallExpr(callee=NameExpr(fullname=fn)) if fn in FURB_119_FUNCS:
return
x = stringify(arg)
msg = f'Replace `f"{{{x}}}"` with `str({x})`'
errors.append(ErrorInfo.from_node(node, msg))
case CallExpr(
callee=MemberExpr(
expr=StrExpr(value="{:{}}"),
name="format",
),
args=[_, arg],
):
NestedFstringIgnorer().accept(arg)
|