fastapi 0.99.1__py3-none-any.whl → 0.100.0__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.
- fastapi/__init__.py +1 -1
- fastapi/_compat.py +616 -0
- fastapi/applications.py +21 -22
- fastapi/datastructures.py +31 -4
- fastapi/dependencies/models.py +1 -1
- fastapi/dependencies/utils.py +76 -120
- fastapi/encoders.py +90 -12
- fastapi/exceptions.py +20 -8
- fastapi/openapi/constants.py +1 -0
- fastapi/openapi/models.py +196 -53
- fastapi/openapi/utils.py +84 -40
- fastapi/param_functions.py +247 -16
- fastapi/params.py +347 -34
- fastapi/routing.py +84 -47
- fastapi/security/oauth2.py +16 -12
- fastapi/types.py +9 -1
- fastapi/utils.py +66 -60
- {fastapi-0.99.1.dist-info → fastapi-0.100.0.dist-info}/METADATA +7 -3
- {fastapi-0.99.1.dist-info → fastapi-0.100.0.dist-info}/RECORD +21 -20
- {fastapi-0.99.1.dist-info → fastapi-0.100.0.dist-info}/WHEEL +0 -0
- {fastapi-0.99.1.dist-info → fastapi-0.100.0.dist-info}/licenses/LICENSE +0 -0
fastapi/routing.py
CHANGED
|
@@ -20,6 +20,14 @@ from typing import (
|
|
|
20
20
|
)
|
|
21
21
|
|
|
22
22
|
from fastapi import params
|
|
23
|
+
from fastapi._compat import (
|
|
24
|
+
ModelField,
|
|
25
|
+
Undefined,
|
|
26
|
+
_get_model_config,
|
|
27
|
+
_model_dump,
|
|
28
|
+
_normalize_errors,
|
|
29
|
+
lenient_issubclass,
|
|
30
|
+
)
|
|
23
31
|
from fastapi.datastructures import Default, DefaultPlaceholder
|
|
24
32
|
from fastapi.dependencies.models import Dependant
|
|
25
33
|
from fastapi.dependencies.utils import (
|
|
@@ -29,13 +37,14 @@ from fastapi.dependencies.utils import (
|
|
|
29
37
|
get_typed_return_annotation,
|
|
30
38
|
solve_dependencies,
|
|
31
39
|
)
|
|
32
|
-
from fastapi.encoders import
|
|
40
|
+
from fastapi.encoders import jsonable_encoder
|
|
33
41
|
from fastapi.exceptions import (
|
|
34
42
|
FastAPIError,
|
|
35
43
|
RequestValidationError,
|
|
44
|
+
ResponseValidationError,
|
|
36
45
|
WebSocketRequestValidationError,
|
|
37
46
|
)
|
|
38
|
-
from fastapi.types import DecoratedCallable
|
|
47
|
+
from fastapi.types import DecoratedCallable, IncEx
|
|
39
48
|
from fastapi.utils import (
|
|
40
49
|
create_cloned_field,
|
|
41
50
|
create_response_field,
|
|
@@ -44,9 +53,6 @@ from fastapi.utils import (
|
|
|
44
53
|
is_body_allowed_for_status_code,
|
|
45
54
|
)
|
|
46
55
|
from pydantic import BaseModel
|
|
47
|
-
from pydantic.error_wrappers import ErrorWrapper, ValidationError
|
|
48
|
-
from pydantic.fields import ModelField, Undefined
|
|
49
|
-
from pydantic.utils import lenient_issubclass
|
|
50
56
|
from starlette import routing
|
|
51
57
|
from starlette.concurrency import run_in_threadpool
|
|
52
58
|
from starlette.exceptions import HTTPException
|
|
@@ -73,14 +79,15 @@ def _prepare_response_content(
|
|
|
73
79
|
exclude_none: bool = False,
|
|
74
80
|
) -> Any:
|
|
75
81
|
if isinstance(res, BaseModel):
|
|
76
|
-
read_with_orm_mode = getattr(res
|
|
82
|
+
read_with_orm_mode = getattr(_get_model_config(res), "read_with_orm_mode", None)
|
|
77
83
|
if read_with_orm_mode:
|
|
78
84
|
# Let from_orm extract the data from this model instead of converting
|
|
79
85
|
# it now to a dict.
|
|
80
86
|
# Otherwise there's no way to extract lazy data that requires attribute
|
|
81
87
|
# access instead of dict iteration, e.g. lazy relationships.
|
|
82
88
|
return res
|
|
83
|
-
return
|
|
89
|
+
return _model_dump(
|
|
90
|
+
res,
|
|
84
91
|
by_alias=True,
|
|
85
92
|
exclude_unset=exclude_unset,
|
|
86
93
|
exclude_defaults=exclude_defaults,
|
|
@@ -115,8 +122,8 @@ async def serialize_response(
|
|
|
115
122
|
*,
|
|
116
123
|
field: Optional[ModelField] = None,
|
|
117
124
|
response_content: Any,
|
|
118
|
-
include: Optional[
|
|
119
|
-
exclude: Optional[
|
|
125
|
+
include: Optional[IncEx] = None,
|
|
126
|
+
exclude: Optional[IncEx] = None,
|
|
120
127
|
by_alias: bool = True,
|
|
121
128
|
exclude_unset: bool = False,
|
|
122
129
|
exclude_defaults: bool = False,
|
|
@@ -125,24 +132,40 @@ async def serialize_response(
|
|
|
125
132
|
) -> Any:
|
|
126
133
|
if field:
|
|
127
134
|
errors = []
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
135
|
+
if not hasattr(field, "serialize"):
|
|
136
|
+
# pydantic v1
|
|
137
|
+
response_content = _prepare_response_content(
|
|
138
|
+
response_content,
|
|
139
|
+
exclude_unset=exclude_unset,
|
|
140
|
+
exclude_defaults=exclude_defaults,
|
|
141
|
+
exclude_none=exclude_none,
|
|
142
|
+
)
|
|
134
143
|
if is_coroutine:
|
|
135
144
|
value, errors_ = field.validate(response_content, {}, loc=("response",))
|
|
136
145
|
else:
|
|
137
146
|
value, errors_ = await run_in_threadpool(
|
|
138
147
|
field.validate, response_content, {}, loc=("response",)
|
|
139
148
|
)
|
|
140
|
-
if isinstance(errors_,
|
|
141
|
-
errors.append(errors_)
|
|
142
|
-
elif isinstance(errors_, list):
|
|
149
|
+
if isinstance(errors_, list):
|
|
143
150
|
errors.extend(errors_)
|
|
151
|
+
elif errors_:
|
|
152
|
+
errors.append(errors_)
|
|
144
153
|
if errors:
|
|
145
|
-
raise
|
|
154
|
+
raise ResponseValidationError(
|
|
155
|
+
errors=_normalize_errors(errors), body=response_content
|
|
156
|
+
)
|
|
157
|
+
|
|
158
|
+
if hasattr(field, "serialize"):
|
|
159
|
+
return field.serialize(
|
|
160
|
+
value,
|
|
161
|
+
include=include,
|
|
162
|
+
exclude=exclude,
|
|
163
|
+
by_alias=by_alias,
|
|
164
|
+
exclude_unset=exclude_unset,
|
|
165
|
+
exclude_defaults=exclude_defaults,
|
|
166
|
+
exclude_none=exclude_none,
|
|
167
|
+
)
|
|
168
|
+
|
|
146
169
|
return jsonable_encoder(
|
|
147
170
|
value,
|
|
148
171
|
include=include,
|
|
@@ -175,8 +198,8 @@ def get_request_handler(
|
|
|
175
198
|
status_code: Optional[int] = None,
|
|
176
199
|
response_class: Union[Type[Response], DefaultPlaceholder] = Default(JSONResponse),
|
|
177
200
|
response_field: Optional[ModelField] = None,
|
|
178
|
-
response_model_include: Optional[
|
|
179
|
-
response_model_exclude: Optional[
|
|
201
|
+
response_model_include: Optional[IncEx] = None,
|
|
202
|
+
response_model_exclude: Optional[IncEx] = None,
|
|
180
203
|
response_model_by_alias: bool = True,
|
|
181
204
|
response_model_exclude_unset: bool = False,
|
|
182
205
|
response_model_exclude_defaults: bool = False,
|
|
@@ -220,7 +243,16 @@ def get_request_handler(
|
|
|
220
243
|
body = body_bytes
|
|
221
244
|
except json.JSONDecodeError as e:
|
|
222
245
|
raise RequestValidationError(
|
|
223
|
-
[
|
|
246
|
+
[
|
|
247
|
+
{
|
|
248
|
+
"type": "json_invalid",
|
|
249
|
+
"loc": ("body", e.pos),
|
|
250
|
+
"msg": "JSON decode error",
|
|
251
|
+
"input": {},
|
|
252
|
+
"ctx": {"error": e.msg},
|
|
253
|
+
}
|
|
254
|
+
],
|
|
255
|
+
body=e.doc,
|
|
224
256
|
) from e
|
|
225
257
|
except HTTPException:
|
|
226
258
|
raise
|
|
@@ -236,7 +268,7 @@ def get_request_handler(
|
|
|
236
268
|
)
|
|
237
269
|
values, errors, background_tasks, sub_response, _ = solved_result
|
|
238
270
|
if errors:
|
|
239
|
-
raise RequestValidationError(errors, body=body)
|
|
271
|
+
raise RequestValidationError(_normalize_errors(errors), body=body)
|
|
240
272
|
else:
|
|
241
273
|
raw_response = await run_endpoint_function(
|
|
242
274
|
dependant=dependant, values=values, is_coroutine=is_coroutine
|
|
@@ -287,7 +319,7 @@ def get_websocket_app(
|
|
|
287
319
|
)
|
|
288
320
|
values, errors, _, _2, _3 = solved_result
|
|
289
321
|
if errors:
|
|
290
|
-
raise WebSocketRequestValidationError(errors)
|
|
322
|
+
raise WebSocketRequestValidationError(_normalize_errors(errors))
|
|
291
323
|
assert dependant.call is not None, "dependant.call must be a function"
|
|
292
324
|
await dependant.call(**values)
|
|
293
325
|
|
|
@@ -348,8 +380,8 @@ class APIRoute(routing.Route):
|
|
|
348
380
|
name: Optional[str] = None,
|
|
349
381
|
methods: Optional[Union[Set[str], List[str]]] = None,
|
|
350
382
|
operation_id: Optional[str] = None,
|
|
351
|
-
response_model_include: Optional[
|
|
352
|
-
response_model_exclude: Optional[
|
|
383
|
+
response_model_include: Optional[IncEx] = None,
|
|
384
|
+
response_model_exclude: Optional[IncEx] = None,
|
|
353
385
|
response_model_by_alias: bool = True,
|
|
354
386
|
response_model_exclude_unset: bool = False,
|
|
355
387
|
response_model_exclude_defaults: bool = False,
|
|
@@ -414,7 +446,11 @@ class APIRoute(routing.Route):
|
|
|
414
446
|
), f"Status code {status_code} must not have a response body"
|
|
415
447
|
response_name = "Response_" + self.unique_id
|
|
416
448
|
self.response_field = create_response_field(
|
|
417
|
-
name=response_name,
|
|
449
|
+
name=response_name,
|
|
450
|
+
type_=self.response_model,
|
|
451
|
+
# TODO: This should actually set mode='serialization', just, that changes the schemas
|
|
452
|
+
# mode="serialization",
|
|
453
|
+
mode="validation",
|
|
418
454
|
)
|
|
419
455
|
# Create a clone of the field, so that a Pydantic submodel is not returned
|
|
420
456
|
# as is just because it's an instance of a subclass of a more limited class
|
|
@@ -423,6 +459,7 @@ class APIRoute(routing.Route):
|
|
|
423
459
|
# would pass the validation and be returned as is.
|
|
424
460
|
# By being a new field, no inheritance will be passed as is. A new model
|
|
425
461
|
# will be always created.
|
|
462
|
+
# TODO: remove when deprecating Pydantic v1
|
|
426
463
|
self.secure_cloned_response_field: Optional[
|
|
427
464
|
ModelField
|
|
428
465
|
] = create_cloned_field(self.response_field)
|
|
@@ -569,8 +606,8 @@ class APIRouter(routing.Router):
|
|
|
569
606
|
deprecated: Optional[bool] = None,
|
|
570
607
|
methods: Optional[Union[Set[str], List[str]]] = None,
|
|
571
608
|
operation_id: Optional[str] = None,
|
|
572
|
-
response_model_include: Optional[
|
|
573
|
-
response_model_exclude: Optional[
|
|
609
|
+
response_model_include: Optional[IncEx] = None,
|
|
610
|
+
response_model_exclude: Optional[IncEx] = None,
|
|
574
611
|
response_model_by_alias: bool = True,
|
|
575
612
|
response_model_exclude_unset: bool = False,
|
|
576
613
|
response_model_exclude_defaults: bool = False,
|
|
@@ -650,8 +687,8 @@ class APIRouter(routing.Router):
|
|
|
650
687
|
deprecated: Optional[bool] = None,
|
|
651
688
|
methods: Optional[List[str]] = None,
|
|
652
689
|
operation_id: Optional[str] = None,
|
|
653
|
-
response_model_include: Optional[
|
|
654
|
-
response_model_exclude: Optional[
|
|
690
|
+
response_model_include: Optional[IncEx] = None,
|
|
691
|
+
response_model_exclude: Optional[IncEx] = None,
|
|
655
692
|
response_model_by_alias: bool = True,
|
|
656
693
|
response_model_exclude_unset: bool = False,
|
|
657
694
|
response_model_exclude_defaults: bool = False,
|
|
@@ -877,8 +914,8 @@ class APIRouter(routing.Router):
|
|
|
877
914
|
responses: Optional[Dict[Union[int, str], Dict[str, Any]]] = None,
|
|
878
915
|
deprecated: Optional[bool] = None,
|
|
879
916
|
operation_id: Optional[str] = None,
|
|
880
|
-
response_model_include: Optional[
|
|
881
|
-
response_model_exclude: Optional[
|
|
917
|
+
response_model_include: Optional[IncEx] = None,
|
|
918
|
+
response_model_exclude: Optional[IncEx] = None,
|
|
882
919
|
response_model_by_alias: bool = True,
|
|
883
920
|
response_model_exclude_unset: bool = False,
|
|
884
921
|
response_model_exclude_defaults: bool = False,
|
|
@@ -933,8 +970,8 @@ class APIRouter(routing.Router):
|
|
|
933
970
|
responses: Optional[Dict[Union[int, str], Dict[str, Any]]] = None,
|
|
934
971
|
deprecated: Optional[bool] = None,
|
|
935
972
|
operation_id: Optional[str] = None,
|
|
936
|
-
response_model_include: Optional[
|
|
937
|
-
response_model_exclude: Optional[
|
|
973
|
+
response_model_include: Optional[IncEx] = None,
|
|
974
|
+
response_model_exclude: Optional[IncEx] = None,
|
|
938
975
|
response_model_by_alias: bool = True,
|
|
939
976
|
response_model_exclude_unset: bool = False,
|
|
940
977
|
response_model_exclude_defaults: bool = False,
|
|
@@ -989,8 +1026,8 @@ class APIRouter(routing.Router):
|
|
|
989
1026
|
responses: Optional[Dict[Union[int, str], Dict[str, Any]]] = None,
|
|
990
1027
|
deprecated: Optional[bool] = None,
|
|
991
1028
|
operation_id: Optional[str] = None,
|
|
992
|
-
response_model_include: Optional[
|
|
993
|
-
response_model_exclude: Optional[
|
|
1029
|
+
response_model_include: Optional[IncEx] = None,
|
|
1030
|
+
response_model_exclude: Optional[IncEx] = None,
|
|
994
1031
|
response_model_by_alias: bool = True,
|
|
995
1032
|
response_model_exclude_unset: bool = False,
|
|
996
1033
|
response_model_exclude_defaults: bool = False,
|
|
@@ -1045,8 +1082,8 @@ class APIRouter(routing.Router):
|
|
|
1045
1082
|
responses: Optional[Dict[Union[int, str], Dict[str, Any]]] = None,
|
|
1046
1083
|
deprecated: Optional[bool] = None,
|
|
1047
1084
|
operation_id: Optional[str] = None,
|
|
1048
|
-
response_model_include: Optional[
|
|
1049
|
-
response_model_exclude: Optional[
|
|
1085
|
+
response_model_include: Optional[IncEx] = None,
|
|
1086
|
+
response_model_exclude: Optional[IncEx] = None,
|
|
1050
1087
|
response_model_by_alias: bool = True,
|
|
1051
1088
|
response_model_exclude_unset: bool = False,
|
|
1052
1089
|
response_model_exclude_defaults: bool = False,
|
|
@@ -1101,8 +1138,8 @@ class APIRouter(routing.Router):
|
|
|
1101
1138
|
responses: Optional[Dict[Union[int, str], Dict[str, Any]]] = None,
|
|
1102
1139
|
deprecated: Optional[bool] = None,
|
|
1103
1140
|
operation_id: Optional[str] = None,
|
|
1104
|
-
response_model_include: Optional[
|
|
1105
|
-
response_model_exclude: Optional[
|
|
1141
|
+
response_model_include: Optional[IncEx] = None,
|
|
1142
|
+
response_model_exclude: Optional[IncEx] = None,
|
|
1106
1143
|
response_model_by_alias: bool = True,
|
|
1107
1144
|
response_model_exclude_unset: bool = False,
|
|
1108
1145
|
response_model_exclude_defaults: bool = False,
|
|
@@ -1157,8 +1194,8 @@ class APIRouter(routing.Router):
|
|
|
1157
1194
|
responses: Optional[Dict[Union[int, str], Dict[str, Any]]] = None,
|
|
1158
1195
|
deprecated: Optional[bool] = None,
|
|
1159
1196
|
operation_id: Optional[str] = None,
|
|
1160
|
-
response_model_include: Optional[
|
|
1161
|
-
response_model_exclude: Optional[
|
|
1197
|
+
response_model_include: Optional[IncEx] = None,
|
|
1198
|
+
response_model_exclude: Optional[IncEx] = None,
|
|
1162
1199
|
response_model_by_alias: bool = True,
|
|
1163
1200
|
response_model_exclude_unset: bool = False,
|
|
1164
1201
|
response_model_exclude_defaults: bool = False,
|
|
@@ -1213,8 +1250,8 @@ class APIRouter(routing.Router):
|
|
|
1213
1250
|
responses: Optional[Dict[Union[int, str], Dict[str, Any]]] = None,
|
|
1214
1251
|
deprecated: Optional[bool] = None,
|
|
1215
1252
|
operation_id: Optional[str] = None,
|
|
1216
|
-
response_model_include: Optional[
|
|
1217
|
-
response_model_exclude: Optional[
|
|
1253
|
+
response_model_include: Optional[IncEx] = None,
|
|
1254
|
+
response_model_exclude: Optional[IncEx] = None,
|
|
1218
1255
|
response_model_by_alias: bool = True,
|
|
1219
1256
|
response_model_exclude_unset: bool = False,
|
|
1220
1257
|
response_model_exclude_defaults: bool = False,
|
|
@@ -1269,8 +1306,8 @@ class APIRouter(routing.Router):
|
|
|
1269
1306
|
responses: Optional[Dict[Union[int, str], Dict[str, Any]]] = None,
|
|
1270
1307
|
deprecated: Optional[bool] = None,
|
|
1271
1308
|
operation_id: Optional[str] = None,
|
|
1272
|
-
response_model_include: Optional[
|
|
1273
|
-
response_model_exclude: Optional[
|
|
1309
|
+
response_model_include: Optional[IncEx] = None,
|
|
1310
|
+
response_model_exclude: Optional[IncEx] = None,
|
|
1274
1311
|
response_model_by_alias: bool = True,
|
|
1275
1312
|
response_model_exclude_unset: bool = False,
|
|
1276
1313
|
response_model_exclude_defaults: bool = False,
|
fastapi/security/oauth2.py
CHANGED
|
@@ -9,6 +9,9 @@ from fastapi.security.utils import get_authorization_scheme_param
|
|
|
9
9
|
from starlette.requests import Request
|
|
10
10
|
from starlette.status import HTTP_401_UNAUTHORIZED, HTTP_403_FORBIDDEN
|
|
11
11
|
|
|
12
|
+
# TODO: import from typing when deprecating Python 3.9
|
|
13
|
+
from typing_extensions import Annotated
|
|
14
|
+
|
|
12
15
|
|
|
13
16
|
class OAuth2PasswordRequestForm:
|
|
14
17
|
"""
|
|
@@ -45,12 +48,13 @@ class OAuth2PasswordRequestForm:
|
|
|
45
48
|
|
|
46
49
|
def __init__(
|
|
47
50
|
self,
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
51
|
+
*,
|
|
52
|
+
grant_type: Annotated[Union[str, None], Form(pattern="password")] = None,
|
|
53
|
+
username: Annotated[str, Form()],
|
|
54
|
+
password: Annotated[str, Form()],
|
|
55
|
+
scope: Annotated[str, Form()] = "",
|
|
56
|
+
client_id: Annotated[Union[str, None], Form()] = None,
|
|
57
|
+
client_secret: Annotated[Union[str, None], Form()] = None,
|
|
54
58
|
):
|
|
55
59
|
self.grant_type = grant_type
|
|
56
60
|
self.username = username
|
|
@@ -95,12 +99,12 @@ class OAuth2PasswordRequestFormStrict(OAuth2PasswordRequestForm):
|
|
|
95
99
|
|
|
96
100
|
def __init__(
|
|
97
101
|
self,
|
|
98
|
-
grant_type: str
|
|
99
|
-
username: str
|
|
100
|
-
password: str
|
|
101
|
-
scope: str
|
|
102
|
-
client_id:
|
|
103
|
-
client_secret:
|
|
102
|
+
grant_type: Annotated[str, Form(pattern="password")],
|
|
103
|
+
username: Annotated[str, Form()],
|
|
104
|
+
password: Annotated[str, Form()],
|
|
105
|
+
scope: Annotated[str, Form()] = "",
|
|
106
|
+
client_id: Annotated[Union[str, None], Form()] = None,
|
|
107
|
+
client_secret: Annotated[Union[str, None], Form()] = None,
|
|
104
108
|
):
|
|
105
109
|
super().__init__(
|
|
106
110
|
grant_type=grant_type,
|
fastapi/types.py
CHANGED
|
@@ -1,3 +1,11 @@
|
|
|
1
|
-
|
|
1
|
+
import types
|
|
2
|
+
from enum import Enum
|
|
3
|
+
from typing import Any, Callable, Dict, Set, Type, TypeVar, Union
|
|
4
|
+
|
|
5
|
+
from pydantic import BaseModel
|
|
2
6
|
|
|
3
7
|
DecoratedCallable = TypeVar("DecoratedCallable", bound=Callable[..., Any])
|
|
8
|
+
UnionType = getattr(types, "UnionType", Union)
|
|
9
|
+
NoneType = getattr(types, "UnionType", None)
|
|
10
|
+
ModelNameMap = Dict[Union[Type[BaseModel], Type[Enum]], str]
|
|
11
|
+
IncEx = Union[Set[int], Set[str], Dict[int, Any], Dict[str, Any]]
|
fastapi/utils.py
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import re
|
|
2
2
|
import warnings
|
|
3
3
|
from dataclasses import is_dataclass
|
|
4
|
-
from enum import Enum
|
|
5
4
|
from typing import (
|
|
6
5
|
TYPE_CHECKING,
|
|
7
6
|
Any,
|
|
@@ -16,13 +15,20 @@ from typing import (
|
|
|
16
15
|
from weakref import WeakKeyDictionary
|
|
17
16
|
|
|
18
17
|
import fastapi
|
|
18
|
+
from fastapi._compat import (
|
|
19
|
+
PYDANTIC_V2,
|
|
20
|
+
BaseConfig,
|
|
21
|
+
ModelField,
|
|
22
|
+
PydanticSchemaGenerationError,
|
|
23
|
+
Undefined,
|
|
24
|
+
UndefinedType,
|
|
25
|
+
Validator,
|
|
26
|
+
lenient_issubclass,
|
|
27
|
+
)
|
|
19
28
|
from fastapi.datastructures import DefaultPlaceholder, DefaultType
|
|
20
|
-
from
|
|
21
|
-
from pydantic import
|
|
22
|
-
from
|
|
23
|
-
from pydantic.fields import FieldInfo, ModelField, UndefinedType
|
|
24
|
-
from pydantic.schema import model_process_schema
|
|
25
|
-
from pydantic.utils import lenient_issubclass
|
|
29
|
+
from pydantic import BaseModel, create_model
|
|
30
|
+
from pydantic.fields import FieldInfo
|
|
31
|
+
from typing_extensions import Literal
|
|
26
32
|
|
|
27
33
|
if TYPE_CHECKING: # pragma: nocover
|
|
28
34
|
from .routing import APIRoute
|
|
@@ -50,24 +56,6 @@ def is_body_allowed_for_status_code(status_code: Union[int, str, None]) -> bool:
|
|
|
50
56
|
return not (current_status_code < 200 or current_status_code in {204, 304})
|
|
51
57
|
|
|
52
58
|
|
|
53
|
-
def get_model_definitions(
|
|
54
|
-
*,
|
|
55
|
-
flat_models: Set[Union[Type[BaseModel], Type[Enum]]],
|
|
56
|
-
model_name_map: Dict[Union[Type[BaseModel], Type[Enum]], str],
|
|
57
|
-
) -> Dict[str, Any]:
|
|
58
|
-
definitions: Dict[str, Dict[str, Any]] = {}
|
|
59
|
-
for model in flat_models:
|
|
60
|
-
m_schema, m_definitions, m_nested_models = model_process_schema(
|
|
61
|
-
model, model_name_map=model_name_map, ref_prefix=REF_PREFIX
|
|
62
|
-
)
|
|
63
|
-
definitions.update(m_definitions)
|
|
64
|
-
model_name = model_name_map[model]
|
|
65
|
-
if "description" in m_schema:
|
|
66
|
-
m_schema["description"] = m_schema["description"].split("\f")[0]
|
|
67
|
-
definitions[model_name] = m_schema
|
|
68
|
-
return definitions
|
|
69
|
-
|
|
70
|
-
|
|
71
59
|
def get_path_param_names(path: str) -> Set[str]:
|
|
72
60
|
return set(re.findall("{(.*?)}", path))
|
|
73
61
|
|
|
@@ -76,30 +64,40 @@ def create_response_field(
|
|
|
76
64
|
name: str,
|
|
77
65
|
type_: Type[Any],
|
|
78
66
|
class_validators: Optional[Dict[str, Validator]] = None,
|
|
79
|
-
default: Optional[Any] =
|
|
80
|
-
required: Union[bool, UndefinedType] =
|
|
67
|
+
default: Optional[Any] = Undefined,
|
|
68
|
+
required: Union[bool, UndefinedType] = Undefined,
|
|
81
69
|
model_config: Type[BaseConfig] = BaseConfig,
|
|
82
70
|
field_info: Optional[FieldInfo] = None,
|
|
83
71
|
alias: Optional[str] = None,
|
|
72
|
+
mode: Literal["validation", "serialization"] = "validation",
|
|
84
73
|
) -> ModelField:
|
|
85
74
|
"""
|
|
86
75
|
Create a new response field. Raises if type_ is invalid.
|
|
87
76
|
"""
|
|
88
77
|
class_validators = class_validators or {}
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
78
|
+
if PYDANTIC_V2:
|
|
79
|
+
field_info = field_info or FieldInfo(
|
|
80
|
+
annotation=type_, default=default, alias=alias
|
|
81
|
+
)
|
|
82
|
+
else:
|
|
83
|
+
field_info = field_info or FieldInfo()
|
|
84
|
+
kwargs = {"name": name, "field_info": field_info}
|
|
85
|
+
if PYDANTIC_V2:
|
|
86
|
+
kwargs.update({"mode": mode})
|
|
87
|
+
else:
|
|
88
|
+
kwargs.update(
|
|
89
|
+
{
|
|
90
|
+
"type_": type_,
|
|
91
|
+
"class_validators": class_validators,
|
|
92
|
+
"default": default,
|
|
93
|
+
"required": required,
|
|
94
|
+
"model_config": model_config,
|
|
95
|
+
"alias": alias,
|
|
96
|
+
}
|
|
101
97
|
)
|
|
102
|
-
|
|
98
|
+
try:
|
|
99
|
+
return ModelField(**kwargs) # type: ignore[arg-type]
|
|
100
|
+
except (RuntimeError, PydanticSchemaGenerationError):
|
|
103
101
|
raise fastapi.exceptions.FastAPIError(
|
|
104
102
|
"Invalid args for response field! Hint: "
|
|
105
103
|
f"check that {type_} is a valid Pydantic field type. "
|
|
@@ -116,6 +114,8 @@ def create_cloned_field(
|
|
|
116
114
|
*,
|
|
117
115
|
cloned_types: Optional[MutableMapping[Type[BaseModel], Type[BaseModel]]] = None,
|
|
118
116
|
) -> ModelField:
|
|
117
|
+
if PYDANTIC_V2:
|
|
118
|
+
return field
|
|
119
119
|
# cloned_types caches already cloned types to support recursive models and improve
|
|
120
120
|
# performance by avoiding unecessary cloning
|
|
121
121
|
if cloned_types is None:
|
|
@@ -136,30 +136,30 @@ def create_cloned_field(
|
|
|
136
136
|
f, cloned_types=cloned_types
|
|
137
137
|
)
|
|
138
138
|
new_field = create_response_field(name=field.name, type_=use_type)
|
|
139
|
-
new_field.has_alias = field.has_alias
|
|
140
|
-
new_field.alias = field.alias
|
|
141
|
-
new_field.class_validators = field.class_validators
|
|
142
|
-
new_field.default = field.default
|
|
143
|
-
new_field.required = field.required
|
|
144
|
-
new_field.model_config = field.model_config
|
|
139
|
+
new_field.has_alias = field.has_alias # type: ignore[attr-defined]
|
|
140
|
+
new_field.alias = field.alias # type: ignore[misc]
|
|
141
|
+
new_field.class_validators = field.class_validators # type: ignore[attr-defined]
|
|
142
|
+
new_field.default = field.default # type: ignore[misc]
|
|
143
|
+
new_field.required = field.required # type: ignore[misc]
|
|
144
|
+
new_field.model_config = field.model_config # type: ignore[attr-defined]
|
|
145
145
|
new_field.field_info = field.field_info
|
|
146
|
-
new_field.allow_none = field.allow_none
|
|
147
|
-
new_field.validate_always = field.validate_always
|
|
148
|
-
if field.sub_fields:
|
|
149
|
-
new_field.sub_fields = [
|
|
146
|
+
new_field.allow_none = field.allow_none # type: ignore[attr-defined]
|
|
147
|
+
new_field.validate_always = field.validate_always # type: ignore[attr-defined]
|
|
148
|
+
if field.sub_fields: # type: ignore[attr-defined]
|
|
149
|
+
new_field.sub_fields = [ # type: ignore[attr-defined]
|
|
150
150
|
create_cloned_field(sub_field, cloned_types=cloned_types)
|
|
151
|
-
for sub_field in field.sub_fields
|
|
151
|
+
for sub_field in field.sub_fields # type: ignore[attr-defined]
|
|
152
152
|
]
|
|
153
|
-
if field.key_field:
|
|
154
|
-
new_field.key_field = create_cloned_field(
|
|
155
|
-
field.key_field, cloned_types=cloned_types
|
|
153
|
+
if field.key_field: # type: ignore[attr-defined]
|
|
154
|
+
new_field.key_field = create_cloned_field( # type: ignore[attr-defined]
|
|
155
|
+
field.key_field, cloned_types=cloned_types # type: ignore[attr-defined]
|
|
156
156
|
)
|
|
157
|
-
new_field.validators = field.validators
|
|
158
|
-
new_field.pre_validators = field.pre_validators
|
|
159
|
-
new_field.post_validators = field.post_validators
|
|
160
|
-
new_field.parse_json = field.parse_json
|
|
161
|
-
new_field.shape = field.shape
|
|
162
|
-
new_field.populate_validators()
|
|
157
|
+
new_field.validators = field.validators # type: ignore[attr-defined]
|
|
158
|
+
new_field.pre_validators = field.pre_validators # type: ignore[attr-defined]
|
|
159
|
+
new_field.post_validators = field.post_validators # type: ignore[attr-defined]
|
|
160
|
+
new_field.parse_json = field.parse_json # type: ignore[attr-defined]
|
|
161
|
+
new_field.shape = field.shape # type: ignore[attr-defined]
|
|
162
|
+
new_field.populate_validators() # type: ignore[attr-defined]
|
|
163
163
|
return new_field
|
|
164
164
|
|
|
165
165
|
|
|
@@ -220,3 +220,9 @@ def get_value_or_default(
|
|
|
220
220
|
if not isinstance(item, DefaultPlaceholder):
|
|
221
221
|
return item
|
|
222
222
|
return first_item
|
|
223
|
+
|
|
224
|
+
|
|
225
|
+
def match_pydantic_error_url(error_type: str) -> Any:
|
|
226
|
+
from dirty_equals import IsStr
|
|
227
|
+
|
|
228
|
+
return IsStr(regex=rf"^https://errors\.pydantic\.dev/.*/v/{error_type}")
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: fastapi
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.100.0
|
|
4
4
|
Summary: FastAPI framework, high performance, easy to learn, fast to code, ready for production
|
|
5
5
|
Project-URL: Homepage, https://github.com/tiangolo/fastapi
|
|
6
6
|
Project-URL: Documentation, https://fastapi.tiangolo.com/
|
|
@@ -36,15 +36,17 @@ Classifier: Topic :: Software Development :: Libraries :: Application Frameworks
|
|
|
36
36
|
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
37
37
|
Classifier: Typing :: Typed
|
|
38
38
|
Requires-Python: >=3.7
|
|
39
|
-
Requires-Dist: pydantic!=1.8,!=1.8.1
|
|
39
|
+
Requires-Dist: pydantic!=1.8,!=1.8.1,!=2.0.0,!=2.0.1,<3.0.0,>=1.7.4
|
|
40
40
|
Requires-Dist: starlette<0.28.0,>=0.27.0
|
|
41
41
|
Requires-Dist: typing-extensions>=4.5.0
|
|
42
42
|
Provides-Extra: all
|
|
43
|
-
Requires-Dist: email-validator>=
|
|
43
|
+
Requires-Dist: email-validator>=2.0.0; extra == 'all'
|
|
44
44
|
Requires-Dist: httpx>=0.23.0; extra == 'all'
|
|
45
45
|
Requires-Dist: itsdangerous>=1.1.0; extra == 'all'
|
|
46
46
|
Requires-Dist: jinja2>=2.11.2; extra == 'all'
|
|
47
47
|
Requires-Dist: orjson>=3.2.1; extra == 'all'
|
|
48
|
+
Requires-Dist: pydantic-extra-types>=2.0.0; extra == 'all'
|
|
49
|
+
Requires-Dist: pydantic-settings>=2.0.0; extra == 'all'
|
|
48
50
|
Requires-Dist: python-multipart>=0.0.5; extra == 'all'
|
|
49
51
|
Requires-Dist: pyyaml>=5.3.1; extra == 'all'
|
|
50
52
|
Requires-Dist: ujson!=4.0.2,!=4.1.0,!=4.2.0,!=4.3.0,!=5.0.0,!=5.1.0,>=4.0.1; extra == 'all'
|
|
@@ -500,6 +502,8 @@ To understand more about it, see the section <a href="https://fastapi.tiangolo.c
|
|
|
500
502
|
Used by Pydantic:
|
|
501
503
|
|
|
502
504
|
* <a href="https://github.com/JoshData/python-email-validator" target="_blank"><code>email_validator</code></a> - for email validation.
|
|
505
|
+
* <a href="https://docs.pydantic.dev/latest/usage/pydantic_settings/" target="_blank"><code>pydantic-settings</code></a> - for settings management.
|
|
506
|
+
* <a href="https://docs.pydantic.dev/latest/usage/types/extra_types/extra_types/" target="_blank"><code>pydantic-extra-types</code></a> - for extra types to be used with Pydantic.
|
|
503
507
|
|
|
504
508
|
Used by Starlette:
|
|
505
509
|
|
|
@@ -1,27 +1,28 @@
|
|
|
1
|
-
fastapi/__init__.py,sha256=
|
|
2
|
-
fastapi/
|
|
1
|
+
fastapi/__init__.py,sha256=cdUs7opWotDE2bN3Mgml7gFtCBbmNhXm5KvrkggJ8-0,1081
|
|
2
|
+
fastapi/_compat.py,sha256=lc_zD5IiWpKQTo83OFziqSBUWmsH2pqOzJVCPx8fCjw,22213
|
|
3
|
+
fastapi/applications.py,sha256=CAjthLITFb8scqk9jTK8d7rKuy_6nGJEkhTWcfph-xI,40021
|
|
3
4
|
fastapi/background.py,sha256=HtN5_pJJrOdalSbuGSMKJAPNWUU5h7rY_BXXubu7-IQ,76
|
|
4
5
|
fastapi/concurrency.py,sha256=h7ZhZG8cEiVx6an1txcB2RKpjW6_xmHwFo9E3vUiMQA,1468
|
|
5
|
-
fastapi/datastructures.py,sha256=
|
|
6
|
-
fastapi/encoders.py,sha256=
|
|
6
|
+
fastapi/datastructures.py,sha256=iWyfPvU6gZuFPHUC1RzRQP6VnLqYWnD75no5uLIxB48,2793
|
|
7
|
+
fastapi/encoders.py,sha256=uvI5zloRkzjlXJ-VnwvPJS5lD7j9n-MYhAoEXNrpV9k,8056
|
|
7
8
|
fastapi/exception_handlers.py,sha256=MBrIOA-ugjJDivIi4rSsUJBdTsjuzN76q4yh0q1COKw,1332
|
|
8
|
-
fastapi/exceptions.py,sha256=
|
|
9
|
+
fastapi/exceptions.py,sha256=B-KRexbd3Vtf_cP_YYY9ZocPGoGn1F2UvJ41Cdi0_3k,1400
|
|
9
10
|
fastapi/logger.py,sha256=I9NNi3ov8AcqbsbC9wl1X-hdItKgYt2XTrx1f99Zpl4,54
|
|
10
|
-
fastapi/param_functions.py,sha256=
|
|
11
|
-
fastapi/params.py,sha256=
|
|
11
|
+
fastapi/param_functions.py,sha256=ycrVFfSANRCgaAaRT7pC9_5okx_e4D0L8NUz6wTUKAw,18411
|
|
12
|
+
fastapi/params.py,sha256=yxRVq1uY-afW839YdE34gsQbVrF02hz-t7lEW3yfxM8,26883
|
|
12
13
|
fastapi/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
13
14
|
fastapi/requests.py,sha256=zayepKFcienBllv3snmWI20Gk0oHNVLU4DDhqXBb4LU,142
|
|
14
15
|
fastapi/responses.py,sha256=on95e4CfSRyNz7MEjqFuzsP-eW8kHWTxEl_Z-Vzb7lA,1242
|
|
15
|
-
fastapi/routing.py,sha256=
|
|
16
|
+
fastapi/routing.py,sha256=bhsAPsSHTpINdtUrYoH-_bEy5E9iXfvVCW9p4KpRKxI,56905
|
|
16
17
|
fastapi/staticfiles.py,sha256=iirGIt3sdY2QZXd36ijs3Cj-T0FuGFda3cd90kM9Ikw,69
|
|
17
18
|
fastapi/templating.py,sha256=4zsuTWgcjcEainMJFAlW6-gnslm6AgOS1SiiDWfmQxk,76
|
|
18
19
|
fastapi/testclient.py,sha256=nBvaAmX66YldReJNZXPOk1sfuo2Q6hs8bOvIaCep6LQ,66
|
|
19
|
-
fastapi/types.py,sha256=
|
|
20
|
-
fastapi/utils.py,sha256=
|
|
20
|
+
fastapi/types.py,sha256=WZJ1jvm1MCwIrxxRYxKwtXS9HqcGk0RnCbLzrMZh-lI,428
|
|
21
|
+
fastapi/utils.py,sha256=_vUwlqa4dq8M0Rl3Pro051teIccx36Z4hgecGH8F_oA,8179
|
|
21
22
|
fastapi/websockets.py,sha256=419uncYObEKZG0YcrXscfQQYLSWoE10jqxVMetGdR98,222
|
|
22
23
|
fastapi/dependencies/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
23
|
-
fastapi/dependencies/models.py,sha256
|
|
24
|
-
fastapi/dependencies/utils.py,sha256=
|
|
24
|
+
fastapi/dependencies/models.py,sha256=-n-YCxzgVBkurQi49qOTooT71v_oeAhHJ-qQFonxh5o,2494
|
|
25
|
+
fastapi/dependencies/utils.py,sha256=ffWJykKesqKxA_YVmrNhKogCF33AfZ2by0Tzfr_JlvY,29721
|
|
25
26
|
fastapi/middleware/__init__.py,sha256=oQDxiFVcc1fYJUOIFvphnK7pTT5kktmfL32QXpBFvvo,58
|
|
26
27
|
fastapi/middleware/asyncexitstack.py,sha256=LvMyVI1QdmWNWYPZqx295VFavssUfVpUsonPOsMWz1E,1035
|
|
27
28
|
fastapi/middleware/cors.py,sha256=ynwjWQZoc_vbhzZ3_ZXceoaSrslHFHPdoM52rXr0WUU,79
|
|
@@ -30,18 +31,18 @@ fastapi/middleware/httpsredirect.py,sha256=rL8eXMnmLijwVkH7_400zHri1AekfeBd6D6qs
|
|
|
30
31
|
fastapi/middleware/trustedhost.py,sha256=eE5XGRxGa7c5zPnMJDGp3BxaL25k5iVQlhnv-Pk0Pss,109
|
|
31
32
|
fastapi/middleware/wsgi.py,sha256=Z3Ue-7wni4lUZMvH3G9ek__acgYdJstbnpZX_HQAboY,79
|
|
32
33
|
fastapi/openapi/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
33
|
-
fastapi/openapi/constants.py,sha256=
|
|
34
|
+
fastapi/openapi/constants.py,sha256=adGzmis1L1HJRTE3kJ5fmHS_Noq6tIY6pWv_SFzoFDU,153
|
|
34
35
|
fastapi/openapi/docs.py,sha256=HbP76-u4A45BrL4WjLMhA3MBVI9xMx7XiMyDRS_ZO0E,6532
|
|
35
|
-
fastapi/openapi/models.py,sha256=
|
|
36
|
-
fastapi/openapi/utils.py,sha256=
|
|
36
|
+
fastapi/openapi/models.py,sha256=G_yoz3jFjCDZVE5xW0vcQPXRljtqhnbGR8oogEnr-Hc,17733
|
|
37
|
+
fastapi/openapi/utils.py,sha256=2c-l7jCmOpL3k6hiQwEmW5HS0uTczaEhzOXeHRUTv9E,21047
|
|
37
38
|
fastapi/security/__init__.py,sha256=bO8pNmxqVRXUjfl2mOKiVZLn0FpBQ61VUYVjmppnbJw,881
|
|
38
39
|
fastapi/security/api_key.py,sha256=92kxZjj9OuIvQCUpLszP9qlILRgx6hCh1x-bZdhmQDU,2939
|
|
39
40
|
fastapi/security/base.py,sha256=dl4pvbC-RxjfbWgPtCWd8MVU-7CB2SZ22rJDXVCXO6c,141
|
|
40
41
|
fastapi/security/http.py,sha256=VbTm1k6t6EjJAnCnYVquSOmSK7fATdKRgq0-TgC7FnQ,5964
|
|
41
|
-
fastapi/security/oauth2.py,sha256=
|
|
42
|
+
fastapi/security/oauth2.py,sha256=fXQdWuTtUKSZ9Lyrj9fDuQoXAmXTd9AVFDrrwStJKX0,8579
|
|
42
43
|
fastapi/security/open_id_connect_url.py,sha256=GKK84g6OZbOFCEZJyd27pGjpaClGxeZrYOemUzyhfbU,1141
|
|
43
44
|
fastapi/security/utils.py,sha256=bd8T0YM7UQD5ATKucr1bNtAvz_Y3__dVNAv5UebiPvc,293
|
|
44
|
-
fastapi-0.
|
|
45
|
-
fastapi-0.
|
|
46
|
-
fastapi-0.
|
|
47
|
-
fastapi-0.
|
|
45
|
+
fastapi-0.100.0.dist-info/METADATA,sha256=0copRVgfwPRo1J78FqTMFv5X022nfOI8Wvwr-8Cd6JA,23461
|
|
46
|
+
fastapi-0.100.0.dist-info/WHEEL,sha256=KGYbc1zXlYddvwxnNty23BeaKzh7YuoSIvIMO4jEhvw,87
|
|
47
|
+
fastapi-0.100.0.dist-info/licenses/LICENSE,sha256=Tsif_IFIW5f-xYSy1KlhAy7v_oNEU4lP2cEnSQbMdE4,1086
|
|
48
|
+
fastapi-0.100.0.dist-info/RECORD,,
|
|
File without changes
|