File: simplify_strip.py

package info (click to toggle)
python-refurb 1.27.0-1
  • links: PTS, VCS
  • area: main
  • in suites: sid
  • size: 1,700 kB
  • sloc: python: 9,468; makefile: 40; sh: 6
file content (100 lines) | stat: -rw-r--r-- 2,750 bytes parent folder | download
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
90
91
92
93
94
95
96
97
98
99
100
from dataclasses import dataclass

from mypy.nodes import CallExpr, MemberExpr, NameExpr, StrExpr, Var

from refurb.error import Error


@dataclass
class ErrorInfo(Error):
    """
    In some situations the `.lstrip()`, `.rstrip()` and `.strip()` string
    methods can be written more succinctly: `strip()` is the same thing as
    calling both `lstrip()` and `rstrip()` together, and all the strip
    functions take an iterable argument of the characters to strip, meaning
    you don't need to call strip methods multiple times with different
    arguments, you can just concatenate them and call it once.

    Bad:

    ```
    name = input().lstrip().rstrip()

    num = "  -123".lstrip(" ").lstrip("-")
    ```

    Good:

    ```
    name = input().strip()

    num = "  -123".lstrip(" -")
    ```
    """

    name = "simplify-strip"
    code = 159
    categories = ("readability", "string")


STRIP_FUNCS = ("lstrip", "rstrip", "strip")


def check(node: CallExpr, errors: list[Error]) -> None:
    match node:
        case CallExpr(
            callee=MemberExpr(
                expr=CallExpr(
                    callee=MemberExpr(expr=expr, name=lhs_func),
                    args=lhs_args,
                ),
                name=rhs_func,
            ),
            args=rhs_args,
        ) if rhs_func in STRIP_FUNCS and lhs_func in STRIP_FUNCS:
            match expr:
                case StrExpr():
                    pass

                case NameExpr(node=Var(type=ty)) if str(ty) == "builtins.str":
                    pass

                case _:
                    return

            exprs: list[str]

            match lhs_args, rhs_args:
                case [], []:
                    lhs_arg = rhs_arg = ""

                    if lhs_func == rhs_func:
                        exprs = [f"{lhs_func}()"]
                    else:
                        exprs = ["strip()"]

                case (
                    [StrExpr(value=lhs_arg)],
                    [StrExpr(value=rhs_arg)],
                ):
                    if lhs_func == rhs_func:
                        combined = "".join(sorted(set(lhs_arg + rhs_arg)))
                        exprs = [f"{lhs_func}({combined!r})"]

                    elif lhs_arg == rhs_arg:
                        exprs = [f"strip({lhs_arg!r})"]

                    else:
                        return

                    lhs_arg = repr(lhs_arg)
                    rhs_arg = repr(rhs_arg)

                case _:
                    return

            lhs = f"{lhs_func}({lhs_arg})"
            rhs = f"{rhs_func}({rhs_arg})"
            new = f"x.{'.'.join(exprs)}"

            errors.append(ErrorInfo.from_node(node, f"Replace `x.{lhs}.{rhs}` with `{new}`"))