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,23 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Arpit Arya
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
22
+
23
+ SPDX-License-Identifier: MIT
@@ -0,0 +1,14 @@
1
+ codelaw-rules-python
2
+
3
+ data/python/stdlib.json lists the top-level module names of the Python
4
+ standard library: the union of `sys.stdlib_module_names` from CPython 3.10,
5
+ 3.11, 3.12, 3.13 and 3.14 (https://www.python.org), released under the Python
6
+ Software Foundation License Version 2 (PSF-2.0). Copyright (c) 2001 Python
7
+ Software Foundation; All Rights Reserved.
8
+
9
+ The rules are reimplementations written for codelaw. Their `ported_from:`
10
+ fields name the Ruff rules (https://docs.astral.sh/ruff/, version 0.17.1, MIT
11
+ License, Copyright (c) 2022 Charles Marsh) whose documented behaviour they
12
+ reproduce; Ruff's rules in turn reimplement Pyflakes (MIT), pycodestyle
13
+ (MIT/Expat), flake8-bugbear (MIT) and isort (MIT). No code or documentation
14
+ text from any of them is included.
@@ -0,0 +1,189 @@
1
+ # codelaw-rules-python
2
+
3
+ codelaw's rules for Python — one of the first-party rule packs
4
+ ([SR-RULE-PACKS](../../records/0101_rule-packs.md)). It ships on
5
+ PyPI only, as `codelaw-rules-python`; npm has no copy (L6 decision 11).
6
+
7
+ ```toml
8
+ # codelaw.toml
9
+ extends = ["codelaw:py/recommended"]
10
+ preview = true # every rule is `preview` until a corpus run promotes it
11
+ ```
12
+
13
+ ## Presets
14
+
15
+ <!-- docs-sync:presets -->
16
+
17
+ | preset | rules | ported from |
18
+ |---|---|---|
19
+ | `py/recommended` | 96 | Ruff 0.17.1: its long-standing default `E4`, `E7`, `E9`, `F` (55 of 59 rules; the other four are below), all 39 stable flake8-bugbear `B` rules, isort's `I001` and `RUF022` |
20
+ | `py/conventions` | 2 | opt-in house conventions from the starter catalogue: `py/no-print`, `py/no-tunable-literals` |
21
+ | `py/project` | 1 | `graph/no-unused-exports`, the rule that reads the whole repo, from vulture's unused-function check — opt in |
22
+
23
+ <!-- /docs-sync:presets -->
24
+
25
+ The starter catalogue's other Python rules match a Ruff rule, so they sit in
26
+ `py/recommended` under their starter ids: `py/no-bare-except` (`E722`),
27
+ `py/no-mutable-default` (`B006`) and `py/all-sorted` (`RUF022`).
28
+
29
+ Every rule is **reimplemented**, not vendored: the matchers, messages and docs
30
+ are written fresh from Ruff's documented behaviour, and each rule's
31
+ `ported_from:` names the source rule. 97 rules are `native:` (Rust, in
32
+ `crates/codelaw-native/src/python_*.rs`); `py/no-tunable-literals` is a
33
+ `match:` rule.
34
+
35
+ ## Ported
36
+
37
+ | Ruff | rule | fix |
38
+ |---|---|---|
39
+ | `E401` | `py/multiple-imports-on-one-line` | auto |
40
+ | `E402` | `py/module-import-not-at-top` | manual |
41
+ | `E711` | `py/none-comparison` | suggest |
42
+ | `E712` | `py/true-false-comparison` | suggest |
43
+ | `E713` | `py/not-in-test` | auto |
44
+ | `E714` | `py/not-is-test` | auto |
45
+ | `E721` | `py/type-comparison` | manual |
46
+ | `E722` | `py/no-bare-except` | suggest |
47
+ | `E731` | `py/lambda-assignment` | suggest |
48
+ | `E741` | `py/ambiguous-variable-name` | manual |
49
+ | `E742` | `py/ambiguous-class-name` | manual |
50
+ | `E743` | `py/ambiguous-function-name` | manual |
51
+ | `F401` | `py/unused-import` | suggest |
52
+ | `F402` | `py/import-shadowed-by-loop-var` | manual |
53
+ | `F403` | `py/no-star-import` | manual |
54
+ | `F404` | `py/late-future-import` | manual |
55
+ | `F405` | `py/star-import-usage` | manual |
56
+ | `F406` | `py/nested-star-import` | manual |
57
+ | `F407` | `py/undefined-future-feature` | manual |
58
+ | `F501` | `py/percent-format-invalid` | manual |
59
+ | `F502` | `py/percent-format-expected-mapping` | manual |
60
+ | `F503` | `py/percent-format-expected-sequence` | manual |
61
+ | `F504` | `py/percent-format-unused-named-argument` | auto |
62
+ | `F505` | `py/percent-format-missing-argument` | manual |
63
+ | `F506` | `py/percent-format-mixed-placeholders` | manual |
64
+ | `F507` | `py/percent-format-count-mismatch` | manual |
65
+ | `F508` | `py/percent-format-star-requires-sequence` | manual |
66
+ | `F509` | `py/percent-format-unsupported-character` | manual |
67
+ | `F521` | `py/format-invalid` | manual |
68
+ | `F522` | `py/format-unused-named-argument` | auto |
69
+ | `F523` | `py/format-unused-positional-argument` | auto |
70
+ | `F524` | `py/format-missing-argument` | manual |
71
+ | `F525` | `py/format-mixed-numbering` | manual |
72
+ | `F541` | `py/f-string-missing-placeholders` | auto |
73
+ | `F601` | `py/duplicate-dict-key` | manual |
74
+ | `F602` | `py/duplicate-dict-key-variable` | manual |
75
+ | `F621` | `py/too-many-expressions-in-star-assignment` | manual |
76
+ | `F622` | `py/multiple-starred-expressions` | manual |
77
+ | `F631` | `py/assert-tuple` | manual |
78
+ | `F632` | `py/is-literal` | suggest |
79
+ | `F633` | `py/invalid-print-syntax` | manual |
80
+ | `F634` | `py/if-tuple` | manual |
81
+ | `F701` | `py/break-outside-loop` | manual |
82
+ | `F702` | `py/continue-outside-loop` | manual |
83
+ | `F704` | `py/yield-outside-function` | manual |
84
+ | `F706` | `py/return-outside-function` | manual |
85
+ | `F707` | `py/default-except-not-last` | manual |
86
+ | `F722` | `py/forward-annotation-syntax-error` | manual |
87
+ | `F811` | `py/redefined-while-unused` | manual |
88
+ | `F821` | `py/undefined-name` | manual |
89
+ | `F822` | `py/undefined-export` | manual |
90
+ | `F823` | `py/undefined-local` | manual |
91
+ | `F841` | `py/unused-variable` | suggest |
92
+ | `F842` | `py/unused-annotation` | auto |
93
+ | `F901` | `py/raise-not-implemented` | suggest |
94
+ | `B002` | `py/unary-prefix-increment` | manual |
95
+ | `B003` | `py/assignment-to-os-environ` | manual |
96
+ | `B004` | `py/unreliable-callable-check` | suggest |
97
+ | `B005` | `py/strip-with-multi-characters` | manual |
98
+ | `B006` | `py/no-mutable-default` | suggest |
99
+ | `B007` | `py/unused-loop-control-variable` | suggest |
100
+ | `B008` | `py/function-call-in-default-argument` | manual |
101
+ | `B009` | `py/getattr-with-constant` | auto |
102
+ | `B010` | `py/setattr-with-constant` | auto |
103
+ | `B011` | `py/assert-false` | suggest |
104
+ | `B012` | `py/jump-statement-in-finally` | manual |
105
+ | `B013` | `py/redundant-tuple-in-exception-handler` | auto |
106
+ | `B014` | `py/duplicate-handler-exception` | auto |
107
+ | `B015` | `py/useless-comparison` | manual |
108
+ | `B016` | `py/raise-literal` | manual |
109
+ | `B017` | `py/assert-raises-exception` | manual |
110
+ | `B018` | `py/useless-expression` | manual |
111
+ | `B019` | `py/cached-instance-method` | manual |
112
+ | `B020` | `py/loop-variable-overrides-iterator` | manual |
113
+ | `B021` | `py/f-string-docstring` | manual |
114
+ | `B022` | `py/useless-contextlib-suppress` | manual |
115
+ | `B023` | `py/function-uses-loop-variable` | manual |
116
+ | `B024` | `py/abstract-base-class-without-abstract-method` | manual |
117
+ | `B025` | `py/duplicate-try-block-exception` | manual |
118
+ | `B026` | `py/star-arg-unpacking-after-keyword-arg` | manual |
119
+ | `B027` | `py/empty-method-without-abstract-decorator` | manual |
120
+ | `B028` | `py/no-explicit-stacklevel` | suggest |
121
+ | `B029` | `py/except-with-empty-tuple` | manual |
122
+ | `B030` | `py/except-with-non-exception-classes` | manual |
123
+ | `B031` | `py/reuse-of-groupby-generator` | manual |
124
+ | `B032` | `py/unintentional-type-annotation` | manual |
125
+ | `B033` | `py/duplicate-set-value` | auto |
126
+ | `B034` | `py/re-sub-positional-args` | manual |
127
+ | `B035` | `py/static-key-dict-comprehension` | manual |
128
+ | `B039` | `py/mutable-contextvar-default` | manual |
129
+ | `B904` | `py/raise-without-from-inside-except` | manual |
130
+ | `B905` | `py/zip-without-explicit-strict` | suggest |
131
+ | `B911` | `py/batched-without-explicit-strict` | suggest |
132
+ | `B912` | `py/map-without-explicit-strict` | suggest |
133
+ | `I001` | `py/unsorted-imports` | auto |
134
+ | `RUF022` | `py/all-sorted` | auto |
135
+
136
+ ## How the ports differ
137
+
138
+ Every rule matches Ruff's documented behaviour on Ruff's own examples. Where
139
+ codelaw differs, the rule's `## Differences from the source` section says so.
140
+ The ones that shape results most:
141
+
142
+ | area | difference |
143
+ |---|---|
144
+ | name resolution | The semantic model is per scope, not per statement: a name bound anywhere in a scope counts as bound everywhere in it, and `del` is not tracked. Ruff follows the order statements run in, so it reports a few more `F821`, `F405`, `F811` and `F401` findings in code that rebinds names conditionally. |
145
+ | `py/redefined-while-unused` | A read anywhere between two definitions counts as a use, even inside a function body that only runs later, and so does a mention in a string annotation; as in Ruff, a redefinition in a different branch (`if __name__ == "__main__":`) is not reported; a parameter shadowing a module-level import is not reported. |
146
+ | `py/unused-import` | Names in string annotations, and in strings inside typing generics (`Dict["Name", int]`), `cast()` and `TypeVar(bound=)`, count as uses; `# type:` comments don't, as in Ruff 0.17. Not reported for imports in a class body, `import a.b` loading a submodule of a package also imported under an alias, or importing `__all__` (Ruff reports that one). Imports inside a `try` body are reported without an edit. The fix is `suggest`, because an import can run code. |
147
+ | `py/unused-variable` | As in Ruff, unpacking is reported only from a tuple or list literal. Outside functions only an unused `except … as name` is reported. |
148
+ | `py/unsorted-imports` | Checks order only. Blank lines after the import block and line wrapping are the formatter's; `from m import a, b as c` and `import a, b` are not reported for their shape. First-party modules come from the `known_first_party` option. |
149
+ | `py/no-star-import` | Relative star imports (`from .sub import *`) are not reported: that is how a package re-exports its submodules. |
150
+ | `py/zip-without-explicit-strict`, `py/batched-without-explicit-strict`, `py/map-without-explicit-strict` | The Python version comes from each rule's `target_version` option, not a project setting; unset, the rules run. The fix adds `strict=False`, which keeps today's behaviour (Ruff has no fix for `B911`); it is `suggest`, not `auto`, because with the version unknown the keyword may not exist and would raise `TypeError` (W-17). |
151
+ | `py/no-mutable-default` | The fix is offered for unannotated parameters whose default doesn't read the parameter's own name. |
152
+ | `# noqa` | Ruff's suppression comments are not read; codelaw has its own. |
153
+
154
+ ## Not ported, and why
155
+
156
+ | rule | why |
157
+ |---|---|
158
+ | pycodestyle `E1`, `E2`, `E3`, `E5`, `W` | never: layout, which the formatter owns |
159
+ | `E701`, `E702`, `E703` | never: layout (statements per line, semicolons) |
160
+ | `E902` io-error | the engine reports files it can't read (`codelaw/not-utf8`, `codelaw/internal-error`) |
161
+ | `E999` syntax-error | removed upstream in Ruff 0.8.0 |
162
+ | `B043`, `B901`, `B903`, `B909` | preview-only in Ruff 0.17.1, not part of its stable `B` set |
163
+ | flake8-bugbear `B950` | never: line length is layout, and Ruff doesn't implement it |
164
+ | `I002` missing-required-import | needs the project's list of required imports; not part of `I001` |
165
+
166
+ The rest of Ruff 0.16's wider default (`UP`, `SIM`, `PL`, `C4`, …) is planned
167
+ for a later `py/strict` preset ([W-03](../../work/open/W-03-rule-packs.md)).
168
+
169
+ ## Data
170
+
171
+ `data/python/stdlib.json` — the standard library's top-level module names, the
172
+ union of CPython 3.10–3.14's `sys.stdlib_module_names`, so modules removed in
173
+ recent versions (`distutils`, `asynchat`, `imp`, …) still count as standard
174
+ library. `py/unsorted-imports` uses it to place imports in sections. See
175
+ `NOTICE`.
176
+
177
+ ## Corpus check
178
+
179
+ Each run checks every rule and compares it line by line with Ruff 0.17.1
180
+ running the same rules (`--select E4,E7,E9,F,B,I001,RUF022`).
181
+
182
+ | corpus | files | findings | rules where codelaw and Ruff differ, and why |
183
+ |---|---|---|---|
184
+ | CPython 3.9 standard library (`lib/python3.9`, no tests) | 420 | 1,526 | I001 (blank lines after imports, left to the formatter), F821/F405/F811/F401/F841 (Ruff follows statement order and `del`; codelaw doesn't report `from io import __all__`), F401/I001 `# noqa` lines, B028 (a `warnings` name with a `None` fallback), B012 (codelaw reports every `return` in a `finally`), B024 (`class ABC` inside `abc.py`), B912 (`target_version` unset) |
185
+ | pip 21.2.4 `pip/_internal` | 141 | 310 | F401: 12 lines, every one marked `# noqa`; B912 (`target_version` unset) |
186
+ | rich 15.0.0 (fully annotated) | 100 | 98 | I001: 5 blocks missing only a blank line between sections; F401 and B007: one `# noqa` line each |
187
+
188
+ Applying every `auto` and `suggest` fix to all three corpora changed 368 files,
189
+ broke none, and left no fixable finding behind.
@@ -0,0 +1,6 @@
1
+ name = "codelaw-rules-python"
2
+ version = "0.2.0"
3
+ min_codelaw = "0.1.0"
4
+ languages = ["python"]
5
+ presets = ["py/recommended", "py/conventions", "py/project"]
6
+ description = "codelaw's rules for Python; PyPI only (SR-RULE-PACKS)"
@@ -0,0 +1,103 @@
1
+ # `codelaw test --corpus` finding counts (SR-RULE-AUTHORING decision 4).
2
+ # Rewrite with --update-snapshot, and review the change.
3
+
4
+ [findings]
5
+ "graph/no-unused-exports" = 24
6
+ "py/abstract-base-class-without-abstract-method" = 1
7
+ "py/all-sorted" = 3
8
+ "py/ambiguous-class-name" = 0
9
+ "py/ambiguous-function-name" = 0
10
+ "py/ambiguous-variable-name" = 3
11
+ "py/assert-false" = 4
12
+ "py/assert-raises-exception" = 8
13
+ "py/assert-tuple" = 0
14
+ "py/assignment-to-os-environ" = 0
15
+ "py/batched-without-explicit-strict" = 0
16
+ "py/break-outside-loop" = 0
17
+ "py/cached-instance-method" = 7
18
+ "py/continue-outside-loop" = 0
19
+ "py/default-except-not-last" = 0
20
+ "py/duplicate-dict-key" = 0
21
+ "py/duplicate-dict-key-variable" = 0
22
+ "py/duplicate-handler-exception" = 0
23
+ "py/duplicate-set-value" = 0
24
+ "py/duplicate-try-block-exception" = 0
25
+ "py/empty-method-without-abstract-decorator" = 0
26
+ "py/except-with-empty-tuple" = 0
27
+ "py/except-with-non-exception-classes" = 0
28
+ "py/f-string-docstring" = 0
29
+ "py/f-string-missing-placeholders" = 2
30
+ "py/format-invalid" = 0
31
+ "py/format-missing-argument" = 0
32
+ "py/format-mixed-numbering" = 0
33
+ "py/format-unused-named-argument" = 0
34
+ "py/format-unused-positional-argument" = 0
35
+ "py/forward-annotation-syntax-error" = 7
36
+ "py/function-call-in-default-argument" = 4
37
+ "py/function-uses-loop-variable" = 5
38
+ "py/getattr-with-constant" = 2
39
+ "py/if-tuple" = 0
40
+ "py/import-shadowed-by-loop-var" = 0
41
+ "py/invalid-print-syntax" = 0
42
+ "py/is-literal" = 0
43
+ "py/jump-statement-in-finally" = 0
44
+ "py/lambda-assignment" = 0
45
+ "py/late-future-import" = 0
46
+ "py/loop-variable-overrides-iterator" = 0
47
+ "py/map-without-explicit-strict" = 0
48
+ "py/module-import-not-at-top" = 25
49
+ "py/multiple-imports-on-one-line" = 0
50
+ "py/multiple-starred-expressions" = 0
51
+ "py/mutable-contextvar-default" = 0
52
+ "py/nested-star-import" = 0
53
+ "py/no-bare-except" = 6
54
+ "py/no-explicit-stacklevel" = 9
55
+ "py/no-mutable-default" = 0
56
+ "py/no-print" = 0
57
+ "py/no-star-import" = 0
58
+ "py/no-tunable-literals" = 417
59
+ "py/none-comparison" = 10
60
+ "py/not-in-test" = 0
61
+ "py/not-is-test" = 0
62
+ "py/percent-format-count-mismatch" = 0
63
+ "py/percent-format-expected-mapping" = 0
64
+ "py/percent-format-expected-sequence" = 0
65
+ "py/percent-format-invalid" = 0
66
+ "py/percent-format-missing-argument" = 0
67
+ "py/percent-format-mixed-placeholders" = 0
68
+ "py/percent-format-star-requires-sequence" = 0
69
+ "py/percent-format-unsupported-character" = 0
70
+ "py/percent-format-unused-named-argument" = 0
71
+ "py/raise-literal" = 0
72
+ "py/raise-not-implemented" = 0
73
+ "py/raise-without-from-inside-except" = 44
74
+ "py/re-sub-positional-args" = 0
75
+ "py/redefined-while-unused" = 3
76
+ "py/redundant-tuple-in-exception-handler" = 0
77
+ "py/return-outside-function" = 0
78
+ "py/reuse-of-groupby-generator" = 0
79
+ "py/setattr-with-constant" = 1
80
+ "py/star-arg-unpacking-after-keyword-arg" = 0
81
+ "py/star-import-usage" = 109
82
+ "py/static-key-dict-comprehension" = 0
83
+ "py/strip-with-multi-characters" = 0
84
+ "py/too-many-expressions-in-star-assignment" = 0
85
+ "py/true-false-comparison" = 20
86
+ "py/type-comparison" = 1
87
+ "py/unary-prefix-increment" = 0
88
+ "py/undefined-export" = 0
89
+ "py/undefined-future-feature" = 0
90
+ "py/undefined-local" = 0
91
+ "py/undefined-name" = 5
92
+ "py/unintentional-type-annotation" = 0
93
+ "py/unreliable-callable-check" = 0
94
+ "py/unsorted-imports" = 180
95
+ "py/unused-annotation" = 0
96
+ "py/unused-import" = 18
97
+ "py/unused-loop-control-variable" = 21
98
+ "py/unused-variable" = 9
99
+ "py/useless-comparison" = 2
100
+ "py/useless-contextlib-suppress" = 0
101
+ "py/useless-expression" = 32
102
+ "py/yield-outside-function" = 0
103
+ "py/zip-without-explicit-strict" = 25
@@ -0,0 +1,331 @@
1
+ [
2
+ "__future__",
3
+ "_abc",
4
+ "_aix_support",
5
+ "_android_support",
6
+ "_apple_support",
7
+ "_ast",
8
+ "_ast_unparse",
9
+ "_asyncio",
10
+ "_bisect",
11
+ "_blake2",
12
+ "_bootsubprocess",
13
+ "_bz2",
14
+ "_codecs",
15
+ "_codecs_cn",
16
+ "_codecs_hk",
17
+ "_codecs_iso2022",
18
+ "_codecs_jp",
19
+ "_codecs_kr",
20
+ "_codecs_tw",
21
+ "_collections",
22
+ "_collections_abc",
23
+ "_colorize",
24
+ "_compat_pickle",
25
+ "_compression",
26
+ "_contextvars",
27
+ "_crypt",
28
+ "_csv",
29
+ "_ctypes",
30
+ "_curses",
31
+ "_curses_panel",
32
+ "_datetime",
33
+ "_dbm",
34
+ "_decimal",
35
+ "_elementtree",
36
+ "_frozen_importlib",
37
+ "_frozen_importlib_external",
38
+ "_functools",
39
+ "_gdbm",
40
+ "_hashlib",
41
+ "_heapq",
42
+ "_hmac",
43
+ "_imp",
44
+ "_interpchannels",
45
+ "_interpqueues",
46
+ "_interpreters",
47
+ "_io",
48
+ "_ios_support",
49
+ "_json",
50
+ "_locale",
51
+ "_lsprof",
52
+ "_lzma",
53
+ "_markupbase",
54
+ "_md5",
55
+ "_msi",
56
+ "_multibytecodec",
57
+ "_multiprocessing",
58
+ "_opcode",
59
+ "_opcode_metadata",
60
+ "_operator",
61
+ "_osx_support",
62
+ "_overlapped",
63
+ "_pickle",
64
+ "_posixshmem",
65
+ "_posixsubprocess",
66
+ "_py_abc",
67
+ "_py_warnings",
68
+ "_pydatetime",
69
+ "_pydecimal",
70
+ "_pyio",
71
+ "_pylong",
72
+ "_pyrepl",
73
+ "_queue",
74
+ "_random",
75
+ "_remote_debugging",
76
+ "_scproxy",
77
+ "_sha1",
78
+ "_sha2",
79
+ "_sha256",
80
+ "_sha3",
81
+ "_sha512",
82
+ "_signal",
83
+ "_sitebuiltins",
84
+ "_socket",
85
+ "_sqlite3",
86
+ "_sre",
87
+ "_ssl",
88
+ "_stat",
89
+ "_statistics",
90
+ "_string",
91
+ "_strptime",
92
+ "_struct",
93
+ "_suggestions",
94
+ "_symtable",
95
+ "_sysconfig",
96
+ "_thread",
97
+ "_threading_local",
98
+ "_tkinter",
99
+ "_tokenize",
100
+ "_tracemalloc",
101
+ "_types",
102
+ "_typing",
103
+ "_uuid",
104
+ "_warnings",
105
+ "_weakref",
106
+ "_weakrefset",
107
+ "_winapi",
108
+ "_wmi",
109
+ "_zoneinfo",
110
+ "_zstd",
111
+ "abc",
112
+ "aifc",
113
+ "annotationlib",
114
+ "antigravity",
115
+ "argparse",
116
+ "array",
117
+ "ast",
118
+ "asynchat",
119
+ "asyncio",
120
+ "asyncore",
121
+ "atexit",
122
+ "audioop",
123
+ "base64",
124
+ "bdb",
125
+ "binascii",
126
+ "binhex",
127
+ "bisect",
128
+ "builtins",
129
+ "bz2",
130
+ "cProfile",
131
+ "calendar",
132
+ "cgi",
133
+ "cgitb",
134
+ "chunk",
135
+ "cmath",
136
+ "cmd",
137
+ "code",
138
+ "codecs",
139
+ "codeop",
140
+ "collections",
141
+ "colorsys",
142
+ "compileall",
143
+ "compression",
144
+ "concurrent",
145
+ "configparser",
146
+ "contextlib",
147
+ "contextvars",
148
+ "copy",
149
+ "copyreg",
150
+ "crypt",
151
+ "csv",
152
+ "ctypes",
153
+ "curses",
154
+ "dataclasses",
155
+ "datetime",
156
+ "dbm",
157
+ "decimal",
158
+ "difflib",
159
+ "dis",
160
+ "distutils",
161
+ "doctest",
162
+ "email",
163
+ "encodings",
164
+ "ensurepip",
165
+ "enum",
166
+ "errno",
167
+ "faulthandler",
168
+ "fcntl",
169
+ "filecmp",
170
+ "fileinput",
171
+ "fnmatch",
172
+ "fractions",
173
+ "ftplib",
174
+ "functools",
175
+ "gc",
176
+ "genericpath",
177
+ "getopt",
178
+ "getpass",
179
+ "gettext",
180
+ "glob",
181
+ "graphlib",
182
+ "grp",
183
+ "gzip",
184
+ "hashlib",
185
+ "heapq",
186
+ "hmac",
187
+ "html",
188
+ "http",
189
+ "idlelib",
190
+ "imaplib",
191
+ "imghdr",
192
+ "imp",
193
+ "importlib",
194
+ "inspect",
195
+ "io",
196
+ "ipaddress",
197
+ "itertools",
198
+ "json",
199
+ "keyword",
200
+ "lib2to3",
201
+ "linecache",
202
+ "locale",
203
+ "logging",
204
+ "lzma",
205
+ "mailbox",
206
+ "mailcap",
207
+ "marshal",
208
+ "math",
209
+ "mimetypes",
210
+ "mmap",
211
+ "modulefinder",
212
+ "msilib",
213
+ "msvcrt",
214
+ "multiprocessing",
215
+ "netrc",
216
+ "nis",
217
+ "nntplib",
218
+ "nt",
219
+ "ntpath",
220
+ "nturl2path",
221
+ "numbers",
222
+ "opcode",
223
+ "operator",
224
+ "optparse",
225
+ "os",
226
+ "ossaudiodev",
227
+ "pathlib",
228
+ "pdb",
229
+ "pickle",
230
+ "pickletools",
231
+ "pipes",
232
+ "pkgutil",
233
+ "platform",
234
+ "plistlib",
235
+ "poplib",
236
+ "posix",
237
+ "posixpath",
238
+ "pprint",
239
+ "profile",
240
+ "pstats",
241
+ "pty",
242
+ "pwd",
243
+ "py_compile",
244
+ "pyclbr",
245
+ "pydoc",
246
+ "pydoc_data",
247
+ "pyexpat",
248
+ "queue",
249
+ "quopri",
250
+ "random",
251
+ "re",
252
+ "readline",
253
+ "reprlib",
254
+ "resource",
255
+ "rlcompleter",
256
+ "runpy",
257
+ "sched",
258
+ "secrets",
259
+ "select",
260
+ "selectors",
261
+ "shelve",
262
+ "shlex",
263
+ "shutil",
264
+ "signal",
265
+ "site",
266
+ "smtpd",
267
+ "smtplib",
268
+ "sndhdr",
269
+ "socket",
270
+ "socketserver",
271
+ "spwd",
272
+ "sqlite3",
273
+ "sre_compile",
274
+ "sre_constants",
275
+ "sre_parse",
276
+ "ssl",
277
+ "stat",
278
+ "statistics",
279
+ "string",
280
+ "stringprep",
281
+ "struct",
282
+ "subprocess",
283
+ "sunau",
284
+ "symtable",
285
+ "sys",
286
+ "sysconfig",
287
+ "syslog",
288
+ "tabnanny",
289
+ "tarfile",
290
+ "telnetlib",
291
+ "tempfile",
292
+ "termios",
293
+ "textwrap",
294
+ "this",
295
+ "threading",
296
+ "time",
297
+ "timeit",
298
+ "tkinter",
299
+ "token",
300
+ "tokenize",
301
+ "tomllib",
302
+ "trace",
303
+ "traceback",
304
+ "tracemalloc",
305
+ "tty",
306
+ "turtle",
307
+ "turtledemo",
308
+ "types",
309
+ "typing",
310
+ "unicodedata",
311
+ "unittest",
312
+ "urllib",
313
+ "uu",
314
+ "uuid",
315
+ "venv",
316
+ "warnings",
317
+ "wave",
318
+ "weakref",
319
+ "webbrowser",
320
+ "winreg",
321
+ "winsound",
322
+ "wsgiref",
323
+ "xdrlib",
324
+ "xml",
325
+ "xmlrpc",
326
+ "zipapp",
327
+ "zipfile",
328
+ "zipimport",
329
+ "zlib",
330
+ "zoneinfo"
331
+ ]
@@ -0,0 +1,5 @@
1
+ description = "House conventions from codelaw's starter catalogue that no upstream preset covers; opt-in"
2
+
3
+ [rules]
4
+ "py/no-print" = "warn"
5
+ "py/no-tunable-literals" = "warn"
@@ -0,0 +1,4 @@
1
+ description = "The rule that reads the whole repo: module-level names nothing imports (vulture); opt in"
2
+
3
+ [rules]
4
+ "graph/no-unused-exports" = "error"