14
14
from pydantic_core import PydanticUndefined
15
15
from typing_extensions import TypeIs , get_origin
16
16
from typing_inspection import typing_objects
17
+ from typing_inspection .introspection import AnnotationSource
17
18
18
19
from pydantic import PydanticDeprecatedSince211
19
20
from pydantic .errors import PydanticUserError
@@ -166,17 +167,6 @@ def collect_model_fields( # noqa: C901
166
167
167
168
assigned_value = getattr (cls , ann_name , PydanticUndefined )
168
169
169
- if _is_finalvar_with_default_val (ann_type , assigned_value ):
170
- warnings .warn (
171
- f'Annotation { ann_name !r} is marked as final and has a default value. Pydantic treats { ann_name !r} as a '
172
- 'class variable, but it will be considered as a normal field in V3 to be aligned with dataclasses. If you '
173
- f'still want { ann_name !r} to be considered as a class variable, annotate it as: `ClassVar[<type>] = <default>.`' ,
174
- category = PydanticDeprecatedSince211 ,
175
- # Incorrect when `create_model` is used, but the chance that final with a default is used is low in that case:
176
- stacklevel = 4 ,
177
- )
178
- class_vars .add (ann_name )
179
- continue
180
170
if not is_valid_field_name (ann_name ):
181
171
continue
182
172
if cls .__pydantic_root_model__ and ann_name != 'root' :
@@ -223,7 +213,7 @@ def collect_model_fields( # noqa: C901
223
213
# The field was not found on any base classes; this seems to be caused by fields not getting
224
214
# generated thanks to models not being fully defined while initializing recursive models.
225
215
# Nothing stops us from just creating a new FieldInfo for this type hint, so we do this.
226
- field_info = FieldInfo_ .from_annotation (ann_type )
216
+ field_info = FieldInfo_ .from_annotation (ann_type , _source = AnnotationSource . CLASS )
227
217
228
218
if not evaluated :
229
219
field_info ._complete = False
@@ -245,13 +235,24 @@ def collect_model_fields( # noqa: C901
245
235
copy (assigned_value ) if not evaluated and isinstance (assigned_value , FieldInfo_ ) else assigned_value
246
236
)
247
237
248
- field_info = FieldInfo_ .from_annotated_attribute (ann_type , assigned_value )
238
+ field_info = FieldInfo_ .from_annotated_attribute (ann_type , assigned_value , _source = AnnotationSource . CLASS )
249
239
if not evaluated :
250
240
field_info ._complete = False
251
241
# Store the original annotation and assignment value that should be used to rebuild
252
242
# the field info later:
253
243
field_info ._original_annotation = ann_type
254
244
field_info ._original_assignment = original_assignment
245
+ elif 'final' in field_info ._qualifiers and not field_info .is_required ():
246
+ warnings .warn (
247
+ f'Annotation { ann_name !r} is marked as final and has a default value. Pydantic treats { ann_name !r} as a '
248
+ 'class variable, but it will be considered as a normal field in V3 to be aligned with dataclasses. If you '
249
+ f'still want { ann_name !r} to be considered as a class variable, annotate it as: `ClassVar[<type>] = <default>.`' ,
250
+ category = PydanticDeprecatedSince211 ,
251
+ # Incorrect when `create_model` is used, but the chance that final with a default is used is low in that case:
252
+ stacklevel = 4 ,
253
+ )
254
+ class_vars .add (ann_name )
255
+ continue
255
256
256
257
# attributes which are fields are removed from the class namespace:
257
258
# 1. To match the behaviour of annotation-only fields
@@ -297,20 +298,6 @@ def _warn_on_nested_alias_in_annotation(ann_type: type[Any], ann_name: str) -> N
297
298
return
298
299
299
300
300
- def _is_finalvar_with_default_val (ann_type : type [Any ], assigned_value : Any ) -> bool :
301
- if assigned_value is PydanticUndefined :
302
- return False
303
-
304
- FieldInfo = import_cached_field_info ()
305
-
306
- if isinstance (assigned_value , FieldInfo ) and assigned_value .is_required ():
307
- return False
308
- elif not _typing_extra .is_finalvar (ann_type ):
309
- return False
310
- else :
311
- return True
312
-
313
-
314
301
def rebuild_model_fields (
315
302
cls : type [BaseModel ],
316
303
* ,
@@ -340,9 +327,11 @@ def rebuild_model_fields(
340
327
ann = _generics .replace_types (ann , typevars_map )
341
328
342
329
if (assign := field_info ._original_assignment ) is PydanticUndefined :
343
- rebuilt_fields [f_name ] = FieldInfo_ .from_annotation (ann )
330
+ rebuilt_fields [f_name ] = FieldInfo_ .from_annotation (ann , _source = AnnotationSource . CLASS )
344
331
else :
345
- rebuilt_fields [f_name ] = FieldInfo_ .from_annotated_attribute (ann , assign )
332
+ rebuilt_fields [f_name ] = FieldInfo_ .from_annotated_attribute (
333
+ ann , assign , _source = AnnotationSource .CLASS
334
+ )
346
335
347
336
return rebuilt_fields
348
337
@@ -411,9 +400,13 @@ def collect_dataclass_fields(
411
400
412
401
# TODO: same note as above re validate_assignment
413
402
continue
414
- field_info = FieldInfo_ .from_annotated_attribute (ann_type , dataclass_field .default )
403
+ field_info = FieldInfo_ .from_annotated_attribute (
404
+ ann_type , dataclass_field .default , _source = AnnotationSource .DATACLASS
405
+ )
415
406
else :
416
- field_info = FieldInfo_ .from_annotated_attribute (ann_type , dataclass_field )
407
+ field_info = FieldInfo_ .from_annotated_attribute (
408
+ ann_type , dataclass_field , _source = AnnotationSource .DATACLASS
409
+ )
417
410
418
411
fields [ann_name ] = field_info
419
412
0 commit comments