Skip to content

Commit 1ca51a8

Browse files
committed
[ruff] Add more Pydantic models variants to the list of default copy semantics (RUF012)
1 parent 470f852 commit 1ca51a8

File tree

3 files changed

+71
-54
lines changed

3 files changed

+71
-54
lines changed

crates/ruff_linter/resources/test/fixtures/ruff/RUF012.py

Lines changed: 19 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -81,28 +81,42 @@ class Config(BaseConfig):
8181
final_variable: Final[list[int]] = []
8282

8383

84+
from pydantic.v1 import BaseModel as V1BaseModel
85+
86+
87+
class I(V1BaseModel):
88+
mutable_default: list[int] = []
89+
90+
91+
from pydantic.v1.generics import GenericModel
92+
93+
94+
class J(GenericModel):
95+
mutable_default: list[int] = []
96+
97+
8498
def sqlmodel_import_checker():
8599
from sqlmodel.main import SQLModel
86100

87-
class I(SQLModel):
101+
class K(SQLModel):
88102
id: int
89103
mutable_default: list[int] = []
90104

91105
from sqlmodel import SQLModel
92106

93-
class J(SQLModel):
107+
class L(SQLModel):
94108
id: int
95109
name: str
96110

97111

98-
class K(SQLModel):
112+
class M(SQLModel):
99113
id: int
100114
i_s: list[J] = []
101115

102116

103-
class L(SQLModel):
117+
class N(SQLModel):
104118
id: int
105-
i_j: list[K] = list()
119+
i_j: list[L] = list()
106120

107121
# Lint should account for deferred annotations
108122
# See https://github.com/astral-sh/ruff/issues/15857

crates/ruff_linter/src/rules/ruff/rules/helpers.rs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -165,7 +165,7 @@ pub(super) fn dataclass_kind<'a>(
165165

