mirror of https://github.com/astral-sh/ruff
Ignore `ClassVar` annotation for `RUF008`, `RUF009` (#4081)
This commit is contained in:
parent
4d3a1e0581
commit
37483f3ac9
|
|
@ -1,6 +1,6 @@
|
|||
import typing
|
||||
from dataclasses import dataclass, field
|
||||
from typing import Sequence
|
||||
from typing import ClassVar, Sequence
|
||||
|
||||
KNOWINGLY_MUTABLE_DEFAULT = []
|
||||
|
||||
|
|
@ -13,6 +13,7 @@ class A:
|
|||
ignored_via_comment: list[int] = [] # noqa: RUF008
|
||||
correct_code: list[int] = KNOWINGLY_MUTABLE_DEFAULT
|
||||
perfectly_fine: list[int] = field(default_factory=list)
|
||||
class_variable: typing.ClassVar[list[int]] = []
|
||||
|
||||
|
||||
@dataclass
|
||||
|
|
@ -23,3 +24,4 @@ class B:
|
|||
ignored_via_comment: list[int] = [] # noqa: RUF008
|
||||
correct_code: list[int] = KNOWINGLY_MUTABLE_DEFAULT
|
||||
perfectly_fine: list[int] = field(default_factory=list)
|
||||
class_variable: ClassVar[list[int]] = []
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
import typing
|
||||
from dataclasses import dataclass
|
||||
from typing import NamedTuple
|
||||
from typing import ClassVar, NamedTuple
|
||||
|
||||
|
||||
def default_function() -> list[int]:
|
||||
|
|
@ -13,6 +14,8 @@ class ImmutableType(NamedTuple):
|
|||
@dataclass()
|
||||
class A:
|
||||
hidden_mutable_default: list[int] = default_function()
|
||||
class_variable: typing.ClassVar[list[int]] = default_function()
|
||||
another_class_var: ClassVar[list[int]] = default_function()
|
||||
|
||||
|
||||
DEFAULT_IMMUTABLETYPE_FOR_ALL_DATACLASSES = ImmutableType(40)
|
||||
|
|
|
|||
|
|
@ -151,13 +151,26 @@ fn is_allowed_func(context: &Context, func: &Expr) -> bool {
|
|||
})
|
||||
}
|
||||
|
||||
/// Returns `true` if the given [`Expr`] is a `typing.ClassVar` annotation.
|
||||
fn is_class_var_annotation(context: &Context, annotation: &Expr) -> bool {
|
||||
let ExprKind::Subscript { value, .. } = &annotation.node else {
|
||||
return false;
|
||||
};
|
||||
context.match_typing_expr(value, "ClassVar")
|
||||
}
|
||||
|
||||
/// RUF009
|
||||
pub fn function_call_in_dataclass_defaults(checker: &mut Checker, body: &[Stmt]) {
|
||||
for statement in body {
|
||||
if let StmtKind::AnnAssign {
|
||||
value: Some(expr), ..
|
||||
annotation,
|
||||
value: Some(expr),
|
||||
..
|
||||
} = &statement.node
|
||||
{
|
||||
if is_class_var_annotation(&checker.ctx, annotation) {
|
||||
continue;
|
||||
}
|
||||
if let ExprKind::Call { func, .. } = &expr.node {
|
||||
if !is_allowed_func(&checker.ctx, func) {
|
||||
checker.diagnostics.push(Diagnostic::new(
|
||||
|
|
@ -181,7 +194,10 @@ pub fn mutable_dataclass_default(checker: &mut Checker, body: &[Stmt]) {
|
|||
value: Some(value),
|
||||
..
|
||||
} => {
|
||||
if !is_immutable_annotation(&checker.ctx, annotation) && is_mutable_expr(value) {
|
||||
if !is_class_var_annotation(&checker.ctx, annotation)
|
||||
&& !is_immutable_annotation(&checker.ctx, annotation)
|
||||
&& is_mutable_expr(value)
|
||||
{
|
||||
checker
|
||||
.diagnostics
|
||||
.push(Diagnostic::new(MutableDataclassDefault, Range::from(value)));
|
||||
|
|
|
|||
|
|
@ -21,24 +21,24 @@ RUF008.py:12:26: RUF008 Do not use mutable default values for dataclass attribut
|
|||
16 | correct_code: list[int] = KNOWINGLY_MUTABLE_DEFAULT
|
||||
|
|
||||
|
||||
RUF008.py:20:34: RUF008 Do not use mutable default values for dataclass attributes
|
||||
RUF008.py:21:34: RUF008 Do not use mutable default values for dataclass attributes
|
||||
|
|
||||
20 | @dataclass
|
||||
21 | class B:
|
||||
22 | mutable_default: list[int] = []
|
||||
21 | @dataclass
|
||||
22 | class B:
|
||||
23 | mutable_default: list[int] = []
|
||||
| ^^ RUF008
|
||||
23 | immutable_annotation: Sequence[int] = []
|
||||
24 | without_annotation = []
|
||||
24 | immutable_annotation: Sequence[int] = []
|
||||
25 | without_annotation = []
|
||||
|
|
||||
|
||||
RUF008.py:22:26: RUF008 Do not use mutable default values for dataclass attributes
|
||||
RUF008.py:23:26: RUF008 Do not use mutable default values for dataclass attributes
|
||||
|
|
||||
22 | mutable_default: list[int] = []
|
||||
23 | immutable_annotation: Sequence[int] = []
|
||||
24 | without_annotation = []
|
||||
23 | mutable_default: list[int] = []
|
||||
24 | immutable_annotation: Sequence[int] = []
|
||||
25 | without_annotation = []
|
||||
| ^^ RUF008
|
||||
25 | ignored_via_comment: list[int] = [] # noqa: RUF008
|
||||
26 | correct_code: list[int] = KNOWINGLY_MUTABLE_DEFAULT
|
||||
26 | ignored_via_comment: list[int] = [] # noqa: RUF008
|
||||
27 | correct_code: list[int] = KNOWINGLY_MUTABLE_DEFAULT
|
||||
|
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -1,42 +1,44 @@
|
|||
---
|
||||
source: crates/ruff/src/rules/ruff/mod.rs
|
||||
---
|
||||
RUF009.py:15:41: RUF009 Do not perform function call `default_function` in dataclass defaults
|
||||
RUF009.py:16:41: RUF009 Do not perform function call `default_function` in dataclass defaults
|
||||
|
|
||||
15 | @dataclass()
|
||||
16 | class A:
|
||||
17 | hidden_mutable_default: list[int] = default_function()
|
||||
16 | @dataclass()
|
||||
17 | class A:
|
||||
18 | hidden_mutable_default: list[int] = default_function()
|
||||
| ^^^^^^^^^^^^^^^^^^ RUF009
|
||||
19 | class_variable: typing.ClassVar[list[int]] = default_function()
|
||||
20 | another_class_var: ClassVar[list[int]] = default_function()
|
||||
|
|
||||
|
||||
RUF009.py:24:41: RUF009 Do not perform function call `default_function` in dataclass defaults
|
||||
RUF009.py:27:41: RUF009 Do not perform function call `default_function` in dataclass defaults
|
||||
|
|
||||
24 | @dataclass
|
||||
25 | class B:
|
||||
26 | hidden_mutable_default: list[int] = default_function()
|
||||
27 | @dataclass
|
||||
28 | class B:
|
||||
29 | hidden_mutable_default: list[int] = default_function()
|
||||
| ^^^^^^^^^^^^^^^^^^ RUF009
|
||||
27 | another_dataclass: A = A()
|
||||
28 | not_optimal: ImmutableType = ImmutableType(20)
|
||||
30 | another_dataclass: A = A()
|
||||
31 | not_optimal: ImmutableType = ImmutableType(20)
|
||||
|
|
||||
|
||||
RUF009.py:25:28: RUF009 Do not perform function call `A` in dataclass defaults
|
||||
RUF009.py:28:28: RUF009 Do not perform function call `A` in dataclass defaults
|
||||
|
|
||||
25 | class B:
|
||||
26 | hidden_mutable_default: list[int] = default_function()
|
||||
27 | another_dataclass: A = A()
|
||||
28 | class B:
|
||||
29 | hidden_mutable_default: list[int] = default_function()
|
||||
30 | another_dataclass: A = A()
|
||||
| ^^^ RUF009
|
||||
28 | not_optimal: ImmutableType = ImmutableType(20)
|
||||
29 | good_variant: ImmutableType = DEFAULT_IMMUTABLETYPE_FOR_ALL_DATACLASSES
|
||||
31 | not_optimal: ImmutableType = ImmutableType(20)
|
||||
32 | good_variant: ImmutableType = DEFAULT_IMMUTABLETYPE_FOR_ALL_DATACLASSES
|
||||
|
|
||||
|
||||
RUF009.py:26:34: RUF009 Do not perform function call `ImmutableType` in dataclass defaults
|
||||
RUF009.py:29:34: RUF009 Do not perform function call `ImmutableType` in dataclass defaults
|
||||
|
|
||||
26 | hidden_mutable_default: list[int] = default_function()
|
||||
27 | another_dataclass: A = A()
|
||||
28 | not_optimal: ImmutableType = ImmutableType(20)
|
||||
29 | hidden_mutable_default: list[int] = default_function()
|
||||
30 | another_dataclass: A = A()
|
||||
31 | not_optimal: ImmutableType = ImmutableType(20)
|
||||
| ^^^^^^^^^^^^^^^^^ RUF009
|
||||
29 | good_variant: ImmutableType = DEFAULT_IMMUTABLETYPE_FOR_ALL_DATACLASSES
|
||||
30 | okay_variant: A = DEFAULT_A_FOR_ALL_DATACLASSES
|
||||
32 | good_variant: ImmutableType = DEFAULT_IMMUTABLETYPE_FOR_ALL_DATACLASSES
|
||||
33 | okay_variant: A = DEFAULT_A_FOR_ALL_DATACLASSES
|
||||
|
|
||||
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue