koichi12 commited on
Commit
56392d0
·
verified ·
1 Parent(s): f2e88b6

Add files using upload-large-folder tool

Browse files
This view is limited to 50 files because it contains too many changes.   See raw diff
Files changed (50) hide show
  1. .venv/lib/python3.11/site-packages/fastapi/__pycache__/__init__.cpython-311.pyc +0 -0
  2. .venv/lib/python3.11/site-packages/fastapi/__pycache__/_compat.cpython-311.pyc +0 -0
  3. .venv/lib/python3.11/site-packages/fastapi/__pycache__/cli.cpython-311.pyc +0 -0
  4. .venv/lib/python3.11/site-packages/fastapi/__pycache__/concurrency.cpython-311.pyc +0 -0
  5. .venv/lib/python3.11/site-packages/fastapi/__pycache__/encoders.cpython-311.pyc +0 -0
  6. .venv/lib/python3.11/site-packages/fastapi/__pycache__/exception_handlers.cpython-311.pyc +0 -0
  7. .venv/lib/python3.11/site-packages/fastapi/__pycache__/exceptions.cpython-311.pyc +0 -0
  8. .venv/lib/python3.11/site-packages/fastapi/__pycache__/params.cpython-311.pyc +0 -0
  9. .venv/lib/python3.11/site-packages/fastapi/__pycache__/responses.cpython-311.pyc +0 -0
  10. .venv/lib/python3.11/site-packages/fastapi/__pycache__/routing.cpython-311.pyc +0 -0
  11. .venv/lib/python3.11/site-packages/fastapi/__pycache__/staticfiles.cpython-311.pyc +0 -0
  12. .venv/lib/python3.11/site-packages/fastapi/__pycache__/types.cpython-311.pyc +0 -0
  13. .venv/lib/python3.11/site-packages/fastapi/__pycache__/utils.cpython-311.pyc +0 -0
  14. .venv/lib/python3.11/site-packages/fastapi/dependencies/__init__.py +0 -0
  15. .venv/lib/python3.11/site-packages/fastapi/dependencies/__pycache__/__init__.cpython-311.pyc +0 -0
  16. .venv/lib/python3.11/site-packages/fastapi/dependencies/__pycache__/models.cpython-311.pyc +0 -0
  17. .venv/lib/python3.11/site-packages/fastapi/dependencies/__pycache__/utils.cpython-311.pyc +0 -0
  18. .venv/lib/python3.11/site-packages/fastapi/dependencies/models.py +37 -0
  19. .venv/lib/python3.11/site-packages/fastapi/dependencies/utils.py +972 -0
  20. .venv/lib/python3.11/site-packages/fastapi/openapi/__init__.py +0 -0
  21. .venv/lib/python3.11/site-packages/fastapi/openapi/__pycache__/__init__.cpython-311.pyc +0 -0
  22. .venv/lib/python3.11/site-packages/fastapi/openapi/__pycache__/constants.cpython-311.pyc +0 -0
  23. .venv/lib/python3.11/site-packages/fastapi/openapi/__pycache__/docs.cpython-311.pyc +0 -0
  24. .venv/lib/python3.11/site-packages/fastapi/openapi/__pycache__/models.cpython-311.pyc +0 -0
  25. .venv/lib/python3.11/site-packages/fastapi/openapi/__pycache__/utils.cpython-311.pyc +0 -0
  26. .venv/lib/python3.11/site-packages/fastapi/openapi/constants.py +3 -0
  27. .venv/lib/python3.11/site-packages/fastapi/openapi/docs.py +344 -0
  28. .venv/lib/python3.11/site-packages/fastapi/openapi/models.py +445 -0
  29. .venv/lib/python3.11/site-packages/fastapi/openapi/utils.py +548 -0
  30. .venv/lib/python3.11/site-packages/fastapi/security/__init__.py +15 -0
  31. .venv/lib/python3.11/site-packages/fastapi/security/__pycache__/__init__.cpython-311.pyc +0 -0
  32. .venv/lib/python3.11/site-packages/fastapi/security/__pycache__/api_key.cpython-311.pyc +0 -0
  33. .venv/lib/python3.11/site-packages/fastapi/security/__pycache__/base.cpython-311.pyc +0 -0
  34. .venv/lib/python3.11/site-packages/fastapi/security/__pycache__/http.cpython-311.pyc +0 -0
  35. .venv/lib/python3.11/site-packages/fastapi/security/__pycache__/oauth2.cpython-311.pyc +0 -0
  36. .venv/lib/python3.11/site-packages/fastapi/security/__pycache__/open_id_connect_url.cpython-311.pyc +0 -0
  37. .venv/lib/python3.11/site-packages/fastapi/security/__pycache__/utils.cpython-311.pyc +0 -0
  38. .venv/lib/python3.11/site-packages/fastapi/security/api_key.py +288 -0
  39. .venv/lib/python3.11/site-packages/fastapi/security/base.py +6 -0
  40. .venv/lib/python3.11/site-packages/fastapi/security/http.py +420 -0
  41. .venv/lib/python3.11/site-packages/fastapi/security/oauth2.py +638 -0
  42. .venv/lib/python3.11/site-packages/fastapi/security/open_id_connect_url.py +84 -0
  43. .venv/lib/python3.11/site-packages/fastapi/security/utils.py +10 -0
  44. .venv/lib/python3.11/site-packages/partial_json_parser/__init__.py +7 -0
  45. .venv/lib/python3.11/site-packages/partial_json_parser/__pycache__/version.cpython-311.pyc +0 -0
  46. .venv/lib/python3.11/site-packages/partial_json_parser/core/__pycache__/api.cpython-311.pyc +0 -0
  47. .venv/lib/python3.11/site-packages/partial_json_parser/core/__pycache__/complete.cpython-311.pyc +0 -0
  48. .venv/lib/python3.11/site-packages/partial_json_parser/core/__pycache__/exceptions.cpython-311.pyc +0 -0
  49. .venv/lib/python3.11/site-packages/partial_json_parser/core/__pycache__/myelin.cpython-311.pyc +0 -0
  50. .venv/lib/python3.11/site-packages/partial_json_parser/core/__pycache__/options.cpython-311.pyc +0 -0
.venv/lib/python3.11/site-packages/fastapi/__pycache__/__init__.cpython-311.pyc ADDED
Binary file (1.39 kB). View file
 
.venv/lib/python3.11/site-packages/fastapi/__pycache__/_compat.cpython-311.pyc ADDED
Binary file (32.7 kB). View file
 
.venv/lib/python3.11/site-packages/fastapi/__pycache__/cli.cpython-311.pyc ADDED
Binary file (758 Bytes). View file
 
.venv/lib/python3.11/site-packages/fastapi/__pycache__/concurrency.cpython-311.pyc ADDED
Binary file (1.86 kB). View file
 
.venv/lib/python3.11/site-packages/fastapi/__pycache__/encoders.cpython-311.pyc ADDED
Binary file (12.4 kB). View file
 
.venv/lib/python3.11/site-packages/fastapi/__pycache__/exception_handlers.cpython-311.pyc ADDED
Binary file (2.34 kB). View file
 
.venv/lib/python3.11/site-packages/fastapi/__pycache__/exceptions.cpython-311.pyc ADDED
Binary file (8.25 kB). View file
 
.venv/lib/python3.11/site-packages/fastapi/__pycache__/params.cpython-311.pyc ADDED
Binary file (29.2 kB). View file
 
.venv/lib/python3.11/site-packages/fastapi/__pycache__/responses.cpython-311.pyc ADDED
Binary file (2.75 kB). View file
 
.venv/lib/python3.11/site-packages/fastapi/__pycache__/routing.cpython-311.pyc ADDED
Binary file (92.5 kB). View file
 
.venv/lib/python3.11/site-packages/fastapi/__pycache__/staticfiles.cpython-311.pyc ADDED
Binary file (254 Bytes). View file
 
.venv/lib/python3.11/site-packages/fastapi/__pycache__/types.cpython-311.pyc ADDED
Binary file (911 Bytes). View file
 
.venv/lib/python3.11/site-packages/fastapi/__pycache__/utils.cpython-311.pyc ADDED
Binary file (9.39 kB). View file
 
.venv/lib/python3.11/site-packages/fastapi/dependencies/__init__.py ADDED
File without changes
.venv/lib/python3.11/site-packages/fastapi/dependencies/__pycache__/__init__.cpython-311.pyc ADDED
Binary file (193 Bytes). View file
 
.venv/lib/python3.11/site-packages/fastapi/dependencies/__pycache__/models.cpython-311.pyc ADDED
Binary file (3.38 kB). View file
 
.venv/lib/python3.11/site-packages/fastapi/dependencies/__pycache__/utils.cpython-311.pyc ADDED
Binary file (41.8 kB). View file
 
.venv/lib/python3.11/site-packages/fastapi/dependencies/models.py ADDED
@@ -0,0 +1,37 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from dataclasses import dataclass, field
2
+ from typing import Any, Callable, List, Optional, Sequence, Tuple
3
+
4
+ from fastapi._compat import ModelField
5
+ from fastapi.security.base import SecurityBase
6
+
7
+
8
+ @dataclass
9
+ class SecurityRequirement:
10
+ security_scheme: SecurityBase
11
+ scopes: Optional[Sequence[str]] = None
12
+
13
+
14
+ @dataclass
15
+ class Dependant:
16
+ path_params: List[ModelField] = field(default_factory=list)
17
+ query_params: List[ModelField] = field(default_factory=list)
18
+ header_params: List[ModelField] = field(default_factory=list)
19
+ cookie_params: List[ModelField] = field(default_factory=list)
20
+ body_params: List[ModelField] = field(default_factory=list)
21
+ dependencies: List["Dependant"] = field(default_factory=list)
22
+ security_requirements: List[SecurityRequirement] = field(default_factory=list)
23
+ name: Optional[str] = None
24
+ call: Optional[Callable[..., Any]] = None
25
+ request_param_name: Optional[str] = None
26
+ websocket_param_name: Optional[str] = None
27
+ http_connection_param_name: Optional[str] = None
28
+ response_param_name: Optional[str] = None
29
+ background_tasks_param_name: Optional[str] = None
30
+ security_scopes_param_name: Optional[str] = None
31
+ security_scopes: Optional[List[str]] = None
32
+ use_cache: bool = True
33
+ path: Optional[str] = None
34
+ cache_key: Tuple[Optional[Callable[..., Any]], Tuple[str, ...]] = field(init=False)
35
+
36
+ def __post_init__(self) -> None:
37
+ self.cache_key = (self.call, tuple(sorted(set(self.security_scopes or []))))
.venv/lib/python3.11/site-packages/fastapi/dependencies/utils.py ADDED
@@ -0,0 +1,972 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import inspect
2
+ from contextlib import AsyncExitStack, contextmanager
3
+ from copy import copy, deepcopy
4
+ from dataclasses import dataclass
5
+ from typing import (
6
+ Any,
7
+ Callable,
8
+ Coroutine,
9
+ Dict,
10
+ ForwardRef,
11
+ List,
12
+ Mapping,
13
+ Optional,
14
+ Sequence,
15
+ Tuple,
16
+ Type,
17
+ Union,
18
+ cast,
19
+ )
20
+
21
+ import anyio
22
+ from fastapi import params
23
+ from fastapi._compat import (
24
+ PYDANTIC_V2,
25
+ ErrorWrapper,
26
+ ModelField,
27
+ RequiredParam,
28
+ Undefined,
29
+ _regenerate_error_with_loc,
30
+ copy_field_info,
31
+ create_body_model,
32
+ evaluate_forwardref,
33
+ field_annotation_is_scalar,
34
+ get_annotation_from_field_info,
35
+ get_cached_model_fields,
36
+ get_missing_field_error,
37
+ is_bytes_field,
38
+ is_bytes_sequence_field,
39
+ is_scalar_field,
40
+ is_scalar_sequence_field,
41
+ is_sequence_field,
42
+ is_uploadfile_or_nonable_uploadfile_annotation,
43
+ is_uploadfile_sequence_annotation,
44
+ lenient_issubclass,
45
+ sequence_types,
46
+ serialize_sequence_value,
47
+ value_is_sequence,
48
+ )
49
+ from fastapi.background import BackgroundTasks
50
+ from fastapi.concurrency import (
51
+ asynccontextmanager,
52
+ contextmanager_in_threadpool,
53
+ )
54
+ from fastapi.dependencies.models import Dependant, SecurityRequirement
55
+ from fastapi.logger import logger
56
+ from fastapi.security.base import SecurityBase
57
+ from fastapi.security.oauth2 import OAuth2, SecurityScopes
58
+ from fastapi.security.open_id_connect_url import OpenIdConnect
59
+ from fastapi.utils import create_model_field, get_path_param_names
60
+ from pydantic import BaseModel
61
+ from pydantic.fields import FieldInfo
62
+ from starlette.background import BackgroundTasks as StarletteBackgroundTasks
63
+ from starlette.concurrency import run_in_threadpool
64
+ from starlette.datastructures import (
65
+ FormData,
66
+ Headers,
67
+ ImmutableMultiDict,
68
+ QueryParams,
69
+ UploadFile,
70
+ )
71
+ from starlette.requests import HTTPConnection, Request
72
+ from starlette.responses import Response
73
+ from starlette.websockets import WebSocket
74
+ from typing_extensions import Annotated, get_args, get_origin
75
+
76
+ multipart_not_installed_error = (
77
+ 'Form data requires "python-multipart" to be installed. \n'
78
+ 'You can install "python-multipart" with: \n\n'
79
+ "pip install python-multipart\n"
80
+ )
81
+ multipart_incorrect_install_error = (
82
+ 'Form data requires "python-multipart" to be installed. '
83
+ 'It seems you installed "multipart" instead. \n'
84
+ 'You can remove "multipart" with: \n\n'
85
+ "pip uninstall multipart\n\n"
86
+ 'And then install "python-multipart" with: \n\n'
87
+ "pip install python-multipart\n"
88
+ )
89
+
90
+
91
+ def ensure_multipart_is_installed() -> None:
92
+ try:
93
+ from python_multipart import __version__
94
+
95
+ # Import an attribute that can be mocked/deleted in testing
96
+ assert __version__ > "0.0.12"
97
+ except (ImportError, AssertionError):
98
+ try:
99
+ # __version__ is available in both multiparts, and can be mocked
100
+ from multipart import __version__ # type: ignore[no-redef,import-untyped]
101
+
102
+ assert __version__
103
+ try:
104
+ # parse_options_header is only available in the right multipart
105
+ from multipart.multipart import ( # type: ignore[import-untyped]
106
+ parse_options_header,
107
+ )
108
+
109
+ assert parse_options_header
110
+ except ImportError:
111
+ logger.error(multipart_incorrect_install_error)
112
+ raise RuntimeError(multipart_incorrect_install_error) from None
113
+ except ImportError:
114
+ logger.error(multipart_not_installed_error)
115
+ raise RuntimeError(multipart_not_installed_error) from None
116
+
117
+
118
+ def get_param_sub_dependant(
119
+ *,
120
+ param_name: str,
121
+ depends: params.Depends,
122
+ path: str,
123
+ security_scopes: Optional[List[str]] = None,
124
+ ) -> Dependant:
125
+ assert depends.dependency
126
+ return get_sub_dependant(
127
+ depends=depends,
128
+ dependency=depends.dependency,
129
+ path=path,
130
+ name=param_name,
131
+ security_scopes=security_scopes,
132
+ )
133
+
134
+
135
+ def get_parameterless_sub_dependant(*, depends: params.Depends, path: str) -> Dependant:
136
+ assert callable(
137
+ depends.dependency
138
+ ), "A parameter-less dependency must have a callable dependency"
139
+ return get_sub_dependant(depends=depends, dependency=depends.dependency, path=path)
140
+
141
+
142
+ def get_sub_dependant(
143
+ *,
144
+ depends: params.Depends,
145
+ dependency: Callable[..., Any],
146
+ path: str,
147
+ name: Optional[str] = None,
148
+ security_scopes: Optional[List[str]] = None,
149
+ ) -> Dependant:
150
+ security_requirement = None
151
+ security_scopes = security_scopes or []
152
+ if isinstance(depends, params.Security):
153
+ dependency_scopes = depends.scopes
154
+ security_scopes.extend(dependency_scopes)
155
+ if isinstance(dependency, SecurityBase):
156
+ use_scopes: List[str] = []
157
+ if isinstance(dependency, (OAuth2, OpenIdConnect)):
158
+ use_scopes = security_scopes
159
+ security_requirement = SecurityRequirement(
160
+ security_scheme=dependency, scopes=use_scopes
161
+ )
162
+ sub_dependant = get_dependant(
163
+ path=path,
164
+ call=dependency,
165
+ name=name,
166
+ security_scopes=security_scopes,
167
+ use_cache=depends.use_cache,
168
+ )
169
+ if security_requirement:
170
+ sub_dependant.security_requirements.append(security_requirement)
171
+ return sub_dependant
172
+
173
+
174
+ CacheKey = Tuple[Optional[Callable[..., Any]], Tuple[str, ...]]
175
+
176
+
177
+ def get_flat_dependant(
178
+ dependant: Dependant,
179
+ *,
180
+ skip_repeats: bool = False,
181
+ visited: Optional[List[CacheKey]] = None,
182
+ ) -> Dependant:
183
+ if visited is None:
184
+ visited = []
185
+ visited.append(dependant.cache_key)
186
+
187
+ flat_dependant = Dependant(
188
+ path_params=dependant.path_params.copy(),
189
+ query_params=dependant.query_params.copy(),
190
+ header_params=dependant.header_params.copy(),
191
+ cookie_params=dependant.cookie_params.copy(),
192
+ body_params=dependant.body_params.copy(),
193
+ security_requirements=dependant.security_requirements.copy(),
194
+ use_cache=dependant.use_cache,
195
+ path=dependant.path,
196
+ )
197
+ for sub_dependant in dependant.dependencies:
198
+ if skip_repeats and sub_dependant.cache_key in visited:
199
+ continue
200
+ flat_sub = get_flat_dependant(
201
+ sub_dependant, skip_repeats=skip_repeats, visited=visited
202
+ )
203
+ flat_dependant.path_params.extend(flat_sub.path_params)
204
+ flat_dependant.query_params.extend(flat_sub.query_params)
205
+ flat_dependant.header_params.extend(flat_sub.header_params)
206
+ flat_dependant.cookie_params.extend(flat_sub.cookie_params)
207
+ flat_dependant.body_params.extend(flat_sub.body_params)
208
+ flat_dependant.security_requirements.extend(flat_sub.security_requirements)
209
+ return flat_dependant
210
+
211
+
212
+ def _get_flat_fields_from_params(fields: List[ModelField]) -> List[ModelField]:
213
+ if not fields:
214
+ return fields
215
+ first_field = fields[0]
216
+ if len(fields) == 1 and lenient_issubclass(first_field.type_, BaseModel):
217
+ fields_to_extract = get_cached_model_fields(first_field.type_)
218
+ return fields_to_extract
219
+ return fields
220
+
221
+
222
+ def get_flat_params(dependant: Dependant) -> List[ModelField]:
223
+ flat_dependant = get_flat_dependant(dependant, skip_repeats=True)
224
+ path_params = _get_flat_fields_from_params(flat_dependant.path_params)
225
+ query_params = _get_flat_fields_from_params(flat_dependant.query_params)
226
+ header_params = _get_flat_fields_from_params(flat_dependant.header_params)
227
+ cookie_params = _get_flat_fields_from_params(flat_dependant.cookie_params)
228
+ return path_params + query_params + header_params + cookie_params
229
+
230
+
231
+ def get_typed_signature(call: Callable[..., Any]) -> inspect.Signature:
232
+ signature = inspect.signature(call)
233
+ globalns = getattr(call, "__globals__", {})
234
+ typed_params = [
235
+ inspect.Parameter(
236
+ name=param.name,
237
+ kind=param.kind,
238
+ default=param.default,
239
+ annotation=get_typed_annotation(param.annotation, globalns),
240
+ )
241
+ for param in signature.parameters.values()
242
+ ]
243
+ typed_signature = inspect.Signature(typed_params)
244
+ return typed_signature
245
+
246
+
247
+ def get_typed_annotation(annotation: Any, globalns: Dict[str, Any]) -> Any:
248
+ if isinstance(annotation, str):
249
+ annotation = ForwardRef(annotation)
250
+ annotation = evaluate_forwardref(annotation, globalns, globalns)
251
+ return annotation
252
+
253
+
254
+ def get_typed_return_annotation(call: Callable[..., Any]) -> Any:
255
+ signature = inspect.signature(call)
256
+ annotation = signature.return_annotation
257
+
258
+ if annotation is inspect.Signature.empty:
259
+ return None
260
+
261
+ globalns = getattr(call, "__globals__", {})
262
+ return get_typed_annotation(annotation, globalns)
263
+
264
+
265
+ def get_dependant(
266
+ *,
267
+ path: str,
268
+ call: Callable[..., Any],
269
+ name: Optional[str] = None,
270
+ security_scopes: Optional[List[str]] = None,
271
+ use_cache: bool = True,
272
+ ) -> Dependant:
273
+ path_param_names = get_path_param_names(path)
274
+ endpoint_signature = get_typed_signature(call)
275
+ signature_params = endpoint_signature.parameters
276
+ dependant = Dependant(
277
+ call=call,
278
+ name=name,
279
+ path=path,
280
+ security_scopes=security_scopes,
281
+ use_cache=use_cache,
282
+ )
283
+ for param_name, param in signature_params.items():
284
+ is_path_param = param_name in path_param_names
285
+ param_details = analyze_param(
286
+ param_name=param_name,
287
+ annotation=param.annotation,
288
+ value=param.default,
289
+ is_path_param=is_path_param,
290
+ )
291
+ if param_details.depends is not None:
292
+ sub_dependant = get_param_sub_dependant(
293
+ param_name=param_name,
294
+ depends=param_details.depends,
295
+ path=path,
296
+ security_scopes=security_scopes,
297
+ )
298
+ dependant.dependencies.append(sub_dependant)
299
+ continue
300
+ if add_non_field_param_to_dependency(
301
+ param_name=param_name,
302
+ type_annotation=param_details.type_annotation,
303
+ dependant=dependant,
304
+ ):
305
+ assert (
306
+ param_details.field is None
307
+ ), f"Cannot specify multiple FastAPI annotations for {param_name!r}"
308
+ continue
309
+ assert param_details.field is not None
310
+ if isinstance(param_details.field.field_info, params.Body):
311
+ dependant.body_params.append(param_details.field)
312
+ else:
313
+ add_param_to_fields(field=param_details.field, dependant=dependant)
314
+ return dependant
315
+
316
+
317
+ def add_non_field_param_to_dependency(
318
+ *, param_name: str, type_annotation: Any, dependant: Dependant
319
+ ) -> Optional[bool]:
320
+ if lenient_issubclass(type_annotation, Request):
321
+ dependant.request_param_name = param_name
322
+ return True
323
+ elif lenient_issubclass(type_annotation, WebSocket):
324
+ dependant.websocket_param_name = param_name
325
+ return True
326
+ elif lenient_issubclass(type_annotation, HTTPConnection):
327
+ dependant.http_connection_param_name = param_name
328
+ return True
329
+ elif lenient_issubclass(type_annotation, Response):
330
+ dependant.response_param_name = param_name
331
+ return True
332
+ elif lenient_issubclass(type_annotation, StarletteBackgroundTasks):
333
+ dependant.background_tasks_param_name = param_name
334
+ return True
335
+ elif lenient_issubclass(type_annotation, SecurityScopes):
336
+ dependant.security_scopes_param_name = param_name
337
+ return True
338
+ return None
339
+
340
+
341
+ @dataclass
342
+ class ParamDetails:
343
+ type_annotation: Any
344
+ depends: Optional[params.Depends]
345
+ field: Optional[ModelField]
346
+
347
+
348
+ def analyze_param(
349
+ *,
350
+ param_name: str,
351
+ annotation: Any,
352
+ value: Any,
353
+ is_path_param: bool,
354
+ ) -> ParamDetails:
355
+ field_info = None
356
+ depends = None
357
+ type_annotation: Any = Any
358
+ use_annotation: Any = Any
359
+ if annotation is not inspect.Signature.empty:
360
+ use_annotation = annotation
361
+ type_annotation = annotation
362
+ # Extract Annotated info
363
+ if get_origin(use_annotation) is Annotated:
364
+ annotated_args = get_args(annotation)
365
+ type_annotation = annotated_args[0]
366
+ fastapi_annotations = [
367
+ arg
368
+ for arg in annotated_args[1:]
369
+ if isinstance(arg, (FieldInfo, params.Depends))
370
+ ]
371
+ fastapi_specific_annotations = [
372
+ arg
373
+ for arg in fastapi_annotations
374
+ if isinstance(arg, (params.Param, params.Body, params.Depends))
375
+ ]
376
+ if fastapi_specific_annotations:
377
+ fastapi_annotation: Union[FieldInfo, params.Depends, None] = (
378
+ fastapi_specific_annotations[-1]
379
+ )
380
+ else:
381
+ fastapi_annotation = None
382
+ # Set default for Annotated FieldInfo
383
+ if isinstance(fastapi_annotation, FieldInfo):
384
+ # Copy `field_info` because we mutate `field_info.default` below.
385
+ field_info = copy_field_info(
386
+ field_info=fastapi_annotation, annotation=use_annotation
387
+ )
388
+ assert (
389
+ field_info.default is Undefined or field_info.default is RequiredParam
390
+ ), (
391
+ f"`{field_info.__class__.__name__}` default value cannot be set in"
392
+ f" `Annotated` for {param_name!r}. Set the default value with `=` instead."
393
+ )
394
+ if value is not inspect.Signature.empty:
395
+ assert not is_path_param, "Path parameters cannot have default values"
396
+ field_info.default = value
397
+ else:
398
+ field_info.default = RequiredParam
399
+ # Get Annotated Depends
400
+ elif isinstance(fastapi_annotation, params.Depends):
401
+ depends = fastapi_annotation
402
+ # Get Depends from default value
403
+ if isinstance(value, params.Depends):
404
+ assert depends is None, (
405
+ "Cannot specify `Depends` in `Annotated` and default value"
406
+ f" together for {param_name!r}"
407
+ )
408
+ assert field_info is None, (
409
+ "Cannot specify a FastAPI annotation in `Annotated` and `Depends` as a"
410
+ f" default value together for {param_name!r}"
411
+ )
412
+ depends = value
413
+ # Get FieldInfo from default value
414
+ elif isinstance(value, FieldInfo):
415
+ assert field_info is None, (
416
+ "Cannot specify FastAPI annotations in `Annotated` and default value"
417
+ f" together for {param_name!r}"
418
+ )
419
+ field_info = value
420
+ if PYDANTIC_V2:
421
+ field_info.annotation = type_annotation
422
+
423
+ # Get Depends from type annotation
424
+ if depends is not None and depends.dependency is None:
425
+ # Copy `depends` before mutating it
426
+ depends = copy(depends)
427
+ depends.dependency = type_annotation
428
+
429
+ # Handle non-param type annotations like Request
430
+ if lenient_issubclass(
431
+ type_annotation,
432
+ (
433
+ Request,
434
+ WebSocket,
435
+ HTTPConnection,
436
+ Response,
437
+ StarletteBackgroundTasks,
438
+ SecurityScopes,
439
+ ),
440
+ ):
441
+ assert depends is None, f"Cannot specify `Depends` for type {type_annotation!r}"
442
+ assert (
443
+ field_info is None
444
+ ), f"Cannot specify FastAPI annotation for type {type_annotation!r}"
445
+ # Handle default assignations, neither field_info nor depends was not found in Annotated nor default value
446
+ elif field_info is None and depends is None:
447
+ default_value = value if value is not inspect.Signature.empty else RequiredParam
448
+ if is_path_param:
449
+ # We might check here that `default_value is RequiredParam`, but the fact is that the same
450
+ # parameter might sometimes be a path parameter and sometimes not. See
451
+ # `tests/test_infer_param_optionality.py` for an example.
452
+ field_info = params.Path(annotation=use_annotation)
453
+ elif is_uploadfile_or_nonable_uploadfile_annotation(
454
+ type_annotation
455
+ ) or is_uploadfile_sequence_annotation(type_annotation):
456
+ field_info = params.File(annotation=use_annotation, default=default_value)
457
+ elif not field_annotation_is_scalar(annotation=type_annotation):
458
+ field_info = params.Body(annotation=use_annotation, default=default_value)
459
+ else:
460
+ field_info = params.Query(annotation=use_annotation, default=default_value)
461
+
462
+ field = None
463
+ # It's a field_info, not a dependency
464
+ if field_info is not None:
465
+ # Handle field_info.in_
466
+ if is_path_param:
467
+ assert isinstance(field_info, params.Path), (
468
+ f"Cannot use `{field_info.__class__.__name__}` for path param"
469
+ f" {param_name!r}"
470
+ )
471
+ elif (
472
+ isinstance(field_info, params.Param)
473
+ and getattr(field_info, "in_", None) is None
474
+ ):
475
+ field_info.in_ = params.ParamTypes.query
476
+ use_annotation_from_field_info = get_annotation_from_field_info(
477
+ use_annotation,
478
+ field_info,
479
+ param_name,
480
+ )
481
+ if isinstance(field_info, params.Form):
482
+ ensure_multipart_is_installed()
483
+ if not field_info.alias and getattr(field_info, "convert_underscores", None):
484
+ alias = param_name.replace("_", "-")
485
+ else:
486
+ alias = field_info.alias or param_name
487
+ field_info.alias = alias
488
+ field = create_model_field(
489
+ name=param_name,
490
+ type_=use_annotation_from_field_info,
491
+ default=field_info.default,
492
+ alias=alias,
493
+ required=field_info.default in (RequiredParam, Undefined),
494
+ field_info=field_info,
495
+ )
496
+ if is_path_param:
497
+ assert is_scalar_field(
498
+ field=field
499
+ ), "Path params must be of one of the supported types"
500
+ elif isinstance(field_info, params.Query):
501
+ assert (
502
+ is_scalar_field(field)
503
+ or is_scalar_sequence_field(field)
504
+ or (
505
+ lenient_issubclass(field.type_, BaseModel)
506
+ # For Pydantic v1
507
+ and getattr(field, "shape", 1) == 1
508
+ )
509
+ )
510
+
511
+ return ParamDetails(type_annotation=type_annotation, depends=depends, field=field)
512
+
513
+
514
+ def add_param_to_fields(*, field: ModelField, dependant: Dependant) -> None:
515
+ field_info = field.field_info
516
+ field_info_in = getattr(field_info, "in_", None)
517
+ if field_info_in == params.ParamTypes.path:
518
+ dependant.path_params.append(field)
519
+ elif field_info_in == params.ParamTypes.query:
520
+ dependant.query_params.append(field)
521
+ elif field_info_in == params.ParamTypes.header:
522
+ dependant.header_params.append(field)
523
+ else:
524
+ assert (
525
+ field_info_in == params.ParamTypes.cookie
526
+ ), f"non-body parameters must be in path, query, header or cookie: {field.name}"
527
+ dependant.cookie_params.append(field)
528
+
529
+
530
+ def is_coroutine_callable(call: Callable[..., Any]) -> bool:
531
+ if inspect.isroutine(call):
532
+ return inspect.iscoroutinefunction(call)
533
+ if inspect.isclass(call):
534
+ return False
535
+ dunder_call = getattr(call, "__call__", None) # noqa: B004
536
+ return inspect.iscoroutinefunction(dunder_call)
537
+
538
+
539
+ def is_async_gen_callable(call: Callable[..., Any]) -> bool:
540
+ if inspect.isasyncgenfunction(call):
541
+ return True
542
+ dunder_call = getattr(call, "__call__", None) # noqa: B004
543
+ return inspect.isasyncgenfunction(dunder_call)
544
+
545
+
546
+ def is_gen_callable(call: Callable[..., Any]) -> bool:
547
+ if inspect.isgeneratorfunction(call):
548
+ return True
549
+ dunder_call = getattr(call, "__call__", None) # noqa: B004
550
+ return inspect.isgeneratorfunction(dunder_call)
551
+
552
+
553
+ async def solve_generator(
554
+ *, call: Callable[..., Any], stack: AsyncExitStack, sub_values: Dict[str, Any]
555
+ ) -> Any:
556
+ if is_gen_callable(call):
557
+ cm = contextmanager_in_threadpool(contextmanager(call)(**sub_values))
558
+ elif is_async_gen_callable(call):
559
+ cm = asynccontextmanager(call)(**sub_values)
560
+ return await stack.enter_async_context(cm)
561
+
562
+
563
+ @dataclass
564
+ class SolvedDependency:
565
+ values: Dict[str, Any]
566
+ errors: List[Any]
567
+ background_tasks: Optional[StarletteBackgroundTasks]
568
+ response: Response
569
+ dependency_cache: Dict[Tuple[Callable[..., Any], Tuple[str]], Any]
570
+
571
+
572
+ async def solve_dependencies(
573
+ *,
574
+ request: Union[Request, WebSocket],
575
+ dependant: Dependant,
576
+ body: Optional[Union[Dict[str, Any], FormData]] = None,
577
+ background_tasks: Optional[StarletteBackgroundTasks] = None,
578
+ response: Optional[Response] = None,
579
+ dependency_overrides_provider: Optional[Any] = None,
580
+ dependency_cache: Optional[Dict[Tuple[Callable[..., Any], Tuple[str]], Any]] = None,
581
+ async_exit_stack: AsyncExitStack,
582
+ embed_body_fields: bool,
583
+ ) -> SolvedDependency:
584
+ values: Dict[str, Any] = {}
585
+ errors: List[Any] = []
586
+ if response is None:
587
+ response = Response()
588
+ del response.headers["content-length"]
589
+ response.status_code = None # type: ignore
590
+ dependency_cache = dependency_cache or {}
591
+ sub_dependant: Dependant
592
+ for sub_dependant in dependant.dependencies:
593
+ sub_dependant.call = cast(Callable[..., Any], sub_dependant.call)
594
+ sub_dependant.cache_key = cast(
595
+ Tuple[Callable[..., Any], Tuple[str]], sub_dependant.cache_key
596
+ )
597
+ call = sub_dependant.call
598
+ use_sub_dependant = sub_dependant
599
+ if (
600
+ dependency_overrides_provider
601
+ and dependency_overrides_provider.dependency_overrides
602
+ ):
603
+ original_call = sub_dependant.call
604
+ call = getattr(
605
+ dependency_overrides_provider, "dependency_overrides", {}
606
+ ).get(original_call, original_call)
607
+ use_path: str = sub_dependant.path # type: ignore
608
+ use_sub_dependant = get_dependant(
609
+ path=use_path,
610
+ call=call,
611
+ name=sub_dependant.name,
612
+ security_scopes=sub_dependant.security_scopes,
613
+ )
614
+
615
+ solved_result = await solve_dependencies(
616
+ request=request,
617
+ dependant=use_sub_dependant,
618
+ body=body,
619
+ background_tasks=background_tasks,
620
+ response=response,
621
+ dependency_overrides_provider=dependency_overrides_provider,
622
+ dependency_cache=dependency_cache,
623
+ async_exit_stack=async_exit_stack,
624
+ embed_body_fields=embed_body_fields,
625
+ )
626
+ background_tasks = solved_result.background_tasks
627
+ dependency_cache.update(solved_result.dependency_cache)
628
+ if solved_result.errors:
629
+ errors.extend(solved_result.errors)
630
+ continue
631
+ if sub_dependant.use_cache and sub_dependant.cache_key in dependency_cache:
632
+ solved = dependency_cache[sub_dependant.cache_key]
633
+ elif is_gen_callable(call) or is_async_gen_callable(call):
634
+ solved = await solve_generator(
635
+ call=call, stack=async_exit_stack, sub_values=solved_result.values
636
+ )
637
+ elif is_coroutine_callable(call):
638
+ solved = await call(**solved_result.values)
639
+ else:
640
+ solved = await run_in_threadpool(call, **solved_result.values)
641
+ if sub_dependant.name is not None:
642
+ values[sub_dependant.name] = solved
643
+ if sub_dependant.cache_key not in dependency_cache:
644
+ dependency_cache[sub_dependant.cache_key] = solved
645
+ path_values, path_errors = request_params_to_args(
646
+ dependant.path_params, request.path_params
647
+ )
648
+ query_values, query_errors = request_params_to_args(
649
+ dependant.query_params, request.query_params
650
+ )
651
+ header_values, header_errors = request_params_to_args(
652
+ dependant.header_params, request.headers
653
+ )
654
+ cookie_values, cookie_errors = request_params_to_args(
655
+ dependant.cookie_params, request.cookies
656
+ )
657
+ values.update(path_values)
658
+ values.update(query_values)
659
+ values.update(header_values)
660
+ values.update(cookie_values)
661
+ errors += path_errors + query_errors + header_errors + cookie_errors
662
+ if dependant.body_params:
663
+ (
664
+ body_values,
665
+ body_errors,
666
+ ) = await request_body_to_args( # body_params checked above
667
+ body_fields=dependant.body_params,
668
+ received_body=body,
669
+ embed_body_fields=embed_body_fields,
670
+ )
671
+ values.update(body_values)
672
+ errors.extend(body_errors)
673
+ if dependant.http_connection_param_name:
674
+ values[dependant.http_connection_param_name] = request
675
+ if dependant.request_param_name and isinstance(request, Request):
676
+ values[dependant.request_param_name] = request
677
+ elif dependant.websocket_param_name and isinstance(request, WebSocket):
678
+ values[dependant.websocket_param_name] = request
679
+ if dependant.background_tasks_param_name:
680
+ if background_tasks is None:
681
+ background_tasks = BackgroundTasks()
682
+ values[dependant.background_tasks_param_name] = background_tasks
683
+ if dependant.response_param_name:
684
+ values[dependant.response_param_name] = response
685
+ if dependant.security_scopes_param_name:
686
+ values[dependant.security_scopes_param_name] = SecurityScopes(
687
+ scopes=dependant.security_scopes
688
+ )
689
+ return SolvedDependency(
690
+ values=values,
691
+ errors=errors,
692
+ background_tasks=background_tasks,
693
+ response=response,
694
+ dependency_cache=dependency_cache,
695
+ )
696
+
697
+
698
+ def _validate_value_with_model_field(
699
+ *, field: ModelField, value: Any, values: Dict[str, Any], loc: Tuple[str, ...]
700
+ ) -> Tuple[Any, List[Any]]:
701
+ if value is None:
702
+ if field.required:
703
+ return None, [get_missing_field_error(loc=loc)]
704
+ else:
705
+ return deepcopy(field.default), []
706
+ v_, errors_ = field.validate(value, values, loc=loc)
707
+ if isinstance(errors_, ErrorWrapper):
708
+ return None, [errors_]
709
+ elif isinstance(errors_, list):
710
+ new_errors = _regenerate_error_with_loc(errors=errors_, loc_prefix=())
711
+ return None, new_errors
712
+ else:
713
+ return v_, []
714
+
715
+
716
+ def _get_multidict_value(
717
+ field: ModelField, values: Mapping[str, Any], alias: Union[str, None] = None
718
+ ) -> Any:
719
+ alias = alias or field.alias
720
+ if is_sequence_field(field) and isinstance(values, (ImmutableMultiDict, Headers)):
721
+ value = values.getlist(alias)
722
+ else:
723
+ value = values.get(alias, None)
724
+ if (
725
+ value is None
726
+ or (
727
+ isinstance(field.field_info, params.Form)
728
+ and isinstance(value, str) # For type checks
729
+ and value == ""
730
+ )
731
+ or (is_sequence_field(field) and len(value) == 0)
732
+ ):
733
+ if field.required:
734
+ return
735
+ else:
736
+ return deepcopy(field.default)
737
+ return value
738
+
739
+
740
+ def request_params_to_args(
741
+ fields: Sequence[ModelField],
742
+ received_params: Union[Mapping[str, Any], QueryParams, Headers],
743
+ ) -> Tuple[Dict[str, Any], List[Any]]:
744
+ values: Dict[str, Any] = {}
745
+ errors: List[Dict[str, Any]] = []
746
+
747
+ if not fields:
748
+ return values, errors
749
+
750
+ first_field = fields[0]
751
+ fields_to_extract = fields
752
+ single_not_embedded_field = False
753
+ if len(fields) == 1 and lenient_issubclass(first_field.type_, BaseModel):
754
+ fields_to_extract = get_cached_model_fields(first_field.type_)
755
+ single_not_embedded_field = True
756
+
757
+ params_to_process: Dict[str, Any] = {}
758
+
759
+ processed_keys = set()
760
+
761
+ for field in fields_to_extract:
762
+ alias = None
763
+ if isinstance(received_params, Headers):
764
+ # Handle fields extracted from a Pydantic Model for a header, each field
765
+ # doesn't have a FieldInfo of type Header with the default convert_underscores=True
766
+ convert_underscores = getattr(field.field_info, "convert_underscores", True)
767
+ if convert_underscores:
768
+ alias = (
769
+ field.alias
770
+ if field.alias != field.name
771
+ else field.name.replace("_", "-")
772
+ )
773
+ value = _get_multidict_value(field, received_params, alias=alias)
774
+ if value is not None:
775
+ params_to_process[field.name] = value
776
+ processed_keys.add(alias or field.alias)
777
+ processed_keys.add(field.name)
778
+
779
+ for key, value in received_params.items():
780
+ if key not in processed_keys:
781
+ params_to_process[key] = value
782
+
783
+ if single_not_embedded_field:
784
+ field_info = first_field.field_info
785
+ assert isinstance(
786
+ field_info, params.Param
787
+ ), "Params must be subclasses of Param"
788
+ loc: Tuple[str, ...] = (field_info.in_.value,)
789
+ v_, errors_ = _validate_value_with_model_field(
790
+ field=first_field, value=params_to_process, values=values, loc=loc
791
+ )
792
+ return {first_field.name: v_}, errors_
793
+
794
+ for field in fields:
795
+ value = _get_multidict_value(field, received_params)
796
+ field_info = field.field_info
797
+ assert isinstance(
798
+ field_info, params.Param
799
+ ), "Params must be subclasses of Param"
800
+ loc = (field_info.in_.value, field.alias)
801
+ v_, errors_ = _validate_value_with_model_field(
802
+ field=field, value=value, values=values, loc=loc
803
+ )
804
+ if errors_:
805
+ errors.extend(errors_)
806
+ else:
807
+ values[field.name] = v_
808
+ return values, errors
809
+
810
+
811
+ def _should_embed_body_fields(fields: List[ModelField]) -> bool:
812
+ if not fields:
813
+ return False
814
+ # More than one dependency could have the same field, it would show up as multiple
815
+ # fields but it's the same one, so count them by name
816
+ body_param_names_set = {field.name for field in fields}
817
+ # A top level field has to be a single field, not multiple
818
+ if len(body_param_names_set) > 1:
819
+ return True
820
+ first_field = fields[0]
821
+ # If it explicitly specifies it is embedded, it has to be embedded
822
+ if getattr(first_field.field_info, "embed", None):
823
+ return True
824
+ # If it's a Form (or File) field, it has to be a BaseModel to be top level
825
+ # otherwise it has to be embedded, so that the key value pair can be extracted
826
+ if isinstance(first_field.field_info, params.Form) and not lenient_issubclass(
827
+ first_field.type_, BaseModel
828
+ ):
829
+ return True
830
+ return False
831
+
832
+
833
+ async def _extract_form_body(
834
+ body_fields: List[ModelField],
835
+ received_body: FormData,
836
+ ) -> Dict[str, Any]:
837
+ values = {}
838
+ first_field = body_fields[0]
839
+ first_field_info = first_field.field_info
840
+
841
+ for field in body_fields:
842
+ value = _get_multidict_value(field, received_body)
843
+ if (
844
+ isinstance(first_field_info, params.File)
845
+ and is_bytes_field(field)
846
+ and isinstance(value, UploadFile)
847
+ ):
848
+ value = await value.read()
849
+ elif (
850
+ is_bytes_sequence_field(field)
851
+ and isinstance(first_field_info, params.File)
852
+ and value_is_sequence(value)
853
+ ):
854
+ # For types
855
+ assert isinstance(value, sequence_types) # type: ignore[arg-type]
856
+ results: List[Union[bytes, str]] = []
857
+
858
+ async def process_fn(
859
+ fn: Callable[[], Coroutine[Any, Any, Any]],
860
+ ) -> None:
861
+ result = await fn()
862
+ results.append(result) # noqa: B023
863
+
864
+ async with anyio.create_task_group() as tg:
865
+ for sub_value in value:
866
+ tg.start_soon(process_fn, sub_value.read)
867
+ value = serialize_sequence_value(field=field, value=results)
868
+ if value is not None:
869
+ values[field.alias] = value
870
+ for key, value in received_body.items():
871
+ if key not in values:
872
+ values[key] = value
873
+ return values
874
+
875
+
876
+ async def request_body_to_args(
877
+ body_fields: List[ModelField],
878
+ received_body: Optional[Union[Dict[str, Any], FormData]],
879
+ embed_body_fields: bool,
880
+ ) -> Tuple[Dict[str, Any], List[Dict[str, Any]]]:
881
+ values: Dict[str, Any] = {}
882
+ errors: List[Dict[str, Any]] = []
883
+ assert body_fields, "request_body_to_args() should be called with fields"
884
+ single_not_embedded_field = len(body_fields) == 1 and not embed_body_fields
885
+ first_field = body_fields[0]
886
+ body_to_process = received_body
887
+
888
+ fields_to_extract: List[ModelField] = body_fields
889
+
890
+ if single_not_embedded_field and lenient_issubclass(first_field.type_, BaseModel):
891
+ fields_to_extract = get_cached_model_fields(first_field.type_)
892
+
893
+ if isinstance(received_body, FormData):
894
+ body_to_process = await _extract_form_body(fields_to_extract, received_body)
895
+
896
+ if single_not_embedded_field:
897
+ loc: Tuple[str, ...] = ("body",)
898
+ v_, errors_ = _validate_value_with_model_field(
899
+ field=first_field, value=body_to_process, values=values, loc=loc
900
+ )
901
+ return {first_field.name: v_}, errors_
902
+ for field in body_fields:
903
+ loc = ("body", field.alias)
904
+ value: Optional[Any] = None
905
+ if body_to_process is not None:
906
+ try:
907
+ value = body_to_process.get(field.alias)
908
+ # If the received body is a list, not a dict
909
+ except AttributeError:
910
+ errors.append(get_missing_field_error(loc))
911
+ continue
912
+ v_, errors_ = _validate_value_with_model_field(
913
+ field=field, value=value, values=values, loc=loc
914
+ )
915
+ if errors_:
916
+ errors.extend(errors_)
917
+ else:
918
+ values[field.name] = v_
919
+ return values, errors
920
+
921
+
922
+ def get_body_field(
923
+ *, flat_dependant: Dependant, name: str, embed_body_fields: bool
924
+ ) -> Optional[ModelField]:
925
+ """
926
+ Get a ModelField representing the request body for a path operation, combining
927
+ all body parameters into a single field if necessary.
928
+
929
+ Used to check if it's form data (with `isinstance(body_field, params.Form)`)
930
+ or JSON and to generate the JSON Schema for a request body.
931
+
932
+ This is **not** used to validate/parse the request body, that's done with each
933
+ individual body parameter.
934
+ """
935
+ if not flat_dependant.body_params:
936
+ return None
937
+ first_param = flat_dependant.body_params[0]
938
+ if not embed_body_fields:
939
+ return first_param
940
+ model_name = "Body_" + name
941
+ BodyModel = create_body_model(
942
+ fields=flat_dependant.body_params, model_name=model_name
943
+ )
944
+ required = any(True for f in flat_dependant.body_params if f.required)
945
+ BodyFieldInfo_kwargs: Dict[str, Any] = {
946
+ "annotation": BodyModel,
947
+ "alias": "body",
948
+ }
949
+ if not required:
950
+ BodyFieldInfo_kwargs["default"] = None
951
+ if any(isinstance(f.field_info, params.File) for f in flat_dependant.body_params):
952
+ BodyFieldInfo: Type[params.Body] = params.File
953
+ elif any(isinstance(f.field_info, params.Form) for f in flat_dependant.body_params):
954
+ BodyFieldInfo = params.Form
955
+ else:
956
+ BodyFieldInfo = params.Body
957
+
958
+ body_param_media_types = [
959
+ f.field_info.media_type
960
+ for f in flat_dependant.body_params
961
+ if isinstance(f.field_info, params.Body)
962
+ ]
963
+ if len(set(body_param_media_types)) == 1:
964
+ BodyFieldInfo_kwargs["media_type"] = body_param_media_types[0]
965
+ final_field = create_model_field(
966
+ name="body",
967
+ type_=BodyModel,
968
+ required=required,
969
+ alias="body",
970
+ field_info=BodyFieldInfo(**BodyFieldInfo_kwargs),
971
+ )
972
+ return final_field
.venv/lib/python3.11/site-packages/fastapi/openapi/__init__.py ADDED
File without changes
.venv/lib/python3.11/site-packages/fastapi/openapi/__pycache__/__init__.cpython-311.pyc ADDED
Binary file (188 Bytes). View file
 
.venv/lib/python3.11/site-packages/fastapi/openapi/__pycache__/constants.cpython-311.pyc ADDED
Binary file (358 Bytes). View file
 
.venv/lib/python3.11/site-packages/fastapi/openapi/__pycache__/docs.cpython-311.pyc ADDED
Binary file (11.4 kB). View file
 
.venv/lib/python3.11/site-packages/fastapi/openapi/__pycache__/models.cpython-311.pyc ADDED
Binary file (28.9 kB). View file
 
.venv/lib/python3.11/site-packages/fastapi/openapi/__pycache__/utils.cpython-311.pyc ADDED
Binary file (22.6 kB). View file
 
.venv/lib/python3.11/site-packages/fastapi/openapi/constants.py ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ METHODS_WITH_BODY = {"GET", "HEAD", "POST", "PUT", "DELETE", "PATCH"}
2
+ REF_PREFIX = "#/components/schemas/"
3
+ REF_TEMPLATE = "#/components/schemas/{model}"
.venv/lib/python3.11/site-packages/fastapi/openapi/docs.py ADDED
@@ -0,0 +1,344 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import json
2
+ from typing import Any, Dict, Optional
3
+
4
+ from fastapi.encoders import jsonable_encoder
5
+ from starlette.responses import HTMLResponse
6
+ from typing_extensions import Annotated, Doc
7
+
8
+ swagger_ui_default_parameters: Annotated[
9
+ Dict[str, Any],
10
+ Doc(
11
+ """
12
+ Default configurations for Swagger UI.
13
+
14
+ You can use it as a template to add any other configurations needed.
15
+ """
16
+ ),
17
+ ] = {
18
+ "dom_id": "#swagger-ui",
19
+ "layout": "BaseLayout",
20
+ "deepLinking": True,
21
+ "showExtensions": True,
22
+ "showCommonExtensions": True,
23
+ }
24
+
25
+
26
+ def get_swagger_ui_html(
27
+ *,
28
+ openapi_url: Annotated[
29
+ str,
30
+ Doc(
31
+ """
32
+ The OpenAPI URL that Swagger UI should load and use.
33
+
34
+ This is normally done automatically by FastAPI using the default URL
35
+ `/openapi.json`.
36
+ """
37
+ ),
38
+ ],
39
+ title: Annotated[
40
+ str,
41
+ Doc(
42
+ """
43
+ The HTML `<title>` content, normally shown in the browser tab.
44
+ """
45
+ ),
46
+ ],
47
+ swagger_js_url: Annotated[
48
+ str,
49
+ Doc(
50
+ """
51
+ The URL to use to load the Swagger UI JavaScript.
52
+
53
+ It is normally set to a CDN URL.
54
+ """
55
+ ),
56
+ ] = "https://cdn.jsdelivr.net/npm/swagger-ui-dist@5/swagger-ui-bundle.js",
57
+ swagger_css_url: Annotated[
58
+ str,
59
+ Doc(
60
+ """
61
+ The URL to use to load the Swagger UI CSS.
62
+
63
+ It is normally set to a CDN URL.
64
+ """
65
+ ),
66
+ ] = "https://cdn.jsdelivr.net/npm/swagger-ui-dist@5/swagger-ui.css",
67
+ swagger_favicon_url: Annotated[
68
+ str,
69
+ Doc(
70
+ """
71
+ The URL of the favicon to use. It is normally shown in the browser tab.
72
+ """
73
+ ),
74
+ ] = "https://fastapi.tiangolo.com/img/favicon.png",
75
+ oauth2_redirect_url: Annotated[
76
+ Optional[str],
77
+ Doc(
78
+ """
79
+ The OAuth2 redirect URL, it is normally automatically handled by FastAPI.
80
+ """
81
+ ),
82
+ ] = None,
83
+ init_oauth: Annotated[
84
+ Optional[Dict[str, Any]],
85
+ Doc(
86
+ """
87
+ A dictionary with Swagger UI OAuth2 initialization configurations.
88
+ """
89
+ ),
90
+ ] = None,
91
+ swagger_ui_parameters: Annotated[
92
+ Optional[Dict[str, Any]],
93
+ Doc(
94
+ """
95
+ Configuration parameters for Swagger UI.
96
+
97
+ It defaults to [swagger_ui_default_parameters][fastapi.openapi.docs.swagger_ui_default_parameters].
98
+ """
99
+ ),
100
+ ] = None,
101
+ ) -> HTMLResponse:
102
+ """
103
+ Generate and return the HTML that loads Swagger UI for the interactive
104
+ API docs (normally served at `/docs`).
105
+
106
+ You would only call this function yourself if you needed to override some parts,
107
+ for example the URLs to use to load Swagger UI's JavaScript and CSS.
108
+
109
+ Read more about it in the
110
+ [FastAPI docs for Configure Swagger UI](https://fastapi.tiangolo.com/how-to/configure-swagger-ui/)
111
+ and the [FastAPI docs for Custom Docs UI Static Assets (Self-Hosting)](https://fastapi.tiangolo.com/how-to/custom-docs-ui-assets/).
112
+ """
113
+ current_swagger_ui_parameters = swagger_ui_default_parameters.copy()
114
+ if swagger_ui_parameters:
115
+ current_swagger_ui_parameters.update(swagger_ui_parameters)
116
+
117
+ html = f"""
118
+ <!DOCTYPE html>
119
+ <html>
120
+ <head>
121
+ <link type="text/css" rel="stylesheet" href="{swagger_css_url}">
122
+ <link rel="shortcut icon" href="{swagger_favicon_url}">
123
+ <title>{title}</title>
124
+ </head>
125
+ <body>
126
+ <div id="swagger-ui">
127
+ </div>
128
+ <script src="{swagger_js_url}"></script>
129
+ <!-- `SwaggerUIBundle` is now available on the page -->
130
+ <script>
131
+ const ui = SwaggerUIBundle({{
132
+ url: '{openapi_url}',
133
+ """
134
+
135
+ for key, value in current_swagger_ui_parameters.items():
136
+ html += f"{json.dumps(key)}: {json.dumps(jsonable_encoder(value))},\n"
137
+
138
+ if oauth2_redirect_url:
139
+ html += f"oauth2RedirectUrl: window.location.origin + '{oauth2_redirect_url}',"
140
+
141
+ html += """
142
+ presets: [
143
+ SwaggerUIBundle.presets.apis,
144
+ SwaggerUIBundle.SwaggerUIStandalonePreset
145
+ ],
146
+ })"""
147
+
148
+ if init_oauth:
149
+ html += f"""
150
+ ui.initOAuth({json.dumps(jsonable_encoder(init_oauth))})
151
+ """
152
+
153
+ html += """
154
+ </script>
155
+ </body>
156
+ </html>
157
+ """
158
+ return HTMLResponse(html)
159
+
160
+
161
+ def get_redoc_html(
162
+ *,
163
+ openapi_url: Annotated[
164
+ str,
165
+ Doc(
166
+ """
167
+ The OpenAPI URL that ReDoc should load and use.
168
+
169
+ This is normally done automatically by FastAPI using the default URL
170
+ `/openapi.json`.
171
+ """
172
+ ),
173
+ ],
174
+ title: Annotated[
175
+ str,
176
+ Doc(
177
+ """
178
+ The HTML `<title>` content, normally shown in the browser tab.
179
+ """
180
+ ),
181
+ ],
182
+ redoc_js_url: Annotated[
183
+ str,
184
+ Doc(
185
+ """
186
+ The URL to use to load the ReDoc JavaScript.
187
+
188
+ It is normally set to a CDN URL.
189
+ """
190
+ ),
191
+ ] = "https://cdn.jsdelivr.net/npm/redoc@next/bundles/redoc.standalone.js",
192
+ redoc_favicon_url: Annotated[
193
+ str,
194
+ Doc(
195
+ """
196
+ The URL of the favicon to use. It is normally shown in the browser tab.
197
+ """
198
+ ),
199
+ ] = "https://fastapi.tiangolo.com/img/favicon.png",
200
+ with_google_fonts: Annotated[
201
+ bool,
202
+ Doc(
203
+ """
204
+ Load and use Google Fonts.
205
+ """
206
+ ),
207
+ ] = True,
208
+ ) -> HTMLResponse:
209
+ """
210
+ Generate and return the HTML response that loads ReDoc for the alternative
211
+ API docs (normally served at `/redoc`).
212
+
213
+ You would only call this function yourself if you needed to override some parts,
214
+ for example the URLs to use to load ReDoc's JavaScript and CSS.
215
+
216
+ Read more about it in the
217
+ [FastAPI docs for Custom Docs UI Static Assets (Self-Hosting)](https://fastapi.tiangolo.com/how-to/custom-docs-ui-assets/).
218
+ """
219
+ html = f"""
220
+ <!DOCTYPE html>
221
+ <html>
222
+ <head>
223
+ <title>{title}</title>
224
+ <!-- needed for adaptive design -->
225
+ <meta charset="utf-8"/>
226
+ <meta name="viewport" content="width=device-width, initial-scale=1">
227
+ """
228
+ if with_google_fonts:
229
+ html += """
230
+ <link href="https://fonts.googleapis.com/css?family=Montserrat:300,400,700|Roboto:300,400,700" rel="stylesheet">
231
+ """
232
+ html += f"""
233
+ <link rel="shortcut icon" href="{redoc_favicon_url}">
234
+ <!--
235
+ ReDoc doesn't change outer page styles
236
+ -->
237
+ <style>
238
+ body {{
239
+ margin: 0;
240
+ padding: 0;
241
+ }}
242
+ </style>
243
+ </head>
244
+ <body>
245
+ <noscript>
246
+ ReDoc requires Javascript to function. Please enable it to browse the documentation.
247
+ </noscript>
248
+ <redoc spec-url="{openapi_url}"></redoc>
249
+ <script src="{redoc_js_url}"> </script>
250
+ </body>
251
+ </html>
252
+ """
253
+ return HTMLResponse(html)
254
+
255
+
256
+ def get_swagger_ui_oauth2_redirect_html() -> HTMLResponse:
257
+ """
258
+ Generate the HTML response with the OAuth2 redirection for Swagger UI.
259
+
260
+ You normally don't need to use or change this.
261
+ """
262
+ # copied from https://github.com/swagger-api/swagger-ui/blob/v4.14.0/dist/oauth2-redirect.html
263
+ html = """
264
+ <!doctype html>
265
+ <html lang="en-US">
266
+ <head>
267
+ <title>Swagger UI: OAuth2 Redirect</title>
268
+ </head>
269
+ <body>
270
+ <script>
271
+ 'use strict';
272
+ function run () {
273
+ var oauth2 = window.opener.swaggerUIRedirectOauth2;
274
+ var sentState = oauth2.state;
275
+ var redirectUrl = oauth2.redirectUrl;
276
+ var isValid, qp, arr;
277
+
278
+ if (/code|token|error/.test(window.location.hash)) {
279
+ qp = window.location.hash.substring(1).replace('?', '&');
280
+ } else {
281
+ qp = location.search.substring(1);
282
+ }
283
+
284
+ arr = qp.split("&");
285
+ arr.forEach(function (v,i,_arr) { _arr[i] = '"' + v.replace('=', '":"') + '"';});
286
+ qp = qp ? JSON.parse('{' + arr.join() + '}',
287
+ function (key, value) {
288
+ return key === "" ? value : decodeURIComponent(value);
289
+ }
290
+ ) : {};
291
+
292
+ isValid = qp.state === sentState;
293
+
294
+ if ((
295
+ oauth2.auth.schema.get("flow") === "accessCode" ||
296
+ oauth2.auth.schema.get("flow") === "authorizationCode" ||
297
+ oauth2.auth.schema.get("flow") === "authorization_code"
298
+ ) && !oauth2.auth.code) {
299
+ if (!isValid) {
300
+ oauth2.errCb({
301
+ authId: oauth2.auth.name,
302
+ source: "auth",
303
+ level: "warning",
304
+ message: "Authorization may be unsafe, passed state was changed in server. The passed state wasn't returned from auth server."
305
+ });
306
+ }
307
+
308
+ if (qp.code) {
309
+ delete oauth2.state;
310
+ oauth2.auth.code = qp.code;
311
+ oauth2.callback({auth: oauth2.auth, redirectUrl: redirectUrl});
312
+ } else {
313
+ let oauthErrorMsg;
314
+ if (qp.error) {
315
+ oauthErrorMsg = "["+qp.error+"]: " +
316
+ (qp.error_description ? qp.error_description+ ". " : "no accessCode received from the server. ") +
317
+ (qp.error_uri ? "More info: "+qp.error_uri : "");
318
+ }
319
+
320
+ oauth2.errCb({
321
+ authId: oauth2.auth.name,
322
+ source: "auth",
323
+ level: "error",
324
+ message: oauthErrorMsg || "[Authorization failed]: no accessCode received from the server."
325
+ });
326
+ }
327
+ } else {
328
+ oauth2.callback({auth: oauth2.auth, token: qp, isValid: isValid, redirectUrl: redirectUrl});
329
+ }
330
+ window.close();
331
+ }
332
+
333
+ if (document.readyState !== 'loading') {
334
+ run();
335
+ } else {
336
+ document.addEventListener('DOMContentLoaded', function () {
337
+ run();
338
+ });
339
+ }
340
+ </script>
341
+ </body>
342
+ </html>
343
+ """
344
+ return HTMLResponse(content=html)
.venv/lib/python3.11/site-packages/fastapi/openapi/models.py ADDED
@@ -0,0 +1,445 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from enum import Enum
2
+ from typing import Any, Callable, Dict, Iterable, List, Optional, Set, Type, Union
3
+
4
+ from fastapi._compat import (
5
+ PYDANTIC_V2,
6
+ CoreSchema,
7
+ GetJsonSchemaHandler,
8
+ JsonSchemaValue,
9
+ _model_rebuild,
10
+ with_info_plain_validator_function,
11
+ )
12
+ from fastapi.logger import logger
13
+ from pydantic import AnyUrl, BaseModel, Field
14
+ from typing_extensions import Annotated, Literal, TypedDict
15
+ from typing_extensions import deprecated as typing_deprecated
16
+
17
+ try:
18
+ import email_validator
19
+
20
+ assert email_validator # make autoflake ignore the unused import
21
+ from pydantic import EmailStr
22
+ except ImportError: # pragma: no cover
23
+
24
+ class EmailStr(str): # type: ignore
25
+ @classmethod
26
+ def __get_validators__(cls) -> Iterable[Callable[..., Any]]:
27
+ yield cls.validate
28
+
29
+ @classmethod
30
+ def validate(cls, v: Any) -> str:
31
+ logger.warning(
32
+ "email-validator not installed, email fields will be treated as str.\n"
33
+ "To install, run: pip install email-validator"
34
+ )
35
+ return str(v)
36
+
37
+ @classmethod
38
+ def _validate(cls, __input_value: Any, _: Any) -> str:
39
+ logger.warning(
40
+ "email-validator not installed, email fields will be treated as str.\n"
41
+ "To install, run: pip install email-validator"
42
+ )
43
+ return str(__input_value)
44
+
45
+ @classmethod
46
+ def __get_pydantic_json_schema__(
47
+ cls, core_schema: CoreSchema, handler: GetJsonSchemaHandler
48
+ ) -> JsonSchemaValue:
49
+ return {"type": "string", "format": "email"}
50
+
51
+ @classmethod
52
+ def __get_pydantic_core_schema__(
53
+ cls, source: Type[Any], handler: Callable[[Any], CoreSchema]
54
+ ) -> CoreSchema:
55
+ return with_info_plain_validator_function(cls._validate)
56
+
57
+
58
+ class BaseModelWithConfig(BaseModel):
59
+ if PYDANTIC_V2:
60
+ model_config = {"extra": "allow"}
61
+
62
+ else:
63
+
64
+ class Config:
65
+ extra = "allow"
66
+
67
+
68
+ class Contact(BaseModelWithConfig):
69
+ name: Optional[str] = None
70
+ url: Optional[AnyUrl] = None
71
+ email: Optional[EmailStr] = None
72
+
73
+
74
+ class License(BaseModelWithConfig):
75
+ name: str
76
+ identifier: Optional[str] = None
77
+ url: Optional[AnyUrl] = None
78
+
79
+
80
+ class Info(BaseModelWithConfig):
81
+ title: str
82
+ summary: Optional[str] = None
83
+ description: Optional[str] = None
84
+ termsOfService: Optional[str] = None
85
+ contact: Optional[Contact] = None
86
+ license: Optional[License] = None
87
+ version: str
88
+
89
+
90
+ class ServerVariable(BaseModelWithConfig):
91
+ enum: Annotated[Optional[List[str]], Field(min_length=1)] = None
92
+ default: str
93
+ description: Optional[str] = None
94
+
95
+
96
+ class Server(BaseModelWithConfig):
97
+ url: Union[AnyUrl, str]
98
+ description: Optional[str] = None
99
+ variables: Optional[Dict[str, ServerVariable]] = None
100
+
101
+
102
+ class Reference(BaseModel):
103
+ ref: str = Field(alias="$ref")
104
+
105
+
106
+ class Discriminator(BaseModel):
107
+ propertyName: str
108
+ mapping: Optional[Dict[str, str]] = None
109
+
110
+
111
+ class XML(BaseModelWithConfig):
112
+ name: Optional[str] = None
113
+ namespace: Optional[str] = None
114
+ prefix: Optional[str] = None
115
+ attribute: Optional[bool] = None
116
+ wrapped: Optional[bool] = None
117
+
118
+
119
+ class ExternalDocumentation(BaseModelWithConfig):
120
+ description: Optional[str] = None
121
+ url: AnyUrl
122
+
123
+
124
+ class Schema(BaseModelWithConfig):
125
+ # Ref: JSON Schema 2020-12: https://json-schema.org/draft/2020-12/json-schema-core.html#name-the-json-schema-core-vocabu
126
+ # Core Vocabulary
127
+ schema_: Optional[str] = Field(default=None, alias="$schema")
128
+ vocabulary: Optional[str] = Field(default=None, alias="$vocabulary")
129
+ id: Optional[str] = Field(default=None, alias="$id")
130
+ anchor: Optional[str] = Field(default=None, alias="$anchor")
131
+ dynamicAnchor: Optional[str] = Field(default=None, alias="$dynamicAnchor")
132
+ ref: Optional[str] = Field(default=None, alias="$ref")
133
+ dynamicRef: Optional[str] = Field(default=None, alias="$dynamicRef")
134
+ defs: Optional[Dict[str, "SchemaOrBool"]] = Field(default=None, alias="$defs")
135
+ comment: Optional[str] = Field(default=None, alias="$comment")
136
+ # Ref: JSON Schema 2020-12: https://json-schema.org/draft/2020-12/json-schema-core.html#name-a-vocabulary-for-applying-s
137
+ # A Vocabulary for Applying Subschemas
138
+ allOf: Optional[List["SchemaOrBool"]] = None
139
+ anyOf: Optional[List["SchemaOrBool"]] = None
140
+ oneOf: Optional[List["SchemaOrBool"]] = None
141
+ not_: Optional["SchemaOrBool"] = Field(default=None, alias="not")
142
+ if_: Optional["SchemaOrBool"] = Field(default=None, alias="if")
143
+ then: Optional["SchemaOrBool"] = None
144
+ else_: Optional["SchemaOrBool"] = Field(default=None, alias="else")
145
+ dependentSchemas: Optional[Dict[str, "SchemaOrBool"]] = None
146
+ prefixItems: Optional[List["SchemaOrBool"]] = None
147
+ # TODO: uncomment and remove below when deprecating Pydantic v1
148
+ # It generales a list of schemas for tuples, before prefixItems was available
149
+ # items: Optional["SchemaOrBool"] = None
150
+ items: Optional[Union["SchemaOrBool", List["SchemaOrBool"]]] = None
151
+ contains: Optional["SchemaOrBool"] = None
152
+ properties: Optional[Dict[str, "SchemaOrBool"]] = None
153
+ patternProperties: Optional[Dict[str, "SchemaOrBool"]] = None
154
+ additionalProperties: Optional["SchemaOrBool"] = None
155
+ propertyNames: Optional["SchemaOrBool"] = None
156
+ unevaluatedItems: Optional["SchemaOrBool"] = None
157
+ unevaluatedProperties: Optional["SchemaOrBool"] = None
158
+ # Ref: JSON Schema Validation 2020-12: https://json-schema.org/draft/2020-12/json-schema-validation.html#name-a-vocabulary-for-structural
159
+ # A Vocabulary for Structural Validation
160
+ type: Optional[str] = None
161
+ enum: Optional[List[Any]] = None
162
+ const: Optional[Any] = None
163
+ multipleOf: Optional[float] = Field(default=None, gt=0)
164
+ maximum: Optional[float] = None
165
+ exclusiveMaximum: Optional[float] = None
166
+ minimum: Optional[float] = None
167
+ exclusiveMinimum: Optional[float] = None
168
+ maxLength: Optional[int] = Field(default=None, ge=0)
169
+ minLength: Optional[int] = Field(default=None, ge=0)
170
+ pattern: Optional[str] = None
171
+ maxItems: Optional[int] = Field(default=None, ge=0)
172
+ minItems: Optional[int] = Field(default=None, ge=0)
173
+ uniqueItems: Optional[bool] = None
174
+ maxContains: Optional[int] = Field(default=None, ge=0)
175
+ minContains: Optional[int] = Field(default=None, ge=0)
176
+ maxProperties: Optional[int] = Field(default=None, ge=0)
177
+ minProperties: Optional[int] = Field(default=None, ge=0)
178
+ required: Optional[List[str]] = None
179
+ dependentRequired: Optional[Dict[str, Set[str]]] = None
180
+ # Ref: JSON Schema Validation 2020-12: https://json-schema.org/draft/2020-12/json-schema-validation.html#name-vocabularies-for-semantic-c
181
+ # Vocabularies for Semantic Content With "format"
182
+ format: Optional[str] = None
183
+ # Ref: JSON Schema Validation 2020-12: https://json-schema.org/draft/2020-12/json-schema-validation.html#name-a-vocabulary-for-the-conten
184
+ # A Vocabulary for the Contents of String-Encoded Data
185
+ contentEncoding: Optional[str] = None
186
+ contentMediaType: Optional[str] = None
187
+ contentSchema: Optional["SchemaOrBool"] = None
188
+ # Ref: JSON Schema Validation 2020-12: https://json-schema.org/draft/2020-12/json-schema-validation.html#name-a-vocabulary-for-basic-meta
189
+ # A Vocabulary for Basic Meta-Data Annotations
190
+ title: Optional[str] = None
191
+ description: Optional[str] = None
192
+ default: Optional[Any] = None
193
+ deprecated: Optional[bool] = None
194
+ readOnly: Optional[bool] = None
195
+ writeOnly: Optional[bool] = None
196
+ examples: Optional[List[Any]] = None
197
+ # Ref: OpenAPI 3.1.0: https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.1.0.md#schema-object
198
+ # Schema Object
199
+ discriminator: Optional[Discriminator] = None
200
+ xml: Optional[XML] = None
201
+ externalDocs: Optional[ExternalDocumentation] = None
202
+ example: Annotated[
203
+ Optional[Any],
204
+ typing_deprecated(
205
+ "Deprecated in OpenAPI 3.1.0 that now uses JSON Schema 2020-12, "
206
+ "although still supported. Use examples instead."
207
+ ),
208
+ ] = None
209
+
210
+
211
+ # Ref: https://json-schema.org/draft/2020-12/json-schema-core.html#name-json-schema-documents
212
+ # A JSON Schema MUST be an object or a boolean.
213
+ SchemaOrBool = Union[Schema, bool]
214
+
215
+
216
+ class Example(TypedDict, total=False):
217
+ summary: Optional[str]
218
+ description: Optional[str]
219
+ value: Optional[Any]
220
+ externalValue: Optional[AnyUrl]
221
+
222
+ if PYDANTIC_V2: # type: ignore [misc]
223
+ __pydantic_config__ = {"extra": "allow"}
224
+
225
+ else:
226
+
227
+ class Config:
228
+ extra = "allow"
229
+
230
+
231
+ class ParameterInType(Enum):
232
+ query = "query"
233
+ header = "header"
234
+ path = "path"
235
+ cookie = "cookie"
236
+
237
+
238
+ class Encoding(BaseModelWithConfig):
239
+ contentType: Optional[str] = None
240
+ headers: Optional[Dict[str, Union["Header", Reference]]] = None
241
+ style: Optional[str] = None
242
+ explode: Optional[bool] = None
243
+ allowReserved: Optional[bool] = None
244
+
245
+
246
+ class MediaType(BaseModelWithConfig):
247
+ schema_: Optional[Union[Schema, Reference]] = Field(default=None, alias="schema")
248
+ example: Optional[Any] = None
249
+ examples: Optional[Dict[str, Union[Example, Reference]]] = None
250
+ encoding: Optional[Dict[str, Encoding]] = None
251
+
252
+
253
+ class ParameterBase(BaseModelWithConfig):
254
+ description: Optional[str] = None
255
+ required: Optional[bool] = None
256
+ deprecated: Optional[bool] = None
257
+ # Serialization rules for simple scenarios
258
+ style: Optional[str] = None
259
+ explode: Optional[bool] = None
260
+ allowReserved: Optional[bool] = None
261
+ schema_: Optional[Union[Schema, Reference]] = Field(default=None, alias="schema")
262
+ example: Optional[Any] = None
263
+ examples: Optional[Dict[str, Union[Example, Reference]]] = None
264
+ # Serialization rules for more complex scenarios
265
+ content: Optional[Dict[str, MediaType]] = None
266
+
267
+
268
+ class Parameter(ParameterBase):
269
+ name: str
270
+ in_: ParameterInType = Field(alias="in")
271
+
272
+
273
+ class Header(ParameterBase):
274
+ pass
275
+
276
+
277
+ class RequestBody(BaseModelWithConfig):
278
+ description: Optional[str] = None
279
+ content: Dict[str, MediaType]
280
+ required: Optional[bool] = None
281
+
282
+
283
+ class Link(BaseModelWithConfig):
284
+ operationRef: Optional[str] = None
285
+ operationId: Optional[str] = None
286
+ parameters: Optional[Dict[str, Union[Any, str]]] = None
287
+ requestBody: Optional[Union[Any, str]] = None
288
+ description: Optional[str] = None
289
+ server: Optional[Server] = None
290
+
291
+
292
+ class Response(BaseModelWithConfig):
293
+ description: str
294
+ headers: Optional[Dict[str, Union[Header, Reference]]] = None
295
+ content: Optional[Dict[str, MediaType]] = None
296
+ links: Optional[Dict[str, Union[Link, Reference]]] = None
297
+
298
+
299
+ class Operation(BaseModelWithConfig):
300
+ tags: Optional[List[str]] = None
301
+ summary: Optional[str] = None
302
+ description: Optional[str] = None
303
+ externalDocs: Optional[ExternalDocumentation] = None
304
+ operationId: Optional[str] = None
305
+ parameters: Optional[List[Union[Parameter, Reference]]] = None
306
+ requestBody: Optional[Union[RequestBody, Reference]] = None
307
+ # Using Any for Specification Extensions
308
+ responses: Optional[Dict[str, Union[Response, Any]]] = None
309
+ callbacks: Optional[Dict[str, Union[Dict[str, "PathItem"], Reference]]] = None
310
+ deprecated: Optional[bool] = None
311
+ security: Optional[List[Dict[str, List[str]]]] = None
312
+ servers: Optional[List[Server]] = None
313
+
314
+
315
+ class PathItem(BaseModelWithConfig):
316
+ ref: Optional[str] = Field(default=None, alias="$ref")
317
+ summary: Optional[str] = None
318
+ description: Optional[str] = None
319
+ get: Optional[Operation] = None
320
+ put: Optional[Operation] = None
321
+ post: Optional[Operation] = None
322
+ delete: Optional[Operation] = None
323
+ options: Optional[Operation] = None
324
+ head: Optional[Operation] = None
325
+ patch: Optional[Operation] = None
326
+ trace: Optional[Operation] = None
327
+ servers: Optional[List[Server]] = None
328
+ parameters: Optional[List[Union[Parameter, Reference]]] = None
329
+
330
+
331
+ class SecuritySchemeType(Enum):
332
+ apiKey = "apiKey"
333
+ http = "http"
334
+ oauth2 = "oauth2"
335
+ openIdConnect = "openIdConnect"
336
+
337
+
338
+ class SecurityBase(BaseModelWithConfig):
339
+ type_: SecuritySchemeType = Field(alias="type")
340
+ description: Optional[str] = None
341
+
342
+
343
+ class APIKeyIn(Enum):
344
+ query = "query"
345
+ header = "header"
346
+ cookie = "cookie"
347
+
348
+
349
+ class APIKey(SecurityBase):
350
+ type_: SecuritySchemeType = Field(default=SecuritySchemeType.apiKey, alias="type")
351
+ in_: APIKeyIn = Field(alias="in")
352
+ name: str
353
+
354
+
355
+ class HTTPBase(SecurityBase):
356
+ type_: SecuritySchemeType = Field(default=SecuritySchemeType.http, alias="type")
357
+ scheme: str
358
+
359
+
360
+ class HTTPBearer(HTTPBase):
361
+ scheme: Literal["bearer"] = "bearer"
362
+ bearerFormat: Optional[str] = None
363
+
364
+
365
+ class OAuthFlow(BaseModelWithConfig):
366
+ refreshUrl: Optional[str] = None
367
+ scopes: Dict[str, str] = {}
368
+
369
+
370
+ class OAuthFlowImplicit(OAuthFlow):
371
+ authorizationUrl: str
372
+
373
+
374
+ class OAuthFlowPassword(OAuthFlow):
375
+ tokenUrl: str
376
+
377
+
378
+ class OAuthFlowClientCredentials(OAuthFlow):
379
+ tokenUrl: str
380
+
381
+
382
+ class OAuthFlowAuthorizationCode(OAuthFlow):
383
+ authorizationUrl: str
384
+ tokenUrl: str
385
+
386
+
387
+ class OAuthFlows(BaseModelWithConfig):
388
+ implicit: Optional[OAuthFlowImplicit] = None
389
+ password: Optional[OAuthFlowPassword] = None
390
+ clientCredentials: Optional[OAuthFlowClientCredentials] = None
391
+ authorizationCode: Optional[OAuthFlowAuthorizationCode] = None
392
+
393
+
394
+ class OAuth2(SecurityBase):
395
+ type_: SecuritySchemeType = Field(default=SecuritySchemeType.oauth2, alias="type")
396
+ flows: OAuthFlows
397
+
398
+
399
+ class OpenIdConnect(SecurityBase):
400
+ type_: SecuritySchemeType = Field(
401
+ default=SecuritySchemeType.openIdConnect, alias="type"
402
+ )
403
+ openIdConnectUrl: str
404
+
405
+
406
+ SecurityScheme = Union[APIKey, HTTPBase, OAuth2, OpenIdConnect, HTTPBearer]
407
+
408
+
409
+ class Components(BaseModelWithConfig):
410
+ schemas: Optional[Dict[str, Union[Schema, Reference]]] = None
411
+ responses: Optional[Dict[str, Union[Response, Reference]]] = None
412
+ parameters: Optional[Dict[str, Union[Parameter, Reference]]] = None
413
+ examples: Optional[Dict[str, Union[Example, Reference]]] = None
414
+ requestBodies: Optional[Dict[str, Union[RequestBody, Reference]]] = None
415
+ headers: Optional[Dict[str, Union[Header, Reference]]] = None
416
+ securitySchemes: Optional[Dict[str, Union[SecurityScheme, Reference]]] = None
417
+ links: Optional[Dict[str, Union[Link, Reference]]] = None
418
+ # Using Any for Specification Extensions
419
+ callbacks: Optional[Dict[str, Union[Dict[str, PathItem], Reference, Any]]] = None
420
+ pathItems: Optional[Dict[str, Union[PathItem, Reference]]] = None
421
+
422
+
423
+ class Tag(BaseModelWithConfig):
424
+ name: str
425
+ description: Optional[str] = None
426
+ externalDocs: Optional[ExternalDocumentation] = None
427
+
428
+
429
+ class OpenAPI(BaseModelWithConfig):
430
+ openapi: str
431
+ info: Info
432
+ jsonSchemaDialect: Optional[str] = None
433
+ servers: Optional[List[Server]] = None
434
+ # Using Any for Specification Extensions
435
+ paths: Optional[Dict[str, Union[PathItem, Any]]] = None
436
+ webhooks: Optional[Dict[str, Union[PathItem, Reference]]] = None
437
+ components: Optional[Components] = None
438
+ security: Optional[List[Dict[str, List[str]]]] = None
439
+ tags: Optional[List[Tag]] = None
440
+ externalDocs: Optional[ExternalDocumentation] = None
441
+
442
+
443
+ _model_rebuild(Schema)
444
+ _model_rebuild(Operation)
445
+ _model_rebuild(Encoding)
.venv/lib/python3.11/site-packages/fastapi/openapi/utils.py ADDED
@@ -0,0 +1,548 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import http.client
2
+ import inspect
3
+ import warnings
4
+ from typing import Any, Dict, List, Optional, Sequence, Set, Tuple, Type, Union, cast
5
+
6
+ from fastapi import routing
7
+ from fastapi._compat import (
8
+ GenerateJsonSchema,
9
+ JsonSchemaValue,
10
+ ModelField,
11
+ Undefined,
12
+ get_compat_model_name_map,
13
+ get_definitions,
14
+ get_schema_from_model_field,
15
+ lenient_issubclass,
16
+ )
17
+ from fastapi.datastructures import DefaultPlaceholder
18
+ from fastapi.dependencies.models import Dependant
19
+ from fastapi.dependencies.utils import (
20
+ _get_flat_fields_from_params,
21
+ get_flat_dependant,
22
+ get_flat_params,
23
+ )
24
+ from fastapi.encoders import jsonable_encoder
25
+ from fastapi.openapi.constants import METHODS_WITH_BODY, REF_PREFIX, REF_TEMPLATE
26
+ from fastapi.openapi.models import OpenAPI
27
+ from fastapi.params import Body, ParamTypes
28
+ from fastapi.responses import Response
29
+ from fastapi.types import ModelNameMap
30
+ from fastapi.utils import (
31
+ deep_dict_update,
32
+ generate_operation_id_for_path,
33
+ is_body_allowed_for_status_code,
34
+ )
35
+ from starlette.responses import JSONResponse
36
+ from starlette.routing import BaseRoute
37
+ from starlette.status import HTTP_422_UNPROCESSABLE_ENTITY
38
+ from typing_extensions import Literal
39
+
40
+ validation_error_definition = {
41
+ "title": "ValidationError",
42
+ "type": "object",
43
+ "properties": {
44
+ "loc": {
45
+ "title": "Location",
46
+ "type": "array",
47
+ "items": {"anyOf": [{"type": "string"}, {"type": "integer"}]},
48
+ },
49
+ "msg": {"title": "Message", "type": "string"},
50
+ "type": {"title": "Error Type", "type": "string"},
51
+ },
52
+ "required": ["loc", "msg", "type"],
53
+ }
54
+
55
+ validation_error_response_definition = {
56
+ "title": "HTTPValidationError",
57
+ "type": "object",
58
+ "properties": {
59
+ "detail": {
60
+ "title": "Detail",
61
+ "type": "array",
62
+ "items": {"$ref": REF_PREFIX + "ValidationError"},
63
+ }
64
+ },
65
+ }
66
+
67
+ status_code_ranges: Dict[str, str] = {
68
+ "1XX": "Information",
69
+ "2XX": "Success",
70
+ "3XX": "Redirection",
71
+ "4XX": "Client Error",
72
+ "5XX": "Server Error",
73
+ "DEFAULT": "Default Response",
74
+ }
75
+
76
+
77
+ def get_openapi_security_definitions(
78
+ flat_dependant: Dependant,
79
+ ) -> Tuple[Dict[str, Any], List[Dict[str, Any]]]:
80
+ security_definitions = {}
81
+ operation_security = []
82
+ for security_requirement in flat_dependant.security_requirements:
83
+ security_definition = jsonable_encoder(
84
+ security_requirement.security_scheme.model,
85
+ by_alias=True,
86
+ exclude_none=True,
87
+ )
88
+ security_name = security_requirement.security_scheme.scheme_name
89
+ security_definitions[security_name] = security_definition
90
+ operation_security.append({security_name: security_requirement.scopes})
91
+ return security_definitions, operation_security
92
+
93
+
94
+ def _get_openapi_operation_parameters(
95
+ *,
96
+ dependant: Dependant,
97
+ schema_generator: GenerateJsonSchema,
98
+ model_name_map: ModelNameMap,
99
+ field_mapping: Dict[
100
+ Tuple[ModelField, Literal["validation", "serialization"]], JsonSchemaValue
101
+ ],
102
+ separate_input_output_schemas: bool = True,
103
+ ) -> List[Dict[str, Any]]:
104
+ parameters = []
105
+ flat_dependant = get_flat_dependant(dependant, skip_repeats=True)
106
+ path_params = _get_flat_fields_from_params(flat_dependant.path_params)
107
+ query_params = _get_flat_fields_from_params(flat_dependant.query_params)
108
+ header_params = _get_flat_fields_from_params(flat_dependant.header_params)
109
+ cookie_params = _get_flat_fields_from_params(flat_dependant.cookie_params)
110
+ parameter_groups = [
111
+ (ParamTypes.path, path_params),
112
+ (ParamTypes.query, query_params),
113
+ (ParamTypes.header, header_params),
114
+ (ParamTypes.cookie, cookie_params),
115
+ ]
116
+ for param_type, param_group in parameter_groups:
117
+ for param in param_group:
118
+ field_info = param.field_info
119
+ # field_info = cast(Param, field_info)
120
+ if not getattr(field_info, "include_in_schema", True):
121
+ continue
122
+ param_schema = get_schema_from_model_field(
123
+ field=param,
124
+ schema_generator=schema_generator,
125
+ model_name_map=model_name_map,
126
+ field_mapping=field_mapping,
127
+ separate_input_output_schemas=separate_input_output_schemas,
128
+ )
129
+ parameter = {
130
+ "name": param.alias,
131
+ "in": param_type.value,
132
+ "required": param.required,
133
+ "schema": param_schema,
134
+ }
135
+ if field_info.description:
136
+ parameter["description"] = field_info.description
137
+ openapi_examples = getattr(field_info, "openapi_examples", None)
138
+ example = getattr(field_info, "example", None)
139
+ if openapi_examples:
140
+ parameter["examples"] = jsonable_encoder(openapi_examples)
141
+ elif example != Undefined:
142
+ parameter["example"] = jsonable_encoder(example)
143
+ if getattr(field_info, "deprecated", None):
144
+ parameter["deprecated"] = True
145
+ parameters.append(parameter)
146
+ return parameters
147
+
148
+
149
+ def get_openapi_operation_request_body(
150
+ *,
151
+ body_field: Optional[ModelField],
152
+ schema_generator: GenerateJsonSchema,
153
+ model_name_map: ModelNameMap,
154
+ field_mapping: Dict[
155
+ Tuple[ModelField, Literal["validation", "serialization"]], JsonSchemaValue
156
+ ],
157
+ separate_input_output_schemas: bool = True,
158
+ ) -> Optional[Dict[str, Any]]:
159
+ if not body_field:
160
+ return None
161
+ assert isinstance(body_field, ModelField)
162
+ body_schema = get_schema_from_model_field(
163
+ field=body_field,
164
+ schema_generator=schema_generator,
165
+ model_name_map=model_name_map,
166
+ field_mapping=field_mapping,
167
+ separate_input_output_schemas=separate_input_output_schemas,
168
+ )
169
+ field_info = cast(Body, body_field.field_info)
170
+ request_media_type = field_info.media_type
171
+ required = body_field.required
172
+ request_body_oai: Dict[str, Any] = {}
173
+ if required:
174
+ request_body_oai["required"] = required
175
+ request_media_content: Dict[str, Any] = {"schema": body_schema}
176
+ if field_info.openapi_examples:
177
+ request_media_content["examples"] = jsonable_encoder(
178
+ field_info.openapi_examples
179
+ )
180
+ elif field_info.example != Undefined:
181
+ request_media_content["example"] = jsonable_encoder(field_info.example)
182
+ request_body_oai["content"] = {request_media_type: request_media_content}
183
+ return request_body_oai
184
+
185
+
186
+ def generate_operation_id(
187
+ *, route: routing.APIRoute, method: str
188
+ ) -> str: # pragma: nocover
189
+ warnings.warn(
190
+ "fastapi.openapi.utils.generate_operation_id() was deprecated, "
191
+ "it is not used internally, and will be removed soon",
192
+ DeprecationWarning,
193
+ stacklevel=2,
194
+ )
195
+ if route.operation_id:
196
+ return route.operation_id
197
+ path: str = route.path_format
198
+ return generate_operation_id_for_path(name=route.name, path=path, method=method)
199
+
200
+
201
+ def generate_operation_summary(*, route: routing.APIRoute, method: str) -> str:
202
+ if route.summary:
203
+ return route.summary
204
+ return route.name.replace("_", " ").title()
205
+
206
+
207
+ def get_openapi_operation_metadata(
208
+ *, route: routing.APIRoute, method: str, operation_ids: Set[str]
209
+ ) -> Dict[str, Any]:
210
+ operation: Dict[str, Any] = {}
211
+ if route.tags:
212
+ operation["tags"] = route.tags
213
+ operation["summary"] = generate_operation_summary(route=route, method=method)
214
+ if route.description:
215
+ operation["description"] = route.description
216
+ operation_id = route.operation_id or route.unique_id
217
+ if operation_id in operation_ids:
218
+ message = (
219
+ f"Duplicate Operation ID {operation_id} for function "
220
+ + f"{route.endpoint.__name__}"
221
+ )
222
+ file_name = getattr(route.endpoint, "__globals__", {}).get("__file__")
223
+ if file_name:
224
+ message += f" at {file_name}"
225
+ warnings.warn(message, stacklevel=1)
226
+ operation_ids.add(operation_id)
227
+ operation["operationId"] = operation_id
228
+ if route.deprecated:
229
+ operation["deprecated"] = route.deprecated
230
+ return operation
231
+
232
+
233
+ def get_openapi_path(
234
+ *,
235
+ route: routing.APIRoute,
236
+ operation_ids: Set[str],
237
+ schema_generator: GenerateJsonSchema,
238
+ model_name_map: ModelNameMap,
239
+ field_mapping: Dict[
240
+ Tuple[ModelField, Literal["validation", "serialization"]], JsonSchemaValue
241
+ ],
242
+ separate_input_output_schemas: bool = True,
243
+ ) -> Tuple[Dict[str, Any], Dict[str, Any], Dict[str, Any]]:
244
+ path = {}
245
+ security_schemes: Dict[str, Any] = {}
246
+ definitions: Dict[str, Any] = {}
247
+ assert route.methods is not None, "Methods must be a list"
248
+ if isinstance(route.response_class, DefaultPlaceholder):
249
+ current_response_class: Type[Response] = route.response_class.value
250
+ else:
251
+ current_response_class = route.response_class
252
+ assert current_response_class, "A response class is needed to generate OpenAPI"
253
+ route_response_media_type: Optional[str] = current_response_class.media_type
254
+ if route.include_in_schema:
255
+ for method in route.methods:
256
+ operation = get_openapi_operation_metadata(
257
+ route=route, method=method, operation_ids=operation_ids
258
+ )
259
+ parameters: List[Dict[str, Any]] = []
260
+ flat_dependant = get_flat_dependant(route.dependant, skip_repeats=True)
261
+ security_definitions, operation_security = get_openapi_security_definitions(
262
+ flat_dependant=flat_dependant
263
+ )
264
+ if operation_security:
265
+ operation.setdefault("security", []).extend(operation_security)
266
+ if security_definitions:
267
+ security_schemes.update(security_definitions)
268
+ operation_parameters = _get_openapi_operation_parameters(
269
+ dependant=route.dependant,
270
+ schema_generator=schema_generator,
271
+ model_name_map=model_name_map,
272
+ field_mapping=field_mapping,
273
+ separate_input_output_schemas=separate_input_output_schemas,
274
+ )
275
+ parameters.extend(operation_parameters)
276
+ if parameters:
277
+ all_parameters = {
278
+ (param["in"], param["name"]): param for param in parameters
279
+ }
280
+ required_parameters = {
281
+ (param["in"], param["name"]): param
282
+ for param in parameters
283
+ if param.get("required")
284
+ }
285
+ # Make sure required definitions of the same parameter take precedence
286
+ # over non-required definitions
287
+ all_parameters.update(required_parameters)
288
+ operation["parameters"] = list(all_parameters.values())
289
+ if method in METHODS_WITH_BODY:
290
+ request_body_oai = get_openapi_operation_request_body(
291
+ body_field=route.body_field,
292
+ schema_generator=schema_generator,
293
+ model_name_map=model_name_map,
294
+ field_mapping=field_mapping,
295
+ separate_input_output_schemas=separate_input_output_schemas,
296
+ )
297
+ if request_body_oai:
298
+ operation["requestBody"] = request_body_oai
299
+ if route.callbacks:
300
+ callbacks = {}
301
+ for callback in route.callbacks:
302
+ if isinstance(callback, routing.APIRoute):
303
+ (
304
+ cb_path,
305
+ cb_security_schemes,
306
+ cb_definitions,
307
+ ) = get_openapi_path(
308
+ route=callback,
309
+ operation_ids=operation_ids,
310
+ schema_generator=schema_generator,
311
+ model_name_map=model_name_map,
312
+ field_mapping=field_mapping,
313
+ separate_input_output_schemas=separate_input_output_schemas,
314
+ )
315
+ callbacks[callback.name] = {callback.path: cb_path}
316
+ operation["callbacks"] = callbacks
317
+ if route.status_code is not None:
318
+ status_code = str(route.status_code)
319
+ else:
320
+ # It would probably make more sense for all response classes to have an
321
+ # explicit default status_code, and to extract it from them, instead of
322
+ # doing this inspection tricks, that would probably be in the future
323
+ # TODO: probably make status_code a default class attribute for all
324
+ # responses in Starlette
325
+ response_signature = inspect.signature(current_response_class.__init__)
326
+ status_code_param = response_signature.parameters.get("status_code")
327
+ if status_code_param is not None:
328
+ if isinstance(status_code_param.default, int):
329
+ status_code = str(status_code_param.default)
330
+ operation.setdefault("responses", {}).setdefault(status_code, {})[
331
+ "description"
332
+ ] = route.response_description
333
+ if route_response_media_type and is_body_allowed_for_status_code(
334
+ route.status_code
335
+ ):
336
+ response_schema = {"type": "string"}
337
+ if lenient_issubclass(current_response_class, JSONResponse):
338
+ if route.response_field:
339
+ response_schema = get_schema_from_model_field(
340
+ field=route.response_field,
341
+ schema_generator=schema_generator,
342
+ model_name_map=model_name_map,
343
+ field_mapping=field_mapping,
344
+ separate_input_output_schemas=separate_input_output_schemas,
345
+ )
346
+ else:
347
+ response_schema = {}
348
+ operation.setdefault("responses", {}).setdefault(
349
+ status_code, {}
350
+ ).setdefault("content", {}).setdefault(route_response_media_type, {})[
351
+ "schema"
352
+ ] = response_schema
353
+ if route.responses:
354
+ operation_responses = operation.setdefault("responses", {})
355
+ for (
356
+ additional_status_code,
357
+ additional_response,
358
+ ) in route.responses.items():
359
+ process_response = additional_response.copy()
360
+ process_response.pop("model", None)
361
+ status_code_key = str(additional_status_code).upper()
362
+ if status_code_key == "DEFAULT":
363
+ status_code_key = "default"
364
+ openapi_response = operation_responses.setdefault(
365
+ status_code_key, {}
366
+ )
367
+ assert isinstance(
368
+ process_response, dict
369
+ ), "An additional response must be a dict"
370
+ field = route.response_fields.get(additional_status_code)
371
+ additional_field_schema: Optional[Dict[str, Any]] = None
372
+ if field:
373
+ additional_field_schema = get_schema_from_model_field(
374
+ field=field,
375
+ schema_generator=schema_generator,
376
+ model_name_map=model_name_map,
377
+ field_mapping=field_mapping,
378
+ separate_input_output_schemas=separate_input_output_schemas,
379
+ )
380
+ media_type = route_response_media_type or "application/json"
381
+ additional_schema = (
382
+ process_response.setdefault("content", {})
383
+ .setdefault(media_type, {})
384
+ .setdefault("schema", {})
385
+ )
386
+ deep_dict_update(additional_schema, additional_field_schema)
387
+ status_text: Optional[str] = status_code_ranges.get(
388
+ str(additional_status_code).upper()
389
+ ) or http.client.responses.get(int(additional_status_code))
390
+ description = (
391
+ process_response.get("description")
392
+ or openapi_response.get("description")
393
+ or status_text
394
+ or "Additional Response"
395
+ )
396
+ deep_dict_update(openapi_response, process_response)
397
+ openapi_response["description"] = description
398
+ http422 = str(HTTP_422_UNPROCESSABLE_ENTITY)
399
+ all_route_params = get_flat_params(route.dependant)
400
+ if (all_route_params or route.body_field) and not any(
401
+ status in operation["responses"]
402
+ for status in [http422, "4XX", "default"]
403
+ ):
404
+ operation["responses"][http422] = {
405
+ "description": "Validation Error",
406
+ "content": {
407
+ "application/json": {
408
+ "schema": {"$ref": REF_PREFIX + "HTTPValidationError"}
409
+ }
410
+ },
411
+ }
412
+ if "ValidationError" not in definitions:
413
+ definitions.update(
414
+ {
415
+ "ValidationError": validation_error_definition,
416
+ "HTTPValidationError": validation_error_response_definition,
417
+ }
418
+ )
419
+ if route.openapi_extra:
420
+ deep_dict_update(operation, route.openapi_extra)
421
+ path[method.lower()] = operation
422
+ return path, security_schemes, definitions
423
+
424
+
425
+ def get_fields_from_routes(
426
+ routes: Sequence[BaseRoute],
427
+ ) -> List[ModelField]:
428
+ body_fields_from_routes: List[ModelField] = []
429
+ responses_from_routes: List[ModelField] = []
430
+ request_fields_from_routes: List[ModelField] = []
431
+ callback_flat_models: List[ModelField] = []
432
+ for route in routes:
433
+ if getattr(route, "include_in_schema", None) and isinstance(
434
+ route, routing.APIRoute
435
+ ):
436
+ if route.body_field:
437
+ assert isinstance(
438
+ route.body_field, ModelField
439
+ ), "A request body must be a Pydantic Field"
440
+ body_fields_from_routes.append(route.body_field)
441
+ if route.response_field:
442
+ responses_from_routes.append(route.response_field)
443
+ if route.response_fields:
444
+ responses_from_routes.extend(route.response_fields.values())
445
+ if route.callbacks:
446
+ callback_flat_models.extend(get_fields_from_routes(route.callbacks))
447
+ params = get_flat_params(route.dependant)
448
+ request_fields_from_routes.extend(params)
449
+
450
+ flat_models = callback_flat_models + list(
451
+ body_fields_from_routes + responses_from_routes + request_fields_from_routes
452
+ )
453
+ return flat_models
454
+
455
+
456
+ def get_openapi(
457
+ *,
458
+ title: str,
459
+ version: str,
460
+ openapi_version: str = "3.1.0",
461
+ summary: Optional[str] = None,
462
+ description: Optional[str] = None,
463
+ routes: Sequence[BaseRoute],
464
+ webhooks: Optional[Sequence[BaseRoute]] = None,
465
+ tags: Optional[List[Dict[str, Any]]] = None,
466
+ servers: Optional[List[Dict[str, Union[str, Any]]]] = None,
467
+ terms_of_service: Optional[str] = None,
468
+ contact: Optional[Dict[str, Union[str, Any]]] = None,
469
+ license_info: Optional[Dict[str, Union[str, Any]]] = None,
470
+ separate_input_output_schemas: bool = True,
471
+ ) -> Dict[str, Any]:
472
+ info: Dict[str, Any] = {"title": title, "version": version}
473
+ if summary:
474
+ info["summary"] = summary
475
+ if description:
476
+ info["description"] = description
477
+ if terms_of_service:
478
+ info["termsOfService"] = terms_of_service
479
+ if contact:
480
+ info["contact"] = contact
481
+ if license_info:
482
+ info["license"] = license_info
483
+ output: Dict[str, Any] = {"openapi": openapi_version, "info": info}
484
+ if servers:
485
+ output["servers"] = servers
486
+ components: Dict[str, Dict[str, Any]] = {}
487
+ paths: Dict[str, Dict[str, Any]] = {}
488
+ webhook_paths: Dict[str, Dict[str, Any]] = {}
489
+ operation_ids: Set[str] = set()
490
+ all_fields = get_fields_from_routes(list(routes or []) + list(webhooks or []))
491
+ model_name_map = get_compat_model_name_map(all_fields)
492
+ schema_generator = GenerateJsonSchema(ref_template=REF_TEMPLATE)
493
+ field_mapping, definitions = get_definitions(
494
+ fields=all_fields,
495
+ schema_generator=schema_generator,
496
+ model_name_map=model_name_map,
497
+ separate_input_output_schemas=separate_input_output_schemas,
498
+ )
499
+ for route in routes or []:
500
+ if isinstance(route, routing.APIRoute):
501
+ result = get_openapi_path(
502
+ route=route,
503
+ operation_ids=operation_ids,
504
+ schema_generator=schema_generator,
505
+ model_name_map=model_name_map,
506
+ field_mapping=field_mapping,
507
+ separate_input_output_schemas=separate_input_output_schemas,
508
+ )
509
+ if result:
510
+ path, security_schemes, path_definitions = result
511
+ if path:
512
+ paths.setdefault(route.path_format, {}).update(path)
513
+ if security_schemes:
514
+ components.setdefault("securitySchemes", {}).update(
515
+ security_schemes
516
+ )
517
+ if path_definitions:
518
+ definitions.update(path_definitions)
519
+ for webhook in webhooks or []:
520
+ if isinstance(webhook, routing.APIRoute):
521
+ result = get_openapi_path(
522
+ route=webhook,
523
+ operation_ids=operation_ids,
524
+ schema_generator=schema_generator,
525
+ model_name_map=model_name_map,
526
+ field_mapping=field_mapping,
527
+ separate_input_output_schemas=separate_input_output_schemas,
528
+ )
529
+ if result:
530
+ path, security_schemes, path_definitions = result
531
+ if path:
532
+ webhook_paths.setdefault(webhook.path_format, {}).update(path)
533
+ if security_schemes:
534
+ components.setdefault("securitySchemes", {}).update(
535
+ security_schemes
536
+ )
537
+ if path_definitions:
538
+ definitions.update(path_definitions)
539
+ if definitions:
540
+ components["schemas"] = {k: definitions[k] for k in sorted(definitions)}
541
+ if components:
542
+ output["components"] = components
543
+ output["paths"] = paths
544
+ if webhook_paths:
545
+ output["webhooks"] = webhook_paths
546
+ if tags:
547
+ output["tags"] = tags
548
+ return jsonable_encoder(OpenAPI(**output), by_alias=True, exclude_none=True) # type: ignore
.venv/lib/python3.11/site-packages/fastapi/security/__init__.py ADDED
@@ -0,0 +1,15 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from .api_key import APIKeyCookie as APIKeyCookie
2
+ from .api_key import APIKeyHeader as APIKeyHeader
3
+ from .api_key import APIKeyQuery as APIKeyQuery
4
+ from .http import HTTPAuthorizationCredentials as HTTPAuthorizationCredentials
5
+ from .http import HTTPBasic as HTTPBasic
6
+ from .http import HTTPBasicCredentials as HTTPBasicCredentials
7
+ from .http import HTTPBearer as HTTPBearer
8
+ from .http import HTTPDigest as HTTPDigest
9
+ from .oauth2 import OAuth2 as OAuth2
10
+ from .oauth2 import OAuth2AuthorizationCodeBearer as OAuth2AuthorizationCodeBearer
11
+ from .oauth2 import OAuth2PasswordBearer as OAuth2PasswordBearer
12
+ from .oauth2 import OAuth2PasswordRequestForm as OAuth2PasswordRequestForm
13
+ from .oauth2 import OAuth2PasswordRequestFormStrict as OAuth2PasswordRequestFormStrict
14
+ from .oauth2 import SecurityScopes as SecurityScopes
15
+ from .open_id_connect_url import OpenIdConnect as OpenIdConnect
.venv/lib/python3.11/site-packages/fastapi/security/__pycache__/__init__.cpython-311.pyc ADDED
Binary file (1.07 kB). View file
 
