Skip to content

Commit b61eda2

Browse files
AlexWaygoodMichaReiser
authored andcommitted
[ruff-0.8] Spruce up docs for newly stabilised rules (#14466)
## Summary - Expand some docs where they're unclear about the motivation, or assume some knowledge that hasn't been introduced yet - Add more links to external docs - Rename PYI063 from `PrePep570PositionalArgument` to `Pep484StylePositionalOnlyParameter` - Rename the file `parenthesize_logical_operators.rs` to `parenthesize_chained_operators.rs`, since the rule is called `ParenthesizeChainedOperators`, not `ParenthesizeLogicalOperators` ## Test Plan `cargo test`
1 parent b4858a9 commit b61eda2

17 files changed

+124
-101
lines changed

crates/ruff_linter/src/checkers/ast/analyze/statement.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -180,8 +180,8 @@ pub(crate) fn statement(stmt: &Stmt, checker: &mut Checker) {
180180
if checker.enabled(Rule::RedundantNumericUnion) {
181181
flake8_pyi::rules::redundant_numeric_union(checker, parameters);
182182
}
183-
if checker.enabled(Rule::PrePep570PositionalArgument) {
184-
flake8_pyi::rules::pre_pep570_positional_argument(checker, function_def);
183+
if checker.enabled(Rule::Pep484StylePositionalOnlyParameter) {
184+
flake8_pyi::rules::pep_484_positional_parameter(checker, function_def);
185185
}
186186
if checker.enabled(Rule::DunderFunctionName) {
187187
if let Some(diagnostic) = pep8_naming::rules::dunder_function_name(

crates/ruff_linter/src/codes.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -791,7 +791,7 @@ pub fn code_to_rule(linter: Linter, code: &str) -> Option<(RuleGroup, Rule)> {
791791
(Flake8Pyi, "059") => (RuleGroup::Preview, rules::flake8_pyi::rules::GenericNotLastBaseClass),
792792
(Flake8Pyi, "061") => (RuleGroup::Preview, rules::flake8_pyi::rules::RedundantNoneLiteral),
793793
(Flake8Pyi, "062") => (RuleGroup::Stable, rules::flake8_pyi::rules::DuplicateLiteralMember),
794-
(Flake8Pyi, "063") => (RuleGroup::Stable, rules::flake8_pyi::rules::PrePep570PositionalArgument),
794+
(Flake8Pyi, "063") => (RuleGroup::Stable, rules::flake8_pyi::rules::Pep484StylePositionalOnlyParameter),
795795
(Flake8Pyi, "064") => (RuleGroup::Stable, rules::flake8_pyi::rules::RedundantFinalLiteral),
796796
(Flake8Pyi, "066") => (RuleGroup::Stable, rules::flake8_pyi::rules::BadVersionInfoOrder),
797797

crates/ruff_linter/src/rules/fastapi/rules/fastapi_non_annotated_dependency.rs

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -11,14 +11,14 @@ use crate::rules::fastapi::rules::is_fastapi_route;
1111
use crate::settings::types::PythonVersion;
1212

1313
/// ## What it does
14-
/// Identifies FastAPI routes with deprecated uses of `Depends`.
14+
/// Identifies FastAPI routes with deprecated uses of `Depends` or similar.
1515
///
1616
/// ## Why is this bad?
17-
/// The FastAPI documentation recommends the use of `Annotated` for defining
18-
/// route dependencies and parameters, rather than using `Depends` directly
19-
/// with a default value.
20-
///
21-
/// This approach is also suggested for various route parameters, including Body and Cookie, as it helps ensure consistency and clarity in defining dependencies and parameters.
17+
/// The [FastAPI documentation] recommends the use of [`typing.Annotated`] for
18+
/// defining route dependencies and parameters, rather than using `Depends`,
19+
/// `Query` or similar as a default value for a parameter. Using this approach
20+
/// everywhere helps ensure consistency and clarity in defining dependencies
21+
/// and parameters.
2222
///
2323
/// ## Example
2424
///
@@ -55,7 +55,9 @@ use crate::settings::types::PythonVersion;
5555
/// async def read_items(commons: Annotated[dict, Depends(common_parameters)]):
5656
/// return commons
5757
/// ```
58-
58+
///
59+
/// [fastAPI documentation]: https://fastapi.tiangolo.com/tutorial/query-params-str-validations/?h=annotated#advantages-of-annotated
60+
/// [typing.Annotated]: https://docs.python.org/3/library/typing.html#typing.Annotated
5961
#[violation]
6062
pub struct FastApiNonAnnotatedDependency;
6163

@@ -72,7 +74,7 @@ impl Violation for FastApiNonAnnotatedDependency {
7274
}
7375
}
7476

75-
/// RUF103
77+
/// FAST002
7678
pub(crate) fn fastapi_non_annotated_dependency(
7779
checker: &mut Checker,
7880
function_def: &ast::StmtFunctionDef,

crates/ruff_linter/src/rules/fastapi/rules/fastapi_redundant_response_model.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ impl AlwaysFixableViolation for FastApiRedundantResponseModel {
7373
}
7474
}
7575

76-
/// RUF102
76+
/// FAST001
7777
pub(crate) fn fastapi_redundant_response_model(
7878
checker: &mut Checker,
7979
function_def: &StmtFunctionDef,

crates/ruff_linter/src/rules/flake8_bugbear/rules/mutable_contextvar_default.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,9 @@ use crate::checkers::ast::Checker;
1919
/// the `ContextVar`. If the object is modified, those modifications will persist
2020
/// across calls, which can lead to unexpected behavior.
2121
///
22-
/// Instead, prefer to use immutable data structures; or, take `None` as a
23-
/// default, and initialize a new mutable object inside for each call using the
24-
/// `.set()` method.
22+
/// Instead, prefer to use immutable data structures. Alternatively, take
23+
/// `None` as a default, and initialize a new mutable object inside for each
24+
/// call using the `.set()` method.
2525
///
2626
/// Types outside the standard library can be marked as immutable with the
2727
/// [`lint.flake8-bugbear.extend-immutable-calls`] configuration option.

crates/ruff_linter/src/rules/flake8_pyi/mod.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -66,8 +66,8 @@ mod tests {
6666
#[test_case(Rule::PatchVersionComparison, Path::new("PYI004.pyi"))]
6767
#[test_case(Rule::QuotedAnnotationInStub, Path::new("PYI020.py"))]
6868
#[test_case(Rule::QuotedAnnotationInStub, Path::new("PYI020.pyi"))]
69-
#[test_case(Rule::PrePep570PositionalArgument, Path::new("PYI063.py"))]
70-
#[test_case(Rule::PrePep570PositionalArgument, Path::new("PYI063.pyi"))]
69+
#[test_case(Rule::Pep484StylePositionalOnlyParameter, Path::new("PYI063.py"))]
70+
#[test_case(Rule::Pep484StylePositionalOnlyParameter, Path::new("PYI063.pyi"))]
7171
#[test_case(Rule::RedundantFinalLiteral, Path::new("PYI064.py"))]
7272
#[test_case(Rule::RedundantFinalLiteral, Path::new("PYI064.pyi"))]
7373
#[test_case(Rule::RedundantLiteralUnion, Path::new("PYI051.py"))]

crates/ruff_linter/src/rules/flake8_pyi/rules/bad_version_info_comparison.rs

Lines changed: 14 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,9 @@ use crate::registry::Rule;
1616
/// Comparing `sys.version_info` with `==` or `<=` has unexpected behavior
1717
/// and can lead to bugs.
1818
///
19-
/// For example, `sys.version_info > (3, 8)` will also match `3.8.10`,
20-
/// while `sys.version_info <= (3, 8)` will _not_ match `3.8.10`:
19+
/// For example, `sys.version_info > (3, 8, 1)` will resolve to `True` if your
20+
/// Python version is 3.8.1; meanwhile, `sys.version_info <= (3, 8)` will _not_
21+
/// resolve to `True` if your Python version is 3.8.10:
2122
///
2223
/// ```python
2324
/// >>> import sys
@@ -61,16 +62,20 @@ impl Violation for BadVersionInfoComparison {
6162
}
6263

6364
/// ## What it does
64-
/// Checks for if-else statements with `sys.version_info` comparisons that use
65-
/// `<` comparators.
65+
/// Checks for code that branches on `sys.version_info` comparisons where
66+
/// branches corresponding to older Python versions come before branches
67+
/// corresponding to newer Python versions.
6668
///
6769
/// ## Why is this bad?
6870
/// As a convention, branches that correspond to newer Python versions should
69-
/// come first when using `sys.version_info` comparisons. This makes it easier
70-
/// to understand the desired behavior, which typically corresponds to the
71-
/// latest Python versions.
71+
/// come first. This makes it easier to understand the desired behavior, which
72+
/// typically corresponds to the latest Python versions.
7273
///
73-
/// In [preview], this rule will also flag non-stub files.
74+
/// This rule enforces the convention by checking for `if` tests that compare
75+
/// `sys.version_info` with `<` rather than `>=`.
76+
///
77+
/// By default, this rule only applies to stub files.
78+
/// In [preview], it will also flag this anti-pattern in non-stub files.
7479
///
7580
/// ## Example
7681
///
@@ -101,7 +106,7 @@ pub struct BadVersionInfoOrder;
101106
impl Violation for BadVersionInfoOrder {
102107
#[derive_message_formats]
103108
fn message(&self) -> String {
104-
"Use `>=` when using `if`-`else` with `sys.version_info` comparisons".to_string()
109+
"Put branches for newer Python versions first when branching on `sys.version_info` comparisons".to_string()
105110
}
106111
}
107112

crates/ruff_linter/src/rules/flake8_pyi/rules/pre_pep570_positional_argument.rs

Lines changed: 19 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,19 @@ use crate::checkers::ast::Checker;
88
use crate::settings::types::PythonVersion;
99

1010
/// ## What it does
11-
/// Checks for the presence of [PEP 484]-style positional-only arguments.
11+
/// Checks for the presence of [PEP 484]-style positional-only parameters.
1212
///
1313
/// ## Why is this bad?
14-
/// Historically, [PEP 484] recommended prefixing positional-only arguments
15-
/// with a double underscore (`__`). However, [PEP 570] introduced a dedicated
16-
/// syntax for positional-only arguments, which should be preferred.
14+
/// Historically, [PEP 484] recommended prefixing parameter names with double
15+
/// underscores (`__`) to indicate to a type checker that they were
16+
/// positional-only. However, [PEP 570] (introduced in Python 3.8) introduced
17+
/// dedicated syntax for positional-only arguments. If a forward slash (`/`) is
18+
/// present in a function signature on Python 3.8+, all parameters prior to the
19+
/// slash are interpreted as positional-only.
20+
///
21+
/// The new syntax should be preferred as it is more widely used, more concise
22+
/// and more readable. It is also respected by Python at runtime, whereas the
23+
/// old-style syntax was only understood by type checkers.
1724
///
1825
/// ## Example
1926
///
@@ -33,12 +40,12 @@ use crate::settings::types::PythonVersion;
3340
/// [PEP 484]: https://peps.python.org/pep-0484/#positional-only-arguments
3441
/// [PEP 570]: https://peps.python.org/pep-0570
3542
#[violation]
36-
pub struct PrePep570PositionalArgument;
43+
pub struct Pep484StylePositionalOnlyParameter;
3744

38-
impl Violation for PrePep570PositionalArgument {
45+
impl Violation for Pep484StylePositionalOnlyParameter {
3946
#[derive_message_formats]
4047
fn message(&self) -> String {
41-
"Use PEP 570 syntax for positional-only arguments".to_string()
48+
"Use PEP 570 syntax for positional-only parameters".to_string()
4249
}
4350

4451
fn fix_title(&self) -> Option<String> {
@@ -47,7 +54,7 @@ impl Violation for PrePep570PositionalArgument {
4754
}
4855

4956
/// PYI063
50-
pub(crate) fn pre_pep570_positional_argument(
57+
pub(crate) fn pep_484_positional_parameter(
5158
checker: &mut Checker,
5259
function_def: &ast::StmtFunctionDef,
5360
) {
@@ -82,18 +89,18 @@ pub(crate) fn pre_pep570_positional_argument(
8289
));
8390

8491
if let Some(arg) = function_def.parameters.args.get(skip) {
85-
if is_pre_pep570_positional_only(arg) {
92+
if is_old_style_positional_only(arg) {
8693
checker.diagnostics.push(Diagnostic::new(
87-
PrePep570PositionalArgument,
94+
Pep484StylePositionalOnlyParameter,
8895
arg.identifier(),
8996
));
9097
}
9198
}
9299
}
93100

94-
/// Returns `true` if the [`ParameterWithDefault`] is an old-style positional-only argument (i.e.,
101+
/// Returns `true` if the [`ParameterWithDefault`] is an old-style positional-only parameter (i.e.,
95102
/// its name starts with `__` and does not end with `__`).
96-
fn is_pre_pep570_positional_only(arg: &ParameterWithDefault) -> bool {
103+
fn is_old_style_positional_only(arg: &ParameterWithDefault) -> bool {
97104
let arg_name = &arg.parameter.name;
98105
arg_name.starts_with("__") && !arg_name.ends_with("__")
99106
}

crates/ruff_linter/src/rules/flake8_pyi/rules/redundant_final_literal.rs

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,18 +11,25 @@ use crate::Locator;
1111
/// Checks for redundant `Final[Literal[...]]` annotations.
1212
///
1313
/// ## Why is this bad?
14-
/// A `Final[Literal[...]]` annotation can be replaced with `Final`; the literal
15-
/// use is unnecessary.
14+
/// All constant variables annotated as `Final` are understood as implicitly
15+
/// having `Literal` types by a type checker. As such, a `Final[Literal[...]]`
16+
/// annotation can often be replaced with a bare `Final`, annotation, which
17+
/// will have the same meaning to the type checker while being more concise and
18+
/// more readable.
1619
///
1720
/// ## Example
1821
///
1922
/// ```pyi
23+
/// from typing import Final, Literal
24+
///
2025
/// x: Final[Literal[42]]
2126
/// y: Final[Literal[42]] = 42
2227
/// ```
2328
///
2429
/// Use instead:
2530
/// ```pyi
31+
/// from typing import Final, Literal
32+
///
2633
/// x: Final = 42
2734
/// y: Final = 42
2835
/// ```

crates/ruff_linter/src/rules/flake8_pyi/snapshots/ruff_linter__rules__flake8_pyi__tests__PYI063_PYI063.py.snap

Lines changed: 10 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,7 @@
11
---
22
source: crates/ruff_linter/src/rules/flake8_pyi/mod.rs
3-
snapshot_kind: text
43
---
5-
PYI063.py:6:9: PYI063 Use PEP 570 syntax for positional-only arguments
4+
PYI063.py:6:9: PYI063 Use PEP 570 syntax for positional-only parameters
65
|
76
4 | from typing import Self
87
5 |
@@ -13,7 +12,7 @@ PYI063.py:6:9: PYI063 Use PEP 570 syntax for positional-only arguments
1312
|
1413
= help: Add `/` to function signature
1514

16-
PYI063.py:7:14: PYI063 Use PEP 570 syntax for positional-only arguments
15+
PYI063.py:7:14: PYI063 Use PEP 570 syntax for positional-only parameters
1716
|
1817
6 | def bad(__x: int) -> None: ... # PYI063
1918
7 | def also_bad(__x: int, __y: str) -> None: ... # PYI063
@@ -22,7 +21,7 @@ PYI063.py:7:14: PYI063 Use PEP 570 syntax for positional-only arguments
2221
|
2322
= help: Add `/` to function signature
2423

25-
PYI063.py:8:15: PYI063 Use PEP 570 syntax for positional-only arguments
24+
PYI063.py:8:15: PYI063 Use PEP 570 syntax for positional-only parameters
2625
|
2726
6 | def bad(__x: int) -> None: ... # PYI063
2827
7 | def also_bad(__x: int, __y: str) -> None: ... # PYI063
@@ -33,7 +32,7 @@ PYI063.py:8:15: PYI063 Use PEP 570 syntax for positional-only arguments
3332
|
3433
= help: Add `/` to function signature
3534

36-
PYI063.py:24:14: PYI063 Use PEP 570 syntax for positional-only arguments
35+
PYI063.py:24:14: PYI063 Use PEP 570 syntax for positional-only parameters
3736
|
3837
22 | def bad(__self) -> None: ... # PYI063
3938
23 | @staticmethod
@@ -44,7 +43,7 @@ PYI063.py:24:14: PYI063 Use PEP 570 syntax for positional-only arguments
4443
|
4544
= help: Add `/` to function signature
4645

47-
PYI063.py:25:22: PYI063 Use PEP 570 syntax for positional-only arguments
46+
PYI063.py:25:22: PYI063 Use PEP 570 syntax for positional-only parameters
4847
|
4948
23 | @staticmethod
5049
24 | def bad2(__self) -> None: ... # PYI063
@@ -55,7 +54,7 @@ PYI063.py:25:22: PYI063 Use PEP 570 syntax for positional-only arguments
5554
|
5655
= help: Add `/` to function signature
5756

58-
PYI063.py:26:25: PYI063 Use PEP 570 syntax for positional-only arguments
57+
PYI063.py:26:25: PYI063 Use PEP 570 syntax for positional-only parameters
5958
|
6059
24 | def bad2(__self) -> None: ... # PYI063
6160
25 | def bad3(__self, __x: int) -> None: ... # PYI063
@@ -66,7 +65,7 @@ PYI063.py:26:25: PYI063 Use PEP 570 syntax for positional-only arguments
6665
|
6766
= help: Add `/` to function signature
6867

69-
PYI063.py:28:25: PYI063 Use PEP 570 syntax for positional-only arguments
68+
PYI063.py:28:25: PYI063 Use PEP 570 syntax for positional-only parameters
7069
|
7170
26 | def still_bad(self, __x_: int) -> None: ... # PYI063
7271
27 | @staticmethod
@@ -77,7 +76,7 @@ PYI063.py:28:25: PYI063 Use PEP 570 syntax for positional-only arguments
7776
|
7877
= help: Add `/` to function signature
7978

80-
PYI063.py:30:23: PYI063 Use PEP 570 syntax for positional-only arguments
79+
PYI063.py:30:23: PYI063 Use PEP 570 syntax for positional-only parameters
8180
|
8281
28 | def this_is_bad_too(__x: int) -> None: ... # PYI063
8382
29 | @classmethod
@@ -88,7 +87,7 @@ PYI063.py:30:23: PYI063 Use PEP 570 syntax for positional-only arguments
8887
|
8988
= help: Add `/` to function signature
9089

91-
PYI063.py:52:23: PYI063 Use PEP 570 syntax for positional-only arguments
90+
PYI063.py:52:23: PYI063 Use PEP 570 syntax for positional-only parameters
9291
|
9392
50 | class Metaclass(type):
9493
51 | @classmethod
@@ -99,7 +98,7 @@ PYI063.py:52:23: PYI063 Use PEP 570 syntax for positional-only arguments
9998
|
10099
= help: Add `/` to function signature
101100

102-
PYI063.py:56:26: PYI063 Use PEP 570 syntax for positional-only arguments
101+
PYI063.py:56:26: PYI063 Use PEP 570 syntax for positional-only parameters
103102
|
104103
54 | class Metaclass2(type):
105104
55 | @classmethod

0 commit comments

Comments
 (0)