flake8_simplify 0.31.0__tar.gz → 0.31.1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {flake8_simplify-0.31.0 → flake8_simplify-0.31.1}/PKG-INFO +1 -1
- flake8_simplify-0.31.1/flake8_simplify/__init__.py +45 -0
- flake8_simplify-0.31.1/flake8_simplify/registry.py +71 -0
- {flake8_simplify-0.31.0 → flake8_simplify-0.31.1}/flake8_simplify/rules/ast_assign.py +25 -47
- {flake8_simplify-0.31.0 → flake8_simplify-0.31.1}/flake8_simplify/rules/ast_bool_op.py +46 -54
- {flake8_simplify-0.31.0 → flake8_simplify-0.31.1}/flake8_simplify/rules/ast_call.py +40 -84
- flake8_simplify-0.31.1/flake8_simplify/rules/ast_classdef.py +19 -0
- {flake8_simplify-0.31.0 → flake8_simplify-0.31.1}/flake8_simplify/rules/ast_compare.py +15 -24
- flake8_simplify-0.31.1/flake8_simplify/rules/ast_expr.py +64 -0
- {flake8_simplify-0.31.0 → flake8_simplify-0.31.1}/flake8_simplify/rules/ast_for.py +80 -80
- flake8_simplify-0.31.1/flake8_simplify/rules/ast_if.py +505 -0
- flake8_simplify-0.31.1/flake8_simplify/rules/ast_ifexp.py +61 -0
- {flake8_simplify-0.31.0 → flake8_simplify-0.31.1}/flake8_simplify/rules/ast_subscript.py +10 -14
- flake8_simplify-0.31.1/flake8_simplify/rules/ast_try.py +94 -0
- flake8_simplify-0.31.1/flake8_simplify/rules/ast_unary_op.py +82 -0
- {flake8_simplify-0.31.0 → flake8_simplify-0.31.1}/flake8_simplify/rules/ast_with.py +7 -8
- {flake8_simplify-0.31.0 → flake8_simplify-0.31.1}/flake8_simplify/utils.py +64 -69
- {flake8_simplify-0.31.0 → flake8_simplify-0.31.1}/pyproject.toml +4 -1
- {flake8_simplify-0.31.0 → flake8_simplify-0.31.1}/pyproject.toml.orig +5 -1
- flake8_simplify-0.31.0/flake8_simplify/__init__.py +0 -170
- flake8_simplify-0.31.0/flake8_simplify/constants.py +0 -5
- flake8_simplify-0.31.0/flake8_simplify/rules/ast_classdef.py +0 -22
- flake8_simplify-0.31.0/flake8_simplify/rules/ast_expr.py +0 -100
- flake8_simplify-0.31.0/flake8_simplify/rules/ast_if.py +0 -512
- flake8_simplify-0.31.0/flake8_simplify/rules/ast_ifexp.py +0 -65
- flake8_simplify-0.31.0/flake8_simplify/rules/ast_try.py +0 -93
- flake8_simplify-0.31.0/flake8_simplify/rules/ast_unary_op.py +0 -103
- {flake8_simplify-0.31.0 → flake8_simplify-0.31.1}/LICENSE +0 -0
- {flake8_simplify-0.31.0 → flake8_simplify-0.31.1}/README.md +0 -0
- {flake8_simplify-0.31.0 → flake8_simplify-0.31.1}/flake8_simplify/rules/__init__.py +0 -0
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import ast
|
|
2
|
+
import importlib.metadata as importlib_metadata
|
|
3
|
+
from collections.abc import Generator
|
|
4
|
+
from typing import Any
|
|
5
|
+
|
|
6
|
+
from flake8_simplify.registry import get_rules
|
|
7
|
+
from flake8_simplify.utils import add_meta
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class Visitor(ast.NodeVisitor):
|
|
11
|
+
"""Run every rule registered for a node type on each node of that type."""
|
|
12
|
+
|
|
13
|
+
def __init__(self) -> None:
|
|
14
|
+
self.errors: list[tuple[int, int, str]] = []
|
|
15
|
+
self._rules = get_rules()
|
|
16
|
+
|
|
17
|
+
def visit(self, node: ast.AST) -> Any:
|
|
18
|
+
for rule in self._rules.get(type(node), ()):
|
|
19
|
+
for violation in rule.check(node):
|
|
20
|
+
self.errors.append(
|
|
21
|
+
(
|
|
22
|
+
violation.node.lineno,
|
|
23
|
+
violation.node.col_offset,
|
|
24
|
+
f"{rule.code} {violation.message}",
|
|
25
|
+
)
|
|
26
|
+
)
|
|
27
|
+
self.generic_visit(node)
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
class Plugin:
|
|
31
|
+
name = __name__
|
|
32
|
+
version = importlib_metadata.version(__name__) # type: ignore
|
|
33
|
+
|
|
34
|
+
def __init__(self, tree: ast.AST):
|
|
35
|
+
self._tree = tree
|
|
36
|
+
|
|
37
|
+
def run(self) -> Generator[tuple[int, int, str, type[Any]], None, None]:
|
|
38
|
+
visitor = Visitor()
|
|
39
|
+
|
|
40
|
+
# Add parent
|
|
41
|
+
add_meta(self._tree)
|
|
42
|
+
visitor.visit(self._tree)
|
|
43
|
+
|
|
44
|
+
for line, col, msg in visitor.errors:
|
|
45
|
+
yield line, col, msg, type(self)
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Registry of all rules.
|
|
3
|
+
|
|
4
|
+
A rule is a generator function which receives a node of the given type and
|
|
5
|
+
yields a ``Violation`` for every problem it finds. It registers itself with
|
|
6
|
+
the ``rule`` decorator; modules in ``flake8_simplify/rules/`` are discovered
|
|
7
|
+
automatically, so adding a rule never requires touching any other file.
|
|
8
|
+
|
|
9
|
+
@rule("SIM120", ast.ClassDef)
|
|
10
|
+
def get_sim120(node: ast.ClassDef) -> Iterator[Violation]:
|
|
11
|
+
if ...:
|
|
12
|
+
yield Violation(node, "Use 'class Foo:' ...")
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
import ast
|
|
16
|
+
import functools
|
|
17
|
+
import importlib
|
|
18
|
+
import pkgutil
|
|
19
|
+
from collections import defaultdict
|
|
20
|
+
from collections.abc import Callable, Iterator
|
|
21
|
+
from dataclasses import dataclass
|
|
22
|
+
from typing import Any, NamedTuple
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
class Violation(NamedTuple):
|
|
26
|
+
"""A problem found by a rule, reported at the position of ``node``."""
|
|
27
|
+
|
|
28
|
+
node: ast.expr | ast.stmt
|
|
29
|
+
message: str
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
@dataclass(frozen=True)
|
|
33
|
+
class Rule:
|
|
34
|
+
code: str
|
|
35
|
+
node_type: type[ast.AST]
|
|
36
|
+
check: Callable[[Any], Iterator[Violation]]
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
_RULES: list[Rule] = []
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def rule(
|
|
43
|
+
code: str,
|
|
44
|
+
node_type: type[ast.AST],
|
|
45
|
+
) -> Callable[
|
|
46
|
+
[Callable[[Any], Iterator[Violation]]],
|
|
47
|
+
Callable[[Any], Iterator[Violation]],
|
|
48
|
+
]:
|
|
49
|
+
"""Register a function as the check for ``code`` on ``node_type`` nodes."""
|
|
50
|
+
|
|
51
|
+
def register(
|
|
52
|
+
check: Callable[[Any], Iterator[Violation]],
|
|
53
|
+
) -> Callable[[Any], Iterator[Violation]]:
|
|
54
|
+
_RULES.append(Rule(code, node_type, check))
|
|
55
|
+
return check
|
|
56
|
+
|
|
57
|
+
return register
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
@functools.cache
|
|
61
|
+
def get_rules() -> dict[type[ast.AST], list[Rule]]:
|
|
62
|
+
"""Get all rules, grouped by the type of node they check."""
|
|
63
|
+
from flake8_simplify import rules
|
|
64
|
+
|
|
65
|
+
for module in pkgutil.iter_modules(rules.__path__):
|
|
66
|
+
importlib.import_module(f"{rules.__name__}.{module.name}")
|
|
67
|
+
|
|
68
|
+
by_node_type: dict[type[ast.AST], list[Rule]] = defaultdict(list)
|
|
69
|
+
for registered in _RULES:
|
|
70
|
+
by_node_type[registered.node_type].append(registered)
|
|
71
|
+
return by_node_type
|
|
@@ -1,25 +1,18 @@
|
|
|
1
1
|
import ast
|
|
2
|
+
from collections.abc import Iterator
|
|
2
3
|
|
|
3
|
-
from flake8_simplify.
|
|
4
|
+
from flake8_simplify.registry import Violation, rule
|
|
5
|
+
from flake8_simplify.utils import (
|
|
6
|
+
expression_uses_variable,
|
|
7
|
+
get_next_sibling,
|
|
8
|
+
get_parent,
|
|
9
|
+
in_same_block,
|
|
10
|
+
to_source,
|
|
11
|
+
)
|
|
4
12
|
|
|
5
13
|
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
Check that both statements are part of the same statement list.
|
|
9
|
-
|
|
10
|
-
Siblings are linked across the fields of a node, so the last statement of
|
|
11
|
-
``body`` has the first statement of ``orelse`` as next sibling.
|
|
12
|
-
"""
|
|
13
|
-
parent = getattr(first, "parent", None)
|
|
14
|
-
if parent is None:
|
|
15
|
-
return True
|
|
16
|
-
for _, value in ast.iter_fields(parent):
|
|
17
|
-
if isinstance(value, list) and first in value:
|
|
18
|
-
return second in value
|
|
19
|
-
return True
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
def get_sim904(node: ast.Assign) -> list[tuple[int, int, str]]:
|
|
14
|
+
@rule("SIM904", ast.Assign)
|
|
15
|
+
def get_sim904(node: ast.Assign) -> Iterator[Violation]:
|
|
23
16
|
"""
|
|
24
17
|
Assign values to dictionary directly at initialization.
|
|
25
18
|
|
|
@@ -52,9 +45,8 @@ def get_sim904(node: ast.Assign) -> list[tuple[int, int, str]]:
|
|
|
52
45
|
),
|
|
53
46
|
]
|
|
54
47
|
"""
|
|
55
|
-
RULE = "
|
|
56
|
-
|
|
57
|
-
n2 = node.next_sibling # type: ignore
|
|
48
|
+
RULE = "Initialize dictionary '{dict_name}' directly"
|
|
49
|
+
n2 = get_next_sibling(node)
|
|
58
50
|
if not (
|
|
59
51
|
isinstance(node.value, ast.Dict)
|
|
60
52
|
and isinstance(n2, ast.Assign)
|
|
@@ -64,26 +56,20 @@ def get_sim904(node: ast.Assign) -> list[tuple[int, int, str]]:
|
|
|
64
56
|
and isinstance(n2.targets[0].value, ast.Name)
|
|
65
57
|
and isinstance(node.targets[0], ast.Name)
|
|
66
58
|
and n2.targets[0].value.id == node.targets[0].id
|
|
67
|
-
and
|
|
59
|
+
and in_same_block(node, n2)
|
|
68
60
|
):
|
|
69
|
-
return
|
|
61
|
+
return
|
|
70
62
|
|
|
71
63
|
dict_name = to_source(node.targets[0])
|
|
72
64
|
# Capture cases where the assigned value uses another dictionary value
|
|
73
65
|
if expression_uses_variable(n2.value, dict_name):
|
|
74
|
-
return
|
|
66
|
+
return
|
|
75
67
|
|
|
76
|
-
|
|
77
|
-
(
|
|
78
|
-
node.lineno,
|
|
79
|
-
node.col_offset,
|
|
80
|
-
RULE.format(dict_name=dict_name),
|
|
81
|
-
)
|
|
82
|
-
)
|
|
83
|
-
return errors
|
|
68
|
+
yield Violation(node, RULE.format(dict_name=dict_name))
|
|
84
69
|
|
|
85
70
|
|
|
86
|
-
|
|
71
|
+
@rule("SIM909", ast.Assign)
|
|
72
|
+
def get_sim909(node: ast.Assign) -> Iterator[Violation]:
|
|
87
73
|
"""
|
|
88
74
|
Avoid reflexive assignments
|
|
89
75
|
|
|
@@ -103,8 +89,7 @@ def get_sim909(node: Assign) -> list[tuple[int, int, str]]:
|
|
|
103
89
|
),
|
|
104
90
|
]
|
|
105
91
|
"""
|
|
106
|
-
RULE = "
|
|
107
|
-
errors: list[tuple[int, int, str]] = []
|
|
92
|
+
RULE = "Remove reflexive assignment '{code}'"
|
|
108
93
|
|
|
109
94
|
names = []
|
|
110
95
|
if isinstance(node.value, (ast.Name, ast.Subscript, ast.Tuple)):
|
|
@@ -113,18 +98,11 @@ def get_sim909(node: Assign) -> list[tuple[int, int, str]]:
|
|
|
113
98
|
names.append(to_source(target))
|
|
114
99
|
|
|
115
100
|
if len(names) == len(set(names)):
|
|
116
|
-
return
|
|
101
|
+
return
|
|
117
102
|
|
|
118
|
-
if isinstance(node
|
|
119
|
-
return
|
|
103
|
+
if isinstance(get_parent(node), ast.ClassDef):
|
|
104
|
+
return
|
|
120
105
|
|
|
121
|
-
code = to_source(node
|
|
106
|
+
code = to_source(node)
|
|
122
107
|
|
|
123
|
-
|
|
124
|
-
(
|
|
125
|
-
node.lineno,
|
|
126
|
-
node.col_offset,
|
|
127
|
-
RULE.format(code=code),
|
|
128
|
-
)
|
|
129
|
-
)
|
|
130
|
-
return errors
|
|
108
|
+
yield Violation(node, RULE.format(code=code))
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import ast
|
|
2
2
|
from collections import defaultdict
|
|
3
|
+
from collections.abc import Iterator
|
|
3
4
|
|
|
4
|
-
from flake8_simplify.
|
|
5
|
+
from flake8_simplify.registry import Violation, rule
|
|
5
6
|
from flake8_simplify.utils import (
|
|
6
7
|
_get_duplicated_isinstance_call_by_node,
|
|
7
8
|
is_same_expression,
|
|
@@ -9,24 +10,24 @@ from flake8_simplify.utils import (
|
|
|
9
10
|
)
|
|
10
11
|
|
|
11
12
|
|
|
13
|
+
@rule("SIM101", ast.BoolOp)
|
|
12
14
|
def get_sim101(
|
|
13
15
|
node: ast.BoolOp,
|
|
14
|
-
) ->
|
|
16
|
+
) -> Iterator[Violation]:
|
|
15
17
|
"""Get a positions where the duplicate isinstance problem appears."""
|
|
16
|
-
errors: list[tuple[int, int, str]] = []
|
|
17
18
|
if not isinstance(node.op, ast.Or):
|
|
18
|
-
return
|
|
19
|
+
return
|
|
19
20
|
|
|
20
21
|
RULE = (
|
|
21
|
-
"
|
|
22
|
+
"Multiple isinstance-calls which can be merged into a single "
|
|
22
23
|
"call for variable '{var}'"
|
|
23
24
|
)
|
|
24
25
|
for var in _get_duplicated_isinstance_call_by_node(node):
|
|
25
|
-
|
|
26
|
-
return errors
|
|
26
|
+
yield Violation(node, RULE.format(var=var))
|
|
27
27
|
|
|
28
28
|
|
|
29
|
-
|
|
29
|
+
@rule("SIM109", ast.BoolOp)
|
|
30
|
+
def get_sim109(node: ast.BoolOp) -> Iterator[Violation]:
|
|
30
31
|
"""
|
|
31
32
|
Check if multiple equalities with the same value are combined via "or".
|
|
32
33
|
|
|
@@ -46,9 +47,8 @@ def get_sim109(node: ast.BoolOp) -> list[tuple[int, int, str]]:
|
|
|
46
47
|
],
|
|
47
48
|
)
|
|
48
49
|
"""
|
|
49
|
-
errors: list[tuple[int, int, str]] = []
|
|
50
50
|
if not isinstance(node.op, ast.Or):
|
|
51
|
-
return
|
|
51
|
+
return
|
|
52
52
|
equalities = [
|
|
53
53
|
value
|
|
54
54
|
for value in node.values
|
|
@@ -64,25 +64,22 @@ def get_sim109(node: ast.BoolOp) -> list[tuple[int, int, str]]:
|
|
|
64
64
|
and isinstance(eq.comparators[0], ast.Name)
|
|
65
65
|
):
|
|
66
66
|
id2vals[eq.left.id].append(eq.comparators[0])
|
|
67
|
-
RULE = "
|
|
67
|
+
RULE = "Use '{value} in {values}' instead of '{or_op}'"
|
|
68
68
|
for value, values in id2vals.items():
|
|
69
69
|
if len(values) == 1:
|
|
70
70
|
continue
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
node
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
values=f"({to_source(ast.Tuple(elts=values))})", # type: ignore
|
|
79
|
-
),
|
|
80
|
-
)
|
|
71
|
+
yield Violation(
|
|
72
|
+
node,
|
|
73
|
+
RULE.format(
|
|
74
|
+
or_op=to_source(node),
|
|
75
|
+
value=value,
|
|
76
|
+
values=f"({to_source(ast.Tuple(elts=values))})", # type: ignore
|
|
77
|
+
),
|
|
81
78
|
)
|
|
82
|
-
return errors
|
|
83
79
|
|
|
84
80
|
|
|
85
|
-
|
|
81
|
+
@rule("SIM220", ast.BoolOp)
|
|
82
|
+
def get_sim220(node: ast.BoolOp) -> Iterator[Violation]:
|
|
86
83
|
"""
|
|
87
84
|
Get a list of all calls of the type "a and not a".
|
|
88
85
|
|
|
@@ -97,9 +94,8 @@ def get_sim220(node: ast.BoolOp) -> list[tuple[int, int, str]]:
|
|
|
97
94
|
],
|
|
98
95
|
)
|
|
99
96
|
"""
|
|
100
|
-
errors: list[tuple[int, int, str]] = []
|
|
101
97
|
if not (isinstance(node.op, ast.And) and len(node.values) >= 2):
|
|
102
|
-
return
|
|
98
|
+
return
|
|
103
99
|
# We have a boolean And. Let's make sure there is two times the same
|
|
104
100
|
# expression, but once with a "not"
|
|
105
101
|
negated_expressions = []
|
|
@@ -110,19 +106,19 @@ def get_sim220(node: ast.BoolOp) -> list[tuple[int, int, str]]:
|
|
|
110
106
|
else:
|
|
111
107
|
non_negated_expressions.append(exp)
|
|
112
108
|
if len(negated_expressions) == 0:
|
|
113
|
-
return
|
|
109
|
+
return
|
|
114
110
|
|
|
115
|
-
RULE = "
|
|
111
|
+
RULE = "Use 'False' instead of '{a} and not {a}'"
|
|
116
112
|
for negated_expression in negated_expressions:
|
|
117
113
|
for non_negated_expression in non_negated_expressions:
|
|
118
114
|
if is_same_expression(negated_expression, non_negated_expression):
|
|
119
115
|
a = to_source(negated_expression)
|
|
120
|
-
|
|
121
|
-
return
|
|
122
|
-
return errors
|
|
116
|
+
yield Violation(node, RULE.format(a=a))
|
|
117
|
+
return
|
|
123
118
|
|
|
124
119
|
|
|
125
|
-
|
|
120
|
+
@rule("SIM221", ast.BoolOp)
|
|
121
|
+
def get_sim221(node: ast.BoolOp) -> Iterator[Violation]:
|
|
126
122
|
"""
|
|
127
123
|
Get a list of all calls of the type "a or not a".
|
|
128
124
|
|
|
@@ -137,9 +133,8 @@ def get_sim221(node: ast.BoolOp) -> list[tuple[int, int, str]]:
|
|
|
137
133
|
],
|
|
138
134
|
)
|
|
139
135
|
"""
|
|
140
|
-
errors: list[tuple[int, int, str]] = []
|
|
141
136
|
if not (isinstance(node.op, ast.Or) and len(node.values) >= 2):
|
|
142
|
-
return
|
|
137
|
+
return
|
|
143
138
|
# We have a boolean OR. Let's make sure there is two times the same
|
|
144
139
|
# expression, but once with a "not"
|
|
145
140
|
negated_expressions = []
|
|
@@ -150,19 +145,19 @@ def get_sim221(node: ast.BoolOp) -> list[tuple[int, int, str]]:
|
|
|
150
145
|
else:
|
|
151
146
|
non_negated_expressions.append(exp)
|
|
152
147
|
if len(negated_expressions) == 0:
|
|
153
|
-
return
|
|
148
|
+
return
|
|
154
149
|
|
|
155
|
-
RULE = "
|
|
150
|
+
RULE = "Use 'True' instead of '{a} or not {a}'"
|
|
156
151
|
for negated_expression in negated_expressions:
|
|
157
152
|
for non_negated_expression in non_negated_expressions:
|
|
158
153
|
if is_same_expression(negated_expression, non_negated_expression):
|
|
159
154
|
a = to_source(negated_expression)
|
|
160
|
-
|
|
161
|
-
return
|
|
162
|
-
return errors
|
|
155
|
+
yield Violation(node, RULE.format(a=a))
|
|
156
|
+
return
|
|
163
157
|
|
|
164
158
|
|
|
165
|
-
|
|
159
|
+
@rule("SIM222", ast.BoolOp)
|
|
160
|
+
def get_sim222(node: ast.BoolOp) -> Iterator[Violation]:
|
|
166
161
|
"""
|
|
167
162
|
Get a list of all calls of the type "... or True".
|
|
168
163
|
|
|
@@ -177,19 +172,18 @@ def get_sim222(node: ast.BoolOp) -> list[tuple[int, int, str]]:
|
|
|
177
172
|
],
|
|
178
173
|
)
|
|
179
174
|
"""
|
|
180
|
-
errors: list[tuple[int, int, str]] = []
|
|
181
175
|
if not (isinstance(node.op, ast.Or)):
|
|
182
|
-
return
|
|
176
|
+
return
|
|
183
177
|
|
|
184
|
-
RULE = "
|
|
178
|
+
RULE = "Use 'True' instead of '... or True'"
|
|
185
179
|
for exp in node.values:
|
|
186
|
-
if isinstance(exp,
|
|
187
|
-
|
|
188
|
-
return
|
|
189
|
-
return errors
|
|
180
|
+
if isinstance(exp, ast.Constant) and exp.value is True:
|
|
181
|
+
yield Violation(node, RULE)
|
|
182
|
+
return
|
|
190
183
|
|
|
191
184
|
|
|
192
|
-
|
|
185
|
+
@rule("SIM223", ast.BoolOp)
|
|
186
|
+
def get_sim223(node: ast.BoolOp) -> Iterator[Violation]:
|
|
193
187
|
"""
|
|
194
188
|
Get a list of all calls of the type "... and False".
|
|
195
189
|
|
|
@@ -204,13 +198,11 @@ def get_sim223(node: ast.BoolOp) -> list[tuple[int, int, str]]:
|
|
|
204
198
|
],
|
|
205
199
|
)
|
|
206
200
|
"""
|
|
207
|
-
errors: list[tuple[int, int, str]] = []
|
|
208
201
|
if not (isinstance(node.op, ast.And)):
|
|
209
|
-
return
|
|
202
|
+
return
|
|
210
203
|
|
|
211
|
-
RULE = "
|
|
204
|
+
RULE = "Use 'False' instead of '... and False'"
|
|
212
205
|
for exp in node.values:
|
|
213
|
-
if isinstance(exp,
|
|
214
|
-
|
|
215
|
-
return
|
|
216
|
-
return errors
|
|
206
|
+
if isinstance(exp, ast.Constant) and exp.value is False:
|
|
207
|
+
yield Violation(node, RULE)
|
|
208
|
+
return
|
|
@@ -1,14 +1,13 @@
|
|
|
1
1
|
import ast
|
|
2
2
|
import json
|
|
3
|
-
import
|
|
3
|
+
from collections.abc import Iterator
|
|
4
4
|
|
|
5
|
-
from flake8_simplify.
|
|
6
|
-
from flake8_simplify.utils import
|
|
5
|
+
from flake8_simplify.registry import Violation, rule
|
|
6
|
+
from flake8_simplify.utils import get_parent, to_source
|
|
7
7
|
|
|
8
|
-
logger = logging.getLogger(__name__)
|
|
9
8
|
|
|
10
|
-
|
|
11
|
-
def get_sim115(node: Call) ->
|
|
9
|
+
@rule("SIM115", ast.Call)
|
|
10
|
+
def get_sim115(node: ast.Call) -> Iterator[Violation]:
|
|
12
11
|
"""
|
|
13
12
|
Find places where open() is called without a context handler.
|
|
14
13
|
|
|
@@ -36,22 +35,21 @@ def get_sim115(node: Call) -> list[tuple[int, int, str]]:
|
|
|
36
35
|
),
|
|
37
36
|
),
|
|
38
37
|
"""
|
|
39
|
-
RULE = "
|
|
40
|
-
errors: list[tuple[int, int, str]] = []
|
|
38
|
+
RULE = "Use context handler for opening files"
|
|
41
39
|
if not (
|
|
42
40
|
isinstance(node.func, ast.Name)
|
|
43
41
|
and node.func.id == "open"
|
|
44
|
-
and not isinstance(node
|
|
42
|
+
and not isinstance(get_parent(node), ast.withitem)
|
|
45
43
|
):
|
|
46
|
-
return
|
|
47
|
-
|
|
48
|
-
return errors
|
|
44
|
+
return
|
|
45
|
+
yield Violation(node, RULE)
|
|
49
46
|
|
|
50
47
|
|
|
51
48
|
# Experimental rules
|
|
52
49
|
|
|
53
50
|
|
|
54
|
-
|
|
51
|
+
@rule("SIM901", ast.Call)
|
|
52
|
+
def get_sim901(node: ast.Call) -> Iterator[Violation]:
|
|
55
53
|
"""
|
|
56
54
|
Get a list of all calls of the type "bool(comparison)".
|
|
57
55
|
|
|
@@ -67,60 +65,44 @@ def get_sim901(node: ast.Call) -> list[tuple[int, int, str]]:
|
|
|
67
65
|
keywords=[],
|
|
68
66
|
)
|
|
69
67
|
"""
|
|
70
|
-
RULE = "
|
|
71
|
-
errors: list[tuple[int, int, str]] = []
|
|
68
|
+
RULE = "Use '{expected}' instead of '{actual}'"
|
|
72
69
|
if not (
|
|
73
70
|
isinstance(node.func, ast.Name)
|
|
74
71
|
and node.func.id == "bool"
|
|
75
72
|
and len(node.args) == 1
|
|
76
73
|
and isinstance(node.args[0], ast.Compare)
|
|
77
74
|
):
|
|
78
|
-
return
|
|
75
|
+
return
|
|
79
76
|
|
|
80
77
|
actual = to_source(node)
|
|
81
78
|
expected = to_source(node.args[0])
|
|
82
79
|
|
|
83
|
-
|
|
84
|
-
(
|
|
85
|
-
node.lineno,
|
|
86
|
-
node.col_offset,
|
|
87
|
-
RULE.format(actual=actual, expected=expected),
|
|
88
|
-
)
|
|
89
|
-
)
|
|
90
|
-
return errors
|
|
80
|
+
yield Violation(node, RULE.format(actual=actual, expected=expected))
|
|
91
81
|
|
|
92
82
|
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
83
|
+
@rule("SIM905", ast.Call)
|
|
84
|
+
def get_sim905(node: ast.Call) -> Iterator[Violation]:
|
|
85
|
+
RULE = "Use '{expected}' instead of '{actual}'"
|
|
96
86
|
if not (
|
|
97
87
|
isinstance(node.func, ast.Attribute)
|
|
98
88
|
and node.func.attr == "split"
|
|
99
89
|
and isinstance(node.func.value, ast.Constant)
|
|
100
90
|
and isinstance(node.func.value.value, str)
|
|
91
|
+
and not node.args
|
|
92
|
+
and not node.keywords
|
|
101
93
|
):
|
|
102
|
-
return
|
|
94
|
+
return
|
|
103
95
|
|
|
104
|
-
|
|
105
|
-
value = node.func.value.value
|
|
106
|
-
else:
|
|
107
|
-
value = node.func.value.value
|
|
96
|
+
value = node.func.value.value
|
|
108
97
|
|
|
109
98
|
expected = json.dumps(value.split())
|
|
110
99
|
actual = to_source(node.func.value) + ".split()"
|
|
111
|
-
|
|
112
|
-
(
|
|
113
|
-
node.lineno,
|
|
114
|
-
node.col_offset,
|
|
115
|
-
RULE.format(expected=expected, actual=actual),
|
|
116
|
-
)
|
|
117
|
-
)
|
|
118
|
-
return errors
|
|
100
|
+
yield Violation(node, RULE.format(expected=expected, actual=actual))
|
|
119
101
|
|
|
120
102
|
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
103
|
+
@rule("SIM906", ast.Call)
|
|
104
|
+
def get_sim906(node: ast.Call) -> Iterator[Violation]:
|
|
105
|
+
RULE = "Use '{expected}' instead of '{actual}'"
|
|
124
106
|
if not (
|
|
125
107
|
isinstance(node.func, ast.Attribute)
|
|
126
108
|
and isinstance(node.func.value, ast.Attribute)
|
|
@@ -142,7 +124,7 @@ def get_sim906(node: ast.Call) -> list[tuple[int, int, str]]:
|
|
|
142
124
|
for arg in node.args
|
|
143
125
|
)
|
|
144
126
|
):
|
|
145
|
-
return
|
|
127
|
+
return
|
|
146
128
|
|
|
147
129
|
def get_os_path_join_args(node: ast.Call) -> list[str]:
|
|
148
130
|
names: list[str] = []
|
|
@@ -157,31 +139,21 @@ def get_sim906(node: ast.Call) -> list[tuple[int, int, str]]:
|
|
|
157
139
|
and arg.func.attr == "join"
|
|
158
140
|
):
|
|
159
141
|
names = names + get_os_path_join_args(arg)
|
|
160
|
-
elif isinstance(arg, ast.Name):
|
|
161
|
-
names.append(arg.id)
|
|
162
142
|
elif isinstance(arg, ast.Constant) and isinstance(arg.value, str):
|
|
163
|
-
names.append(
|
|
143
|
+
names.append(repr(arg.value))
|
|
164
144
|
else:
|
|
165
|
-
|
|
166
|
-
f"Unexpected os.path.join arg: {arg} -- {to_source(arg)}"
|
|
167
|
-
)
|
|
145
|
+
names.append(to_source(arg))
|
|
168
146
|
return names
|
|
169
147
|
|
|
170
148
|
names = get_os_path_join_args(node)
|
|
171
149
|
|
|
172
150
|
actual = to_source(node)
|
|
173
151
|
expected = f"os.path.join({', '.join(names)})"
|
|
174
|
-
|
|
175
|
-
(
|
|
176
|
-
node.lineno,
|
|
177
|
-
node.col_offset,
|
|
178
|
-
RULE.format(actual=actual, expected=expected),
|
|
179
|
-
)
|
|
180
|
-
)
|
|
181
|
-
return errors
|
|
152
|
+
yield Violation(node, RULE.format(actual=actual, expected=expected))
|
|
182
153
|
|
|
183
154
|
|
|
184
|
-
|
|
155
|
+
@rule("SIM910", ast.Call)
|
|
156
|
+
def get_sim910(node: ast.Call) -> Iterator[Violation]:
|
|
185
157
|
"""
|
|
186
158
|
Get a list of all usages of "dict.get(key, None)"
|
|
187
159
|
|
|
@@ -202,38 +174,31 @@ def get_sim910(node: Call) -> list[tuple[int, int, str]]:
|
|
|
202
174
|
),
|
|
203
175
|
),
|
|
204
176
|
"""
|
|
205
|
-
RULE = "
|
|
206
|
-
errors: list[tuple[int, int, str]] = []
|
|
177
|
+
RULE = "Use '{expected}' instead of '{actual}'"
|
|
207
178
|
if not (
|
|
208
179
|
isinstance(node.func, ast.Attribute)
|
|
209
180
|
and node.func.attr == "get"
|
|
210
181
|
and isinstance(node.func.ctx, ast.Load)
|
|
211
182
|
):
|
|
212
|
-
return
|
|
183
|
+
return
|
|
213
184
|
|
|
214
185
|
# check the argument value
|
|
215
186
|
if not (
|
|
216
187
|
len(node.args) == 2
|
|
217
|
-
and isinstance(node.args[1],
|
|
188
|
+
and isinstance(node.args[1], ast.Constant)
|
|
218
189
|
and node.args[1].value is None
|
|
219
190
|
):
|
|
220
|
-
return
|
|
191
|
+
return
|
|
221
192
|
|
|
222
193
|
actual = to_source(node)
|
|
223
194
|
func = to_source(node.func)
|
|
224
195
|
key = to_source(node.args[0])
|
|
225
196
|
expected = f"{func}({key})"
|
|
226
|
-
|
|
227
|
-
(
|
|
228
|
-
node.lineno,
|
|
229
|
-
node.col_offset,
|
|
230
|
-
RULE.format(actual=actual, expected=expected),
|
|
231
|
-
)
|
|
232
|
-
)
|
|
233
|
-
return errors
|
|
197
|
+
yield Violation(node, RULE.format(actual=actual, expected=expected))
|
|
234
198
|
|
|
235
199
|
|
|
236
|
-
|
|
200
|
+
@rule("SIM911", ast.Call)
|
|
201
|
+
def get_sim911(node: ast.AST) -> Iterator[Violation]:
|
|
237
202
|
"""
|
|
238
203
|
Find nodes representing the expression "zip(_.keys(), _.values())".
|
|
239
204
|
|
|
@@ -267,10 +232,8 @@ def get_sim911(node: ast.AST) -> list[tuple[int, int, str]]:
|
|
|
267
232
|
)
|
|
268
233
|
"""
|
|
269
234
|
RULE = (
|
|
270
|
-
"
|
|
271
|
-
"'zip({name}.keys(), {name}.values())'"
|
|
235
|
+
"Use '{name}.items()' instead of 'zip({name}.keys(), {name}.values())'"
|
|
272
236
|
)
|
|
273
|
-
errors: list[tuple[int, int, str]] = []
|
|
274
237
|
|
|
275
238
|
if isinstance(node, ast.Call) and (
|
|
276
239
|
isinstance(node.func, ast.Name)
|
|
@@ -289,11 +252,4 @@ def get_sim911(node: ast.AST) -> list[tuple[int, int, str]]:
|
|
|
289
252
|
and second_arg.func.attr == "values"
|
|
290
253
|
and first_arg.func.value.id == second_arg.func.value.id
|
|
291
254
|
):
|
|
292
|
-
|
|
293
|
-
(
|
|
294
|
-
node.lineno,
|
|
295
|
-
node.col_offset,
|
|
296
|
-
RULE.format(name=first_arg.func.value.id),
|
|
297
|
-
)
|
|
298
|
-
)
|
|
299
|
-
return errors
|
|
255
|
+
yield Violation(node, RULE.format(name=first_arg.func.value.id))
|