fastapi 0.128.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.
Files changed (53) hide show
  1. fastapi/__init__.py +25 -0
  2. fastapi/__main__.py +3 -0
  3. fastapi/_compat/__init__.py +41 -0
  4. fastapi/_compat/shared.py +206 -0
  5. fastapi/_compat/v2.py +568 -0
  6. fastapi/applications.py +4669 -0
  7. fastapi/background.py +60 -0
  8. fastapi/cli.py +13 -0
  9. fastapi/concurrency.py +41 -0
  10. fastapi/datastructures.py +183 -0
  11. fastapi/dependencies/__init__.py +0 -0
  12. fastapi/dependencies/models.py +193 -0
  13. fastapi/dependencies/utils.py +1021 -0
  14. fastapi/encoders.py +346 -0
  15. fastapi/exception_handlers.py +34 -0
  16. fastapi/exceptions.py +246 -0
  17. fastapi/logger.py +3 -0
  18. fastapi/middleware/__init__.py +1 -0
  19. fastapi/middleware/asyncexitstack.py +18 -0
  20. fastapi/middleware/cors.py +1 -0
  21. fastapi/middleware/gzip.py +1 -0
  22. fastapi/middleware/httpsredirect.py +3 -0
  23. fastapi/middleware/trustedhost.py +3 -0
  24. fastapi/middleware/wsgi.py +1 -0
  25. fastapi/openapi/__init__.py +0 -0
  26. fastapi/openapi/constants.py +3 -0
  27. fastapi/openapi/docs.py +344 -0
  28. fastapi/openapi/models.py +438 -0
  29. fastapi/openapi/utils.py +567 -0
  30. fastapi/param_functions.py +2369 -0
  31. fastapi/params.py +755 -0
  32. fastapi/py.typed +0 -0
  33. fastapi/requests.py +2 -0
  34. fastapi/responses.py +48 -0
  35. fastapi/routing.py +4508 -0
  36. fastapi/security/__init__.py +15 -0
  37. fastapi/security/api_key.py +318 -0
  38. fastapi/security/base.py +6 -0
  39. fastapi/security/http.py +423 -0
  40. fastapi/security/oauth2.py +663 -0
  41. fastapi/security/open_id_connect_url.py +94 -0
  42. fastapi/security/utils.py +10 -0
  43. fastapi/staticfiles.py +1 -0
  44. fastapi/templating.py +1 -0
  45. fastapi/testclient.py +1 -0
  46. fastapi/types.py +11 -0
  47. fastapi/utils.py +164 -0
  48. fastapi/websockets.py +3 -0
  49. fastapi-0.128.0.dist-info/METADATA +645 -0
  50. fastapi-0.128.0.dist-info/RECORD +53 -0
  51. fastapi-0.128.0.dist-info/WHEEL +4 -0
  52. fastapi-0.128.0.dist-info/entry_points.txt +5 -0
  53. fastapi-0.128.0.dist-info/licenses/LICENSE +21 -0
