mirror of
https://github.com/astral-sh/ruff.git
synced 2025-09-26 11:59:35 +00:00
Allow typing.Final
for mutable-class-default annotations
(RUF012
) (#5274)
## Summary See: https://github.com/astral-sh/ruff/issues/5243.
This commit is contained in:
parent
1229600e1d
commit
ac146e11f0
5 changed files with 33 additions and 20 deletions
|
@ -1,5 +1,5 @@
|
|||
import typing
|
||||
from typing import ClassVar, Sequence
|
||||
from typing import ClassVar, Sequence, Final
|
||||
|
||||
KNOWINGLY_MUTABLE_DEFAULT = []
|
||||
|
||||
|
@ -10,6 +10,7 @@ class A:
|
|||
without_annotation = []
|
||||
correct_code: list[int] = KNOWINGLY_MUTABLE_DEFAULT
|
||||
class_variable: typing.ClassVar[list[int]] = []
|
||||
final_variable: typing.Final[list[int]] = []
|
||||
|
||||
|
||||
class B:
|
||||
|
@ -18,6 +19,7 @@ class B:
|
|||
without_annotation = []
|
||||
correct_code: list[int] = KNOWINGLY_MUTABLE_DEFAULT
|
||||
class_variable: ClassVar[list[int]] = []
|
||||
final_variable: Final[list[int]] = []
|
||||
|
||||
|
||||
from dataclasses import dataclass, field
|
||||
|
@ -31,6 +33,7 @@ class C:
|
|||
correct_code: list[int] = KNOWINGLY_MUTABLE_DEFAULT
|
||||
perfectly_fine: list[int] = field(default_factory=list)
|
||||
class_variable: ClassVar[list[int]] = []
|
||||
final_variable: Final[list[int]] = []
|
||||
|
||||
|
||||
from pydantic import BaseModel
|
||||
|
@ -43,3 +46,4 @@ class D(BaseModel):
|
|||
correct_code: list[int] = KNOWINGLY_MUTABLE_DEFAULT
|
||||
perfectly_fine: list[int] = field(default_factory=list)
|
||||
class_variable: ClassVar[list[int]] = []
|
||||
final_variable: Final[list[int]] = []
|
||||
|
|
|
@ -55,7 +55,7 @@ use crate::rules::ruff::rules::helpers::{
|
|||
/// - `flake8-bugbear.extend-immutable-calls`
|
||||
#[violation]
|
||||
pub struct FunctionCallInDataclassDefaultArgument {
|
||||
pub name: Option<String>,
|
||||
name: Option<String>,
|
||||
}
|
||||
|
||||
impl Violation for FunctionCallInDataclassDefaultArgument {
|
||||
|
|
|
@ -18,6 +18,14 @@ pub(super) fn is_class_var_annotation(annotation: &Expr, semantic: &SemanticMode
|
|||
semantic.match_typing_expr(value, "ClassVar")
|
||||
}
|
||||
|
||||
/// Returns `true` if the given [`Expr`] is a `typing.Final` annotation.
|
||||
pub(super) fn is_final_annotation(annotation: &Expr, semantic: &SemanticModel) -> bool {
|
||||
let Expr::Subscript(ast::ExprSubscript { value, .. }) = &annotation else {
|
||||
return false;
|
||||
};
|
||||
semantic.match_typing_expr(value, "Final")
|
||||
}
|
||||
|
||||
/// Returns `true` if the given class is a dataclass.
|
||||
pub(super) fn is_dataclass(class_def: &ast::StmtClassDef, semantic: &SemanticModel) -> bool {
|
||||
class_def.decorator_list.iter().any(|decorator| {
|
||||
|
|
|
@ -6,7 +6,7 @@ use ruff_python_semantic::analyze::typing::{is_immutable_annotation, is_mutable_
|
|||
|
||||
use crate::checkers::ast::Checker;
|
||||
use crate::rules::ruff::rules::helpers::{
|
||||
is_class_var_annotation, is_dataclass, is_pydantic_model,
|
||||
is_class_var_annotation, is_dataclass, is_final_annotation, is_pydantic_model,
|
||||
};
|
||||
|
||||
/// ## What it does
|
||||
|
@ -56,6 +56,7 @@ pub(crate) fn mutable_class_default(checker: &mut Checker, class_def: &ast::Stmt
|
|||
}) => {
|
||||
if is_mutable_expr(value, checker.semantic())
|
||||
&& !is_class_var_annotation(annotation, checker.semantic())
|
||||
&& !is_final_annotation(annotation, checker.semantic())
|
||||
&& !is_immutable_annotation(annotation, checker.semantic())
|
||||
&& !is_dataclass(class_def, checker.semantic())
|
||||
{
|
||||
|
|
|
@ -20,33 +20,33 @@ RUF012.py:10:26: RUF012 Mutable class attributes should be annotated with `typin
|
|||
12 | class_variable: typing.ClassVar[list[int]] = []
|
||||
|
|
||||
|
||||
RUF012.py:16:34: RUF012 Mutable class attributes should be annotated with `typing.ClassVar`
|
||||
RUF012.py:17:34: RUF012 Mutable class attributes should be annotated with `typing.ClassVar`
|
||||
|
|
||||
15 | class B:
|
||||
16 | mutable_default: list[int] = []
|
||||
16 | class B:
|
||||
17 | mutable_default: list[int] = []
|
||||
| ^^ RUF012
|
||||
17 | immutable_annotation: Sequence[int] = []
|
||||
18 | without_annotation = []
|
||||
18 | immutable_annotation: Sequence[int] = []
|
||||
19 | without_annotation = []
|
||||
|
|
||||
|
||||
RUF012.py:18:26: RUF012 Mutable class attributes should be annotated with `typing.ClassVar`
|
||||
RUF012.py:19:26: RUF012 Mutable class attributes should be annotated with `typing.ClassVar`
|
||||
|
|
||||
16 | mutable_default: list[int] = []
|
||||
17 | immutable_annotation: Sequence[int] = []
|
||||
18 | without_annotation = []
|
||||
17 | mutable_default: list[int] = []
|
||||
18 | immutable_annotation: Sequence[int] = []
|
||||
19 | without_annotation = []
|
||||
| ^^ RUF012
|
||||
19 | correct_code: list[int] = KNOWINGLY_MUTABLE_DEFAULT
|
||||
20 | class_variable: ClassVar[list[int]] = []
|
||||
20 | correct_code: list[int] = KNOWINGLY_MUTABLE_DEFAULT
|
||||
21 | class_variable: ClassVar[list[int]] = []
|
||||
|
|
||||
|
||||
RUF012.py:30:26: RUF012 Mutable class attributes should be annotated with `typing.ClassVar`
|
||||
RUF012.py:32:26: RUF012 Mutable class attributes should be annotated with `typing.ClassVar`
|
||||
|
|
||||
28 | mutable_default: list[int] = []
|
||||
29 | immutable_annotation: Sequence[int] = []
|
||||
30 | without_annotation = []
|
||||
30 | mutable_default: list[int] = []
|
||||
31 | immutable_annotation: Sequence[int] = []
|
||||
32 | without_annotation = []
|
||||
| ^^ RUF012
|
||||
31 | correct_code: list[int] = KNOWINGLY_MUTABLE_DEFAULT
|
||||
32 | perfectly_fine: list[int] = field(default_factory=list)
|
||||
33 | correct_code: list[int] = KNOWINGLY_MUTABLE_DEFAULT
|
||||
34 | perfectly_fine: list[int] = field(default_factory=list)
|
||||
|
|
||||
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue