fastapi 0.118.3__py3-none-any.whl → 0.119.1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of fastapi might be problematic. Click here for more details.

@@ -0,0 +1,211 @@
1
+ import sys
2
+ import types
3
+ import typing
4
+ from collections import deque
5
+ from dataclasses import is_dataclass
6
+ from typing import (
7
+ Any,
8
+ Deque,
9
+ FrozenSet,
10
+ List,
11
+ Mapping,
12
+ Sequence,
13
+ Set,
14
+ Tuple,
15
+ Type,
16
+ Union,
17
+ )
18
+
19
+ from fastapi._compat import may_v1
20
+ from fastapi.types import UnionType
21
+ from pydantic import BaseModel
22
+ from pydantic.version import VERSION as PYDANTIC_VERSION
23
+ from starlette.datastructures import UploadFile
24
+ from typing_extensions import Annotated, get_args, get_origin
25
+
26
+ # Copy from Pydantic v2, compatible with v1
27
+ if sys.version_info < (3, 9):
28
+ # Pydantic no longer supports Python 3.8, this might be incorrect, but the code
29
+ # this is used for is also never reached in this codebase, as it's a copy of
30
+ # Pydantic's lenient_issubclass, just for compatibility with v1
31
+ # TODO: remove when dropping support for Python 3.8
32
+ WithArgsTypes: Tuple[Any, ...] = ()
33
+ elif sys.version_info < (3, 10):
34
+ WithArgsTypes: tuple[Any, ...] = (typing._GenericAlias, types.GenericAlias) # type: ignore[attr-defined]
35
+ else:
36
+ WithArgsTypes: tuple[Any, ...] = (
37
+ typing._GenericAlias, # type: ignore[attr-defined]
38
+ types.GenericAlias,
39
+ types.UnionType,
40
+ ) # pyright: ignore[reportAttributeAccessIssue]
41
+
42
+ PYDANTIC_VERSION_MINOR_TUPLE = tuple(int(x) for x in PYDANTIC_VERSION.split(".")[:2])
43
+ PYDANTIC_V2 = PYDANTIC_VERSION_MINOR_TUPLE[0] == 2
44
+
45
+
46
+ sequence_annotation_to_type = {
47
+ Sequence: list,
48
+ List: list,
49
+ list: list,
50
+ Tuple: tuple,
51
+ tuple: tuple,
52
+ Set: set,
53
+ set: set,
54
+ FrozenSet: frozenset,
55
+ frozenset: frozenset,
56
+ Deque: deque,
57
+ deque: deque,
58
+ }
59
+
60
+ sequence_types = tuple(sequence_annotation_to_type.keys())
61
+
62
+ Url: Type[Any]
63
+
64
+
65
+ # Copy of Pydantic v2, compatible with v1
66
+ def lenient_issubclass(
67
+ cls: Any, class_or_tuple: Union[Type[Any], Tuple[Type[Any], ...], None]
68
+ ) -> bool:
69
+ try:
70
+ return isinstance(cls, type) and issubclass(cls, class_or_tuple) # type: ignore[arg-type]
71
+ except TypeError: # pragma: no cover
72
+ if isinstance(cls, WithArgsTypes):
73
+ return False
74
+ raise # pragma: no cover
75
+
76
+
77
+ def _annotation_is_sequence(annotation: Union[Type[Any], None]) -> bool:
78
+ if lenient_issubclass(annotation, (str, bytes)):
79
+ return False
80
+ return lenient_issubclass(annotation, sequence_types) # type: ignore[arg-type]
81
+
82
+
83
+ def field_annotation_is_sequence(annotation: Union[Type[Any], None]) -> bool:
84
+ origin = get_origin(annotation)
85
+ if origin is Union or origin is UnionType:
86
+ for arg in get_args(annotation):
87
+ if field_annotation_is_sequence(arg):
88
+ return True
89
+ return False
90
+ return _annotation_is_sequence(annotation) or _annotation_is_sequence(
91
+ get_origin(annotation)
92
+ )
93
+
94
+
95
+ def value_is_sequence(value: Any) -> bool:
96
+ return isinstance(value, sequence_types) and not isinstance(value, (str, bytes)) # type: ignore[arg-type]
97
+
98
+
99
+ def _annotation_is_complex(annotation: Union[Type[Any], None]) -> bool:
100
+ return (
101
+ lenient_issubclass(
102
+ annotation, (BaseModel, may_v1.BaseModel, Mapping, UploadFile)
103
+ )
104
+ or _annotation_is_sequence(annotation)
105
+ or is_dataclass(annotation)
106
+ )
107
+
108
+
109
+ def field_annotation_is_complex(annotation: Union[Type[Any], None]) -> bool:
110
+ origin = get_origin(annotation)
111
+ if origin is Union or origin is UnionType:
112
+ return any(field_annotation_is_complex(arg) for arg in get_args(annotation))
113
+
114
+ if origin is Annotated:
115
+ return field_annotation_is_complex(get_args(annotation)[0])
116
+
117
+ return (
118
+ _annotation_is_complex(annotation)
119
+ or _annotation_is_complex(origin)
120
+ or hasattr(origin, "__pydantic_core_schema__")
121
+ or hasattr(origin, "__get_pydantic_core_schema__")
122
+ )
123
+
124
+
125
+ def field_annotation_is_scalar(annotation: Any) -> bool:
126
+ # handle Ellipsis here to make tuple[int, ...] work nicely
127
+ return annotation is Ellipsis or not field_annotation_is_complex(annotation)
128
+
129
+
130
+ def field_annotation_is_scalar_sequence(annotation: Union[Type[Any], None]) -> bool:
131
+ origin = get_origin(annotation)
132
+ if origin is Union or origin is UnionType:
133
+ at_least_one_scalar_sequence = False
134
+ for arg in get_args(annotation):
135
+ if field_annotation_is_scalar_sequence(arg):
136
+ at_least_one_scalar_sequence = True
137
+ continue
138
+ elif not field_annotation_is_scalar(arg):
139
+ return False
140
+ return at_least_one_scalar_sequence
141
+ return field_annotation_is_sequence(annotation) and all(
142
+ field_annotation_is_scalar(sub_annotation)
143
+ for sub_annotation in get_args(annotation)
144
+ )
145
+
146
+
147
+ def is_bytes_or_nonable_bytes_annotation(annotation: Any) -> bool:
148
+ if lenient_issubclass(annotation, bytes):
149
+ return True
150
+ origin = get_origin(annotation)
151
+ if origin is Union or origin is UnionType:
152
+ for arg in get_args(annotation):
153
+ if lenient_issubclass(arg, bytes):
154
+ return True
155
+ return False
156
+
157
+
158
+ def is_uploadfile_or_nonable_uploadfile_annotation(annotation: Any) -> bool:
159
+ if lenient_issubclass(annotation, UploadFile):
160
+ return True
161
+ origin = get_origin(annotation)
162
+ if origin is Union or origin is UnionType:
163
+ for arg in get_args(annotation):
164
+ if lenient_issubclass(arg, UploadFile):
165
+ return True
166
+ return False
167
+
168
+
169
+ def is_bytes_sequence_annotation(annotation: Any) -> bool:
170
+ origin = get_origin(annotation)
171
+ if origin is Union or origin is UnionType:
172
+ at_least_one = False
173
+ for arg in get_args(annotation):
174
+ if is_bytes_sequence_annotation(arg):
175
+ at_least_one = True
176
+ continue
177
+ return at_least_one
178
+ return field_annotation_is_sequence(annotation) and all(
179
+ is_bytes_or_nonable_bytes_annotation(sub_annotation)
180
+ for sub_annotation in get_args(annotation)
181
+ )
182
+
183
+
184
+ def is_uploadfile_sequence_annotation(annotation: Any) -> bool:
185
+ origin = get_origin(annotation)
186
+ if origin is Union or origin is UnionType:
187
+ at_least_one = False
188
+ for arg in get_args(annotation):
189
+ if is_uploadfile_sequence_annotation(arg):
190
+ at_least_one = True
191
+ continue
192
+ return at_least_one
193
+ return field_annotation_is_sequence(annotation) and all(
194
+ is_uploadfile_or_nonable_uploadfile_annotation(sub_annotation)
195
+ for sub_annotation in get_args(annotation)
196
+ )
197
+
198
+
199
+ def annotation_is_pydantic_v1(annotation: Any) -> bool:
200
+ if lenient_issubclass(annotation, may_v1.BaseModel):
201
+ return True
202
+ origin = get_origin(annotation)
203
+ if origin is Union or origin is UnionType:
204
+ for arg in get_args(annotation):
205
+ if lenient_issubclass(arg, may_v1.BaseModel):
206
+ return True
207
+ if field_annotation_is_sequence(annotation):
208
+ for sub_annotation in get_args(annotation):
209
+ if annotation_is_pydantic_v1(sub_annotation):
210
+ return True
211
+ return False
fastapi/_compat/v1.py ADDED
@@ -0,0 +1,312 @@
1
+ from copy import copy
2
+ from dataclasses import dataclass, is_dataclass
3
+ from enum import Enum
4
+ from typing import (
5
+ Any,
6
+ Callable,
7
+ Dict,
8
+ List,
9
+ Sequence,
10
+ Set,
11
+ Tuple,
12
+ Type,
13
+ Union,
14
+ )
15
+
16
+ from fastapi._compat import shared
17
+ from fastapi.openapi.constants import REF_PREFIX as REF_PREFIX
18
+ from fastapi.types import ModelNameMap
19
+ from pydantic.version import VERSION as PYDANTIC_VERSION
20
+ from typing_extensions import Literal
21
+
22
+ PYDANTIC_VERSION_MINOR_TUPLE = tuple(int(x) for x in PYDANTIC_VERSION.split(".")[:2])
23
+ PYDANTIC_V2 = PYDANTIC_VERSION_MINOR_TUPLE[0] == 2
24
+ # Keeping old "Required" functionality from Pydantic V1, without
25
+ # shadowing typing.Required.
26
+ RequiredParam: Any = Ellipsis
27
+
28
+ if not PYDANTIC_V2:
29
+ from pydantic import BaseConfig as BaseConfig
30
+ from pydantic import BaseModel as BaseModel
31
+ from pydantic import ValidationError as ValidationError
32
+ from pydantic import create_model as create_model
33
+ from pydantic.class_validators import Validator as Validator
34
+ from pydantic.color import Color as Color
35
+ from pydantic.error_wrappers import ErrorWrapper as ErrorWrapper
36
+ from pydantic.errors import MissingError
37
+ from pydantic.fields import ( # type: ignore[attr-defined]
38
+ SHAPE_FROZENSET,
39
+ SHAPE_LIST,
40
+ SHAPE_SEQUENCE,
41
+ SHAPE_SET,
42
+ SHAPE_SINGLETON,
43
+ SHAPE_TUPLE,
44
+ SHAPE_TUPLE_ELLIPSIS,
45
+ )
46
+ from pydantic.fields import FieldInfo as FieldInfo
47
+ from pydantic.fields import ModelField as ModelField # type: ignore[attr-defined]
48
+ from pydantic.fields import Undefined as Undefined # type: ignore[attr-defined]
49
+ from pydantic.fields import ( # type: ignore[attr-defined]
50
+ UndefinedType as UndefinedType,
51
+ )
52
+ from pydantic.networks import AnyUrl as AnyUrl
53
+ from pydantic.networks import NameEmail as NameEmail
54
+ from pydantic.schema import TypeModelSet as TypeModelSet
55
+ from pydantic.schema import (
56
+ field_schema,
57
+ model_process_schema,
58
+ )
59
+ from pydantic.schema import (
60
+ get_annotation_from_field_info as get_annotation_from_field_info,
61
+ )
62
+ from pydantic.schema import get_flat_models_from_field as get_flat_models_from_field
63
+ from pydantic.schema import (
64
+ get_flat_models_from_fields as get_flat_models_from_fields,
65
+ )
66
+ from pydantic.schema import get_model_name_map as get_model_name_map
67
+ from pydantic.types import SecretBytes as SecretBytes
68
+ from pydantic.types import SecretStr as SecretStr
69
+ from pydantic.typing import evaluate_forwardref as evaluate_forwardref
70
+ from pydantic.utils import lenient_issubclass as lenient_issubclass
71
+
72
+
73
+ else:
74
+ from pydantic.v1 import BaseConfig as BaseConfig # type: ignore[assignment]
75
+ from pydantic.v1 import BaseModel as BaseModel # type: ignore[assignment]
76
+ from pydantic.v1 import ( # type: ignore[assignment]
77
+ ValidationError as ValidationError,
78
+ )
79
+ from pydantic.v1 import create_model as create_model # type: ignore[no-redef]
80
+ from pydantic.v1.class_validators import Validator as Validator
81
+ from pydantic.v1.color import Color as Color # type: ignore[assignment]
82
+ from pydantic.v1.error_wrappers import ErrorWrapper as ErrorWrapper
83
+ from pydantic.v1.errors import MissingError
84
+ from pydantic.v1.fields import (
85
+ SHAPE_FROZENSET,
86
+ SHAPE_LIST,
87
+ SHAPE_SEQUENCE,
88
+ SHAPE_SET,
89
+ SHAPE_SINGLETON,
90
+ SHAPE_TUPLE,
91
+ SHAPE_TUPLE_ELLIPSIS,
92
+ )
93
+ from pydantic.v1.fields import FieldInfo as FieldInfo # type: ignore[assignment]
94
+ from pydantic.v1.fields import ModelField as ModelField
95
+ from pydantic.v1.fields import Undefined as Undefined
96
+ from pydantic.v1.fields import UndefinedType as UndefinedType
97
+ from pydantic.v1.networks import AnyUrl as AnyUrl
98
+ from pydantic.v1.networks import ( # type: ignore[assignment]
99
+ NameEmail as NameEmail,
100
+ )
101
+ from pydantic.v1.schema import TypeModelSet as TypeModelSet
102
+ from pydantic.v1.schema import (
103
+ field_schema,
104
+ model_process_schema,
105
+ )
106
+ from pydantic.v1.schema import (
107
+ get_annotation_from_field_info as get_annotation_from_field_info,
108
+ )
109
+ from pydantic.v1.schema import (
110
+ get_flat_models_from_field as get_flat_models_from_field,
111
+ )
112
+ from pydantic.v1.schema import (
113
+ get_flat_models_from_fields as get_flat_models_from_fields,
114
+ )
115
+ from pydantic.v1.schema import get_model_name_map as get_model_name_map
116
+ from pydantic.v1.types import ( # type: ignore[assignment]
117
+ SecretBytes as SecretBytes,
118
+ )
119
+ from pydantic.v1.types import ( # type: ignore[assignment]
120
+ SecretStr as SecretStr,
121
+ )
122
+ from pydantic.v1.typing import evaluate_forwardref as evaluate_forwardref
123
+ from pydantic.v1.utils import lenient_issubclass as lenient_issubclass
124
+
125
+
126
+ GetJsonSchemaHandler = Any
127
+ JsonSchemaValue = Dict[str, Any]
128
+ CoreSchema = Any
129
+ Url = AnyUrl
130
+
131
+ sequence_shapes = {
132
+ SHAPE_LIST,
133
+ SHAPE_SET,
134
+ SHAPE_FROZENSET,
135
+ SHAPE_TUPLE,
136
+ SHAPE_SEQUENCE,
137
+ SHAPE_TUPLE_ELLIPSIS,
138
+ }
139
+ sequence_shape_to_type = {
140
+ SHAPE_LIST: list,
141
+ SHAPE_SET: set,
142
+ SHAPE_TUPLE: tuple,
143
+ SHAPE_SEQUENCE: list,
144
+ SHAPE_TUPLE_ELLIPSIS: list,
145
+ }
146
+
147
+
148
+ @dataclass
149
+ class GenerateJsonSchema:
150
+ ref_template: str
151
+
152
+
153
+ class PydanticSchemaGenerationError(Exception):
154
+ pass
155
+
156
+
157
+ RequestErrorModel: Type[BaseModel] = create_model("Request")
158
+
159
+
160
+ def with_info_plain_validator_function(
161
+ function: Callable[..., Any],
162
+ *,
163
+ ref: Union[str, None] = None,
164
+ metadata: Any = None,
165
+ serialization: Any = None,
166
+ ) -> Any:
167
+ return {}
168
+
169
+
170
+ def get_model_definitions(
171
+ *,
172
+ flat_models: Set[Union[Type[BaseModel], Type[Enum]]],
173
+ model_name_map: Dict[Union[Type[BaseModel], Type[Enum]], str],
174
+ ) -> Dict[str, Any]:
175
+ definitions: Dict[str, Dict[str, Any]] = {}
176
+ for model in flat_models:
177
+ m_schema, m_definitions, m_nested_models = model_process_schema(
178
+ model, model_name_map=model_name_map, ref_prefix=REF_PREFIX
179
+ )
180
+ definitions.update(m_definitions)
181
+ model_name = model_name_map[model]
182
+ definitions[model_name] = m_schema
183
+ for m_schema in definitions.values():
184
+ if "description" in m_schema:
185
+ m_schema["description"] = m_schema["description"].split("\f")[0]
186
+ return definitions
187
+
188
+
189
+ def is_pv1_scalar_field(field: ModelField) -> bool:
190
+ from fastapi import params
191
+
192
+ field_info = field.field_info
193
+ if not (
194
+ field.shape == SHAPE_SINGLETON
195
+ and not lenient_issubclass(field.type_, BaseModel)
196
+ and not lenient_issubclass(field.type_, dict)
197
+ and not shared.field_annotation_is_sequence(field.type_)
198
+ and not is_dataclass(field.type_)
199
+ and not isinstance(field_info, params.Body)
200
+ ):
201
+ return False
202
+ if field.sub_fields:
203
+ if not all(is_pv1_scalar_field(f) for f in field.sub_fields):
204
+ return False
205
+ return True
206
+
207
+
208
+ def is_pv1_scalar_sequence_field(field: ModelField) -> bool:
209
+ if (field.shape in sequence_shapes) and not lenient_issubclass(
210
+ field.type_, BaseModel
211
+ ):
212
+ if field.sub_fields is not None:
213
+ for sub_field in field.sub_fields:
214
+ if not is_pv1_scalar_field(sub_field):
215
+ return False
216
+ return True
217
+ if shared._annotation_is_sequence(field.type_):
218
+ return True
219
+ return False
220
+
221
+
222
+ def _model_rebuild(model: Type[BaseModel]) -> None:
223
+ model.update_forward_refs()
224
+
225
+
226
+ def _model_dump(
227
+ model: BaseModel, mode: Literal["json", "python"] = "json", **kwargs: Any
228
+ ) -> Any:
229
+ return model.dict(**kwargs)
230
+
231
+
232
+ def _get_model_config(model: BaseModel) -> Any:
233
+ return model.__config__ # type: ignore[attr-defined]
234
+
235
+
236
+ def get_schema_from_model_field(
237
+ *,
238
+ field: ModelField,
239
+ model_name_map: ModelNameMap,
240
+ field_mapping: Dict[
241
+ Tuple[ModelField, Literal["validation", "serialization"]], JsonSchemaValue
242
+ ],
243
+ separate_input_output_schemas: bool = True,
244
+ ) -> Dict[str, Any]:
245
+ return field_schema( # type: ignore[no-any-return]
246
+ field, model_name_map=model_name_map, ref_prefix=REF_PREFIX
247
+ )[0]
248
+
249
+
250
+ # def get_compat_model_name_map(fields: List[ModelField]) -> ModelNameMap:
251
+ # models = get_flat_models_from_fields(fields, known_models=set())
252
+ # return get_model_name_map(models) # type: ignore[no-any-return]
253
+
254
+
255
+ def get_definitions(
256
+ *,
257
+ fields: List[ModelField],
258
+ model_name_map: ModelNameMap,
259
+ separate_input_output_schemas: bool = True,
260
+ ) -> Tuple[
261
+ Dict[Tuple[ModelField, Literal["validation", "serialization"]], JsonSchemaValue],
262
+ Dict[str, Dict[str, Any]],
263
+ ]:
264
+ models = get_flat_models_from_fields(fields, known_models=set())
265
+ return {}, get_model_definitions(flat_models=models, model_name_map=model_name_map)
266
+
267
+
268
+ def is_scalar_field(field: ModelField) -> bool:
269
+ return is_pv1_scalar_field(field)
270
+
271
+
272
+ def is_sequence_field(field: ModelField) -> bool:
273
+ return field.shape in sequence_shapes or shared._annotation_is_sequence(field.type_)
274
+
275
+
276
+ def is_scalar_sequence_field(field: ModelField) -> bool:
277
+ return is_pv1_scalar_sequence_field(field)
278
+
279
+
280
+ def is_bytes_field(field: ModelField) -> bool:
281
+ return lenient_issubclass(field.type_, bytes) # type: ignore[no-any-return]
282
+
283
+
284
+ def is_bytes_sequence_field(field: ModelField) -> bool:
285
+ return field.shape in sequence_shapes and lenient_issubclass(field.type_, bytes)
286
+
287
+
288
+ def copy_field_info(*, field_info: FieldInfo, annotation: Any) -> FieldInfo:
289
+ return copy(field_info)
290
+
291
+
292
+ def serialize_sequence_value(*, field: ModelField, value: Any) -> Sequence[Any]:
293
+ return sequence_shape_to_type[field.shape](value) # type: ignore[no-any-return]
294
+
295
+
296
+ def get_missing_field_error(loc: Tuple[str, ...]) -> Dict[str, Any]:
297
+ missing_field_error = ErrorWrapper(MissingError(), loc=loc)
298
+ new_error = ValidationError([missing_field_error], RequestErrorModel)
299
+ return new_error.errors()[0] # type: ignore[return-value]
300
+
301
+
302
+ def create_body_model(
303
+ *, fields: Sequence[ModelField], model_name: str
304
+ ) -> Type[BaseModel]:
305
+ BodyModel = create_model(model_name)
306
+ for f in fields:
307
+ BodyModel.__fields__[f.name] = f # type: ignore[index]
308
+ return BodyModel
309
+
310
+
311
+ def get_model_fields(model: Type[BaseModel]) -> List[ModelField]:
312
+ return list(model.__fields__.values()) # type: ignore[attr-defined]