.venv/lib/python3.11/site-packages/fastapi/security/__pycache__/api_key.cpython-311.pyc ADDED
Binary file (10.3 kB). View file
 
.venv/lib/python3.11/site-packages/fastapi/security/__pycache__/base.cpython-311.pyc ADDED
Binary file (588 Bytes). View file
 
.venv/lib/python3.11/site-packages/fastapi/security/__pycache__/http.cpython-311.pyc ADDED
Binary file (15.2 kB). View file
 
.venv/lib/python3.11/site-packages/fastapi/security/__pycache__/oauth2.cpython-311.pyc ADDED
Binary file (20.3 kB). View file
 
.venv/lib/python3.11/site-packages/fastapi/security/__pycache__/open_id_connect_url.cpython-311.pyc ADDED
Binary file (3.54 kB). View file
 
.venv/lib/python3.11/site-packages/fastapi/security/__pycache__/utils.cpython-311.pyc ADDED
Binary file (668 Bytes). View file
 
.venv/lib/python3.11/site-packages/fastapi/security/api_key.py ADDED
@@ -0,0 +1,288 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from typing import Optional
2
+
3
+ from fastapi.openapi.models import APIKey, APIKeyIn
4
+ from fastapi.security.base import SecurityBase
5
+ from starlette.exceptions import HTTPException
6
+ from starlette.requests import Request
7
+ from starlette.status import HTTP_403_FORBIDDEN
8
+ from typing_extensions import Annotated, Doc
9
+
10
+
11
+ class APIKeyBase(SecurityBase):
12
+ @staticmethod
13
+ def check_api_key(api_key: Optional[str], auto_error: bool) -> Optional[str]:
14
+ if not api_key:
15
+ if auto_error:
16
+ raise HTTPException(
17
+ status_code=HTTP_403_FORBIDDEN, detail="Not authenticated"
18
+ )
19
+ return None
20
+ return api_key
21
+
22
+
23
+ class APIKeyQuery(APIKeyBase):
24
+ """
25
+ API key authentication using a query parameter.
26
+
27
+ This defines the name of the query parameter that should be provided in the request
28
+ with the API key and integrates that into the OpenAPI documentation. It extracts
29
+ the key value sent in the query parameter automatically and provides it as the
30
+ dependency result. But it doesn't define how to send that API key to the client.
31
+
32
+ ## Usage
33
+
34
+ Create an instance object and use that object as the dependency in `Depends()`.
35
+
36
+ The dependency result will be a string containing the key value.
37
+
38
+ ## Example
39
+
40
+ ```python
41
+ from fastapi import Depends, FastAPI
42
+ from fastapi.security import APIKeyQuery
43
+
44
+ app = FastAPI()
45
+
46
+ query_scheme = APIKeyQuery(name="api_key")
47
+
48
+
49
+ @app.get("/items/")
50
+ async def read_items(api_key: str = Depends(query_scheme)):
51
+ return {"api_key": api_key}
52
+ ```
53
+ """
54
+
55
+ def __init__(
56
+ self,
57
+ *,
58
+ name: Annotated[
59
+ str,
60
+ Doc("Query parameter name."),
61
+ ],
62
+ scheme_name: Annotated[
63
+ Optional[str],
64
+ Doc(
65
+ """
66
+ Security scheme name.
67
+
68
+ It will be included in the generated OpenAPI (e.g. visible at `/docs`).
69
+ """
70
+ ),
71
+ ] = None,
72
+ description: Annotated[
73
+ Optional[str],
74
+ Doc(
75
+ """
76
+ Security scheme description.
77
+
78
+ It will be included in the generated OpenAPI (e.g. visible at `/docs`).
79
+ """
80
+ ),
81
+ ] = None,
82
+ auto_error: Annotated[
83
+ bool,
84
+ Doc(
85
+ """
86
+ By default, if the query parameter is not provided, `APIKeyQuery` will
87
+ automatically cancel the request and send the client an error.
88
+
89
+ If `auto_error` is set to `False`, when the query parameter is not
90
+ available, instead of erroring out, the dependency result will be
91
+ `None`.
92
+
93
+ This is useful when you want to have optional authentication.
94
+
95
+ It is also useful when you want to have authentication that can be
96
+ provided in one of multiple optional ways (for example, in a query
97
+ parameter or in an HTTP Bearer token).
98
+ """
99
+ ),
100
+ ] = True,
101
+ ):
102
+ self.model: APIKey = APIKey(
103
+ **{"in": APIKeyIn.query}, # type: ignore[arg-type]
104
+ name=name,
105
+ description=description,
106
+ )
107
+ self.scheme_name = scheme_name or self.__class__.__name__
108
+ self.auto_error = auto_error
109
+
110
+ async def __call__(self, request: Request) -> Optional[str]:
111
+ api_key = request.query_params.get(self.model.name)
112
+ return self.check_api_key(api_key, self.auto_error)
113
+
114
+
115
+ class APIKeyHeader(APIKeyBase):
116
+ """
117
+ API key authentication using a header.
118
+
119
+ This defines the name of the header that should be provided in the request with
120
+ the API key and integrates that into the OpenAPI documentation. It extracts
121
+ the key value sent in the header automatically and provides it as the dependency
122
+ result. But it doesn't define how to send that key to the client.
123
+
124
+ ## Usage
125
+
126
+ Create an instance object and use that object as the dependency in `Depends()`.
127
+
128
+ The dependency result will be a string containing the key value.
129
+
130
+ ## Example
131
+
132
+ ```python
133
+ from fastapi import Depends, FastAPI
134
+ from fastapi.security import APIKeyHeader
135
+
136
+ app = FastAPI()
137
+
138
+ header_scheme = APIKeyHeader(name="x-key")
139
+
140
+
141
+ @app.get("/items/")
142
+ async def read_items(key: str = Depends(header_scheme)):
143
+ return {"key": key}
144
+ ```
145
+ """
146
+
147
+ def __init__(
148
+ self,
149
+ *,
150
+ name: Annotated[str, Doc("Header name.")],
151
+ scheme_name: Annotated[
152
+ Optional[str],
153
+ Doc(
154
+ """
155
+ Security scheme name.
156
+
157
+ It will be included in the generated OpenAPI (e.g. visible at `/docs`).
158
+ """
159
+ ),
160
+ ] = None,
161
+ description: Annotated[
162
+ Optional[str],
163
+ Doc(
164
+ """
165
+ Security scheme description.
166
+
167
+ It will be included in the generated OpenAPI (e.g. visible at `/docs`).
168
+ """
169
+ ),
170
+ ] = None,
171
+ auto_error: Annotated[
172
+ bool,
173
+ Doc(
174
+ """
175
+ By default, if the header is not provided, `APIKeyHeader` will
176
+ automatically cancel the request and send the client an error.
177
+
178
+ If `auto_error` is set to `False`, when the header is not available,
179
+ instead of erroring out, the dependency result will be `None`.
180
+
181
+ This is useful when you want to have optional authentication.
182
+
183
+ It is also useful when you want to have authentication that can be
184
+ provided in one of multiple optional ways (for example, in a header or
185
+ in an HTTP Bearer token).
186
+ """
187
+ ),
188
+ ] = True,
189
+ ):
190
+ self.model: APIKey = APIKey(
191
+ **{"in": APIKeyIn.header}, # type: ignore[arg-type]
192
+ name=name,
193
+ description=description,
194
+ )
195
+ self.scheme_name = scheme_name or self.__class__.__name__
196
+ self.auto_error = auto_error
197
+
198
+ async def __call__(self, request: Request) -> Optional[str]:
199
+ api_key = request.headers.get(self.model.name)
200
+ return self.check_api_key(api_key, self.auto_error)
201
+
202
+
203
+ class APIKeyCookie(APIKeyBase):
204
+ """
205
+ API key authentication using a cookie.
206
+
207
+ This defines the name of the cookie that should be provided in the request with
208
+ the API key and integrates that into the OpenAPI documentation. It extracts
209
+ the key value sent in the cookie automatically and provides it as the dependency
210
+ result. But it doesn't define how to set that cookie.
211
+
212
+ ## Usage
213
+
214
+ Create an instance object and use that object as the dependency in `Depends()`.
215
+
216
+ The dependency result will be a string containing the key value.
217
+
218
+ ## Example
219
+
220
+ ```python
221
+ from fastapi import Depends, FastAPI
222
+ from fastapi.security import APIKeyCookie
223
+
224
+ app = FastAPI()
225
+
226
+ cookie_scheme = APIKeyCookie(name="session")
227
+
228
+
229
+ @app.get("/items/")
230
+ async def read_items(session: str = Depends(cookie_scheme)):
231
+ return {"session": session}
232
+ ```
233
+ """
234
+
235
+ def __init__(
236
+ self,
237
+ *,
238
+ name: Annotated[str, Doc("Cookie name.")],
239
+ scheme_name: Annotated[
240
+ Optional[str],
241
+ Doc(
242
+ """
243
+ Security scheme name.
244
+
245
+ It will be included in the generated OpenAPI (e.g. visible at `/docs`).
246
+ """
247
+ ),
248
+ ] = None,
249
+ description: Annotated[
250
+ Optional[str],
251
+ Doc(
252
+ """
253
+ Security scheme description.
254
+
255
+ It will be included in the generated OpenAPI (e.g. visible at `/docs`).
256
+ """
257
+ ),
258
+ ] = None,
259
+ auto_error: Annotated[
260
+ bool,
261
+ Doc(
262
+ """
263
+ By default, if the cookie is not provided, `APIKeyCookie` will
264
+ automatically cancel the request and send the client an error.
265
+
266
+ If `auto_error` is set to `False`, when the cookie is not available,
267
+ instead of erroring out, the dependency result will be `None`.
268
+
269
+ This is useful when you want to have optional authentication.
270
+
271
+ It is also useful when you want to have authentication that can be
272
+ provided in one of multiple optional ways (for example, in a cookie or
273
+ in an HTTP Bearer token).
274
+ """
275
+ ),
276
+ ] = True,
277
+ ):
278
+ self.model: APIKey = APIKey(
279
+ **{"in": APIKeyIn.cookie}, # type: ignore[arg-type]
280
+ name=name,
281
+ description=description,
282
+ )
283
+ self.scheme_name = scheme_name or self.__class__.__name__
284
+ self.auto_error = auto_error
285
+
286
+ async def __call__(self, request: Request) -> Optional[str]:
287
+ api_key = request.cookies.get(self.model.name)
288
+ return self.check_api_key(api_key, self.auto_error)
.venv/lib/python3.11/site-packages/fastapi/security/base.py ADDED
@@ -0,0 +1,6 @@
 
 
 
 
 
 
 
