File: with_suppress.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 (75 lines) | stat: -rw-r--r-- 1,935 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
from dataclasses import dataclass

from mypy.nodes import Block, NameExpr, PassStmt, TryStmt, TupleExpr

from refurb.error import Error


@dataclass
class ErrorInfo(Error):
    """
    Often times you want to handle an exception and just ignore it. You can do
    this with a `try`/`except` block with a single `pass` in the `except`
    block, but there is a simpler and more concise way using the `suppress()`
    function from `contextlib`:

    Bad:

    ```
    try:
        f()

    except FileNotFoundError:
        pass
    ```

    Good:

    ```
    with suppress(FileNotFoundError):
        f()
    ```

    Note: `suppress()` is slower than using `try`/`except`, so for performance
    critical code you might consider ignoring this check.
    """

    name = "use-with-suppress"
    code = 107
    categories = ("contextlib", "readability")


def check(node: TryStmt, errors: list[Error]) -> None:
    match node:
        case TryStmt(
            handlers=[Block(body=[PassStmt()])],
            types=[types],
            else_body=None,
            finally_body=None,
        ):
            match types:
                case NameExpr(name=name):
                    inner = name
                    except_inner = f" {inner}"

                case TupleExpr(items=items):
                    if any(not isinstance(item, NameExpr) for item in items):
                        return

                    inner = ", ".join(item.name for item in items)  # type: ignore

                    except_inner = f" ({inner})"

                case None:
                    inner = "BaseException"
                    except_inner = ""

                case _:
                    return

            errors.append(
                ErrorInfo.from_node(
                    node,
                    f"Replace `try: ... except{except_inner}: pass` with `with suppress({inner}): ...`",  # noqa: E501
                )
            )