fastapi 0.96.1__py3-none-any.whl → 0.100.0b1__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/applications.py CHANGED
@@ -15,12 +15,12 @@ from typing import (
15
15
 
16
16
  from fastapi import routing
17
17
  from fastapi.datastructures import Default, DefaultPlaceholder
18
- from fastapi.encoders import DictIntStrAny, SetIntStr
19
18
  from fastapi.exception_handlers import (
20
19
  http_exception_handler,
21
20
  request_validation_exception_handler,
21
+ websocket_request_validation_exception_handler,
22
22
  )
23
- from fastapi.exceptions import RequestValidationError
23
+ from fastapi.exceptions import RequestValidationError, WebSocketRequestValidationError
24
24
  from fastapi.logger import logger
25
25
  from fastapi.middleware.asyncexitstack import AsyncExitStackMiddleware
26
26
  from fastapi.openapi.docs import (
@@ -30,7 +30,7 @@ from fastapi.openapi.docs import (
30
30
  )
31
31
  from fastapi.openapi.utils import get_openapi
32
32
  from fastapi.params import Depends
33
- from fastapi.types import DecoratedCallable
33
+ from fastapi.types import DecoratedCallable, IncEx
34
34
  from fastapi.utils import generate_unique_id
35
35
  from starlette.applications import Starlette
36
36
  from starlette.datastructures import State
@@ -145,6 +145,11 @@ class FastAPI(Starlette):
145
145
  self.exception_handlers.setdefault(
146
146
  RequestValidationError, request_validation_exception_handler
147
147
  )
148
+ self.exception_handlers.setdefault(
149
+ WebSocketRequestValidationError,
150
+ # Starlette still has incorrect type specification for the handlers
151
+ websocket_request_validation_exception_handler, # type: ignore
152
+ )
148
153
 
149
154
  self.user_middleware: List[Middleware] = (
150
155
  [] if middleware is None else list(middleware)
@@ -291,8 +296,8 @@ class FastAPI(Starlette):
291
296
  deprecated: Optional[bool] = None,
292
297
  methods: Optional[List[str]] = None,
293
298
  operation_id: Optional[str] = None,
294
- response_model_include: Optional[Union[SetIntStr, DictIntStrAny]] = None,
295
- response_model_exclude: Optional[Union[SetIntStr, DictIntStrAny]] = None,
299
+ response_model_include: Optional[IncEx] = None,
300
+ response_model_exclude: Optional[IncEx] = None,
296
301
  response_model_by_alias: bool = True,
297
302
  response_model_exclude_unset: bool = False,
298
303
  response_model_exclude_defaults: bool = False,
@@ -349,8 +354,8 @@ class FastAPI(Starlette):
349
354
  deprecated: Optional[bool] = None,
350
355
  methods: Optional[List[str]] = None,
351
356
  operation_id: Optional[str] = None,
352
- response_model_include: Optional[Union[SetIntStr, DictIntStrAny]] = None,
353
- response_model_exclude: Optional[Union[SetIntStr, DictIntStrAny]] = None,
357
+ response_model_include: Optional[IncEx] = None,
358
+ response_model_exclude: Optional[IncEx] = None,
354
359
  response_model_by_alias: bool = True,
355
360
  response_model_exclude_unset: bool = False,
356
361
  response_model_exclude_defaults: bool = False,
@@ -395,15 +400,34 @@ class FastAPI(Starlette):
395
400
  return decorator
396
401
 
397
402
  def add_api_websocket_route(
398
- self, path: str, endpoint: Callable[..., Any], name: Optional[str] = None
403
+ self,
404
+ path: str,
405
+ endpoint: Callable[..., Any],
406
+ name: Optional[str] = None,
407
+ *,
408
+ dependencies: Optional[Sequence[Depends]] = None,
399
409
  ) -> None:
400
- self.router.add_api_websocket_route(path, endpoint, name=name)
410
+ self.router.add_api_websocket_route(
411
+ path,
412
+ endpoint,
413
+ name=name,
414
+ dependencies=dependencies,
415
+ )
401
416
 
402
417
  def websocket(
403
- self, path: str, name: Optional[str] = None
418
+ self,
419
+ path: str,
420
+ name: Optional[str] = None,
421
+ *,
422
+ dependencies: Optional[Sequence[Depends]] = None,
404
423
  ) -> Callable[[DecoratedCallable], DecoratedCallable]:
405
424
  def decorator(func: DecoratedCallable) -> DecoratedCallable:
406
- self.add_api_websocket_route(path, func, name=name)
425
+ self.add_api_websocket_route(
426
+ path,
427
+ func,
428
+ name=name,
429
+ dependencies=dependencies,
430
+ )
407
431
  return func
408
432
 
409
433
  return decorator
@@ -451,8 +475,8 @@ class FastAPI(Starlette):
451
475
  responses: Optional[Dict[Union[int, str], Dict[str, Any]]] = None,
452
476
  deprecated: Optional[bool] = None,
453
477
  operation_id: Optional[str] = None,
454
- response_model_include: Optional[Union[SetIntStr, DictIntStrAny]] = None,
455
- response_model_exclude: Optional[Union[SetIntStr, DictIntStrAny]] = None,
478
+ response_model_include: Optional[IncEx] = None,
479
+ response_model_exclude: Optional[IncEx] = None,
456
480
  response_model_by_alias: bool = True,
457
481
  response_model_exclude_unset: bool = False,
458
482
  response_model_exclude_defaults: bool = False,
@@ -506,8 +530,8 @@ class FastAPI(Starlette):
506
530
  responses: Optional[Dict[Union[int, str], Dict[str, Any]]] = None,
507
531
  deprecated: Optional[bool] = None,
508
532
  operation_id: Optional[str] = None,
509
- response_model_include: Optional[Union[SetIntStr, DictIntStrAny]] = None,
510
- response_model_exclude: Optional[Union[SetIntStr, DictIntStrAny]] = None,
533
+ response_model_include: Optional[IncEx] = None,
534
+ response_model_exclude: Optional[IncEx] = None,
511
535
  response_model_by_alias: bool = True,
512
536
  response_model_exclude_unset: bool = False,
513
537
  response_model_exclude_defaults: bool = False,
@@ -561,8 +585,8 @@ class FastAPI(Starlette):
561
585
  responses: Optional[Dict[Union[int, str], Dict[str, Any]]] = None,
562
586
  deprecated: Optional[bool] = None,
563
587
  operation_id: Optional[str] = None,
564
- response_model_include: Optional[Union[SetIntStr, DictIntStrAny]] = None,
565
- response_model_exclude: Optional[Union[SetIntStr, DictIntStrAny]] = None,
588
+ response_model_include: Optional[IncEx] = None,
589
+ response_model_exclude: Optional[IncEx] = None,
566
590
  response_model_by_alias: bool = True,
567
591
  response_model_exclude_unset: bool = False,
568
592
  response_model_exclude_defaults: bool = False,
@@ -616,8 +640,8 @@ class FastAPI(Starlette):
616
640
  responses: Optional[Dict[Union[int, str], Dict[str, Any]]] = None,
617
641
  deprecated: Optional[bool] = None,
618
642
  operation_id: Optional[str] = None,
619
- response_model_include: Optional[Union[SetIntStr, DictIntStrAny]] = None,
620
- response_model_exclude: Optional[Union[SetIntStr, DictIntStrAny]] = None,
643
+ response_model_include: Optional[IncEx] = None,
644
+ response_model_exclude: Optional[IncEx] = None,
621
645
  response_model_by_alias: bool = True,
622
646
  response_model_exclude_unset: bool = False,
623
647
  response_model_exclude_defaults: bool = False,
@@ -671,8 +695,8 @@ class FastAPI(Starlette):
671
695
  responses: Optional[Dict[Union[int, str], Dict[str, Any]]] = None,
672
696
  deprecated: Optional[bool] = None,
673
697
  operation_id: Optional[str] = None,
674
- response_model_include: Optional[Union[SetIntStr, DictIntStrAny]] = None,
675
- response_model_exclude: Optional[Union[SetIntStr, DictIntStrAny]] = None,
698
+ response_model_include: Optional[IncEx] = None,
699
+ response_model_exclude: Optional[IncEx] = None,
676
700
  response_model_by_alias: bool = True,
677
701
  response_model_exclude_unset: bool = False,
678
702
  response_model_exclude_defaults: bool = False,
@@ -726,8 +750,8 @@ class FastAPI(Starlette):
726
750
  responses: Optional[Dict[Union[int, str], Dict[str, Any]]] = None,
727
751
  deprecated: Optional[bool] = None,
728
752
  operation_id: Optional[str] = None,
729
- response_model_include: Optional[Union[SetIntStr, DictIntStrAny]] = None,
730
- response_model_exclude: Optional[Union[SetIntStr, DictIntStrAny]] = None,
753
+ response_model_include: Optional[IncEx] = None,
754
+ response_model_exclude: Optional[IncEx] = None,
731
755
  response_model_by_alias: bool = True,
732
756
  response_model_exclude_unset: bool = False,
733
757
  response_model_exclude_defaults: bool = False,
@@ -781,8 +805,8 @@ class FastAPI(Starlette):
781
805
  responses: Optional[Dict[Union[int, str], Dict[str, Any]]] = None,
782
806
  deprecated: Optional[bool] = None,
783
807
  operation_id: Optional[str] = None,
784
- response_model_include: Optional[Union[SetIntStr, DictIntStrAny]] = None,
785
- response_model_exclude: Optional[Union[SetIntStr, DictIntStrAny]] = None,
808
+ response_model_include: Optional[IncEx] = None,
809
+ response_model_exclude: Optional[IncEx] = None,
786
810
  response_model_by_alias: bool = True,
787
811
  response_model_exclude_unset: bool = False,
788
812
  response_model_exclude_defaults: bool = False,
@@ -836,8 +860,8 @@ class FastAPI(Starlette):
836
860
  responses: Optional[Dict[Union[int, str], Dict[str, Any]]] = None,
837
861
  deprecated: Optional[bool] = None,
838
862
  operation_id: Optional[str] = None,
839
- response_model_include: Optional[Union[SetIntStr, DictIntStrAny]] = None,
840
- response_model_exclude: Optional[Union[SetIntStr, DictIntStrAny]] = None,
863
+ response_model_include: Optional[IncEx] = None,
864
+ response_model_exclude: Optional[IncEx] = None,
841
865
  response_model_by_alias: bool = True,
842
866
  response_model_exclude_unset: bool = False,
843
867
  response_model_exclude_defaults: bool = False,
fastapi/datastructures.py CHANGED
@@ -1,5 +1,12 @@
1
- from typing import Any, Callable, Dict, Iterable, Type, TypeVar
2
-
1
+ from typing import Any, Callable, Dict, Iterable, Type, TypeVar, cast
2
+
3
+ from fastapi._compat import (
4
+ PYDANTIC_V2,
5
+ CoreSchema,
6
+ GetJsonSchemaHandler,
7
+ JsonSchemaValue,
8
+ general_plain_validator_function,
9
+ )
3
10
  from starlette.datastructures import URL as URL # noqa: F401
4
11
  from starlette.datastructures import Address as Address # noqa: F401
5
12
  from starlette.datastructures import FormData as FormData # noqa: F401
@@ -21,8 +28,28 @@ class UploadFile(StarletteUploadFile):
21
28
  return v
22
29
 
23
30
  @classmethod
24
- def __modify_schema__(cls, field_schema: Dict[str, Any]) -> None:
25
- field_schema.update({"type": "string", "format": "binary"})
31
+ def _validate(cls, __input_value: Any, _: Any) -> "UploadFile":
32
+ if not isinstance(__input_value, StarletteUploadFile):
33
+ raise ValueError(f"Expected UploadFile, received: {type(__input_value)}")
34
+ return cast(UploadFile, __input_value)
35
+
36
+ if not PYDANTIC_V2:
37
+
38
+ @classmethod
39
+ def __modify_schema__(cls, field_schema: Dict[str, Any]) -> None:
40
+ field_schema.update({"type": "string", "format": "binary"})
41
+
42
+ @classmethod
43
+ def __get_pydantic_json_schema__(
44
+ cls, core_schema: CoreSchema, handler: GetJsonSchemaHandler
45
+ ) -> JsonSchemaValue:
46
+ return {"type": "string", "format": "binary"}
47
+
48
+ @classmethod
49
+ def __get_pydantic_core_schema__(
50
+ cls, source: Type[Any], handler: Callable[[Any], CoreSchema]
51
+ ) -> CoreSchema:
52
+ return general_plain_validator_function(cls._validate)
26
53
 
27
54
 
28
55
  class DefaultPlaceholder:
@@ -1,7 +1,7 @@
1
1
  from typing import Any, Callable, List, Optional, Sequence
2
2
 
3
+ from fastapi._compat import ModelField
3
4
  from fastapi.security.base import SecurityBase
4
- from pydantic.fields import ModelField
5
5
 
6
6
 
7
7
  class SecurityRequirement:
@@ -1,7 +1,6 @@
1
- import dataclasses
2
1
  import inspect
3
2
  from contextlib import contextmanager
4
- from copy import copy, deepcopy
3
+ from copy import deepcopy
5
4
  from typing import (
6
5
  Any,
7
6
  Callable,
@@ -20,6 +19,31 @@ from typing import (
20
19
 
21
20
  import anyio
22
21
  from fastapi import params
22
+ from fastapi._compat import (
23
+ PYDANTIC_V2,
24
+ ErrorWrapper,
25
+ ModelField,
26
+ Required,
27
+ Undefined,
28
+ _regenerate_error_with_loc,
29
+ copy_field_info,
30
+ create_body_model,
31
+ evaluate_forwardref,
32
+ field_annotation_is_scalar,
33
+ get_annotation_from_field_info,
34
+ get_missing_field_error,
35
+ is_bytes_field,
36
+ is_bytes_sequence_field,
37
+ is_scalar_field,
38
+ is_scalar_sequence_field,
39
+ is_sequence_field,
40
+ is_uploadfile_or_nonable_uploadfile_annotation,
41
+ is_uploadfile_sequence_annotation,
42
+ lenient_issubclass,
43
+ sequence_types,
44
+ serialize_sequence_value,
45
+ value_is_sequence,
46
+ )
23
47
  from fastapi.concurrency import (
24
48
  AsyncExitStack,
25
49
  asynccontextmanager,
@@ -31,50 +55,14 @@ from fastapi.security.base import SecurityBase
31
55
  from fastapi.security.oauth2 import OAuth2, SecurityScopes
32
56
  from fastapi.security.open_id_connect_url import OpenIdConnect
33
57
  from fastapi.utils import create_response_field, get_path_param_names
34
- from pydantic import BaseModel, create_model
35
- from pydantic.error_wrappers import ErrorWrapper
36
- from pydantic.errors import MissingError
37
- from pydantic.fields import (
38
- SHAPE_FROZENSET,
39
- SHAPE_LIST,
40
- SHAPE_SEQUENCE,
41
- SHAPE_SET,
42
- SHAPE_SINGLETON,
43
- SHAPE_TUPLE,
44
- SHAPE_TUPLE_ELLIPSIS,
45
- FieldInfo,
46
- ModelField,
47
- Required,
48
- Undefined,
49
- )
50
- from pydantic.schema import get_annotation_from_field_info
51
- from pydantic.typing import evaluate_forwardref, get_args, get_origin
52
- from pydantic.utils import lenient_issubclass
58
+ from pydantic.fields import FieldInfo
53
59
  from starlette.background import BackgroundTasks
54
60
  from starlette.concurrency import run_in_threadpool
55
61
  from starlette.datastructures import FormData, Headers, QueryParams, UploadFile
56
62
  from starlette.requests import HTTPConnection, Request
57
63
  from starlette.responses import Response
58
64
  from starlette.websockets import WebSocket
59
- from typing_extensions import Annotated
60
-
61
- sequence_shapes = {
62
- SHAPE_LIST,
63
- SHAPE_SET,
64
- SHAPE_FROZENSET,
65
- SHAPE_TUPLE,
66
- SHAPE_SEQUENCE,
67
- SHAPE_TUPLE_ELLIPSIS,
68
- }
69
- sequence_types = (list, set, tuple)
70
- sequence_shape_to_type = {
71
- SHAPE_LIST: list,
72
- SHAPE_SET: set,
73
- SHAPE_TUPLE: tuple,
74
- SHAPE_SEQUENCE: list,
75
- SHAPE_TUPLE_ELLIPSIS: list,
76
- }
77
-
65
+ from typing_extensions import Annotated, get_args, get_origin
78
66
 
79
67
  multipart_not_installed_error = (
80
68
  'Form data requires "python-multipart" to be installed. \n'
@@ -216,36 +204,6 @@ def get_flat_params(dependant: Dependant) -> List[ModelField]:
216
204
  )
217
205
 
218
206
 
219
- def is_scalar_field(field: ModelField) -> bool:
220
- field_info = field.field_info
221
- if not (
222
- field.shape == SHAPE_SINGLETON
223
- and not lenient_issubclass(field.type_, BaseModel)
224
- and not lenient_issubclass(field.type_, sequence_types + (dict,))
225
- and not dataclasses.is_dataclass(field.type_)
226
- and not isinstance(field_info, params.Body)
227
- ):
228
- return False
229
- if field.sub_fields:
230
- if not all(is_scalar_field(f) for f in field.sub_fields):
231
- return False
232
- return True
233
-
234
-
235
- def is_scalar_sequence_field(field: ModelField) -> bool:
236
- if (field.shape in sequence_shapes) and not lenient_issubclass(
237
- field.type_, BaseModel
238
- ):
239
- if field.sub_fields is not None:
240
- for sub_field in field.sub_fields:
241
- if not is_scalar_field(sub_field):
242
- return False
243
- return True
244
- if lenient_issubclass(field.type_, sequence_types):
245
- return True
246
- return False
247
-
248
-
249
207
  def get_typed_signature(call: Callable[..., Any]) -> inspect.Signature:
250
208
  signature = inspect.signature(call)
251
209
  globalns = getattr(call, "__globals__", {})
@@ -364,12 +322,11 @@ def analyze_param(
364
322
  is_path_param: bool,
365
323
  ) -> Tuple[Any, Optional[params.Depends], Optional[ModelField]]:
366
324
  field_info = None
367
- used_default_field_info = False
368
325
  depends = None
369
326
  type_annotation: Any = Any
370
327
  if (
371
328
  annotation is not inspect.Signature.empty
372
- and get_origin(annotation) is Annotated # type: ignore[comparison-overlap]
329
+ and get_origin(annotation) is Annotated
373
330
  ):
374
331
  annotated_args = get_args(annotation)
375
332
  type_annotation = annotated_args[0]
@@ -384,7 +341,9 @@ def analyze_param(
384
341
  fastapi_annotation = next(iter(fastapi_annotations), None)
385
342
  if isinstance(fastapi_annotation, FieldInfo):
386
343
  # Copy `field_info` because we mutate `field_info.default` below.
387
- field_info = copy(fastapi_annotation)
344
+ field_info = copy_field_info(
345
+ field_info=fastapi_annotation, annotation=annotation
346
+ )
388
347
  assert field_info.default is Undefined or field_info.default is Required, (
389
348
  f"`{field_info.__class__.__name__}` default value cannot be set in"
390
349
  f" `Annotated` for {param_name!r}. Set the default value with `=` instead."
@@ -415,6 +374,8 @@ def analyze_param(
415
374
  f" together for {param_name!r}"
416
375
  )
417
376
  field_info = value
377
+ if PYDANTIC_V2:
378
+ field_info.annotation = type_annotation
418
379
 
419
380
  if depends is not None and depends.dependency is None:
420
381
  depends.dependency = type_annotation
@@ -433,10 +394,15 @@ def analyze_param(
433
394
  # We might check here that `default_value is Required`, but the fact is that the same
434
395
  # parameter might sometimes be a path parameter and sometimes not. See
435
396
  # `tests/test_infer_param_optionality.py` for an example.
436
- field_info = params.Path()
397
+ field_info = params.Path(annotation=type_annotation)
398
+ elif is_uploadfile_or_nonable_uploadfile_annotation(
399
+ type_annotation
400
+ ) or is_uploadfile_sequence_annotation(type_annotation):
401
+ field_info = params.File(annotation=type_annotation, default=default_value)
402
+ elif not field_annotation_is_scalar(annotation=type_annotation):
403
+ field_info = params.Body(annotation=type_annotation, default=default_value)
437
404
  else:
438
- field_info = params.Query(default=default_value)
439
- used_default_field_info = True
405
+ field_info = params.Query(annotation=type_annotation, default=default_value)
440
406
 
441
407
  field = None
442
408
  if field_info is not None:
@@ -450,8 +416,8 @@ def analyze_param(
450
416
  and getattr(field_info, "in_", None) is None
451
417
  ):
452
418
  field_info.in_ = params.ParamTypes.query
453
- annotation = get_annotation_from_field_info(
454
- annotation if annotation is not inspect.Signature.empty else Any,
419
+ use_annotation = get_annotation_from_field_info(
420
+ type_annotation,
455
421
  field_info,
456
422
  param_name,
457
423
  )
@@ -459,19 +425,15 @@ def analyze_param(
459
425
  alias = param_name.replace("_", "-")
460
426
  else:
461
427
  alias = field_info.alias or param_name
428
+ field_info.alias = alias
462
429
  field = create_response_field(
463
430
  name=param_name,
464
- type_=annotation,
431
+ type_=use_annotation,
465
432
  default=field_info.default,
466
433
  alias=alias,
467
434
  required=field_info.default in (Required, Undefined),
468
435
  field_info=field_info,
469
436
  )
470
- if used_default_field_info:
471
- if lenient_issubclass(field.type_, UploadFile):
472
- field.field_info = params.File(field_info.default)
473
- elif not is_scalar_field(field=field):
474
- field.field_info = params.Body(field_info.default)
475
437
 
476
438
  return type_annotation, depends, field
477
439
 
@@ -554,13 +516,13 @@ async def solve_dependencies(
554
516
  dependency_cache: Optional[Dict[Tuple[Callable[..., Any], Tuple[str]], Any]] = None,
555
517
  ) -> Tuple[
556
518
  Dict[str, Any],
557
- List[ErrorWrapper],
519
+ List[Any],
558
520
  Optional[BackgroundTasks],
559
521
  Response,
560
522
  Dict[Tuple[Callable[..., Any], Tuple[str]], Any],
561
523
  ]:
562
524
  values: Dict[str, Any] = {}
563
- errors: List[ErrorWrapper] = []
525
+ errors: List[Any] = []
564
526
  if response is None:
565
527
  response = Response()
566
528
  del response.headers["content-length"]
@@ -674,7 +636,7 @@ async def solve_dependencies(
674
636
  def request_params_to_args(
675
637
  required_params: Sequence[ModelField],
676
638
  received_params: Union[Mapping[str, Any], QueryParams, Headers],
677
- ) -> Tuple[Dict[str, Any], List[ErrorWrapper]]:
639
+ ) -> Tuple[Dict[str, Any], List[Any]]:
678
640
  values = {}
679
641
  errors = []
680
642
  for field in required_params:
@@ -688,23 +650,19 @@ def request_params_to_args(
688
650
  assert isinstance(
689
651
  field_info, params.Param
690
652
  ), "Params must be subclasses of Param"
653
+ loc = (field_info.in_.value, field.alias)
691
654
  if value is None:
692
655
  if field.required:
693
- errors.append(
694
- ErrorWrapper(
695
- MissingError(), loc=(field_info.in_.value, field.alias)
696
- )
697
- )
656
+ errors.append(get_missing_field_error(loc=loc))
698
657
  else:
699
658
  values[field.name] = deepcopy(field.default)
700
659
  continue
701
- v_, errors_ = field.validate(
702
- value, values, loc=(field_info.in_.value, field.alias)
703
- )
660
+ v_, errors_ = field.validate(value, values, loc=loc)
704
661
  if isinstance(errors_, ErrorWrapper):
705
662
  errors.append(errors_)
706
663
  elif isinstance(errors_, list):
707
- errors.extend(errors_)
664
+ new_errors = _regenerate_error_with_loc(errors=errors_, loc_prefix=())
665
+ errors.extend(new_errors)
708
666
  else:
709
667
  values[field.name] = v_
710
668
  return values, errors
@@ -713,9 +671,9 @@ def request_params_to_args(
713
671
  async def request_body_to_args(
714
672
  required_params: List[ModelField],
715
673
  received_body: Optional[Union[Dict[str, Any], FormData]],
716
- ) -> Tuple[Dict[str, Any], List[ErrorWrapper]]:
674
+ ) -> Tuple[Dict[str, Any], List[Dict[str, Any]]]:
717
675
  values = {}
718
- errors = []
676
+ errors: List[Dict[str, Any]] = []
719
677
  if required_params:
720
678
  field = required_params[0]
721
679
  field_info = field.field_info
@@ -733,9 +691,7 @@ async def request_body_to_args(
733
691
 
734
692
  value: Optional[Any] = None
735
693
  if received_body is not None:
736
- if (
737
- field.shape in sequence_shapes or field.type_ in sequence_types
738
- ) and isinstance(received_body, FormData):
694
+ if (is_sequence_field(field)) and isinstance(received_body, FormData):
739
695
  value = received_body.getlist(field.alias)
740
696
  else:
741
697
  try:
@@ -748,7 +704,7 @@ async def request_body_to_args(
748
704
  or (isinstance(field_info, params.Form) and value == "")
749
705
  or (
750
706
  isinstance(field_info, params.Form)
751
- and field.shape in sequence_shapes
707
+ and is_sequence_field(field)
752
708
  and len(value) == 0
753
709
  )
754
710
  ):
@@ -759,16 +715,17 @@ async def request_body_to_args(
759
715
  continue
760
716
  if (
761
717
  isinstance(field_info, params.File)
762
- and lenient_issubclass(field.type_, bytes)
718
+ and is_bytes_field(field)
763
719
  and isinstance(value, UploadFile)
764
720
  ):
765
721
  value = await value.read()
766
722
  elif (
767
- field.shape in sequence_shapes
723
+ is_bytes_sequence_field(field)
768
724
  and isinstance(field_info, params.File)
769
- and lenient_issubclass(field.type_, bytes)
770
- and isinstance(value, sequence_types)
725
+ and value_is_sequence(value)
771
726
  ):
727
+ # For types
728
+ assert isinstance(value, sequence_types) # type: ignore[arg-type]
772
729
  results: List[Union[bytes, str]] = []
773
730
 
774
731
  async def process_fn(
@@ -780,24 +737,19 @@ async def request_body_to_args(
780
737
  async with anyio.create_task_group() as tg:
781
738
  for sub_value in value:
782
739
  tg.start_soon(process_fn, sub_value.read)
783
- value = sequence_shape_to_type[field.shape](results)
740
+ value = serialize_sequence_value(field=field, value=results)
784
741
 
785
742
  v_, errors_ = field.validate(value, values, loc=loc)
786
743
 
787
- if isinstance(errors_, ErrorWrapper):
788
- errors.append(errors_)
789
- elif isinstance(errors_, list):
744
+ if isinstance(errors_, list):
790
745
  errors.extend(errors_)
746
+ elif errors_:
747
+ errors.append(errors_)
791
748
  else:
792
749
  values[field.name] = v_
793
750
  return values, errors
794
751
 
795
752
 
796
- def get_missing_field_error(loc: Tuple[str, ...]) -> ErrorWrapper:
797
- missing_field_error = ErrorWrapper(MissingError(), loc=loc)
798
- return missing_field_error
799
-
800
-
801
753
  def get_body_field(*, dependant: Dependant, name: str) -> Optional[ModelField]:
802
754
  flat_dependant = get_flat_dependant(dependant)
803
755
  if not flat_dependant.body_params:
@@ -815,12 +767,16 @@ def get_body_field(*, dependant: Dependant, name: str) -> Optional[ModelField]:
815
767
  for param in flat_dependant.body_params:
816
768
  setattr(param.field_info, "embed", True) # noqa: B010
817
769
  model_name = "Body_" + name
818
- BodyModel: Type[BaseModel] = create_model(model_name)
819
- for f in flat_dependant.body_params:
820
- BodyModel.__fields__[f.name] = f
770
+ BodyModel = create_body_model(
771
+ fields=flat_dependant.body_params, model_name=model_name
772
+ )
821
773
  required = any(True for f in flat_dependant.body_params if f.required)
822
-
823
- BodyFieldInfo_kwargs: Dict[str, Any] = {"default": None}
774
+ BodyFieldInfo_kwargs: Dict[str, Any] = {
775
+ "annotation": BodyModel,
776
+ "alias": "body",
777
+ }
778
+ if not required:
779
+ BodyFieldInfo_kwargs["default"] = None
824
780
  if any(isinstance(f.field_info, params.File) for f in flat_dependant.body_params):
825
781
  BodyFieldInfo: Type[params.Body] = params.File
826
782
  elif any(isinstance(f.field_info, params.Form) for f in flat_dependant.body_params):