[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`
This commit is contained in:
Alex Waygood 2024-11-20 08:00:12 +00:00 committed by Micha Reiser
parent aa7ac2ce0f
commit cef12f4925
17 changed files with 124 additions and 101 deletions

View file

@ -180,8 +180,8 @@ pub(crate) fn statement(stmt: &Stmt, checker: &mut Checker) {
if checker.enabled(Rule::RedundantNumericUnion) { if checker.enabled(Rule::RedundantNumericUnion) {
flake8_pyi::rules::redundant_numeric_union(checker, parameters); flake8_pyi::rules::redundant_numeric_union(checker, parameters);
} }
if checker.enabled(Rule::PrePep570PositionalArgument) { if checker.enabled(Rule::Pep484StylePositionalOnlyParameter) {
flake8_pyi::rules::pre_pep570_positional_argument(checker, function_def); flake8_pyi::rules::pep_484_positional_parameter(checker, function_def);
} }
if checker.enabled(Rule::DunderFunctionName) { if checker.enabled(Rule::DunderFunctionName) {
if let Some(diagnostic) = pep8_naming::rules::dunder_function_name( if let Some(diagnostic) = pep8_naming::rules::dunder_function_name(

View file

@ -791,7 +791,7 @@ pub fn code_to_rule(linter: Linter, code: &str) -> Option<(RuleGroup, Rule)> {
(Flake8Pyi, "059") => (RuleGroup::Preview, rules::flake8_pyi::rules::GenericNotLastBaseClass), (Flake8Pyi, "059") => (RuleGroup::Preview, rules::flake8_pyi::rules::GenericNotLastBaseClass),
(Flake8Pyi, "061") => (RuleGroup::Preview, rules::flake8_pyi::rules::RedundantNoneLiteral), (Flake8Pyi, "061") => (RuleGroup::Preview, rules::flake8_pyi::rules::RedundantNoneLiteral),
(Flake8Pyi, "062") => (RuleGroup::Stable, rules::flake8_pyi::rules::DuplicateLiteralMember), (Flake8Pyi, "062") => (RuleGroup::Stable, rules::flake8_pyi::rules::DuplicateLiteralMember),
(Flake8Pyi, "063") => (RuleGroup::Stable, rules::flake8_pyi::rules::PrePep570PositionalArgument), (Flake8Pyi, "063") => (RuleGroup::Stable, rules::flake8_pyi::rules::Pep484StylePositionalOnlyParameter),
(Flake8Pyi, "064") => (RuleGroup::Stable, rules::flake8_pyi::rules::RedundantFinalLiteral), (Flake8Pyi, "064") => (RuleGroup::Stable, rules::flake8_pyi::rules::RedundantFinalLiteral),
(Flake8Pyi, "066") => (RuleGroup::Stable, rules::flake8_pyi::rules::BadVersionInfoOrder), (Flake8Pyi, "066") => (RuleGroup::Stable, rules::flake8_pyi::rules::BadVersionInfoOrder),

View file

@ -11,14 +11,14 @@ use crate::rules::fastapi::rules::is_fastapi_route;
use crate::settings::types::PythonVersion; use crate::settings::types::PythonVersion;
/// ## What it does /// ## What it does
/// Identifies FastAPI routes with deprecated uses of `Depends`. /// Identifies FastAPI routes with deprecated uses of `Depends` or similar.
/// ///
/// ## Why is this bad? /// ## Why is this bad?
/// The FastAPI documentation recommends the use of `Annotated` for defining /// The [FastAPI documentation] recommends the use of [`typing.Annotated`] for
/// route dependencies and parameters, rather than using `Depends` directly /// defining route dependencies and parameters, rather than using `Depends`,
/// with a default value. /// `Query` or similar as a default value for a parameter. Using this approach
/// /// everywhere helps ensure consistency and clarity in defining dependencies
/// 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. /// and parameters.
/// ///
/// ## Example /// ## Example
/// ///
@ -55,7 +55,9 @@ use crate::settings::types::PythonVersion;
/// async def read_items(commons: Annotated[dict, Depends(common_parameters)]): /// async def read_items(commons: Annotated[dict, Depends(common_parameters)]):
/// return commons /// return commons
/// ``` /// ```
///
/// [fastAPI documentation]: https://fastapi.tiangolo.com/tutorial/query-params-str-validations/?h=annotated#advantages-of-annotated
/// [typing.Annotated]: https://docs.python.org/3/library/typing.html#typing.Annotated
#[violation] #[violation]
pub struct FastApiNonAnnotatedDependency; pub struct FastApiNonAnnotatedDependency;
@ -72,7 +74,7 @@ impl Violation for FastApiNonAnnotatedDependency {
} }
} }
/// RUF103 /// FAST002
pub(crate) fn fastapi_non_annotated_dependency( pub(crate) fn fastapi_non_annotated_dependency(
checker: &mut Checker, checker: &mut Checker,
function_def: &ast::StmtFunctionDef, function_def: &ast::StmtFunctionDef,

View file

@ -73,7 +73,7 @@ impl AlwaysFixableViolation for FastApiRedundantResponseModel {
} }
} }
/// RUF102 /// FAST001
pub(crate) fn fastapi_redundant_response_model( pub(crate) fn fastapi_redundant_response_model(
checker: &mut Checker, checker: &mut Checker,
function_def: &StmtFunctionDef, function_def: &StmtFunctionDef,

View file

@ -19,9 +19,9 @@ use crate::checkers::ast::Checker;
/// the `ContextVar`. If the object is modified, those modifications will persist /// the `ContextVar`. If the object is modified, those modifications will persist
/// across calls, which can lead to unexpected behavior. /// across calls, which can lead to unexpected behavior.
/// ///
/// Instead, prefer to use immutable data structures; or, take `None` as a /// Instead, prefer to use immutable data structures. Alternatively, take
/// default, and initialize a new mutable object inside for each call using the /// `None` as a default, and initialize a new mutable object inside for each
/// `.set()` method. /// call using the `.set()` method.
/// ///
/// Types outside the standard library can be marked as immutable with the /// Types outside the standard library can be marked as immutable with the
/// [`lint.flake8-bugbear.extend-immutable-calls`] configuration option. /// [`lint.flake8-bugbear.extend-immutable-calls`] configuration option.

View file

@ -66,8 +66,8 @@ mod tests {
#[test_case(Rule::PatchVersionComparison, Path::new("PYI004.pyi"))] #[test_case(Rule::PatchVersionComparison, Path::new("PYI004.pyi"))]
#[test_case(Rule::QuotedAnnotationInStub, Path::new("PYI020.py"))] #[test_case(Rule::QuotedAnnotationInStub, Path::new("PYI020.py"))]
#[test_case(Rule::QuotedAnnotationInStub, Path::new("PYI020.pyi"))] #[test_case(Rule::QuotedAnnotationInStub, Path::new("PYI020.pyi"))]
#[test_case(Rule::PrePep570PositionalArgument, Path::new("PYI063.py"))] #[test_case(Rule::Pep484StylePositionalOnlyParameter, Path::new("PYI063.py"))]
#[test_case(Rule::PrePep570PositionalArgument, Path::new("PYI063.pyi"))] #[test_case(Rule::Pep484StylePositionalOnlyParameter, Path::new("PYI063.pyi"))]
#[test_case(Rule::RedundantFinalLiteral, Path::new("PYI064.py"))] #[test_case(Rule::RedundantFinalLiteral, Path::new("PYI064.py"))]
#[test_case(Rule::RedundantFinalLiteral, Path::new("PYI064.pyi"))] #[test_case(Rule::RedundantFinalLiteral, Path::new("PYI064.pyi"))]
#[test_case(Rule::RedundantLiteralUnion, Path::new("PYI051.py"))] #[test_case(Rule::RedundantLiteralUnion, Path::new("PYI051.py"))]

View file

@ -16,8 +16,9 @@ use crate::registry::Rule;
/// Comparing `sys.version_info` with `==` or `<=` has unexpected behavior /// Comparing `sys.version_info` with `==` or `<=` has unexpected behavior
/// and can lead to bugs. /// and can lead to bugs.
/// ///
/// For example, `sys.version_info > (3, 8)` will also match `3.8.10`, /// For example, `sys.version_info > (3, 8, 1)` will resolve to `True` if your
/// while `sys.version_info <= (3, 8)` will _not_ match `3.8.10`: /// Python version is 3.8.1; meanwhile, `sys.version_info <= (3, 8)` will _not_
/// resolve to `True` if your Python version is 3.8.10:
/// ///
/// ```python /// ```python
/// >>> import sys /// >>> import sys
@ -61,16 +62,20 @@ impl Violation for BadVersionInfoComparison {
} }
/// ## What it does /// ## What it does
/// Checks for if-else statements with `sys.version_info` comparisons that use /// Checks for code that branches on `sys.version_info` comparisons where
/// `<` comparators. /// branches corresponding to older Python versions come before branches
/// corresponding to newer Python versions.
/// ///
/// ## Why is this bad? /// ## Why is this bad?
/// As a convention, branches that correspond to newer Python versions should /// As a convention, branches that correspond to newer Python versions should
/// come first when using `sys.version_info` comparisons. This makes it easier /// come first. This makes it easier to understand the desired behavior, which
/// to understand the desired behavior, which typically corresponds to the /// typically corresponds to the latest Python versions.
/// latest Python versions.
/// ///
/// In [preview], this rule will also flag non-stub files. /// This rule enforces the convention by checking for `if` tests that compare
/// `sys.version_info` with `<` rather than `>=`.
///
/// By default, this rule only applies to stub files.
/// In [preview], it will also flag this anti-pattern in non-stub files.
/// ///
/// ## Example /// ## Example
/// ///
@ -101,7 +106,7 @@ pub struct BadVersionInfoOrder;
impl Violation for BadVersionInfoOrder { impl Violation for BadVersionInfoOrder {
#[derive_message_formats] #[derive_message_formats]
fn message(&self) -> String { fn message(&self) -> String {
"Use `>=` when using `if`-`else` with `sys.version_info` comparisons".to_string() "Put branches for newer Python versions first when branching on `sys.version_info` comparisons".to_string()
} }
} }

View file

@ -8,12 +8,19 @@ use crate::checkers::ast::Checker;
use crate::settings::types::PythonVersion; use crate::settings::types::PythonVersion;
/// ## What it does /// ## What it does
/// Checks for the presence of [PEP 484]-style positional-only arguments. /// Checks for the presence of [PEP 484]-style positional-only parameters.
/// ///
/// ## Why is this bad? /// ## Why is this bad?
/// Historically, [PEP 484] recommended prefixing positional-only arguments /// Historically, [PEP 484] recommended prefixing parameter names with double
/// with a double underscore (`__`). However, [PEP 570] introduced a dedicated /// underscores (`__`) to indicate to a type checker that they were
/// syntax for positional-only arguments, which should be preferred. /// positional-only. However, [PEP 570] (introduced in Python 3.8) introduced
/// dedicated syntax for positional-only arguments. If a forward slash (`/`) is
/// present in a function signature on Python 3.8+, all parameters prior to the
/// slash are interpreted as positional-only.
///
/// The new syntax should be preferred as it is more widely used, more concise
/// and more readable. It is also respected by Python at runtime, whereas the
/// old-style syntax was only understood by type checkers.
/// ///
/// ## Example /// ## Example
/// ///
@ -33,12 +40,12 @@ use crate::settings::types::PythonVersion;
/// [PEP 484]: https://peps.python.org/pep-0484/#positional-only-arguments /// [PEP 484]: https://peps.python.org/pep-0484/#positional-only-arguments
/// [PEP 570]: https://peps.python.org/pep-0570 /// [PEP 570]: https://peps.python.org/pep-0570
#[violation] #[violation]
pub struct PrePep570PositionalArgument; pub struct Pep484StylePositionalOnlyParameter;
impl Violation for PrePep570PositionalArgument { impl Violation for Pep484StylePositionalOnlyParameter {
#[derive_message_formats] #[derive_message_formats]
fn message(&self) -> String { fn message(&self) -> String {
"Use PEP 570 syntax for positional-only arguments".to_string() "Use PEP 570 syntax for positional-only parameters".to_string()
} }
fn fix_title(&self) -> Option<String> { fn fix_title(&self) -> Option<String> {
@ -47,7 +54,7 @@ impl Violation for PrePep570PositionalArgument {
} }
/// PYI063 /// PYI063
pub(crate) fn pre_pep570_positional_argument( pub(crate) fn pep_484_positional_parameter(
checker: &mut Checker, checker: &mut Checker,
function_def: &ast::StmtFunctionDef, function_def: &ast::StmtFunctionDef,
) { ) {
@ -82,18 +89,18 @@ pub(crate) fn pre_pep570_positional_argument(
)); ));
if let Some(arg) = function_def.parameters.args.get(skip) { if let Some(arg) = function_def.parameters.args.get(skip) {
if is_pre_pep570_positional_only(arg) { if is_old_style_positional_only(arg) {
checker.diagnostics.push(Diagnostic::new( checker.diagnostics.push(Diagnostic::new(
PrePep570PositionalArgument, Pep484StylePositionalOnlyParameter,
arg.identifier(), arg.identifier(),
)); ));
} }
} }
} }
/// Returns `true` if the [`ParameterWithDefault`] is an old-style positional-only argument (i.e., /// Returns `true` if the [`ParameterWithDefault`] is an old-style positional-only parameter (i.e.,
/// its name starts with `__` and does not end with `__`). /// its name starts with `__` and does not end with `__`).
fn is_pre_pep570_positional_only(arg: &ParameterWithDefault) -> bool { fn is_old_style_positional_only(arg: &ParameterWithDefault) -> bool {
let arg_name = &arg.parameter.name; let arg_name = &arg.parameter.name;
arg_name.starts_with("__") && !arg_name.ends_with("__") arg_name.starts_with("__") && !arg_name.ends_with("__")
} }

View file

@ -11,18 +11,25 @@ use crate::Locator;
/// Checks for redundant `Final[Literal[...]]` annotations. /// Checks for redundant `Final[Literal[...]]` annotations.
/// ///
/// ## Why is this bad? /// ## Why is this bad?
/// A `Final[Literal[...]]` annotation can be replaced with `Final`; the literal /// All constant variables annotated as `Final` are understood as implicitly
/// use is unnecessary. /// having `Literal` types by a type checker. As such, a `Final[Literal[...]]`
/// annotation can often be replaced with a bare `Final`, annotation, which
/// will have the same meaning to the type checker while being more concise and
/// more readable.
/// ///
/// ## Example /// ## Example
/// ///
/// ```pyi /// ```pyi
/// from typing import Final, Literal
///
/// x: Final[Literal[42]] /// x: Final[Literal[42]]
/// y: Final[Literal[42]] = 42 /// y: Final[Literal[42]] = 42
/// ``` /// ```
/// ///
/// Use instead: /// Use instead:
/// ```pyi /// ```pyi
/// from typing import Final, Literal
///
/// x: Final = 42 /// x: Final = 42
/// y: Final = 42 /// y: Final = 42
/// ``` /// ```

View file

@ -1,8 +1,7 @@
--- ---
source: crates/ruff_linter/src/rules/flake8_pyi/mod.rs source: crates/ruff_linter/src/rules/flake8_pyi/mod.rs
snapshot_kind: text
--- ---
PYI063.py:6:9: PYI063 Use PEP 570 syntax for positional-only arguments PYI063.py:6:9: PYI063 Use PEP 570 syntax for positional-only parameters
| |
4 | from typing import Self 4 | from typing import Self
5 | 5 |
@ -13,7 +12,7 @@ PYI063.py:6:9: PYI063 Use PEP 570 syntax for positional-only arguments
| |
= help: Add `/` to function signature = help: Add `/` to function signature
PYI063.py:7:14: PYI063 Use PEP 570 syntax for positional-only arguments PYI063.py:7:14: PYI063 Use PEP 570 syntax for positional-only parameters
| |
6 | def bad(__x: int) -> None: ... # PYI063 6 | def bad(__x: int) -> None: ... # PYI063
7 | def also_bad(__x: int, __y: str) -> None: ... # PYI063 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
| |
= help: Add `/` to function signature = help: Add `/` to function signature
PYI063.py:8:15: PYI063 Use PEP 570 syntax for positional-only arguments PYI063.py:8:15: PYI063 Use PEP 570 syntax for positional-only parameters
| |
6 | def bad(__x: int) -> None: ... # PYI063 6 | def bad(__x: int) -> None: ... # PYI063
7 | def also_bad(__x: int, __y: str) -> None: ... # PYI063 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
| |
= help: Add `/` to function signature = help: Add `/` to function signature
PYI063.py:24:14: PYI063 Use PEP 570 syntax for positional-only arguments PYI063.py:24:14: PYI063 Use PEP 570 syntax for positional-only parameters
| |
22 | def bad(__self) -> None: ... # PYI063 22 | def bad(__self) -> None: ... # PYI063
23 | @staticmethod 23 | @staticmethod
@ -44,7 +43,7 @@ PYI063.py:24:14: PYI063 Use PEP 570 syntax for positional-only arguments
| |
= help: Add `/` to function signature = help: Add `/` to function signature
PYI063.py:25:22: PYI063 Use PEP 570 syntax for positional-only arguments PYI063.py:25:22: PYI063 Use PEP 570 syntax for positional-only parameters
| |
23 | @staticmethod 23 | @staticmethod
24 | def bad2(__self) -> None: ... # PYI063 24 | def bad2(__self) -> None: ... # PYI063
@ -55,7 +54,7 @@ PYI063.py:25:22: PYI063 Use PEP 570 syntax for positional-only arguments
| |
= help: Add `/` to function signature = help: Add `/` to function signature
PYI063.py:26:25: PYI063 Use PEP 570 syntax for positional-only arguments PYI063.py:26:25: PYI063 Use PEP 570 syntax for positional-only parameters
| |
24 | def bad2(__self) -> None: ... # PYI063 24 | def bad2(__self) -> None: ... # PYI063
25 | def bad3(__self, __x: int) -> None: ... # PYI063 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
| |
= help: Add `/` to function signature = help: Add `/` to function signature
PYI063.py:28:25: PYI063 Use PEP 570 syntax for positional-only arguments PYI063.py:28:25: PYI063 Use PEP 570 syntax for positional-only parameters
| |
26 | def still_bad(self, __x_: int) -> None: ... # PYI063 26 | def still_bad(self, __x_: int) -> None: ... # PYI063
27 | @staticmethod 27 | @staticmethod
@ -77,7 +76,7 @@ PYI063.py:28:25: PYI063 Use PEP 570 syntax for positional-only arguments
| |
= help: Add `/` to function signature = help: Add `/` to function signature
PYI063.py:30:23: PYI063 Use PEP 570 syntax for positional-only arguments PYI063.py:30:23: PYI063 Use PEP 570 syntax for positional-only parameters
| |
28 | def this_is_bad_too(__x: int) -> None: ... # PYI063 28 | def this_is_bad_too(__x: int) -> None: ... # PYI063
29 | @classmethod 29 | @classmethod
@ -88,7 +87,7 @@ PYI063.py:30:23: PYI063 Use PEP 570 syntax for positional-only arguments
| |
= help: Add `/` to function signature = help: Add `/` to function signature
PYI063.py:52:23: PYI063 Use PEP 570 syntax for positional-only arguments PYI063.py:52:23: PYI063 Use PEP 570 syntax for positional-only parameters
| |
50 | class Metaclass(type): 50 | class Metaclass(type):
51 | @classmethod 51 | @classmethod
@ -99,7 +98,7 @@ PYI063.py:52:23: PYI063 Use PEP 570 syntax for positional-only arguments
| |
= help: Add `/` to function signature = help: Add `/` to function signature
PYI063.py:56:26: PYI063 Use PEP 570 syntax for positional-only arguments PYI063.py:56:26: PYI063 Use PEP 570 syntax for positional-only parameters
| |
54 | class Metaclass2(type): 54 | class Metaclass2(type):
55 | @classmethod 55 | @classmethod

View file

@ -1,8 +1,7 @@
--- ---
source: crates/ruff_linter/src/rules/flake8_pyi/mod.rs source: crates/ruff_linter/src/rules/flake8_pyi/mod.rs
snapshot_kind: text
--- ---
PYI063.pyi:6:9: PYI063 Use PEP 570 syntax for positional-only arguments PYI063.pyi:6:9: PYI063 Use PEP 570 syntax for positional-only parameters
| |
4 | from typing import Self 4 | from typing import Self
5 | 5 |
@ -13,7 +12,7 @@ PYI063.pyi:6:9: PYI063 Use PEP 570 syntax for positional-only arguments
| |
= help: Add `/` to function signature = help: Add `/` to function signature
PYI063.pyi:7:14: PYI063 Use PEP 570 syntax for positional-only arguments PYI063.pyi:7:14: PYI063 Use PEP 570 syntax for positional-only parameters
| |
6 | def bad(__x: int) -> None: ... # PYI063 6 | def bad(__x: int) -> None: ... # PYI063
7 | def also_bad(__x: int, __y: str) -> None: ... # PYI063 7 | def also_bad(__x: int, __y: str) -> None: ... # PYI063
@ -22,7 +21,7 @@ PYI063.pyi:7:14: PYI063 Use PEP 570 syntax for positional-only arguments
| |
= help: Add `/` to function signature = help: Add `/` to function signature
PYI063.pyi:8:15: PYI063 Use PEP 570 syntax for positional-only arguments PYI063.pyi:8:15: PYI063 Use PEP 570 syntax for positional-only parameters
| |
6 | def bad(__x: int) -> None: ... # PYI063 6 | def bad(__x: int) -> None: ... # PYI063
7 | def also_bad(__x: int, __y: str) -> None: ... # PYI063 7 | def also_bad(__x: int, __y: str) -> None: ... # PYI063
@ -33,7 +32,7 @@ PYI063.pyi:8:15: PYI063 Use PEP 570 syntax for positional-only arguments
| |
= help: Add `/` to function signature = help: Add `/` to function signature
PYI063.pyi:24:14: PYI063 Use PEP 570 syntax for positional-only arguments PYI063.pyi:24:14: PYI063 Use PEP 570 syntax for positional-only parameters
| |
22 | def bad(__self) -> None: ... # PYI063 22 | def bad(__self) -> None: ... # PYI063
23 | @staticmethod 23 | @staticmethod
@ -44,7 +43,7 @@ PYI063.pyi:24:14: PYI063 Use PEP 570 syntax for positional-only arguments
| |
= help: Add `/` to function signature = help: Add `/` to function signature
PYI063.pyi:25:22: PYI063 Use PEP 570 syntax for positional-only arguments PYI063.pyi:25:22: PYI063 Use PEP 570 syntax for positional-only parameters
| |
23 | @staticmethod 23 | @staticmethod
24 | def bad2(__self) -> None: ... # PYI063 24 | def bad2(__self) -> None: ... # PYI063
@ -55,7 +54,7 @@ PYI063.pyi:25:22: PYI063 Use PEP 570 syntax for positional-only arguments
| |
= help: Add `/` to function signature = help: Add `/` to function signature
PYI063.pyi:26:25: PYI063 Use PEP 570 syntax for positional-only arguments PYI063.pyi:26:25: PYI063 Use PEP 570 syntax for positional-only parameters
| |
24 | def bad2(__self) -> None: ... # PYI063 24 | def bad2(__self) -> None: ... # PYI063
25 | def bad3(__self, __x: int) -> None: ... # PYI063 25 | def bad3(__self, __x: int) -> None: ... # PYI063
@ -66,7 +65,7 @@ PYI063.pyi:26:25: PYI063 Use PEP 570 syntax for positional-only arguments
| |
= help: Add `/` to function signature = help: Add `/` to function signature
PYI063.pyi:28:25: PYI063 Use PEP 570 syntax for positional-only arguments PYI063.pyi:28:25: PYI063 Use PEP 570 syntax for positional-only parameters
| |
26 | def still_bad(self, __x_: int) -> None: ... # PYI063 # TODO: doesn't get raised here 26 | def still_bad(self, __x_: int) -> None: ... # PYI063 # TODO: doesn't get raised here
27 | @staticmethod 27 | @staticmethod
@ -77,7 +76,7 @@ PYI063.pyi:28:25: PYI063 Use PEP 570 syntax for positional-only arguments
| |
= help: Add `/` to function signature = help: Add `/` to function signature
PYI063.pyi:30:23: PYI063 Use PEP 570 syntax for positional-only arguments PYI063.pyi:30:23: PYI063 Use PEP 570 syntax for positional-only parameters
| |
28 | def this_is_bad_too(__x: int) -> None: ... # PYI063 28 | def this_is_bad_too(__x: int) -> None: ... # PYI063
29 | @classmethod 29 | @classmethod
@ -88,7 +87,7 @@ PYI063.pyi:30:23: PYI063 Use PEP 570 syntax for positional-only arguments
| |
= help: Add `/` to function signature = help: Add `/` to function signature
PYI063.pyi:52:23: PYI063 Use PEP 570 syntax for positional-only arguments PYI063.pyi:52:23: PYI063 Use PEP 570 syntax for positional-only parameters
| |
50 | class Metaclass(type): 50 | class Metaclass(type):
51 | @classmethod 51 | @classmethod
@ -99,7 +98,7 @@ PYI063.pyi:52:23: PYI063 Use PEP 570 syntax for positional-only arguments
| |
= help: Add `/` to function signature = help: Add `/` to function signature
PYI063.pyi:56:26: PYI063 Use PEP 570 syntax for positional-only arguments PYI063.pyi:56:26: PYI063 Use PEP 570 syntax for positional-only parameters
| |
54 | class Metaclass2(type): 54 | class Metaclass2(type):
55 | @classmethod 55 | @classmethod

View file

@ -1,8 +1,7 @@
--- ---
source: crates/ruff_linter/src/rules/flake8_pyi/mod.rs source: crates/ruff_linter/src/rules/flake8_pyi/mod.rs
snapshot_kind: text
--- ---
PYI066.pyi:3:4: PYI066 Use `>=` when using `if`-`else` with `sys.version_info` comparisons PYI066.pyi:3:4: PYI066 Put branches for newer Python versions first when branching on `sys.version_info` comparisons
| |
1 | import sys 1 | import sys
2 | 2 |
@ -12,7 +11,7 @@ PYI066.pyi:3:4: PYI066 Use `>=` when using `if`-`else` with `sys.version_info` c
5 | else: 5 | else:
| |
PYI066.pyi:8:4: PYI066 Use `>=` when using `if`-`else` with `sys.version_info` comparisons PYI066.pyi:8:4: PYI066 Put branches for newer Python versions first when branching on `sys.version_info` comparisons
| |
6 | def foo(x, *, bar=True): ... 6 | def foo(x, *, bar=True): ...
7 | 7 |
@ -22,7 +21,7 @@ PYI066.pyi:8:4: PYI066 Use `>=` when using `if`-`else` with `sys.version_info` c
10 | elif sys.version_info < (3, 9): # Y066 When using if/else with sys.version_info, put the code for new Python versions first, e.g. "if sys.version_info >= (3, 9)" 10 | elif sys.version_info < (3, 9): # Y066 When using if/else with sys.version_info, put the code for new Python versions first, e.g. "if sys.version_info >= (3, 9)"
| |
PYI066.pyi:10:6: PYI066 Use `>=` when using `if`-`else` with `sys.version_info` comparisons PYI066.pyi:10:6: PYI066 Put branches for newer Python versions first when branching on `sys.version_info` comparisons
| |
8 | if sys.version_info < (3, 8): # Y066 When using if/else with sys.version_info, put the code for new Python versions first, e.g. "if sys.version_info >= (3, 8)" 8 | if sys.version_info < (3, 8): # Y066 When using if/else with sys.version_info, put the code for new Python versions first, e.g. "if sys.version_info >= (3, 8)"
9 | def bar(x): ... 9 | def bar(x): ...
@ -32,7 +31,7 @@ PYI066.pyi:10:6: PYI066 Use `>=` when using `if`-`else` with `sys.version_info`
12 | elif sys.version_info < (3, 11): # Y066 When using if/else with sys.version_info, put the code for new Python versions first, e.g. "if sys.version_info >= (3, 10)" 12 | elif sys.version_info < (3, 11): # Y066 When using if/else with sys.version_info, put the code for new Python versions first, e.g. "if sys.version_info >= (3, 10)"
| |
PYI066.pyi:12:6: PYI066 Use `>=` when using `if`-`else` with `sys.version_info` comparisons PYI066.pyi:12:6: PYI066 Put branches for newer Python versions first when branching on `sys.version_info` comparisons
| |
10 | elif sys.version_info < (3, 9): # Y066 When using if/else with sys.version_info, put the code for new Python versions first, e.g. "if sys.version_info >= (3, 9)" 10 | elif sys.version_info < (3, 9): # Y066 When using if/else with sys.version_info, put the code for new Python versions first, e.g. "if sys.version_info >= (3, 9)"
11 | def bar(x, *, bar=True): ... 11 | def bar(x, *, bar=True): ...
@ -42,7 +41,7 @@ PYI066.pyi:12:6: PYI066 Use `>=` when using `if`-`else` with `sys.version_info`
14 | else: 14 | else:
| |
PYI066.pyi:20:6: PYI066 Use `>=` when using `if`-`else` with `sys.version_info` comparisons PYI066.pyi:20:6: PYI066 Put branches for newer Python versions first when branching on `sys.version_info` comparisons
| |
18 | if sys.version_info >= (3, 5): 18 | if sys.version_info >= (3, 5):
19 | ... 19 | ...

View file

@ -6,14 +6,18 @@ use ruff_text_size::{Ranged, TextRange};
use crate::checkers::ast::Checker; use crate::checkers::ast::Checker;
/// ## What it does /// ## What it does
/// Checks for unnecessary default type arguments. /// Checks for unnecessary default type arguments for `Generator` and
/// `AsyncGenerator` on Python 3.13+.
/// ///
/// ## Why is this bad? /// ## Why is this bad?
/// Python 3.13 introduced the ability for type parameters to specify default /// Python 3.13 introduced the ability for type parameters to specify default
/// values. As such, the default type arguments for some types in the standard /// values. Following this change, several standard-library classes were
/// library (e.g., Generator, AsyncGenerator) are now optional. /// updated to add default values for some of their type parameters. For
/// example, `Generator[int]` is now equivalent to
/// `Generator[int, None, None]`, as the second and third type parameters of
/// `Generator` now default to `None`.
/// ///
/// Omitting type parameters that match the default values can make the code /// Omitting type arguments that match the default values can make the code
/// more concise and easier to read. /// more concise and easier to read.
/// ///
/// ## Examples /// ## Examples

View file

@ -10,13 +10,15 @@ use crate::checkers::ast::Checker;
/// Checks for uses of `assert expression, print(message)`. /// Checks for uses of `assert expression, print(message)`.
/// ///
/// ## Why is this bad? /// ## Why is this bad?
/// The return value of the second expression is used as the contents of the /// If an `assert x, y` assertion fails, the Python interpreter raises an
/// `AssertionError` raised by the `assert` statement. Using a `print` expression /// `AssertionError`, and the evaluated value of `y` is used as the contents of
/// in this context will output the message to `stdout`, before raising an /// that assertion error. The `print` function always returns `None`, however,
/// empty `AssertionError(None)`. /// so the evaluated value of a call to `print` will always be `None`.
/// ///
/// Instead, remove the `print` and pass the message directly as the second /// Using a `print` call in this context will therefore output the message to
/// expression, allowing `stderr` to capture the message in a well-formatted context. /// `stdout`, before raising an empty `AssertionError(None)`. Instead, remove
/// the `print` and pass the message directly as the second expression,
/// allowing `stderr` to capture the message in a well-formatted context.
/// ///
/// ## Example /// ## Example
/// ```python /// ```python
@ -41,7 +43,7 @@ pub struct AssertWithPrintMessage;
impl AlwaysFixableViolation for AssertWithPrintMessage { impl AlwaysFixableViolation for AssertWithPrintMessage {
#[derive_message_formats] #[derive_message_formats]
fn message(&self) -> String { fn message(&self) -> String {
"`print()` expression in `assert` statement is likely unintentional".to_string() "`print()` call in `assert` statement is likely unintentional".to_string()
} }
fn fix_title(&self) -> String { fn fix_title(&self) -> String {

View file

@ -19,7 +19,7 @@ pub(crate) use mutable_dataclass_default::*;
pub(crate) use mutable_fromkeys_value::*; pub(crate) use mutable_fromkeys_value::*;
pub(crate) use never_union::*; pub(crate) use never_union::*;
pub(crate) use none_not_at_end_of_union::*; pub(crate) use none_not_at_end_of_union::*;
pub(crate) use parenthesize_logical_operators::*; pub(crate) use parenthesize_chained_operators::*;
pub(crate) use post_init_default::*; pub(crate) use post_init_default::*;
pub(crate) use quadratic_list_summation::*; pub(crate) use quadratic_list_summation::*;
pub(crate) use redirected_noqa::*; pub(crate) use redirected_noqa::*;
@ -61,7 +61,7 @@ mod mutable_dataclass_default;
mod mutable_fromkeys_value; mod mutable_fromkeys_value;
mod never_union; mod never_union;
mod none_not_at_end_of_union; mod none_not_at_end_of_union;
mod parenthesize_logical_operators; mod parenthesize_chained_operators;
mod post_init_default; mod post_init_default;
mod quadratic_list_summation; mod quadratic_list_summation;
mod redirected_noqa; mod redirected_noqa;

View file

@ -1,8 +1,7 @@
--- ---
source: crates/ruff_linter/src/rules/ruff/mod.rs source: crates/ruff_linter/src/rules/ruff/mod.rs
snapshot_kind: text
--- ---
RUF030.py:6:14: RUF030 [*] `print()` expression in `assert` statement is likely unintentional RUF030.py:6:14: RUF030 [*] `print()` call in `assert` statement is likely unintentional
| |
4 | # Expects: 4 | # Expects:
5 | # - single StringLiteral 5 | # - single StringLiteral
@ -23,7 +22,7 @@ RUF030.py:6:14: RUF030 [*] `print()` expression in `assert` statement is likely
8 8 | # Concatenated string literals 8 8 | # Concatenated string literals
9 9 | # Expects: 9 9 | # Expects:
RUF030.py:11:14: RUF030 [*] `print()` expression in `assert` statement is likely unintentional RUF030.py:11:14: RUF030 [*] `print()` call in `assert` statement is likely unintentional
| |
9 | # Expects: 9 | # Expects:
10 | # - single StringLiteral 10 | # - single StringLiteral
@ -44,7 +43,7 @@ RUF030.py:11:14: RUF030 [*] `print()` expression in `assert` statement is likely
13 13 | # Positional arguments, string literals 13 13 | # Positional arguments, string literals
14 14 | # Expects: 14 14 | # Expects:
RUF030.py:16:14: RUF030 [*] `print()` expression in `assert` statement is likely unintentional RUF030.py:16:14: RUF030 [*] `print()` call in `assert` statement is likely unintentional
| |
14 | # Expects: 14 | # Expects:
15 | # - single StringLiteral concatenated with " " 15 | # - single StringLiteral concatenated with " "
@ -65,7 +64,7 @@ RUF030.py:16:14: RUF030 [*] `print()` expression in `assert` statement is likely
18 18 | # Concatenated string literals combined with Positional arguments 18 18 | # Concatenated string literals combined with Positional arguments
19 19 | # Expects: 19 19 | # Expects:
RUF030.py:21:14: RUF030 [*] `print()` expression in `assert` statement is likely unintentional RUF030.py:21:14: RUF030 [*] `print()` call in `assert` statement is likely unintentional
| |
19 | # Expects: 19 | # Expects:
20 | # - single stringliteral concatenated with " " only between `print` and `is` 20 | # - single stringliteral concatenated with " " only between `print` and `is`
@ -86,7 +85,7 @@ RUF030.py:21:14: RUF030 [*] `print()` expression in `assert` statement is likely
23 23 | # Positional arguments, string literals with a variable 23 23 | # Positional arguments, string literals with a variable
24 24 | # Expects: 24 24 | # Expects:
RUF030.py:26:14: RUF030 [*] `print()` expression in `assert` statement is likely unintentional RUF030.py:26:14: RUF030 [*] `print()` call in `assert` statement is likely unintentional
| |
24 | # Expects: 24 | # Expects:
25 | # - single FString concatenated with " " 25 | # - single FString concatenated with " "
@ -107,7 +106,7 @@ RUF030.py:26:14: RUF030 [*] `print()` expression in `assert` statement is likely
28 28 | # Mixed brackets string literals 28 28 | # Mixed brackets string literals
29 29 | # Expects: 29 29 | # Expects:
RUF030.py:31:14: RUF030 [*] `print()` expression in `assert` statement is likely unintentional RUF030.py:31:14: RUF030 [*] `print()` call in `assert` statement is likely unintentional
| |
29 | # Expects: 29 | # Expects:
30 | # - single StringLiteral concatenated with " " 30 | # - single StringLiteral concatenated with " "
@ -128,7 +127,7 @@ RUF030.py:31:14: RUF030 [*] `print()` expression in `assert` statement is likely
33 33 | # Mixed brackets with other brackets inside 33 33 | # Mixed brackets with other brackets inside
34 34 | # Expects: 34 34 | # Expects:
RUF030.py:36:14: RUF030 [*] `print()` expression in `assert` statement is likely unintentional RUF030.py:36:14: RUF030 [*] `print()` call in `assert` statement is likely unintentional
| |
34 | # Expects: 34 | # Expects:
35 | # - single StringLiteral concatenated with " " and escaped brackets 35 | # - single StringLiteral concatenated with " " and escaped brackets
@ -149,7 +148,7 @@ RUF030.py:36:14: RUF030 [*] `print()` expression in `assert` statement is likely
38 38 | # Positional arguments, string literals with a separator 38 38 | # Positional arguments, string literals with a separator
39 39 | # Expects: 39 39 | # Expects:
RUF030.py:41:14: RUF030 [*] `print()` expression in `assert` statement is likely unintentional RUF030.py:41:14: RUF030 [*] `print()` call in `assert` statement is likely unintentional
| |
39 | # Expects: 39 | # Expects:
40 | # - single StringLiteral concatenated with "|" 40 | # - single StringLiteral concatenated with "|"
@ -170,7 +169,7 @@ RUF030.py:41:14: RUF030 [*] `print()` expression in `assert` statement is likely
43 43 | # Positional arguments, string literals with None as separator 43 43 | # Positional arguments, string literals with None as separator
44 44 | # Expects: 44 44 | # Expects:
RUF030.py:46:14: RUF030 [*] `print()` expression in `assert` statement is likely unintentional RUF030.py:46:14: RUF030 [*] `print()` call in `assert` statement is likely unintentional
| |
44 | # Expects: 44 | # Expects:
45 | # - single StringLiteral concatenated with " " 45 | # - single StringLiteral concatenated with " "
@ -191,7 +190,7 @@ RUF030.py:46:14: RUF030 [*] `print()` expression in `assert` statement is likely
48 48 | # Positional arguments, string literals with variable as separator, needs f-string 48 48 | # Positional arguments, string literals with variable as separator, needs f-string
49 49 | # Expects: 49 49 | # Expects:
RUF030.py:51:14: RUF030 [*] `print()` expression in `assert` statement is likely unintentional RUF030.py:51:14: RUF030 [*] `print()` call in `assert` statement is likely unintentional
| |
49 | # Expects: 49 | # Expects:
50 | # - single FString concatenated with "{U00A0}" 50 | # - single FString concatenated with "{U00A0}"
@ -212,7 +211,7 @@ RUF030.py:51:14: RUF030 [*] `print()` expression in `assert` statement is likely
53 53 | # Unnecessary f-string 53 53 | # Unnecessary f-string
54 54 | # Expects: 54 54 | # Expects:
RUF030.py:56:14: RUF030 [*] `print()` expression in `assert` statement is likely unintentional RUF030.py:56:14: RUF030 [*] `print()` call in `assert` statement is likely unintentional
| |
54 | # Expects: 54 | # Expects:
55 | # - single StringLiteral 55 | # - single StringLiteral
@ -233,7 +232,7 @@ RUF030.py:56:14: RUF030 [*] `print()` expression in `assert` statement is likely
58 58 | # Positional arguments, string literals and f-strings 58 58 | # Positional arguments, string literals and f-strings
59 59 | # Expects: 59 59 | # Expects:
RUF030.py:61:14: RUF030 [*] `print()` expression in `assert` statement is likely unintentional RUF030.py:61:14: RUF030 [*] `print()` call in `assert` statement is likely unintentional
| |
59 | # Expects: 59 | # Expects:
60 | # - single FString concatenated with " " 60 | # - single FString concatenated with " "
@ -254,7 +253,7 @@ RUF030.py:61:14: RUF030 [*] `print()` expression in `assert` statement is likely
63 63 | # Positional arguments, string literals and f-strings with a separator 63 63 | # Positional arguments, string literals and f-strings with a separator
64 64 | # Expects: 64 64 | # Expects:
RUF030.py:66:14: RUF030 [*] `print()` expression in `assert` statement is likely unintentional RUF030.py:66:14: RUF030 [*] `print()` call in `assert` statement is likely unintentional
| |
64 | # Expects: 64 | # Expects:
65 | # - single FString concatenated with "|" 65 | # - single FString concatenated with "|"
@ -275,7 +274,7 @@ RUF030.py:66:14: RUF030 [*] `print()` expression in `assert` statement is likely
68 68 | # A single f-string 68 68 | # A single f-string
69 69 | # Expects: 69 69 | # Expects:
RUF030.py:71:14: RUF030 [*] `print()` expression in `assert` statement is likely unintentional RUF030.py:71:14: RUF030 [*] `print()` call in `assert` statement is likely unintentional
| |
69 | # Expects: 69 | # Expects:
70 | # - single FString 70 | # - single FString
@ -296,7 +295,7 @@ RUF030.py:71:14: RUF030 [*] `print()` expression in `assert` statement is likely
73 73 | # A single f-string with a redundant separator 73 73 | # A single f-string with a redundant separator
74 74 | # Expects: 74 74 | # Expects:
RUF030.py:76:14: RUF030 [*] `print()` expression in `assert` statement is likely unintentional RUF030.py:76:14: RUF030 [*] `print()` call in `assert` statement is likely unintentional
| |
74 | # Expects: 74 | # Expects:
75 | # - single FString 75 | # - single FString
@ -317,7 +316,7 @@ RUF030.py:76:14: RUF030 [*] `print()` expression in `assert` statement is likely
78 78 | # Complex f-string with variable as separator 78 78 | # Complex f-string with variable as separator
79 79 | # Expects: 79 79 | # Expects:
RUF030.py:83:14: RUF030 [*] `print()` expression in `assert` statement is likely unintentional RUF030.py:83:14: RUF030 [*] `print()` call in `assert` statement is likely unintentional
| |
81 | condition = "True is True" 81 | condition = "True is True"
82 | maintainer = "John Doe" 82 | maintainer = "John Doe"
@ -338,7 +337,7 @@ RUF030.py:83:14: RUF030 [*] `print()` expression in `assert` statement is likely
85 85 | # Empty print 85 85 | # Empty print
86 86 | # Expects: 86 86 | # Expects:
RUF030.py:88:14: RUF030 [*] `print()` expression in `assert` statement is likely unintentional RUF030.py:88:14: RUF030 [*] `print()` call in `assert` statement is likely unintentional
| |
86 | # Expects: 86 | # Expects:
87 | # - `msg` entirely removed from assertion 87 | # - `msg` entirely removed from assertion
@ -359,7 +358,7 @@ RUF030.py:88:14: RUF030 [*] `print()` expression in `assert` statement is likely
90 90 | # Empty print with separator 90 90 | # Empty print with separator
91 91 | # Expects: 91 91 | # Expects:
RUF030.py:93:14: RUF030 [*] `print()` expression in `assert` statement is likely unintentional RUF030.py:93:14: RUF030 [*] `print()` call in `assert` statement is likely unintentional
| |
91 | # Expects: 91 | # Expects:
92 | # - `msg` entirely removed from assertion 92 | # - `msg` entirely removed from assertion
@ -380,7 +379,7 @@ RUF030.py:93:14: RUF030 [*] `print()` expression in `assert` statement is likely
95 95 | # Custom print function that actually returns a string 95 95 | # Custom print function that actually returns a string
96 96 | # Expects: 96 96 | # Expects:
RUF030.py:108:14: RUF030 [*] `print()` expression in `assert` statement is likely unintentional RUF030.py:108:14: RUF030 [*] `print()` call in `assert` statement is likely unintentional
| |
106 | # Expects: 106 | # Expects:
107 | # - single StringLiteral 107 | # - single StringLiteral