1
+ from fastapi.openapi.models import SecurityBase as SecurityBaseModel
2
+
3
+
4
+ class SecurityBase:
5
+ model: SecurityBaseModel
6
+ scheme_name: str
.venv/lib/python3.11/site-packages/fastapi/security/http.py ADDED
@@ -0,0 +1,420 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import binascii
2
+ from base64 import b64decode
3
+ from typing import Optional
4
+
5
+ from fastapi.exceptions import HTTPException
6
+ from fastapi.openapi.models import HTTPBase as HTTPBaseModel
7
+ from fastapi.openapi.models import HTTPBearer as HTTPBearerModel
8
+ from fastapi.security.base import SecurityBase
9
+ from fastapi.security.utils import get_authorization_scheme_param
10
+ from pydantic import BaseModel
11
+ from starlette.requests import Request
12
+ from starlette.status import HTTP_401_UNAUTHORIZED, HTTP_403_FORBIDDEN
13
+ from typing_extensions import Annotated, Doc
14
+
15
+
16
+ class HTTPBasicCredentials(BaseModel):
17
+ """
18
+ The HTTP Basic credentials given as the result of using `HTTPBasic` in a
19
+ dependency.
20
+
21
+ Read more about it in the
22
+ [FastAPI docs for HTTP Basic Auth](https://fastapi.tiangolo.com/advanced/security/http-basic-auth/).
23
+ """
24
+
25
+ username: Annotated[str, Doc("The HTTP Basic username.")]
26
+ password: Annotated[str, Doc("The HTTP Basic password.")]
27
+
28
+
29
+ class HTTPAuthorizationCredentials(BaseModel):
30
+ """
31
+ The HTTP authorization credentials in the result of using `HTTPBearer` or
32
+ `HTTPDigest` in a dependency.
33
+
34
+ The HTTP authorization header value is split by the first space.
35
+
36
+ The first part is the `scheme`, the second part is the `credentials`.
37
+
38
+ For example, in an HTTP Bearer token scheme, the client will send a header
39
+ like:
40
+
41
+ ```
42
+ Authorization: Bearer deadbeef12346
43
+ ```
44
+
45
+ In this case:
46
+
47
+ * `scheme` will have the value `"Bearer"`
48
+ * `credentials` will have the value `"deadbeef12346"`
49
+ """
50
+
51
+ scheme: Annotated[
52
+ str,
53
+ Doc(
54
+ """
55
+ The HTTP authorization scheme extracted from the header value.
56
+ """
57
+ ),
58
+ ]
59
+ credentials: Annotated[
60
+ str,
61
+ Doc(
62
+ """
63
+ The HTTP authorization credentials extracted from the header value.
64
+ """
65
+ ),
66
+ ]
67
+
68
+
69
+ class HTTPBase(SecurityBase):
70
+ def __init__(
71
+ self,
72
+ *,
73
+ scheme: str,
74
+ scheme_name: Optional[str] = None,
75
+ description: Optional[str] = None,
76
+ auto_error: bool = True,
77
+ ):
78
+ self.model = HTTPBaseModel(scheme=scheme, description=description)
79
+ self.scheme_name = scheme_name or self.__class__.__name__
80
+ self.auto_error = auto_error
81
+
82
+ async def __call__(
83
+ self, request: Request
84
+ ) -> Optional[HTTPAuthorizationCredentials]:
85
+ authorization = request.headers.get("Authorization")
86
+ scheme, credentials = get_authorization_scheme_param(authorization)
87
+ if not (authorization and scheme and credentials):
88
+ if self.auto_error:
89
+ raise HTTPException(
90
+ status_code=HTTP_403_FORBIDDEN, detail="Not authenticated"
91
+ )
92
+ else:
93
+ return None
94
+ return HTTPAuthorizationCredentials(scheme=scheme, credentials=credentials)
95
+
96
+
97
+ class HTTPBasic(HTTPBase):
98
+ """
99
+ HTTP Basic authentication.
100
+
101
+ ## Usage
102
+
103
+ Create an instance object and use that object as the dependency in `Depends()`.
104
+
105
+ The dependency result will be an `HTTPBasicCredentials` object containing the
106
+ `username` and the `password`.
107
+
108
+ Read more about it in the
109
+ [FastAPI docs for HTTP Basic Auth](https://fastapi.tiangolo.com/advanced/security/http-basic-auth/).
110
+
111
+ ## Example
112
+
113
+ ```python
114
+ from typing import Annotated
115
+
116
+ from fastapi import Depends, FastAPI
117
+ from fastapi.security import HTTPBasic, HTTPBasicCredentials
118
+
119
+ app = FastAPI()
120
+
121
+ security = HTTPBasic()
122
+
123
+
124
+ @app.get("/users/me")
125
+ def read_current_user(credentials: Annotated[HTTPBasicCredentials, Depends(security)]):
126
+ return {"username": credentials.username, "password": credentials.password}
127
+ ```
128
+ """
129
+
130
+ def __init__(
131
+ self,
132
+ *,
133
+ scheme_name: Annotated[
134
+ Optional[str],
135
+ Doc(
136
+ """
137
+ Security scheme name.
138
+
139
+ It will be included in the generated OpenAPI (e.g. visible at `/docs`).
140
+ """
141
+ ),
142
+ ] = None,
143
+ realm: Annotated[
144
+ Optional[str],
145
+ Doc(
146
+ """
147
+ HTTP Basic authentication realm.
148
+ """
149
+ ),
150
+ ] = None,
151
+ description: Annotated[
152
+ Optional[str],
153
+ Doc(
154
+ """
155
+ Security scheme description.
156
+
157
+ It will be included in the generated OpenAPI (e.g. visible at `/docs`).
158
+ """
159
+ ),
160
+ ] = None,
161
+ auto_error: Annotated[
162
+ bool,
163
+ Doc(
164
+ """
165
+ By default, if the HTTP Basic authentication is not provided (a
166
+ header), `HTTPBasic` will automatically cancel the request and send the
167
+ client an error.
168
+
169
+ If `auto_error` is set to `False`, when the HTTP Basic authentication
170
+ is not available, instead of erroring out, the dependency result will
171
+ be `None`.
172
+
173
+ This is useful when you want to have optional authentication.
174
+
175
+ It is also useful when you want to have authentication that can be
176
+ provided in one of multiple optional ways (for example, in HTTP Basic
177
+ authentication or in an HTTP Bearer token).
178
+ """
179
+ ),
180
+ ] = True,
181
+ ):
182
+ self.model = HTTPBaseModel(scheme="basic", description=description)
183
+ self.scheme_name = scheme_name or self.__class__.__name__
184
+ self.realm = realm
185
+ self.auto_error = auto_error
186
+
187
+ async def __call__( # type: ignore
188
+ self, request: Request
189
+ ) -> Optional[HTTPBasicCredentials]:
190
+ authorization = request.headers.get("Authorization")
191
+ scheme, param = get_authorization_scheme_param(authorization)
192
+ if self.realm:
193
+ unauthorized_headers = {"WWW-Authenticate": f'Basic realm="{self.realm}"'}
194
+ else:
195
+ unauthorized_headers = {"WWW-Authenticate": "Basic"}
196
+ if not authorization or scheme.lower() != "basic":
197
+ if self.auto_error:
198
+ raise HTTPException(
199
+ status_code=HTTP_401_UNAUTHORIZED,
200
+ detail="Not authenticated",
201
+ headers=unauthorized_headers,
202
+ )
203
+ else:
204
+ return None
205
+ invalid_user_credentials_exc = HTTPException(
206
+ status_code=HTTP_401_UNAUTHORIZED,
207
+ detail="Invalid authentication credentials",
208
+ headers=unauthorized_headers,
209
+ )
210
+ try:
211
+ data = b64decode(param).decode("ascii")
212
+ except (ValueError, UnicodeDecodeError, binascii.Error):
213
+ raise invalid_user_credentials_exc # noqa: B904
214
+ username, separator, password = data.partition(":")
215
+ if not separator:
216
+ raise invalid_user_credentials_exc
217
+ return HTTPBasicCredentials(username=username, password=password)
218
+
219
+
220
+ class HTTPBearer(HTTPBase):
221
+ """
222
+ HTTP Bearer token authentication.
223
+
224
+ ## Usage
225
+
226
+ Create an instance object and use that object as the dependency in `Depends()`.
227
+
228
+ The dependency result will be an `HTTPAuthorizationCredentials` object containing
229
+ the `scheme` and the `credentials`.
230
+
231
+ ## Example
232
+
233
+ ```python
234
+ from typing import Annotated
235
+
236
+ from fastapi import Depends, FastAPI
237
+ from fastapi.security import HTTPAuthorizationCredentials, HTTPBearer
238
+
239
+ app = FastAPI()
240
+
241
+ security = HTTPBearer()
242
+
243
+
244
+ @app.get("/users/me")
245
+ def read_current_user(
246
+ credentials: Annotated[HTTPAuthorizationCredentials, Depends(security)]
247
+ ):
248
+ return {"scheme": credentials.scheme, "credentials": credentials.credentials}
249
+ ```
250
+ """
251
+
252
+ def __init__(
253
+ self,
254
+ *,
255
+ bearerFormat: Annotated[Optional[str], Doc("Bearer token format.")] = None,
256
+ scheme_name: Annotated[
257
+ Optional[str],
258
+ Doc(
259
+ """
260
+ Security scheme name.
261
+
262
+ It will be included in the generated OpenAPI (e.g. visible at `/docs`).
263
+ """
264
+ ),
265
+ ] = None,
266
+ description: Annotated[
267
+ Optional[str],
268
+ Doc(
269
+ """
270
+ Security scheme description.
271
+
272
+ It will be included in the generated OpenAPI (e.g. visible at `/docs`).
273
+ """
274
+ ),
275
+ ] = None,
276
+ auto_error: Annotated[
277
+ bool,
278
+ Doc(
279
+ """
280
+ By default, if the HTTP Bearer token is not provided (in an
281
+ `Authorization` header), `HTTPBearer` will automatically cancel the
282
+ request and send the client an error.
283
+
284
+ If `auto_error` is set to `False`, when the HTTP Bearer token
285
+ is not available, instead of erroring out, the dependency result will
286
+ be `None`.
287
+
288
+ This is useful when you want to have optional authentication.
289
+
290
+ It is also useful when you want to have authentication that can be
291
+ provided in one of multiple optional ways (for example, in an HTTP
292
+ Bearer token or in a cookie).
293
+ """
294
+ ),
295
+ ] = True,
296
+ ):
297
+ self.model = HTTPBearerModel(bearerFormat=bearerFormat, description=description)
298
+ self.scheme_name = scheme_name or self.__class__.__name__
299
+ self.auto_error = auto_error
300
+
301
+ async def __call__(
302
+ self, request: Request
303
+ ) -> Optional[HTTPAuthorizationCredentials]:
304
+ authorization = request.headers.get("Authorization")
305
+ scheme, credentials = get_authorization_scheme_param(authorization)
306
+ if not (authorization and scheme and credentials):
307
+ if self.auto_error:
308
+ raise HTTPException(
309
+ status_code=HTTP_403_FORBIDDEN, detail="Not authenticated"
310
+ )
311
+ else:
312
+ return None
313
+ if scheme.lower() != "bearer":
314
+ if self.auto_error:
315
+ raise HTTPException(
316
+ status_code=HTTP_403_FORBIDDEN,
317
+ detail="Invalid authentication credentials",
318
+ )
319
+ else:
320
+ return None
321
+ return HTTPAuthorizationCredentials(scheme=scheme, credentials=credentials)
322
+
323
+
324
+ class HTTPDigest(HTTPBase):
325
+ """
326
+ HTTP Digest authentication.
327
+
328
+ ## Usage
329
+
330
+ Create an instance object and use that object as the dependency in `Depends()`.
331
+
332
+ The dependency result will be an `HTTPAuthorizationCredentials` object containing
333
+ the `scheme` and the `credentials`.
334
+
335
+ ## Example
336
+
337
+ ```python
338
+ from typing import Annotated
339
+
340
+ from fastapi import Depends, FastAPI
341
+ from fastapi.security import HTTPAuthorizationCredentials, HTTPDigest
342
+
343
+ app = FastAPI()
344
+
345
+ security = HTTPDigest()
346
+
347
+
348
+ @app.get("/users/me")
349
+ def read_current_user(
350
+ credentials: Annotated[HTTPAuthorizationCredentials, Depends(security)]
351
+ ):
352
+ return {"scheme": credentials.scheme, "credentials": credentials.credentials}
353
+ ```
354
+ """
355
+
356
+ def __init__(
357
+ self,
358
+ *,
359
+ scheme_name: Annotated[
360
+ Optional[str],
361
+ Doc(
362
+ """
363
+ Security scheme name.
364
+
365
+ It will be included in the generated OpenAPI (e.g. visible at `/docs`).
366
+ """
367
+ ),
368
+ ] = None,
369
+ description: Annotated[
370
+ Optional[str],
371
+ Doc(
372
+ """
373
+ Security scheme description.
374
+
375
+ It will be included in the generated OpenAPI (e.g. visible at `/docs`).
376
+ """
377
+ ),
378
+ ] = None,
379
+ auto_error: Annotated[
380
+ bool,
381
+ Doc(
382
+ """
383
+ By default, if the HTTP Digest is not provided, `HTTPDigest` will
384
+ automatically cancel the request and send the client an error.
385
+
386
+ If `auto_error` is set to `False`, when the HTTP Digest is not
387
+ available, instead of erroring out, the dependency result will
388
+ be `None`.
389
+
390
+ This is useful when you want to have optional authentication.
391
+
392
+ It is also useful when you want to have authentication that can be
393
+ provided in one of multiple optional ways (for example, in HTTP
394
+ Digest or in a cookie).
395
+ """
396
+ ),
397
+ ] = True,
398
+ ):
399
+ self.model = HTTPBaseModel(scheme="digest", description=description)
400
+ self.scheme_name = scheme_name or self.__class__.__name__
401
+ self.auto_error = auto_error
402
+
403
+ async def __call__(
404
+ self, request: Request
405
+ ) -> Optional[HTTPAuthorizationCredentials]:
406
+ authorization = request.headers.get("Authorization")
407
+ scheme, credentials = get_authorization_scheme_param(authorization)
408
+ if not (authorization and scheme and credentials):
409
+ if self.auto_error:
410
+ raise HTTPException(
411
+ status_code=HTTP_403_FORBIDDEN, detail="Not authenticated"
412
+ )
413
+ else:
414
+ return None
415
+ if scheme.lower() != "digest":
416
+ raise HTTPException(
417
+ status_code=HTTP_403_FORBIDDEN,
418
+ detail="Invalid authentication credentials",
419
+ )
420
+ return HTTPAuthorizationCredentials(scheme=scheme, credentials=credentials)
.venv/lib/python3.11/site-packages/fastapi/security/oauth2.py ADDED
@@ -0,0 +1,638 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from typing import Any, Dict, List, Optional, Union, cast
2
+
3
+ from fastapi.exceptions import HTTPException
4
+ from fastapi.openapi.models import OAuth2 as OAuth2Model
5
+ from fastapi.openapi.models import OAuthFlows as OAuthFlowsModel
6
+ from fastapi.param_functions import Form
7
+ from fastapi.security.base import SecurityBase
8
+ from fastapi.security.utils import get_authorization_scheme_param
9
+ from starlette.requests import Request
10
+ from starlette.status import HTTP_401_UNAUTHORIZED, HTTP_403_FORBIDDEN
11
+
12
+ # TODO: import from typing when deprecating Python 3.9
13
+ from typing_extensions import Annotated, Doc
14
+
15
+
16
+ class OAuth2PasswordRequestForm:
17
+ """
18
+ This is a dependency class to collect the `username` and `password` as form data
19
+ for an OAuth2 password flow.
20
+
21
+ The OAuth2 specification dictates that for a password flow the data should be
22
+ collected using form data (instead of JSON) and that it should have the specific
23
+ fields `username` and `password`.
24
+
25
+ All the initialization parameters are extracted from the request.
26
+
27
+ Read more about it in the
28
+ [FastAPI docs for Simple OAuth2 with Password and Bearer](https://fastapi.tiangolo.com/tutorial/security/simple-oauth2/).
29
+
30
+ ## Example
31
+
32
+ ```python
33
+ from typing import Annotated
34
+
35
+ from fastapi import Depends, FastAPI
36
+ from fastapi.security import OAuth2PasswordRequestForm
37
+
38
+ app = FastAPI()
39
+
40
+
41
+ @app.post("/login")
42
+ def login(form_data: Annotated[OAuth2PasswordRequestForm, Depends()]):
43
+ data = {}
44
+ data["scopes"] = []
45
+ for scope in form_data.scopes:
46
+ data["scopes"].append(scope)
47
+ if form_data.client_id:
48
+ data["client_id"] = form_data.client_id
49
+ if form_data.client_secret:
50
+ data["client_secret"] = form_data.client_secret
51
+ return data
52
+ ```
53
+
54
+ Note that for OAuth2 the scope `items:read` is a single scope in an opaque string.
55
+ You could have custom internal logic to separate it by colon characters (`:`) or
56
+ similar, and get the two parts `items` and `read`. Many applications do that to
57
+ group and organize permissions, you could do it as well in your application, just
58
+ know that that it is application specific, it's not part of the specification.
59
+ """
60
+
61
+ def __init__(
62
+ self,
63
+ *,
64
+ grant_type: Annotated[
65
+ Union[str, None],
66
+ Form(pattern="^password$"),
67
+ Doc(
68
+ """
69
+ The OAuth2 spec says it is required and MUST be the fixed string
70
+ "password". Nevertheless, this dependency class is permissive and
71
+ allows not passing it. If you want to enforce it, use instead the
72
+ `OAuth2PasswordRequestFormStrict` dependency.
73
+ """
74
+ ),
75
+ ] = None,
76
+ username: Annotated[
77
+ str,
78
+ Form(),
79
+ Doc(
80
+ """
81
+ `username` string. The OAuth2 spec requires the exact field name
82
+ `username`.
83
+ """
84
+ ),
85
+ ],
86
+ password: Annotated[
87
+ str,
88
+ Form(),
89
+ Doc(
90
+ """
91
+ `password` string. The OAuth2 spec requires the exact field name
92
+ `password".
93
+ """
94
+ ),
95
+ ],
96
+ scope: Annotated[
97
+ str,
98
+ Form(),
99
+ Doc(
100
+ """
101
+ A single string with actually several scopes separated by spaces. Each
102
+ scope is also a string.
103
+
104
+ For example, a single string with:
105
+
106
+ ```python
107
+ "items:read items:write users:read profile openid"
108
+ ````
109
+
110
+ would represent the scopes:
111
+
112
+ * `items:read`
113
+ * `items:write`
114
+ * `users:read`
115
+ * `profile`
116
+ * `openid`
117
+ """
118
+ ),
119
+ ] = "",
120
+ client_id: Annotated[
121
+ Union[str, None],
122
+ Form(),
123
+ Doc(
124
+ """
125
+ If there's a `client_id`, it can be sent as part of the form fields.
126
+ But the OAuth2 specification recommends sending the `client_id` and
127
+ `client_secret` (if any) using HTTP Basic auth.
128
+ """
129
+ ),
130
+ ] = None,
131
+ client_secret: Annotated[
132
+ Union[str, None],
133
+ Form(),
134
+ Doc(
135
+ """
136
+ If there's a `client_password` (and a `client_id`), they can be sent
137
+ as part of the form fields. But the OAuth2 specification recommends
138
+ sending the `client_id` and `client_secret` (if any) using HTTP Basic
139
+ auth.
140
+ """
141
+ ),
142
+ ] = None,
143
+ ):
144
+ self.grant_type = grant_type
145
+ self.username = username
146
+ self.password = password
147
+ self.scopes = scope.split()
148
+ self.client_id = client_id
149
+ self.client_secret = client_secret
150
+
151
+
152
+ class OAuth2PasswordRequestFormStrict(OAuth2PasswordRequestForm):
153
+ """
154
+ This is a dependency class to collect the `username` and `password` as form data
155
+ for an OAuth2 password flow.
156
+
157
+ The OAuth2 specification dictates that for a password flow the data should be
158
+ collected using form data (instead of JSON) and that it should have the specific
159
+ fields `username` and `password`.
160
+
161
+ All the initialization parameters are extracted from the request.
162
+
163
+ The only difference between `OAuth2PasswordRequestFormStrict` and
164
+ `OAuth2PasswordRequestForm` is that `OAuth2PasswordRequestFormStrict` requires the
165
+ client to send the form field `grant_type` with the value `"password"`, which
166
+ is required in the OAuth2 specification (it seems that for no particular reason),
167
+ while for `OAuth2PasswordRequestForm` `grant_type` is optional.
168
+
169
+ Read more about it in the
170
+ [FastAPI docs for Simple OAuth2 with Password and Bearer](https://fastapi.tiangolo.com/tutorial/security/simple-oauth2/).
171
+
172
+ ## Example
173
+
174
+ ```python
175
+ from typing import Annotated
176
+
177
+ from fastapi import Depends, FastAPI
178
+ from fastapi.security import OAuth2PasswordRequestForm
179
+
180
+ app = FastAPI()
181
+
182
+
183
+ @app.post("/login")
184
+ def login(form_data: Annotated[OAuth2PasswordRequestFormStrict, Depends()]):
185
+ data = {}
186
+ data["scopes"] = []
187
+ for scope in form_data.scopes:
188
+ data["scopes"].append(scope)
189
+ if form_data.client_id:
190
+ data["client_id"] = form_data.client_id
191
+ if form_data.client_secret:
192
+ data["client_secret"] = form_data.client_secret
193
+ return data
194
+ ```
195
+
196
+ Note that for OAuth2 the scope `items:read` is a single scope in an opaque string.
197
+ You could have custom internal logic to separate it by colon characters (`:`) or
198
+ similar, and get the two parts `items` and `read`. Many applications do that to
199
+ group and organize permissions, you could do it as well in your application, just
200
+ know that that it is application specific, it's not part of the specification.
201
+
202
+
203
+ grant_type: the OAuth2 spec says it is required and MUST be the fixed string "password".
204
+ This dependency is strict about it. If you want to be permissive, use instead the
205
+ OAuth2PasswordRequestForm dependency class.
206
+ username: username string. The OAuth2 spec requires the exact field name "username".
207
+ password: password string. The OAuth2 spec requires the exact field name "password".
208
+ scope: Optional string. Several scopes (each one a string) separated by spaces. E.g.
209
+ "items:read items:write users:read profile openid"
210
+ client_id: optional string. OAuth2 recommends sending the client_id and client_secret (if any)
211
+ using HTTP Basic auth, as: client_id:client_secret
212
+ client_secret: optional string. OAuth2 recommends sending the client_id and client_secret (if any)
213
+ using HTTP Basic auth, as: client_id:client_secret
214
+ """
215
+
216
+ def __init__(
217
+ self,
218
+ grant_type: Annotated[
219
+ str,
220
+ Form(pattern="^password$"),
221
+ Doc(
222
+ """
223
+ The OAuth2 spec says it is required and MUST be the fixed string
224
+ "password". This dependency is strict about it. If you want to be
225
+ permissive, use instead the `OAuth2PasswordRequestForm` dependency
226
+ class.
227
+ """
228
+ ),
229
+ ],
230
+ username: Annotated[
231
+ str,
232
+ Form(),
233
+ Doc(
234
+ """
235
+ `username` string. The OAuth2 spec requires the exact field name
236
+ `username`.
237
+ """
238
+ ),
239
+ ],
240
+ password: Annotated[
241
+ str,
242
+ Form(),
243
+ Doc(
244
+ """
245
+ `password` string. The OAuth2 spec requires the exact field name
246
+ `password".
247
+ """
248
+ ),
249
+ ],
250
+ scope: Annotated[
251
+ str,
252
+ Form(),
253
+ Doc(
254
+ """
255
+ A single string with actually several scopes separated by spaces. Each
256
+ scope is also a string.
257
+
258
+ For example, a single string with:
259
+
260
+ ```python
261
+ "items:read items:write users:read profile openid"
262
+ ````
263
+
264
+ would represent the scopes:
265
+
266
+ * `items:read`
267
+ * `items:write`
268
+ * `users:read`
269
+ * `profile`
270
+ * `openid`
271
+ """
272
+ ),
273
+ ] = "",
274
+ client_id: Annotated[
275
+ Union[str, None],
276
+ Form(),
277
+ Doc(
278
+ """
279
+ If there's a `client_id`, it can be sent as part of the form fields.
280
+ But the OAuth2 specification recommends sending the `client_id` and
281
+ `client_secret` (if any) using HTTP Basic auth.
282
+ """
283
+ ),
284
+ ] = None,
285
+ client_secret: Annotated[
286
+ Union[str, None],
287
+ Form(),
288
+ Doc(
289
+ """
290
+ If there's a `client_password` (and a `client_id`), they can be sent
291
+ as part of the form fields. But the OAuth2 specification recommends
292
+ sending the `client_id` and `client_secret` (if any) using HTTP Basic
293
+ auth.
294
+ """
295
+ ),
296
+ ] = None,
297
+ ):
298
+ super().__init__(
299
+ grant_type=grant_type,
300
+ username=username,
301
+ password=password,
302
+ scope=scope,
303
+ client_id=client_id,
304
+ client_secret=client_secret,
305
+ )
306
+
307
+
308
+ class OAuth2(SecurityBase):
309
+ """
310
+ This is the base class for OAuth2 authentication, an instance of it would be used
311
+ as a dependency. All other OAuth2 classes inherit from it and customize it for
312
+ each OAuth2 flow.
313
+
314
+ You normally would not create a new class inheriting from it but use one of the
315
+ existing subclasses, and maybe compose them if you want to support multiple flows.
316
+
317
+ Read more about it in the
318
+ [FastAPI docs for Security](https://fastapi.tiangolo.com/tutorial/security/).
319
+ """
320
+
321
+ def __init__(
322
+ self,
323
+ *,
324
+ flows: Annotated[
325
+ Union[OAuthFlowsModel, Dict[str, Dict[str, Any]]],
326
+ Doc(
327
+ """
328
+ The dictionary of OAuth2 flows.
329
+ """
330
+ ),
331
+ ] = OAuthFlowsModel(),
332
+ scheme_name: Annotated[
333
+ Optional[str],
334
+ Doc(
335
+ """
336
+ Security scheme name.
337
+
338
+ It will be included in the generated OpenAPI (e.g. visible at `/docs`).
339
+ """
340
+ ),
341
+ ] = None,
342
+ description: Annotated[
343
+ Optional[str],
344
+ Doc(
345
+ """
346
+ Security scheme description.
347
+
348
+ It will be included in the generated OpenAPI (e.g. visible at `/docs`).
349
+ """
350
+ ),
351
+ ] = None,
352
+ auto_error: Annotated[
353
+ bool,
354
+ Doc(
355
+ """
356
+ By default, if no HTTP Authorization header is provided, required for
357
+ OAuth2 authentication, it will automatically cancel the request and
358
+ send the client an error.
359
+
360
+ If `auto_error` is set to `False`, when the HTTP Authorization header
361
+ is not available, instead of erroring out, the dependency result will
362
+ be `None`.
363
+
364
+ This is useful when you want to have optional authentication.
365
+
366
+ It is also useful when you want to have authentication that can be
367
+ provided in one of multiple optional ways (for example, with OAuth2
368
+ or in a cookie).
369
+ """
370
+ ),
371
+ ] = True,
372
+ ):
373
+ self.model = OAuth2Model(
374
+ flows=cast(OAuthFlowsModel, flows), description=description
375
+ )
376
+ self.scheme_name = scheme_name or self.__class__.__name__
377
+ self.auto_error = auto_error
378
+
379
+ async def __call__(self, request: Request) -> Optional[str]:
380
+ authorization = request.headers.get("Authorization")
381
+ if not authorization:
382
+ if self.auto_error:
383
+ raise HTTPException(
384
+ status_code=HTTP_403_FORBIDDEN, detail="Not authenticated"
385
+ )
386
+ else:
387
+ return None
388
+ return authorization
389
+
390
+
391
+ class OAuth2PasswordBearer(OAuth2):
392
+ """
393
+ OAuth2 flow for authentication using a bearer token obtained with a password.
394
+ An instance of it would be used as a dependency.
395
+
396
+ Read more about it in the
397
+ [FastAPI docs for Simple OAuth2 with Password and Bearer](https://fastapi.tiangolo.com/tutorial/security/simple-oauth2/).
398
+ """
399
+
400
+ def __init__(
401
+ self,
402
+ tokenUrl: Annotated[
403
+ str,
404
+ Doc(
405
+ """
406
+ The URL to obtain the OAuth2 token. This would be the *path operation*
407
+ that has `OAuth2PasswordRequestForm` as a dependency.
408
+ """
409
+ ),
410
+ ],
411
+ scheme_name: Annotated[
412
+ Optional[str],
413
+ Doc(
414
+ """
415
+ Security scheme name.
416
+
417
+ It will be included in the generated OpenAPI (e.g. visible at `/docs`).
418
+ """
419
+ ),
420
+ ] = None,
421
+ scopes: Annotated[
422
+ Optional[Dict[str, str]],
423
+ Doc(
424
+ """
425
+ The OAuth2 scopes that would be required by the *path operations* that
426
+ use this dependency.
427
+ """
428
+ ),
429
+ ] = None,
430
+ description: Annotated[
431
+ Optional[str],
432
+ Doc(
433
+ """
434
+ Security scheme description.
435
+
436
+ It will be included in the generated OpenAPI (e.g. visible at `/docs`).
437
+ """
438
+ ),
439
+ ] = None,
440
+ auto_error: Annotated[
441
+ bool,
442
+ Doc(
443
+ """
444
+ By default, if no HTTP Authorization header is provided, required for
445
+ OAuth2 authentication, it will automatically cancel the request and
446
+ send the client an error.
447
+
448
+ If `auto_error` is set to `False`, when the HTTP Authorization header
449
+ is not available, instead of erroring out, the dependency result will
450
+ be `None`.
451
+
452
+ This is useful when you want to have optional authentication.
453
+
454
+ It is also useful when you want to have authentication that can be
455
+ provided in one of multiple optional ways (for example, with OAuth2
456
+ or in a cookie).
457
+ """
458
+ ),
459
+ ] = True,
460
+ ):
461
+ if not scopes:
462
+ scopes = {}
463
+ flows = OAuthFlowsModel(
464
+ password=cast(Any, {"tokenUrl": tokenUrl, "scopes": scopes})
465
+ )
466
+ super().__init__(
467
+ flows=flows,
468
+ scheme_name=scheme_name,
469
+ description=description,
470
+ auto_error=auto_error,
471
+ )
472
+
473
+ async def __call__(self, request: Request) -> Optional[str]:
474
+ authorization = request.headers.get("Authorization")
475
+ scheme, param = get_authorization_scheme_param(authorization)
476
+ if not authorization or scheme.lower() != "bearer":
477
+ if self.auto_error:
478
+ raise HTTPException(
479
+ status_code=HTTP_401_UNAUTHORIZED,
480
+ detail="Not authenticated",
481
+ headers={"WWW-Authenticate": "Bearer"},
482
+ )
483
+ else:
484
+ return None
485
+ return param
486
+
487
+
488
+ class OAuth2AuthorizationCodeBearer(OAuth2):
489
+ """
490
+ OAuth2 flow for authentication using a bearer token obtained with an OAuth2 code
491
+ flow. An instance of it would be used as a dependency.
492
+ """
493
+
494
+ def __init__(
495
+ self,
496
+ authorizationUrl: str,
497
+ tokenUrl: Annotated[
498
+ str,
499
+ Doc(
500
+ """
501
+ The URL to obtain the OAuth2 token.
502
+ """
503
+ ),
504
+ ],
505
+ refreshUrl: Annotated[
506
+ Optional[str],
507
+ Doc(
508
+ """
509
+ The URL to refresh the token and obtain a new one.
510
+ """
511
+ ),
512
+ ] = None,
513
+ scheme_name: Annotated[
514
+ Optional[str],
515
+ Doc(
516
+ """
517
+ Security scheme name.
518
+
519
+ It will be included in the generated OpenAPI (e.g. visible at `/docs`).
520
+ """
521
+ ),
522
+ ] = None,
523
+ scopes: Annotated[
524
+ Optional[Dict[str, str]],
525
+ Doc(
526
+ """
527
+ The OAuth2 scopes that would be required by the *path operations* that
528
+ use this dependency.
529
+ """
530
+ ),
531
+ ] = None,
532
+ description: Annotated[
533
+ Optional[str],
534
+ Doc(
535
+ """
536
+ Security scheme description.
537
+
538
+ It will be included in the generated OpenAPI (e.g. visible at `/docs`).
539
+ """
540
+ ),
541
+ ] = None,
542
+ auto_error: Annotated[
543
+ bool,
544
+ Doc(
545
+ """
546
+ By default, if no HTTP Authorization header is provided, required for
547
+ OAuth2 authentication, it will automatically cancel the request and
548
+ send the client an error.
549
+
550
+ If `auto_error` is set to `False`, when the HTTP Authorization header
551
+ is not available, instead of erroring out, the dependency result will
552
+ be `None`.
553
+
554
+ This is useful when you want to have optional authentication.
555
+
556
+ It is also useful when you want to have authentication that can be
557
+ provided in one of multiple optional ways (for example, with OAuth2
558
+ or in a cookie).
559
+ """
560
+ ),
561
+ ] = True,
562
+ ):
563
+ if not scopes:
564
+ scopes = {}
565
+ flows = OAuthFlowsModel(
566
+ authorizationCode=cast(
567
+ Any,
568
+ {
569
+ "authorizationUrl": authorizationUrl,
570
+ "tokenUrl": tokenUrl,
571
+ "refreshUrl": refreshUrl,
572
+ "scopes": scopes,
573
+ },
574
+ )
575
+ )
576
+ super().__init__(
577
+ flows=flows,
578
+ scheme_name=scheme_name,
579
+ description=description,
580
+ auto_error=auto_error,
581
+ )
582
+
583
+ async def __call__(self, request: Request) -> Optional[str]:
584
+ authorization = request.headers.get("Authorization")
585
+ scheme, param = get_authorization_scheme_param(authorization)
586
+ if not authorization or scheme.lower() != "bearer":
587
+ if self.auto_error:
588
+ raise HTTPException(
589
+ status_code=HTTP_401_UNAUTHORIZED,
590
+ detail="Not authenticated",
591
+ headers={"WWW-Authenticate": "Bearer"},
592
+ )
593
+ else:
594
+ return None # pragma: nocover
595
+ return param
596
+
597
+
598
+ class SecurityScopes:
599
+ """
600
+ This is a special class that you can define in a parameter in a dependency to
601
+ obtain the OAuth2 scopes required by all the dependencies in the same chain.
602
+
603
+ This way, multiple dependencies can have different scopes, even when used in the
604
+ same *path operation*. And with this, you can access all the scopes required in
605
+ all those dependencies in a single place.
606
+
607
+ Read more about it in the
608
+ [FastAPI docs for OAuth2 scopes](https://fastapi.tiangolo.com/advanced/security/oauth2-scopes/).
609
+ """
610
+
611
+ def __init__(
612
+ self,
613
+ scopes: Annotated[
614
+ Optional[List[str]],
615
+ Doc(
616
+ """
617
+ This will be filled by FastAPI.
618
+ """
619
+ ),
620
+ ] = None,
621
+ ):
622
+ self.scopes: Annotated[
623
+ List[str],
624
+ Doc(
625
+ """
626
+ The list of all the scopes required by dependencies.
627
+ """
628
+ ),
629
+ ] = scopes or []
630
+ self.scope_str: Annotated[
631
+ str,
632
+ Doc(
633
+ """
634
+ All the scopes required by all the dependencies in a single string
635
+ separated by spaces, as defined in the OAuth2 specification.
636
+ """
637
+ ),
638
+ ] = " ".join(self.scopes)
.venv/lib/python3.11/site-packages/fastapi/security/open_id_connect_url.py ADDED
@@ -0,0 +1,84 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from typing import Optional
2
+
3
+ from fastapi.openapi.models import OpenIdConnect as OpenIdConnectModel
4
+ from fastapi.security.base import SecurityBase
5
+ from starlette.exceptions import HTTPException
6
+ from starlette.requests import Request
7
+ from starlette.status import HTTP_403_FORBIDDEN
8
+ from typing_extensions import Annotated, Doc
9
+
10
+
11
+ class OpenIdConnect(SecurityBase):
12
+ """
13
+ OpenID Connect authentication class. An instance of it would be used as a
14
+ dependency.
15
+ """
16
+
17
+ def __init__(
18
+ self,
19
+ *,
20
+ openIdConnectUrl: Annotated[
21
+ str,
22
+ Doc(
23
+ """
24
+ The OpenID Connect URL.
25
+ """
26
+ ),
27
+ ],
28
+ scheme_name: Annotated[
29
+ Optional[str],
30
+ Doc(
31
+ """
32
+ Security scheme name.
33
+
34
+ It will be included in the generated OpenAPI (e.g. visible at `/docs`).
35
+ """
36
+ ),
37
+ ] = None,
38
+ description: Annotated[
39
+ Optional[str],
40
+ Doc(
41
+ """
42
+ Security scheme description.
43
+
44
+ It will be included in the generated OpenAPI (e.g. visible at `/docs`).
45
+ """
46
+ ),
47
+ ] = None,
48
+ auto_error: Annotated[
49
+ bool,
50
+ Doc(
51
+ """
52
+ By default, if no HTTP Authorization header is provided, required for
53
+ OpenID Connect authentication, it will automatically cancel the request
54
+ and send the client an error.
55
+
56
+ If `auto_error` is set to `False`, when the HTTP Authorization header
57
+ is not available, instead of erroring out, the dependency result will
58
+ be `None`.
59
+
60
+ This is useful when you want to have optional authentication.
61
+
62
+ It is also useful when you want to have authentication that can be
63
+ provided in one of multiple optional ways (for example, with OpenID
64
+ Connect or in a cookie).
65
+ """
66
+ ),
67
+ ] = True,
68
+ ):
69
+ self.model = OpenIdConnectModel(
70
+ openIdConnectUrl=openIdConnectUrl, description=description
71
+ )
72
+ self.scheme_name = scheme_name or self.__class__.__name__
73
+ self.auto_error = auto_error
74
+
75
+ async def __call__(self, request: Request) -> Optional[str]:
76
+ authorization = request.headers.get("Authorization")
77
+ if not authorization:
78
+ if self.auto_error:
79
+ raise HTTPException(
80
+ status_code=HTTP_403_FORBIDDEN, detail="Not authenticated"
81
+ )
82
+ else:
83
+ return None
84
+ return authorization
.venv/lib/python3.11/site-packages/fastapi/security/utils.py ADDED
@@ -0,0 +1,10 @@
 
 
 
 
 
 
 
 
 
 
 
