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,48 @@
1
+ # py/lambda-assignment — cases
2
+
3
+ ### case: in a class body
4
+
5
+ ```py
6
+ class C:
7
+ m = lambda self: 1
8
+ ```
9
+
10
+ ```expect
11
+ 2:5 py/lambda-assignment
12
+ ```
13
+
14
+ ```fixed
15
+ class C:
16
+ def m(self):
17
+ return 1
18
+ ```
19
+
20
+ ### case: tuple targets are fine
21
+
22
+ ```py
23
+ a, b = lambda: 0, 1
24
+ ```
25
+
26
+ ```expect
27
+ (none)
28
+ ```
29
+
30
+ ### case: lambda as an argument
31
+
32
+ ```py
33
+ items.sort(key=lambda x: x[1])
34
+ ```
35
+
36
+ ```expect
37
+ (none)
38
+ ```
39
+
40
+ ### case: attribute targets are fine
41
+
42
+ ```py
43
+ obj.handler = lambda: None
44
+ ```
45
+
46
+ ```expect
47
+ (none)
48
+ ```
@@ -0,0 +1,35 @@
1
+ ---
2
+ id: py/lambda-assignment
3
+ title: "Define functions with `def`, not a lambda"
4
+ languages: [python]
5
+ fix: suggest
6
+ fix_reason: "an annotation on the assigned name is dropped by the rewrite"
7
+ stability: preview
8
+ ported_from: [ruff/E731]
9
+ since: 0.1.0
10
+ ---
11
+
12
+ A name is bound to a function with `def`, not by assigning a `lambda`.
13
+
14
+ **Why:** A `def` has a real name in tracebacks, can have a docstring, and reads as a definition.
15
+
16
+ ```codelaw
17
+ native: py/lambda-assignment
18
+ ```
19
+
20
+ ## Examples
21
+
22
+ ### Python
23
+
24
+ #### Don't
25
+
26
+ ```py
27
+ double = lambda x: x * 2
28
+ ```
29
+
30
+ #### Do
31
+
32
+ ```py
33
+ def double(x):
34
+ return x * 2
35
+ ```
@@ -0,0 +1,40 @@
1
+ ---
2
+ id: py/late-future-import
3
+ title: "`from __future__` imports come first"
4
+ languages: [python]
5
+ fix: manual
6
+ fix_reason: "moving statements above it can change the order the module runs in"
7
+ stability: preview
8
+ ported_from: [ruff/F404]
9
+ since: 0.1.0
10
+ ---
11
+
12
+ `from __future__ import …` statements come before any other statement except the module docstring.
13
+
14
+ **Why:** Python rejects a late `__future__` import with a `SyntaxError`.
15
+
16
+ ```codelaw
17
+ native: py/late-future-import
18
+ ```
19
+
20
+ ## How to fix
21
+
22
+ 1. Move the `from __future__` import to the top, after the docstring.
23
+
24
+ ## Examples
25
+
26
+ ### Python
27
+
28
+ #### Don't
29
+
30
+ ```py
31
+ import os
32
+ from __future__ import annotations
33
+ ```
34
+
35
+ #### Do
36
+
37
+ ```py
38
+ from __future__ import annotations
39
+ import os
40
+ ```
@@ -0,0 +1,41 @@
1
+ ---
2
+ id: py/loop-variable-overrides-iterator
3
+ title: "A loop variable doesn't replace what it iterates"
4
+ languages: [python]
5
+ fix: manual
6
+ fix_reason: "the new name is the author's choice"
7
+ stability: preview
8
+ ported_from: [ruff/B020]
9
+ since: 0.1.0
10
+ ---
11
+
12
+ A `for` loop's target doesn't reuse a name read in the iterable expression.
13
+
14
+ **Why:** After the loop the name holds the last item, not the collection, which breaks later uses.
15
+
16
+ ```codelaw
17
+ native: py/loop-variable-overrides-iterator
18
+ ```
19
+
20
+ ## How to fix
21
+
22
+ 1. Give the loop variable its own name.
23
+ 2. Update its uses in the loop body.
24
+
25
+ ## Examples
26
+
27
+ ### Python
28
+
29
+ #### Don't
30
+
31
+ ```py
32
+ for items in items:
33
+ print(items)
34
+ ```
35
+
36
+ #### Do
37
+
38
+ ```py
39
+ for item in items:
40
+ print(item)
41
+ ```
@@ -0,0 +1,46 @@
1
+ ---
2
+ id: py/map-without-explicit-strict
3
+ title: "`map()` over several iterables says `strict=`"
4
+ languages: [python]
5
+ fix: suggest
6
+ fix_reason: "`strict=` exists only from Python 3.14, 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.14 the rule is off" }
10
+ ported_from: [ruff/B912]
11
+ since: 0.1.0
12
+ ---
13
+
14
+ `map()` with two or more iterables is called with `strict=True` or `strict=False`.
15
+
16
+ **Why:** Without it, `map` silently stops at the shortest input. The keyword exists from Python 3.14.
17
+
18
+ ```codelaw
19
+ native: py/map-without-explicit-strict
20
+ ```
21
+
22
+ ## Examples
23
+
24
+ ### Python
25
+
26
+ #### Don't
27
+
28
+ ```py
29
+ sums = map(add, xs, ys)
30
+ ```
31
+
32
+ #### Do
33
+
34
+ ```py
35
+ sums = map(add, xs, ys, strict=True)
36
+ ```
37
+
38
+ #### Fixed
39
+
40
+ ```py
41
+ sums = map(add, xs, ys, strict=False)
42
+ ```
43
+
44
+ ## Differences from the source
45
+
46
+ The fix is `suggest` (`codelaw fix --unsafe`), not `auto`: the `target_version` option may be unset, and on Python before 3.14 the keyword raises `TypeError`.
@@ -0,0 +1,64 @@
1
+ # py/module-import-not-at-top — cases
2
+
3
+ ### case: sys.path change before imports
4
+
5
+ ```py
6
+ import sys
7
+ sys.path.insert(0, 'lib')
8
+ import local_module
9
+ ```
10
+
11
+ ```expect
12
+ (none)
13
+ ```
14
+
15
+ ### case: os.environ change before imports
16
+
17
+ ```py
18
+ import os
19
+ os.environ['X'] = '1'
20
+ import other
21
+ ```
22
+
23
+ ```expect
24
+ (none)
25
+ ```
26
+
27
+ ### case: conditional imports
28
+
29
+ ```py
30
+ try:
31
+ import ujson as json
32
+ except ImportError:
33
+ import json
34
+ import os
35
+ ```
36
+
37
+ ```expect
38
+ (none)
39
+ ```
40
+
41
+ ### case: dunders and docstring
42
+
43
+ ```py
44
+ """Doc."""
45
+ __all__ = []
46
+ __version__ = '1'
47
+ import os
48
+ ```
49
+
50
+ ```expect
51
+ (none)
52
+ ```
53
+
54
+ ### case: imports inside functions are fine
55
+
56
+ ```py
57
+ x = 1
58
+ def f():
59
+ import os
60
+ ```
61
+
62
+ ```expect
63
+ (none)
64
+ ```
@@ -0,0 +1,46 @@
1
+ ---
2
+ id: py/module-import-not-at-top
3
+ title: "Module-level imports come first"
4
+ languages: [python]
5
+ fix: manual
6
+ fix_reason: "moving an import above other code changes the order the module runs in"
7
+ stability: preview
8
+ ported_from: [ruff/E402]
9
+ since: 0.1.0
10
+ ---
11
+
12
+ Module-level imports come before other code. A docstring, `__future__` imports, dunder assignments (`__all__`, `__version__`), `if`/`try`/`with` blocks and changes to `sys.path` or `os.environ` may come before them.
13
+
14
+ **Why:** Imports scattered through a module hide its dependencies, and code that runs before them can change what they load.
15
+
16
+ ```codelaw
17
+ native: py/module-import-not-at-top
18
+ ```
19
+
20
+ ## How to fix
21
+
22
+ 1. Move the import up to the module's import section.
23
+ 2. If it must run after the code above it, keep it and suppress the finding with a comment saying why.
24
+
25
+ ## Examples
26
+
27
+ ### Python
28
+
29
+ #### Don't
30
+
31
+ ```py
32
+ import os
33
+
34
+ print(os.name)
35
+
36
+ import sys
37
+ ```
38
+
39
+ #### Do
40
+
41
+ ```py
42
+ import os
43
+ import sys
44
+
45
+ print(os.name)
46
+ ```
@@ -0,0 +1,38 @@
1
+ ---
2
+ id: py/multiple-imports-on-one-line
3
+ title: "One module per `import` statement"
4
+ languages: [python]
5
+ fix: auto
6
+ stability: preview
7
+ ported_from: [ruff/E401]
8
+ since: 0.1.0
9
+ ---
10
+
11
+ Each `import` statement imports one module; `import os, sys` is split into one line per module.
12
+
13
+ **Why:** One import per line keeps diffs and merges clean and lets import sorting work line by line.
14
+
15
+ ```codelaw
16
+ native: py/multiple-imports-on-one-line
17
+ ```
18
+
19
+ ## Examples
20
+
21
+ ### Python
22
+
23
+ #### Don't
24
+
25
+ ```py
26
+ import os, sys
27
+ ```
28
+
29
+ #### Do
30
+
31
+ ```py
32
+ import os
33
+ import sys
34
+ ```
35
+
36
+ ## Differences from the source
37
+
38
+ `from module import a, b` is not affected; it imports from one module.
@@ -0,0 +1,38 @@
1
+ ---
2
+ id: py/multiple-starred-expressions
3
+ title: "One starred target per assignment"
4
+ languages: [python]
5
+ fix: manual
6
+ fix_reason: "which target should collect the rest has to be chosen"
7
+ stability: preview
8
+ ported_from: [ruff/F622]
9
+ since: 0.1.0
10
+ ---
11
+
12
+ An unpacking assignment has at most one starred target.
13
+
14
+ **Why:** Two starred targets are ambiguous and a `SyntaxError`.
15
+
16
+ ```codelaw
17
+ native: py/multiple-starred-expressions
18
+ ```
19
+
20
+ ## How to fix
21
+
22
+ 1. Keep the star on one target only.
23
+
24
+ ## Examples
25
+
26
+ ### Python
27
+
28
+ #### Don't
29
+
30
+ ```py
31
+ *head, *tail = values
32
+ ```
33
+
34
+ #### Do
35
+
36
+ ```py
37
+ head, *tail = values
38
+ ```
@@ -0,0 +1,42 @@
1
+ ---
2
+ id: py/mutable-contextvar-default
3
+ title: "No mutable `ContextVar` defaults"
4
+ languages: [python]
5
+ fix: manual
6
+ fix_reason: "whether to use an immutable value or set a fresh one per context is a design choice"
7
+ stability: preview
8
+ ported_from: [ruff/B039]
9
+ since: 0.1.0
10
+ ---
11
+
12
+ A `ContextVar`'s `default=` isn't a list, dict, set or the result of a call.
13
+
14
+ **Why:** The default is one object shared by every context, so mutating it leaks state between them.
15
+
16
+ ```codelaw
17
+ native: py/mutable-contextvar-default
18
+ ```
19
+
20
+ ## How to fix
21
+
22
+ 1. Use an immutable default, or no default and set a fresh value in each context.
23
+
24
+ ## Examples
25
+
26
+ ### Python
27
+
28
+ #### Don't
29
+
30
+ ```py
31
+ from contextvars import ContextVar
32
+
33
+ items = ContextVar("items", default=[])
34
+ ```
35
+
36
+ #### Do
37
+
38
+ ```py
39
+ from contextvars import ContextVar
40
+
41
+ items = ContextVar("items", default=())
42
+ ```
@@ -0,0 +1,42 @@
1
+ ---
2
+ id: py/nested-star-import
3
+ title: "`import *` only at module level"
4
+ languages: [python]
5
+ fix: manual
6
+ fix_reason: "the names to import must be chosen by reading the function"
7
+ stability: preview
8
+ ported_from: [ruff/F406]
9
+ since: 0.1.0
10
+ ---
11
+
12
+ `from module import *` appears only at the top level of a module.
13
+
14
+ **Why:** Inside a function or class a star import is a `SyntaxError` in Python 3.
15
+
16
+ ```codelaw
17
+ native: py/nested-star-import
18
+ ```
19
+
20
+ ## How to fix
21
+
22
+ 1. Replace the star with the names the function uses.
23
+
24
+ ## Examples
25
+
26
+ ### Python
27
+
28
+ #### Don't
29
+
30
+ ```py
31
+ def paths():
32
+ from os.path import *
33
+ return join("a", "b")
34
+ ```
35
+
36
+ #### Do
37
+
38
+ ```py
39
+ def paths():
40
+ from os.path import join
41
+ return join("a", "b")
42
+ ```
@@ -0,0 +1,37 @@
1
+ # py/no-bare-except — cases
2
+
3
+ ### case: a handler that re-raises
4
+
5
+ ```py
6
+ try:
7
+ run()
8
+ except:
9
+ cleanup()
10
+ raise
11
+ ```
12
+
13
+ ```expect
14
+ (none)
15
+ ```
16
+
17
+ ### case: a conditional re-raise still swallows
18
+
19
+ ```py
20
+ try:
21
+ run()
22
+ except:
23
+ if strict:
24
+ raise
25
+ ```
26
+
27
+ ```expect
28
+ 3:1 py/no-bare-except
29
+ ```
30
+
31
+ ```fixed
32
+ try:
33
+ run()
34
+ except Exception:
35
+ if strict:
36
+ raise
37
+ ```
@@ -0,0 +1,44 @@
1
+ ---
2
+ id: py/no-bare-except
3
+ title: "No bare `except:`"
4
+ languages: [python]
5
+ fix: suggest
6
+ fix_reason: "`except Exception:` no longer catches `KeyboardInterrupt` and `SystemExit`"
7
+ stability: preview
8
+ ported_from: [ruff/E722]
9
+ since: 0.1.0
10
+ ---
11
+
12
+ Every `except` clause names the exceptions it handles.
13
+
14
+ **Why:** A bare `except:` also catches `KeyboardInterrupt` and `SystemExit`, so Ctrl-C and `sys.exit()` stop working inside it.
15
+
16
+ ```codelaw
17
+ native: py/no-bare-except
18
+ ```
19
+
20
+ ## Examples
21
+
22
+ ### Python
23
+
24
+ #### Don't
25
+
26
+ ```py
27
+ try:
28
+ run()
29
+ except:
30
+ log()
31
+ ```
32
+
33
+ #### Do
34
+
35
+ ```py
36
+ try:
37
+ run()
38
+ except Exception:
39
+ log()
40
+ ```
41
+
42
+ ## Differences from the source
43
+
44
+ As in Ruff, a handler whose body re-raises with a bare `raise` is allowed: it doesn't swallow anything.
@@ -0,0 +1,38 @@
1
+ ---
2
+ id: py/no-explicit-stacklevel
3
+ title: "`warnings.warn` sets `stacklevel`"
4
+ languages: [python]
5
+ fix: suggest
6
+ fix_reason: "`stacklevel=2` is right for a warning about the caller; deeper helpers need a higher value"
7
+ stability: preview
8
+ ported_from: [ruff/B028]
9
+ since: 0.1.0
10
+ ---
11
+
12
+ `warnings.warn()` is called with `stacklevel=` (or `skip_file_prefixes=`).
13
+
14
+ **Why:** The default `stacklevel=1` points the warning at the line inside your library, not at the caller who needs to change.
15
+
16
+ ```codelaw
17
+ native: py/no-explicit-stacklevel
18
+ ```
19
+
20
+ ## Examples
21
+
22
+ ### Python
23
+
24
+ #### Don't
25
+
26
+ ```py
27
+ import warnings
28
+
29
+ warnings.warn("old API")
30
+ ```
31
+
32
+ #### Do
33
+
34
+ ```py
35
+ import warnings
36
+
37
+ warnings.warn("old API", stacklevel=2)
38
+ ```
@@ -0,0 +1,58 @@
1
+ # py/no-mutable-default — cases
2
+
3
+ ### case: after a docstring
4
+
5
+ ```py
6
+ def f(x={}):
7
+ """Doc."""
8
+ return x
9
+ ```
10
+
11
+ ```expect
12
+ 1:9 py/no-mutable-default
13
+ ```
14
+
15
+ ```fixed
16
+ def f(x=None):
17
+ """Doc."""
18
+ if x is None:
19
+ x = {}
20
+ return x
21
+ ```
22
+
23
+ ### case: constructor calls
24
+
25
+ ```py
26
+ def f(a=set(), b=dict(k=1), c=list()):
27
+ pass
28
+ ```
29
+
30
+ ```expect
31
+ 1:9 py/no-mutable-default
32
+ 1:18 py/no-mutable-default
33
+ 1:31 py/no-mutable-default
34
+ ```
35
+
36
+ ### case: a default that reads the outer name: no edit
37
+
38
+ ```py
39
+ def outer(lnum):
40
+ def reader(lnum=[lnum]):
41
+ return lnum
42
+ return reader
43
+ ```
44
+
45
+ ```expect
46
+ 2:21 py/no-mutable-default
47
+ ```
48
+
49
+ ### case: immutable defaults
50
+
51
+ ```py
52
+ def f(a=(), b=frozenset(), c=None, d=""):
53
+ pass
54
+ ```
55
+
56
+ ```expect
57
+ (none)
58
+ ```
@@ -0,0 +1,44 @@
1
+ ---
2
+ id: py/no-mutable-default
3
+ title: "No mutable default arguments"
4
+ languages: [python]
5
+ fix: suggest
6
+ fix_reason: "callers that relied on the shared object, or passed `None` on purpose, behave differently"
7
+ stability: preview
8
+ ported_from: [ruff/B006]
9
+ since: 0.1.0
10
+ ---
11
+
12
+ A parameter's default isn't a list, dict or set (literal, comprehension or constructor call).
13
+
14
+ **Why:** The default is created once, when the function is defined, and every call shares and mutates the same object.
15
+
16
+ ```codelaw
17
+ native: py/no-mutable-default
18
+ ```
19
+
20
+ ## Examples
21
+
22
+ ### Python
23
+
24
+ #### Don't
25
+
26
+ ```py
27
+ def append(item, items=[]):
28
+ items.append(item)
29
+ return items
30
+ ```
31
+
32
+ #### Do
33
+
34
+ ```py
35
+ def append(item, items=None):
36
+ if items is None:
37
+ items = []
38
+ items.append(item)
39
+ return items
40
+ ```
41
+
42
+ ## Differences from the source
43
+
44
+ The fix is offered only for unannotated parameters whose default doesn't read the parameter's own name; Ruff also rewrites an annotation to `T | None`.