166166
/// Returns `true` if the given class has "default copy" semantics.
167167
///
168-
/// For example, Pydantic `BaseModel` and `BaseSettings` subclassses copy attribute defaults on
168+
/// For example, Pydantic `BaseModel` and `BaseSettings` subclasses copy attribute defaults on
169169
/// instance creation. As such, the use of mutable default values is safe for such classes.
170170
pub(super) fn has_default_copy_semantics(
171171
class_def: &ast::StmtClassDef,
@@ -174,7 +174,10 @@ pub(super) fn has_default_copy_semantics(
174174
analyze::class::any_qualified_base_class(class_def, semantic, &|qualified_name| {
175175
matches!(
176176
qualified_name.segments(),
177-
["pydantic", "BaseModel" | "BaseSettings" | "BaseConfig"]
177+
["pydantic", "BaseModel" | "RootModel" | "BaseSettings" | "BaseConfig"]
178+
| ["pydantic", "generics", "GenericModel"]
179+
| ["pydantic", "v1", "BaseModel" | "BaseSettings" | "BaseConfig"]
180+
| ["pydantic", "v1", "generics", "GenericModel"]
178181
| ["pydantic_settings", "BaseSettings"]
179182
| ["msgspec", "Struct"]
180183
| ["sqlmodel", "SQLModel"]

crates/ruff_linter/src/rules/ruff/snapshots/ruff_linter__rules__ruff__tests__RUF012_RUF012.py.snap

Lines changed: 47 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -31,78 +31,78 @@ RUF012.py:25:26: RUF012 Mutable class attributes should be annotated with `typin
3131
27 | class_variable: ClassVar[list[int]] = []
3232
|
3333

34-
RUF012.py:89:38: RUF012 Mutable class attributes should be annotated with `typing.ClassVar`
35-
|
36-
87 | class I(SQLModel):
37-
88 | id: int
38-
89 | mutable_default: list[int] = []
39-
| ^^ RUF012
40-
90 |
41-
91 | from sqlmodel import SQLModel
42-
|
34+
RUF012.py:103:38: RUF012 Mutable class attributes should be annotated with `typing.ClassVar`
35+
|
36+
101 | class K(SQLModel):
37+
102 | id: int
38+
103 | mutable_default: list[int] = []
39+
| ^^ RUF012
40+
104 |
41+
105 | from sqlmodel import SQLModel
42+
|
4343

44-
RUF012.py:114:36: RUF012 Mutable class attributes should be annotated with `typing.ClassVar`
44+
RUF012.py:128:36: RUF012 Mutable class attributes should be annotated with `typing.ClassVar`
4545
|
46-
112 | }
47-
113 |
48-
114 | mutable_default: 'list[int]' = []
46+
126 | }
47+
127 |
48+
128 | mutable_default: 'list[int]' = []
4949
| ^^ RUF012
50-
115 | immutable_annotation: 'Sequence[int]'= []
51-
116 | without_annotation = []
50+
129 | immutable_annotation: 'Sequence[int]'= []
51+
130 | without_annotation = []
5252
|
5353

54-
RUF012.py:115:44: RUF012 Mutable class attributes should be annotated with `typing.ClassVar`
54+
RUF012.py:129:44: RUF012 Mutable class attributes should be annotated with `typing.ClassVar`
5555
|
56-
114 | mutable_default: 'list[int]' = []
57-
115 | immutable_annotation: 'Sequence[int]'= []
56+
128 | mutable_default: 'list[int]' = []
57+
129 | immutable_annotation: 'Sequence[int]'= []
5858
| ^^ RUF012
59-
116 | without_annotation = []
60-
117 | class_variable: 'ClassVar[list[int]]' = []
59+
130 | without_annotation = []
60+
131 | class_variable: 'ClassVar[list[int]]' = []
6161
|
6262

63-
RUF012.py:116:26: RUF012 Mutable class attributes should be annotated with `typing.ClassVar`
63+
RUF012.py:130:26: RUF012 Mutable class attributes should be annotated with `typing.ClassVar`
6464
|
65-
114 | mutable_default: 'list[int]' = []
66-
115 | immutable_annotation: 'Sequence[int]'= []
67-
116 | without_annotation = []
65+
128 | mutable_default: 'list[int]' = []
66+
129 | immutable_annotation: 'Sequence[int]'= []
67+
130 | without_annotation = []
6868
| ^^ RUF012
69-
117 | class_variable: 'ClassVar[list[int]]' = []
70-
118 | final_variable: 'Final[list[int]]' = []
69+
131 | class_variable: 'ClassVar[list[int]]' = []
70+
132 | final_variable: 'Final[list[int]]' = []
7171
|
7272

73-
RUF012.py:117:45: RUF012 Mutable class attributes should be annotated with `typing.ClassVar`
73+
RUF012.py:131:45: RUF012 Mutable class attributes should be annotated with `typing.ClassVar`
7474
|
75-
115 | immutable_annotation: 'Sequence[int]'= []
76-
116 | without_annotation = []
77-
117 | class_variable: 'ClassVar[list[int]]' = []
75+
129 | immutable_annotation: 'Sequence[int]'= []
76+
130 | without_annotation = []
77+
131 | class_variable: 'ClassVar[list[int]]' = []
7878
| ^^ RUF012
79-
118 | final_variable: 'Final[list[int]]' = []
80-
119 | class_variable_without_subscript: 'ClassVar' = []
79+
132 | final_variable: 'Final[list[int]]' = []
80+
133 | class_variable_without_subscript: 'ClassVar' = []
8181
|
8282

83-
RUF012.py:118:42: RUF012 Mutable class attributes should be annotated with `typing.ClassVar`
83+
RUF012.py:132:42: RUF012 Mutable class attributes should be annotated with `typing.ClassVar`
8484
|
85-
116 | without_annotation = []
86-
117 | class_variable: 'ClassVar[list[int]]' = []
87-
118 | final_variable: 'Final[list[int]]' = []
85+
130 | without_annotation = []
86+
131 | class_variable: 'ClassVar[list[int]]' = []
87+
132 | final_variable: 'Final[list[int]]' = []
8888
| ^^ RUF012
89-
119 | class_variable_without_subscript: 'ClassVar' = []
90-
120 | final_variable_without_subscript: 'Final' = []
89+
133 | class_variable_without_subscript: 'ClassVar' = []
90+
134 | final_variable_without_subscript: 'Final' = []
9191
|
9292

93-
RUF012.py:119:52: RUF012 Mutable class attributes should be annotated with `typing.ClassVar`
93+
RUF012.py:133:52: RUF012 Mutable class attributes should be annotated with `typing.ClassVar`
9494
|
95-
117 | class_variable: 'ClassVar[list[int]]' = []
96-
118 | final_variable: 'Final[list[int]]' = []
97-
119 | class_variable_without_subscript: 'ClassVar' = []
95+
131 | class_variable: 'ClassVar[list[int]]' = []
96+
132 | final_variable: 'Final[list[int]]' = []
97+
133 | class_variable_without_subscript: 'ClassVar' = []
9898
| ^^ RUF012
99-
120 | final_variable_without_subscript: 'Final' = []
99+
134 | final_variable_without_subscript: 'Final' = []
100100
|
101101

102-
RUF012.py:120:49: RUF012 Mutable class attributes should be annotated with `typing.ClassVar`
102+
RUF012.py:134:49: RUF012 Mutable class attributes should be annotated with `typing.ClassVar`
103103
|
104-
118 | final_variable: 'Final[list[int]]' = []
105-
119 | class_variable_without_subscript: 'ClassVar' = []
106-
120 | final_variable_without_subscript: 'Final' = []
104+
132 | final_variable: 'Final[list[int]]' = []
105+
133 | class_variable_without_subscript: 'ClassVar' = []
106+
134 | final_variable_without_subscript: 'Final' = []
107107
| ^^ RUF012
108108
|

0 commit comments

Comments
 (0)