37
37
overload ,
38
38
)
39
39
from uuid import UUID
40
- from warnings import warn
41
40
from zoneinfo import ZoneInfo
42
41
43
42
import typing_extensions
62
61
from ..functional_validators import AfterValidator ,BeforeValidator ,FieldValidatorModes ,PlainValidator ,WrapValidator
63
62
from ..json_schema import JsonSchemaValue
64
63
from ..version import version_short
65
- from ..warnings import PydanticArbitraryTypeWarning ,PydanticDeprecatedSince20
64
+ from ..warnings import ArbitraryTypeWarning ,PydanticDeprecatedSince20 , UnsupportedFieldAttributeWarning
66
65
from .import _decorators ,_discriminated_union ,_known_annotated_metadata ,_repr ,_typing_extra
67
66
from ._config import ConfigWrapper ,ConfigWrapperStack
68
67
from ._core_metadata import CoreMetadata ,update_core_metadata
164
163
]
165
164
166
165
VALIDATE_CALL_SUPPORTED_TYPES = get_args (ValidateCallSupportedTypes )
166
+ UNSUPPORTED_STANDALONE_FIELDINFO_ATTRIBUTES = [
167
+ ('alias' ,None ),
168
+ ('validation_alias' ,None ),
169
+ ('serialization_alias' ,None ),
170
+ # will be set if any alias is set, so disable it to avoid double warnings:
171
+ # 'alias_priority',
172
+ ('default' ,PydanticUndefined ),
173
+ ('default_factory' ,None ),
174
+ ('exclude' ,None ),
175
+ ('deprecated' ,None ),
176
+ ('repr' ,True ),
177
+ ('validate_default' ,None ),
178
+ ('frozen' ,None ),
179
+ ('init' ,None ),
180
+ ('init_var' ,None ),
181
+ ('kw_only' ,None ),
182
+ ]
183
+ """`FieldInfo` attributes (and their default value) that can't be used outside of a model (e.g. in a type adapter or a PEP 695 type alias)."""
167
184
168
185
_mode_to_validator :dict [
169
186
FieldValidatorModes ,type [BeforeValidator | AfterValidator | PlainValidator | WrapValidator ]
@@ -562,7 +579,15 @@ def _mapping_schema(self, tp: Any, keys_type: Any, values_type: Any) -> CoreSche
562
579
563
580
mapped_origin = MAPPING_ORIGIN_MAP [tp ]
564
581
keys_schema = self .generate_schema (keys_type )
565
- values_schema = self .generate_schema (values_type )
582
+ with warnings .catch_warnings ():
583
+ # We kind of abused `Field()` default factories to be able to specify
584
+ # the `defaultdict`'s `default_factory`. As a consequence, we get warnings
585
+ # as normally `FieldInfo.default_factory` is unsupported in the context where
586
+ # `Field()` is used and our only solution is to ignore them (note that this might
587
+ # wrongfully ignore valid warnings, e.g. if the `value_type` is a PEP 695 type alias
588
+ # with unsupported metadata).
589
+ warnings .simplefilter ('ignore' ,category = UnsupportedFieldAttributeWarning )
590
+ values_schema = self .generate_schema (values_type )
566
591
dict_schema = core_schema .dict_schema (keys_schema ,values_schema ,strict = False )
567
592
568
593
if mapped_origin is dict :
@@ -614,12 +639,12 @@ def _fraction_schema(self) -> CoreSchema:
614
639
615
640
def _arbitrary_type_schema (self ,tp :Any )-> CoreSchema :
616
641
if not isinstance (tp ,type ):
617
- warn (
642
+ warnings . warn (
618
643
f'{ tp !r} is not a Python type (it may be an instance of an object),'
619
644
' Pydantic will allow any object with no validation since we cannot even'
620
645
' enforce that the input is an instance of the given type.'
621
646
' To get rid of this error wrap the type with `pydantic.SkipValidation`.' ,
622
- PydanticArbitraryTypeWarning ,
647
+ ArbitraryTypeWarning ,
623
648
)
624
649
return core_schema .any_schema ()
625
650
return core_schema .is_instance_schema (tp )
@@ -903,12 +928,12 @@ def _generate_schema_from_get_schema_method(self, obj: Any, source: Any) -> core
903
928
from pydantic .v1 import BaseModel as BaseModelV1
904
929
905
930
if issubclass (obj ,BaseModelV1 ):
906
- warn (
931
+ warnings . warn (
907
932
f'Mixing V1 models and V2 models (or constructs, like `TypeAdapter`) is not supported. Please upgrade `{ obj .__name__ } ` to V2.' ,
908
933
UserWarning ,
909
934
)
910
935
else :
911
- warn (
936
+ warnings . warn (
912
937
'`__get_validators__` is deprecated and will be removed, use `__get_pydantic_core_schema__` instead.' ,
913
938
PydanticDeprecatedSince20 ,
914
939
)
@@ -1525,7 +1550,14 @@ def _generate_parameter_schema(
1525
1550
update_field_from_config (self ._config_wrapper ,name ,field )
1526
1551
1527
1552
with self .field_name_stack .push (name ):
1528
- schema = self ._apply_annotations (field .annotation , [field ])
1553
+ schema = self ._apply_annotations (
1554
+ field .annotation ,
1555
+ [field ],
1556
+ # Because we pass `field` as metadata above (required for attributes relevant for
1557
+ # JSON Scheme generation), we need to ignore the potential warnings about `FieldInfo`
1558
+ # attributes that will not be used:
1559
+ check_unsupported_field_info_attributes = False ,
1560
+ )
1529
1561
1530
1562
if not field .is_required ():
1531
1563
schema = wrap_default (field ,schema )
@@ -1567,7 +1599,14 @@ def _generate_parameter_v3_schema(
1567
1599
update_field_from_config (self ._config_wrapper ,name ,field )
1568
1600
1569
1601
with self .field_name_stack .push (name ):
1570
- schema = self ._apply_annotations (field .annotation , [field ])
1602
+ schema = self ._apply_annotations (
1603
+ field .annotation ,
1604
+ [field ],
1605
+ # Because we pass `field` as metadata above (required for attributes relevant for
1606
+ # JSON Scheme generation), we need to ignore the potential warnings about `FieldInfo`
1607
+ # attributes that will not be used:
1608
+ check_unsupported_field_info_attributes = False ,
1609
+ )
1571
1610
1572
1611
if not field .is_required ():
1573
1612
schema = wrap_default (field ,schema )
@@ -2120,6 +2159,7 @@ def _apply_annotations(
2120
2159
source_type :Any ,
2121
2160
annotations :list [Any ],
2122
2161
transform_inner_schema :Callable [[CoreSchema ],CoreSchema ]= lambda x :x ,
2162
+ check_unsupported_field_info_attributes :bool = True ,
2123
2163
)-> CoreSchema :
2124
2164
"""Apply arguments from `Annotated` or from `FieldInfo` to a schema.
2125
2165
@@ -2150,7 +2190,10 @@ def inner_handler(obj: Any) -> CoreSchema:
2150
2190
if annotation is None :
2151
2191
continue
2152
2192
get_inner_schema = self ._get_wrapped_inner_schema (
2153
- get_inner_schema ,annotation ,pydantic_js_annotation_functions
2193
+ get_inner_schema ,
2194
+ annotation ,
2195
+ pydantic_js_annotation_functions ,
2196
+ check_unsupported_field_info_attributes = check_unsupported_field_info_attributes ,
2154
2197
)
2155
2198
2156
2199
schema = get_inner_schema (source_type )
@@ -2159,10 +2202,31 @@ def inner_handler(obj: Any) -> CoreSchema:
2159
2202
update_core_metadata (core_metadata ,pydantic_js_annotation_functions = pydantic_js_annotation_functions )
2160
2203
return _add_custom_serialization_from_json_encoders (self ._config_wrapper .json_encoders ,source_type ,schema )
2161
2204
2162
- def _apply_single_annotation (self ,schema :core_schema .CoreSchema ,metadata :Any )-> core_schema .CoreSchema :
2205
+ def _apply_single_annotation (
2206
+ self ,
2207
+ schema :core_schema .CoreSchema ,
2208
+ metadata :Any ,
2209
+ check_unsupported_field_info_attributes :bool = True ,
2210
+ )-> core_schema .CoreSchema :
2163
2211
FieldInfo = import_cached_field_info ()
2164
2212
2165
2213
if isinstance (metadata ,FieldInfo ):
2214
+ if (
2215
+ check_unsupported_field_info_attributes
2216
+ # HACK: we don't want to emit the warning for `FieldInfo` subclasses, because FastAPI does weird manipulations
2217
+ # with its subclasses and their annotations:
2218
+ and type (metadata )is FieldInfo
2219
+ and (unsupported_attributes := self ._get_unsupported_field_info_attributes (metadata ))
2220
+ ):
2221
+ for attr ,value in unsupported_attributes :
2222
+ warnings .warn (
2223
+ f'The{ attr !r} attribute with value{ value !r} was provided to the `Field()` function, '
2224
+ f'which has no effect in the context it was used.{ attr !r} is field-specific metadata, '
2225
+ 'and can only be attached to a model field using `Annotated` metadata or by assignment. '
2226
+ 'This may have happened because an `Annotated` type alias using the `type` statement was '
2227
+ 'used, or if the `Field()` function was attached to a single member of a union type.' ,
2228
+ category = UnsupportedFieldAttributeWarning ,
2229
+ )
2166
2230
for field_metadata in metadata .metadata :
2167
2231
schema = self ._apply_single_annotation (schema ,field_metadata )
2168
2232
@@ -2217,11 +2281,34 @@ def _apply_single_annotation_json_schema(
2217
2281
)
2218
2282
return schema
2219
2283
2284
+ def _get_unsupported_field_info_attributes (self ,field_info :FieldInfo )-> list [tuple [str ,Any ]]:
2285
+ """Get the list of unsupported `FieldInfo` attributes when not directly used in `Annotated` for field annotations."""
2286
+ unused_metadata :list [tuple [str ,Any ]]= []
2287
+ for unused_metadata_name ,unset_value in UNSUPPORTED_STANDALONE_FIELDINFO_ATTRIBUTES :
2288
+ if (
2289
+ (unused_metadata_value := getattr (field_info ,unused_metadata_name ))is not unset_value
2290
+ # `default` and `default_factory` can still be used with a type adapter, so only include them
2291
+ # if used with a model-like class:
2292
+ and (
2293
+ unused_metadata_name not in ('default' ,'default_factory' )
2294
+ or self .model_type_stack .get ()is not None
2295
+ )
2296
+ ):
2297
+ # Setting `alias` will set `validation/serialization_alias` as well, so we want to avoid duplicate warnings:
2298
+ if (
2299
+ unused_metadata_name not in ('validation_alias' ,'serialization_alias' )
2300
+ or 'alias' not in field_info ._attributes_set
2301
+ ):
2302
+ unused_metadata .append ((unused_metadata_name ,unused_metadata_value ))
2303
+
2304
+ return unused_metadata
2305
+
2220
2306
def _get_wrapped_inner_schema (
2221
2307
self ,
2222
2308
get_inner_schema :GetCoreSchemaHandler ,
2223
2309
annotation :Any ,
2224
2310
pydantic_js_annotation_functions :list [GetJsonSchemaFunction ],
2311
+ check_unsupported_field_info_attributes :bool = False ,
2225
2312
)-> CallbackGetCoreSchemaHandler :
2226
2313
annotation_get_schema :GetCoreSchemaFunction | None = getattr (annotation ,'__get_pydantic_core_schema__' ,None )
2227
2314
@@ -2230,7 +2317,11 @@ def new_handler(source: Any) -> core_schema.CoreSchema:
2230
2317
schema = annotation_get_schema (source ,get_inner_schema )
2231
2318
else :
2232
2319
schema = get_inner_schema (source )
2233
- schema = self ._apply_single_annotation (schema ,annotation )
2320
+ schema = self ._apply_single_annotation (
2321
+ schema ,
2322
+ annotation ,
2323
+ check_unsupported_field_info_attributes = check_unsupported_field_info_attributes ,
2324
+ )
2234
2325
schema = self ._apply_single_annotation_json_schema (schema ,annotation )
2235
2326
2236
2327
metadata_js_function = _extract_get_pydantic_json_schema (annotation )