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,38 @@
1
+ ---
2
+ id: py/star-arg-unpacking-after-keyword-arg
3
+ title: "No `*args` after a keyword argument"
4
+ languages: [python]
5
+ fix: manual
6
+ fix_reason: "reordering arguments can change which parameter each value binds to"
7
+ stability: preview
8
+ ported_from: [ruff/B026]
9
+ since: 0.1.0
10
+ ---
11
+
12
+ In a call, `*args` comes before keyword arguments.
13
+
14
+ **Why:** `f(x=1, *args)` fills positions before `x`, which reads backwards and can raise `TypeError` for a duplicate argument.
15
+
16
+ ```codelaw
17
+ native: py/star-arg-unpacking-after-keyword-arg
18
+ ```
19
+
20
+ ## How to fix
21
+
22
+ 1. Move `*args` before the keyword arguments and check the call still binds as intended.
23
+
24
+ ## Examples
25
+
26
+ ### Python
27
+
28
+ #### Don't
29
+
30
+ ```py
31
+ run(name="build", *steps)
32
+ ```
33
+
34
+ #### Do
35
+
36
+ ```py
37
+ run(*steps, name="build")
38
+ ```
@@ -0,0 +1,35 @@
1
+ # py/star-import-usage — cases
2
+
3
+ ### case: names in __all__
4
+
5
+ ```py
6
+ from os.path import *
7
+ __all__ = ["join", "local"]
8
+ local = 1
9
+ ```
10
+
11
+ ```expect
12
+ 2:12 py/star-import-usage
13
+ ```
14
+
15
+ ### case: submodule bound by a relative star import
16
+
17
+ ```py
18
+ from .events import *
19
+ print(events.__all__)
20
+ ```
21
+
22
+ ```expect
23
+ (none)
24
+ ```
25
+
26
+ ### case: builtins
27
+
28
+ ```py
29
+ from os.path import *
30
+ print(len([]))
31
+ ```
32
+
33
+ ```expect
34
+ (none)
35
+ ```
@@ -0,0 +1,43 @@
1
+ ---
2
+ id: py/star-import-usage
3
+ title: "No names that may only come from a star import"
4
+ languages: [python]
5
+ fix: manual
6
+ fix_reason: "the module the name comes from has to be confirmed by a person"
7
+ stability: preview
8
+ ported_from: [ruff/F405]
9
+ since: 0.1.0
10
+ ---
11
+
12
+ In a module with `from module import *`, every name the module reads is defined locally or imported by name.
13
+
14
+ **Why:** With a star import in play, a misspelled name and a name the star import provides look the same; neither can be checked.
15
+
16
+ ```codelaw
17
+ native: py/star-import-usage
18
+ ```
19
+
20
+ ## How to fix
21
+
22
+ 1. Find which star-imported module provides the name.
23
+ 2. Import it by name from that module.
24
+
25
+ ## Examples
26
+
27
+ ### Python
28
+
29
+ #### Don't
30
+
31
+ ```py
32
+ from os.path import *
33
+
34
+ print(join("a", "b"))
35
+ ```
36
+
37
+ #### Do
38
+
39
+ ```py
40
+ from os.path import join
41
+
42
+ print(join("a", "b"))
43
+ ```
@@ -0,0 +1,38 @@
1
+ ---
2
+ id: py/static-key-dict-comprehension
3
+ title: "A dict comprehension's key varies"
4
+ languages: [python]
5
+ fix: manual
6
+ fix_reason: "the intended key can't be inferred"
7
+ stability: preview
8
+ ported_from: [ruff/B035]
9
+ since: 0.1.0
10
+ ---
11
+
12
+ A dict comprehension's key depends on the loop variables.
13
+
14
+ **Why:** A constant key gives a dict with one entry, keeping only the last value.
15
+
16
+ ```codelaw
17
+ native: py/static-key-dict-comprehension
18
+ ```
19
+
20
+ ## How to fix
21
+
22
+ 1. Use a key that depends on the loop variable.
23
+
24
+ ## Examples
25
+
26
+ ### Python
27
+
28
+ #### Don't
29
+
30
+ ```py
31
+ sizes = {"size": len(name) for name in names}
32
+ ```
33
+
34
+ #### Do
35
+
36
+ ```py
37
+ sizes = {name: len(name) for name in names}
38
+ ```
@@ -0,0 +1,39 @@
1
+ ---
2
+ id: py/strip-with-multi-characters
3
+ title: "`.strip()` isn't given a word"
4
+ languages: [python]
5
+ fix: manual
6
+ fix_reason: "whether a character set or a suffix was meant needs a person"
7
+ stability: preview
8
+ ported_from: [ruff/B005]
9
+ since: 0.1.0
10
+ ---
11
+
12
+ `.strip()`, `.lstrip()` and `.rstrip()` aren't passed a multi-character string with repeated characters.
13
+
14
+ **Why:** They remove any of the given characters, not a prefix or suffix; `"note.txt".rstrip(".txt")` gives `"no"`.
15
+
16
+ ```codelaw
17
+ native: py/strip-with-multi-characters
18
+ ```
19
+
20
+ ## How to fix
21
+
22
+ 1. Use `.removeprefix()` / `.removesuffix()` to remove a fixed string.
23
+ 2. Keep `.strip()` only for a set of characters, written without repeats.
24
+
25
+ ## Examples
26
+
27
+ ### Python
28
+
29
+ #### Don't
30
+
31
+ ```py
32
+ name = filename.rstrip(".txt")
33
+ ```
34
+
35
+ #### Do
36
+
37
+ ```py
38
+ name = filename.removesuffix(".txt")
39
+ ```
@@ -0,0 +1,38 @@
1
+ ---
2
+ id: py/too-many-expressions-in-star-assignment
3
+ title: "A starred assignment stays within Python's limits"
4
+ languages: [python]
5
+ fix: manual
6
+ fix_reason: "how to restructure the unpacking is a design choice"
7
+ stability: preview
8
+ ported_from: [ruff/F621]
9
+ since: 0.1.0
10
+ ---
11
+
12
+ An unpacking assignment with a starred target has fewer than 256 targets before the star and fewer than 2^24 after it.
13
+
14
+ **Why:** Past those limits the assignment is a `SyntaxError`.
15
+
16
+ ```codelaw
17
+ native: py/too-many-expressions-in-star-assignment
18
+ ```
19
+
20
+ ## How to fix
21
+
22
+ 1. Unpack into a list and index it instead of naming every target.
23
+
24
+ ## Examples
25
+
26
+ ### Python
27
+
28
+ #### Don't
29
+
30
+ ```py
31
+ a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28, a29, a30, a31, a32, a33, a34, a35, a36, a37, a38, a39, a40, a41, a42, a43, a44, a45, a46, a47, a48, a49, a50, a51, a52, a53, a54, a55, a56, a57, a58, a59, a60, a61, a62, a63, a64, a65, a66, a67, a68, a69, a70, a71, a72, a73, a74, a75, a76, a77, a78, a79, a80, a81, a82, a83, a84, a85, a86, a87, a88, a89, a90, a91, a92, a93, a94, a95, a96, a97, a98, a99, a100, a101, a102, a103, a104, a105, a106, a107, a108, a109, a110, a111, a112, a113, a114, a115, a116, a117, a118, a119, a120, a121, a122, a123, a124, a125, a126, a127, a128, a129, a130, a131, a132, a133, a134, a135, a136, a137, a138, a139, a140, a141, a142, a143, a144, a145, a146, a147, a148, a149, a150, a151, a152, a153, a154, a155, a156, a157, a158, a159, a160, a161, a162, a163, a164, a165, a166, a167, a168, a169, a170, a171, a172, a173, a174, a175, a176, a177, a178, a179, a180, a181, a182, a183, a184, a185, a186, a187, a188, a189, a190, a191, a192, a193, a194, a195, a196, a197, a198, a199, a200, a201, a202, a203, a204, a205, a206, a207, a208, a209, a210, a211, a212, a213, a214, a215, a216, a217, a218, a219, a220, a221, a222, a223, a224, a225, a226, a227, a228, a229, a230, a231, a232, a233, a234, a235, a236, a237, a238, a239, a240, a241, a242, a243, a244, a245, a246, a247, a248, a249, a250, a251, a252, a253, a254, a255, *rest = values
32
+ ```
33
+
34
+ #### Do
35
+
36
+ ```py
37
+ first, *rest = values
38
+ ```
@@ -0,0 +1,33 @@
1
+ # py/true-false-comparison — cases
2
+
3
+ ### case: comparing with False
4
+
5
+ ```py
6
+ if done == False:
7
+ wait()
8
+ ```
9
+
10
+ ```expect
11
+ 1:4 py/true-false-comparison
12
+ ```
13
+
14
+ ```fixed
15
+ if not done:
16
+ wait()
17
+ ```
18
+
19
+ ### case: compound operand is parenthesised
20
+
21
+ ```py
22
+ if a or b != True:
23
+ stop()
24
+ ```
25
+
26
+ ```expect
27
+ 1:9 py/true-false-comparison
28
+ ```
29
+
30
+ ```fixed
31
+ if a or not b:
32
+ stop()
33
+ ```
@@ -0,0 +1,36 @@
1
+ ---
2
+ id: py/true-false-comparison
3
+ title: "Don't compare with `True` or `False`"
4
+ languages: [python]
5
+ fix: suggest
6
+ fix_reason: "`x == True` is false for truthy values such as `1` or `[0]`; testing `x` makes them true"
7
+ stability: preview
8
+ ported_from: [ruff/E712]
9
+ since: 0.1.0
10
+ ---
11
+
12
+ A condition tests a value directly (`if ready:`, `if not ready:`) instead of comparing it with `True` or `False`.
13
+
14
+ **Why:** `== True` is noise and is false for truthy values that aren't exactly `True`.
15
+
16
+ ```codelaw
17
+ native: py/true-false-comparison
18
+ ```
19
+
20
+ ## Examples
21
+
22
+ ### Python
23
+
24
+ #### Don't
25
+
26
+ ```py
27
+ if ready == True:
28
+ start()
29
+ ```
30
+
31
+ #### Do
32
+
33
+ ```py
34
+ if ready:
35
+ start()
36
+ ```
@@ -0,0 +1,41 @@
1
+ ---
2
+ id: py/type-comparison
3
+ title: "Don't compare types with `==`"
4
+ languages: [python]
5
+ fix: manual
6
+ fix_reason: "`isinstance` accepts subclasses, which may or may not be intended"
7
+ stability: preview
8
+ ported_from: [ruff/E721]
9
+ since: 0.1.0
10
+ ---
11
+
12
+ Types are compared with `is` / `is not`, or checked with `isinstance()`, not with `==` or `!=` against `type(x)`.
13
+
14
+ **Why:** `type(x) == T` ignores subclasses and can be fooled by a metaclass's `__eq__`.
15
+
16
+ ```codelaw
17
+ native: py/type-comparison
18
+ ```
19
+
20
+ ## How to fix
21
+
22
+ 1. Use `isinstance(x, T)` when subclasses should match.
23
+ 2. Use `type(x) is T` when only the exact type should.
24
+
25
+ ## Examples
26
+
27
+ ### Python
28
+
29
+ #### Don't
30
+
31
+ ```py
32
+ if type(value) == int:
33
+ count(value)
34
+ ```
35
+
36
+ #### Do
37
+
38
+ ```py
39
+ if isinstance(value, int):
40
+ count(value)
41
+ ```
@@ -0,0 +1,40 @@
1
+ ---
2
+ id: py/unary-prefix-increment
3
+ title: "No `++x` or `--x`"
4
+ languages: [python]
5
+ fix: manual
6
+ fix_reason: "whether an increment or a plain value was meant needs a person"
7
+ stability: preview
8
+ ported_from: [ruff/B002]
9
+ since: 0.1.0
10
+ ---
11
+
12
+ Code doesn't write `++x` or `--x`.
13
+
14
+ **Why:** Python has no increment operator: `++x` is `+(+x)`, which leaves `x` unchanged.
15
+
16
+ ```codelaw
17
+ native: py/unary-prefix-increment
18
+ ```
19
+
20
+ ## How to fix
21
+
22
+ 1. Write `x += 1` (or `x -= 1`) if an increment was meant.
23
+
24
+ ## Examples
25
+
26
+ ### Python
27
+
28
+ #### Don't
29
+
30
+ ```py
31
+ count = 1
32
+ ++count
33
+ ```
34
+
35
+ #### Do
36
+
37
+ ```py
38
+ count = 1
39
+ count += 1
40
+ ```
@@ -0,0 +1,55 @@
1
+ ---
2
+ id: py/undefined-export
3
+ title: "`__all__` names only defined names"
4
+ languages: [python]
5
+ exclude: ["**/__init__.py"]
6
+ fix: manual
7
+ fix_reason: "whether the name should be defined or dropped from `__all__` needs a person"
8
+ stability: preview
9
+ ported_from: [ruff/F822]
10
+ since: 0.1.0
11
+ ---
12
+
13
+ Every name in a module's `__all__` is defined or imported in that module.
14
+
15
+ **Why:** `from module import *` raises `AttributeError` on a missing name, and readers trust `__all__` as the module's API.
16
+
17
+ ```codelaw
18
+ native: py/undefined-export
19
+ ```
20
+
21
+ ## How to fix
22
+
23
+ 1. Define or import the name, or remove it from `__all__`.
24
+
25
+ ## Examples
26
+
27
+ ### Python
28
+
29
+ #### Don't
30
+
31
+ ```py
32
+ __all__ = ["load", "save"]
33
+
34
+
35
+ def load():
36
+ pass
37
+ ```
38
+
39
+ #### Do
40
+
41
+ ```py
42
+ __all__ = ["load", "save"]
43
+
44
+
45
+ def load():
46
+ pass
47
+
48
+
49
+ def save():
50
+ pass
51
+ ```
52
+
53
+ ## Differences from the source
54
+
55
+ `__init__.py` files are excluded, as in Ruff, because their `__all__` may list submodules.
@@ -0,0 +1,39 @@
1
+ ---
2
+ id: py/undefined-future-feature
3
+ title: "`from __future__` names only real features"
4
+ languages: [python]
5
+ fix: manual
6
+ fix_reason: "the intended feature can't be guessed from a misspelling"
7
+ stability: preview
8
+ ported_from: [ruff/F407]
9
+ since: 0.1.0
10
+ ---
11
+
12
+ Every name imported from `__future__` is a feature Python defines.
13
+
14
+ **Why:** An unknown feature is a `SyntaxError` when the module is compiled.
15
+
16
+ ```codelaw
17
+ native: py/undefined-future-feature
18
+ ```
19
+
20
+ ## How to fix
21
+
22
+ 1. Check the name against the `__future__` module's features.
23
+ 2. Correct it or remove the import.
24
+
25
+ ## Examples
26
+
27
+ ### Python
28
+
29
+ #### Don't
30
+
31
+ ```py
32
+ from __future__ import braces
33
+ ```
34
+
35
+ #### Do
36
+
37
+ ```py
38
+ from __future__ import annotations
39
+ ```
@@ -0,0 +1,80 @@
1
+ # py/undefined-local — cases
2
+
3
+ ### case: annotations of a nested function
4
+
5
+ ```py
6
+ from typing import Any, Iterator
7
+
8
+
9
+ def _unique(fn):
10
+ def unique(*args: Any, **kw: Any) -> Iterator[Any]:
11
+ return fn(*args, **kw)
12
+ return unique
13
+ ```
14
+
15
+ ```expect
16
+ (none)
17
+ ```
18
+
19
+ ### case: builtin annotation in a nested function
20
+
21
+ ```py
22
+ def outer():
23
+ def parse_line(line: str) -> str:
24
+ return line
25
+ return parse_line
26
+ ```
27
+
28
+ ```expect
29
+ (none)
30
+ ```
31
+
32
+ ### case: read in the right-hand side
33
+
34
+ ```py
35
+ x = 1
36
+ def f():
37
+ x = x + 1
38
+ ```
39
+
40
+ ```expect
41
+ 3:9 py/undefined-local
42
+ ```
43
+
44
+ ### case: assigned first
45
+
46
+ ```py
47
+ x = 1
48
+ def f():
49
+ x = 2
50
+ return x
51
+ ```
52
+
53
+ ```expect
54
+ (none)
55
+ ```
56
+
57
+ ### case: global declaration
58
+
59
+ ```py
60
+ x = 1
61
+ def f():
62
+ global x
63
+ x += 1
64
+ ```
65
+
66
+ ```expect
67
+ (none)
68
+ ```
69
+
70
+ ### case: no outer name
71
+
72
+ ```py
73
+ def f():
74
+ print(y)
75
+ y = 1
76
+ ```
77
+
78
+ ```expect
79
+ (none)
80
+ ```
@@ -0,0 +1,48 @@
1
+ ---
2
+ id: py/undefined-local
3
+ title: "A local isn't read before the function assigns it"
4
+ languages: [python]
5
+ fix: manual
6
+ fix_reason: "whether to declare it `global`, `nonlocal` or rename the local needs a person"
7
+ stability: preview
8
+ ported_from: [ruff/F823]
9
+ since: 0.1.0
10
+ ---
11
+
12
+ A function doesn't read a name before assigning it, when that name also exists in an enclosing scope.
13
+
14
+ **Why:** Assigning makes the name local for the whole function, so the earlier read raises `UnboundLocalError` instead of seeing the outer value.
15
+
16
+ ```codelaw
17
+ native: py/undefined-local
18
+ ```
19
+
20
+ ## How to fix
21
+
22
+ 1. Declare the name `global` or `nonlocal` if the outer one is meant.
23
+ 2. Otherwise rename the local variable.
24
+
25
+ ## Examples
26
+
27
+ ### Python
28
+
29
+ #### Don't
30
+
31
+ ```py
32
+ count = 0
33
+
34
+
35
+ def increment():
36
+ count += 1
37
+ ```
38
+
39
+ #### Do
40
+
41
+ ```py
42
+ count = 0
43
+
44
+
45
+ def increment():
46
+ global count
47
+ count += 1
48
+ ```