fastapi 0.97.0__py3-none-any.whl → 0.100.0b1__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 +597 -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 +91 -13
- fastapi/exceptions.py +20 -8
- fastapi/openapi/constants.py +1 -0
- fastapi/openapi/models.py +195 -52
- fastapi/openapi/utils.py +62 -39
- fastapi/param_functions.py +15 -1
- fastapi/params.py +57 -8
- fastapi/routing.py +79 -46
- fastapi/security/oauth2.py +16 -12
- fastapi/types.py +9 -1
- fastapi/utils.py +62 -60
- {fastapi-0.97.0.dist-info → fastapi-0.100.0b1.dist-info}/METADATA +3 -3
- {fastapi-0.97.0.dist-info → fastapi-0.100.0b1.dist-info}/RECORD +21 -20
- {fastapi-0.97.0.dist-info → fastapi-0.100.0b1.dist-info}/WHEEL +0 -0
- {fastapi-0.97.0.dist-info → fastapi-0.100.0b1.dist-info}/licenses/LICENSE +0 -0
fastapi/exceptions.py
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
from typing import Any, Dict, Optional, Sequence, Type
|
|
2
2
|
|
|
3
|
-
from pydantic import BaseModel,
|
|
4
|
-
from pydantic.error_wrappers import ErrorList
|
|
3
|
+
from pydantic import BaseModel, create_model
|
|
5
4
|
from starlette.exceptions import HTTPException as StarletteHTTPException
|
|
6
5
|
from starlette.exceptions import WebSocketException as WebSocketException # noqa: F401
|
|
7
6
|
|
|
@@ -26,12 +25,25 @@ class FastAPIError(RuntimeError):
|
|
|
26
25
|
"""
|
|
27
26
|
|
|
28
27
|
|
|
29
|
-
class
|
|
30
|
-
def __init__(self, errors: Sequence[
|
|
28
|
+
class ValidationException(Exception):
|
|
29
|
+
def __init__(self, errors: Sequence[Any]) -> None:
|
|
30
|
+
self._errors = errors
|
|
31
|
+
|
|
32
|
+
def errors(self) -> Sequence[Any]:
|
|
33
|
+
return self._errors
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
class RequestValidationError(ValidationException):
|
|
37
|
+
def __init__(self, errors: Sequence[Any], *, body: Any = None) -> None:
|
|
38
|
+
super().__init__(errors)
|
|
31
39
|
self.body = body
|
|
32
|
-
super().__init__(errors, RequestErrorModel)
|
|
33
40
|
|
|
34
41
|
|
|
35
|
-
class WebSocketRequestValidationError(
|
|
36
|
-
|
|
37
|
-
|
|
42
|
+
class WebSocketRequestValidationError(ValidationException):
|
|
43
|
+
pass
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
class ResponseValidationError(ValidationException):
|
|
47
|
+
def __init__(self, errors: Sequence[Any], *, body: Any = None) -> None:
|
|
48
|
+
super().__init__(errors)
|
|
49
|
+
self.body = body
|
fastapi/openapi/constants.py
CHANGED
fastapi/openapi/models.py
CHANGED
|
@@ -1,12 +1,20 @@
|
|
|
1
1
|
from enum import Enum
|
|
2
|
-
from typing import Any, Callable, Dict, Iterable, List, Optional, Union
|
|
3
|
-
|
|
2
|
+
from typing import Any, Callable, Dict, Iterable, List, Optional, Type, Union
|
|
3
|
+
|
|
4
|
+
from fastapi._compat import (
|
|
5
|
+
PYDANTIC_V2,
|
|
6
|
+
CoreSchema,
|
|
7
|
+
GetJsonSchemaHandler,
|
|
8
|
+
JsonSchemaValue,
|
|
9
|
+
_model_rebuild,
|
|
10
|
+
general_plain_validator_function,
|
|
11
|
+
)
|
|
4
12
|
from fastapi.logger import logger
|
|
5
13
|
from pydantic import AnyUrl, BaseModel, Field
|
|
6
14
|
from typing_extensions import Literal
|
|
7
15
|
|
|
8
16
|
try:
|
|
9
|
-
import email_validator
|
|
17
|
+
import email_validator
|
|
10
18
|
|
|
11
19
|
assert email_validator # make autoflake ignore the unused import
|
|
12
20
|
from pydantic import EmailStr
|
|
@@ -25,22 +33,52 @@ except ImportError: # pragma: no cover
|
|
|
25
33
|
)
|
|
26
34
|
return str(v)
|
|
27
35
|
|
|
36
|
+
@classmethod
|
|
37
|
+
def _validate(cls, __input_value: Any, _: Any) -> str:
|
|
38
|
+
logger.warning(
|
|
39
|
+
"email-validator not installed, email fields will be treated as str.\n"
|
|
40
|
+
"To install, run: pip install email-validator"
|
|
41
|
+
)
|
|
42
|
+
return str(__input_value)
|
|
43
|
+
|
|
44
|
+
@classmethod
|
|
45
|
+
def __get_pydantic_json_schema__(
|
|
46
|
+
cls, core_schema: CoreSchema, handler: GetJsonSchemaHandler
|
|
47
|
+
) -> JsonSchemaValue:
|
|
48
|
+
return {"type": "string", "format": "email"}
|
|
49
|
+
|
|
50
|
+
@classmethod
|
|
51
|
+
def __get_pydantic_core_schema__(
|
|
52
|
+
cls, source: Type[Any], handler: Callable[[Any], CoreSchema]
|
|
53
|
+
) -> CoreSchema:
|
|
54
|
+
return general_plain_validator_function(cls._validate)
|
|
55
|
+
|
|
28
56
|
|
|
29
57
|
class Contact(BaseModel):
|
|
30
58
|
name: Optional[str] = None
|
|
31
59
|
url: Optional[AnyUrl] = None
|
|
32
60
|
email: Optional[EmailStr] = None
|
|
33
61
|
|
|
34
|
-
|
|
35
|
-
|
|
62
|
+
if PYDANTIC_V2:
|
|
63
|
+
model_config = {"extra": "allow"}
|
|
64
|
+
|
|
65
|
+
else:
|
|
66
|
+
|
|
67
|
+
class Config:
|
|
68
|
+
extra = "allow"
|
|
36
69
|
|
|
37
70
|
|
|
38
71
|
class License(BaseModel):
|
|
39
72
|
name: str
|
|
40
73
|
url: Optional[AnyUrl] = None
|
|
41
74
|
|
|
42
|
-
|
|
43
|
-
|
|
75
|
+
if PYDANTIC_V2:
|
|
76
|
+
model_config = {"extra": "allow"}
|
|
77
|
+
|
|
78
|
+
else:
|
|
79
|
+
|
|
80
|
+
class Config:
|
|
81
|
+
extra = "allow"
|
|
44
82
|
|
|
45
83
|
|
|
46
84
|
class Info(BaseModel):
|
|
@@ -51,8 +89,13 @@ class Info(BaseModel):
|
|
|
51
89
|
license: Optional[License] = None
|
|
52
90
|
version: str
|
|
53
91
|
|
|
54
|
-
|
|
55
|
-
|
|
92
|
+
if PYDANTIC_V2:
|
|
93
|
+
model_config = {"extra": "allow"}
|
|
94
|
+
|
|
95
|
+
else:
|
|
96
|
+
|
|
97
|
+
class Config:
|
|
98
|
+
extra = "allow"
|
|
56
99
|
|
|
57
100
|
|
|
58
101
|
class ServerVariable(BaseModel):
|
|
@@ -60,8 +103,13 @@ class ServerVariable(BaseModel):
|
|
|
60
103
|
default: str
|
|
61
104
|
description: Optional[str] = None
|
|
62
105
|
|
|
63
|
-
|
|
64
|
-
|
|
106
|
+
if PYDANTIC_V2:
|
|
107
|
+
model_config = {"extra": "allow"}
|
|
108
|
+
|
|
109
|
+
else:
|
|
110
|
+
|
|
111
|
+
class Config:
|
|
112
|
+
extra = "allow"
|
|
65
113
|
|
|
66
114
|
|
|
67
115
|
class Server(BaseModel):
|
|
@@ -69,8 +117,13 @@ class Server(BaseModel):
|
|
|
69
117
|
description: Optional[str] = None
|
|
70
118
|
variables: Optional[Dict[str, ServerVariable]] = None
|
|
71
119
|
|
|
72
|
-
|
|
73
|
-
|
|
120
|
+
if PYDANTIC_V2:
|
|
121
|
+
model_config = {"extra": "allow"}
|
|
122
|
+
|
|
123
|
+
else:
|
|
124
|
+
|
|
125
|
+
class Config:
|
|
126
|
+
extra = "allow"
|
|
74
127
|
|
|
75
128
|
|
|
76
129
|
class Reference(BaseModel):
|
|
@@ -89,16 +142,26 @@ class XML(BaseModel):
|
|
|
89
142
|
attribute: Optional[bool] = None
|
|
90
143
|
wrapped: Optional[bool] = None
|
|
91
144
|
|
|
92
|
-
|
|
93
|
-
|
|
145
|
+
if PYDANTIC_V2:
|
|
146
|
+
model_config = {"extra": "allow"}
|
|
147
|
+
|
|
148
|
+
else:
|
|
149
|
+
|
|
150
|
+
class Config:
|
|
151
|
+
extra = "allow"
|
|
94
152
|
|
|
95
153
|
|
|
96
154
|
class ExternalDocumentation(BaseModel):
|
|
97
155
|
description: Optional[str] = None
|
|
98
156
|
url: AnyUrl
|
|
99
157
|
|
|
100
|
-
|
|
101
|
-
|
|
158
|
+
if PYDANTIC_V2:
|
|
159
|
+
model_config = {"extra": "allow"}
|
|
160
|
+
|
|
161
|
+
else:
|
|
162
|
+
|
|
163
|
+
class Config:
|
|
164
|
+
extra = "allow"
|
|
102
165
|
|
|
103
166
|
|
|
104
167
|
class Schema(BaseModel):
|
|
@@ -139,8 +202,13 @@ class Schema(BaseModel):
|
|
|
139
202
|
example: Optional[Any] = None
|
|
140
203
|
deprecated: Optional[bool] = None
|
|
141
204
|
|
|
142
|
-
|
|
143
|
-
|
|
205
|
+
if PYDANTIC_V2:
|
|
206
|
+
model_config = {"extra": "allow"}
|
|
207
|
+
|
|
208
|
+
else:
|
|
209
|
+
|
|
210
|
+
class Config:
|
|
211
|
+
extra = "allow"
|
|
144
212
|
|
|
145
213
|
|
|
146
214
|
class Example(BaseModel):
|
|
@@ -149,8 +217,13 @@ class Example(BaseModel):
|
|
|
149
217
|
value: Optional[Any] = None
|
|
150
218
|
externalValue: Optional[AnyUrl] = None
|
|
151
219
|
|
|
152
|
-
|
|
153
|
-
|
|
220
|
+
if PYDANTIC_V2:
|
|
221
|
+
model_config = {"extra": "allow"}
|
|
222
|
+
|
|
223
|
+
else:
|
|
224
|
+
|
|
225
|
+
class Config:
|
|
226
|
+
extra = "allow"
|
|
154
227
|
|
|
155
228
|
|
|
156
229
|
class ParameterInType(Enum):
|
|
@@ -167,8 +240,13 @@ class Encoding(BaseModel):
|
|
|
167
240
|
explode: Optional[bool] = None
|
|
168
241
|
allowReserved: Optional[bool] = None
|
|
169
242
|
|
|
170
|
-
|
|
171
|
-
|
|
243
|
+
if PYDANTIC_V2:
|
|
244
|
+
model_config = {"extra": "allow"}
|
|
245
|
+
|
|
246
|
+
else:
|
|
247
|
+
|
|
248
|
+
class Config:
|
|
249
|
+
extra = "allow"
|
|
172
250
|
|
|
173
251
|
|
|
174
252
|
class MediaType(BaseModel):
|
|
@@ -177,8 +255,13 @@ class MediaType(BaseModel):
|
|
|
177
255
|
examples: Optional[Dict[str, Union[Example, Reference]]] = None
|
|
178
256
|
encoding: Optional[Dict[str, Encoding]] = None
|
|
179
257
|
|
|
180
|
-
|
|
181
|
-
|
|
258
|
+
if PYDANTIC_V2:
|
|
259
|
+
model_config = {"extra": "allow"}
|
|
260
|
+
|
|
261
|
+
else:
|
|
262
|
+
|
|
263
|
+
class Config:
|
|
264
|
+
extra = "allow"
|
|
182
265
|
|
|
183
266
|
|
|
184
267
|
class ParameterBase(BaseModel):
|
|
@@ -195,8 +278,13 @@ class ParameterBase(BaseModel):
|
|
|
195
278
|
# Serialization rules for more complex scenarios
|
|
196
279
|
content: Optional[Dict[str, MediaType]] = None
|
|
197
280
|
|
|
198
|
-
|
|
199
|
-
|
|
281
|
+
if PYDANTIC_V2:
|
|
282
|
+
model_config = {"extra": "allow"}
|
|
283
|
+
|
|
284
|
+
else:
|
|
285
|
+
|
|
286
|
+
class Config:
|
|
287
|
+
extra = "allow"
|
|
200
288
|
|
|
201
289
|
|
|
202
290
|
class Parameter(ParameterBase):
|
|
@@ -213,8 +301,13 @@ class RequestBody(BaseModel):
|
|
|
213
301
|
content: Dict[str, MediaType]
|
|
214
302
|
required: Optional[bool] = None
|
|
215
303
|
|
|
216
|
-
|
|
217
|
-
|
|
304
|
+
if PYDANTIC_V2:
|
|
305
|
+
model_config = {"extra": "allow"}
|
|
306
|
+
|
|
307
|
+
else:
|
|
308
|
+
|
|
309
|
+
class Config:
|
|
310
|
+
extra = "allow"
|
|
218
311
|
|
|
219
312
|
|
|
220
313
|
class Link(BaseModel):
|
|
@@ -225,8 +318,13 @@ class Link(BaseModel):
|
|
|
225
318
|
description: Optional[str] = None
|
|
226
319
|
server: Optional[Server] = None
|
|
227
320
|
|
|
228
|
-
|
|
229
|
-
|
|
321
|
+
if PYDANTIC_V2:
|
|
322
|
+
model_config = {"extra": "allow"}
|
|
323
|
+
|
|
324
|
+
else:
|
|
325
|
+
|
|
326
|
+
class Config:
|
|
327
|
+
extra = "allow"
|
|
230
328
|
|
|
231
329
|
|
|
232
330
|
class Response(BaseModel):
|
|
@@ -235,8 +333,13 @@ class Response(BaseModel):
|
|
|
235
333
|
content: Optional[Dict[str, MediaType]] = None
|
|
236
334
|
links: Optional[Dict[str, Union[Link, Reference]]] = None
|
|
237
335
|
|
|
238
|
-
|
|
239
|
-
|
|
336
|
+
if PYDANTIC_V2:
|
|
337
|
+
model_config = {"extra": "allow"}
|
|
338
|
+
|
|
339
|
+
else:
|
|
340
|
+
|
|
341
|
+
class Config:
|
|
342
|
+
extra = "allow"
|
|
240
343
|
|
|
241
344
|
|
|
242
345
|
class Operation(BaseModel):
|
|
@@ -254,8 +357,13 @@ class Operation(BaseModel):
|
|
|
254
357
|
security: Optional[List[Dict[str, List[str]]]] = None
|
|
255
358
|
servers: Optional[List[Server]] = None
|
|
256
359
|
|
|
257
|
-
|
|
258
|
-
|
|
360
|
+
if PYDANTIC_V2:
|
|
361
|
+
model_config = {"extra": "allow"}
|
|
362
|
+
|
|
363
|
+
else:
|
|
364
|
+
|
|
365
|
+
class Config:
|
|
366
|
+
extra = "allow"
|
|
259
367
|
|
|
260
368
|
|
|
261
369
|
class PathItem(BaseModel):
|
|
@@ -273,8 +381,13 @@ class PathItem(BaseModel):
|
|
|
273
381
|
servers: Optional[List[Server]] = None
|
|
274
382
|
parameters: Optional[List[Union[Parameter, Reference]]] = None
|
|
275
383
|
|
|
276
|
-
|
|
277
|
-
|
|
384
|
+
if PYDANTIC_V2:
|
|
385
|
+
model_config = {"extra": "allow"}
|
|
386
|
+
|
|
387
|
+
else:
|
|
388
|
+
|
|
389
|
+
class Config:
|
|
390
|
+
extra = "allow"
|
|
278
391
|
|
|
279
392
|
|
|
280
393
|
class SecuritySchemeType(Enum):
|
|
@@ -288,8 +401,13 @@ class SecurityBase(BaseModel):
|
|
|
288
401
|
type_: SecuritySchemeType = Field(alias="type")
|
|
289
402
|
description: Optional[str] = None
|
|
290
403
|
|
|
291
|
-
|
|
292
|
-
|
|
404
|
+
if PYDANTIC_V2:
|
|
405
|
+
model_config = {"extra": "allow"}
|
|
406
|
+
|
|
407
|
+
else:
|
|
408
|
+
|
|
409
|
+
class Config:
|
|
410
|
+
extra = "allow"
|
|
293
411
|
|
|
294
412
|
|
|
295
413
|
class APIKeyIn(Enum):
|
|
@@ -318,8 +436,13 @@ class OAuthFlow(BaseModel):
|
|
|
318
436
|
refreshUrl: Optional[str] = None
|
|
319
437
|
scopes: Dict[str, str] = {}
|
|
320
438
|
|
|
321
|
-
|
|
322
|
-
|
|
439
|
+
if PYDANTIC_V2:
|
|
440
|
+
model_config = {"extra": "allow"}
|
|
441
|
+
|
|
442
|
+
else:
|
|
443
|
+
|
|
444
|
+
class Config:
|
|
445
|
+
extra = "allow"
|
|
323
446
|
|
|
324
447
|
|
|
325
448
|
class OAuthFlowImplicit(OAuthFlow):
|
|
@@ -345,8 +468,13 @@ class OAuthFlows(BaseModel):
|
|
|
345
468
|
clientCredentials: Optional[OAuthFlowClientCredentials] = None
|
|
346
469
|
authorizationCode: Optional[OAuthFlowAuthorizationCode] = None
|
|
347
470
|
|
|
348
|
-
|
|
349
|
-
|
|
471
|
+
if PYDANTIC_V2:
|
|
472
|
+
model_config = {"extra": "allow"}
|
|
473
|
+
|
|
474
|
+
else:
|
|
475
|
+
|
|
476
|
+
class Config:
|
|
477
|
+
extra = "allow"
|
|
350
478
|
|
|
351
479
|
|
|
352
480
|
class OAuth2(SecurityBase):
|
|
@@ -376,8 +504,13 @@ class Components(BaseModel):
|
|
|
376
504
|
# Using Any for Specification Extensions
|
|
377
505
|
callbacks: Optional[Dict[str, Union[Dict[str, PathItem], Reference, Any]]] = None
|
|
378
506
|
|
|
379
|
-
|
|
380
|
-
|
|
507
|
+
if PYDANTIC_V2:
|
|
508
|
+
model_config = {"extra": "allow"}
|
|
509
|
+
|
|
510
|
+
else:
|
|
511
|
+
|
|
512
|
+
class Config:
|
|
513
|
+
extra = "allow"
|
|
381
514
|
|
|
382
515
|
|
|
383
516
|
class Tag(BaseModel):
|
|
@@ -385,8 +518,13 @@ class Tag(BaseModel):
|
|
|
385
518
|
description: Optional[str] = None
|
|
386
519
|
externalDocs: Optional[ExternalDocumentation] = None
|
|
387
520
|
|
|
388
|
-
|
|
389
|
-
|
|
521
|
+
if PYDANTIC_V2:
|
|
522
|
+
model_config = {"extra": "allow"}
|
|
523
|
+
|
|
524
|
+
else:
|
|
525
|
+
|
|
526
|
+
class Config:
|
|
527
|
+
extra = "allow"
|
|
390
528
|
|
|
391
529
|
|
|
392
530
|
class OpenAPI(BaseModel):
|
|
@@ -400,10 +538,15 @@ class OpenAPI(BaseModel):
|
|
|
400
538
|
tags: Optional[List[Tag]] = None
|
|
401
539
|
externalDocs: Optional[ExternalDocumentation] = None
|
|
402
540
|
|
|
403
|
-
|
|
404
|
-
|
|
541
|
+
if PYDANTIC_V2:
|
|
542
|
+
model_config = {"extra": "allow"}
|
|
543
|
+
|
|
544
|
+
else:
|
|
545
|
+
|
|
546
|
+
class Config:
|
|
547
|
+
extra = "allow"
|
|
405
548
|
|
|
406
549
|
|
|
407
|
-
Schema
|
|
408
|
-
Operation
|
|
409
|
-
Encoding
|
|
550
|
+
_model_rebuild(Schema)
|
|
551
|
+
_model_rebuild(Operation)
|
|
552
|
+
_model_rebuild(Encoding)
|
fastapi/openapi/utils.py
CHANGED
|
@@ -1,32 +1,32 @@
|
|
|
1
1
|
import http.client
|
|
2
2
|
import inspect
|
|
3
3
|
import warnings
|
|
4
|
-
from enum import Enum
|
|
5
4
|
from typing import Any, Dict, List, Optional, Sequence, Set, Tuple, Type, Union, cast
|
|
6
5
|
|
|
7
6
|
from fastapi import routing
|
|
7
|
+
from fastapi._compat import (
|
|
8
|
+
GenerateJsonSchema,
|
|
9
|
+
ModelField,
|
|
10
|
+
Undefined,
|
|
11
|
+
get_compat_model_name_map,
|
|
12
|
+
get_definitions,
|
|
13
|
+
get_schema_from_model_field,
|
|
14
|
+
lenient_issubclass,
|
|
15
|
+
)
|
|
8
16
|
from fastapi.datastructures import DefaultPlaceholder
|
|
9
17
|
from fastapi.dependencies.models import Dependant
|
|
10
18
|
from fastapi.dependencies.utils import get_flat_dependant, get_flat_params
|
|
11
19
|
from fastapi.encoders import jsonable_encoder
|
|
12
|
-
from fastapi.openapi.constants import METHODS_WITH_BODY, REF_PREFIX
|
|
20
|
+
from fastapi.openapi.constants import METHODS_WITH_BODY, REF_PREFIX, REF_TEMPLATE
|
|
13
21
|
from fastapi.openapi.models import OpenAPI
|
|
14
22
|
from fastapi.params import Body, Param
|
|
15
23
|
from fastapi.responses import Response
|
|
24
|
+
from fastapi.types import ModelNameMap
|
|
16
25
|
from fastapi.utils import (
|
|
17
26
|
deep_dict_update,
|
|
18
27
|
generate_operation_id_for_path,
|
|
19
|
-
get_model_definitions,
|
|
20
28
|
is_body_allowed_for_status_code,
|
|
21
29
|
)
|
|
22
|
-
from pydantic import BaseModel
|
|
23
|
-
from pydantic.fields import ModelField, Undefined
|
|
24
|
-
from pydantic.schema import (
|
|
25
|
-
field_schema,
|
|
26
|
-
get_flat_models_from_fields,
|
|
27
|
-
get_model_name_map,
|
|
28
|
-
)
|
|
29
|
-
from pydantic.utils import lenient_issubclass
|
|
30
30
|
from starlette.responses import JSONResponse
|
|
31
31
|
from starlette.routing import BaseRoute
|
|
32
32
|
from starlette.status import HTTP_422_UNPROCESSABLE_ENTITY
|
|
@@ -88,7 +88,8 @@ def get_openapi_security_definitions(
|
|
|
88
88
|
def get_openapi_operation_parameters(
|
|
89
89
|
*,
|
|
90
90
|
all_route_params: Sequence[ModelField],
|
|
91
|
-
|
|
91
|
+
schema_generator: GenerateJsonSchema,
|
|
92
|
+
model_name_map: ModelNameMap,
|
|
92
93
|
) -> List[Dict[str, Any]]:
|
|
93
94
|
parameters = []
|
|
94
95
|
for param in all_route_params:
|
|
@@ -96,13 +97,16 @@ def get_openapi_operation_parameters(
|
|
|
96
97
|
field_info = cast(Param, field_info)
|
|
97
98
|
if not field_info.include_in_schema:
|
|
98
99
|
continue
|
|
100
|
+
param_schema = get_schema_from_model_field(
|
|
101
|
+
field=param,
|
|
102
|
+
schema_generator=schema_generator,
|
|
103
|
+
model_name_map=model_name_map,
|
|
104
|
+
)
|
|
99
105
|
parameter = {
|
|
100
106
|
"name": param.alias,
|
|
101
107
|
"in": field_info.in_.value,
|
|
102
108
|
"required": param.required,
|
|
103
|
-
"schema":
|
|
104
|
-
param, model_name_map=model_name_map, ref_prefix=REF_PREFIX
|
|
105
|
-
)[0],
|
|
109
|
+
"schema": param_schema,
|
|
106
110
|
}
|
|
107
111
|
if field_info.description:
|
|
108
112
|
parameter["description"] = field_info.description
|
|
@@ -119,13 +123,16 @@ def get_openapi_operation_parameters(
|
|
|
119
123
|
def get_openapi_operation_request_body(
|
|
120
124
|
*,
|
|
121
125
|
body_field: Optional[ModelField],
|
|
122
|
-
|
|
126
|
+
schema_generator: GenerateJsonSchema,
|
|
127
|
+
model_name_map: ModelNameMap,
|
|
123
128
|
) -> Optional[Dict[str, Any]]:
|
|
124
129
|
if not body_field:
|
|
125
130
|
return None
|
|
126
131
|
assert isinstance(body_field, ModelField)
|
|
127
|
-
body_schema
|
|
128
|
-
body_field,
|
|
132
|
+
body_schema = get_schema_from_model_field(
|
|
133
|
+
field=body_field,
|
|
134
|
+
schema_generator=schema_generator,
|
|
135
|
+
model_name_map=model_name_map,
|
|
129
136
|
)
|
|
130
137
|
field_info = cast(Body, body_field.field_info)
|
|
131
138
|
request_media_type = field_info.media_type
|
|
@@ -190,7 +197,11 @@ def get_openapi_operation_metadata(
|
|
|
190
197
|
|
|
191
198
|
|
|
192
199
|
def get_openapi_path(
|
|
193
|
-
*,
|
|
200
|
+
*,
|
|
201
|
+
route: routing.APIRoute,
|
|
202
|
+
operation_ids: Set[str],
|
|
203
|
+
schema_generator: GenerateJsonSchema,
|
|
204
|
+
model_name_map: ModelNameMap,
|
|
194
205
|
) -> Tuple[Dict[str, Any], Dict[str, Any], Dict[str, Any]]:
|
|
195
206
|
path = {}
|
|
196
207
|
security_schemes: Dict[str, Any] = {}
|
|
@@ -218,7 +229,9 @@ def get_openapi_path(
|
|
|
218
229
|
security_schemes.update(security_definitions)
|
|
219
230
|
all_route_params = get_flat_params(route.dependant)
|
|
220
231
|
operation_parameters = get_openapi_operation_parameters(
|
|
221
|
-
all_route_params=all_route_params,
|
|
232
|
+
all_route_params=all_route_params,
|
|
233
|
+
schema_generator=schema_generator,
|
|
234
|
+
model_name_map=model_name_map,
|
|
222
235
|
)
|
|
223
236
|
parameters.extend(operation_parameters)
|
|
224
237
|
if parameters:
|
|
@@ -236,7 +249,9 @@ def get_openapi_path(
|
|
|
236
249
|
operation["parameters"] = list(all_parameters.values())
|
|
237
250
|
if method in METHODS_WITH_BODY:
|
|
238
251
|
request_body_oai = get_openapi_operation_request_body(
|
|
239
|
-
body_field=route.body_field,
|
|
252
|
+
body_field=route.body_field,
|
|
253
|
+
schema_generator=schema_generator,
|
|
254
|
+
model_name_map=model_name_map,
|
|
240
255
|
)
|
|
241
256
|
if request_body_oai:
|
|
242
257
|
operation["requestBody"] = request_body_oai
|
|
@@ -250,8 +265,9 @@ def get_openapi_path(
|
|
|
250
265
|
cb_definitions,
|
|
251
266
|
) = get_openapi_path(
|
|
252
267
|
route=callback,
|
|
253
|
-
model_name_map=model_name_map,
|
|
254
268
|
operation_ids=operation_ids,
|
|
269
|
+
schema_generator=schema_generator,
|
|
270
|
+
model_name_map=model_name_map,
|
|
255
271
|
)
|
|
256
272
|
callbacks[callback.name] = {callback.path: cb_path}
|
|
257
273
|
operation["callbacks"] = callbacks
|
|
@@ -277,10 +293,10 @@ def get_openapi_path(
|
|
|
277
293
|
response_schema = {"type": "string"}
|
|
278
294
|
if lenient_issubclass(current_response_class, JSONResponse):
|
|
279
295
|
if route.response_field:
|
|
280
|
-
response_schema
|
|
281
|
-
route.response_field,
|
|
296
|
+
response_schema = get_schema_from_model_field(
|
|
297
|
+
field=route.response_field,
|
|
298
|
+
schema_generator=schema_generator,
|
|
282
299
|
model_name_map=model_name_map,
|
|
283
|
-
ref_prefix=REF_PREFIX,
|
|
284
300
|
)
|
|
285
301
|
else:
|
|
286
302
|
response_schema = {}
|
|
@@ -309,8 +325,10 @@ def get_openapi_path(
|
|
|
309
325
|
field = route.response_fields.get(additional_status_code)
|
|
310
326
|
additional_field_schema: Optional[Dict[str, Any]] = None
|
|
311
327
|
if field:
|
|
312
|
-
additional_field_schema
|
|
313
|
-
field
|
|
328
|
+
additional_field_schema = get_schema_from_model_field(
|
|
329
|
+
field=field,
|
|
330
|
+
schema_generator=schema_generator,
|
|
331
|
+
model_name_map=model_name_map,
|
|
314
332
|
)
|
|
315
333
|
media_type = route_response_media_type or "application/json"
|
|
316
334
|
additional_schema = (
|
|
@@ -356,13 +374,13 @@ def get_openapi_path(
|
|
|
356
374
|
return path, security_schemes, definitions
|
|
357
375
|
|
|
358
376
|
|
|
359
|
-
def
|
|
377
|
+
def get_fields_from_routes(
|
|
360
378
|
routes: Sequence[BaseRoute],
|
|
361
|
-
) ->
|
|
379
|
+
) -> List[ModelField]:
|
|
362
380
|
body_fields_from_routes: List[ModelField] = []
|
|
363
381
|
responses_from_routes: List[ModelField] = []
|
|
364
382
|
request_fields_from_routes: List[ModelField] = []
|
|
365
|
-
callback_flat_models:
|
|
383
|
+
callback_flat_models: List[ModelField] = []
|
|
366
384
|
for route in routes:
|
|
367
385
|
if getattr(route, "include_in_schema", None) and isinstance(
|
|
368
386
|
route, routing.APIRoute
|
|
@@ -377,13 +395,12 @@ def get_flat_models_from_routes(
|
|
|
377
395
|
if route.response_fields:
|
|
378
396
|
responses_from_routes.extend(route.response_fields.values())
|
|
379
397
|
if route.callbacks:
|
|
380
|
-
callback_flat_models
|
|
398
|
+
callback_flat_models.extend(get_fields_from_routes(route.callbacks))
|
|
381
399
|
params = get_flat_params(route.dependant)
|
|
382
400
|
request_fields_from_routes.extend(params)
|
|
383
401
|
|
|
384
|
-
flat_models = callback_flat_models
|
|
385
|
-
body_fields_from_routes + responses_from_routes + request_fields_from_routes
|
|
386
|
-
known_models=set(),
|
|
402
|
+
flat_models = callback_flat_models + list(
|
|
403
|
+
body_fields_from_routes + responses_from_routes + request_fields_from_routes
|
|
387
404
|
)
|
|
388
405
|
return flat_models
|
|
389
406
|
|
|
@@ -416,15 +433,21 @@ def get_openapi(
|
|
|
416
433
|
components: Dict[str, Dict[str, Any]] = {}
|
|
417
434
|
paths: Dict[str, Dict[str, Any]] = {}
|
|
418
435
|
operation_ids: Set[str] = set()
|
|
419
|
-
|
|
420
|
-
model_name_map =
|
|
421
|
-
|
|
422
|
-
|
|
436
|
+
all_fields = get_fields_from_routes(routes)
|
|
437
|
+
model_name_map = get_compat_model_name_map(all_fields)
|
|
438
|
+
schema_generator = GenerateJsonSchema(ref_template=REF_TEMPLATE)
|
|
439
|
+
definitions = get_definitions(
|
|
440
|
+
fields=all_fields,
|
|
441
|
+
schema_generator=schema_generator,
|
|
442
|
+
model_name_map=model_name_map,
|
|
423
443
|
)
|
|
424
444
|
for route in routes:
|
|
425
445
|
if isinstance(route, routing.APIRoute):
|
|
426
446
|
result = get_openapi_path(
|
|
427
|
-
route=route,
|
|
447
|
+
route=route,
|
|
448
|
+
operation_ids=operation_ids,
|
|
449
|
+
schema_generator=schema_generator,
|
|
450
|
+
model_name_map=model_name_map,
|
|
428
451
|
)
|
|
429
452
|
if result:
|
|
430
453
|
path, security_schemes, path_definitions = result
|