codelaw-rules-python 0.2.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.
- codelaw_rules_python/LICENSE +23 -0
- codelaw_rules_python/NOTICE +14 -0
- codelaw_rules_python/README.md +189 -0
- codelaw_rules_python/codelaw-pack.toml +6 -0
- codelaw_rules_python/corpus.toml +103 -0
- codelaw_rules_python/data/python/stdlib.json +331 -0
- codelaw_rules_python/presets/py/conventions.toml +5 -0
- codelaw_rules_python/presets/py/project.toml +4 -0
- codelaw_rules_python/presets/py/recommended.toml +99 -0
- codelaw_rules_python/rules/graph/no-unused-exports.md +69 -0
- codelaw_rules_python/rules/py/abstract-base-class-without-abstract-method.md +50 -0
- codelaw_rules_python/rules/py/all-sorted.cases.md +74 -0
- codelaw_rules_python/rules/py/all-sorted.md +33 -0
- codelaw_rules_python/rules/py/ambiguous-class-name.md +41 -0
- codelaw_rules_python/rules/py/ambiguous-function-name.md +41 -0
- codelaw_rules_python/rules/py/ambiguous-variable-name.cases.md +47 -0
- codelaw_rules_python/rules/py/ambiguous-variable-name.md +39 -0
- codelaw_rules_python/rules/py/assert-false.md +34 -0
- codelaw_rules_python/rules/py/assert-raises-exception.md +51 -0
- codelaw_rules_python/rules/py/assert-tuple.md +38 -0
- codelaw_rules_python/rules/py/assignment-to-os-environ.md +43 -0
- codelaw_rules_python/rules/py/batched-without-explicit-strict.md +52 -0
- codelaw_rules_python/rules/py/break-outside-loop.md +41 -0
- codelaw_rules_python/rules/py/cached-instance-method.md +55 -0
- codelaw_rules_python/rules/py/continue-outside-loop.md +41 -0
- codelaw_rules_python/rules/py/default-except-not-last.md +48 -0
- codelaw_rules_python/rules/py/duplicate-dict-key-variable.md +39 -0
- codelaw_rules_python/rules/py/duplicate-dict-key.md +39 -0
- codelaw_rules_python/rules/py/duplicate-handler-exception.md +43 -0
- codelaw_rules_python/rules/py/duplicate-set-value.md +33 -0
- codelaw_rules_python/rules/py/duplicate-try-block-exception.md +47 -0
- codelaw_rules_python/rules/py/empty-method-without-abstract-decorator.md +55 -0
- codelaw_rules_python/rules/py/except-with-empty-tuple.md +44 -0
- codelaw_rules_python/rules/py/except-with-non-exception-classes.md +44 -0
- codelaw_rules_python/rules/py/f-string-docstring.md +40 -0
- codelaw_rules_python/rules/py/f-string-missing-placeholders.cases.md +49 -0
- codelaw_rules_python/rules/py/f-string-missing-placeholders.md +33 -0
- codelaw_rules_python/rules/py/format-invalid.md +38 -0
- codelaw_rules_python/rules/py/format-missing-argument.md +38 -0
- codelaw_rules_python/rules/py/format-mixed-numbering.md +38 -0
- codelaw_rules_python/rules/py/format-unused-named-argument.md +37 -0
- codelaw_rules_python/rules/py/format-unused-positional-argument.md +37 -0
- codelaw_rules_python/rules/py/forward-annotation-syntax-error.md +44 -0
- codelaw_rules_python/rules/py/function-call-in-default-argument.md +51 -0
- codelaw_rules_python/rules/py/function-uses-loop-variable.cases.md +57 -0
- codelaw_rules_python/rules/py/function-uses-loop-variable.md +42 -0
- codelaw_rules_python/rules/py/getattr-with-constant.cases.md +31 -0
- codelaw_rules_python/rules/py/getattr-with-constant.md +33 -0
- codelaw_rules_python/rules/py/if-tuple.md +40 -0
- codelaw_rules_python/rules/py/import-shadowed-by-loop-var.md +45 -0
- codelaw_rules_python/rules/py/invalid-print-syntax.md +42 -0
- codelaw_rules_python/rules/py/is-literal.md +36 -0
- codelaw_rules_python/rules/py/jump-statement-in-finally.cases.md +15 -0
- codelaw_rules_python/rules/py/jump-statement-in-finally.md +47 -0
- codelaw_rules_python/rules/py/lambda-assignment.cases.md +48 -0
- codelaw_rules_python/rules/py/lambda-assignment.md +35 -0
- codelaw_rules_python/rules/py/late-future-import.md +40 -0
- codelaw_rules_python/rules/py/loop-variable-overrides-iterator.md +41 -0
- codelaw_rules_python/rules/py/map-without-explicit-strict.md +46 -0
- codelaw_rules_python/rules/py/module-import-not-at-top.cases.md +64 -0
- codelaw_rules_python/rules/py/module-import-not-at-top.md +46 -0
- codelaw_rules_python/rules/py/multiple-imports-on-one-line.md +38 -0
- codelaw_rules_python/rules/py/multiple-starred-expressions.md +38 -0
- codelaw_rules_python/rules/py/mutable-contextvar-default.md +42 -0
- codelaw_rules_python/rules/py/nested-star-import.md +42 -0
- codelaw_rules_python/rules/py/no-bare-except.cases.md +37 -0
- codelaw_rules_python/rules/py/no-bare-except.md +44 -0
- codelaw_rules_python/rules/py/no-explicit-stacklevel.md +38 -0
- codelaw_rules_python/rules/py/no-mutable-default.cases.md +58 -0
- codelaw_rules_python/rules/py/no-mutable-default.md +44 -0
- codelaw_rules_python/rules/py/no-print.cases.md +40 -0
- codelaw_rules_python/rules/py/no-print.md +42 -0
- codelaw_rules_python/rules/py/no-star-import.cases.md +11 -0
- codelaw_rules_python/rules/py/no-star-import.md +47 -0
- codelaw_rules_python/rules/py/no-tunable-literals.cases.md +54 -0
- codelaw_rules_python/rules/py/no-tunable-literals.md +58 -0
- codelaw_rules_python/rules/py/none-comparison.md +36 -0
- codelaw_rules_python/rules/py/not-in-test.md +35 -0
- codelaw_rules_python/rules/py/not-is-test.md +35 -0
- codelaw_rules_python/rules/py/percent-format-count-mismatch.md +38 -0
- codelaw_rules_python/rules/py/percent-format-expected-mapping.md +38 -0
- codelaw_rules_python/rules/py/percent-format-expected-sequence.md +38 -0
- codelaw_rules_python/rules/py/percent-format-invalid.md +38 -0
- codelaw_rules_python/rules/py/percent-format-missing-argument.md +38 -0
- codelaw_rules_python/rules/py/percent-format-mixed-placeholders.md +38 -0
- codelaw_rules_python/rules/py/percent-format-star-requires-sequence.md +38 -0
- codelaw_rules_python/rules/py/percent-format-unsupported-character.md +38 -0
- codelaw_rules_python/rules/py/percent-format-unused-named-argument.md +37 -0
- codelaw_rules_python/rules/py/raise-literal.md +38 -0
- codelaw_rules_python/rules/py/raise-not-implemented.md +38 -0
- codelaw_rules_python/rules/py/raise-without-from-inside-except.cases.md +46 -0
- codelaw_rules_python/rules/py/raise-without-from-inside-except.md +45 -0
- codelaw_rules_python/rules/py/re-sub-positional-args.md +42 -0
- codelaw_rules_python/rules/py/redefined-while-unused.cases.md +186 -0
- codelaw_rules_python/rules/py/redefined-while-unused.md +53 -0
- codelaw_rules_python/rules/py/redundant-tuple-in-exception-handler.md +39 -0
- codelaw_rules_python/rules/py/return-outside-function.md +41 -0
- codelaw_rules_python/rules/py/reuse-of-groupby-generator.cases.md +40 -0
- codelaw_rules_python/rules/py/reuse-of-groupby-generator.md +46 -0
- codelaw_rules_python/rules/py/setattr-with-constant.md +33 -0
- codelaw_rules_python/rules/py/star-arg-unpacking-after-keyword-arg.md +38 -0
- codelaw_rules_python/rules/py/star-import-usage.cases.md +35 -0
- codelaw_rules_python/rules/py/star-import-usage.md +43 -0
- codelaw_rules_python/rules/py/static-key-dict-comprehension.md +38 -0
- codelaw_rules_python/rules/py/strip-with-multi-characters.md +39 -0
- codelaw_rules_python/rules/py/too-many-expressions-in-star-assignment.md +38 -0
- codelaw_rules_python/rules/py/true-false-comparison.cases.md +33 -0
- codelaw_rules_python/rules/py/true-false-comparison.md +36 -0
- codelaw_rules_python/rules/py/type-comparison.md +41 -0
- codelaw_rules_python/rules/py/unary-prefix-increment.md +40 -0
- codelaw_rules_python/rules/py/undefined-export.md +55 -0
- codelaw_rules_python/rules/py/undefined-future-feature.md +39 -0
- codelaw_rules_python/rules/py/undefined-local.cases.md +80 -0
- codelaw_rules_python/rules/py/undefined-local.md +48 -0
- codelaw_rules_python/rules/py/undefined-name.cases.md +132 -0
- codelaw_rules_python/rules/py/undefined-name.md +42 -0
- codelaw_rules_python/rules/py/unintentional-type-annotation.cases.md +13 -0
- codelaw_rules_python/rules/py/unintentional-type-annotation.md +38 -0
- codelaw_rules_python/rules/py/unreliable-callable-check.md +36 -0
- codelaw_rules_python/rules/py/unsorted-imports.cases.md +159 -0
- codelaw_rules_python/rules/py/unsorted-imports.md +60 -0
- codelaw_rules_python/rules/py/unused-annotation.md +36 -0
- codelaw_rules_python/rules/py/unused-import.cases.md +289 -0
- codelaw_rules_python/rules/py/unused-import.md +45 -0
- codelaw_rules_python/rules/py/unused-loop-control-variable.cases.md +30 -0
- codelaw_rules_python/rules/py/unused-loop-control-variable.md +36 -0
- codelaw_rules_python/rules/py/unused-variable.cases.md +169 -0
- codelaw_rules_python/rules/py/unused-variable.md +38 -0
- codelaw_rules_python/rules/py/useless-comparison.md +41 -0
- codelaw_rules_python/rules/py/useless-contextlib-suppress.md +44 -0
- codelaw_rules_python/rules/py/useless-expression.cases.md +57 -0
- codelaw_rules_python/rules/py/useless-expression.md +45 -0
- codelaw_rules_python/rules/py/yield-outside-function.md +44 -0
- codelaw_rules_python/rules/py/zip-without-explicit-strict.cases.md +26 -0
- codelaw_rules_python/rules/py/zip-without-explicit-strict.md +46 -0
- codelaw_rules_python-0.2.0.dist-info/METADATA +199 -0
- codelaw_rules_python-0.2.0.dist-info/RECORD +139 -0
- codelaw_rules_python-0.2.0.dist-info/WHEEL +4 -0
- codelaw_rules_python-0.2.0.dist-info/licenses/LICENSE +23 -0
|
@@ -0,0 +1,289 @@
|
|
|
1
|
+
# py/unused-import — cases
|
|
2
|
+
|
|
3
|
+
### case: every import of a block
|
|
4
|
+
|
|
5
|
+
```py
|
|
6
|
+
from typing import TYPE_CHECKING
|
|
7
|
+
|
|
8
|
+
if TYPE_CHECKING:
|
|
9
|
+
import a
|
|
10
|
+
import b
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
```expect
|
|
14
|
+
4:12 py/unused-import
|
|
15
|
+
5:12 py/unused-import
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
```fixed
|
|
19
|
+
from typing import TYPE_CHECKING
|
|
20
|
+
|
|
21
|
+
if TYPE_CHECKING:
|
|
22
|
+
pass
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
### case: names nested in an annotation with a default
|
|
26
|
+
|
|
27
|
+
```py
|
|
28
|
+
from typing import List, Optional
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def main(args: Optional[List[str]] = None) -> int:
|
|
32
|
+
return 0
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
```expect
|
|
36
|
+
(none)
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
### case: annotated assignment
|
|
40
|
+
|
|
41
|
+
```py
|
|
42
|
+
from typing import Any, Set
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
def f():
|
|
46
|
+
seen: Set[Any] = set()
|
|
47
|
+
return seen
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
```expect
|
|
51
|
+
(none)
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
### case: string inside a typing generic
|
|
55
|
+
|
|
56
|
+
```py
|
|
57
|
+
from typing import TYPE_CHECKING, Dict
|
|
58
|
+
|
|
59
|
+
if TYPE_CHECKING:
|
|
60
|
+
from .names import NormalizedName
|
|
61
|
+
|
|
62
|
+
PackageSet = Dict["NormalizedName", int]
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
```expect
|
|
66
|
+
(none)
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
### case: type comments are not read, as in Ruff
|
|
70
|
+
|
|
71
|
+
```py
|
|
72
|
+
from types import TracebackType
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
def f(a):
|
|
76
|
+
# type: (TracebackType) -> None
|
|
77
|
+
pass
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
```expect
|
|
81
|
+
1:19 py/unused-import
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
### case: listed in __all__
|
|
85
|
+
|
|
86
|
+
```py
|
|
87
|
+
from .core import load
|
|
88
|
+
|
|
89
|
+
__all__ = ["load"]
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
```expect
|
|
93
|
+
(none)
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
### case: redundant alias re-exports
|
|
97
|
+
|
|
98
|
+
```py
|
|
99
|
+
from .core import load as load
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
```expect
|
|
103
|
+
(none)
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
### case: used only in a string annotation
|
|
107
|
+
|
|
108
|
+
```py
|
|
109
|
+
from typing import TYPE_CHECKING
|
|
110
|
+
|
|
111
|
+
if TYPE_CHECKING:
|
|
112
|
+
from .models import User
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
def name(user: "User") -> str:
|
|
116
|
+
return ""
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
```expect
|
|
120
|
+
(none)
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
### case: future imports are used by being there
|
|
124
|
+
|
|
125
|
+
```py
|
|
126
|
+
from __future__ import annotations
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
```expect
|
|
130
|
+
(none)
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
### case: one of several names
|
|
134
|
+
|
|
135
|
+
```py
|
|
136
|
+
from os.path import join, split
|
|
137
|
+
|
|
138
|
+
print(join("a", "b"))
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
```expect
|
|
142
|
+
1:27 py/unused-import
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
```fixed
|
|
146
|
+
from os.path import join
|
|
147
|
+
|
|
148
|
+
print(join("a", "b"))
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
### case: import with a fallback in except
|
|
152
|
+
|
|
153
|
+
```py
|
|
154
|
+
try:
|
|
155
|
+
import ujson
|
|
156
|
+
except ImportError:
|
|
157
|
+
ujson = None
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
```expect
|
|
161
|
+
(none)
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
### case: last import of a name wins
|
|
165
|
+
|
|
166
|
+
```py
|
|
167
|
+
import re
|
|
168
|
+
import re
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
```expect
|
|
172
|
+
2:8 py/unused-import
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
### case: function-level import
|
|
176
|
+
|
|
177
|
+
```py
|
|
178
|
+
def f():
|
|
179
|
+
import json
|
|
180
|
+
return 1
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
```expect
|
|
184
|
+
2:12 py/unused-import
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
```fixed
|
|
188
|
+
def f():
|
|
189
|
+
return 1
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
### case: used inside a function
|
|
193
|
+
|
|
194
|
+
```py
|
|
195
|
+
import json
|
|
196
|
+
|
|
197
|
+
|
|
198
|
+
def f():
|
|
199
|
+
return json.dumps({})
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
```expect
|
|
203
|
+
(none)
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
### case: importing __all__ defines the exports
|
|
207
|
+
|
|
208
|
+
```py
|
|
209
|
+
from io import __all__
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
```expect
|
|
213
|
+
(none)
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
### case: class-level import is an attribute
|
|
217
|
+
|
|
218
|
+
```py
|
|
219
|
+
class C:
|
|
220
|
+
from os.path import join
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
```expect
|
|
224
|
+
(none)
|
|
225
|
+
```
|
|
226
|
+
|
|
227
|
+
### case: submodule loaded for an aliased package
|
|
228
|
+
|
|
229
|
+
```py
|
|
230
|
+
import multiprocessing as mp
|
|
231
|
+
import multiprocessing.connection
|
|
232
|
+
|
|
233
|
+
mp.connection.wait([])
|
|
234
|
+
```
|
|
235
|
+
|
|
236
|
+
```expect
|
|
237
|
+
(none)
|
|
238
|
+
```
|
|
239
|
+
|
|
240
|
+
### case: __all__ built from another module's __all__
|
|
241
|
+
|
|
242
|
+
```py
|
|
243
|
+
from . import sub
|
|
244
|
+
from .sub import *
|
|
245
|
+
from .core import load
|
|
246
|
+
|
|
247
|
+
__all__ = ["sub"] + sub.__all__
|
|
248
|
+
```
|
|
249
|
+
|
|
250
|
+
```expect
|
|
251
|
+
(none)
|
|
252
|
+
```
|
|
253
|
+
|
|
254
|
+
### case: __all__ extended and appended
|
|
255
|
+
|
|
256
|
+
```py
|
|
257
|
+
from .core import load, save, drop
|
|
258
|
+
|
|
259
|
+
__all__ = []
|
|
260
|
+
__all__.extend(["load"])
|
|
261
|
+
__all__.append("save")
|
|
262
|
+
```
|
|
263
|
+
|
|
264
|
+
```expect
|
|
265
|
+
1:31 py/unused-import
|
|
266
|
+
```
|
|
267
|
+
|
|
268
|
+
```fixed
|
|
269
|
+
from .core import load, save
|
|
270
|
+
|
|
271
|
+
__all__ = []
|
|
272
|
+
__all__.extend(["load"])
|
|
273
|
+
__all__.append("save")
|
|
274
|
+
```
|
|
275
|
+
|
|
276
|
+
### case: a TypeAlias string uses a TYPE_CHECKING import
|
|
277
|
+
|
|
278
|
+
```py
|
|
279
|
+
from typing import TYPE_CHECKING, TypeAlias
|
|
280
|
+
|
|
281
|
+
if TYPE_CHECKING:
|
|
282
|
+
from decimal import Decimal
|
|
283
|
+
|
|
284
|
+
Number: TypeAlias = "Decimal | int"
|
|
285
|
+
```
|
|
286
|
+
|
|
287
|
+
```expect
|
|
288
|
+
(none)
|
|
289
|
+
```
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
---
|
|
2
|
+
id: py/unused-import
|
|
3
|
+
title: "Every imported name is used"
|
|
4
|
+
languages: [python]
|
|
5
|
+
fix: suggest
|
|
6
|
+
fix_reason: "an import can run code when it loads, and removing it drops that side effect"
|
|
7
|
+
stability: preview
|
|
8
|
+
ported_from: [ruff/F401]
|
|
9
|
+
since: 0.1.0
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
Each name an `import` binds is read somewhere in the module, listed in `__all__`, or re-exported with `import x as x`.
|
|
13
|
+
|
|
14
|
+
**Why:** An unused import is dead weight: it slows start-up, can hide a missing dependency, and makes readers look for a use that isn't there.
|
|
15
|
+
|
|
16
|
+
```codelaw
|
|
17
|
+
native: py/unused-import
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
## Examples
|
|
21
|
+
|
|
22
|
+
### Python
|
|
23
|
+
|
|
24
|
+
#### Don't
|
|
25
|
+
|
|
26
|
+
```py
|
|
27
|
+
import os
|
|
28
|
+
import sys
|
|
29
|
+
|
|
30
|
+
print(sys.argv)
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
#### Do
|
|
34
|
+
|
|
35
|
+
```py
|
|
36
|
+
import sys
|
|
37
|
+
|
|
38
|
+
print(sys.argv)
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
## Differences from the source
|
|
42
|
+
|
|
43
|
+
- An import counts as used when any read of its name exists, wherever it is; Ruff tracks each binding separately, so `import os` read and then imported again unused is reported by Ruff only.
|
|
44
|
+
- Ruff's special handling of `__init__.py` (suggesting `__all__` or a redundant alias instead of removal) is not reproduced; the fix is `suggest` everywhere.
|
|
45
|
+
- Imports inside a `try` body (the "is this installed?" pattern) are reported without an edit.
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
# py/unused-loop-control-variable — cases
|
|
2
|
+
|
|
3
|
+
### case: one of two targets
|
|
4
|
+
|
|
5
|
+
```py
|
|
6
|
+
for key, value in pairs:
|
|
7
|
+
print(value)
|
|
8
|
+
```
|
|
9
|
+
|
|
10
|
+
```expect
|
|
11
|
+
1:5 py/unused-loop-control-variable
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
```fixed
|
|
15
|
+
for _key, value in pairs:
|
|
16
|
+
print(value)
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
### case: used after the loop: no rename
|
|
20
|
+
|
|
21
|
+
```py
|
|
22
|
+
def f(xs):
|
|
23
|
+
for x in xs:
|
|
24
|
+
pass
|
|
25
|
+
return x
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
```expect
|
|
29
|
+
2:9 py/unused-loop-control-variable
|
|
30
|
+
```
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
---
|
|
2
|
+
id: py/unused-loop-control-variable
|
|
3
|
+
title: "Every loop variable is used in the loop"
|
|
4
|
+
languages: [python]
|
|
5
|
+
fix: suggest
|
|
6
|
+
fix_reason: "the rename is offered only when the name isn't used anywhere else; otherwise a person decides"
|
|
7
|
+
stability: preview
|
|
8
|
+
ported_from: [ruff/B007]
|
|
9
|
+
since: 0.1.0
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
A `for` loop's target names are read in the loop body, or start with `_` to say they aren't needed.
|
|
13
|
+
|
|
14
|
+
**Why:** An unused loop variable often means the body uses the wrong name.
|
|
15
|
+
|
|
16
|
+
```codelaw
|
|
17
|
+
native: py/unused-loop-control-variable
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
## Examples
|
|
21
|
+
|
|
22
|
+
### Python
|
|
23
|
+
|
|
24
|
+
#### Don't
|
|
25
|
+
|
|
26
|
+
```py
|
|
27
|
+
for i in range(3):
|
|
28
|
+
retry()
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
#### Do
|
|
32
|
+
|
|
33
|
+
```py
|
|
34
|
+
for _i in range(3):
|
|
35
|
+
retry()
|
|
36
|
+
```
|
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
# py/unused-variable — cases
|
|
2
|
+
|
|
3
|
+
### case: read in a nested format spec
|
|
4
|
+
|
|
5
|
+
```py
|
|
6
|
+
def f(x):
|
|
7
|
+
width = 3
|
|
8
|
+
return f"{x:{width}d}"
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
```expect
|
|
12
|
+
(none)
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
### case: underscore names are deliberate
|
|
16
|
+
|
|
17
|
+
```py
|
|
18
|
+
def f():
|
|
19
|
+
_ = compute()
|
|
20
|
+
_unused = 1
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
```expect
|
|
24
|
+
(none)
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
### case: locals() reads everything
|
|
28
|
+
|
|
29
|
+
```py
|
|
30
|
+
def f():
|
|
31
|
+
x = 1
|
|
32
|
+
return locals()
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
```expect
|
|
36
|
+
(none)
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
### case: nonlocal use
|
|
40
|
+
|
|
41
|
+
```py
|
|
42
|
+
def f():
|
|
43
|
+
x = 1
|
|
44
|
+
def g():
|
|
45
|
+
nonlocal x
|
|
46
|
+
x = 2
|
|
47
|
+
return g
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
```expect
|
|
51
|
+
(none)
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
### case: module level is not checked
|
|
55
|
+
|
|
56
|
+
```py
|
|
57
|
+
x = 1
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
```expect
|
|
61
|
+
(none)
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
### case: only the last assignment is reported
|
|
65
|
+
|
|
66
|
+
```py
|
|
67
|
+
def f():
|
|
68
|
+
x = 1
|
|
69
|
+
x = 2
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
```expect
|
|
73
|
+
3:5 py/unused-variable
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
```fixed
|
|
77
|
+
def f():
|
|
78
|
+
pass
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
### case: unused exception name
|
|
82
|
+
|
|
83
|
+
```py
|
|
84
|
+
def f():
|
|
85
|
+
try:
|
|
86
|
+
pass
|
|
87
|
+
except ValueError as err:
|
|
88
|
+
pass
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
```expect
|
|
92
|
+
4:26 py/unused-variable
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
```fixed
|
|
96
|
+
def f():
|
|
97
|
+
try:
|
|
98
|
+
pass
|
|
99
|
+
except ValueError:
|
|
100
|
+
pass
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
### case: loop variables are B007's business
|
|
104
|
+
|
|
105
|
+
```py
|
|
106
|
+
def f():
|
|
107
|
+
for i in range(3):
|
|
108
|
+
pass
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
```expect
|
|
112
|
+
(none)
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
### case: augmented assignment reads
|
|
116
|
+
|
|
117
|
+
```py
|
|
118
|
+
def f():
|
|
119
|
+
t = 1
|
|
120
|
+
t += 1
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
```expect
|
|
124
|
+
(none)
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
### case: unpacking a call result is not reported
|
|
128
|
+
|
|
129
|
+
```py
|
|
130
|
+
def f(s):
|
|
131
|
+
name, j = s.split()
|
|
132
|
+
return j
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
```expect
|
|
136
|
+
(none)
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
### case: unpacking a tuple literal is
|
|
140
|
+
|
|
141
|
+
```py
|
|
142
|
+
def f():
|
|
143
|
+
head, tail = 1, 2
|
|
144
|
+
return tail
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
```expect
|
|
148
|
+
2:5 py/unused-variable
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
### case: module-level except name
|
|
152
|
+
|
|
153
|
+
```py
|
|
154
|
+
try:
|
|
155
|
+
pass
|
|
156
|
+
except ValueError as err:
|
|
157
|
+
pass
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
```expect
|
|
161
|
+
3:22 py/unused-variable
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
```fixed
|
|
165
|
+
try:
|
|
166
|
+
pass
|
|
167
|
+
except ValueError:
|
|
168
|
+
pass
|
|
169
|
+
```
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
---
|
|
2
|
+
id: py/unused-variable
|
|
3
|
+
title: "Every local variable is used"
|
|
4
|
+
languages: [python]
|
|
5
|
+
fix: suggest
|
|
6
|
+
fix_reason: "dropping the assignment of a plain value is safe, but the right edit for unpacking or `with` targets needs a person"
|
|
7
|
+
stability: preview
|
|
8
|
+
ported_from: [ruff/F841]
|
|
9
|
+
since: 0.1.0
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
A variable assigned in a function (including `with … as` and `except … as` targets) is read somewhere in that function.
|
|
13
|
+
|
|
14
|
+
**Why:** An unused local is dead code, or a sign the value was meant to be used and isn't.
|
|
15
|
+
|
|
16
|
+
```codelaw
|
|
17
|
+
native: py/unused-variable
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
## Examples
|
|
21
|
+
|
|
22
|
+
### Python
|
|
23
|
+
|
|
24
|
+
#### Don't
|
|
25
|
+
|
|
26
|
+
```py
|
|
27
|
+
def handle(request):
|
|
28
|
+
result = compute(request)
|
|
29
|
+
return None
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
#### Do
|
|
33
|
+
|
|
34
|
+
```py
|
|
35
|
+
def handle(request):
|
|
36
|
+
compute(request)
|
|
37
|
+
return None
|
|
38
|
+
```
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
---
|
|
2
|
+
id: py/useless-comparison
|
|
3
|
+
title: "No comparison whose result is thrown away"
|
|
4
|
+
languages: [python]
|
|
5
|
+
fix: manual
|
|
6
|
+
fix_reason: "whether an assignment or an assertion was meant needs a person"
|
|
7
|
+
stability: preview
|
|
8
|
+
ported_from: [ruff/B015]
|
|
9
|
+
since: 0.1.0
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
A comparison isn't written as a statement on its own.
|
|
13
|
+
|
|
14
|
+
**Why:** Its result is discarded; usually `=` or `assert` was meant.
|
|
15
|
+
|
|
16
|
+
```codelaw
|
|
17
|
+
native: py/useless-comparison
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
## How to fix
|
|
21
|
+
|
|
22
|
+
1. Use `=` if an assignment was meant, or `assert` if a check was.
|
|
23
|
+
2. Otherwise remove the line.
|
|
24
|
+
|
|
25
|
+
## Examples
|
|
26
|
+
|
|
27
|
+
### Python
|
|
28
|
+
|
|
29
|
+
#### Don't
|
|
30
|
+
|
|
31
|
+
```py
|
|
32
|
+
def reset(state):
|
|
33
|
+
state.count == 0
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
#### Do
|
|
37
|
+
|
|
38
|
+
```py
|
|
39
|
+
def reset(state):
|
|
40
|
+
state.count = 0
|
|
41
|
+
```
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
---
|
|
2
|
+
id: py/useless-contextlib-suppress
|
|
3
|
+
title: "`contextlib.suppress()` names an exception"
|
|
4
|
+
languages: [python]
|
|
5
|
+
fix: manual
|
|
6
|
+
fix_reason: "the exceptions to suppress are the author's choice"
|
|
7
|
+
stability: preview
|
|
8
|
+
ported_from: [ruff/B022]
|
|
9
|
+
since: 0.1.0
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
`contextlib.suppress()` is called with at least one exception class.
|
|
13
|
+
|
|
14
|
+
**Why:** With no arguments it suppresses nothing, so the `with` block is misleading.
|
|
15
|
+
|
|
16
|
+
```codelaw
|
|
17
|
+
native: py/useless-contextlib-suppress
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
## How to fix
|
|
21
|
+
|
|
22
|
+
1. Pass the exceptions to suppress, or remove the `with` statement.
|
|
23
|
+
|
|
24
|
+
## Examples
|
|
25
|
+
|
|
26
|
+
### Python
|
|
27
|
+
|
|
28
|
+
#### Don't
|
|
29
|
+
|
|
30
|
+
```py
|
|
31
|
+
import contextlib
|
|
32
|
+
|
|
33
|
+
with contextlib.suppress():
|
|
34
|
+
remove("cache")
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
#### Do
|
|
38
|
+
|
|
39
|
+
```py
|
|
40
|
+
import contextlib
|
|
41
|
+
|
|
42
|
+
with contextlib.suppress(FileNotFoundError):
|
|
43
|
+
remove("cache")
|
|
44
|
+
```
|