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.
Files changed (139) hide show
  1. codelaw_rules_python/LICENSE +23 -0
  2. codelaw_rules_python/NOTICE +14 -0
  3. codelaw_rules_python/README.md +189 -0
  4. codelaw_rules_python/codelaw-pack.toml +6 -0
  5. codelaw_rules_python/corpus.toml +103 -0
  6. codelaw_rules_python/data/python/stdlib.json +331 -0
  7. codelaw_rules_python/presets/py/conventions.toml +5 -0
  8. codelaw_rules_python/presets/py/project.toml +4 -0
  9. codelaw_rules_python/presets/py/recommended.toml +99 -0
  10. codelaw_rules_python/rules/graph/no-unused-exports.md +69 -0
  11. codelaw_rules_python/rules/py/abstract-base-class-without-abstract-method.md +50 -0
  12. codelaw_rules_python/rules/py/all-sorted.cases.md +74 -0
  13. codelaw_rules_python/rules/py/all-sorted.md +33 -0
  14. codelaw_rules_python/rules/py/ambiguous-class-name.md +41 -0
  15. codelaw_rules_python/rules/py/ambiguous-function-name.md +41 -0
  16. codelaw_rules_python/rules/py/ambiguous-variable-name.cases.md +47 -0
  17. codelaw_rules_python/rules/py/ambiguous-variable-name.md +39 -0
  18. codelaw_rules_python/rules/py/assert-false.md +34 -0
  19. codelaw_rules_python/rules/py/assert-raises-exception.md +51 -0
  20. codelaw_rules_python/rules/py/assert-tuple.md +38 -0
  21. codelaw_rules_python/rules/py/assignment-to-os-environ.md +43 -0
  22. codelaw_rules_python/rules/py/batched-without-explicit-strict.md +52 -0
  23. codelaw_rules_python/rules/py/break-outside-loop.md +41 -0
  24. codelaw_rules_python/rules/py/cached-instance-method.md +55 -0
  25. codelaw_rules_python/rules/py/continue-outside-loop.md +41 -0
  26. codelaw_rules_python/rules/py/default-except-not-last.md +48 -0
  27. codelaw_rules_python/rules/py/duplicate-dict-key-variable.md +39 -0
  28. codelaw_rules_python/rules/py/duplicate-dict-key.md +39 -0
  29. codelaw_rules_python/rules/py/duplicate-handler-exception.md +43 -0
  30. codelaw_rules_python/rules/py/duplicate-set-value.md +33 -0
  31. codelaw_rules_python/rules/py/duplicate-try-block-exception.md +47 -0
  32. codelaw_rules_python/rules/py/empty-method-without-abstract-decorator.md +55 -0
  33. codelaw_rules_python/rules/py/except-with-empty-tuple.md +44 -0
  34. codelaw_rules_python/rules/py/except-with-non-exception-classes.md +44 -0
  35. codelaw_rules_python/rules/py/f-string-docstring.md +40 -0
  36. codelaw_rules_python/rules/py/f-string-missing-placeholders.cases.md +49 -0
  37. codelaw_rules_python/rules/py/f-string-missing-placeholders.md +33 -0
  38. codelaw_rules_python/rules/py/format-invalid.md +38 -0
  39. codelaw_rules_python/rules/py/format-missing-argument.md +38 -0
  40. codelaw_rules_python/rules/py/format-mixed-numbering.md +38 -0
  41. codelaw_rules_python/rules/py/format-unused-named-argument.md +37 -0
  42. codelaw_rules_python/rules/py/format-unused-positional-argument.md +37 -0
  43. codelaw_rules_python/rules/py/forward-annotation-syntax-error.md +44 -0
  44. codelaw_rules_python/rules/py/function-call-in-default-argument.md +51 -0
  45. codelaw_rules_python/rules/py/function-uses-loop-variable.cases.md +57 -0
  46. codelaw_rules_python/rules/py/function-uses-loop-variable.md +42 -0
  47. codelaw_rules_python/rules/py/getattr-with-constant.cases.md +31 -0
  48. codelaw_rules_python/rules/py/getattr-with-constant.md +33 -0
  49. codelaw_rules_python/rules/py/if-tuple.md +40 -0
  50. codelaw_rules_python/rules/py/import-shadowed-by-loop-var.md +45 -0
  51. codelaw_rules_python/rules/py/invalid-print-syntax.md +42 -0
  52. codelaw_rules_python/rules/py/is-literal.md +36 -0
  53. codelaw_rules_python/rules/py/jump-statement-in-finally.cases.md +15 -0
  54. codelaw_rules_python/rules/py/jump-statement-in-finally.md +47 -0
  55. codelaw_rules_python/rules/py/lambda-assignment.cases.md +48 -0
  56. codelaw_rules_python/rules/py/lambda-assignment.md +35 -0
  57. codelaw_rules_python/rules/py/late-future-import.md +40 -0
  58. codelaw_rules_python/rules/py/loop-variable-overrides-iterator.md +41 -0
  59. codelaw_rules_python/rules/py/map-without-explicit-strict.md +46 -0
  60. codelaw_rules_python/rules/py/module-import-not-at-top.cases.md +64 -0
  61. codelaw_rules_python/rules/py/module-import-not-at-top.md +46 -0
  62. codelaw_rules_python/rules/py/multiple-imports-on-one-line.md +38 -0
  63. codelaw_rules_python/rules/py/multiple-starred-expressions.md +38 -0
  64. codelaw_rules_python/rules/py/mutable-contextvar-default.md +42 -0
  65. codelaw_rules_python/rules/py/nested-star-import.md +42 -0
  66. codelaw_rules_python/rules/py/no-bare-except.cases.md +37 -0
  67. codelaw_rules_python/rules/py/no-bare-except.md +44 -0
  68. codelaw_rules_python/rules/py/no-explicit-stacklevel.md +38 -0
  69. codelaw_rules_python/rules/py/no-mutable-default.cases.md +58 -0
  70. codelaw_rules_python/rules/py/no-mutable-default.md +44 -0
  71. codelaw_rules_python/rules/py/no-print.cases.md +40 -0
  72. codelaw_rules_python/rules/py/no-print.md +42 -0
  73. codelaw_rules_python/rules/py/no-star-import.cases.md +11 -0
  74. codelaw_rules_python/rules/py/no-star-import.md +47 -0
  75. codelaw_rules_python/rules/py/no-tunable-literals.cases.md +54 -0
  76. codelaw_rules_python/rules/py/no-tunable-literals.md +58 -0
  77. codelaw_rules_python/rules/py/none-comparison.md +36 -0
  78. codelaw_rules_python/rules/py/not-in-test.md +35 -0
  79. codelaw_rules_python/rules/py/not-is-test.md +35 -0
  80. codelaw_rules_python/rules/py/percent-format-count-mismatch.md +38 -0
  81. codelaw_rules_python/rules/py/percent-format-expected-mapping.md +38 -0
  82. codelaw_rules_python/rules/py/percent-format-expected-sequence.md +38 -0
  83. codelaw_rules_python/rules/py/percent-format-invalid.md +38 -0
  84. codelaw_rules_python/rules/py/percent-format-missing-argument.md +38 -0
  85. codelaw_rules_python/rules/py/percent-format-mixed-placeholders.md +38 -0
  86. codelaw_rules_python/rules/py/percent-format-star-requires-sequence.md +38 -0
  87. codelaw_rules_python/rules/py/percent-format-unsupported-character.md +38 -0
  88. codelaw_rules_python/rules/py/percent-format-unused-named-argument.md +37 -0
  89. codelaw_rules_python/rules/py/raise-literal.md +38 -0
  90. codelaw_rules_python/rules/py/raise-not-implemented.md +38 -0
  91. codelaw_rules_python/rules/py/raise-without-from-inside-except.cases.md +46 -0
  92. codelaw_rules_python/rules/py/raise-without-from-inside-except.md +45 -0
  93. codelaw_rules_python/rules/py/re-sub-positional-args.md +42 -0
  94. codelaw_rules_python/rules/py/redefined-while-unused.cases.md +186 -0
  95. codelaw_rules_python/rules/py/redefined-while-unused.md +53 -0
  96. codelaw_rules_python/rules/py/redundant-tuple-in-exception-handler.md +39 -0
  97. codelaw_rules_python/rules/py/return-outside-function.md +41 -0
  98. codelaw_rules_python/rules/py/reuse-of-groupby-generator.cases.md +40 -0
  99. codelaw_rules_python/rules/py/reuse-of-groupby-generator.md +46 -0
  100. codelaw_rules_python/rules/py/setattr-with-constant.md +33 -0
  101. codelaw_rules_python/rules/py/star-arg-unpacking-after-keyword-arg.md +38 -0
  102. codelaw_rules_python/rules/py/star-import-usage.cases.md +35 -0
  103. codelaw_rules_python/rules/py/star-import-usage.md +43 -0
  104. codelaw_rules_python/rules/py/static-key-dict-comprehension.md +38 -0
  105. codelaw_rules_python/rules/py/strip-with-multi-characters.md +39 -0
  106. codelaw_rules_python/rules/py/too-many-expressions-in-star-assignment.md +38 -0
  107. codelaw_rules_python/rules/py/true-false-comparison.cases.md +33 -0
  108. codelaw_rules_python/rules/py/true-false-comparison.md +36 -0
  109. codelaw_rules_python/rules/py/type-comparison.md +41 -0
  110. codelaw_rules_python/rules/py/unary-prefix-increment.md +40 -0
  111. codelaw_rules_python/rules/py/undefined-export.md +55 -0
  112. codelaw_rules_python/rules/py/undefined-future-feature.md +39 -0
  113. codelaw_rules_python/rules/py/undefined-local.cases.md +80 -0
  114. codelaw_rules_python/rules/py/undefined-local.md +48 -0
  115. codelaw_rules_python/rules/py/undefined-name.cases.md +132 -0
  116. codelaw_rules_python/rules/py/undefined-name.md +42 -0
  117. codelaw_rules_python/rules/py/unintentional-type-annotation.cases.md +13 -0
  118. codelaw_rules_python/rules/py/unintentional-type-annotation.md +38 -0
  119. codelaw_rules_python/rules/py/unreliable-callable-check.md +36 -0
  120. codelaw_rules_python/rules/py/unsorted-imports.cases.md +159 -0
  121. codelaw_rules_python/rules/py/unsorted-imports.md +60 -0
  122. codelaw_rules_python/rules/py/unused-annotation.md +36 -0
  123. codelaw_rules_python/rules/py/unused-import.cases.md +289 -0
  124. codelaw_rules_python/rules/py/unused-import.md +45 -0
  125. codelaw_rules_python/rules/py/unused-loop-control-variable.cases.md +30 -0
  126. codelaw_rules_python/rules/py/unused-loop-control-variable.md +36 -0
  127. codelaw_rules_python/rules/py/unused-variable.cases.md +169 -0
  128. codelaw_rules_python/rules/py/unused-variable.md +38 -0
  129. codelaw_rules_python/rules/py/useless-comparison.md +41 -0
  130. codelaw_rules_python/rules/py/useless-contextlib-suppress.md +44 -0
  131. codelaw_rules_python/rules/py/useless-expression.cases.md +57 -0
  132. codelaw_rules_python/rules/py/useless-expression.md +45 -0
  133. codelaw_rules_python/rules/py/yield-outside-function.md +44 -0
  134. codelaw_rules_python/rules/py/zip-without-explicit-strict.cases.md +26 -0
  135. codelaw_rules_python/rules/py/zip-without-explicit-strict.md +46 -0
  136. codelaw_rules_python-0.2.0.dist-info/METADATA +199 -0
  137. codelaw_rules_python-0.2.0.dist-info/RECORD +139 -0
  138. codelaw_rules_python-0.2.0.dist-info/WHEEL +4 -0
  139. codelaw_rules_python-0.2.0.dist-info/licenses/LICENSE +23 -0
