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,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
+ ```