Ignore `ClassVar` annotation for `RUF008`, `RUF009` (#4081)

This commit is contained in:
Dhruv Manilawala 2023-04-25 05:28:30 +05:30 committed by GitHub
parent 4d3a1e0581
commit 37483f3ac9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 61 additions and 38 deletions

View File

@ -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]] = []

View File

@ -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)

View File

@ -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)));

View File

@ -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
|

View File

@ -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
|