fastapi 0.112.1__py3-none-any.whl → 0.112.3__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of fastapi might be problematic. Click here for more details.
- fastapi/__init__.py +1 -1
- fastapi/applications.py +1 -1
- fastapi/dependencies/models.py +27 -48
- fastapi/dependencies/utils.py +77 -59
- fastapi/param_functions.py +1 -1
- fastapi/params.py +2 -2
- fastapi/routing.py +55 -23
- fastapi/utils.py +6 -9
- {fastapi-0.112.1.dist-info → fastapi-0.112.3.dist-info}/METADATA +8 -7
- {fastapi-0.112.1.dist-info → fastapi-0.112.3.dist-info}/RECORD +13 -13
- {fastapi-0.112.1.dist-info → fastapi-0.112.3.dist-info}/WHEEL +0 -0
- {fastapi-0.112.1.dist-info → fastapi-0.112.3.dist-info}/entry_points.txt +0 -0
- {fastapi-0.112.1.dist-info → fastapi-0.112.3.dist-info}/licenses/LICENSE +0 -0
fastapi/__init__.py
CHANGED
fastapi/applications.py
CHANGED
|
@@ -1056,7 +1056,7 @@ class FastAPI(Starlette):
|
|
|
1056
1056
|
def add_api_route(
|
|
1057
1057
|
self,
|
|
1058
1058
|
path: str,
|
|
1059
|
-
endpoint: Callable[...,
|
|
1059
|
+
endpoint: Callable[..., Any],
|
|
1060
1060
|
*,
|
|
1061
1061
|
response_model: Any = Default(None),
|
|
1062
1062
|
status_code: Optional[int] = None,
|
fastapi/dependencies/models.py
CHANGED
|
@@ -1,58 +1,37 @@
|
|
|
1
|
-
from
|
|
1
|
+
from dataclasses import dataclass, field
|
|
2
|
+
from typing import Any, Callable, List, Optional, Sequence, Tuple
|
|
2
3
|
|
|
3
4
|
from fastapi._compat import ModelField
|
|
4
5
|
from fastapi.security.base import SecurityBase
|
|
5
6
|
|
|
6
7
|
|
|
8
|
+
@dataclass
|
|
7
9
|
class SecurityRequirement:
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
):
|
|
11
|
-
self.security_scheme = security_scheme
|
|
12
|
-
self.scopes = scopes
|
|
10
|
+
security_scheme: SecurityBase
|
|
11
|
+
scopes: Optional[Sequence[str]] = None
|
|
13
12
|
|
|
14
13
|
|
|
14
|
+
@dataclass
|
|
15
15
|
class Dependant:
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
) -> None:
|
|
38
|
-
self.path_params = path_params or []
|
|
39
|
-
self.query_params = query_params or []
|
|
40
|
-
self.header_params = header_params or []
|
|
41
|
-
self.cookie_params = cookie_params or []
|
|
42
|
-
self.body_params = body_params or []
|
|
43
|
-
self.dependencies = dependencies or []
|
|
44
|
-
self.security_requirements = security_schemes or []
|
|
45
|
-
self.request_param_name = request_param_name
|
|
46
|
-
self.websocket_param_name = websocket_param_name
|
|
47
|
-
self.http_connection_param_name = http_connection_param_name
|
|
48
|
-
self.response_param_name = response_param_name
|
|
49
|
-
self.background_tasks_param_name = background_tasks_param_name
|
|
50
|
-
self.security_scopes = security_scopes
|
|
51
|
-
self.security_scopes_param_name = security_scopes_param_name
|
|
52
|
-
self.name = name
|
|
53
|
-
self.call = call
|
|
54
|
-
self.use_cache = use_cache
|
|
55
|
-
# Store the path to be able to re-generate a dependable from it in overrides
|
|
56
|
-
self.path = path
|
|
57
|
-
# Save the cache key at creation to optimize performance
|
|
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:
|
|
58
37
|
self.cache_key = (self.call, tuple(sorted(set(self.security_scopes or []))))
|
fastapi/dependencies/utils.py
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import inspect
|
|
2
2
|
from contextlib import AsyncExitStack, contextmanager
|
|
3
3
|
from copy import copy, deepcopy
|
|
4
|
+
from dataclasses import dataclass
|
|
4
5
|
from typing import (
|
|
5
6
|
Any,
|
|
6
7
|
Callable,
|
|
@@ -54,7 +55,7 @@ from fastapi.logger import logger
|
|
|
54
55
|
from fastapi.security.base import SecurityBase
|
|
55
56
|
from fastapi.security.oauth2 import OAuth2, SecurityScopes
|
|
56
57
|
from fastapi.security.open_id_connect_url import OpenIdConnect
|
|
57
|
-
from fastapi.utils import
|
|
58
|
+
from fastapi.utils import create_model_field, get_path_param_names
|
|
58
59
|
from pydantic.fields import FieldInfo
|
|
59
60
|
from starlette.background import BackgroundTasks as StarletteBackgroundTasks
|
|
60
61
|
from starlette.concurrency import run_in_threadpool
|
|
@@ -79,25 +80,23 @@ multipart_incorrect_install_error = (
|
|
|
79
80
|
)
|
|
80
81
|
|
|
81
82
|
|
|
82
|
-
def
|
|
83
|
-
|
|
84
|
-
|
|
83
|
+
def ensure_multipart_is_installed() -> None:
|
|
84
|
+
try:
|
|
85
|
+
# __version__ is available in both multiparts, and can be mocked
|
|
86
|
+
from multipart import __version__ # type: ignore
|
|
87
|
+
|
|
88
|
+
assert __version__
|
|
85
89
|
try:
|
|
86
|
-
#
|
|
87
|
-
from multipart import
|
|
88
|
-
|
|
89
|
-
assert
|
|
90
|
-
try:
|
|
91
|
-
# parse_options_header is only available in the right multipart
|
|
92
|
-
from multipart.multipart import parse_options_header # type: ignore
|
|
93
|
-
|
|
94
|
-
assert parse_options_header
|
|
95
|
-
except ImportError:
|
|
96
|
-
logger.error(multipart_incorrect_install_error)
|
|
97
|
-
raise RuntimeError(multipart_incorrect_install_error) from None
|
|
90
|
+
# parse_options_header is only available in the right multipart
|
|
91
|
+
from multipart.multipart import parse_options_header # type: ignore
|
|
92
|
+
|
|
93
|
+
assert parse_options_header
|
|
98
94
|
except ImportError:
|
|
99
|
-
logger.error(
|
|
100
|
-
raise RuntimeError(
|
|
95
|
+
logger.error(multipart_incorrect_install_error)
|
|
96
|
+
raise RuntimeError(multipart_incorrect_install_error) from None
|
|
97
|
+
except ImportError:
|
|
98
|
+
logger.error(multipart_not_installed_error)
|
|
99
|
+
raise RuntimeError(multipart_not_installed_error) from None
|
|
101
100
|
|
|
102
101
|
|
|
103
102
|
def get_param_sub_dependant(
|
|
@@ -175,7 +174,7 @@ def get_flat_dependant(
|
|
|
175
174
|
header_params=dependant.header_params.copy(),
|
|
176
175
|
cookie_params=dependant.cookie_params.copy(),
|
|
177
176
|
body_params=dependant.body_params.copy(),
|
|
178
|
-
|
|
177
|
+
security_requirements=dependant.security_requirements.copy(),
|
|
179
178
|
use_cache=dependant.use_cache,
|
|
180
179
|
path=dependant.path,
|
|
181
180
|
)
|
|
@@ -258,16 +257,16 @@ def get_dependant(
|
|
|
258
257
|
)
|
|
259
258
|
for param_name, param in signature_params.items():
|
|
260
259
|
is_path_param = param_name in path_param_names
|
|
261
|
-
|
|
260
|
+
param_details = analyze_param(
|
|
262
261
|
param_name=param_name,
|
|
263
262
|
annotation=param.annotation,
|
|
264
263
|
value=param.default,
|
|
265
264
|
is_path_param=is_path_param,
|
|
266
265
|
)
|
|
267
|
-
if depends is not None:
|
|
266
|
+
if param_details.depends is not None:
|
|
268
267
|
sub_dependant = get_param_sub_dependant(
|
|
269
268
|
param_name=param_name,
|
|
270
|
-
depends=depends,
|
|
269
|
+
depends=param_details.depends,
|
|
271
270
|
path=path,
|
|
272
271
|
security_scopes=security_scopes,
|
|
273
272
|
)
|
|
@@ -275,18 +274,18 @@ def get_dependant(
|
|
|
275
274
|
continue
|
|
276
275
|
if add_non_field_param_to_dependency(
|
|
277
276
|
param_name=param_name,
|
|
278
|
-
type_annotation=type_annotation,
|
|
277
|
+
type_annotation=param_details.type_annotation,
|
|
279
278
|
dependant=dependant,
|
|
280
279
|
):
|
|
281
280
|
assert (
|
|
282
|
-
|
|
281
|
+
param_details.field is None
|
|
283
282
|
), f"Cannot specify multiple FastAPI annotations for {param_name!r}"
|
|
284
283
|
continue
|
|
285
|
-
assert
|
|
286
|
-
if is_body_param(param_field=
|
|
287
|
-
dependant.body_params.append(
|
|
284
|
+
assert param_details.field is not None
|
|
285
|
+
if is_body_param(param_field=param_details.field, is_path_param=is_path_param):
|
|
286
|
+
dependant.body_params.append(param_details.field)
|
|
288
287
|
else:
|
|
289
|
-
add_param_to_fields(field=
|
|
288
|
+
add_param_to_fields(field=param_details.field, dependant=dependant)
|
|
290
289
|
return dependant
|
|
291
290
|
|
|
292
291
|
|
|
@@ -314,13 +313,20 @@ def add_non_field_param_to_dependency(
|
|
|
314
313
|
return None
|
|
315
314
|
|
|
316
315
|
|
|
316
|
+
@dataclass
|
|
317
|
+
class ParamDetails:
|
|
318
|
+
type_annotation: Any
|
|
319
|
+
depends: Optional[params.Depends]
|
|
320
|
+
field: Optional[ModelField]
|
|
321
|
+
|
|
322
|
+
|
|
317
323
|
def analyze_param(
|
|
318
324
|
*,
|
|
319
325
|
param_name: str,
|
|
320
326
|
annotation: Any,
|
|
321
327
|
value: Any,
|
|
322
328
|
is_path_param: bool,
|
|
323
|
-
) ->
|
|
329
|
+
) -> ParamDetails:
|
|
324
330
|
field_info = None
|
|
325
331
|
depends = None
|
|
326
332
|
type_annotation: Any = Any
|
|
@@ -328,6 +334,7 @@ def analyze_param(
|
|
|
328
334
|
if annotation is not inspect.Signature.empty:
|
|
329
335
|
use_annotation = annotation
|
|
330
336
|
type_annotation = annotation
|
|
337
|
+
# Extract Annotated info
|
|
331
338
|
if get_origin(use_annotation) is Annotated:
|
|
332
339
|
annotated_args = get_args(annotation)
|
|
333
340
|
type_annotation = annotated_args[0]
|
|
@@ -342,11 +349,12 @@ def analyze_param(
|
|
|
342
349
|
if isinstance(arg, (params.Param, params.Body, params.Depends))
|
|
343
350
|
]
|
|
344
351
|
if fastapi_specific_annotations:
|
|
345
|
-
fastapi_annotation: Union[
|
|
346
|
-
|
|
347
|
-
|
|
352
|
+
fastapi_annotation: Union[FieldInfo, params.Depends, None] = (
|
|
353
|
+
fastapi_specific_annotations[-1]
|
|
354
|
+
)
|
|
348
355
|
else:
|
|
349
356
|
fastapi_annotation = None
|
|
357
|
+
# Set default for Annotated FieldInfo
|
|
350
358
|
if isinstance(fastapi_annotation, FieldInfo):
|
|
351
359
|
# Copy `field_info` because we mutate `field_info.default` below.
|
|
352
360
|
field_info = copy_field_info(
|
|
@@ -361,9 +369,10 @@ def analyze_param(
|
|
|
361
369
|
field_info.default = value
|
|
362
370
|
else:
|
|
363
371
|
field_info.default = Required
|
|
372
|
+
# Get Annotated Depends
|
|
364
373
|
elif isinstance(fastapi_annotation, params.Depends):
|
|
365
374
|
depends = fastapi_annotation
|
|
366
|
-
|
|
375
|
+
# Get Depends from default value
|
|
367
376
|
if isinstance(value, params.Depends):
|
|
368
377
|
assert depends is None, (
|
|
369
378
|
"Cannot specify `Depends` in `Annotated` and default value"
|
|
@@ -374,6 +383,7 @@ def analyze_param(
|
|
|
374
383
|
f" default value together for {param_name!r}"
|
|
375
384
|
)
|
|
376
385
|
depends = value
|
|
386
|
+
# Get FieldInfo from default value
|
|
377
387
|
elif isinstance(value, FieldInfo):
|
|
378
388
|
assert field_info is None, (
|
|
379
389
|
"Cannot specify FastAPI annotations in `Annotated` and default value"
|
|
@@ -383,11 +393,13 @@ def analyze_param(
|
|
|
383
393
|
if PYDANTIC_V2:
|
|
384
394
|
field_info.annotation = type_annotation
|
|
385
395
|
|
|
396
|
+
# Get Depends from type annotation
|
|
386
397
|
if depends is not None and depends.dependency is None:
|
|
387
398
|
# Copy `depends` before mutating it
|
|
388
399
|
depends = copy(depends)
|
|
389
400
|
depends.dependency = type_annotation
|
|
390
401
|
|
|
402
|
+
# Handle non-param type annotations like Request
|
|
391
403
|
if lenient_issubclass(
|
|
392
404
|
type_annotation,
|
|
393
405
|
(
|
|
@@ -403,6 +415,7 @@ def analyze_param(
|
|
|
403
415
|
assert (
|
|
404
416
|
field_info is None
|
|
405
417
|
), f"Cannot specify FastAPI annotation for type {type_annotation!r}"
|
|
418
|
+
# Handle default assignations, neither field_info nor depends was not found in Annotated nor default value
|
|
406
419
|
elif field_info is None and depends is None:
|
|
407
420
|
default_value = value if value is not inspect.Signature.empty else Required
|
|
408
421
|
if is_path_param:
|
|
@@ -420,7 +433,9 @@ def analyze_param(
|
|
|
420
433
|
field_info = params.Query(annotation=use_annotation, default=default_value)
|
|
421
434
|
|
|
422
435
|
field = None
|
|
436
|
+
# It's a field_info, not a dependency
|
|
423
437
|
if field_info is not None:
|
|
438
|
+
# Handle field_info.in_
|
|
424
439
|
if is_path_param:
|
|
425
440
|
assert isinstance(field_info, params.Path), (
|
|
426
441
|
f"Cannot use `{field_info.__class__.__name__}` for path param"
|
|
@@ -436,12 +451,14 @@ def analyze_param(
|
|
|
436
451
|
field_info,
|
|
437
452
|
param_name,
|
|
438
453
|
)
|
|
454
|
+
if isinstance(field_info, params.Form):
|
|
455
|
+
ensure_multipart_is_installed()
|
|
439
456
|
if not field_info.alias and getattr(field_info, "convert_underscores", None):
|
|
440
457
|
alias = param_name.replace("_", "-")
|
|
441
458
|
else:
|
|
442
459
|
alias = field_info.alias or param_name
|
|
443
460
|
field_info.alias = alias
|
|
444
|
-
field =
|
|
461
|
+
field = create_model_field(
|
|
445
462
|
name=param_name,
|
|
446
463
|
type_=use_annotation_from_field_info,
|
|
447
464
|
default=field_info.default,
|
|
@@ -450,7 +467,7 @@ def analyze_param(
|
|
|
450
467
|
field_info=field_info,
|
|
451
468
|
)
|
|
452
469
|
|
|
453
|
-
return type_annotation, depends, field
|
|
470
|
+
return ParamDetails(type_annotation=type_annotation, depends=depends, field=field)
|
|
454
471
|
|
|
455
472
|
|
|
456
473
|
def is_body_param(*, param_field: ModelField, is_path_param: bool) -> bool:
|
|
@@ -521,6 +538,15 @@ async def solve_generator(
|
|
|
521
538
|
return await stack.enter_async_context(cm)
|
|
522
539
|
|
|
523
540
|
|
|
541
|
+
@dataclass
|
|
542
|
+
class SolvedDependency:
|
|
543
|
+
values: Dict[str, Any]
|
|
544
|
+
errors: List[Any]
|
|
545
|
+
background_tasks: Optional[StarletteBackgroundTasks]
|
|
546
|
+
response: Response
|
|
547
|
+
dependency_cache: Dict[Tuple[Callable[..., Any], Tuple[str]], Any]
|
|
548
|
+
|
|
549
|
+
|
|
524
550
|
async def solve_dependencies(
|
|
525
551
|
*,
|
|
526
552
|
request: Union[Request, WebSocket],
|
|
@@ -531,13 +557,7 @@ async def solve_dependencies(
|
|
|
531
557
|
dependency_overrides_provider: Optional[Any] = None,
|
|
532
558
|
dependency_cache: Optional[Dict[Tuple[Callable[..., Any], Tuple[str]], Any]] = None,
|
|
533
559
|
async_exit_stack: AsyncExitStack,
|
|
534
|
-
) ->
|
|
535
|
-
Dict[str, Any],
|
|
536
|
-
List[Any],
|
|
537
|
-
Optional[StarletteBackgroundTasks],
|
|
538
|
-
Response,
|
|
539
|
-
Dict[Tuple[Callable[..., Any], Tuple[str]], Any],
|
|
540
|
-
]:
|
|
560
|
+
) -> SolvedDependency:
|
|
541
561
|
values: Dict[str, Any] = {}
|
|
542
562
|
errors: List[Any] = []
|
|
543
563
|
if response is None:
|
|
@@ -579,27 +599,21 @@ async def solve_dependencies(
|
|
|
579
599
|
dependency_cache=dependency_cache,
|
|
580
600
|
async_exit_stack=async_exit_stack,
|
|
581
601
|
)
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
_, # the subdependency returns the same response we have
|
|
587
|
-
sub_dependency_cache,
|
|
588
|
-
) = solved_result
|
|
589
|
-
dependency_cache.update(sub_dependency_cache)
|
|
590
|
-
if sub_errors:
|
|
591
|
-
errors.extend(sub_errors)
|
|
602
|
+
background_tasks = solved_result.background_tasks
|
|
603
|
+
dependency_cache.update(solved_result.dependency_cache)
|
|
604
|
+
if solved_result.errors:
|
|
605
|
+
errors.extend(solved_result.errors)
|
|
592
606
|
continue
|
|
593
607
|
if sub_dependant.use_cache and sub_dependant.cache_key in dependency_cache:
|
|
594
608
|
solved = dependency_cache[sub_dependant.cache_key]
|
|
595
609
|
elif is_gen_callable(call) or is_async_gen_callable(call):
|
|
596
610
|
solved = await solve_generator(
|
|
597
|
-
call=call, stack=async_exit_stack, sub_values=
|
|
611
|
+
call=call, stack=async_exit_stack, sub_values=solved_result.values
|
|
598
612
|
)
|
|
599
613
|
elif is_coroutine_callable(call):
|
|
600
|
-
solved = await call(**
|
|
614
|
+
solved = await call(**solved_result.values)
|
|
601
615
|
else:
|
|
602
|
-
solved = await run_in_threadpool(call, **
|
|
616
|
+
solved = await run_in_threadpool(call, **solved_result.values)
|
|
603
617
|
if sub_dependant.name is not None:
|
|
604
618
|
values[sub_dependant.name] = solved
|
|
605
619
|
if sub_dependant.cache_key not in dependency_cache:
|
|
@@ -646,7 +660,13 @@ async def solve_dependencies(
|
|
|
646
660
|
values[dependant.security_scopes_param_name] = SecurityScopes(
|
|
647
661
|
scopes=dependant.security_scopes
|
|
648
662
|
)
|
|
649
|
-
return
|
|
663
|
+
return SolvedDependency(
|
|
664
|
+
values=values,
|
|
665
|
+
errors=errors,
|
|
666
|
+
background_tasks=background_tasks,
|
|
667
|
+
response=response,
|
|
668
|
+
dependency_cache=dependency_cache,
|
|
669
|
+
)
|
|
650
670
|
|
|
651
671
|
|
|
652
672
|
def request_params_to_args(
|
|
@@ -775,7 +795,6 @@ def get_body_field(*, dependant: Dependant, name: str) -> Optional[ModelField]:
|
|
|
775
795
|
embed = getattr(field_info, "embed", None)
|
|
776
796
|
body_param_names_set = {param.name for param in flat_dependant.body_params}
|
|
777
797
|
if len(body_param_names_set) == 1 and not embed:
|
|
778
|
-
check_file_field(first_param)
|
|
779
798
|
return first_param
|
|
780
799
|
# If one field requires to embed, all have to be embedded
|
|
781
800
|
# in case a sub-dependency is evaluated with a single unique body field
|
|
@@ -807,12 +826,11 @@ def get_body_field(*, dependant: Dependant, name: str) -> Optional[ModelField]:
|
|
|
807
826
|
]
|
|
808
827
|
if len(set(body_param_media_types)) == 1:
|
|
809
828
|
BodyFieldInfo_kwargs["media_type"] = body_param_media_types[0]
|
|
810
|
-
final_field =
|
|
829
|
+
final_field = create_model_field(
|
|
811
830
|
name="body",
|
|
812
831
|
type_=BodyModel,
|
|
813
832
|
required=required,
|
|
814
833
|
alias="body",
|
|
815
834
|
field_info=BodyFieldInfo(**BodyFieldInfo_kwargs),
|
|
816
835
|
)
|
|
817
|
-
check_file_field(final_field)
|
|
818
836
|
return final_field
|
fastapi/param_functions.py
CHANGED
|
@@ -2343,7 +2343,7 @@ def Security( # noqa: N802
|
|
|
2343
2343
|
```python
|
|
2344
2344
|
from typing import Annotated
|
|
2345
2345
|
|
|
2346
|
-
from fastapi import
|
|
2346
|
+
from fastapi import Security, FastAPI
|
|
2347
2347
|
|
|
2348
2348
|
from .db import User
|
|
2349
2349
|
from .security import get_current_active_user
|
fastapi/params.py
CHANGED
|
@@ -91,7 +91,7 @@ class Param(FieldInfo):
|
|
|
91
91
|
max_length=max_length,
|
|
92
92
|
discriminator=discriminator,
|
|
93
93
|
multiple_of=multiple_of,
|
|
94
|
-
|
|
94
|
+
allow_inf_nan=allow_inf_nan,
|
|
95
95
|
max_digits=max_digits,
|
|
96
96
|
decimal_places=decimal_places,
|
|
97
97
|
**extra,
|
|
@@ -547,7 +547,7 @@ class Body(FieldInfo):
|
|
|
547
547
|
max_length=max_length,
|
|
548
548
|
discriminator=discriminator,
|
|
549
549
|
multiple_of=multiple_of,
|
|
550
|
-
|
|
550
|
+
allow_inf_nan=allow_inf_nan,
|
|
551
551
|
max_digits=max_digits,
|
|
552
552
|
decimal_places=decimal_places,
|
|
553
553
|
**extra,
|
fastapi/routing.py
CHANGED
|
@@ -3,14 +3,16 @@ import dataclasses
|
|
|
3
3
|
import email.message
|
|
4
4
|
import inspect
|
|
5
5
|
import json
|
|
6
|
-
from contextlib import AsyncExitStack
|
|
6
|
+
from contextlib import AsyncExitStack, asynccontextmanager
|
|
7
7
|
from enum import Enum, IntEnum
|
|
8
8
|
from typing import (
|
|
9
9
|
Any,
|
|
10
|
+
AsyncIterator,
|
|
10
11
|
Callable,
|
|
11
12
|
Coroutine,
|
|
12
13
|
Dict,
|
|
13
14
|
List,
|
|
15
|
+
Mapping,
|
|
14
16
|
Optional,
|
|
15
17
|
Sequence,
|
|
16
18
|
Set,
|
|
@@ -47,7 +49,7 @@ from fastapi.exceptions import (
|
|
|
47
49
|
from fastapi.types import DecoratedCallable, IncEx
|
|
48
50
|
from fastapi.utils import (
|
|
49
51
|
create_cloned_field,
|
|
50
|
-
|
|
52
|
+
create_model_field,
|
|
51
53
|
generate_unique_id,
|
|
52
54
|
get_value_or_default,
|
|
53
55
|
is_body_allowed_for_status_code,
|
|
@@ -67,7 +69,7 @@ from starlette.routing import (
|
|
|
67
69
|
websocket_session,
|
|
68
70
|
)
|
|
69
71
|
from starlette.routing import Mount as Mount # noqa
|
|
70
|
-
from starlette.types import ASGIApp, Lifespan, Scope
|
|
72
|
+
from starlette.types import AppType, ASGIApp, Lifespan, Scope
|
|
71
73
|
from starlette.websockets import WebSocket
|
|
72
74
|
from typing_extensions import Annotated, Doc, deprecated
|
|
73
75
|
|
|
@@ -119,6 +121,23 @@ def _prepare_response_content(
|
|
|
119
121
|
return res
|
|
120
122
|
|
|
121
123
|
|
|
124
|
+
def _merge_lifespan_context(
|
|
125
|
+
original_context: Lifespan[Any], nested_context: Lifespan[Any]
|
|
126
|
+
) -> Lifespan[Any]:
|
|
127
|
+
@asynccontextmanager
|
|
128
|
+
async def merged_lifespan(
|
|
129
|
+
app: AppType,
|
|
130
|
+
) -> AsyncIterator[Optional[Mapping[str, Any]]]:
|
|
131
|
+
async with original_context(app) as maybe_original_state:
|
|
132
|
+
async with nested_context(app) as maybe_nested_state:
|
|
133
|
+
if maybe_nested_state is None and maybe_original_state is None:
|
|
134
|
+
yield None # old ASGI compatibility
|
|
135
|
+
else:
|
|
136
|
+
yield {**(maybe_nested_state or {}), **(maybe_original_state or {})}
|
|
137
|
+
|
|
138
|
+
return merged_lifespan # type: ignore[return-value]
|
|
139
|
+
|
|
140
|
+
|
|
122
141
|
async def serialize_response(
|
|
123
142
|
*,
|
|
124
143
|
field: Optional[ModelField] = None,
|
|
@@ -273,26 +292,34 @@ def get_request_handler(
|
|
|
273
292
|
dependency_overrides_provider=dependency_overrides_provider,
|
|
274
293
|
async_exit_stack=async_exit_stack,
|
|
275
294
|
)
|
|
276
|
-
|
|
295
|
+
errors = solved_result.errors
|
|
277
296
|
if not errors:
|
|
278
297
|
raw_response = await run_endpoint_function(
|
|
279
|
-
dependant=dependant,
|
|
298
|
+
dependant=dependant,
|
|
299
|
+
values=solved_result.values,
|
|
300
|
+
is_coroutine=is_coroutine,
|
|
280
301
|
)
|
|
281
302
|
if isinstance(raw_response, Response):
|
|
282
303
|
if raw_response.background is None:
|
|
283
|
-
raw_response.background = background_tasks
|
|
304
|
+
raw_response.background = solved_result.background_tasks
|
|
284
305
|
response = raw_response
|
|
285
306
|
else:
|
|
286
|
-
response_args: Dict[str, Any] = {
|
|
307
|
+
response_args: Dict[str, Any] = {
|
|
308
|
+
"background": solved_result.background_tasks
|
|
309
|
+
}
|
|
287
310
|
# If status_code was set, use it, otherwise use the default from the
|
|
288
311
|
# response class, in the case of redirect it's 307
|
|
289
312
|
current_status_code = (
|
|
290
|
-
status_code
|
|
313
|
+
status_code
|
|
314
|
+
if status_code
|
|
315
|
+
else solved_result.response.status_code
|
|
291
316
|
)
|
|
292
317
|
if current_status_code is not None:
|
|
293
318
|
response_args["status_code"] = current_status_code
|
|
294
|
-
if
|
|
295
|
-
response_args["status_code"] =
|
|
319
|
+
if solved_result.response.status_code:
|
|
320
|
+
response_args["status_code"] = (
|
|
321
|
+
solved_result.response.status_code
|
|
322
|
+
)
|
|
296
323
|
content = await serialize_response(
|
|
297
324
|
field=response_field,
|
|
298
325
|
response_content=raw_response,
|
|
@@ -307,7 +334,7 @@ def get_request_handler(
|
|
|
307
334
|
response = actual_response_class(content, **response_args)
|
|
308
335
|
if not is_body_allowed_for_status_code(response.status_code):
|
|
309
336
|
response.body = b""
|
|
310
|
-
response.headers.raw.extend(
|
|
337
|
+
response.headers.raw.extend(solved_result.response.headers.raw)
|
|
311
338
|
if errors:
|
|
312
339
|
validation_error = RequestValidationError(
|
|
313
340
|
_normalize_errors(errors), body=body
|
|
@@ -341,11 +368,12 @@ def get_websocket_app(
|
|
|
341
368
|
dependency_overrides_provider=dependency_overrides_provider,
|
|
342
369
|
async_exit_stack=async_exit_stack,
|
|
343
370
|
)
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
371
|
+
if solved_result.errors:
|
|
372
|
+
raise WebSocketRequestValidationError(
|
|
373
|
+
_normalize_errors(solved_result.errors)
|
|
374
|
+
)
|
|
347
375
|
assert dependant.call is not None, "dependant.call must be a function"
|
|
348
|
-
await dependant.call(**values)
|
|
376
|
+
await dependant.call(**solved_result.values)
|
|
349
377
|
|
|
350
378
|
return app
|
|
351
379
|
|
|
@@ -454,9 +482,9 @@ class APIRoute(routing.Route):
|
|
|
454
482
|
methods = ["GET"]
|
|
455
483
|
self.methods: Set[str] = {method.upper() for method in methods}
|
|
456
484
|
if isinstance(generate_unique_id_function, DefaultPlaceholder):
|
|
457
|
-
current_generate_unique_id: Callable[
|
|
458
|
-
|
|
459
|
-
|
|
485
|
+
current_generate_unique_id: Callable[[APIRoute], str] = (
|
|
486
|
+
generate_unique_id_function.value
|
|
487
|
+
)
|
|
460
488
|
else:
|
|
461
489
|
current_generate_unique_id = generate_unique_id_function
|
|
462
490
|
self.unique_id = self.operation_id or current_generate_unique_id(self)
|
|
@@ -469,7 +497,7 @@ class APIRoute(routing.Route):
|
|
|
469
497
|
status_code
|
|
470
498
|
), f"Status code {status_code} must not have a response body"
|
|
471
499
|
response_name = "Response_" + self.unique_id
|
|
472
|
-
self.response_field =
|
|
500
|
+
self.response_field = create_model_field(
|
|
473
501
|
name=response_name,
|
|
474
502
|
type_=self.response_model,
|
|
475
503
|
mode="serialization",
|
|
@@ -482,9 +510,9 @@ class APIRoute(routing.Route):
|
|
|
482
510
|
# By being a new field, no inheritance will be passed as is. A new model
|
|
483
511
|
# will always be created.
|
|
484
512
|
# TODO: remove when deprecating Pydantic v1
|
|
485
|
-
self.secure_cloned_response_field: Optional[
|
|
486
|
-
|
|
487
|
-
|
|
513
|
+
self.secure_cloned_response_field: Optional[ModelField] = (
|
|
514
|
+
create_cloned_field(self.response_field)
|
|
515
|
+
)
|
|
488
516
|
else:
|
|
489
517
|
self.response_field = None # type: ignore
|
|
490
518
|
self.secure_cloned_response_field = None
|
|
@@ -502,7 +530,7 @@ class APIRoute(routing.Route):
|
|
|
502
530
|
additional_status_code
|
|
503
531
|
), f"Status code {additional_status_code} must not have a response body"
|
|
504
532
|
response_name = f"Response_{additional_status_code}_{self.unique_id}"
|
|
505
|
-
response_field =
|
|
533
|
+
response_field = create_model_field(name=response_name, type_=model)
|
|
506
534
|
response_fields[additional_status_code] = response_field
|
|
507
535
|
if response_fields:
|
|
508
536
|
self.response_fields: Dict[Union[int, str], ModelField] = response_fields
|
|
@@ -1308,6 +1336,10 @@ class APIRouter(routing.Router):
|
|
|
1308
1336
|
self.add_event_handler("startup", handler)
|
|
1309
1337
|
for handler in router.on_shutdown:
|
|
1310
1338
|
self.add_event_handler("shutdown", handler)
|
|
1339
|
+
self.lifespan_context = _merge_lifespan_context(
|
|
1340
|
+
self.lifespan_context,
|
|
1341
|
+
router.lifespan_context,
|
|
1342
|
+
)
|
|
1311
1343
|
|
|
1312
1344
|
def get(
|
|
1313
1345
|
self,
|
fastapi/utils.py
CHANGED
|
@@ -34,9 +34,9 @@ if TYPE_CHECKING: # pragma: nocover
|
|
|
34
34
|
from .routing import APIRoute
|
|
35
35
|
|
|
36
36
|
# Cache for `create_cloned_field`
|
|
37
|
-
_CLONED_TYPES_CACHE: MutableMapping[
|
|
38
|
-
|
|
39
|
-
|
|
37
|
+
_CLONED_TYPES_CACHE: MutableMapping[Type[BaseModel], Type[BaseModel]] = (
|
|
38
|
+
WeakKeyDictionary()
|
|
39
|
+
)
|
|
40
40
|
|
|
41
41
|
|
|
42
42
|
def is_body_allowed_for_status_code(status_code: Union[int, str, None]) -> bool:
|
|
@@ -60,9 +60,9 @@ def get_path_param_names(path: str) -> Set[str]:
|
|
|
60
60
|
return set(re.findall("{(.*?)}", path))
|
|
61
61
|
|
|
62
62
|
|
|
63
|
-
def
|
|
63
|
+
def create_model_field(
|
|
64
64
|
name: str,
|
|
65
|
-
type_:
|
|
65
|
+
type_: Any,
|
|
66
66
|
class_validators: Optional[Dict[str, Validator]] = None,
|
|
67
67
|
default: Optional[Any] = Undefined,
|
|
68
68
|
required: Union[bool, UndefinedType] = Undefined,
|
|
@@ -71,9 +71,6 @@ def create_response_field(
|
|
|
71
71
|
alias: Optional[str] = None,
|
|
72
72
|
mode: Literal["validation", "serialization"] = "validation",
|
|
73
73
|
) -> ModelField:
|
|
74
|
-
"""
|
|
75
|
-
Create a new response field. Raises if type_ is invalid.
|
|
76
|
-
"""
|
|
77
74
|
class_validators = class_validators or {}
|
|
78
75
|
if PYDANTIC_V2:
|
|
79
76
|
field_info = field_info or FieldInfo(
|
|
@@ -135,7 +132,7 @@ def create_cloned_field(
|
|
|
135
132
|
use_type.__fields__[f.name] = create_cloned_field(
|
|
136
133
|
f, cloned_types=cloned_types
|
|
137
134
|
)
|
|
138
|
-
new_field =
|
|
135
|
+
new_field = create_model_field(name=field.name, type_=use_type)
|
|
139
136
|
new_field.has_alias = field.has_alias # type: ignore[attr-defined]
|
|
140
137
|
new_field.alias = field.alias # type: ignore[misc]
|
|
141
138
|
new_field.class_validators = field.class_validators # type: ignore[attr-defined]
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: fastapi
|
|
3
|
-
Version: 0.112.
|
|
3
|
+
Version: 0.112.3
|
|
4
4
|
Summary: FastAPI framework, high performance, easy to learn, fast to code, ready for production
|
|
5
5
|
Author-Email: =?utf-8?q?Sebasti=C3=A1n_Ram=C3=ADrez?= <tiangolo@gmail.com>
|
|
6
6
|
Classifier: Intended Audience :: Information Technology
|
|
@@ -43,7 +43,7 @@ Requires-Dist: fastapi-cli[standard]>=0.0.5; extra == "standard"
|
|
|
43
43
|
Requires-Dist: httpx>=0.23.0; extra == "standard"
|
|
44
44
|
Requires-Dist: jinja2>=2.11.2; extra == "standard"
|
|
45
45
|
Requires-Dist: python-multipart>=0.0.7; extra == "standard"
|
|
46
|
-
Requires-Dist:
|
|
46
|
+
Requires-Dist: email-validator>=2.0.0; extra == "standard"
|
|
47
47
|
Requires-Dist: uvicorn[standard]>=0.12.0; extra == "standard"
|
|
48
48
|
Requires-Dist: fastapi-cli[standard]>=0.0.5; extra == "all"
|
|
49
49
|
Requires-Dist: httpx>=0.23.0; extra == "all"
|
|
@@ -53,7 +53,7 @@ Requires-Dist: itsdangerous>=1.1.0; extra == "all"
|
|
|
53
53
|
Requires-Dist: pyyaml>=5.3.1; extra == "all"
|
|
54
54
|
Requires-Dist: ujson!=4.0.2,!=4.1.0,!=4.2.0,!=4.3.0,!=5.0.0,!=5.1.0,>=4.0.1; extra == "all"
|
|
55
55
|
Requires-Dist: orjson>=3.2.1; extra == "all"
|
|
56
|
-
Requires-Dist:
|
|
56
|
+
Requires-Dist: email-validator>=2.0.0; extra == "all"
|
|
57
57
|
Requires-Dist: uvicorn[standard]>=0.12.0; extra == "all"
|
|
58
58
|
Requires-Dist: pydantic-settings>=2.0.0; extra == "all"
|
|
59
59
|
Requires-Dist: pydantic-extra-types>=2.0.0; extra == "all"
|
|
@@ -115,9 +115,8 @@ The key features are:
|
|
|
115
115
|
<a href="https://bump.sh/fastapi?utm_source=fastapi&utm_medium=referral&utm_campaign=sponsor" target="_blank" title="Automate FastAPI documentation generation with Bump.sh"><img src="https://fastapi.tiangolo.com/img/sponsors/bump-sh.svg"></a>
|
|
116
116
|
<a href="https://github.com/scalar/scalar/?utm_source=fastapi&utm_medium=website&utm_campaign=main-badge" target="_blank" title="Scalar: Beautiful Open-Source API References from Swagger/OpenAPI files"><img src="https://fastapi.tiangolo.com/img/sponsors/scalar.svg"></a>
|
|
117
117
|
<a href="https://www.propelauth.com/?utm_source=fastapi&utm_campaign=1223&utm_medium=mainbadge" target="_blank" title="Auth, user management and more for your B2B product"><img src="https://fastapi.tiangolo.com/img/sponsors/propelauth.png"></a>
|
|
118
|
-
<a href="https://docs.withcoherence.com/
|
|
118
|
+
<a href="https://docs.withcoherence.com/coherence-templates/full-stack-template/#fastapi?utm_medium=advertising&utm_source=fastapi&utm_campaign=docs" target="_blank" title="Coherence"><img src="https://fastapi.tiangolo.com/img/sponsors/coherence.png"></a>
|
|
119
119
|
<a href="https://www.mongodb.com/developer/languages/python/python-quickstart-fastapi/?utm_campaign=fastapi_framework&utm_source=fastapi_sponsorship&utm_medium=web_referral" target="_blank" title="Simplify Full Stack Development with FastAPI & MongoDB"><img src="https://fastapi.tiangolo.com/img/sponsors/mongodb.png"></a>
|
|
120
|
-
<a href="https://konghq.com/products/kong-konnect?utm_medium=referral&utm_source=github&utm_campaign=platform&utm_content=fast-api" target="_blank" title="Kong Konnect - API management platform"><img src="https://fastapi.tiangolo.com/img/sponsors/kong.png"></a>
|
|
121
120
|
<a href="https://zuplo.link/fastapi-gh" target="_blank" title="Zuplo: Scale, Protect, Document, and Monetize your FastAPI"><img src="https://fastapi.tiangolo.com/img/sponsors/zuplo.png"></a>
|
|
122
121
|
<a href="https://fine.dev?ref=fastapibadge" target="_blank" title="Fine's AI FastAPI Workflow: Effortlessly Deploy and Integrate FastAPI into Your Project"><img src="https://fastapi.tiangolo.com/img/sponsors/fine.png"></a>
|
|
123
122
|
<a href="https://liblab.com?utm_source=fastapi" target="_blank" title="liblab - Generate SDKs from FastAPI"><img src="https://fastapi.tiangolo.com/img/sponsors/liblab.png"></a>
|
|
@@ -195,6 +194,8 @@ FastAPI stands on the shoulders of giants:
|
|
|
195
194
|
|
|
196
195
|
## Installation
|
|
197
196
|
|
|
197
|
+
Create and activate a <a href="https://fastapi.tiangolo.com/virtual-environments/" class="external-link" target="_blank">virtual environment</a> and then install FastAPI:
|
|
198
|
+
|
|
198
199
|
<div class="termy">
|
|
199
200
|
|
|
200
201
|
```console
|
|
@@ -455,7 +456,7 @@ Coming back to the previous code example, **FastAPI** will:
|
|
|
455
456
|
* Check if there is an optional query parameter named `q` (as in `http://127.0.0.1:8000/items/foo?q=somequery`) for `GET` requests.
|
|
456
457
|
* As the `q` parameter is declared with `= None`, it is optional.
|
|
457
458
|
* Without the `None` it would be required (as is the body in the case with `PUT`).
|
|
458
|
-
* For `PUT` requests to `/items/{item_id}`,
|
|
459
|
+
* For `PUT` requests to `/items/{item_id}`, read the body as JSON:
|
|
459
460
|
* Check that it has a required attribute `name` that should be a `str`.
|
|
460
461
|
* Check that it has a required attribute `price` that has to be a `float`.
|
|
461
462
|
* Check that it has an optional attribute `is_offer`, that should be a `bool`, if present.
|
|
@@ -525,7 +526,7 @@ When you install FastAPI with `pip install "fastapi[standard]"` it comes the `st
|
|
|
525
526
|
|
|
526
527
|
Used by Pydantic:
|
|
527
528
|
|
|
528
|
-
* <a href="https://github.com/JoshData/python-email-validator" target="_blank"><code>
|
|
529
|
+
* <a href="https://github.com/JoshData/python-email-validator" target="_blank"><code>email-validator</code></a> - for email validation.
|
|
529
530
|
|
|
530
531
|
Used by Starlette:
|
|
531
532
|
|
|
@@ -1,18 +1,18 @@
|
|
|
1
|
-
fastapi-0.112.
|
|
2
|
-
fastapi-0.112.
|
|
3
|
-
fastapi-0.112.
|
|
4
|
-
fastapi-0.112.
|
|
5
|
-
fastapi/__init__.py,sha256=
|
|
1
|
+
fastapi-0.112.3.dist-info/METADATA,sha256=w1QfelBarqYSCtz6ly70TBP_VdihTpwEujW_K2sRg9E,27273
|
|
2
|
+
fastapi-0.112.3.dist-info/WHEEL,sha256=rSwsxJWe3vzyR5HCwjWXQruDgschpei4h_giTm0dJVE,90
|
|
3
|
+
fastapi-0.112.3.dist-info/entry_points.txt,sha256=Nn2-rs4A5_lQZko2b9QqCKQx9Irx0agGbxq3QLgjBxQ,46
|
|
4
|
+
fastapi-0.112.3.dist-info/licenses/LICENSE,sha256=Tsif_IFIW5f-xYSy1KlhAy7v_oNEU4lP2cEnSQbMdE4,1086
|
|
5
|
+
fastapi/__init__.py,sha256=o7tP2gKcsHMiiJNCKrN-GA3wqWjGR54B379fYdZ3_TA,1081
|
|
6
6
|
fastapi/__main__.py,sha256=bKePXLdO4SsVSM6r9SVoLickJDcR2c0cTOxZRKq26YQ,37
|
|
7
7
|
fastapi/_compat.py,sha256=OjE3FUZ0IPXqIJWKhoWKDNCHv4so-FQ-rfN8ngQZeFE,23134
|
|
8
|
-
fastapi/applications.py,sha256=
|
|
8
|
+
fastapi/applications.py,sha256=Ix-o9pQAWhEDf9J0Q1hZ0nBB1uP72c-Y3oiYzvrwqiM,176316
|
|
9
9
|
fastapi/background.py,sha256=rouLirxUANrcYC824MSMypXL_Qb2HYg2YZqaiEqbEKI,1768
|
|
10
10
|
fastapi/cli.py,sha256=OYhZb0NR_deuT5ofyPF2NoNBzZDNOP8Salef2nk-HqA,418
|
|
11
11
|
fastapi/concurrency.py,sha256=AYLnS4judDUmXsNRICtoKSP0prfYDcS8ehBtYW9JhQQ,1403
|
|
12
12
|
fastapi/datastructures.py,sha256=b2PEz77XGq-u3Ur1Inwk0AGjOsQZO49yF9C7IPJ15cY,5766
|
|
13
13
|
fastapi/dependencies/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
14
|
-
fastapi/dependencies/models.py,sha256
|
|
15
|
-
fastapi/dependencies/utils.py,sha256=
|
|
14
|
+
fastapi/dependencies/models.py,sha256=Pjl6vx-4nZ5Tta9kJa3-RfQKkXtCpS09-FhMgs9eWNs,1507
|
|
15
|
+
fastapi/dependencies/utils.py,sha256=2J36LBZMJTZ6jadjANaG-voTfzgs8JpROJCRtoLoSFA,30922
|
|
16
16
|
fastapi/encoders.py,sha256=LvwYmFeOz4tVwvgBoC5rvZnbr7hZr73KGrU8O7zSptU,11068
|
|
17
17
|
fastapi/exception_handlers.py,sha256=MBrIOA-ugjJDivIi4rSsUJBdTsjuzN76q4yh0q1COKw,1332
|
|
18
18
|
fastapi/exceptions.py,sha256=taNixuFEXb67lI1bnX1ubq8y8TseJ4yoPlWjyP0fTzk,4969
|
|
@@ -28,12 +28,12 @@ fastapi/openapi/constants.py,sha256=adGzmis1L1HJRTE3kJ5fmHS_Noq6tIY6pWv_SFzoFDU,
|
|
|
28
28
|
fastapi/openapi/docs.py,sha256=XcQq-ZbQdC5sI0gIGu5MoHK1q-OFaqws7-ORTo6sjY4,10348
|
|
29
29
|
fastapi/openapi/models.py,sha256=PqkxQiqcEgjKuhfUIWPZPQcyTcubtUCB3vcObLsB7VE,15397
|
|
30
30
|
fastapi/openapi/utils.py,sha256=asSbOKDuagDfpByNQvPy7OM0sqOBdUmqh64BH-n-5f0,22286
|
|
31
|
-
fastapi/param_functions.py,sha256=
|
|
32
|
-
fastapi/params.py,sha256=
|
|
31
|
+
fastapi/param_functions.py,sha256=Xp1h5RBdb-zlatMJ8_USkut2G9HJAgkMx7HBky9wHbM,64006
|
|
32
|
+
fastapi/params.py,sha256=CWumi-CkfxWSg4I2KpPxIvQyKdeW_Lnct9reHnrTwW8,28199
|
|
33
33
|
fastapi/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
34
34
|
fastapi/requests.py,sha256=zayepKFcienBllv3snmWI20Gk0oHNVLU4DDhqXBb4LU,142
|
|
35
35
|
fastapi/responses.py,sha256=QNQQlwpKhQoIPZTTWkpc9d_QGeGZ_aVQPaDV3nQ8m7c,1761
|
|
36
|
-
fastapi/routing.py,sha256=
|
|
36
|
+
fastapi/routing.py,sha256=e_H0k8q4HQl88mgi-Jdf-9hBfn2gTvDjixXrufDN3U0,175326
|
|
37
37
|
fastapi/security/__init__.py,sha256=bO8pNmxqVRXUjfl2mOKiVZLn0FpBQ61VUYVjmppnbJw,881
|
|
38
38
|
fastapi/security/api_key.py,sha256=_OqUUjEHG5_MT1IPAhXIGJRCPldTBdSww_DegFy_W8Y,9368
|
|
39
39
|
fastapi/security/base.py,sha256=dl4pvbC-RxjfbWgPtCWd8MVU-7CB2SZ22rJDXVCXO6c,141
|
|
@@ -45,6 +45,6 @@ fastapi/staticfiles.py,sha256=iirGIt3sdY2QZXd36ijs3Cj-T0FuGFda3cd90kM9Ikw,69
|
|
|
45
45
|
fastapi/templating.py,sha256=4zsuTWgcjcEainMJFAlW6-gnslm6AgOS1SiiDWfmQxk,76
|
|
46
46
|
fastapi/testclient.py,sha256=nBvaAmX66YldReJNZXPOk1sfuo2Q6hs8bOvIaCep6LQ,66
|
|
47
47
|
fastapi/types.py,sha256=nFb36sK3DSoqoyo7Miwy3meKK5UdFBgkAgLSzQlUVyI,383
|
|
48
|
-
fastapi/utils.py,sha256=
|
|
48
|
+
fastapi/utils.py,sha256=y8Bj5ttMaI9tS4D60OUgXqKnktBr99NdYUnHHV9LgoY,7948
|
|
49
49
|
fastapi/websockets.py,sha256=419uncYObEKZG0YcrXscfQQYLSWoE10jqxVMetGdR98,222
|
|
50
|
-
fastapi-0.112.
|
|
50
|
+
fastapi-0.112.3.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|