fastapi 0.112.3__py3-none-any.whl → 0.112.4__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of fastapi might be problematic. Click here for more details.
- fastapi/__init__.py +1 -1
- fastapi/_compat.py +15 -0
- fastapi/dependencies/utils.py +172 -128
- fastapi/param_functions.py +2 -2
- fastapi/params.py +1 -2
- fastapi/routing.py +23 -3
- {fastapi-0.112.3.dist-info → fastapi-0.112.4.dist-info}/METADATA +1 -1
- {fastapi-0.112.3.dist-info → fastapi-0.112.4.dist-info}/RECORD +11 -11
- {fastapi-0.112.3.dist-info → fastapi-0.112.4.dist-info}/WHEEL +0 -0
- {fastapi-0.112.3.dist-info → fastapi-0.112.4.dist-info}/entry_points.txt +0 -0
- {fastapi-0.112.3.dist-info → fastapi-0.112.4.dist-info}/licenses/LICENSE +0 -0
fastapi/__init__.py
CHANGED
fastapi/_compat.py
CHANGED
|
@@ -279,6 +279,12 @@ if PYDANTIC_V2:
|
|
|
279
279
|
BodyModel: Type[BaseModel] = create_model(model_name, **field_params) # type: ignore[call-overload]
|
|
280
280
|
return BodyModel
|
|
281
281
|
|
|
282
|
+
def get_model_fields(model: Type[BaseModel]) -> List[ModelField]:
|
|
283
|
+
return [
|
|
284
|
+
ModelField(field_info=field_info, name=name)
|
|
285
|
+
for name, field_info in model.model_fields.items()
|
|
286
|
+
]
|
|
287
|
+
|
|
282
288
|
else:
|
|
283
289
|
from fastapi.openapi.constants import REF_PREFIX as REF_PREFIX
|
|
284
290
|
from pydantic import AnyUrl as Url # noqa: F401
|
|
@@ -513,6 +519,9 @@ else:
|
|
|
513
519
|
BodyModel.__fields__[f.name] = f # type: ignore[index]
|
|
514
520
|
return BodyModel
|
|
515
521
|
|
|
522
|
+
def get_model_fields(model: Type[BaseModel]) -> List[ModelField]:
|
|
523
|
+
return list(model.__fields__.values()) # type: ignore[attr-defined]
|
|
524
|
+
|
|
516
525
|
|
|
517
526
|
def _regenerate_error_with_loc(
|
|
518
527
|
*, errors: Sequence[Any], loc_prefix: Tuple[Union[str, int], ...]
|
|
@@ -532,6 +541,12 @@ def _annotation_is_sequence(annotation: Union[Type[Any], None]) -> bool:
|
|
|
532
541
|
|
|
533
542
|
|
|
534
543
|
def field_annotation_is_sequence(annotation: Union[Type[Any], None]) -> bool:
|
|
544
|
+
origin = get_origin(annotation)
|
|
545
|
+
if origin is Union or origin is UnionType:
|
|
546
|
+
for arg in get_args(annotation):
|
|
547
|
+
if field_annotation_is_sequence(arg):
|
|
548
|
+
return True
|
|
549
|
+
return False
|
|
535
550
|
return _annotation_is_sequence(annotation) or _annotation_is_sequence(
|
|
536
551
|
get_origin(annotation)
|
|
537
552
|
)
|
fastapi/dependencies/utils.py
CHANGED
|
@@ -59,7 +59,13 @@ from fastapi.utils import create_model_field, get_path_param_names
|
|
|
59
59
|
from pydantic.fields import FieldInfo
|
|
60
60
|
from starlette.background import BackgroundTasks as StarletteBackgroundTasks
|
|
61
61
|
from starlette.concurrency import run_in_threadpool
|
|
62
|
-
from starlette.datastructures import
|
|
62
|
+
from starlette.datastructures import (
|
|
63
|
+
FormData,
|
|
64
|
+
Headers,
|
|
65
|
+
ImmutableMultiDict,
|
|
66
|
+
QueryParams,
|
|
67
|
+
UploadFile,
|
|
68
|
+
)
|
|
63
69
|
from starlette.requests import HTTPConnection, Request
|
|
64
70
|
from starlette.responses import Response
|
|
65
71
|
from starlette.websockets import WebSocket
|
|
@@ -282,7 +288,7 @@ def get_dependant(
|
|
|
282
288
|
), f"Cannot specify multiple FastAPI annotations for {param_name!r}"
|
|
283
289
|
continue
|
|
284
290
|
assert param_details.field is not None
|
|
285
|
-
if
|
|
291
|
+
if isinstance(param_details.field.field_info, params.Body):
|
|
286
292
|
dependant.body_params.append(param_details.field)
|
|
287
293
|
else:
|
|
288
294
|
add_param_to_fields(field=param_details.field, dependant=dependant)
|
|
@@ -466,29 +472,16 @@ def analyze_param(
|
|
|
466
472
|
required=field_info.default in (Required, Undefined),
|
|
467
473
|
field_info=field_info,
|
|
468
474
|
)
|
|
475
|
+
if is_path_param:
|
|
476
|
+
assert is_scalar_field(
|
|
477
|
+
field=field
|
|
478
|
+
), "Path params must be of one of the supported types"
|
|
479
|
+
elif isinstance(field_info, params.Query):
|
|
480
|
+
assert is_scalar_field(field) or is_scalar_sequence_field(field)
|
|
469
481
|
|
|
470
482
|
return ParamDetails(type_annotation=type_annotation, depends=depends, field=field)
|
|
471
483
|
|
|
472
484
|
|
|
473
|
-
def is_body_param(*, param_field: ModelField, is_path_param: bool) -> bool:
|
|
474
|
-
if is_path_param:
|
|
475
|
-
assert is_scalar_field(
|
|
476
|
-
field=param_field
|
|
477
|
-
), "Path params must be of one of the supported types"
|
|
478
|
-
return False
|
|
479
|
-
elif is_scalar_field(field=param_field):
|
|
480
|
-
return False
|
|
481
|
-
elif isinstance(
|
|
482
|
-
param_field.field_info, (params.Query, params.Header)
|
|
483
|
-
) and is_scalar_sequence_field(param_field):
|
|
484
|
-
return False
|
|
485
|
-
else:
|
|
486
|
-
assert isinstance(
|
|
487
|
-
param_field.field_info, params.Body
|
|
488
|
-
), f"Param: {param_field.name} can only be a request body, using Body()"
|
|
489
|
-
return True
|
|
490
|
-
|
|
491
|
-
|
|
492
485
|
def add_param_to_fields(*, field: ModelField, dependant: Dependant) -> None:
|
|
493
486
|
field_info = field.field_info
|
|
494
487
|
field_info_in = getattr(field_info, "in_", None)
|
|
@@ -557,6 +550,7 @@ async def solve_dependencies(
|
|
|
557
550
|
dependency_overrides_provider: Optional[Any] = None,
|
|
558
551
|
dependency_cache: Optional[Dict[Tuple[Callable[..., Any], Tuple[str]], Any]] = None,
|
|
559
552
|
async_exit_stack: AsyncExitStack,
|
|
553
|
+
embed_body_fields: bool,
|
|
560
554
|
) -> SolvedDependency:
|
|
561
555
|
values: Dict[str, Any] = {}
|
|
562
556
|
errors: List[Any] = []
|
|
@@ -598,6 +592,7 @@ async def solve_dependencies(
|
|
|
598
592
|
dependency_overrides_provider=dependency_overrides_provider,
|
|
599
593
|
dependency_cache=dependency_cache,
|
|
600
594
|
async_exit_stack=async_exit_stack,
|
|
595
|
+
embed_body_fields=embed_body_fields,
|
|
601
596
|
)
|
|
602
597
|
background_tasks = solved_result.background_tasks
|
|
603
598
|
dependency_cache.update(solved_result.dependency_cache)
|
|
@@ -640,7 +635,9 @@ async def solve_dependencies(
|
|
|
640
635
|
body_values,
|
|
641
636
|
body_errors,
|
|
642
637
|
) = await request_body_to_args( # body_params checked above
|
|
643
|
-
|
|
638
|
+
body_fields=dependant.body_params,
|
|
639
|
+
received_body=body,
|
|
640
|
+
embed_body_fields=embed_body_fields,
|
|
644
641
|
)
|
|
645
642
|
values.update(body_values)
|
|
646
643
|
errors.extend(body_errors)
|
|
@@ -669,138 +666,185 @@ async def solve_dependencies(
|
|
|
669
666
|
)
|
|
670
667
|
|
|
671
668
|
|
|
669
|
+
def _validate_value_with_model_field(
|
|
670
|
+
*, field: ModelField, value: Any, values: Dict[str, Any], loc: Tuple[str, ...]
|
|
671
|
+
) -> Tuple[Any, List[Any]]:
|
|
672
|
+
if value is None:
|
|
673
|
+
if field.required:
|
|
674
|
+
return None, [get_missing_field_error(loc=loc)]
|
|
675
|
+
else:
|
|
676
|
+
return deepcopy(field.default), []
|
|
677
|
+
v_, errors_ = field.validate(value, values, loc=loc)
|
|
678
|
+
if isinstance(errors_, ErrorWrapper):
|
|
679
|
+
return None, [errors_]
|
|
680
|
+
elif isinstance(errors_, list):
|
|
681
|
+
new_errors = _regenerate_error_with_loc(errors=errors_, loc_prefix=())
|
|
682
|
+
return None, new_errors
|
|
683
|
+
else:
|
|
684
|
+
return v_, []
|
|
685
|
+
|
|
686
|
+
|
|
687
|
+
def _get_multidict_value(field: ModelField, values: Mapping[str, Any]) -> Any:
|
|
688
|
+
if is_sequence_field(field) and isinstance(values, (ImmutableMultiDict, Headers)):
|
|
689
|
+
value = values.getlist(field.alias)
|
|
690
|
+
else:
|
|
691
|
+
value = values.get(field.alias, None)
|
|
692
|
+
if (
|
|
693
|
+
value is None
|
|
694
|
+
or (
|
|
695
|
+
isinstance(field.field_info, params.Form)
|
|
696
|
+
and isinstance(value, str) # For type checks
|
|
697
|
+
and value == ""
|
|
698
|
+
)
|
|
699
|
+
or (is_sequence_field(field) and len(value) == 0)
|
|
700
|
+
):
|
|
701
|
+
if field.required:
|
|
702
|
+
return
|
|
703
|
+
else:
|
|
704
|
+
return deepcopy(field.default)
|
|
705
|
+
return value
|
|
706
|
+
|
|
707
|
+
|
|
672
708
|
def request_params_to_args(
|
|
673
|
-
|
|
709
|
+
fields: Sequence[ModelField],
|
|
674
710
|
received_params: Union[Mapping[str, Any], QueryParams, Headers],
|
|
675
711
|
) -> Tuple[Dict[str, Any], List[Any]]:
|
|
676
|
-
values = {}
|
|
712
|
+
values: Dict[str, Any] = {}
|
|
677
713
|
errors = []
|
|
678
|
-
for field in
|
|
679
|
-
|
|
680
|
-
received_params, (QueryParams, Headers)
|
|
681
|
-
):
|
|
682
|
-
value = received_params.getlist(field.alias) or field.default
|
|
683
|
-
else:
|
|
684
|
-
value = received_params.get(field.alias)
|
|
714
|
+
for field in fields:
|
|
715
|
+
value = _get_multidict_value(field, received_params)
|
|
685
716
|
field_info = field.field_info
|
|
686
717
|
assert isinstance(
|
|
687
718
|
field_info, params.Param
|
|
688
719
|
), "Params must be subclasses of Param"
|
|
689
720
|
loc = (field_info.in_.value, field.alias)
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
continue
|
|
696
|
-
v_, errors_ = field.validate(value, values, loc=loc)
|
|
697
|
-
if isinstance(errors_, ErrorWrapper):
|
|
698
|
-
errors.append(errors_)
|
|
699
|
-
elif isinstance(errors_, list):
|
|
700
|
-
new_errors = _regenerate_error_with_loc(errors=errors_, loc_prefix=())
|
|
701
|
-
errors.extend(new_errors)
|
|
721
|
+
v_, errors_ = _validate_value_with_model_field(
|
|
722
|
+
field=field, value=value, values=values, loc=loc
|
|
723
|
+
)
|
|
724
|
+
if errors_:
|
|
725
|
+
errors.extend(errors_)
|
|
702
726
|
else:
|
|
703
727
|
values[field.name] = v_
|
|
704
728
|
return values, errors
|
|
705
729
|
|
|
706
730
|
|
|
731
|
+
def _should_embed_body_fields(fields: List[ModelField]) -> bool:
|
|
732
|
+
if not fields:
|
|
733
|
+
return False
|
|
734
|
+
# More than one dependency could have the same field, it would show up as multiple
|
|
735
|
+
# fields but it's the same one, so count them by name
|
|
736
|
+
body_param_names_set = {field.name for field in fields}
|
|
737
|
+
# A top level field has to be a single field, not multiple
|
|
738
|
+
if len(body_param_names_set) > 1:
|
|
739
|
+
return True
|
|
740
|
+
first_field = fields[0]
|
|
741
|
+
# If it explicitly specifies it is embedded, it has to be embedded
|
|
742
|
+
if getattr(first_field.field_info, "embed", None):
|
|
743
|
+
return True
|
|
744
|
+
# If it's a Form (or File) field, it has to be a BaseModel to be top level
|
|
745
|
+
# otherwise it has to be embedded, so that the key value pair can be extracted
|
|
746
|
+
if isinstance(first_field.field_info, params.Form):
|
|
747
|
+
return True
|
|
748
|
+
return False
|
|
749
|
+
|
|
750
|
+
|
|
751
|
+
async def _extract_form_body(
|
|
752
|
+
body_fields: List[ModelField],
|
|
753
|
+
received_body: FormData,
|
|
754
|
+
) -> Dict[str, Any]:
|
|
755
|
+
values = {}
|
|
756
|
+
first_field = body_fields[0]
|
|
757
|
+
first_field_info = first_field.field_info
|
|
758
|
+
|
|
759
|
+
for field in body_fields:
|
|
760
|
+
value = _get_multidict_value(field, received_body)
|
|
761
|
+
if (
|
|
762
|
+
isinstance(first_field_info, params.File)
|
|
763
|
+
and is_bytes_field(field)
|
|
764
|
+
and isinstance(value, UploadFile)
|
|
765
|
+
):
|
|
766
|
+
value = await value.read()
|
|
767
|
+
elif (
|
|
768
|
+
is_bytes_sequence_field(field)
|
|
769
|
+
and isinstance(first_field_info, params.File)
|
|
770
|
+
and value_is_sequence(value)
|
|
771
|
+
):
|
|
772
|
+
# For types
|
|
773
|
+
assert isinstance(value, sequence_types) # type: ignore[arg-type]
|
|
774
|
+
results: List[Union[bytes, str]] = []
|
|
775
|
+
|
|
776
|
+
async def process_fn(
|
|
777
|
+
fn: Callable[[], Coroutine[Any, Any, Any]],
|
|
778
|
+
) -> None:
|
|
779
|
+
result = await fn()
|
|
780
|
+
results.append(result) # noqa: B023
|
|
781
|
+
|
|
782
|
+
async with anyio.create_task_group() as tg:
|
|
783
|
+
for sub_value in value:
|
|
784
|
+
tg.start_soon(process_fn, sub_value.read)
|
|
785
|
+
value = serialize_sequence_value(field=field, value=results)
|
|
786
|
+
values[field.name] = value
|
|
787
|
+
return values
|
|
788
|
+
|
|
789
|
+
|
|
707
790
|
async def request_body_to_args(
|
|
708
|
-
|
|
791
|
+
body_fields: List[ModelField],
|
|
709
792
|
received_body: Optional[Union[Dict[str, Any], FormData]],
|
|
793
|
+
embed_body_fields: bool,
|
|
710
794
|
) -> Tuple[Dict[str, Any], List[Dict[str, Any]]]:
|
|
711
|
-
values = {}
|
|
795
|
+
values: Dict[str, Any] = {}
|
|
712
796
|
errors: List[Dict[str, Any]] = []
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
except AttributeError:
|
|
736
|
-
errors.append(get_missing_field_error(loc))
|
|
737
|
-
continue
|
|
738
|
-
if (
|
|
739
|
-
value is None
|
|
740
|
-
or (isinstance(field_info, params.Form) and value == "")
|
|
741
|
-
or (
|
|
742
|
-
isinstance(field_info, params.Form)
|
|
743
|
-
and is_sequence_field(field)
|
|
744
|
-
and len(value) == 0
|
|
745
|
-
)
|
|
746
|
-
):
|
|
747
|
-
if field.required:
|
|
748
|
-
errors.append(get_missing_field_error(loc))
|
|
749
|
-
else:
|
|
750
|
-
values[field.name] = deepcopy(field.default)
|
|
797
|
+
assert body_fields, "request_body_to_args() should be called with fields"
|
|
798
|
+
single_not_embedded_field = len(body_fields) == 1 and not embed_body_fields
|
|
799
|
+
first_field = body_fields[0]
|
|
800
|
+
body_to_process = received_body
|
|
801
|
+
if isinstance(received_body, FormData):
|
|
802
|
+
body_to_process = await _extract_form_body(body_fields, received_body)
|
|
803
|
+
|
|
804
|
+
if single_not_embedded_field:
|
|
805
|
+
loc: Tuple[str, ...] = ("body",)
|
|
806
|
+
v_, errors_ = _validate_value_with_model_field(
|
|
807
|
+
field=first_field, value=body_to_process, values=values, loc=loc
|
|
808
|
+
)
|
|
809
|
+
return {first_field.name: v_}, errors_
|
|
810
|
+
for field in body_fields:
|
|
811
|
+
loc = ("body", field.alias)
|
|
812
|
+
value: Optional[Any] = None
|
|
813
|
+
if body_to_process is not None:
|
|
814
|
+
try:
|
|
815
|
+
value = body_to_process.get(field.alias)
|
|
816
|
+
# If the received body is a list, not a dict
|
|
817
|
+
except AttributeError:
|
|
818
|
+
errors.append(get_missing_field_error(loc))
|
|
751
819
|
continue
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
)
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
is_bytes_sequence_field(field)
|
|
760
|
-
and isinstance(field_info, params.File)
|
|
761
|
-
and value_is_sequence(value)
|
|
762
|
-
):
|
|
763
|
-
# For types
|
|
764
|
-
assert isinstance(value, sequence_types) # type: ignore[arg-type]
|
|
765
|
-
results: List[Union[bytes, str]] = []
|
|
766
|
-
|
|
767
|
-
async def process_fn(
|
|
768
|
-
fn: Callable[[], Coroutine[Any, Any, Any]],
|
|
769
|
-
) -> None:
|
|
770
|
-
result = await fn()
|
|
771
|
-
results.append(result) # noqa: B023
|
|
772
|
-
|
|
773
|
-
async with anyio.create_task_group() as tg:
|
|
774
|
-
for sub_value in value:
|
|
775
|
-
tg.start_soon(process_fn, sub_value.read)
|
|
776
|
-
value = serialize_sequence_value(field=field, value=results)
|
|
777
|
-
|
|
778
|
-
v_, errors_ = field.validate(value, values, loc=loc)
|
|
779
|
-
|
|
780
|
-
if isinstance(errors_, list):
|
|
781
|
-
errors.extend(errors_)
|
|
782
|
-
elif errors_:
|
|
783
|
-
errors.append(errors_)
|
|
784
|
-
else:
|
|
785
|
-
values[field.name] = v_
|
|
820
|
+
v_, errors_ = _validate_value_with_model_field(
|
|
821
|
+
field=field, value=value, values=values, loc=loc
|
|
822
|
+
)
|
|
823
|
+
if errors_:
|
|
824
|
+
errors.extend(errors_)
|
|
825
|
+
else:
|
|
826
|
+
values[field.name] = v_
|
|
786
827
|
return values, errors
|
|
787
828
|
|
|
788
829
|
|
|
789
|
-
def get_body_field(
|
|
790
|
-
flat_dependant
|
|
830
|
+
def get_body_field(
|
|
831
|
+
*, flat_dependant: Dependant, name: str, embed_body_fields: bool
|
|
832
|
+
) -> Optional[ModelField]:
|
|
833
|
+
"""
|
|
834
|
+
Get a ModelField representing the request body for a path operation, combining
|
|
835
|
+
all body parameters into a single field if necessary.
|
|
836
|
+
|
|
837
|
+
Used to check if it's form data (with `isinstance(body_field, params.Form)`)
|
|
838
|
+
or JSON and to generate the JSON Schema for a request body.
|
|
839
|
+
|
|
840
|
+
This is **not** used to validate/parse the request body, that's done with each
|
|
841
|
+
individual body parameter.
|
|
842
|
+
"""
|
|
791
843
|
if not flat_dependant.body_params:
|
|
792
844
|
return None
|
|
793
845
|
first_param = flat_dependant.body_params[0]
|
|
794
|
-
|
|
795
|
-
embed = getattr(field_info, "embed", None)
|
|
796
|
-
body_param_names_set = {param.name for param in flat_dependant.body_params}
|
|
797
|
-
if len(body_param_names_set) == 1 and not embed:
|
|
846
|
+
if not embed_body_fields:
|
|
798
847
|
return first_param
|
|
799
|
-
# If one field requires to embed, all have to be embedded
|
|
800
|
-
# in case a sub-dependency is evaluated with a single unique body field
|
|
801
|
-
# That is combined (embedded) with other body fields
|
|
802
|
-
for param in flat_dependant.body_params:
|
|
803
|
-
setattr(param.field_info, "embed", True) # noqa: B010
|
|
804
848
|
model_name = "Body_" + name
|
|
805
849
|
BodyModel = create_body_model(
|
|
806
850
|
fields=flat_dependant.body_params, model_name=model_name
|
fastapi/param_functions.py
CHANGED
|
@@ -1282,7 +1282,7 @@ def Body( # noqa: N802
|
|
|
1282
1282
|
),
|
|
1283
1283
|
] = _Unset,
|
|
1284
1284
|
embed: Annotated[
|
|
1285
|
-
bool,
|
|
1285
|
+
Union[bool, None],
|
|
1286
1286
|
Doc(
|
|
1287
1287
|
"""
|
|
1288
1288
|
When `embed` is `True`, the parameter will be expected in a JSON body as a
|
|
@@ -1294,7 +1294,7 @@ def Body( # noqa: N802
|
|
|
1294
1294
|
[FastAPI docs for Body - Multiple Parameters](https://fastapi.tiangolo.com/tutorial/body-multiple-params/#embed-a-single-body-parameter).
|
|
1295
1295
|
"""
|
|
1296
1296
|
),
|
|
1297
|
-
] =
|
|
1297
|
+
] = None,
|
|
1298
1298
|
media_type: Annotated[
|
|
1299
1299
|
str,
|
|
1300
1300
|
Doc(
|
fastapi/params.py
CHANGED
|
@@ -479,7 +479,7 @@ class Body(FieldInfo):
|
|
|
479
479
|
*,
|
|
480
480
|
default_factory: Union[Callable[[], Any], None] = _Unset,
|
|
481
481
|
annotation: Optional[Any] = None,
|
|
482
|
-
embed: bool =
|
|
482
|
+
embed: Union[bool, None] = None,
|
|
483
483
|
media_type: str = "application/json",
|
|
484
484
|
alias: Optional[str] = None,
|
|
485
485
|
alias_priority: Union[int, None] = _Unset,
|
|
@@ -642,7 +642,6 @@ class Form(Body):
|
|
|
642
642
|
default=default,
|
|
643
643
|
default_factory=default_factory,
|
|
644
644
|
annotation=annotation,
|
|
645
|
-
embed=True,
|
|
646
645
|
media_type=media_type,
|
|
647
646
|
alias=alias,
|
|
648
647
|
alias_priority=alias_priority,
|
fastapi/routing.py
CHANGED
|
@@ -33,8 +33,10 @@ from fastapi._compat import (
|
|
|
33
33
|
from fastapi.datastructures import Default, DefaultPlaceholder
|
|
34
34
|
from fastapi.dependencies.models import Dependant
|
|
35
35
|
from fastapi.dependencies.utils import (
|
|
36
|
+
_should_embed_body_fields,
|
|
36
37
|
get_body_field,
|
|
37
38
|
get_dependant,
|
|
39
|
+
get_flat_dependant,
|
|
38
40
|
get_parameterless_sub_dependant,
|
|
39
41
|
get_typed_return_annotation,
|
|
40
42
|
solve_dependencies,
|
|
@@ -225,6 +227,7 @@ def get_request_handler(
|
|
|
225
227
|
response_model_exclude_defaults: bool = False,
|
|
226
228
|
response_model_exclude_none: bool = False,
|
|
227
229
|
dependency_overrides_provider: Optional[Any] = None,
|
|
230
|
+
embed_body_fields: bool = False,
|
|
228
231
|
) -> Callable[[Request], Coroutine[Any, Any, Response]]:
|
|
229
232
|
assert dependant.call is not None, "dependant.call must be a function"
|
|
230
233
|
is_coroutine = asyncio.iscoroutinefunction(dependant.call)
|
|
@@ -291,6 +294,7 @@ def get_request_handler(
|
|
|
291
294
|
body=body,
|
|
292
295
|
dependency_overrides_provider=dependency_overrides_provider,
|
|
293
296
|
async_exit_stack=async_exit_stack,
|
|
297
|
+
embed_body_fields=embed_body_fields,
|
|
294
298
|
)
|
|
295
299
|
errors = solved_result.errors
|
|
296
300
|
if not errors:
|
|
@@ -354,7 +358,9 @@ def get_request_handler(
|
|
|
354
358
|
|
|
355
359
|
|
|
356
360
|
def get_websocket_app(
|
|
357
|
-
dependant: Dependant,
|
|
361
|
+
dependant: Dependant,
|
|
362
|
+
dependency_overrides_provider: Optional[Any] = None,
|
|
363
|
+
embed_body_fields: bool = False,
|
|
358
364
|
) -> Callable[[WebSocket], Coroutine[Any, Any, Any]]:
|
|
359
365
|
async def app(websocket: WebSocket) -> None:
|
|
360
366
|
async with AsyncExitStack() as async_exit_stack:
|
|
@@ -367,6 +373,7 @@ def get_websocket_app(
|
|
|
367
373
|
dependant=dependant,
|
|
368
374
|
dependency_overrides_provider=dependency_overrides_provider,
|
|
369
375
|
async_exit_stack=async_exit_stack,
|
|
376
|
+
embed_body_fields=embed_body_fields,
|
|
370
377
|
)
|
|
371
378
|
if solved_result.errors:
|
|
372
379
|
raise WebSocketRequestValidationError(
|
|
@@ -399,11 +406,15 @@ class APIWebSocketRoute(routing.WebSocketRoute):
|
|
|
399
406
|
0,
|
|
400
407
|
get_parameterless_sub_dependant(depends=depends, path=self.path_format),
|
|
401
408
|
)
|
|
402
|
-
|
|
409
|
+
self._flat_dependant = get_flat_dependant(self.dependant)
|
|
410
|
+
self._embed_body_fields = _should_embed_body_fields(
|
|
411
|
+
self._flat_dependant.body_params
|
|
412
|
+
)
|
|
403
413
|
self.app = websocket_session(
|
|
404
414
|
get_websocket_app(
|
|
405
415
|
dependant=self.dependant,
|
|
406
416
|
dependency_overrides_provider=dependency_overrides_provider,
|
|
417
|
+
embed_body_fields=self._embed_body_fields,
|
|
407
418
|
)
|
|
408
419
|
)
|
|
409
420
|
|
|
@@ -544,7 +555,15 @@ class APIRoute(routing.Route):
|
|
|
544
555
|
0,
|
|
545
556
|
get_parameterless_sub_dependant(depends=depends, path=self.path_format),
|
|
546
557
|
)
|
|
547
|
-
self.
|
|
558
|
+
self._flat_dependant = get_flat_dependant(self.dependant)
|
|
559
|
+
self._embed_body_fields = _should_embed_body_fields(
|
|
560
|
+
self._flat_dependant.body_params
|
|
561
|
+
)
|
|
562
|
+
self.body_field = get_body_field(
|
|
563
|
+
flat_dependant=self._flat_dependant,
|
|
564
|
+
name=self.unique_id,
|
|
565
|
+
embed_body_fields=self._embed_body_fields,
|
|
566
|
+
)
|
|
548
567
|
self.app = request_response(self.get_route_handler())
|
|
549
568
|
|
|
550
569
|
def get_route_handler(self) -> Callable[[Request], Coroutine[Any, Any, Response]]:
|
|
@@ -561,6 +580,7 @@ class APIRoute(routing.Route):
|
|
|
561
580
|
response_model_exclude_defaults=self.response_model_exclude_defaults,
|
|
562
581
|
response_model_exclude_none=self.response_model_exclude_none,
|
|
563
582
|
dependency_overrides_provider=self.dependency_overrides_provider,
|
|
583
|
+
embed_body_fields=self._embed_body_fields,
|
|
564
584
|
)
|
|
565
585
|
|
|
566
586
|
def matches(self, scope: Scope) -> Tuple[Match, Scope]:
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: fastapi
|
|
3
|
-
Version: 0.112.
|
|
3
|
+
Version: 0.112.4
|
|
4
4
|
Summary: FastAPI framework, high performance, easy to learn, fast to code, ready for production
|
|
5
5
|
Author-Email: =?utf-8?q?Sebasti=C3=A1n_Ram=C3=ADrez?= <tiangolo@gmail.com>
|
|
6
6
|
Classifier: Intended Audience :: Information Technology
|
|
@@ -1,10 +1,10 @@
|
|
|
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.4.dist-info/METADATA,sha256=5HI7E6Ktp7E6DHqe-EAOrbdi9cOuiJqLfxqBCeJGmcQ,27273
|
|
2
|
+
fastapi-0.112.4.dist-info/WHEEL,sha256=rSwsxJWe3vzyR5HCwjWXQruDgschpei4h_giTm0dJVE,90
|
|
3
|
+
fastapi-0.112.4.dist-info/entry_points.txt,sha256=Nn2-rs4A5_lQZko2b9QqCKQx9Irx0agGbxq3QLgjBxQ,46
|
|
4
|
+
fastapi-0.112.4.dist-info/licenses/LICENSE,sha256=Tsif_IFIW5f-xYSy1KlhAy7v_oNEU4lP2cEnSQbMdE4,1086
|
|
5
|
+
fastapi/__init__.py,sha256=6SxOneGIhP_aHxYYRO2T6iyZXYjC_rjss7xd3XsWl7s,1081
|
|
6
6
|
fastapi/__main__.py,sha256=bKePXLdO4SsVSM6r9SVoLickJDcR2c0cTOxZRKq26YQ,37
|
|
7
|
-
fastapi/_compat.py,sha256=
|
|
7
|
+
fastapi/_compat.py,sha256=9AipVKxefipp4ymECk3VhrryBi-Hbh8A7zsljaWdGAk,23723
|
|
8
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
|
|
@@ -12,7 +12,7 @@ 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
14
|
fastapi/dependencies/models.py,sha256=Pjl6vx-4nZ5Tta9kJa3-RfQKkXtCpS09-FhMgs9eWNs,1507
|
|
15
|
-
fastapi/dependencies/utils.py,sha256=
|
|
15
|
+
fastapi/dependencies/utils.py,sha256=k3bs2iNotFYcwO_RihkdJjGrBAr2SNOxf6j5u7vu9gM,31980
|
|
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=uQzNlihlhM80u4Xbstz__D3L3yxpTqLmsC-Hra1WfqE,64018
|
|
32
|
+
fastapi/params.py,sha256=e_ZHoUohqWWbquiBL5AHqdsKJmTMLZzAPii6eCYR3tk,28187
|
|
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=LnYhRsafzHporfz4aDUmEk8qDU2tarLZNjHaKy3RB7w,176148
|
|
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
|
|
@@ -47,4 +47,4 @@ fastapi/testclient.py,sha256=nBvaAmX66YldReJNZXPOk1sfuo2Q6hs8bOvIaCep6LQ,66
|
|
|
47
47
|
fastapi/types.py,sha256=nFb36sK3DSoqoyo7Miwy3meKK5UdFBgkAgLSzQlUVyI,383
|
|
48
48
|
fastapi/utils.py,sha256=y8Bj5ttMaI9tS4D60OUgXqKnktBr99NdYUnHHV9LgoY,7948
|
|
49
49
|
fastapi/websockets.py,sha256=419uncYObEKZG0YcrXscfQQYLSWoE10jqxVMetGdR98,222
|
|
50
|
-
fastapi-0.112.
|
|
50
|
+
fastapi-0.112.4.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|