fastapi 0.117.1__py3-none-any.whl → 0.118.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.
- fastapi/__init__.py +1 -1
- fastapi/_compat.py +15 -3
- fastapi/applications.py +52 -1
- fastapi/middleware/asyncexitstack.py +18 -0
- fastapi/params.py +8 -8
- fastapi/routing.py +199 -130
- fastapi/security/oauth2.py +2 -2
- {fastapi-0.117.1.dist-info → fastapi-0.118.1.dist-info}/METADATA +1 -1
- {fastapi-0.117.1.dist-info → fastapi-0.118.1.dist-info}/RECORD +12 -11
- {fastapi-0.117.1.dist-info → fastapi-0.118.1.dist-info}/WHEEL +0 -0
- {fastapi-0.117.1.dist-info → fastapi-0.118.1.dist-info}/entry_points.txt +0 -0
- {fastapi-0.117.1.dist-info → fastapi-0.118.1.dist-info}/licenses/LICENSE +0 -0
fastapi/__init__.py
CHANGED
fastapi/_compat.py
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import warnings
|
|
1
2
|
from collections import deque
|
|
2
3
|
from copy import copy
|
|
3
4
|
from dataclasses import dataclass, is_dataclass
|
|
@@ -109,9 +110,20 @@ if PYDANTIC_V2:
|
|
|
109
110
|
return self.field_info.annotation
|
|
110
111
|
|
|
111
112
|
def __post_init__(self) -> None:
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
113
|
+
with warnings.catch_warnings():
|
|
114
|
+
# Pydantic >= 2.12.0 warns about field specific metadata that is unused
|
|
115
|
+
# (e.g. `TypeAdapter(Annotated[int, Field(alias='b')])`). In some cases, we
|
|
116
|
+
# end up building the type adapter from a model field annotation so we
|
|
117
|
+
# need to ignore the warning:
|
|
118
|
+
if PYDANTIC_VERSION_MINOR_TUPLE >= (2, 12):
|
|
119
|
+
from pydantic.warnings import UnsupportedFieldAttributeWarning
|
|
120
|
+
|
|
121
|
+
warnings.simplefilter(
|
|
122
|
+
"ignore", category=UnsupportedFieldAttributeWarning
|
|
123
|
+
)
|
|
124
|
+
self._type_adapter: TypeAdapter[Any] = TypeAdapter(
|
|
125
|
+
Annotated[self.field_info.annotation, self.field_info]
|
|
126
|
+
)
|
|
115
127
|
|
|
116
128
|
def get_default(self) -> Any:
|
|
117
129
|
if self.field_info.is_required():
|
fastapi/applications.py
CHANGED
|
@@ -22,6 +22,7 @@ from fastapi.exception_handlers import (
|
|
|
22
22
|
)
|
|
23
23
|
from fastapi.exceptions import RequestValidationError, WebSocketRequestValidationError
|
|
24
24
|
from fastapi.logger import logger
|
|
25
|
+
from fastapi.middleware.asyncexitstack import AsyncExitStackMiddleware
|
|
25
26
|
from fastapi.openapi.docs import (
|
|
26
27
|
get_redoc_html,
|
|
27
28
|
get_swagger_ui_html,
|
|
@@ -36,10 +37,12 @@ from starlette.datastructures import State
|
|
|
36
37
|
from starlette.exceptions import HTTPException
|
|
37
38
|
from starlette.middleware import Middleware
|
|
38
39
|
from starlette.middleware.base import BaseHTTPMiddleware
|
|
40
|
+
from starlette.middleware.errors import ServerErrorMiddleware
|
|
41
|
+
from starlette.middleware.exceptions import ExceptionMiddleware
|
|
39
42
|
from starlette.requests import Request
|
|
40
43
|
from starlette.responses import HTMLResponse, JSONResponse, Response
|
|
41
44
|
from starlette.routing import BaseRoute
|
|
42
|
-
from starlette.types import ASGIApp, Lifespan, Receive, Scope, Send
|
|
45
|
+
from starlette.types import ASGIApp, ExceptionHandler, Lifespan, Receive, Scope, Send
|
|
43
46
|
from typing_extensions import Annotated, Doc, deprecated
|
|
44
47
|
|
|
45
48
|
AppType = TypeVar("AppType", bound="FastAPI")
|
|
@@ -990,6 +993,54 @@ class FastAPI(Starlette):
|
|
|
990
993
|
self.middleware_stack: Union[ASGIApp, None] = None
|
|
991
994
|
self.setup()
|
|
992
995
|
|
|
996
|
+
def build_middleware_stack(self) -> ASGIApp:
|
|
997
|
+
# Duplicate/override from Starlette to add AsyncExitStackMiddleware
|
|
998
|
+
# inside of ExceptionMiddleware, inside of custom user middlewares
|
|
999
|
+
debug = self.debug
|
|
1000
|
+
error_handler = None
|
|
1001
|
+
exception_handlers: dict[Any, ExceptionHandler] = {}
|
|
1002
|
+
|
|
1003
|
+
for key, value in self.exception_handlers.items():
|
|
1004
|
+
if key in (500, Exception):
|
|
1005
|
+
error_handler = value
|
|
1006
|
+
else:
|
|
1007
|
+
exception_handlers[key] = value
|
|
1008
|
+
|
|
1009
|
+
middleware = (
|
|
1010
|
+
[Middleware(ServerErrorMiddleware, handler=error_handler, debug=debug)]
|
|
1011
|
+
+ self.user_middleware
|
|
1012
|
+
+ [
|
|
1013
|
+
Middleware(
|
|
1014
|
+
ExceptionMiddleware, handlers=exception_handlers, debug=debug
|
|
1015
|
+
),
|
|
1016
|
+
# Add FastAPI-specific AsyncExitStackMiddleware for closing files.
|
|
1017
|
+
# Before this was also used for closing dependencies with yield but
|
|
1018
|
+
# those now have their own AsyncExitStack, to properly support
|
|
1019
|
+
# streaming responses while keeping compatibility with the previous
|
|
1020
|
+
# versions (as of writing 0.117.1) that allowed doing
|
|
1021
|
+
# except HTTPException inside a dependency with yield.
|
|
1022
|
+
# This needs to happen after user middlewares because those create a
|
|
1023
|
+
# new contextvars context copy by using a new AnyIO task group.
|
|
1024
|
+
# This AsyncExitStack preserves the context for contextvars, not
|
|
1025
|
+
# strictly necessary for closing files but it was one of the original
|
|
1026
|
+
# intentions.
|
|
1027
|
+
# If the AsyncExitStack lived outside of the custom middlewares and
|
|
1028
|
+
# contextvars were set, for example in a dependency with 'yield'
|
|
1029
|
+
# in that internal contextvars context, the values would not be
|
|
1030
|
+
# available in the outer context of the AsyncExitStack.
|
|
1031
|
+
# By placing the middleware and the AsyncExitStack here, inside all
|
|
1032
|
+
# user middlewares, the same context is used.
|
|
1033
|
+
# This is currently not needed, only for closing files, but used to be
|
|
1034
|
+
# important when dependencies with yield were closed here.
|
|
1035
|
+
Middleware(AsyncExitStackMiddleware),
|
|
1036
|
+
]
|
|
1037
|
+
)
|
|
1038
|
+
|
|
1039
|
+
app = self.router
|
|
1040
|
+
for cls, args, kwargs in reversed(middleware):
|
|
1041
|
+
app = cls(app, *args, **kwargs)
|
|
1042
|
+
return app
|
|
1043
|
+
|
|
993
1044
|
def openapi(self) -> Dict[str, Any]:
|
|
994
1045
|
"""
|
|
995
1046
|
Generate the OpenAPI schema of the application. This is called by FastAPI
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
from contextlib import AsyncExitStack
|
|
2
|
+
|
|
3
|
+
from starlette.types import ASGIApp, Receive, Scope, Send
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
# Used mainly to close files after the request is done, dependencies are closed
|
|
7
|
+
# in their own AsyncExitStack
|
|
8
|
+
class AsyncExitStackMiddleware:
|
|
9
|
+
def __init__(
|
|
10
|
+
self, app: ASGIApp, context_name: str = "fastapi_middleware_astack"
|
|
11
|
+
) -> None:
|
|
12
|
+
self.app = app
|
|
13
|
+
self.context_name = context_name
|
|
14
|
+
|
|
15
|
+
async def __call__(self, scope: Scope, receive: Receive, send: Send) -> None:
|
|
16
|
+
async with AsyncExitStack() as stack:
|
|
17
|
+
scope[self.context_name] = stack
|
|
18
|
+
await self.app(scope, receive, send)
|
fastapi/params.py
CHANGED
|
@@ -22,7 +22,7 @@ class ParamTypes(Enum):
|
|
|
22
22
|
cookie = "cookie"
|
|
23
23
|
|
|
24
24
|
|
|
25
|
-
class Param(FieldInfo):
|
|
25
|
+
class Param(FieldInfo): # type: ignore[misc]
|
|
26
26
|
in_: ParamTypes
|
|
27
27
|
|
|
28
28
|
def __init__(
|
|
@@ -136,7 +136,7 @@ class Param(FieldInfo):
|
|
|
136
136
|
return f"{self.__class__.__name__}({self.default})"
|
|
137
137
|
|
|
138
138
|
|
|
139
|
-
class Path(Param):
|
|
139
|
+
class Path(Param): # type: ignore[misc]
|
|
140
140
|
in_ = ParamTypes.path
|
|
141
141
|
|
|
142
142
|
def __init__(
|
|
@@ -222,7 +222,7 @@ class Path(Param):
|
|
|
222
222
|
)
|
|
223
223
|
|
|
224
224
|
|
|
225
|
-
class Query(Param):
|
|
225
|
+
class Query(Param): # type: ignore[misc]
|
|
226
226
|
in_ = ParamTypes.query
|
|
227
227
|
|
|
228
228
|
def __init__(
|
|
@@ -306,7 +306,7 @@ class Query(Param):
|
|
|
306
306
|
)
|
|
307
307
|
|
|
308
308
|
|
|
309
|
-
class Header(Param):
|
|
309
|
+
class Header(Param): # type: ignore[misc]
|
|
310
310
|
in_ = ParamTypes.header
|
|
311
311
|
|
|
312
312
|
def __init__(
|
|
@@ -392,7 +392,7 @@ class Header(Param):
|
|
|
392
392
|
)
|
|
393
393
|
|
|
394
394
|
|
|
395
|
-
class Cookie(Param):
|
|
395
|
+
class Cookie(Param): # type: ignore[misc]
|
|
396
396
|
in_ = ParamTypes.cookie
|
|
397
397
|
|
|
398
398
|
def __init__(
|
|
@@ -476,7 +476,7 @@ class Cookie(Param):
|
|
|
476
476
|
)
|
|
477
477
|
|
|
478
478
|
|
|
479
|
-
class Body(FieldInfo):
|
|
479
|
+
class Body(FieldInfo): # type: ignore[misc]
|
|
480
480
|
def __init__(
|
|
481
481
|
self,
|
|
482
482
|
default: Any = Undefined,
|
|
@@ -593,7 +593,7 @@ class Body(FieldInfo):
|
|
|
593
593
|
return f"{self.__class__.__name__}({self.default})"
|
|
594
594
|
|
|
595
595
|
|
|
596
|
-
class Form(Body):
|
|
596
|
+
class Form(Body): # type: ignore[misc]
|
|
597
597
|
def __init__(
|
|
598
598
|
self,
|
|
599
599
|
default: Any = Undefined,
|
|
@@ -677,7 +677,7 @@ class Form(Body):
|
|
|
677
677
|
)
|
|
678
678
|
|
|
679
679
|
|
|
680
|
-
class File(Form):
|
|
680
|
+
class File(Form): # type: ignore[misc]
|
|
681
681
|
def __init__(
|
|
682
682
|
self,
|
|
683
683
|
default: Any = Undefined,
|
fastapi/routing.py
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import dataclasses
|
|
2
2
|
import email.message
|
|
3
|
+
import functools
|
|
3
4
|
import inspect
|
|
4
5
|
import json
|
|
5
6
|
import sys
|
|
@@ -8,6 +9,7 @@ from enum import Enum, IntEnum
|
|
|
8
9
|
from typing import (
|
|
9
10
|
Any,
|
|
10
11
|
AsyncIterator,
|
|
12
|
+
Awaitable,
|
|
11
13
|
Callable,
|
|
12
14
|
Collection,
|
|
13
15
|
Coroutine,
|
|
@@ -59,6 +61,8 @@ from fastapi.utils import (
|
|
|
59
61
|
)
|
|
60
62
|
from pydantic import BaseModel
|
|
61
63
|
from starlette import routing
|
|
64
|
+
from starlette._exception_handler import wrap_app_handling_exceptions
|
|
65
|
+
from starlette._utils import is_async_callable
|
|
62
66
|
from starlette.concurrency import run_in_threadpool
|
|
63
67
|
from starlette.exceptions import HTTPException
|
|
64
68
|
from starlette.requests import Request
|
|
@@ -68,11 +72,9 @@ from starlette.routing import (
|
|
|
68
72
|
Match,
|
|
69
73
|
compile_path,
|
|
70
74
|
get_name,
|
|
71
|
-
request_response,
|
|
72
|
-
websocket_session,
|
|
73
75
|
)
|
|
74
76
|
from starlette.routing import Mount as Mount # noqa
|
|
75
|
-
from starlette.types import AppType, ASGIApp, Lifespan, Scope
|
|
77
|
+
from starlette.types import AppType, ASGIApp, Lifespan, Receive, Scope, Send
|
|
76
78
|
from starlette.websockets import WebSocket
|
|
77
79
|
from typing_extensions import Annotated, Doc, deprecated
|
|
78
80
|
|
|
@@ -82,6 +84,73 @@ else: # pragma: no cover
|
|
|
82
84
|
from asyncio import iscoroutinefunction
|
|
83
85
|
|
|
84
86
|
|
|
87
|
+
# Copy of starlette.routing.request_response modified to include the
|
|
88
|
+
# dependencies' AsyncExitStack
|
|
89
|
+
def request_response(
|
|
90
|
+
func: Callable[[Request], Union[Awaitable[Response], Response]],
|
|
91
|
+
) -> ASGIApp:
|
|
92
|
+
"""
|
|
93
|
+
Takes a function or coroutine `func(request) -> response`,
|
|
94
|
+
and returns an ASGI application.
|
|
95
|
+
"""
|
|
96
|
+
f: Callable[[Request], Awaitable[Response]] = (
|
|
97
|
+
func if is_async_callable(func) else functools.partial(run_in_threadpool, func) # type:ignore
|
|
98
|
+
)
|
|
99
|
+
|
|
100
|
+
async def app(scope: Scope, receive: Receive, send: Send) -> None:
|
|
101
|
+
request = Request(scope, receive, send)
|
|
102
|
+
|
|
103
|
+
async def app(scope: Scope, receive: Receive, send: Send) -> None:
|
|
104
|
+
# Starts customization
|
|
105
|
+
response_awaited = False
|
|
106
|
+
async with AsyncExitStack() as stack:
|
|
107
|
+
scope["fastapi_inner_astack"] = stack
|
|
108
|
+
# Same as in Starlette
|
|
109
|
+
response = await f(request)
|
|
110
|
+
await response(scope, receive, send)
|
|
111
|
+
# Continues customization
|
|
112
|
+
response_awaited = True
|
|
113
|
+
if not response_awaited:
|
|
114
|
+
raise FastAPIError(
|
|
115
|
+
"Response not awaited. There's a high chance that the "
|
|
116
|
+
"application code is raising an exception and a dependency with yield "
|
|
117
|
+
"has a block with a bare except, or a block with except Exception, "
|
|
118
|
+
"and is not raising the exception again. Read more about it in the "
|
|
119
|
+
"docs: https://fastapi.tiangolo.com/tutorial/dependencies/dependencies-with-yield/#dependencies-with-yield-and-except"
|
|
120
|
+
)
|
|
121
|
+
|
|
122
|
+
# Same as in Starlette
|
|
123
|
+
await wrap_app_handling_exceptions(app, request)(scope, receive, send)
|
|
124
|
+
|
|
125
|
+
return app
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
# Copy of starlette.routing.websocket_session modified to include the
|
|
129
|
+
# dependencies' AsyncExitStack
|
|
130
|
+
def websocket_session(
|
|
131
|
+
func: Callable[[WebSocket], Awaitable[None]],
|
|
132
|
+
) -> ASGIApp:
|
|
133
|
+
"""
|
|
134
|
+
Takes a coroutine `func(session)`, and returns an ASGI application.
|
|
135
|
+
"""
|
|
136
|
+
# assert asyncio.iscoroutinefunction(func), "WebSocket endpoints must be async"
|
|
137
|
+
|
|
138
|
+
async def app(scope: Scope, receive: Receive, send: Send) -> None:
|
|
139
|
+
session = WebSocket(scope, receive=receive, send=send)
|
|
140
|
+
|
|
141
|
+
async def app(scope: Scope, receive: Receive, send: Send) -> None:
|
|
142
|
+
# Starts customization
|
|
143
|
+
async with AsyncExitStack() as stack:
|
|
144
|
+
scope["fastapi_inner_astack"] = stack
|
|
145
|
+
# Same as in Starlette
|
|
146
|
+
await func(session)
|
|
147
|
+
|
|
148
|
+
# Same as in Starlette
|
|
149
|
+
await wrap_app_handling_exceptions(app, session)(scope, receive, send)
|
|
150
|
+
|
|
151
|
+
return app
|
|
152
|
+
|
|
153
|
+
|
|
85
154
|
def _prepare_response_content(
|
|
86
155
|
res: Any,
|
|
87
156
|
*,
|
|
@@ -246,119 +315,120 @@ def get_request_handler(
|
|
|
246
315
|
|
|
247
316
|
async def app(request: Request) -> Response:
|
|
248
317
|
response: Union[Response, None] = None
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
318
|
+
file_stack = request.scope.get("fastapi_middleware_astack")
|
|
319
|
+
assert isinstance(file_stack, AsyncExitStack), (
|
|
320
|
+
"fastapi_middleware_astack not found in request scope"
|
|
321
|
+
)
|
|
322
|
+
|
|
323
|
+
# Read body and auto-close files
|
|
324
|
+
try:
|
|
325
|
+
body: Any = None
|
|
326
|
+
if body_field:
|
|
327
|
+
if is_body_form:
|
|
328
|
+
body = await request.form()
|
|
329
|
+
file_stack.push_async_callback(body.close)
|
|
330
|
+
else:
|
|
331
|
+
body_bytes = await request.body()
|
|
332
|
+
if body_bytes:
|
|
333
|
+
json_body: Any = Undefined
|
|
334
|
+
content_type_value = request.headers.get("content-type")
|
|
335
|
+
if not content_type_value:
|
|
336
|
+
json_body = await request.json()
|
|
337
|
+
else:
|
|
338
|
+
message = email.message.Message()
|
|
339
|
+
message["content-type"] = content_type_value
|
|
340
|
+
if message.get_content_maintype() == "application":
|
|
341
|
+
subtype = message.get_content_subtype()
|
|
342
|
+
if subtype == "json" or subtype.endswith("+json"):
|
|
343
|
+
json_body = await request.json()
|
|
344
|
+
if json_body != Undefined:
|
|
345
|
+
body = json_body
|
|
346
|
+
else:
|
|
347
|
+
body = body_bytes
|
|
348
|
+
except json.JSONDecodeError as e:
|
|
349
|
+
validation_error = RequestValidationError(
|
|
350
|
+
[
|
|
351
|
+
{
|
|
352
|
+
"type": "json_invalid",
|
|
353
|
+
"loc": ("body", e.pos),
|
|
354
|
+
"msg": "JSON decode error",
|
|
355
|
+
"input": {},
|
|
356
|
+
"ctx": {"error": e.msg},
|
|
357
|
+
}
|
|
358
|
+
],
|
|
359
|
+
body=e.doc,
|
|
360
|
+
)
|
|
361
|
+
raise validation_error from e
|
|
362
|
+
except HTTPException:
|
|
363
|
+
# If a middleware raises an HTTPException, it should be raised again
|
|
364
|
+
raise
|
|
365
|
+
except Exception as e:
|
|
366
|
+
http_error = HTTPException(
|
|
367
|
+
status_code=400, detail="There was an error parsing the body"
|
|
368
|
+
)
|
|
369
|
+
raise http_error from e
|
|
370
|
+
|
|
371
|
+
# Solve dependencies and run path operation function, auto-closing dependencies
|
|
372
|
+
errors: List[Any] = []
|
|
373
|
+
async_exit_stack = request.scope.get("fastapi_inner_astack")
|
|
374
|
+
assert isinstance(async_exit_stack, AsyncExitStack), (
|
|
375
|
+
"fastapi_inner_astack not found in request scope"
|
|
376
|
+
)
|
|
377
|
+
solved_result = await solve_dependencies(
|
|
378
|
+
request=request,
|
|
379
|
+
dependant=dependant,
|
|
380
|
+
body=body,
|
|
381
|
+
dependency_overrides_provider=dependency_overrides_provider,
|
|
382
|
+
async_exit_stack=async_exit_stack,
|
|
383
|
+
embed_body_fields=embed_body_fields,
|
|
384
|
+
)
|
|
385
|
+
errors = solved_result.errors
|
|
386
|
+
if not errors:
|
|
387
|
+
raw_response = await run_endpoint_function(
|
|
388
|
+
dependant=dependant,
|
|
389
|
+
values=solved_result.values,
|
|
390
|
+
is_coroutine=is_coroutine,
|
|
391
|
+
)
|
|
392
|
+
if isinstance(raw_response, Response):
|
|
393
|
+
if raw_response.background is None:
|
|
394
|
+
raw_response.background = solved_result.background_tasks
|
|
395
|
+
response = raw_response
|
|
396
|
+
else:
|
|
397
|
+
response_args: Dict[str, Any] = {
|
|
398
|
+
"background": solved_result.background_tasks
|
|
399
|
+
}
|
|
400
|
+
# If status_code was set, use it, otherwise use the default from the
|
|
401
|
+
# response class, in the case of redirect it's 307
|
|
402
|
+
current_status_code = (
|
|
403
|
+
status_code if status_code else solved_result.response.status_code
|
|
305
404
|
)
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
}
|
|
321
|
-
# If status_code was set, use it, otherwise use the default from the
|
|
322
|
-
# response class, in the case of redirect it's 307
|
|
323
|
-
current_status_code = (
|
|
324
|
-
status_code
|
|
325
|
-
if status_code
|
|
326
|
-
else solved_result.response.status_code
|
|
327
|
-
)
|
|
328
|
-
if current_status_code is not None:
|
|
329
|
-
response_args["status_code"] = current_status_code
|
|
330
|
-
if solved_result.response.status_code:
|
|
331
|
-
response_args["status_code"] = (
|
|
332
|
-
solved_result.response.status_code
|
|
333
|
-
)
|
|
334
|
-
content = await serialize_response(
|
|
335
|
-
field=response_field,
|
|
336
|
-
response_content=raw_response,
|
|
337
|
-
include=response_model_include,
|
|
338
|
-
exclude=response_model_exclude,
|
|
339
|
-
by_alias=response_model_by_alias,
|
|
340
|
-
exclude_unset=response_model_exclude_unset,
|
|
341
|
-
exclude_defaults=response_model_exclude_defaults,
|
|
342
|
-
exclude_none=response_model_exclude_none,
|
|
343
|
-
is_coroutine=is_coroutine,
|
|
344
|
-
)
|
|
345
|
-
response = actual_response_class(content, **response_args)
|
|
346
|
-
if not is_body_allowed_for_status_code(response.status_code):
|
|
347
|
-
response.body = b""
|
|
348
|
-
response.headers.raw.extend(solved_result.response.headers.raw)
|
|
349
|
-
if errors:
|
|
350
|
-
validation_error = RequestValidationError(
|
|
351
|
-
_normalize_errors(errors), body=body
|
|
405
|
+
if current_status_code is not None:
|
|
406
|
+
response_args["status_code"] = current_status_code
|
|
407
|
+
if solved_result.response.status_code:
|
|
408
|
+
response_args["status_code"] = solved_result.response.status_code
|
|
409
|
+
content = await serialize_response(
|
|
410
|
+
field=response_field,
|
|
411
|
+
response_content=raw_response,
|
|
412
|
+
include=response_model_include,
|
|
413
|
+
exclude=response_model_exclude,
|
|
414
|
+
by_alias=response_model_by_alias,
|
|
415
|
+
exclude_unset=response_model_exclude_unset,
|
|
416
|
+
exclude_defaults=response_model_exclude_defaults,
|
|
417
|
+
exclude_none=response_model_exclude_none,
|
|
418
|
+
is_coroutine=is_coroutine,
|
|
352
419
|
)
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
"docs: https://fastapi.tiangolo.com/tutorial/dependencies/dependencies-with-yield/#dependencies-with-yield-and-except"
|
|
420
|
+
response = actual_response_class(content, **response_args)
|
|
421
|
+
if not is_body_allowed_for_status_code(response.status_code):
|
|
422
|
+
response.body = b""
|
|
423
|
+
response.headers.raw.extend(solved_result.response.headers.raw)
|
|
424
|
+
if errors:
|
|
425
|
+
validation_error = RequestValidationError(
|
|
426
|
+
_normalize_errors(errors), body=body
|
|
361
427
|
)
|
|
428
|
+
raise validation_error
|
|
429
|
+
|
|
430
|
+
# Return response
|
|
431
|
+
assert response
|
|
362
432
|
return response
|
|
363
433
|
|
|
364
434
|
return app
|
|
@@ -370,24 +440,23 @@ def get_websocket_app(
|
|
|
370
440
|
embed_body_fields: bool = False,
|
|
371
441
|
) -> Callable[[WebSocket], Coroutine[Any, Any, Any]]:
|
|
372
442
|
async def app(websocket: WebSocket) -> None:
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
443
|
+
async_exit_stack = websocket.scope.get("fastapi_inner_astack")
|
|
444
|
+
assert isinstance(async_exit_stack, AsyncExitStack), (
|
|
445
|
+
"fastapi_inner_astack not found in request scope"
|
|
446
|
+
)
|
|
447
|
+
solved_result = await solve_dependencies(
|
|
448
|
+
request=websocket,
|
|
449
|
+
dependant=dependant,
|
|
450
|
+
dependency_overrides_provider=dependency_overrides_provider,
|
|
451
|
+
async_exit_stack=async_exit_stack,
|
|
452
|
+
embed_body_fields=embed_body_fields,
|
|
453
|
+
)
|
|
454
|
+
if solved_result.errors:
|
|
455
|
+
raise WebSocketRequestValidationError(
|
|
456
|
+
_normalize_errors(solved_result.errors)
|
|
384
457
|
)
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
_normalize_errors(solved_result.errors)
|
|
388
|
-
)
|
|
389
|
-
assert dependant.call is not None, "dependant.call must be a function"
|
|
390
|
-
await dependant.call(**solved_result.values)
|
|
458
|
+
assert dependant.call is not None, "dependant.call must be a function"
|
|
459
|
+
await dependant.call(**solved_result.values)
|
|
391
460
|
|
|
392
461
|
return app
|
|
393
462
|
|
fastapi/security/oauth2.py
CHANGED
|
@@ -89,7 +89,7 @@ class OAuth2PasswordRequestForm:
|
|
|
89
89
|
Doc(
|
|
90
90
|
"""
|
|
91
91
|
`password` string. The OAuth2 spec requires the exact field name
|
|
92
|
-
`password
|
|
92
|
+
`password`.
|
|
93
93
|
"""
|
|
94
94
|
),
|
|
95
95
|
],
|
|
@@ -243,7 +243,7 @@ class OAuth2PasswordRequestFormStrict(OAuth2PasswordRequestForm):
|
|
|
243
243
|
Doc(
|
|
244
244
|
"""
|
|
245
245
|
`password` string. The OAuth2 spec requires the exact field name
|
|
246
|
-
`password
|
|
246
|
+
`password`.
|
|
247
247
|
"""
|
|
248
248
|
),
|
|
249
249
|
],
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: fastapi
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.118.1
|
|
4
4
|
Summary: FastAPI framework, high performance, easy to learn, fast to code, ready for production
|
|
5
5
|
Author-Email: =?utf-8?q?Sebasti=C3=A1n_Ram=C3=ADrez?= <tiangolo@gmail.com>
|
|
6
6
|
Classifier: Intended Audience :: Information Technology
|
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
fastapi-0.
|
|
2
|
-
fastapi-0.
|
|
3
|
-
fastapi-0.
|
|
4
|
-
fastapi-0.
|
|
5
|
-
fastapi/__init__.py,sha256=
|
|
1
|
+
fastapi-0.118.1.dist-info/METADATA,sha256=1lJR4RMjB0KOujx5btnmcdNbsJTv37qlicYZUphrEQY,28135
|
|
2
|
+
fastapi-0.118.1.dist-info/WHEEL,sha256=9P2ygRxDrTJz3gsagc0Z96ukrxjr-LFBGOgv3AuKlCA,90
|
|
3
|
+
fastapi-0.118.1.dist-info/entry_points.txt,sha256=GCf-WbIZxyGT4MUmrPGj1cOHYZoGsNPHAvNkT6hnGeA,61
|
|
4
|
+
fastapi-0.118.1.dist-info/licenses/LICENSE,sha256=Tsif_IFIW5f-xYSy1KlhAy7v_oNEU4lP2cEnSQbMdE4,1086
|
|
5
|
+
fastapi/__init__.py,sha256=b1Y5tsac-lGeykg5dBEqr560dTxtO-XapvIgZVeg3_o,1081
|
|
6
6
|
fastapi/__main__.py,sha256=bKePXLdO4SsVSM6r9SVoLickJDcR2c0cTOxZRKq26YQ,37
|
|
7
|
-
fastapi/_compat.py,sha256=
|
|
8
|
-
fastapi/applications.py,sha256=
|
|
7
|
+
fastapi/_compat.py,sha256=n1chC6BiNuBbXVnvOskZSo_8_VeZ2ESoyaG9gt48W_g,24944
|
|
8
|
+
fastapi/applications.py,sha256=nLbGcVdmCxXsl4aTSuP0WVS_XGY7wXBL3vC7nqlplmA,180276
|
|
9
9
|
fastapi/background.py,sha256=rouLirxUANrcYC824MSMypXL_Qb2HYg2YZqaiEqbEKI,1768
|
|
10
10
|
fastapi/cli.py,sha256=OYhZb0NR_deuT5ofyPF2NoNBzZDNOP8Salef2nk-HqA,418
|
|
11
11
|
fastapi/concurrency.py,sha256=MirfowoSpkMQZ8j_g0ZxaQKpV6eB3G-dB5TgcXCrgEA,1424
|
|
@@ -18,6 +18,7 @@ fastapi/exception_handlers.py,sha256=YVcT8Zy021VYYeecgdyh5YEUjEIHKcLspbkSf4OfbJI
|
|
|
18
18
|
fastapi/exceptions.py,sha256=taNixuFEXb67lI1bnX1ubq8y8TseJ4yoPlWjyP0fTzk,4969
|
|
19
19
|
fastapi/logger.py,sha256=I9NNi3ov8AcqbsbC9wl1X-hdItKgYt2XTrx1f99Zpl4,54
|
|
20
20
|
fastapi/middleware/__init__.py,sha256=oQDxiFVcc1fYJUOIFvphnK7pTT5kktmfL32QXpBFvvo,58
|
|
21
|
+
fastapi/middleware/asyncexitstack.py,sha256=RKGlQpGzg3GLosqVhrxBy_NCZ9qJS7zQeNHt5Y3x-00,637
|
|
21
22
|
fastapi/middleware/cors.py,sha256=ynwjWQZoc_vbhzZ3_ZXceoaSrslHFHPdoM52rXr0WUU,79
|
|
22
23
|
fastapi/middleware/gzip.py,sha256=xM5PcsH8QlAimZw4VDvcmTnqQamslThsfe3CVN2voa0,79
|
|
23
24
|
fastapi/middleware/httpsredirect.py,sha256=rL8eXMnmLijwVkH7_400zHri1AekfeBd6D6qs8ix950,115
|
|
@@ -29,16 +30,16 @@ fastapi/openapi/docs.py,sha256=zSDv4xY6XHcKsaG4zyk1HqSnrZtfZFBB0J7ZBk5YHPE,10345
|
|
|
29
30
|
fastapi/openapi/models.py,sha256=m1BNHxf_RiDTK1uCfMre6XZN5y7krZNA62QEP_2EV9s,15625
|
|
30
31
|
fastapi/openapi/utils.py,sha256=ZI-nwdT2PtX8kaRPJylZo4LJHjYAcoVGxkd181P75x4,23997
|
|
31
32
|
fastapi/param_functions.py,sha256=JHNPLIYvoAwdnZZavIVsxOat8x23fX_Kl33reh7HKl8,64019
|
|
32
|
-
fastapi/params.py,sha256=
|
|
33
|
+
fastapi/params.py,sha256=SnkGa4nNdmRek6oOELBHcSieRGjYvDPTla3EOl5Zlis,28413
|
|
33
34
|
fastapi/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
34
35
|
fastapi/requests.py,sha256=zayepKFcienBllv3snmWI20Gk0oHNVLU4DDhqXBb4LU,142
|
|
35
36
|
fastapi/responses.py,sha256=QNQQlwpKhQoIPZTTWkpc9d_QGeGZ_aVQPaDV3nQ8m7c,1761
|
|
36
|
-
fastapi/routing.py,sha256=
|
|
37
|
+
fastapi/routing.py,sha256=tK_d2K_0kP-rcbAmjjlR0mZMR170QkBITvwHWPq7eiQ,178409
|
|
37
38
|
fastapi/security/__init__.py,sha256=bO8pNmxqVRXUjfl2mOKiVZLn0FpBQ61VUYVjmppnbJw,881
|
|
38
39
|
fastapi/security/api_key.py,sha256=di-0gQ8MKugi2YfmlMoDHk-QMF_vnLGJRFOA6tcZ7fA,9016
|
|
39
40
|
fastapi/security/base.py,sha256=dl4pvbC-RxjfbWgPtCWd8MVU-7CB2SZ22rJDXVCXO6c,141
|
|
40
41
|
fastapi/security/http.py,sha256=rWR2x-5CUsjWmRucYthwRig6MG1o-boyrr4Xo-PuuxU,13606
|
|
41
|
-
fastapi/security/oauth2.py,sha256=
|
|
42
|
+
fastapi/security/oauth2.py,sha256=rKHIUHq29367Qpe0Ez5Gcu1yIIM6SMw7nEfh15gBNIQ,22036
|
|
42
43
|
fastapi/security/open_id_connect_url.py,sha256=8vizZ2tGqEp1ur8SwtVgyHJhGAJ5AqahgcvSpaIioDI,2722
|
|
43
44
|
fastapi/security/utils.py,sha256=bd8T0YM7UQD5ATKucr1bNtAvz_Y3__dVNAv5UebiPvc,293
|
|
44
45
|
fastapi/staticfiles.py,sha256=iirGIt3sdY2QZXd36ijs3Cj-T0FuGFda3cd90kM9Ikw,69
|
|
@@ -47,4 +48,4 @@ fastapi/testclient.py,sha256=nBvaAmX66YldReJNZXPOk1sfuo2Q6hs8bOvIaCep6LQ,66
|
|
|
47
48
|
fastapi/types.py,sha256=nFb36sK3DSoqoyo7Miwy3meKK5UdFBgkAgLSzQlUVyI,383
|
|
48
49
|
fastapi/utils.py,sha256=S59stPvKPUJ7MSkke3FaegSyig_4Uwhd32jnLiMF1jE,8032
|
|
49
50
|
fastapi/websockets.py,sha256=419uncYObEKZG0YcrXscfQQYLSWoE10jqxVMetGdR98,222
|
|
50
|
-
fastapi-0.
|
|
51
|
+
fastapi-0.118.1.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|