1
+ from typing import Optional, Tuple
2
+
3
+
4
+ def get_authorization_scheme_param(
5
+ authorization_header_value: Optional[str],
6
+ ) -> Tuple[str, str]:
7
+ if not authorization_header_value:
8
+ return "", ""
9
+ scheme, _, param = authorization_header_value.partition(" ")
10
+ return scheme, param
.venv/lib/python3.11/site-packages/partial_json_parser/__init__.py ADDED
@@ -0,0 +1,7 @@
 
 
 
 
 
 
 
 
1
+ from .core.api import JSON, ensure_json, parse_json
2
+ from .core.complete import fix
3
+ from .core.exceptions import *
4
+ from .core.myelin import fix_fast
5
+ from .core.options import *
6
+
7
+ loads = decode = parse_json
.venv/lib/python3.11/site-packages/partial_json_parser/__pycache__/version.cpython-311.pyc ADDED
Binary file (219 Bytes). View file
 
.venv/lib/python3.11/site-packages/partial_json_parser/core/__pycache__/api.cpython-311.pyc ADDED
Binary file (1.58 kB). View file
 
.venv/lib/python3.11/site-packages/partial_json_parser/core/__pycache__/complete.cpython-311.pyc ADDED
Binary file (9.55 kB). View file
 
.venv/lib/python3.11/site-packages/partial_json_parser/core/__pycache__/exceptions.cpython-311.pyc ADDED
Binary file (802 Bytes). View file
 
.venv/lib/python3.11/site-packages/partial_json_parser/core/__pycache__/myelin.cpython-311.pyc ADDED
Binary file (11.1 kB). View file
 
.venv/lib/python3.11/site-packages/partial_json_parser/core/__pycache__/options.cpython-311.pyc ADDED
Binary file (1.56 kB). View file