fastapi/background.py ADDED
@@ -0,0 +1,60 @@
1
+ from typing import Annotated, Any, Callable
2
+
3
+ from annotated_doc import Doc
4
+ from starlette.background import BackgroundTasks as StarletteBackgroundTasks
5
+ from typing_extensions import ParamSpec
6
+
7
+ P = ParamSpec("P")
8
+
9
+
10
+ class BackgroundTasks(StarletteBackgroundTasks):
11
+ """
12
+ A collection of background tasks that will be called after a response has been
13
+ sent to the client.
14
+
15
+ Read more about it in the
16
+ [FastAPI docs for Background Tasks](https://fastapi.tiangolo.com/tutorial/background-tasks/).
17
+
18
+ ## Example
19
+
20
+ ```python
21
+ from fastapi import BackgroundTasks, FastAPI
22
+
23
+ app = FastAPI()
24
+
25
+
26
+ def write_notification(email: str, message=""):
27
+ with open("log.txt", mode="w") as email_file:
28
+ content = f"notification for {email}: {message}"
29
+ email_file.write(content)
30
+
31
+
32
+ @app.post("/send-notification/{email}")
33
+ async def send_notification(email: str, background_tasks: BackgroundTasks):
34
+ background_tasks.add_task(write_notification, email, message="some notification")
35
+ return {"message": "Notification sent in the background"}
36
+ ```
37
+ """
38
+
39
+ def add_task(
40
+ self,
41
+ func: Annotated[
42
+ Callable[P, Any],
43
+ Doc(
44
+ """
45
+ The function to call after the response is sent.
46
+
47
+ It can be a regular `def` function or an `async def` function.
48
+ """
49
+ ),
50
+ ],
51
+ *args: P.args,
52
+ **kwargs: P.kwargs,
53
+ ) -> None:
54
+ """
55
+ Add a function to be called in the background after the response is sent.
56
+
57
+ Read more about it in the
58
+ [FastAPI docs for Background Tasks](https://fastapi.tiangolo.com/tutorial/background-tasks/).
59
+ """
60
+ return super().add_task(func, *args, **kwargs)
fastapi/cli.py ADDED
@@ -0,0 +1,13 @@
1
+ try:
2
+ from fastapi_cli.cli import main as cli_main
3
+
4
+ except ImportError: # pragma: no cover
5
+ cli_main = None # type: ignore
6
+
7
+
8
+ def main() -> None:
9
+ if not cli_main: # type: ignore[truthy-function]
10
+ message = 'To use the fastapi command, please install "fastapi[standard]":\n\n\tpip install "fastapi[standard]"\n'
11
+ print(message)
12
+ raise RuntimeError(message) # noqa: B904
13
+ cli_main()
fastapi/concurrency.py ADDED
@@ -0,0 +1,41 @@
1
+ from collections.abc import AsyncGenerator
2
+ from contextlib import AbstractContextManager
3
+ from contextlib import asynccontextmanager as asynccontextmanager
4
+ from typing import TypeVar
5
+
6
+ import anyio.to_thread
7
+ from anyio import CapacityLimiter
8
+ from starlette.concurrency import iterate_in_threadpool as iterate_in_threadpool # noqa
9
+ from starlette.concurrency import run_in_threadpool as run_in_threadpool # noqa
10
+ from starlette.concurrency import ( # noqa
11
+ run_until_first_complete as run_until_first_complete,
12
+ )
13
+
14
+ _T = TypeVar("_T")
15
+
16
+
17
+ @asynccontextmanager
18
+ async def contextmanager_in_threadpool(
19
+ cm: AbstractContextManager[_T],
20
+ ) -> AsyncGenerator[_T, None]:
21
+ # blocking __exit__ from running waiting on a free thread
22
+ # can create race conditions/deadlocks if the context manager itself
23
+ # has its own internal pool (e.g. a database connection pool)
24
+ # to avoid this we let __exit__ run without a capacity limit
25
+ # since we're creating a new limiter for each call, any non-zero limit
26
+ # works (1 is arbitrary)
27
+ exit_limiter = CapacityLimiter(1)
28
+ try:
29
+ yield await run_in_threadpool(cm.__enter__)
30
+ except Exception as e:
31
+ ok = bool(
32
+ await anyio.to_thread.run_sync(
33
+ cm.__exit__, type(e), e, e.__traceback__, limiter=exit_limiter
34
+ )
35
+ )
36
+ if not ok:
37
+ raise e
38
+ else:
39
+ await anyio.to_thread.run_sync(
40
+ cm.__exit__, None, None, None, limiter=exit_limiter
41
+ )
@@ -0,0 +1,183 @@
1
+ from collections.abc import Mapping
2
+ from typing import (
3
+ Annotated,
4
+ Any,
5
+ BinaryIO,
6
+ Callable,
7
+ Optional,
8
+ TypeVar,
9
+ cast,
10
+ )
11
+
12
+ from annotated_doc import Doc
13
+ from pydantic import GetJsonSchemaHandler
14
+ from starlette.datastructures import URL as URL # noqa: F401
15
+ from starlette.datastructures import Address as Address # noqa: F401
16
+ from starlette.datastructures import FormData as FormData # noqa: F401
17
+ from starlette.datastructures import Headers as Headers # noqa: F401
18
+ from starlette.datastructures import QueryParams as QueryParams # noqa: F401
19
+ from starlette.datastructures import State as State # noqa: F401
20
+ from starlette.datastructures import UploadFile as StarletteUploadFile
21
+
22
+
23
+ class UploadFile(StarletteUploadFile):
24
+ """
25
+ A file uploaded in a request.
26
+
27
+ Define it as a *path operation function* (or dependency) parameter.
28
+
29
+ If you are using a regular `def` function, you can use the `upload_file.file`
30
+ attribute to access the raw standard Python file (blocking, not async), useful and
31
+ needed for non-async code.
32
+
33
+ Read more about it in the
34
+ [FastAPI docs for Request Files](https://fastapi.tiangolo.com/tutorial/request-files/).
35
+
36
+ ## Example
37
+
38
+ ```python
39
+ from typing import Annotated
40
+
41
+ from fastapi import FastAPI, File, UploadFile
42
+
43
+ app = FastAPI()
44
+
45
+
46
+ @app.post("/files/")
47
+ async def create_file(file: Annotated[bytes, File()]):
48
+ return {"file_size": len(file)}
49
+
50
+
51
+ @app.post("/uploadfile/")
52
+ async def create_upload_file(file: UploadFile):
53
+ return {"filename": file.filename}
54
+ ```
55
+ """
56
+
57
+ file: Annotated[
58
+ BinaryIO,
59
+ Doc("The standard Python file object (non-async)."),
60
+ ]
61
+ filename: Annotated[Optional[str], Doc("The original file name.")]
62
+ size: Annotated[Optional[int], Doc("The size of the file in bytes.")]
63
+ headers: Annotated[Headers, Doc("The headers of the request.")]
64
+ content_type: Annotated[
65
+ Optional[str], Doc("The content type of the request, from the headers.")
66
+ ]
67
+
68
+ async def write(
69
+ self,
70
+ data: Annotated[
71
+ bytes,
72
+ Doc(
73
+ """
74
+ The bytes to write to the file.
75
+ """
76
+ ),
77
+ ],
78
+ ) -> None:
79
+ """
80
+ Write some bytes to the file.
81
+
82
+ You normally wouldn't use this from a file you read in a request.
83
+
84
+ To be awaitable, compatible with async, this is run in threadpool.
85
+ """
86
+ return await super().write(data)
87
+
88
+ async def read(
89
+ self,
90
+ size: Annotated[
91
+ int,
92
+ Doc(
93
+ """
94
+ The number of bytes to read from the file.
95
+ """
96
+ ),
97
+ ] = -1,
98
+ ) -> bytes:
99
+ """
100
+ Read some bytes from the file.
101
+
102
+ To be awaitable, compatible with async, this is run in threadpool.
103
+ """
104
+ return await super().read(size)
105
+
106
+ async def seek(
107
+ self,
108
+ offset: Annotated[
109
+ int,
110
+ Doc(
111
+ """
112
+ The position in bytes to seek to in the file.
113
+ """
114
+ ),
115
+ ],
116
+ ) -> None:
117
+ """
118
+ Move to a position in the file.
119
+
120
+ Any next read or write will be done from that position.
121
+
122
+ To be awaitable, compatible with async, this is run in threadpool.
123
+ """
124
+ return await super().seek(offset)
125
+
126
+ async def close(self) -> None:
127
+ """
128
+ Close the file.
129
+
130
+ To be awaitable, compatible with async, this is run in threadpool.
131
+ """
132
+ return await super().close()
133
+
134
+ @classmethod
135
+ def _validate(cls, __input_value: Any, _: Any) -> "UploadFile":
136
+ if not isinstance(__input_value, StarletteUploadFile):
137
+ raise ValueError(f"Expected UploadFile, received: {type(__input_value)}")
138
+ return cast(UploadFile, __input_value)
139
+
140
+ @classmethod
141
+ def __get_pydantic_json_schema__(
142
+ cls, core_schema: Mapping[str, Any], handler: GetJsonSchemaHandler
143
+ ) -> dict[str, Any]:
144
+ return {"type": "string", "format": "binary"}
145
+
146
+ @classmethod
147
+ def __get_pydantic_core_schema__(
148
+ cls, source: type[Any], handler: Callable[[Any], Mapping[str, Any]]
149
+ ) -> Mapping[str, Any]:
150
+ from ._compat.v2 import with_info_plain_validator_function
151
+
152
+ return with_info_plain_validator_function(cls._validate)
153
+
154
+
155
+ class DefaultPlaceholder:
156
+ """
157
+ You shouldn't use this class directly.
158
+
159
+ It's used internally to recognize when a default value has been overwritten, even
160
+ if the overridden default value was truthy.
161
+ """
162
+
163
+ def __init__(self, value: Any):
164
+ self.value = value
165
+
166
+ def __bool__(self) -> bool:
167
+ return bool(self.value)
168
+
169
+ def __eq__(self, o: object) -> bool:
170
+ return isinstance(o, DefaultPlaceholder) and o.value == self.value
171
+
172
+
173
+ DefaultType = TypeVar("DefaultType")
174
+
175
+
176
+ def Default(value: DefaultType) -> DefaultType:
177
+ """
178
+ You shouldn't use this function directly.
179
+
180
+ It's used internally to recognize when a default value has been overwritten, even
181
+ if the overridden default value was truthy.
182
+ """
183
+ return DefaultPlaceholder(value) # type: ignore
File without changes
@@ -0,0 +1,193 @@
1
+ import inspect
2
+ import sys
3
+ from dataclasses import dataclass, field
4
+ from functools import cached_property, partial
5
+ from typing import Any, Callable, Optional, Union
6
+
7
+ from fastapi._compat import ModelField
8
+ from fastapi.security.base import SecurityBase
9
+ from fastapi.types import DependencyCacheKey
10
+ from typing_extensions import Literal
11
+
12
+ if sys.version_info >= (3, 13): # pragma: no cover
13
+ from inspect import iscoroutinefunction
14
+ else: # pragma: no cover
15
+ from asyncio import iscoroutinefunction
16
+
17
+
18
+ def _unwrapped_call(call: Optional[Callable[..., Any]]) -> Any:
19
+ if call is None:
20
+ return call # pragma: no cover
21
+ unwrapped = inspect.unwrap(_impartial(call))
22
+ return unwrapped
23
+
24
+
25
+ def _impartial(func: Callable[..., Any]) -> Callable[..., Any]:
26
+ while isinstance(func, partial):
27
+ func = func.func
28
+ return func
29
+
30
+
31
+ @dataclass
32
+ class Dependant:
33
+ path_params: list[ModelField] = field(default_factory=list)
34
+ query_params: list[ModelField] = field(default_factory=list)
35
+ header_params: list[ModelField] = field(default_factory=list)
36
+ cookie_params: list[ModelField] = field(default_factory=list)
37
+ body_params: list[ModelField] = field(default_factory=list)
38
+ dependencies: list["Dependant"] = field(default_factory=list)
39
+ name: Optional[str] = None
40
+ call: Optional[Callable[..., Any]] = None
41
+ request_param_name: Optional[str] = None
42
+ websocket_param_name: Optional[str] = None
43
+ http_connection_param_name: Optional[str] = None
44
+ response_param_name: Optional[str] = None
45
+ background_tasks_param_name: Optional[str] = None
46
+ security_scopes_param_name: Optional[str] = None
47
+ own_oauth_scopes: Optional[list[str]] = None
48
+ parent_oauth_scopes: Optional[list[str]] = None
49
+ use_cache: bool = True
50
+ path: Optional[str] = None
51
+ scope: Union[Literal["function", "request"], None] = None
52
+
53
+ @cached_property
54
+ def oauth_scopes(self) -> list[str]:
55
+ scopes = self.parent_oauth_scopes.copy() if self.parent_oauth_scopes else []
56
+ # This doesn't use a set to preserve order, just in case
57
+ for scope in self.own_oauth_scopes or []:
58
+ if scope not in scopes:
59
+ scopes.append(scope)
60
+ return scopes
61
+
62
+ @cached_property
63
+ def cache_key(self) -> DependencyCacheKey:
64
+ scopes_for_cache = (
65
+ tuple(sorted(set(self.oauth_scopes or []))) if self._uses_scopes else ()
66
+ )
67
+ return (
68
+ self.call,
69
+ scopes_for_cache,
70
+ self.computed_scope or "",
71
+ )
72
+
73
+ @cached_property
74
+ def _uses_scopes(self) -> bool:
75
+ if self.own_oauth_scopes:
76
+ return True
77
+ if self.security_scopes_param_name is not None:
78
+ return True
79
+ if self._is_security_scheme:
80
+ return True
81
+ for sub_dep in self.dependencies:
82
+ if sub_dep._uses_scopes:
83
+ return True
84
+ return False
85
+
86
+ @cached_property
87
+ def _is_security_scheme(self) -> bool:
88
+ if self.call is None:
89
+ return False # pragma: no cover
90
+ unwrapped = _unwrapped_call(self.call)
91
+ return isinstance(unwrapped, SecurityBase)
92
+
93
+ # Mainly to get the type of SecurityBase, but it's the same self.call
94
+ @cached_property
95
+ def _security_scheme(self) -> SecurityBase:
96
+ unwrapped = _unwrapped_call(self.call)
97
+ assert isinstance(unwrapped, SecurityBase)
98
+ return unwrapped
99
+
100
+ @cached_property
101
+ def _security_dependencies(self) -> list["Dependant"]:
102
+ security_deps = [dep for dep in self.dependencies if dep._is_security_scheme]
103
+ return security_deps
104
+
105
+ @cached_property
106
+ def is_gen_callable(self) -> bool:
107
+ if self.call is None:
108
+ return False # pragma: no cover
109
+ if inspect.isgeneratorfunction(
110
+ _impartial(self.call)
111
+ ) or inspect.isgeneratorfunction(_unwrapped_call(self.call)):
112
+ return True
113
+ if inspect.isclass(_unwrapped_call(self.call)):
114
+ return False
115
+ dunder_call = getattr(_impartial(self.call), "__call__", None) # noqa: B004
116
+ if dunder_call is None:
117
+ return False # pragma: no cover
118
+ if inspect.isgeneratorfunction(
119
+ _impartial(dunder_call)
120
+ ) or inspect.isgeneratorfunction(_unwrapped_call(dunder_call)):
121
+ return True
122
+ dunder_unwrapped_call = getattr(_unwrapped_call(self.call), "__call__", None) # noqa: B004
123
+ if dunder_unwrapped_call is None:
124
+ return False # pragma: no cover
125
+ if inspect.isgeneratorfunction(
126
+ _impartial(dunder_unwrapped_call)
127
+ ) or inspect.isgeneratorfunction(_unwrapped_call(dunder_unwrapped_call)):
128
+ return True
129
+ return False
130
+
131
+ @cached_property
132
+ def is_async_gen_callable(self) -> bool:
133
+ if self.call is None:
134
+ return False # pragma: no cover
135
+ if inspect.isasyncgenfunction(
136
+ _impartial(self.call)
137
+ ) or inspect.isasyncgenfunction(_unwrapped_call(self.call)):
138
+ return True
139
+ if inspect.isclass(_unwrapped_call(self.call)):
140
+ return False
141
+ dunder_call = getattr(_impartial(self.call), "__call__", None) # noqa: B004
142
+ if dunder_call is None:
143
+ return False # pragma: no cover
144
+ if inspect.isasyncgenfunction(
145
+ _impartial(dunder_call)
146
+ ) or inspect.isasyncgenfunction(_unwrapped_call(dunder_call)):
147
+ return True
148
+ dunder_unwrapped_call = getattr(_unwrapped_call(self.call), "__call__", None) # noqa: B004
149
+ if dunder_unwrapped_call is None:
150
+ return False # pragma: no cover
151
+ if inspect.isasyncgenfunction(
152
+ _impartial(dunder_unwrapped_call)
153
+ ) or inspect.isasyncgenfunction(_unwrapped_call(dunder_unwrapped_call)):
154
+ return True
155
+ return False
156
+
157
+ @cached_property
158
+ def is_coroutine_callable(self) -> bool:
159
+ if self.call is None:
160
+ return False # pragma: no cover
161
+ if inspect.isroutine(_impartial(self.call)) and iscoroutinefunction(
162
+ _impartial(self.call)
163
+ ):
164
+ return True
165
+ if inspect.isroutine(_unwrapped_call(self.call)) and iscoroutinefunction(
166
+ _unwrapped_call(self.call)
167
+ ):
168
+ return True
169
+ if inspect.isclass(_unwrapped_call(self.call)):
170
+ return False
171
+ dunder_call = getattr(_impartial(self.call), "__call__", None) # noqa: B004
172
+ if dunder_call is None:
173
+ return False # pragma: no cover
174
+ if iscoroutinefunction(_impartial(dunder_call)) or iscoroutinefunction(
175
+ _unwrapped_call(dunder_call)
176
+ ):
177
+ return True
178
+ dunder_unwrapped_call = getattr(_unwrapped_call(self.call), "__call__", None) # noqa: B004
179
+ if dunder_unwrapped_call is None:
180
+ return False # pragma: no cover
181
+ if iscoroutinefunction(
182
+ _impartial(dunder_unwrapped_call)
183
+ ) or iscoroutinefunction(_unwrapped_call(dunder_unwrapped_call)):
184
+ return True
185
+ return False
186
+
187
+ @cached_property
188
+ def computed_scope(self) -> Union[str, None]:
189
+ if self.scope:
190
+ return self.scope
191
+ if self.is_gen_callable or self.is_async_gen_callable:
192
+ return "request"
193
+ return None