File: use_func_name.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 (109 lines) | stat: -rw-r--r-- 2,648 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
101
102
103
104
105
106
107
108
109
from dataclasses import dataclass

from mypy.nodes import (
    ArgKind,
    Argument,
    Block,
    CallExpr,
    DictExpr,
    Expression,
    LambdaExpr,
    ListExpr,
    NameExpr,
    RefExpr,
    ReturnStmt,
    TupleExpr,
)

from refurb.checks.common import stringify
from refurb.error import Error


@dataclass
class ErrorInfo(Error):
    """
    Don't use a lambda if it is just forwarding its arguments to a
    function verbatim:

    Bad:

    ```
    predicate = lambda x: bool(x)

    some_func(lambda x, y: print(x, y))
    ```

    Good:

    ```
    predicate = bool

    some_func(print)
    ```
    """

    name = "use-func-name"
    code = 111
    categories = ("readability",)


def get_lambda_arg_names(args: list[Argument]) -> list[str]:
    return [arg.variable.name for arg in args]


def get_func_arg_names(args: list[Expression]) -> list[str | None]:
    return [arg.name if isinstance(arg, NameExpr) else None for arg in args]


def check(node: LambdaExpr, errors: list[Error]) -> None:
    match node:
        case LambdaExpr(
            arguments=lambda_args,
            body=Block(
                body=[
                    ReturnStmt(expr=CallExpr(callee=RefExpr() as ref) as func),
                ]
            ),
        ) if (
            get_lambda_arg_names(lambda_args) == get_func_arg_names(func.args)
            and all(kind == ArgKind.ARG_POS for kind in func.arg_kinds)
        ):
            func_name = stringify(ref)
            arg_names = get_lambda_arg_names(lambda_args)
            arg_names = ", ".join(arg_names) if arg_names else ""

            _lambda = f"lambda {arg_names}" if arg_names else "lambda"

            errors.append(
                ErrorInfo.from_node(
                    node,
                    f"Replace `{_lambda}: {func_name}({arg_names})` with `{func_name}`",  # noqa: E501
                )
            )

        case LambdaExpr(
            arguments=[],
            body=Block(
                body=[
                    ReturnStmt(
                        expr=ListExpr(items=[]) | DictExpr(items=[]) | TupleExpr(items=[]) as expr,
                    )
                ],
            ),
        ):
            if isinstance(expr, ListExpr):
                old = "[]"
                new = "list"
            elif isinstance(expr, DictExpr):
                old = "{}"
                new = "dict"
            else:
                old = "()"
                new = "tuple"

            errors.append(
                ErrorInfo.from_node(
                    node,
                    f"Replace `lambda: {old}` with `{new}`",
                )
            )