flake8_simplify 0.20.0__py3-none-any.whl

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.
@@ -0,0 +1,177 @@
1
+ # Core Library
2
+ import ast
3
+ import logging
4
+ import sys
5
+ from typing import Any, Generator, List, Tuple, Type
6
+
7
+ # First party
8
+ from flake8_simplify.rules.ast_assign import get_sim904, get_sim909
9
+ from flake8_simplify.rules.ast_bool_op import (
10
+ get_sim101,
11
+ get_sim109,
12
+ get_sim220,
13
+ get_sim221,
14
+ get_sim222,
15
+ get_sim223,
16
+ )
17
+ from flake8_simplify.rules.ast_call import (
18
+ get_sim115,
19
+ get_sim901,
20
+ get_sim905,
21
+ get_sim906,
22
+ get_sim910,
23
+ )
24
+ from flake8_simplify.rules.ast_classdef import get_sim120
25
+ from flake8_simplify.rules.ast_compare import get_sim118, get_sim300
26
+ from flake8_simplify.rules.ast_expr import get_sim112
27
+ from flake8_simplify.rules.ast_for import (
28
+ get_sim104,
29
+ get_sim110_sim111,
30
+ get_sim113,
31
+ )
32
+ from flake8_simplify.rules.ast_if import (
33
+ get_sim102,
34
+ get_sim103,
35
+ get_sim108,
36
+ get_sim114,
37
+ get_sim116,
38
+ get_sim401,
39
+ get_sim908,
40
+ )
41
+ from flake8_simplify.rules.ast_ifexp import get_sim210, get_sim211, get_sim212
42
+ from flake8_simplify.rules.ast_subscript import get_sim907
43
+ from flake8_simplify.rules.ast_try import get_sim105, get_sim107
44
+ from flake8_simplify.rules.ast_unary_op import (
45
+ get_sim201,
46
+ get_sim202,
47
+ get_sim203,
48
+ get_sim208,
49
+ )
50
+ from flake8_simplify.rules.ast_with import get_sim117
51
+ from flake8_simplify.utils import Assign, Call, For, If, UnaryOp
52
+
53
+ logger = logging.getLogger(__name__)
54
+
55
+
56
+ if sys.version_info < (3, 8): # pragma: no cover (<PY38)
57
+ # Third party
58
+ import importlib_metadata
59
+ else: # pragma: no cover (PY38+)
60
+ # Core Library
61
+ import importlib.metadata as importlib_metadata
62
+
63
+
64
+ class Visitor(ast.NodeVisitor):
65
+ def __init__(self) -> None:
66
+ self.errors: List[Tuple[int, int, str]] = []
67
+
68
+ def visit_Assign(self, node: ast.Assign) -> Any:
69
+ self.errors += get_sim904(node)
70
+ self.errors += get_sim909(Assign(node))
71
+ self.generic_visit(node)
72
+
73
+ def visit_Call(self, node: ast.Call) -> Any:
74
+ self.errors += get_sim115(Call(node))
75
+ self.errors += get_sim901(node)
76
+ self.errors += get_sim905(node)
77
+ self.errors += get_sim906(node)
78
+ self.errors += get_sim910(Call(node))
79
+ self.generic_visit(node)
80
+
81
+ def visit_With(self, node: ast.With) -> Any:
82
+ self.errors += get_sim117(node)
83
+ self.generic_visit(node)
84
+
85
+ def visit_Expr(self, node: ast.Expr) -> None:
86
+ self.errors += get_sim112(node)
87
+ self.generic_visit(node)
88
+
89
+ def visit_BoolOp(self, node: ast.BoolOp) -> None:
90
+ self.errors += get_sim101(node)
91
+ self.errors += get_sim109(node)
92
+ self.errors += get_sim220(node)
93
+ self.errors += get_sim221(node)
94
+ self.errors += get_sim222(node)
95
+ self.errors += get_sim223(node)
96
+ self.generic_visit(node)
97
+
98
+ def visit_If(self, node: ast.If) -> None:
99
+ self.errors += get_sim102(node)
100
+ self.errors += get_sim103(node)
101
+ self.errors += get_sim108(If(node))
102
+ self.errors += get_sim114(node)
103
+ self.errors += get_sim116(node)
104
+ self.errors += get_sim908(node)
105
+ self.errors += get_sim401(node)
106
+ self.generic_visit(node)
107
+
108
+ def visit_For(self, node: ast.For) -> None:
109
+ self.errors += get_sim104(node)
110
+ self.errors += get_sim110_sim111(node)
111
+ self.errors += get_sim113(For(node))
112
+ self.generic_visit(node)
113
+
114
+ def visit_Subscript(self, node: ast.Subscript) -> None:
115
+ self.errors += get_sim907(node)
116
+ self.generic_visit(node)
117
+
118
+ def visit_Try(self, node: ast.Try) -> None:
119
+ self.errors += get_sim105(node)
120
+ self.errors += get_sim107(node)
121
+ self.generic_visit(node)
122
+
123
+ def visit_UnaryOp(self, node_v: ast.UnaryOp) -> None:
124
+ node = UnaryOp(node_v)
125
+ self.errors += get_sim201(node)
126
+ self.errors += get_sim202(node)
127
+ self.errors += get_sim203(node)
128
+ self.errors += get_sim208(node)
129
+ self.generic_visit(node)
130
+
131
+ def visit_IfExp(self, node: ast.IfExp) -> None:
132
+ self.errors += get_sim210(node)
133
+ self.errors += get_sim211(node)
134
+ self.errors += get_sim212(node)
135
+ self.generic_visit(node)
136
+
137
+ def visit_Compare(self, node: ast.Compare) -> None:
138
+ self.errors += get_sim118(node)
139
+ self.errors += get_sim300(node)
140
+ self.generic_visit(node)
141
+
142
+ def visit_ClassDef(self, node: ast.ClassDef) -> None:
143
+ self.errors += get_sim120(node)
144
+ self.generic_visit(node)
145
+
146
+
147
+ class Plugin:
148
+ name = __name__
149
+ version = importlib_metadata.version(__name__) # type: ignore
150
+
151
+ def __init__(self, tree: ast.AST):
152
+ self._tree = tree
153
+
154
+ def run(self) -> Generator[Tuple[int, int, str, Type[Any]], None, None]:
155
+ visitor = Visitor()
156
+
157
+ # Add parent
158
+ add_meta(self._tree)
159
+ visitor.visit(self._tree)
160
+
161
+ for line, col, msg in visitor.errors:
162
+ yield line, col, msg, type(self)
163
+
164
+
165
+ def add_meta(root: ast.AST, level: int = 0) -> None:
166
+ previous_sibling = None
167
+ for node in ast.iter_child_nodes(root):
168
+ if level == 0:
169
+ node.parent = root # type: ignore
170
+ node.previous_sibling = previous_sibling # type: ignore
171
+ node.next_sibling = None # type: ignore
172
+ if previous_sibling:
173
+ node.previous_sibling.next_sibling = node # type: ignore
174
+ previous_sibling = node
175
+ for child in ast.iter_child_nodes(node):
176
+ child.parent = node # type: ignore
177
+ add_meta(node, level=level + 1)
@@ -0,0 +1,7 @@
1
+ # Core Library
2
+ import ast
3
+
4
+ # ast.Constant in Python 3.8, ast.NameConstant in Python 3.6 and 3.7
5
+ BOOL_CONST_TYPES = (ast.Constant, ast.NameConstant)
6
+ AST_CONST_TYPES = (ast.Constant, ast.NameConstant, ast.Str, ast.Num)
7
+ STR_TYPES = (ast.Constant, ast.Str)
File without changes
@@ -0,0 +1,116 @@
1
+ # Core Library
2
+ import ast
3
+ from typing import List, Tuple
4
+
5
+ # First party
6
+ from flake8_simplify.utils import Assign, expression_uses_variable, to_source
7
+
8
+
9
+ def get_sim904(node: ast.Assign) -> List[Tuple[int, int, str]]:
10
+ """
11
+ Assign values to dictionary directly at initialization.
12
+
13
+ Example
14
+ -------
15
+ Code:
16
+ # Bad
17
+ a = { }
18
+ a['b] = 'c'
19
+
20
+ # Good
21
+ a = {'b': 'c'}
22
+ Bad AST:
23
+ [
24
+ Assign(
25
+ targets=[Name(id='a', ctx=Store())],
26
+ value=Dict(keys=[], values=[]),
27
+ type_comment=None,
28
+ ),
29
+ Assign(
30
+ targets=[
31
+ Subscript(
32
+ value=Name(id='a', ctx=Load()),
33
+ slice=Constant(value='b', kind=None),
34
+ ctx=Store(),
35
+ ),
36
+ ],
37
+ value=Constant(value='c', kind=None),
38
+ type_comment=None,
39
+ ),
40
+ ]
41
+ """
42
+ RULE = "SIM904 Initialize dictionary '{dict_name}' directly"
43
+ errors: List[Tuple[int, int, str]] = []
44
+ n2 = node.next_sibling # type: ignore
45
+ if not (
46
+ isinstance(node.value, ast.Dict)
47
+ and isinstance(n2, ast.Assign)
48
+ and len(n2.targets) == 1
49
+ and len(node.targets) == 1
50
+ and isinstance(n2.targets[0], ast.Subscript)
51
+ and isinstance(n2.targets[0].value, ast.Name)
52
+ and isinstance(node.targets[0], ast.Name)
53
+ and n2.targets[0].value.id == node.targets[0].id
54
+ ):
55
+ return errors
56
+
57
+ dict_name = to_source(node.targets[0])
58
+ # Capture cases where the assigned value uses another dictionary value
59
+ if expression_uses_variable(n2.value, dict_name):
60
+ return errors
61
+
62
+ errors.append(
63
+ (
64
+ node.lineno,
65
+ node.col_offset,
66
+ RULE.format(dict_name=dict_name),
67
+ )
68
+ )
69
+ return errors
70
+
71
+
72
+ def get_sim909(node: Assign) -> List[Tuple[int, int, str]]:
73
+ """
74
+ Avoid reflexive assignments
75
+
76
+ Example
77
+ -------
78
+ Code:
79
+ # Bad
80
+ foo = foo
81
+
82
+ # Good: Just remove them
83
+ Bad AST:
84
+ [
85
+ Assign(
86
+ targets=[Name(id='foo', ctx=Store())],
87
+ value=Name(id='foo', ctx=Load()),
88
+ type_comment=None,
89
+ ),
90
+ ]
91
+ """
92
+ RULE = "SIM909 Remove reflexive assignment '{code}'"
93
+ errors: List[Tuple[int, int, str]] = []
94
+
95
+ names = []
96
+ if isinstance(node.value, (ast.Name, ast.Subscript, ast.Tuple)):
97
+ names.append(to_source(node.value))
98
+ for target in node.targets:
99
+ names.append(to_source(target))
100
+
101
+ if len(names) == len(set(names)):
102
+ return errors
103
+
104
+ if isinstance(node.parent, ast.ClassDef):
105
+ return errors
106
+
107
+ code = to_source(node.orig)
108
+
109
+ errors.append(
110
+ (
111
+ node.lineno,
112
+ node.col_offset,
113
+ RULE.format(code=code),
114
+ )
115
+ )
116
+ return errors
@@ -0,0 +1,219 @@
1
+ # Core Library
2
+ import ast
3
+ from collections import defaultdict
4
+ from typing import Dict, List, Tuple
5
+
6
+ # First party
7
+ from flake8_simplify.constants import BOOL_CONST_TYPES
8
+ from flake8_simplify.utils import (
9
+ _get_duplicated_isinstance_call_by_node,
10
+ is_same_expression,
11
+ to_source,
12
+ )
13
+
14
+
15
+ def get_sim101(
16
+ node: ast.BoolOp,
17
+ ) -> List[Tuple[int, int, str]]:
18
+ """Get a positions where the duplicate isinstance problem appears."""
19
+ errors: List[Tuple[int, int, str]] = []
20
+ if not isinstance(node.op, ast.Or):
21
+ return errors
22
+
23
+ RULE = (
24
+ "SIM101 Multiple isinstance-calls which can be merged into a single "
25
+ "call for variable '{var}'"
26
+ )
27
+ for var in _get_duplicated_isinstance_call_by_node(node):
28
+ errors.append((node.lineno, node.col_offset, RULE.format(var=var)))
29
+ return errors
30
+
31
+
32
+ def get_sim109(node: ast.BoolOp) -> List[Tuple[int, int, str]]:
33
+ """
34
+ Check if multiple equalities with the same value are combined via "or".
35
+
36
+ BoolOp(
37
+ op=Or(),
38
+ values=[
39
+ Compare(
40
+ left=Name(id='a', ctx=Load()),
41
+ ops=[Eq()],
42
+ comparators=[Name(id='b', ctx=Load())],
43
+ ),
44
+ Compare(
45
+ left=Name(id='a', ctx=Load()),
46
+ ops=[Eq()],
47
+ comparators=[Name(id='c', ctx=Load())],
48
+ ),
49
+ ],
50
+ )
51
+ """
52
+ errors: List[Tuple[int, int, str]] = []
53
+ if not isinstance(node.op, ast.Or):
54
+ return errors
55
+ equalities = [
56
+ value
57
+ for value in node.values
58
+ if isinstance(value, ast.Compare)
59
+ and len(value.ops) == 1
60
+ and isinstance(value.ops[0], ast.Eq)
61
+ ]
62
+ id2vals: Dict[str, List[ast.Name]] = defaultdict(list)
63
+ for eq in equalities:
64
+ if (
65
+ isinstance(eq.left, ast.Name)
66
+ and len(eq.comparators) == 1
67
+ and isinstance(eq.comparators[0], ast.Name)
68
+ ):
69
+ id2vals[eq.left.id].append(eq.comparators[0])
70
+ RULE = "SIM109 Use '{value} in {values}' instead of '{or_op}'"
71
+ for value, values in id2vals.items():
72
+ if len(values) == 1:
73
+ continue
74
+ errors.append(
75
+ (
76
+ node.lineno,
77
+ node.col_offset,
78
+ RULE.format(
79
+ or_op=to_source(node),
80
+ value=value,
81
+ values=f"({to_source(ast.Tuple(elts=values))})",
82
+ ),
83
+ )
84
+ )
85
+ return errors
86
+
87
+
88
+ def get_sim220(node: ast.BoolOp) -> List[Tuple[int, int, str]]:
89
+ """
90
+ Get a list of all calls of the type "a and not a".
91
+
92
+ BoolOp(
93
+ op=And(),
94
+ values=[
95
+ Name(id='a', ctx=Load()),
96
+ UnaryOp(
97
+ op=Not(),
98
+ operand=Name(id='a', ctx=Load()),
99
+ ),
100
+ ],
101
+ )
102
+ """
103
+ errors: List[Tuple[int, int, str]] = []
104
+ if not (isinstance(node.op, ast.And) and len(node.values) >= 2):
105
+ return errors
106
+ # We have a boolean And. Let's make sure there is two times the same
107
+ # expression, but once with a "not"
108
+ negated_expressions = []
109
+ non_negated_expressions = []
110
+ for exp in node.values:
111
+ if isinstance(exp, ast.UnaryOp) and isinstance(exp.op, ast.Not):
112
+ negated_expressions.append(exp.operand)
113
+ else:
114
+ non_negated_expressions.append(exp)
115
+ if len(negated_expressions) == 0:
116
+ return errors
117
+
118
+ RULE = "SIM220 Use 'False' instead of '{a} and not {a}'"
119
+ for negated_expression in negated_expressions:
120
+ for non_negated_expression in non_negated_expressions:
121
+ if is_same_expression(negated_expression, non_negated_expression):
122
+ a = to_source(negated_expression)
123
+ errors.append((node.lineno, node.col_offset, RULE.format(a=a)))
124
+ return errors
125
+ return errors
126
+
127
+
128
+ def get_sim221(node: ast.BoolOp) -> List[Tuple[int, int, str]]:
129
+ """
130
+ Get a list of all calls of the type "a or not a".
131
+
132
+ BoolOp(
133
+ op=Or(),
134
+ values=[
135
+ Name(id='a', ctx=Load()),
136
+ UnaryOp(
137
+ op=Not(),
138
+ operand=Name(id='a', ctx=Load()),
139
+ ),
140
+ ],
141
+ )
142
+ """
143
+ errors: List[Tuple[int, int, str]] = []
144
+ if not (isinstance(node.op, ast.Or) and len(node.values) >= 2):
145
+ return errors
146
+ # We have a boolean OR. Let's make sure there is two times the same
147
+ # expression, but once with a "not"
148
+ negated_expressions = []
149
+ non_negated_expressions = []
150
+ for exp in node.values:
151
+ if isinstance(exp, ast.UnaryOp) and isinstance(exp.op, ast.Not):
152
+ negated_expressions.append(exp.operand)
153
+ else:
154
+ non_negated_expressions.append(exp)
155
+ if len(negated_expressions) == 0:
156
+ return errors
157
+
158
+ RULE = "SIM221 Use 'True' instead of '{a} or not {a}'"
159
+ for negated_expression in negated_expressions:
160
+ for non_negated_expression in non_negated_expressions:
161
+ if is_same_expression(negated_expression, non_negated_expression):
162
+ a = to_source(negated_expression)
163
+ errors.append((node.lineno, node.col_offset, RULE.format(a=a)))
164
+ return errors
165
+ return errors
166
+
167
+
168
+ def get_sim222(node: ast.BoolOp) -> List[Tuple[int, int, str]]:
169
+ """
170
+ Get a list of all calls of the type "... or True".
171
+
172
+ BoolOp(
173
+ op=Or(),
174
+ values=[
175
+ Name(id='a', ctx=Load()),
176
+ UnaryOp(
177
+ op=Not(),
178
+ operand=Name(id='a', ctx=Load()),
179
+ ),
180
+ ],
181
+ )
182
+ """
183
+ errors: List[Tuple[int, int, str]] = []
184
+ if not (isinstance(node.op, ast.Or)):
185
+ return errors
186
+
187
+ RULE = "SIM222 Use 'True' instead of '... or True'"
188
+ for exp in node.values:
189
+ if isinstance(exp, BOOL_CONST_TYPES) and exp.value is True:
190
+ errors.append((node.lineno, node.col_offset, RULE))
191
+ return errors
192
+ return errors
193
+
194
+
195
+ def get_sim223(node: ast.BoolOp) -> List[Tuple[int, int, str]]:
196
+ """
197
+ Get a list of all calls of the type "... and False".
198
+
199
+ BoolOp(
200
+ op=And(),
201
+ values=[
202
+ Name(id='a', ctx=Load()),
203
+ UnaryOp(
204
+ op=Not(),
205
+ operand=Name(id='a', ctx=Load()),
206
+ ),
207
+ ],
208
+ )
209
+ """
210
+ errors: List[Tuple[int, int, str]] = []
211
+ if not (isinstance(node.op, ast.And)):
212
+ return errors
213
+
214
+ RULE = "SIM223 Use 'False' instead of '... and False'"
215
+ for exp in node.values:
216
+ if isinstance(exp, BOOL_CONST_TYPES) and exp.value is False:
217
+ errors.append((node.lineno, node.col_offset, RULE))
218
+ return errors
219
+ return errors