@@ -0,0 +1,52 @@
1
+ ---
2
+ id: py/batched-without-explicit-strict
3
+ title: "`itertools.batched()` says `strict=`"
4
+ languages: [python]
5
+ fix: suggest
6
+ fix_reason: "`strict=` exists only from Python 3.13, and codelaw cannot tell which Python the code runs on: on an older one the added keyword raises `TypeError`"
7
+ stability: preview
8
+ options:
9
+ target_version: { type: string, description: "the oldest Python the code supports; below 3.13 the rule is off" }
10
+ ported_from: [ruff/B911]
11
+ since: 0.1.0
12
+ ---
13
+
14
+ `itertools.batched()` is called with `strict=True` or `strict=False`.
15
+
16
+ **Why:** Without it, a short last batch passes silently. The keyword exists from Python 3.13.
17
+
18
+ ```codelaw
19
+ native: py/batched-without-explicit-strict
20
+ ```
21
+
22
+ ## Examples
23
+
24
+ ### Python
25
+
26
+ #### Don't
27
+
28
+ ```py
29
+ import itertools
30
+
31
+ batches = itertools.batched(items, 2)
32
+ ```
33
+
34
+ #### Do
35
+
36
+ ```py
37
+ import itertools
38
+
39
+ batches = itertools.batched(items, 2, strict=True)
40
+ ```
41
+
42
+ #### Fixed
43
+
44
+ ```py
45
+ import itertools
46
+
47
+ batches = itertools.batched(items, 2, strict=False)
48
+ ```
49
+
50
+ ## Differences from the source
51
+
52
+ Ruff reports this without a fix; codelaw suggests `strict=False`, which keeps today's behaviour. The fix is `suggest` (`codelaw fix --unsafe`), not `auto`: the `target_version` option may be unset, and on Python before 3.13 the keyword raises `TypeError`.
@@ -0,0 +1,41 @@
1
+ ---
2
+ id: py/break-outside-loop
3
+ title: "`break` only inside a loop"
4
+ languages: [python]
5
+ fix: manual
6
+ fix_reason: "what should happen instead (return, raise) is the author's choice"
7
+ stability: preview
8
+ ported_from: [ruff/F701]
9
+ since: 0.1.0
10
+ ---
11
+
12
+ `break` appears only inside the body of a `for` or `while` loop in the same function.
13
+
14
+ **Why:** Outside a loop it is a `SyntaxError`.
15
+
16
+ ```codelaw
17
+ native: py/break-outside-loop
18
+ ```
19
+
20
+ ## How to fix
21
+
22
+ 1. Use `return` to leave a function, or move the `break` into the loop.
23
+
24
+ ## Examples
25
+
26
+ ### Python
27
+
28
+ #### Don't
29
+
30
+ ```py
31
+ def stop():
32
+ break
33
+ ```
34
+
35
+ #### Do
36
+
37
+ ```py
38
+ def stop(items):
39
+ for item in items:
40
+ break
41
+ ```
@@ -0,0 +1,55 @@
1
+ ---
2
+ id: py/cached-instance-method
3
+ title: "No `lru_cache` on methods"
4
+ languages: [python]
5
+ fix: manual
6
+ fix_reason: "where the cache should live is a design choice"
7
+ stability: preview
8
+ ported_from: [ruff/B019]
9
+ since: 0.1.0
10
+ ---
11
+
12
+ `functools.lru_cache` and `functools.cache` decorate functions and static methods, not instance methods.
13
+
14
+ **Why:** The cache keeps a reference to every `self` it sees, so instances are never freed.
15
+
16
+ ```codelaw
17
+ native: py/cached-instance-method
18
+ ```
19
+
20
+ ## How to fix
21
+
22
+ 1. Move the cached work into a module-level function or a `staticmethod`.
23
+ 2. Or keep a per-instance cache in an attribute.
24
+
25
+ ## Examples
26
+
27
+ ### Python
28
+
29
+ #### Don't
30
+
31
+ ```py
32
+ import functools
33
+
34
+
35
+ class Store:
36
+ @functools.lru_cache
37
+ def get(self, key):
38
+ return key.upper()
39
+ ```
40
+
41
+ #### Do
42
+
43
+ ```py
44
+ import functools
45
+
46
+
47
+ @functools.lru_cache
48
+ def normalise(key):
49
+ return key.upper()
50
+
51
+
52
+ class Store:
53
+ def get(self, key):
54
+ return normalise(key)
55
+ ```
@@ -0,0 +1,41 @@
1
+ ---
2
+ id: py/continue-outside-loop
3
+ title: "`continue` only inside a loop"
4
+ languages: [python]
5
+ fix: manual
6
+ fix_reason: "what should happen instead is the author's choice"
7
+ stability: preview
8
+ ported_from: [ruff/F702]
9
+ since: 0.1.0
10
+ ---
11
+
12
+ `continue` appears only inside the body of a `for` or `while` loop in the same function.
13
+
14
+ **Why:** Outside a loop it is a `SyntaxError`.
15
+
16
+ ```codelaw
17
+ native: py/continue-outside-loop
18
+ ```
19
+
20
+ ## How to fix
21
+
22
+ 1. Move the `continue` into the loop, or restructure the code.
23
+
24
+ ## Examples
25
+
26
+ ### Python
27
+
28
+ #### Don't
29
+
30
+ ```py
31
+ def skip():
32
+ continue
33
+ ```
34
+
35
+ #### Do
36
+
37
+ ```py
38
+ def skip(items):
39
+ for item in items:
40
+ continue
41
+ ```
@@ -0,0 +1,48 @@
1
+ ---
2
+ id: py/default-except-not-last
3
+ title: "A bare `except:` is the last handler"
4
+ languages: [python]
5
+ fix: manual
6
+ fix_reason: "reordering handlers changes which one runs"
7
+ stability: preview
8
+ ported_from: [ruff/F707]
9
+ since: 0.1.0
10
+ ---
11
+
12
+ In a `try` statement, a bare `except:` comes after every other handler.
13
+
14
+ **Why:** Handlers after a bare `except:` can never run, and Python rejects it as a `SyntaxError`.
15
+
16
+ ```codelaw
17
+ native: py/default-except-not-last
18
+ ```
19
+
20
+ ## How to fix
21
+
22
+ 1. Move the bare `except:` to the end.
23
+
24
+ ## Examples
25
+
26
+ ### Python
27
+
28
+ #### Don't
29
+
30
+ ```py
31
+ try:
32
+ run()
33
+ except:
34
+ log()
35
+ except ValueError:
36
+ retry()
37
+ ```
38
+
39
+ #### Do
40
+
41
+ ```py
42
+ try:
43
+ run()
44
+ except ValueError:
45
+ retry()
46
+ except:
47
+ log()
48
+ ```
@@ -0,0 +1,39 @@
1
+ ---
2
+ id: py/duplicate-dict-key-variable
3
+ title: "A dict literal doesn't repeat a variable key"
4
+ languages: [python]
5
+ fix: manual
6
+ fix_reason: "which value should win can't be known from the code"
7
+ stability: preview
8
+ ported_from: [ruff/F602]
9
+ since: 0.1.0
10
+ ---
11
+
12
+ A dict literal uses the same variable as a key at most once.
13
+
14
+ **Why:** Both entries have the same key at run time, so only the last value is kept.
15
+
16
+ ```codelaw
17
+ native: py/duplicate-dict-key-variable
18
+ ```
19
+
20
+ ## How to fix
21
+
22
+ 1. Decide which entry is meant.
23
+ 2. Remove the other one, or use a different key.
24
+
25
+ ## Examples
26
+
27
+ ### Python
28
+
29
+ #### Don't
30
+
31
+ ```py
32
+ scores = {player: 1, player: 2}
33
+ ```
34
+
35
+ #### Do
36
+
37
+ ```py
38
+ scores = {player: 1, rival: 2}
39
+ ```
@@ -0,0 +1,39 @@
1
+ ---
2
+ id: py/duplicate-dict-key
3
+ title: "A dict literal doesn't repeat a literal key"
4
+ languages: [python]
5
+ fix: manual
6
+ fix_reason: "which value should win can't be known from the code"
7
+ stability: preview
8
+ ported_from: [ruff/F601]
9
+ since: 0.1.0
10
+ ---
11
+
12
+ A dict literal names each literal key (`"a"`, `1`, `True`) once.
13
+
14
+ **Why:** Only the last value is kept, so the earlier one is silently lost.
15
+
16
+ ```codelaw
17
+ native: py/duplicate-dict-key
18
+ ```
19
+
20
+ ## How to fix
21
+
22
+ 1. Decide which value the key should have.
23
+ 2. Remove the other entry, or give it its own key.
24
+
25
+ ## Examples
26
+
27
+ ### Python
28
+
29
+ #### Don't
30
+
31
+ ```py
32
+ colors = {"red": 1, "red": 2}
33
+ ```
34
+
35
+ #### Do
36
+
37
+ ```py
38
+ colors = {"red": 1, "blue": 2}
39
+ ```
@@ -0,0 +1,43 @@
1
+ ---
2
+ id: py/duplicate-handler-exception
3
+ title: "No exception listed twice in one handler"
4
+ languages: [python]
5
+ fix: auto
6
+ stability: preview
7
+ ported_from: [ruff/B014]
8
+ since: 0.1.0
9
+ ---
10
+
11
+ An `except` tuple names each exception once.
12
+
13
+ **Why:** A repeated exception is redundant and often a typo for a different one.
14
+
15
+ ```codelaw
16
+ native: py/duplicate-handler-exception
17
+ ```
18
+
19
+ ## Examples
20
+
21
+ ### Python
22
+
23
+ #### Don't
24
+
25
+ ```py
26
+ try:
27
+ run()
28
+ except (ValueError, ValueError, TypeError):
29
+ retry()
30
+ ```
31
+
32
+ #### Do
33
+
34
+ ```py
35
+ try:
36
+ run()
37
+ except (ValueError, TypeError):
38
+ retry()
39
+ ```
40
+
41
+ ## Differences from the source
42
+
43
+ Exceptions are compared by name as written; aliases such as `IOError` and `OSError` are not merged.
@@ -0,0 +1,33 @@
1
+ ---
2
+ id: py/duplicate-set-value
3
+ title: "No duplicate items in a set literal"
4
+ languages: [python]
5
+ fix: auto
6
+ stability: preview
7
+ ported_from: [ruff/B033]
8
+ since: 0.1.0
9
+ ---
10
+
11
+ A set literal lists each literal value once.
12
+
13
+ **Why:** A repeated item is dropped silently and is often a typo for another value.
14
+
15
+ ```codelaw
16
+ native: py/duplicate-set-value
17
+ ```
18
+
19
+ ## Examples
20
+
21
+ ### Python
22
+
23
+ #### Don't
24
+
25
+ ```py
26
+ colors = {"red", "green", "red"}
27
+ ```
28
+
29
+ #### Do
30
+
31
+ ```py
32
+ colors = {"red", "green"}
33
+ ```
@@ -0,0 +1,47 @@
1
+ ---
2
+ id: py/duplicate-try-block-exception
3
+ title: "An exception is caught by one handler only"
4
+ languages: [python]
5
+ fix: manual
6
+ fix_reason: "which handler's code is meant to run needs a person"
7
+ stability: preview
8
+ ported_from: [ruff/B025]
9
+ since: 0.1.0
10
+ ---
11
+
12
+ Within one `try` statement, each exception class appears in only one `except` clause.
13
+
14
+ **Why:** Only the first matching handler runs, so the later one is dead code.
15
+
16
+ ```codelaw
17
+ native: py/duplicate-try-block-exception
18
+ ```
19
+
20
+ ## How to fix
21
+
22
+ 1. Merge the handlers' code into the first one, or remove the dead one.
23
+
24
+ ## Examples
25
+
26
+ ### Python
27
+
28
+ #### Don't
29
+
30
+ ```py
31
+ try:
32
+ run()
33
+ except ValueError:
34
+ retry()
35
+ except ValueError:
36
+ log()
37
+ ```
38
+
39
+ #### Do
40
+
41
+ ```py
42
+ try:
43
+ run()
44
+ except ValueError:
45
+ retry()
46
+ log()
47
+ ```
@@ -0,0 +1,55 @@
1
+ ---
2
+ id: py/empty-method-without-abstract-decorator
3
+ title: "Empty methods in an abstract base class are abstract"
4
+ languages: [python]
5
+ fix: manual
6
+ fix_reason: "whether the method is a required hook or an optional no-op is a design decision"
7
+ stability: preview
8
+ ported_from: [ruff/B027]
9
+ since: 0.1.0
10
+ ---
11
+
12
+ In a class that inherits from `ABC`, a method whose body is empty is marked `@abstractmethod`.
13
+
14
+ **Why:** An empty, unmarked method lets subclasses forget to implement it.
15
+
16
+ ```codelaw
17
+ native: py/empty-method-without-abstract-decorator
18
+ ```
19
+
20
+ ## How to fix
21
+
22
+ 1. Add `@abstractmethod` if subclasses must implement it.
23
+ 2. Otherwise give it a docstring saying it is an optional hook.
24
+
25
+ ## Examples
26
+
27
+ ### Python
28
+
29
+ #### Don't
30
+
31
+ ```py
32
+ from abc import ABC, abstractmethod
33
+
34
+
35
+ class Shape(ABC):
36
+ @abstractmethod
37
+ def area(self): ...
38
+
39
+ def draw(self):
40
+ pass
41
+ ```
42
+
43
+ #### Do
44
+
45
+ ```py
46
+ from abc import ABC, abstractmethod
47
+
48
+
49
+ class Shape(ABC):
50
+ @abstractmethod
51
+ def area(self): ...
52
+
53
+ @abstractmethod
54
+ def draw(self): ...
55
+ ```
@@ -0,0 +1,44 @@
1
+ ---
2
+ id: py/except-with-empty-tuple
3
+ title: "No `except ():`"
4
+ languages: [python]
5
+ fix: manual
6
+ fix_reason: "the exceptions to catch are the author's choice"
7
+ stability: preview
8
+ ported_from: [ruff/B029]
9
+ since: 0.1.0
10
+ ---
11
+
12
+ An `except` clause doesn't name an empty tuple.
13
+
14
+ **Why:** `except ():` catches nothing, so the handler never runs.
15
+
16
+ ```codelaw
17
+ native: py/except-with-empty-tuple
18
+ ```
19
+
20
+ ## How to fix
21
+
22
+ 1. Name the exceptions to catch, or remove the handler.
23
+
24
+ ## Examples
25
+
26
+ ### Python
27
+
28
+ #### Don't
29
+
30
+ ```py
31
+ try:
32
+ run()
33
+ except ():
34
+ log()
35
+ ```
36
+
37
+ #### Do
38
+
39
+ ```py
40
+ try:
41
+ run()
42
+ except ValueError:
43
+ log()
44
+ ```
@@ -0,0 +1,44 @@
1
+ ---
2
+ id: py/except-with-non-exception-classes
3
+ title: "`except` names exception classes"
4
+ languages: [python]
5
+ fix: manual
6
+ fix_reason: "the exception class to catch is the author's choice"
7
+ stability: preview
8
+ ported_from: [ruff/B030]
9
+ since: 0.1.0
10
+ ---
11
+
12
+ An `except` clause names exception classes (or tuples of them), not literals or other values.
13
+
14
+ **Why:** Catching anything else raises `TypeError` the moment an exception reaches the handler.
15
+
16
+ ```codelaw
17
+ native: py/except-with-non-exception-classes
18
+ ```
19
+
20
+ ## How to fix
21
+
22
+ 1. Replace the value with the exception class you meant to catch.
23
+
24
+ ## Examples
25
+
26
+ ### Python
27
+
28
+ #### Don't
29
+
30
+ ```py
31
+ try:
32
+ run()
33
+ except "error":
34
+ log()
35
+ ```
36
+
37
+ #### Do
38
+
39
+ ```py
40
+ try:
41
+ run()
42
+ except RuntimeError:
43
+ log()
44
+ ```
@@ -0,0 +1,40 @@
1
+ ---
2
+ id: py/f-string-docstring
3
+ title: "Docstrings aren't f-strings"
4
+ languages: [python]
5
+ fix: manual
6
+ fix_reason: "the docstring's wording has to change to drop the placeholder"
7
+ stability: preview
8
+ ported_from: [ruff/B021]
9
+ since: 0.1.0
10
+ ---
11
+
12
+ The first statement of a module, class or function isn't an f-string meant as its docstring.
13
+
14
+ **Why:** Python treats an f-string there as an expression, so `__doc__` stays empty.
15
+
16
+ ```codelaw
17
+ native: py/f-string-docstring
18
+ ```
19
+
20
+ ## How to fix
21
+
22
+ 1. Remove the `f` prefix and write the text without placeholders.
23
+
24
+ ## Examples
25
+
26
+ ### Python
27
+
28
+ #### Don't
29
+
30
+ ```py
31
+ def greet():
32
+ f"""Say hello to {NAME}."""
33
+ ```
34
+
35
+ #### Do
36
+
37
+ ```py
38
+ def greet():
39
+ """Say hello to the configured name."""
40
+ ```
@@ -0,0 +1,49 @@
1
+ # py/f-string-missing-placeholders — cases
2
+
3
+ ### case: escaped braces become plain braces
4
+
5
+ ```py
6
+ x = f"{{literal}}"
7
+ ```
8
+
9
+ ```expect
10
+ 1:5 py/f-string-missing-placeholders
11
+ ```
12
+
13
+ ```fixed
14
+ x = "{literal}"
15
+ ```
16
+
17
+ ### case: one part of a concatenation has a placeholder
18
+
19
+ ```py
20
+ x = f"a" f"{b}"
21
+ ```
22
+
23
+ ```expect
24
+ (none)
25
+ ```
26
+
27
+ ### case: all parts lack placeholders
28
+
29
+ ```py
30
+ x = (f"a" "b")
31
+ ```
32
+
33
+ ```expect
34
+ 1:6 py/f-string-missing-placeholders
35
+ ```
36
+
37
+ ```fixed
38
+ x = ("a" "b")
39
+ ```
40
+
41
+ ### case: nested f-string in a format spec
42
+
43
+ ```py
44
+ x = f"{v:{w}}"
45
+ ```
46
+
47
+ ```expect
48
+ (none)
49
+ ```
@@ -0,0 +1,33 @@
1
+ ---
2
+ id: py/f-string-missing-placeholders
3
+ title: "An f-string has a placeholder"
4
+ languages: [python]
5
+ fix: auto
6
+ stability: preview
7
+ ported_from: [ruff/F541]
8
+ since: 0.1.0
9
+ ---
10
+
11
+ A string with the `f` prefix contains at least one `{…}` placeholder.
12
+
13
+ **Why:** Without one, the prefix does nothing and often means a placeholder was forgotten.
14
+
15
+ ```codelaw
16
+ native: py/f-string-missing-placeholders
17
+ ```
18
+
19
+ ## Examples
20
+
21
+ ### Python
22
+
23
+ #### Don't
24
+
25
+ ```py
26
+ print(f"hello")
27
+ ```
28
+
29
+ #### Do
30
+
31
+ ```py
32
+ print("hello")
33
+ ```