[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) {
flake8_pyi::rules::redundant_numeric_union(checker, parameters);
}
if checker.enabled(Rule::PrePep570PositionalArgument) {
flake8_pyi::rules::pre_pep570_positional_argument(checker, function_def);
if checker.enabled(Rule::Pep484StylePositionalOnlyParameter) {
flake8_pyi::rules::pep_484_positional_parameter(checker, function_def);
}
if checker.enabled(Rule::DunderFunctionName) {
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, "061") => (RuleGroup::Preview, rules::flake8_pyi::rules::RedundantNoneLiteral),
(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, "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;
/// ## 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?
/// The FastAPI documentation recommends the use of `Annotated` for defining
/// route dependencies and parameters, rather than using `Depends` directly
/// with a default value.
///
/// 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.
/// The [FastAPI documentation] recommends the use of [`typing.Annotated`] for
/// defining route dependencies and parameters, rather than using `Depends`,
/// `Query` or similar as a default value for a parameter. Using this approach
/// everywhere helps ensure consistency and clarity in defining dependencies
/// and parameters.
///
/// ## Example
///
@ -55,7 +55,9 @@ use crate::settings::types::PythonVersion;
/// async def read_items(commons: Annotated[dict, Depends(common_parameters)]):
/// 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]
pub struct FastApiNonAnnotatedDependency;
@ -72,7 +74,7 @@ impl Violation for FastApiNonAnnotatedDependency {
}
}
/// RUF103
/// FAST002
pub(crate) fn fastapi_non_annotated_dependency(
checker: &mut Checker,
function_def: &ast::StmtFunctionDef,

View file

@ -73,7 +73,7 @@ impl AlwaysFixableViolation for FastApiRedundantResponseModel {
}
}
/// RUF102
/// FAST001
pub(crate) fn fastapi_redundant_response_model(
checker: &mut Checker,
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
/// across calls, which can lead to unexpected behavior.
///
/// Instead, prefer to use immutable data structures; or, take `None` as a
/// default, and initialize a new mutable object inside for each call using the
/// `.set()` method.
/// Instead, prefer to use immutable data structures. Alternatively, take
/// `None` as a default, and initialize a new mutable object inside for each
/// call using the `.set()` method.
///
/// Types outside the standard library can be marked as immutable with the
/// [`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::QuotedAnnotationInStub, Path::new("PYI020.py"))]
#[test_case(Rule::QuotedAnnotationInStub, Path::new("PYI020.pyi"))]
#[test_case(Rule::PrePep570PositionalArgument, Path::new("PYI063.py"))]
#[test_case(Rule::PrePep570PositionalArgument, Path::new("PYI063.pyi"))]
#[test_case(Rule::Pep484StylePositionalOnlyParameter, Path::new("PYI063.py"))]
#[test_case(Rule::Pep484StylePositionalOnlyParameter, Path::new("PYI063.pyi"))]
#[test_case(Rule::RedundantFinalLiteral, Path::new("PYI064.py"))]
#[test_case(Rule::RedundantFinalLiteral, Path::new("PYI064.pyi"))]
#[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
/// and can lead to bugs.
///
/// For example, `sys.version_info > (3, 8)` will also match `3.8.10`,
/// while `sys.version_info <= (3, 8)` will _not_ match `3.8.10`:
/// For example, `sys.version_info > (3, 8, 1)` will resolve to `True` if your
/// 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
/// >>> import sys
@ -61,16 +62,20 @@ impl Violation for BadVersionInfoComparison {
}
/// ## What it does
/// Checks for if-else statements with `sys.version_info` comparisons that use
/// `<` comparators.
/// Checks for code that branches on `sys.version_info` comparisons where
/// branches corresponding to older Python versions come before branches
/// corresponding to newer Python versions.
///
/// ## Why is this bad?
/// As a convention, branches that correspond to newer Python versions should
/// come first when using `sys.version_info` comparisons. This makes it easier
/// to understand the desired behavior, which typically corresponds to the
/// latest Python versions.
/// come first. This makes it easier to understand the desired behavior, which
/// typically corresponds to the 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
///
@ -101,7 +106,7 @@ pub struct BadVersionInfoOrder;
impl Violation for BadVersionInfoOrder {
#[derive_message_formats]
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;
/// ## 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?
/// Historically, [PEP 484] recommended prefixing positional-only arguments
/// with a double underscore (`__`). However, [PEP 570] introduced a dedicated
/// syntax for positional-only arguments, which should be preferred.
/// Historically, [PEP 484] recommended prefixing parameter names with double
/// underscores (`__`) to indicate to a type checker that they were
/// 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
///
@ -33,12 +40,12 @@ use crate::settings::types::PythonVersion;
/// [PEP 484]: https://peps.python.org/pep-0484/#positional-only-arguments
/// [PEP 570]: https://peps.python.org/pep-0570
#[violation]
pub struct PrePep570PositionalArgument;
pub struct Pep484StylePositionalOnlyParameter;
impl Violation for PrePep570PositionalArgument {
impl Violation for Pep484StylePositionalOnlyParameter {
#[derive_message_formats]
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> {
@ -47,7 +54,7 @@ impl Violation for PrePep570PositionalArgument {
}
/// PYI063
pub(crate) fn pre_pep570_positional_argument(
pub(crate) fn pep_484_positional_parameter(
checker: &mut Checker,
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 is_pre_pep570_positional_only(arg) {
if is_old_style_positional_only(arg) {
checker.diagnostics.push(Diagnostic::new(
PrePep570PositionalArgument,
Pep484StylePositionalOnlyParameter,
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 `__`).
fn is_pre_pep570_positional_only(arg: &ParameterWithDefault) -> bool {
fn is_old_style_positional_only(arg: &ParameterWithDefault) -> bool {
let arg_name = &arg.parameter.name;
arg_name.starts_with("__") && !arg_name.ends_with("__")
}

View file

@ -11,18 +11,25 @@ use crate::Locator;
/// Checks for redundant `Final[Literal[...]]` annotations.
///
/// ## Why is this bad?
/// A `Final[Literal[...]]` annotation can be replaced with `Final`; the literal
/// use is unnecessary.
/// All constant variables annotated as `Final` are understood as implicitly
/// 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
///
/// ```pyi
/// from typing import Final, Literal
///
/// x: Final[Literal[42]]
/// y: Final[Literal[42]] = 42
/// ```
///
/// Use instead:
/// ```pyi
/// from typing import Final, Literal
///
/// x: Final = 42
/// y: Final = 42
/// ```

View file

@ -1,8 +1,7 @@
---
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
5 |
@ -13,7 +12,7 @@ PYI063.py:6:9: PYI063 Use PEP 570 syntax for positional-only arguments
|
= 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
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
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
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
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
23 | @staticmethod
@ -44,7 +43,7 @@ PYI063.py:24:14: PYI063 Use PEP 570 syntax for positional-only arguments
|
= 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
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
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
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
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
27 | @staticmethod
@ -77,7 +76,7 @@ PYI063.py:28:25: PYI063 Use PEP 570 syntax for positional-only arguments
|
= 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
29 | @classmethod
@ -88,7 +87,7 @@ PYI063.py:30:23: PYI063 Use PEP 570 syntax for positional-only arguments
|
= 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):
51 | @classmethod
@ -99,7 +98,7 @@ PYI063.py:52:23: PYI063 Use PEP 570 syntax for positional-only arguments
|
= 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):
55 | @classmethod

View file

@ -1,8 +1,7 @@
---
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
5 |
@ -13,7 +12,7 @@ PYI063.pyi:6:9: PYI063 Use PEP 570 syntax for positional-only arguments
|
= 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
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
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
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
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
23 | @staticmethod
@ -44,7 +43,7 @@ PYI063.pyi:24:14: PYI063 Use PEP 570 syntax for positional-only arguments
|
= 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
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
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
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
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
27 | @staticmethod
@ -77,7 +76,7 @@ PYI063.pyi:28:25: PYI063 Use PEP 570 syntax for positional-only arguments
|
= 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
29 | @classmethod
@ -88,7 +87,7 @@ PYI063.pyi:30:23: PYI063 Use PEP 570 syntax for positional-only arguments
|
= 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):
51 | @classmethod
@ -99,7 +98,7 @@ PYI063.pyi:52:23: PYI063 Use PEP 570 syntax for positional-only arguments
|
= 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):
55 | @classmethod

View file

@ -1,8 +1,7 @@
---
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
2 |
@ -12,7 +11,7 @@ PYI066.pyi:3:4: PYI066 Use `>=` when using `if`-`else` with `sys.version_info` c
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): ...
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)"
|
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)"
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)"
|
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)"
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:
|
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):
19 | ...

View file

@ -6,14 +6,18 @@ use ruff_text_size::{Ranged, TextRange};
use crate::checkers::ast::Checker;
/// ## 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?
/// 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
/// library (e.g., Generator, AsyncGenerator) are now optional.
/// values. Following this change, several standard-library classes were
/// 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.
///
/// ## Examples

View file

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

View file

@ -19,7 +19,7 @@ pub(crate) use mutable_dataclass_default::*;
pub(crate) use mutable_fromkeys_value::*;
pub(crate) use never_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 quadratic_list_summation::*;
pub(crate) use redirected_noqa::*;
@ -61,7 +61,7 @@ mod mutable_dataclass_default;
mod mutable_fromkeys_value;
mod never_union;
mod none_not_at_end_of_union;
mod parenthesize_logical_operators;
mod parenthesize_chained_operators;
mod post_init_default;
mod quadratic_list_summation;
mod redirected_noqa;

View file

@ -1,8 +1,7 @@
---
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:
5 | # - single StringLiteral
@ -23,7 +22,7 @@ RUF030.py:6:14: RUF030 [*] `print()` expression in `assert` statement is likely
8 8 | # Concatenated string literals
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:
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
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:
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
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:
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
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:
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
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:
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
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:
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
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:
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
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:
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
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:
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
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:
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
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:
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
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:
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
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:
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
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:
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
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"
82 | maintainer = "John Doe"
@ -338,7 +337,7 @@ RUF030.py:83:14: RUF030 [*] `print()` expression in `assert` statement is likely
85 85 | # Empty print
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:
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
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:
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
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:
107 | # - single StringLiteral