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,37 @@
|
|
|
1
|
+
---
|
|
2
|
+
id: py/percent-format-unused-named-argument
|
|
3
|
+
title: "Every key given to `%` is used"
|
|
4
|
+
languages: [python]
|
|
5
|
+
fix: auto
|
|
6
|
+
stability: preview
|
|
7
|
+
ported_from: [ruff/F504]
|
|
8
|
+
since: 0.1.0
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
A dict literal formatted into a `%` string has no keys the string never names.
|
|
12
|
+
|
|
13
|
+
**Why:** An unused key is usually a misspelled placeholder.
|
|
14
|
+
|
|
15
|
+
```codelaw
|
|
16
|
+
native: py/percent-format-unused-named-argument
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
## Examples
|
|
20
|
+
|
|
21
|
+
### Python
|
|
22
|
+
|
|
23
|
+
#### Don't
|
|
24
|
+
|
|
25
|
+
```py
|
|
26
|
+
message = "%(name)s" % {"name": "Ada", "age": 36}
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
#### Do
|
|
30
|
+
|
|
31
|
+
```py
|
|
32
|
+
message = "%(name)s" % {"name": "Ada"}
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
## Differences from the source
|
|
36
|
+
|
|
37
|
+
The fix is offered only when the removed values are plain names or literals, so no call is dropped.
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
---
|
|
2
|
+
id: py/raise-literal
|
|
3
|
+
title: "Raise exceptions, not literals"
|
|
4
|
+
languages: [python]
|
|
5
|
+
fix: manual
|
|
6
|
+
fix_reason: "the exception class to raise is the author's choice"
|
|
7
|
+
stability: preview
|
|
8
|
+
ported_from: [ruff/B016]
|
|
9
|
+
since: 0.1.0
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
`raise` is given an exception, not a string or number literal.
|
|
13
|
+
|
|
14
|
+
**Why:** Raising a literal is a `TypeError` in Python 3.
|
|
15
|
+
|
|
16
|
+
```codelaw
|
|
17
|
+
native: py/raise-literal
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
## How to fix
|
|
21
|
+
|
|
22
|
+
1. Raise an exception class with the literal as its message.
|
|
23
|
+
|
|
24
|
+
## Examples
|
|
25
|
+
|
|
26
|
+
### Python
|
|
27
|
+
|
|
28
|
+
#### Don't
|
|
29
|
+
|
|
30
|
+
```py
|
|
31
|
+
raise "failed"
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
#### Do
|
|
35
|
+
|
|
36
|
+
```py
|
|
37
|
+
raise RuntimeError("failed")
|
|
38
|
+
```
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
---
|
|
2
|
+
id: py/raise-not-implemented
|
|
3
|
+
title: "Raise `NotImplementedError`, not `NotImplemented`"
|
|
4
|
+
languages: [python]
|
|
5
|
+
fix: suggest
|
|
6
|
+
fix_reason: "callers catching the `TypeError` the broken code raised would now see a different exception"
|
|
7
|
+
stability: preview
|
|
8
|
+
ported_from: [ruff/F901]
|
|
9
|
+
since: 0.1.0
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
`raise` uses `NotImplementedError`, not the `NotImplemented` constant.
|
|
13
|
+
|
|
14
|
+
**Why:** `NotImplemented` is a value for binary operators; raising it is a `TypeError`.
|
|
15
|
+
|
|
16
|
+
```codelaw
|
|
17
|
+
native: py/raise-not-implemented
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
## Examples
|
|
21
|
+
|
|
22
|
+
### Python
|
|
23
|
+
|
|
24
|
+
#### Don't
|
|
25
|
+
|
|
26
|
+
```py
|
|
27
|
+
class Shape:
|
|
28
|
+
def area(self):
|
|
29
|
+
raise NotImplemented
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
#### Do
|
|
33
|
+
|
|
34
|
+
```py
|
|
35
|
+
class Shape:
|
|
36
|
+
def area(self):
|
|
37
|
+
raise NotImplementedError
|
|
38
|
+
```
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
# py/raise-without-from-inside-except — cases
|
|
2
|
+
|
|
3
|
+
### case: raise inside a nested try
|
|
4
|
+
|
|
5
|
+
```py
|
|
6
|
+
try:
|
|
7
|
+
run()
|
|
8
|
+
except ValueError:
|
|
9
|
+
try:
|
|
10
|
+
raise KeyError()
|
|
11
|
+
except KeyError:
|
|
12
|
+
pass
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
```expect
|
|
16
|
+
(none)
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
### case: re-raising
|
|
20
|
+
|
|
21
|
+
```py
|
|
22
|
+
try:
|
|
23
|
+
run()
|
|
24
|
+
except ValueError as e:
|
|
25
|
+
raise e
|
|
26
|
+
except KeyError:
|
|
27
|
+
raise
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
```expect
|
|
31
|
+
(none)
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
### case: in a nested function
|
|
35
|
+
|
|
36
|
+
```py
|
|
37
|
+
try:
|
|
38
|
+
run()
|
|
39
|
+
except ValueError:
|
|
40
|
+
def f():
|
|
41
|
+
raise TypeError()
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
```expect
|
|
45
|
+
(none)
|
|
46
|
+
```
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
---
|
|
2
|
+
id: py/raise-without-from-inside-except
|
|
3
|
+
title: "Raising in `except` says `from`"
|
|
4
|
+
languages: [python]
|
|
5
|
+
fix: manual
|
|
6
|
+
fix_reason: "whether to chain the original error or hide it is the author's choice"
|
|
7
|
+
stability: preview
|
|
8
|
+
ported_from: [ruff/B904]
|
|
9
|
+
since: 0.1.0
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
A `raise` inside an `except` block that raises a new exception says `from err` or `from None`.
|
|
13
|
+
|
|
14
|
+
**Why:** Without it, the traceback reads as if a second error happened while handling the first.
|
|
15
|
+
|
|
16
|
+
```codelaw
|
|
17
|
+
native: py/raise-without-from-inside-except
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
## How to fix
|
|
21
|
+
|
|
22
|
+
1. Bind the caught exception (`except E as err:`).
|
|
23
|
+
2. Raise with `from err`, or `from None` to hide the original deliberately.
|
|
24
|
+
|
|
25
|
+
## Examples
|
|
26
|
+
|
|
27
|
+
### Python
|
|
28
|
+
|
|
29
|
+
#### Don't
|
|
30
|
+
|
|
31
|
+
```py
|
|
32
|
+
try:
|
|
33
|
+
value = int(text)
|
|
34
|
+
except ValueError:
|
|
35
|
+
raise TypeError("not a number")
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
#### Do
|
|
39
|
+
|
|
40
|
+
```py
|
|
41
|
+
try:
|
|
42
|
+
value = int(text)
|
|
43
|
+
except ValueError as err:
|
|
44
|
+
raise TypeError("not a number") from err
|
|
45
|
+
```
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
---
|
|
2
|
+
id: py/re-sub-positional-args
|
|
3
|
+
title: "`re.sub` count and flags by keyword"
|
|
4
|
+
languages: [python]
|
|
5
|
+
fix: manual
|
|
6
|
+
fix_reason: "which parameter the value was meant for needs a person"
|
|
7
|
+
stability: preview
|
|
8
|
+
ported_from: [ruff/B034]
|
|
9
|
+
since: 0.1.0
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
`re.sub()`, `re.subn()` and `re.split()` receive `count`, `maxsplit` and `flags` as keyword arguments.
|
|
13
|
+
|
|
14
|
+
**Why:** Their positions are easy to confuse: `re.sub(p, r, s, re.IGNORECASE)` passes the flag as `count`.
|
|
15
|
+
|
|
16
|
+
```codelaw
|
|
17
|
+
native: py/re-sub-positional-args
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
## How to fix
|
|
21
|
+
|
|
22
|
+
1. Pass `count=`, `maxsplit=` and `flags=` by name.
|
|
23
|
+
|
|
24
|
+
## Examples
|
|
25
|
+
|
|
26
|
+
### Python
|
|
27
|
+
|
|
28
|
+
#### Don't
|
|
29
|
+
|
|
30
|
+
```py
|
|
31
|
+
import re
|
|
32
|
+
|
|
33
|
+
text = re.sub("a", "b", text, 1)
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
#### Do
|
|
37
|
+
|
|
38
|
+
```py
|
|
39
|
+
import re
|
|
40
|
+
|
|
41
|
+
text = re.sub("a", "b", text, count=1)
|
|
42
|
+
```
|
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
# py/redefined-while-unused — cases
|
|
2
|
+
|
|
3
|
+
### case: redefinition in another branch
|
|
4
|
+
|
|
5
|
+
```py
|
|
6
|
+
import sys
|
|
7
|
+
|
|
8
|
+
if __name__ == "__main__":
|
|
9
|
+
import sys
|
|
10
|
+
print(sys.argv)
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
```expect
|
|
14
|
+
(none)
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
### case: import used in a string annotation
|
|
18
|
+
|
|
19
|
+
```py
|
|
20
|
+
from typing import TYPE_CHECKING
|
|
21
|
+
|
|
22
|
+
if TYPE_CHECKING:
|
|
23
|
+
from .console import Console
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def f(c: "Console") -> None:
|
|
27
|
+
pass
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
if __name__ == "__main__":
|
|
31
|
+
from rich.console import Console
|
|
32
|
+
Console()
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
```expect
|
|
36
|
+
(none)
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
### case: try/except alternatives
|
|
40
|
+
|
|
41
|
+
```py
|
|
42
|
+
try:
|
|
43
|
+
import json
|
|
44
|
+
except ImportError:
|
|
45
|
+
import simplejson as json
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
```expect
|
|
49
|
+
(none)
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
### case: if/else alternatives
|
|
53
|
+
|
|
54
|
+
```py
|
|
55
|
+
if fast:
|
|
56
|
+
def run(): pass
|
|
57
|
+
else:
|
|
58
|
+
def run(): pass
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
```expect
|
|
62
|
+
(none)
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
### case: property setter
|
|
66
|
+
|
|
67
|
+
```py
|
|
68
|
+
class C:
|
|
69
|
+
@property
|
|
70
|
+
def p(self): pass
|
|
71
|
+
@p.setter
|
|
72
|
+
def p(self, v): pass
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
```expect
|
|
76
|
+
(none)
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
### case: overloads
|
|
80
|
+
|
|
81
|
+
```py
|
|
82
|
+
from typing import overload
|
|
83
|
+
@overload
|
|
84
|
+
def f(a: int): ...
|
|
85
|
+
@overload
|
|
86
|
+
def f(a: str): ...
|
|
87
|
+
def f(a): pass
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
```expect
|
|
91
|
+
(none)
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
### case: import replaced by assignment
|
|
95
|
+
|
|
96
|
+
```py
|
|
97
|
+
import os
|
|
98
|
+
os = 1
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
```expect
|
|
102
|
+
2:1 py/redefined-while-unused
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
### case: plain variables may be reassigned
|
|
106
|
+
|
|
107
|
+
```py
|
|
108
|
+
x = 1
|
|
109
|
+
x = 2
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
```expect
|
|
113
|
+
(none)
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
### case: used in between
|
|
117
|
+
|
|
118
|
+
```py
|
|
119
|
+
import os
|
|
120
|
+
print(os.name)
|
|
121
|
+
import os
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
```expect
|
|
125
|
+
(none)
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
### case: methods in a class
|
|
129
|
+
|
|
130
|
+
```py
|
|
131
|
+
class C:
|
|
132
|
+
def m(self): pass
|
|
133
|
+
def m(self): pass
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
```expect
|
|
137
|
+
3:9 py/redefined-while-unused
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
### case: two submodules of one package
|
|
141
|
+
|
|
142
|
+
```py
|
|
143
|
+
import urllib.parse, urllib.request
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
```expect
|
|
147
|
+
(none)
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
### case: global rebinding from a function
|
|
151
|
+
|
|
152
|
+
```py
|
|
153
|
+
xmlrpclib = None
|
|
154
|
+
def f():
|
|
155
|
+
global xmlrpclib
|
|
156
|
+
import xmlrpc.client as xmlrpclib
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
```expect
|
|
160
|
+
(none)
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
### case: optional accelerator
|
|
164
|
+
|
|
165
|
+
```py
|
|
166
|
+
def cmp_to_key(f): pass
|
|
167
|
+
try:
|
|
168
|
+
from _functools import cmp_to_key
|
|
169
|
+
except ImportError:
|
|
170
|
+
pass
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
```expect
|
|
174
|
+
(none)
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
### case: right-hand side reads the old binding
|
|
178
|
+
|
|
179
|
+
```py
|
|
180
|
+
def _coro(): pass
|
|
181
|
+
_coro = _coro()
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
```expect
|
|
185
|
+
(none)
|
|
186
|
+
```
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
---
|
|
2
|
+
id: py/redefined-while-unused
|
|
3
|
+
title: "A definition isn't replaced before it is used"
|
|
4
|
+
languages: [python]
|
|
5
|
+
fix: manual
|
|
6
|
+
fix_reason: "which definition is meant, and whether to rename or remove the other, needs a person"
|
|
7
|
+
stability: preview
|
|
8
|
+
ported_from: [ruff/F811]
|
|
9
|
+
since: 0.1.0
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
A function, class or import isn't redefined (by another `def`, `class`, import or assignment) before anything reads it.
|
|
13
|
+
|
|
14
|
+
**Why:** The first definition is dead: usually a copy-paste mistake, or a name clash with an import.
|
|
15
|
+
|
|
16
|
+
```codelaw
|
|
17
|
+
native: py/redefined-while-unused
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
## How to fix
|
|
21
|
+
|
|
22
|
+
1. Decide which definition is meant.
|
|
23
|
+
2. Rename or remove the other one.
|
|
24
|
+
|
|
25
|
+
## Examples
|
|
26
|
+
|
|
27
|
+
### Python
|
|
28
|
+
|
|
29
|
+
#### Don't
|
|
30
|
+
|
|
31
|
+
```py
|
|
32
|
+
def load():
|
|
33
|
+
return 1
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def load():
|
|
37
|
+
return 2
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
#### Do
|
|
41
|
+
|
|
42
|
+
```py
|
|
43
|
+
def load():
|
|
44
|
+
return 1
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def reload():
|
|
48
|
+
return 2
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
## Differences from the source
|
|
52
|
+
|
|
53
|
+
A read anywhere between the two definitions counts as a use, including inside a function body Python only runs later.
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
---
|
|
2
|
+
id: py/redundant-tuple-in-exception-handler
|
|
3
|
+
title: "No one-element tuple in `except`"
|
|
4
|
+
languages: [python]
|
|
5
|
+
fix: auto
|
|
6
|
+
stability: preview
|
|
7
|
+
ported_from: [ruff/B013]
|
|
8
|
+
since: 0.1.0
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
An `except` clause catching one exception names it directly, not as a one-element tuple.
|
|
12
|
+
|
|
13
|
+
**Why:** `except (ValueError,):` means the same as `except ValueError:`, with extra noise.
|
|
14
|
+
|
|
15
|
+
```codelaw
|
|
16
|
+
native: py/redundant-tuple-in-exception-handler
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
## Examples
|
|
20
|
+
|
|
21
|
+
### Python
|
|
22
|
+
|
|
23
|
+
#### Don't
|
|
24
|
+
|
|
25
|
+
```py
|
|
26
|
+
try:
|
|
27
|
+
run()
|
|
28
|
+
except (ValueError,):
|
|
29
|
+
retry()
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
#### Do
|
|
33
|
+
|
|
34
|
+
```py
|
|
35
|
+
try:
|
|
36
|
+
run()
|
|
37
|
+
except ValueError:
|
|
38
|
+
retry()
|
|
39
|
+
```
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
---
|
|
2
|
+
id: py/return-outside-function
|
|
3
|
+
title: "`return` only inside a function"
|
|
4
|
+
languages: [python]
|
|
5
|
+
fix: manual
|
|
6
|
+
fix_reason: "the surrounding code has to be restructured"
|
|
7
|
+
stability: preview
|
|
8
|
+
ported_from: [ruff/F706]
|
|
9
|
+
since: 0.1.0
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
`return` appears only inside a function.
|
|
13
|
+
|
|
14
|
+
**Why:** At module or class level it is a `SyntaxError`.
|
|
15
|
+
|
|
16
|
+
```codelaw
|
|
17
|
+
native: py/return-outside-function
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
## How to fix
|
|
21
|
+
|
|
22
|
+
1. Move the code into a function, or remove the `return`.
|
|
23
|
+
|
|
24
|
+
## Examples
|
|
25
|
+
|
|
26
|
+
### Python
|
|
27
|
+
|
|
28
|
+
#### Don't
|
|
29
|
+
|
|
30
|
+
```py
|
|
31
|
+
class Config:
|
|
32
|
+
return None
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
#### Do
|
|
36
|
+
|
|
37
|
+
```py
|
|
38
|
+
class Config:
|
|
39
|
+
def load(self):
|
|
40
|
+
return None
|
|
41
|
+
```
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
# py/reuse-of-groupby-generator — cases
|
|
2
|
+
|
|
3
|
+
### case: single use as a generator source
|
|
4
|
+
|
|
5
|
+
```py
|
|
6
|
+
import itertools
|
|
7
|
+
for k, g in itertools.groupby(items):
|
|
8
|
+
out.extend(e for e in g)
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
```expect
|
|
12
|
+
(none)
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
### case: use in an inner for of a comprehension
|
|
16
|
+
|
|
17
|
+
```py
|
|
18
|
+
import itertools
|
|
19
|
+
for k, g in itertools.groupby(items):
|
|
20
|
+
out = [x for y in ys for x in g]
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
```expect
|
|
24
|
+
3:35 py/reuse-of-groupby-generator
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
### case: separate branches
|
|
28
|
+
|
|
29
|
+
```py
|
|
30
|
+
import itertools
|
|
31
|
+
for k, g in itertools.groupby(rows):
|
|
32
|
+
if k:
|
|
33
|
+
a = list(g)
|
|
34
|
+
else:
|
|
35
|
+
b = list(g)
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
```expect
|
|
39
|
+
(none)
|
|
40
|
+
```
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
---
|
|
2
|
+
id: py/reuse-of-groupby-generator
|
|
3
|
+
title: "A `groupby` group is used once"
|
|
4
|
+
languages: [python]
|
|
5
|
+
fix: manual
|
|
6
|
+
fix_reason: "how to keep the items (a list, `itertools.tee`) is the author's choice"
|
|
7
|
+
stability: preview
|
|
8
|
+
ported_from: [ruff/B031]
|
|
9
|
+
since: 0.1.0
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
The group iterator from `itertools.groupby()` is consumed once inside its loop.
|
|
13
|
+
|
|
14
|
+
**Why:** Each group is a one-shot iterator; a second pass, or a pass inside a nested loop, sees nothing.
|
|
15
|
+
|
|
16
|
+
```codelaw
|
|
17
|
+
native: py/reuse-of-groupby-generator
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
## How to fix
|
|
21
|
+
|
|
22
|
+
1. Materialise the group once with `list(group)` and reuse the list.
|
|
23
|
+
|
|
24
|
+
## Examples
|
|
25
|
+
|
|
26
|
+
### Python
|
|
27
|
+
|
|
28
|
+
#### Don't
|
|
29
|
+
|
|
30
|
+
```py
|
|
31
|
+
import itertools
|
|
32
|
+
|
|
33
|
+
for key, group in itertools.groupby(rows):
|
|
34
|
+
first = list(group)
|
|
35
|
+
rest = list(group)
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
#### Do
|
|
39
|
+
|
|
40
|
+
```py
|
|
41
|
+
import itertools
|
|
42
|
+
|
|
43
|
+
for key, group in itertools.groupby(rows):
|
|
44
|
+
items = list(group)
|
|
45
|
+
first, rest = items[:1], items[1:]
|
|
46
|
+
```
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
---
|
|
2
|
+
id: py/setattr-with-constant
|
|
3
|
+
title: "No `setattr` with a constant name"
|
|
4
|
+
languages: [python]
|
|
5
|
+
fix: auto
|
|
6
|
+
stability: preview
|
|
7
|
+
ported_from: [ruff/B010]
|
|
8
|
+
since: 0.1.0
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
`setattr(obj, "name", value)` as a statement, with a constant, valid attribute name, is written `obj.name = value`.
|
|
12
|
+
|
|
13
|
+
**Why:** `setattr` with a constant name is no safer than an assignment, only harder to read.
|
|
14
|
+
|
|
15
|
+
```codelaw
|
|
16
|
+
native: py/setattr-with-constant
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
## Examples
|
|
20
|
+
|
|
21
|
+
### Python
|
|
22
|
+
|
|
23
|
+
#### Don't
|
|
24
|
+
|
|
25
|
+
```py
|
|
26
|
+
setattr(user, "name", "Ada")
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
#### Do
|
|
30
|
+
|
|
31
|
+
```py
|
|
32
|
+
user.name = "Ada"
|
|
33
|
+
```
|