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,40 @@
|
|
|
1
|
+
# py/no-print — cases
|
|
2
|
+
|
|
3
|
+
### case: adds the import
|
|
4
|
+
|
|
5
|
+
```py
|
|
6
|
+
def run():
|
|
7
|
+
print("x")
|
|
8
|
+
```
|
|
9
|
+
|
|
10
|
+
```expect
|
|
11
|
+
2:5 py/no-print
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
```fixed
|
|
15
|
+
import logging
|
|
16
|
+
def run():
|
|
17
|
+
logging.getLogger(__name__).info("%s", "x")
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
### case: several arguments: no edit
|
|
21
|
+
|
|
22
|
+
```py
|
|
23
|
+
print("a", 1)
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
```expect
|
|
27
|
+
1:1 py/no-print
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
### case: a local print function
|
|
31
|
+
|
|
32
|
+
```py
|
|
33
|
+
def print(x):
|
|
34
|
+
pass
|
|
35
|
+
print(1)
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
```expect
|
|
39
|
+
(none)
|
|
40
|
+
```
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
---
|
|
2
|
+
id: py/no-print
|
|
3
|
+
title: "No `print()` in library code"
|
|
4
|
+
languages: [python]
|
|
5
|
+
files: ["src/**"]
|
|
6
|
+
fix: suggest
|
|
7
|
+
fix_reason: "the log level, and whether the output was meant for the user, need a person"
|
|
8
|
+
stability: preview
|
|
9
|
+
since: 0.1.0
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
Code under `src/` reports through `logging`, not `print()`.
|
|
13
|
+
|
|
14
|
+
**Why:** `print` writes to standard output whether or not the caller wants it; a logger can be filtered, redirected and silenced.
|
|
15
|
+
|
|
16
|
+
```codelaw
|
|
17
|
+
native: py/no-print
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
## Examples
|
|
21
|
+
|
|
22
|
+
### Python
|
|
23
|
+
|
|
24
|
+
#### Don't
|
|
25
|
+
|
|
26
|
+
```py path=src/app.py
|
|
27
|
+
import logging
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def run():
|
|
31
|
+
print("started")
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
#### Do
|
|
35
|
+
|
|
36
|
+
```py path=src/app.py
|
|
37
|
+
import logging
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def run():
|
|
41
|
+
logging.getLogger(__name__).info("%s", "started")
|
|
42
|
+
```
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
---
|
|
2
|
+
id: py/no-star-import
|
|
3
|
+
title: "No `from module import *`"
|
|
4
|
+
languages: [python]
|
|
5
|
+
fix: manual
|
|
6
|
+
fix_reason: "which names the module needs can only be found by reading its uses"
|
|
7
|
+
stability: preview
|
|
8
|
+
ported_from: [ruff/F403]
|
|
9
|
+
since: 0.1.0
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
Modules import the names they use explicitly instead of `from module import *`.
|
|
13
|
+
|
|
14
|
+
**Why:** A star import hides where names come from, can silently replace local names, and stops a linter from finding undefined names.
|
|
15
|
+
|
|
16
|
+
```codelaw
|
|
17
|
+
native: py/no-star-import
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
## How to fix
|
|
21
|
+
|
|
22
|
+
1. List the names the module uses from the star-imported module.
|
|
23
|
+
2. Replace the star with those names.
|
|
24
|
+
|
|
25
|
+
## Examples
|
|
26
|
+
|
|
27
|
+
### Python
|
|
28
|
+
|
|
29
|
+
#### Don't
|
|
30
|
+
|
|
31
|
+
```py
|
|
32
|
+
from os.path import *
|
|
33
|
+
|
|
34
|
+
print(join("a", "b"))
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
#### Do
|
|
38
|
+
|
|
39
|
+
```py
|
|
40
|
+
from os.path import join
|
|
41
|
+
|
|
42
|
+
print(join("a", "b"))
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
## Differences from the source
|
|
46
|
+
|
|
47
|
+
Relative star imports (`from .sub import *`) are not reported: that is how a package re-exports its submodules' API, and Ruff leaves them alone in `__init__.py`.
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
# py/no-tunable-literals — cases
|
|
2
|
+
|
|
3
|
+
### case: default parameter
|
|
4
|
+
|
|
5
|
+
```py
|
|
6
|
+
def connect(host, retries=5):
|
|
7
|
+
pass
|
|
8
|
+
```
|
|
9
|
+
|
|
10
|
+
```expect
|
|
11
|
+
1:19 py/no-tunable-literals
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
### case: module constant is the config point
|
|
15
|
+
|
|
16
|
+
```py
|
|
17
|
+
TIMEOUT = 30
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
```expect
|
|
21
|
+
(none)
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
### case: zero and one are fine
|
|
25
|
+
|
|
26
|
+
```py
|
|
27
|
+
get(url, timeout=0)
|
|
28
|
+
loop(max_items=1)
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
```expect
|
|
32
|
+
(none)
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
### case: unrelated names
|
|
36
|
+
|
|
37
|
+
```py
|
|
38
|
+
resize(width=640)
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
```expect
|
|
42
|
+
(none)
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
### case: local assignment
|
|
46
|
+
|
|
47
|
+
```py
|
|
48
|
+
def f():
|
|
49
|
+
max_size = 1024
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
```expect
|
|
53
|
+
2:5 py/no-tunable-literals
|
|
54
|
+
```
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
---
|
|
2
|
+
id: py/no-tunable-literals
|
|
3
|
+
title: "No hard-coded timeouts, limits or thresholds"
|
|
4
|
+
languages: [python]
|
|
5
|
+
fix: manual
|
|
6
|
+
fix_reason: "which configuration key holds the value is the author's choice"
|
|
7
|
+
stability: preview
|
|
8
|
+
since: 0.1.0
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
A number passed as, defaulted to or assigned to a setting whose name says it is tunable (`timeout`, `retries`, `max_*`, `limit`, `threshold`, `interval`, `delay`, `ttl`, `backoff`, `deadline`, …) comes from configuration, not a literal in the code. `0` and `1` are allowed, and so are module-level `UPPER_CASE` constants, the one place such a value may be written down.
|
|
12
|
+
|
|
13
|
+
**Why:** Tunable values buried in code can't be changed per environment without a release, and the same number drifts apart in several places.
|
|
14
|
+
|
|
15
|
+
```codelaw
|
|
16
|
+
match:
|
|
17
|
+
any:
|
|
18
|
+
- all:
|
|
19
|
+
- kind: keyword_argument
|
|
20
|
+
- has: { field: name, regex: '(?i)(^|_)(timeouts?|retries|retry|attempts|limit|threshold|interval|delay|ttl|backoff|deadline|max|min|maximum|minimum)(_|$)' }
|
|
21
|
+
- has: { field: value, any: [{ kind: integer }, { kind: float }], not: { regex: '^(0|1|0\.0|1\.0)$' } }
|
|
22
|
+
- all:
|
|
23
|
+
- any: [{ kind: default_parameter }, { kind: typed_default_parameter }]
|
|
24
|
+
- has: { field: name, regex: '(?i)(^|_)(timeouts?|retries|retry|attempts|limit|threshold|interval|delay|ttl|backoff|deadline|max|min|maximum|minimum)(_|$)' }
|
|
25
|
+
- has: { field: value, any: [{ kind: integer }, { kind: float }], not: { regex: '^(0|1|0\.0|1\.0)$' } }
|
|
26
|
+
- all:
|
|
27
|
+
- kind: assignment
|
|
28
|
+
- has: { field: left, kind: identifier, regex: '(?i)(^|_)(timeouts?|retries|retry|attempts|limit|threshold|interval|delay|ttl|backoff|deadline|max|min|maximum|minimum)(_|$)', not: { regex: '^[A-Z0-9_]+$' } }
|
|
29
|
+
- has: { field: right, any: [{ kind: integer }, { kind: float }], not: { regex: '^(0|1|0\.0|1\.0)$' } }
|
|
30
|
+
message: "This tunable value is hard-coded; read it from configuration"
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
## How to fix
|
|
34
|
+
|
|
35
|
+
1. Pick or add a configuration key for the value.
|
|
36
|
+
2. Read the value from configuration where it is used.
|
|
37
|
+
3. For a fixed protocol constant, name it as a module-level `UPPER_CASE` constant instead.
|
|
38
|
+
|
|
39
|
+
## Examples
|
|
40
|
+
|
|
41
|
+
### Python
|
|
42
|
+
|
|
43
|
+
#### Don't
|
|
44
|
+
|
|
45
|
+
```py
|
|
46
|
+
def fetch(url):
|
|
47
|
+
return get(url, timeout=30)
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
#### Do
|
|
51
|
+
|
|
52
|
+
```py
|
|
53
|
+
from config import settings
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
def fetch(url):
|
|
57
|
+
return get(url, timeout=settings.http_timeout)
|
|
58
|
+
```
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
---
|
|
2
|
+
id: py/none-comparison
|
|
3
|
+
title: "Compare with `None` using `is`"
|
|
4
|
+
languages: [python]
|
|
5
|
+
fix: suggest
|
|
6
|
+
fix_reason: "an object whose `__eq__` treats itself as equal to `None` would compare differently"
|
|
7
|
+
stability: preview
|
|
8
|
+
ported_from: [ruff/E711]
|
|
9
|
+
since: 0.1.0
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
Comparisons with `None` use `is` / `is not`, not `==` / `!=`.
|
|
13
|
+
|
|
14
|
+
**Why:** `None` is a singleton; `==` calls the other object's `__eq__`, which may not return what you expect.
|
|
15
|
+
|
|
16
|
+
```codelaw
|
|
17
|
+
native: py/none-comparison
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
## Examples
|
|
21
|
+
|
|
22
|
+
### Python
|
|
23
|
+
|
|
24
|
+
#### Don't
|
|
25
|
+
|
|
26
|
+
```py
|
|
27
|
+
if user == None:
|
|
28
|
+
login()
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
#### Do
|
|
32
|
+
|
|
33
|
+
```py
|
|
34
|
+
if user is None:
|
|
35
|
+
login()
|
|
36
|
+
```
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
---
|
|
2
|
+
id: py/not-in-test
|
|
3
|
+
title: "Write `x not in y`"
|
|
4
|
+
languages: [python]
|
|
5
|
+
fix: auto
|
|
6
|
+
stability: preview
|
|
7
|
+
ported_from: [ruff/E713]
|
|
8
|
+
since: 0.1.0
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
A membership test is negated as `x not in y`, not `not x in y`.
|
|
12
|
+
|
|
13
|
+
**Why:** `not x in y` reads as `(not x) in y`; `not in` says what is meant.
|
|
14
|
+
|
|
15
|
+
```codelaw
|
|
16
|
+
native: py/not-in-test
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
## Examples
|
|
20
|
+
|
|
21
|
+
### Python
|
|
22
|
+
|
|
23
|
+
#### Don't
|
|
24
|
+
|
|
25
|
+
```py
|
|
26
|
+
if not item in seen:
|
|
27
|
+
keep(item)
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
#### Do
|
|
31
|
+
|
|
32
|
+
```py
|
|
33
|
+
if item not in seen:
|
|
34
|
+
keep(item)
|
|
35
|
+
```
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
---
|
|
2
|
+
id: py/not-is-test
|
|
3
|
+
title: "Write `x is not y`"
|
|
4
|
+
languages: [python]
|
|
5
|
+
fix: auto
|
|
6
|
+
stability: preview
|
|
7
|
+
ported_from: [ruff/E714]
|
|
8
|
+
since: 0.1.0
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
An identity test is negated as `x is not y`, not `not x is y`.
|
|
12
|
+
|
|
13
|
+
**Why:** `not x is y` reads as `(not x) is y`; `is not` says what is meant.
|
|
14
|
+
|
|
15
|
+
```codelaw
|
|
16
|
+
native: py/not-is-test
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
## Examples
|
|
20
|
+
|
|
21
|
+
### Python
|
|
22
|
+
|
|
23
|
+
#### Don't
|
|
24
|
+
|
|
25
|
+
```py
|
|
26
|
+
if not value is None:
|
|
27
|
+
use(value)
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
#### Do
|
|
31
|
+
|
|
32
|
+
```py
|
|
33
|
+
if value is not None:
|
|
34
|
+
use(value)
|
|
35
|
+
```
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
---
|
|
2
|
+
id: py/percent-format-count-mismatch
|
|
3
|
+
title: "A `%` format string gets one value per placeholder"
|
|
4
|
+
languages: [python]
|
|
5
|
+
fix: manual
|
|
6
|
+
fix_reason: "which value is missing, or extra, can't be inferred"
|
|
7
|
+
stability: preview
|
|
8
|
+
ported_from: [ruff/F507]
|
|
9
|
+
since: 0.1.0
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
The tuple (or single literal) formatted into a `%` string has as many values as the string has positional placeholders.
|
|
13
|
+
|
|
14
|
+
**Why:** Too few values raise `TypeError: not enough arguments`; too many raise `TypeError: not all arguments converted`.
|
|
15
|
+
|
|
16
|
+
```codelaw
|
|
17
|
+
native: py/percent-format-count-mismatch
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
## How to fix
|
|
21
|
+
|
|
22
|
+
1. Add or remove values, or placeholders, until they match.
|
|
23
|
+
|
|
24
|
+
## Examples
|
|
25
|
+
|
|
26
|
+
### Python
|
|
27
|
+
|
|
28
|
+
#### Don't
|
|
29
|
+
|
|
30
|
+
```py
|
|
31
|
+
message = "%s is %s" % ("Ada",)
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
#### Do
|
|
35
|
+
|
|
36
|
+
```py
|
|
37
|
+
message = "%s is %s" % ("Ada", 36)
|
|
38
|
+
```
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
---
|
|
2
|
+
id: py/percent-format-expected-mapping
|
|
3
|
+
title: "Named `%` placeholders get a mapping"
|
|
4
|
+
languages: [python]
|
|
5
|
+
fix: manual
|
|
6
|
+
fix_reason: "the key each value belongs to isn't written anywhere"
|
|
7
|
+
stability: preview
|
|
8
|
+
ported_from: [ruff/F502]
|
|
9
|
+
since: 0.1.0
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
A `%` format string with named placeholders (`%(name)s`) is applied to a mapping, not a tuple.
|
|
13
|
+
|
|
14
|
+
**Why:** Python raises `TypeError: format requires a mapping`.
|
|
15
|
+
|
|
16
|
+
```codelaw
|
|
17
|
+
native: py/percent-format-expected-mapping
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
## How to fix
|
|
21
|
+
|
|
22
|
+
1. Pass a dict whose keys are the placeholder names.
|
|
23
|
+
|
|
24
|
+
## Examples
|
|
25
|
+
|
|
26
|
+
### Python
|
|
27
|
+
|
|
28
|
+
#### Don't
|
|
29
|
+
|
|
30
|
+
```py
|
|
31
|
+
message = "%(name)s" % ("Ada",)
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
#### Do
|
|
35
|
+
|
|
36
|
+
```py
|
|
37
|
+
message = "%(name)s" % {"name": "Ada"}
|
|
38
|
+
```
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
---
|
|
2
|
+
id: py/percent-format-expected-sequence
|
|
3
|
+
title: "Positional `%` placeholders get a tuple"
|
|
4
|
+
languages: [python]
|
|
5
|
+
fix: manual
|
|
6
|
+
fix_reason: "the order of the values isn't written anywhere"
|
|
7
|
+
stability: preview
|
|
8
|
+
ported_from: [ruff/F503]
|
|
9
|
+
since: 0.1.0
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
A `%` format string with two or more positional placeholders is applied to a tuple, not a dict.
|
|
13
|
+
|
|
14
|
+
**Why:** A dict is a single value, so Python raises `TypeError: not enough arguments`.
|
|
15
|
+
|
|
16
|
+
```codelaw
|
|
17
|
+
native: py/percent-format-expected-sequence
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
## How to fix
|
|
21
|
+
|
|
22
|
+
1. Pass a tuple with one value per placeholder, or switch to named placeholders.
|
|
23
|
+
|
|
24
|
+
## Examples
|
|
25
|
+
|
|
26
|
+
### Python
|
|
27
|
+
|
|
28
|
+
#### Don't
|
|
29
|
+
|
|
30
|
+
```py
|
|
31
|
+
message = "%s and %s" % {"a": 1, "b": 2}
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
#### Do
|
|
35
|
+
|
|
36
|
+
```py
|
|
37
|
+
message = "%s and %s" % (1, 2)
|
|
38
|
+
```
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
---
|
|
2
|
+
id: py/percent-format-invalid
|
|
3
|
+
title: "A `%` format string is complete"
|
|
4
|
+
languages: [python]
|
|
5
|
+
fix: manual
|
|
6
|
+
fix_reason: "the intended placeholder can't be inferred"
|
|
7
|
+
stability: preview
|
|
8
|
+
ported_from: [ruff/F501]
|
|
9
|
+
since: 0.1.0
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
A string formatted with `%` has no unfinished placeholder such as `%(name` or a trailing `%`.
|
|
13
|
+
|
|
14
|
+
**Why:** Python raises `ValueError` when the string is formatted.
|
|
15
|
+
|
|
16
|
+
```codelaw
|
|
17
|
+
native: py/percent-format-invalid
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
## How to fix
|
|
21
|
+
|
|
22
|
+
1. Finish the placeholder (`%(name)s`) or escape a literal percent as `%%`.
|
|
23
|
+
|
|
24
|
+
## Examples
|
|
25
|
+
|
|
26
|
+
### Python
|
|
27
|
+
|
|
28
|
+
#### Don't
|
|
29
|
+
|
|
30
|
+
```py
|
|
31
|
+
message = "%(name" % {"name": "Ada"}
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
#### Do
|
|
35
|
+
|
|
36
|
+
```py
|
|
37
|
+
message = "%(name)s" % {"name": "Ada"}
|
|
38
|
+
```
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
---
|
|
2
|
+
id: py/percent-format-missing-argument
|
|
3
|
+
title: "Every named `%` placeholder has a value"
|
|
4
|
+
languages: [python]
|
|
5
|
+
fix: manual
|
|
6
|
+
fix_reason: "the value for the missing key has to be supplied"
|
|
7
|
+
stability: preview
|
|
8
|
+
ported_from: [ruff/F505]
|
|
9
|
+
since: 0.1.0
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
A dict literal formatted into a `%` string has a key for every named placeholder.
|
|
13
|
+
|
|
14
|
+
**Why:** A missing key raises `KeyError` when the string is formatted.
|
|
15
|
+
|
|
16
|
+
```codelaw
|
|
17
|
+
native: py/percent-format-missing-argument
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
## How to fix
|
|
21
|
+
|
|
22
|
+
1. Add the missing key to the dict, or remove the placeholder.
|
|
23
|
+
|
|
24
|
+
## Examples
|
|
25
|
+
|
|
26
|
+
### Python
|
|
27
|
+
|
|
28
|
+
#### Don't
|
|
29
|
+
|
|
30
|
+
```py
|
|
31
|
+
message = "%(name)s is %(age)s" % {"name": "Ada"}
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
#### Do
|
|
35
|
+
|
|
36
|
+
```py
|
|
37
|
+
message = "%(name)s is %(age)s" % {"name": "Ada", "age": 36}
|
|
38
|
+
```
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
---
|
|
2
|
+
id: py/percent-format-mixed-placeholders
|
|
3
|
+
title: "A `%` format string doesn't mix positional and named placeholders"
|
|
4
|
+
languages: [python]
|
|
5
|
+
fix: manual
|
|
6
|
+
fix_reason: "which style the string should use is the author's choice"
|
|
7
|
+
stability: preview
|
|
8
|
+
ported_from: [ruff/F506]
|
|
9
|
+
since: 0.1.0
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
A `%` format string uses either positional placeholders (`%s`) or named ones (`%(name)s`), not both.
|
|
13
|
+
|
|
14
|
+
**Why:** Python can't fill both from one right-hand side and raises `TypeError`.
|
|
15
|
+
|
|
16
|
+
```codelaw
|
|
17
|
+
native: py/percent-format-mixed-placeholders
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
## How to fix
|
|
21
|
+
|
|
22
|
+
1. Name every placeholder, or make every placeholder positional.
|
|
23
|
+
|
|
24
|
+
## Examples
|
|
25
|
+
|
|
26
|
+
### Python
|
|
27
|
+
|
|
28
|
+
#### Don't
|
|
29
|
+
|
|
30
|
+
```py
|
|
31
|
+
message = "%(name)s is %s" % {"name": "Ada"}
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
#### Do
|
|
35
|
+
|
|
36
|
+
```py
|
|
37
|
+
message = "%(name)s is %(age)s" % {"name": "Ada", "age": 36}
|
|
38
|
+
```
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
---
|
|
2
|
+
id: py/percent-format-star-requires-sequence
|
|
3
|
+
title: "A `*` width in a `%` format takes a tuple"
|
|
4
|
+
languages: [python]
|
|
5
|
+
fix: manual
|
|
6
|
+
fix_reason: "the order of the values has to be decided"
|
|
7
|
+
stability: preview
|
|
8
|
+
ported_from: [ruff/F508]
|
|
9
|
+
since: 0.1.0
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
A `%` format string using `*` for a width or precision is applied to a tuple.
|
|
13
|
+
|
|
14
|
+
**Why:** The `*` takes its value from the next positional argument, which a mapping doesn't have.
|
|
15
|
+
|
|
16
|
+
```codelaw
|
|
17
|
+
native: py/percent-format-star-requires-sequence
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
## How to fix
|
|
21
|
+
|
|
22
|
+
1. Pass a tuple with the width before the value.
|
|
23
|
+
|
|
24
|
+
## Examples
|
|
25
|
+
|
|
26
|
+
### Python
|
|
27
|
+
|
|
28
|
+
#### Don't
|
|
29
|
+
|
|
30
|
+
```py
|
|
31
|
+
message = "%*d" % {"width": 5, "value": 1}
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
#### Do
|
|
35
|
+
|
|
36
|
+
```py
|
|
37
|
+
message = "%*d" % (5, 1)
|
|
38
|
+
```
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
---
|
|
2
|
+
id: py/percent-format-unsupported-character
|
|
3
|
+
title: "A `%` format uses known conversions"
|
|
4
|
+
languages: [python]
|
|
5
|
+
fix: manual
|
|
6
|
+
fix_reason: "the intended conversion can't be inferred"
|
|
7
|
+
stability: preview
|
|
8
|
+
ported_from: [ruff/F509]
|
|
9
|
+
since: 0.1.0
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
Each `%` placeholder ends with a conversion Python knows (`s`, `r`, `a`, `d`, `f`, `x`, …).
|
|
13
|
+
|
|
14
|
+
**Why:** An unknown conversion raises `ValueError: unsupported format character`.
|
|
15
|
+
|
|
16
|
+
```codelaw
|
|
17
|
+
native: py/percent-format-unsupported-character
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
## How to fix
|
|
21
|
+
|
|
22
|
+
1. Use the conversion you meant, or escape a literal percent as `%%`.
|
|
23
|
+
|
|
24
|
+
## Examples
|
|
25
|
+
|
|
26
|
+
### Python
|
|
27
|
+
|
|
28
|
+
#### Don't
|
|
29
|
+
|
|
30
|
+
```py
|
|
31
|
+
message = "%y" % (1,)
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
#### Do
|
|
35
|
+
|
|
36
|
+
```py
|
|
37
|
+
message = "%s" % (1,)
|
|
38
|
+
```
|