fastapi 0.118.2__py3-none-any.whl → 0.119.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of fastapi might be problematic. Click here for more details.

fastapi/_compat.py DELETED
@@ -1,680 +0,0 @@
1
- import warnings
2
- from collections import deque
3
- from copy import copy
4
- from dataclasses import dataclass, is_dataclass
5
- from enum import Enum
6
- from functools import lru_cache
7
- from typing import (
8
- Any,
9
- Callable,
10
- Deque,
11
- Dict,
12
- FrozenSet,
13
- List,
14
- Mapping,
15
- Sequence,
16
- Set,
17
- Tuple,
18
- Type,
19
- Union,
20
- cast,
21
- )
22
-
23
- from fastapi.exceptions import RequestErrorModel
24
- from fastapi.types import IncEx, ModelNameMap, UnionType
25
- from pydantic import BaseModel, create_model
26
- from pydantic.version import VERSION as PYDANTIC_VERSION
27
- from starlette.datastructures import UploadFile
28
- from typing_extensions import Annotated, Literal, get_args, get_origin
29
-
30
- PYDANTIC_VERSION_MINOR_TUPLE = tuple(int(x) for x in PYDANTIC_VERSION.split(".")[:2])
31
- PYDANTIC_V2 = PYDANTIC_VERSION_MINOR_TUPLE[0] == 2
32
-
33
-
34
- sequence_annotation_to_type = {
35
- Sequence: list,
36
- List: list,
37
- list: list,
38
- Tuple: tuple,
39
- tuple: tuple,
40
- Set: set,
41
- set: set,
42
- FrozenSet: frozenset,
43
- frozenset: frozenset,
44
- Deque: deque,
45
- deque: deque,
46
- }
47
-
48
- sequence_types = tuple(sequence_annotation_to_type.keys())
49
-
50
- Url: Type[Any]
51
-
52
- if PYDANTIC_V2:
53
- from pydantic import PydanticSchemaGenerationError as PydanticSchemaGenerationError
54
- from pydantic import TypeAdapter
55
- from pydantic import ValidationError as ValidationError
56
- from pydantic._internal._schema_generation_shared import ( # type: ignore[attr-defined]
57
- GetJsonSchemaHandler as GetJsonSchemaHandler,
58
- )
59
- from pydantic._internal._typing_extra import eval_type_lenient
60
- from pydantic._internal._utils import lenient_issubclass as lenient_issubclass
61
- from pydantic.fields import FieldInfo
62
- from pydantic.json_schema import GenerateJsonSchema as GenerateJsonSchema
63
- from pydantic.json_schema import JsonSchemaValue as JsonSchemaValue
64
- from pydantic_core import CoreSchema as CoreSchema
65
- from pydantic_core import PydanticUndefined, PydanticUndefinedType
66
- from pydantic_core import Url as Url
67
-
68
- try:
69
- from pydantic_core.core_schema import (
70
- with_info_plain_validator_function as with_info_plain_validator_function,
71
- )
72
- except ImportError: # pragma: no cover
73
- from pydantic_core.core_schema import (
74
- general_plain_validator_function as with_info_plain_validator_function, # noqa: F401
75
- )
76
-
77
- RequiredParam = PydanticUndefined
78
- Undefined = PydanticUndefined
79
- UndefinedType = PydanticUndefinedType
80
- evaluate_forwardref = eval_type_lenient
81
- Validator = Any
82
-
83
- class BaseConfig:
84
- pass
85
-
86
- class ErrorWrapper(Exception):
87
- pass
88
-
89
- @dataclass
90
- class ModelField:
91
- field_info: FieldInfo
92
- name: str
93
- mode: Literal["validation", "serialization"] = "validation"
94
-
95
- @property
96
- def alias(self) -> str:
97
- a = self.field_info.alias
98
- return a if a is not None else self.name
99
-
100
- @property
101
- def required(self) -> bool:
102
- return self.field_info.is_required()
103
-
104
- @property
105
- def default(self) -> Any:
106
- return self.get_default()
107
-
108
- @property
109
- def type_(self) -> Any:
110
- return self.field_info.annotation
111
-
112
- def __post_init__(self) -> None:
113
- with warnings.catch_warnings():
114
- # Pydantic >= 2.12.0 warns about field specific metadata that is unused
115
- # (e.g. `TypeAdapter(Annotated[int, Field(alias='b')])`). In some cases, we
116
- # end up building the type adapter from a model field annotation so we
117
- # need to ignore the warning:
118
- if PYDANTIC_VERSION_MINOR_TUPLE >= (2, 12):
119
- from pydantic.warnings import UnsupportedFieldAttributeWarning
120
-
121
- warnings.simplefilter(
122
- "ignore", category=UnsupportedFieldAttributeWarning
123
- )
124
- self._type_adapter: TypeAdapter[Any] = TypeAdapter(
125
- Annotated[self.field_info.annotation, self.field_info]
126
- )
127
-
128
- def get_default(self) -> Any:
129
- if self.field_info.is_required():
130
- return Undefined
131
- return self.field_info.get_default(call_default_factory=True)
132
-
133
- def validate(
134
- self,
135
- value: Any,
136
- values: Dict[str, Any] = {}, # noqa: B006
137
- *,
138
- loc: Tuple[Union[int, str], ...] = (),
139
- ) -> Tuple[Any, Union[List[Dict[str, Any]], None]]:
140
- try:
141
- return (
142
- self._type_adapter.validate_python(value, from_attributes=True),
143
- None,
144
- )
145
- except ValidationError as exc:
146
- return None, _regenerate_error_with_loc(
147
- errors=exc.errors(include_url=False), loc_prefix=loc
148
- )
149
-
150
- def serialize(
151
- self,
152
- value: Any,
153
- *,
154
- mode: Literal["json", "python"] = "json",
155
- include: Union[IncEx, None] = None,
156
- exclude: Union[IncEx, None] = None,
157
- by_alias: bool = True,
158
- exclude_unset: bool = False,
159
- exclude_defaults: bool = False,
160
- exclude_none: bool = False,
161
- ) -> Any:
162
- # What calls this code passes a value that already called
163
- # self._type_adapter.validate_python(value)
164
- return self._type_adapter.dump_python(
165
- value,
166
- mode=mode,
167
- include=include,
168
- exclude=exclude,
169
- by_alias=by_alias,
170
- exclude_unset=exclude_unset,
171
- exclude_defaults=exclude_defaults,
172
- exclude_none=exclude_none,
173
- )
174
-
175
- def __hash__(self) -> int:
176
- # Each ModelField is unique for our purposes, to allow making a dict from
177
- # ModelField to its JSON Schema.
178
- return id(self)
179
-
180
- def get_annotation_from_field_info(
181
- annotation: Any, field_info: FieldInfo, field_name: str
182
- ) -> Any:
183
- return annotation
184
-
185
- def _normalize_errors(errors: Sequence[Any]) -> List[Dict[str, Any]]:
186
- return errors # type: ignore[return-value]
187
-
188
- def _model_rebuild(model: Type[BaseModel]) -> None:
189
- model.model_rebuild()
190
-
191
- def _model_dump(
192
- model: BaseModel, mode: Literal["json", "python"] = "json", **kwargs: Any
193
- ) -> Any:
194
- return model.model_dump(mode=mode, **kwargs)
195
-
196
- def _get_model_config(model: BaseModel) -> Any:
197
- return model.model_config
198
-
199
- def get_schema_from_model_field(
200
- *,
201
- field: ModelField,
202
- schema_generator: GenerateJsonSchema,
203
- model_name_map: ModelNameMap,
204
- field_mapping: Dict[
205
- Tuple[ModelField, Literal["validation", "serialization"]], JsonSchemaValue
206
- ],
207
- separate_input_output_schemas: bool = True,
208
- ) -> Dict[str, Any]:
209
- override_mode: Union[Literal["validation"], None] = (
210
- None if separate_input_output_schemas else "validation"
211
- )
212
- # This expects that GenerateJsonSchema was already used to generate the definitions
213
- json_schema = field_mapping[(field, override_mode or field.mode)]
214
- if "$ref" not in json_schema:
215
- # TODO remove when deprecating Pydantic v1
216
- # Ref: https://github.com/pydantic/pydantic/blob/d61792cc42c80b13b23e3ffa74bc37ec7c77f7d1/pydantic/schema.py#L207
217
- json_schema["title"] = (
218
- field.field_info.title or field.alias.title().replace("_", " ")
219
- )
220
- return json_schema
221
-
222
- def get_compat_model_name_map(fields: List[ModelField]) -> ModelNameMap:
223
- return {}
224
-
225
- def get_definitions(
226
- *,
227
- fields: List[ModelField],
228
- schema_generator: GenerateJsonSchema,
229
- model_name_map: ModelNameMap,
230
- separate_input_output_schemas: bool = True,
231
- ) -> Tuple[
232
- Dict[
233
- Tuple[ModelField, Literal["validation", "serialization"]], JsonSchemaValue
234
- ],
235
- Dict[str, Dict[str, Any]],
236
- ]:
237
- override_mode: Union[Literal["validation"], None] = (
238
- None if separate_input_output_schemas else "validation"
239
- )
240
- inputs = [
241
- (field, override_mode or field.mode, field._type_adapter.core_schema)
242
- for field in fields
243
- ]
244
- field_mapping, definitions = schema_generator.generate_definitions(
245
- inputs=inputs
246
- )
247
- for item_def in cast(Dict[str, Dict[str, Any]], definitions).values():
248
- if "description" in item_def:
249
- item_description = cast(str, item_def["description"]).split("\f")[0]
250
- item_def["description"] = item_description
251
- return field_mapping, definitions # type: ignore[return-value]
252
-
253
- def is_scalar_field(field: ModelField) -> bool:
254
- from fastapi import params
255
-
256
- return field_annotation_is_scalar(
257
- field.field_info.annotation
258
- ) and not isinstance(field.field_info, params.Body)
259
-
260
- def is_sequence_field(field: ModelField) -> bool:
261
- return field_annotation_is_sequence(field.field_info.annotation)
262
-
263
- def is_scalar_sequence_field(field: ModelField) -> bool:
264
- return field_annotation_is_scalar_sequence(field.field_info.annotation)
265
-
266
- def is_bytes_field(field: ModelField) -> bool:
267
- return is_bytes_or_nonable_bytes_annotation(field.type_)
268
-
269
- def is_bytes_sequence_field(field: ModelField) -> bool:
270
- return is_bytes_sequence_annotation(field.type_)
271
-
272
- def copy_field_info(*, field_info: FieldInfo, annotation: Any) -> FieldInfo:
273
- cls = type(field_info)
274
- merged_field_info = cls.from_annotation(annotation)
275
- new_field_info = copy(field_info)
276
- new_field_info.metadata = merged_field_info.metadata
277
- new_field_info.annotation = merged_field_info.annotation
278
- return new_field_info
279
-
280
- def serialize_sequence_value(*, field: ModelField, value: Any) -> Sequence[Any]:
281
- origin_type = (
282
- get_origin(field.field_info.annotation) or field.field_info.annotation
283
- )
284
- assert issubclass(origin_type, sequence_types) # type: ignore[arg-type]
285
- return sequence_annotation_to_type[origin_type](value) # type: ignore[no-any-return]
286
-
287
- def get_missing_field_error(loc: Tuple[str, ...]) -> Dict[str, Any]:
288
- error = ValidationError.from_exception_data(
289
- "Field required", [{"type": "missing", "loc": loc, "input": {}}]
290
- ).errors(include_url=False)[0]
291
- error["input"] = None
292
- return error # type: ignore[return-value]
293
-
294
- def create_body_model(
295
- *, fields: Sequence[ModelField], model_name: str
296
- ) -> Type[BaseModel]:
297
- field_params = {f.name: (f.field_info.annotation, f.field_info) for f in fields}
298
- BodyModel: Type[BaseModel] = create_model(model_name, **field_params) # type: ignore[call-overload]
299
- return BodyModel
300
-
301
- def get_model_fields(model: Type[BaseModel]) -> List[ModelField]:
302
- return [
303
- ModelField(field_info=field_info, name=name)
304
- for name, field_info in model.model_fields.items()
305
- ]
306
-
307
- else:
308
- from fastapi.openapi.constants import REF_PREFIX as REF_PREFIX
309
- from pydantic import AnyUrl as Url # noqa: F401
310
- from pydantic import ( # type: ignore[assignment]
311
- BaseConfig as BaseConfig, # noqa: F401
312
- )
313
- from pydantic import ValidationError as ValidationError # noqa: F401
314
- from pydantic.class_validators import ( # type: ignore[no-redef]
315
- Validator as Validator, # noqa: F401
316
- )
317
- from pydantic.error_wrappers import ( # type: ignore[no-redef]
318
- ErrorWrapper as ErrorWrapper, # noqa: F401
319
- )
320
- from pydantic.errors import MissingError
321
- from pydantic.fields import ( # type: ignore[attr-defined]
322
- SHAPE_FROZENSET,
323
- SHAPE_LIST,
324
- SHAPE_SEQUENCE,
325
- SHAPE_SET,
326
- SHAPE_SINGLETON,
327
- SHAPE_TUPLE,
328
- SHAPE_TUPLE_ELLIPSIS,
329
- )
330
- from pydantic.fields import FieldInfo as FieldInfo
331
- from pydantic.fields import ( # type: ignore[no-redef,attr-defined]
332
- ModelField as ModelField, # noqa: F401
333
- )
334
-
335
- # Keeping old "Required" functionality from Pydantic V1, without
336
- # shadowing typing.Required.
337
- RequiredParam: Any = Ellipsis # type: ignore[no-redef]
338
- from pydantic.fields import ( # type: ignore[no-redef,attr-defined]
339
- Undefined as Undefined,
340
- )
341
- from pydantic.fields import ( # type: ignore[no-redef, attr-defined]
342
- UndefinedType as UndefinedType, # noqa: F401
343
- )
344
- from pydantic.schema import (
345
- field_schema,
346
- get_flat_models_from_fields,
347
- get_model_name_map,
348
- model_process_schema,
349
- )
350
- from pydantic.schema import ( # type: ignore[no-redef] # noqa: F401
351
- get_annotation_from_field_info as get_annotation_from_field_info,
352
- )
353
- from pydantic.typing import ( # type: ignore[no-redef]
354
- evaluate_forwardref as evaluate_forwardref, # noqa: F401
355
- )
356
- from pydantic.utils import ( # type: ignore[no-redef]
357
- lenient_issubclass as lenient_issubclass, # noqa: F401
358
- )
359
-
360
- GetJsonSchemaHandler = Any # type: ignore[assignment,misc]
361
- JsonSchemaValue = Dict[str, Any] # type: ignore[misc]
362
- CoreSchema = Any # type: ignore[assignment,misc]
363
-
364
- sequence_shapes = {
365
- SHAPE_LIST,
366
- SHAPE_SET,
367
- SHAPE_FROZENSET,
368
- SHAPE_TUPLE,
369
- SHAPE_SEQUENCE,
370
- SHAPE_TUPLE_ELLIPSIS,
371
- }
372
- sequence_shape_to_type = {
373
- SHAPE_LIST: list,
374
- SHAPE_SET: set,
375
- SHAPE_TUPLE: tuple,
376
- SHAPE_SEQUENCE: list,
377
- SHAPE_TUPLE_ELLIPSIS: list,
378
- }
379
-
380
- @dataclass
381
- class GenerateJsonSchema: # type: ignore[no-redef]
382
- ref_template: str
383
-
384
- class PydanticSchemaGenerationError(Exception): # type: ignore[no-redef]
385
- pass
386
-
387
- def with_info_plain_validator_function( # type: ignore[misc]
388
- function: Callable[..., Any],
389
- *,
390
- ref: Union[str, None] = None,
391
- metadata: Any = None,
392
- serialization: Any = None,
393
- ) -> Any:
394
- return {}
395
-
396
- def get_model_definitions(
397
- *,
398
- flat_models: Set[Union[Type[BaseModel], Type[Enum]]],
399
- model_name_map: Dict[Union[Type[BaseModel], Type[Enum]], str],
400
- ) -> Dict[str, Any]:
401
- definitions: Dict[str, Dict[str, Any]] = {}
402
- for model in flat_models:
403
- m_schema, m_definitions, m_nested_models = model_process_schema(
404
- model, model_name_map=model_name_map, ref_prefix=REF_PREFIX
405
- )
406
- definitions.update(m_definitions)
407
- model_name = model_name_map[model]
408
- definitions[model_name] = m_schema
409
- for m_schema in definitions.values():
410
- if "description" in m_schema:
411
- m_schema["description"] = m_schema["description"].split("\f")[0]
412
- return definitions
413
-
414
- def is_pv1_scalar_field(field: ModelField) -> bool:
415
- from fastapi import params
416
-
417
- field_info = field.field_info
418
- if not (
419
- field.shape == SHAPE_SINGLETON # type: ignore[attr-defined]
420
- and not lenient_issubclass(field.type_, BaseModel)
421
- and not lenient_issubclass(field.type_, dict)
422
- and not field_annotation_is_sequence(field.type_)
423
- and not is_dataclass(field.type_)
424
- and not isinstance(field_info, params.Body)
425
- ):
426
- return False
427
- if field.sub_fields: # type: ignore[attr-defined]
428
- if not all(
429
- is_pv1_scalar_field(f)
430
- for f in field.sub_fields # type: ignore[attr-defined]
431
- ):
432
- return False
433
- return True
434
-
435
- def is_pv1_scalar_sequence_field(field: ModelField) -> bool:
436
- if (field.shape in sequence_shapes) and not lenient_issubclass( # type: ignore[attr-defined]
437
- field.type_, BaseModel
438
- ):
439
- if field.sub_fields is not None: # type: ignore[attr-defined]
440
- for sub_field in field.sub_fields: # type: ignore[attr-defined]
441
- if not is_pv1_scalar_field(sub_field):
442
- return False
443
- return True
444
- if _annotation_is_sequence(field.type_):
445
- return True
446
- return False
447
-
448
- def _normalize_errors(errors: Sequence[Any]) -> List[Dict[str, Any]]:
449
- use_errors: List[Any] = []
450
- for error in errors:
451
- if isinstance(error, ErrorWrapper):
452
- new_errors = ValidationError( # type: ignore[call-arg]
453
- errors=[error], model=RequestErrorModel
454
- ).errors()
455
- use_errors.extend(new_errors)
456
- elif isinstance(error, list):
457
- use_errors.extend(_normalize_errors(error))
458
- else:
459
- use_errors.append(error)
460
- return use_errors
461
-
462
- def _model_rebuild(model: Type[BaseModel]) -> None:
463
- model.update_forward_refs()
464
-
465
- def _model_dump(
466
- model: BaseModel, mode: Literal["json", "python"] = "json", **kwargs: Any
467
- ) -> Any:
468
- return model.dict(**kwargs)
469
-
470
- def _get_model_config(model: BaseModel) -> Any:
471
- return model.__config__ # type: ignore[attr-defined]
472
-
473
- def get_schema_from_model_field(
474
- *,
475
- field: ModelField,
476
- schema_generator: GenerateJsonSchema,
477
- model_name_map: ModelNameMap,
478
- field_mapping: Dict[
479
- Tuple[ModelField, Literal["validation", "serialization"]], JsonSchemaValue
480
- ],
481
- separate_input_output_schemas: bool = True,
482
- ) -> Dict[str, Any]:
483
- # This expects that GenerateJsonSchema was already used to generate the definitions
484
- return field_schema( # type: ignore[no-any-return]
485
- field, model_name_map=model_name_map, ref_prefix=REF_PREFIX
486
- )[0]
487
-
488
- def get_compat_model_name_map(fields: List[ModelField]) -> ModelNameMap:
489
- models = get_flat_models_from_fields(fields, known_models=set())
490
- return get_model_name_map(models) # type: ignore[no-any-return]
491
-
492
- def get_definitions(
493
- *,
494
- fields: List[ModelField],
495
- schema_generator: GenerateJsonSchema,
496
- model_name_map: ModelNameMap,
497
- separate_input_output_schemas: bool = True,
498
- ) -> Tuple[
499
- Dict[
500
- Tuple[ModelField, Literal["validation", "serialization"]], JsonSchemaValue
501
- ],
502
- Dict[str, Dict[str, Any]],
503
- ]:
504
- models = get_flat_models_from_fields(fields, known_models=set())
505
- return {}, get_model_definitions(
506
- flat_models=models, model_name_map=model_name_map
507
- )
508
-
509
- def is_scalar_field(field: ModelField) -> bool:
510
- return is_pv1_scalar_field(field)
511
-
512
- def is_sequence_field(field: ModelField) -> bool:
513
- return field.shape in sequence_shapes or _annotation_is_sequence(field.type_) # type: ignore[attr-defined]
514
-
515
- def is_scalar_sequence_field(field: ModelField) -> bool:
516
- return is_pv1_scalar_sequence_field(field)
517
-
518
- def is_bytes_field(field: ModelField) -> bool:
519
- return lenient_issubclass(field.type_, bytes)
520
-
521
- def is_bytes_sequence_field(field: ModelField) -> bool:
522
- return field.shape in sequence_shapes and lenient_issubclass(field.type_, bytes) # type: ignore[attr-defined]
523
-
524
- def copy_field_info(*, field_info: FieldInfo, annotation: Any) -> FieldInfo:
525
- return copy(field_info)
526
-
527
- def serialize_sequence_value(*, field: ModelField, value: Any) -> Sequence[Any]:
528
- return sequence_shape_to_type[field.shape](value) # type: ignore[no-any-return,attr-defined]
529
-
530
- def get_missing_field_error(loc: Tuple[str, ...]) -> Dict[str, Any]:
531
- missing_field_error = ErrorWrapper(MissingError(), loc=loc) # type: ignore[call-arg]
532
- new_error = ValidationError([missing_field_error], RequestErrorModel)
533
- return new_error.errors()[0] # type: ignore[return-value]
534
-
535
- def create_body_model(
536
- *, fields: Sequence[ModelField], model_name: str
537
- ) -> Type[BaseModel]:
538
- BodyModel = create_model(model_name)
539
- for f in fields:
540
- BodyModel.__fields__[f.name] = f # type: ignore[index]
541
- return BodyModel
542
-
543
- def get_model_fields(model: Type[BaseModel]) -> List[ModelField]:
544
- return list(model.__fields__.values()) # type: ignore[attr-defined]
545
-
546
-
547
- def _regenerate_error_with_loc(
548
- *, errors: Sequence[Any], loc_prefix: Tuple[Union[str, int], ...]
549
- ) -> List[Dict[str, Any]]:
550
- updated_loc_errors: List[Any] = [
551
- {**err, "loc": loc_prefix + err.get("loc", ())}
552
- for err in _normalize_errors(errors)
553
- ]
554
-
555
- return updated_loc_errors
556
-
557
-
558
- def _annotation_is_sequence(annotation: Union[Type[Any], None]) -> bool:
559
- if lenient_issubclass(annotation, (str, bytes)):
560
- return False
561
- return lenient_issubclass(annotation, sequence_types)
562
-
563
-
564
- def field_annotation_is_sequence(annotation: Union[Type[Any], None]) -> bool:
565
- origin = get_origin(annotation)
566
- if origin is Union or origin is UnionType:
567
- for arg in get_args(annotation):
568
- if field_annotation_is_sequence(arg):
569
- return True
570
- return False
571
- return _annotation_is_sequence(annotation) or _annotation_is_sequence(
572
- get_origin(annotation)
573
- )
574
-
575
-
576
- def value_is_sequence(value: Any) -> bool:
577
- return isinstance(value, sequence_types) and not isinstance(value, (str, bytes)) # type: ignore[arg-type]
578
-
579
-
580
- def _annotation_is_complex(annotation: Union[Type[Any], None]) -> bool:
581
- return (
582
- lenient_issubclass(annotation, (BaseModel, Mapping, UploadFile))
583
- or _annotation_is_sequence(annotation)
584
- or is_dataclass(annotation)
585
- )
586
-
587
-
588
- def field_annotation_is_complex(annotation: Union[Type[Any], None]) -> bool:
589
- origin = get_origin(annotation)
590
- if origin is Union or origin is UnionType:
591
- return any(field_annotation_is_complex(arg) for arg in get_args(annotation))
592
-
593
- if origin is Annotated:
594
- return field_annotation_is_complex(get_args(annotation)[0])
595
-
596
- return (
597
- _annotation_is_complex(annotation)
598
- or _annotation_is_complex(origin)
599
- or hasattr(origin, "__pydantic_core_schema__")
600
- or hasattr(origin, "__get_pydantic_core_schema__")
601
- )
602
-
603
-
604
- def field_annotation_is_scalar(annotation: Any) -> bool:
605
- # handle Ellipsis here to make tuple[int, ...] work nicely
606
- return annotation is Ellipsis or not field_annotation_is_complex(annotation)
607
-
608
-
609
- def field_annotation_is_scalar_sequence(annotation: Union[Type[Any], None]) -> bool:
610
- origin = get_origin(annotation)
611
- if origin is Union or origin is UnionType:
612
- at_least_one_scalar_sequence = False
613
- for arg in get_args(annotation):
614
- if field_annotation_is_scalar_sequence(arg):
615
- at_least_one_scalar_sequence = True
616
- continue
617
- elif not field_annotation_is_scalar(arg):
618
- return False
619
- return at_least_one_scalar_sequence
620
- return field_annotation_is_sequence(annotation) and all(
621
- field_annotation_is_scalar(sub_annotation)
622
- for sub_annotation in get_args(annotation)
623
- )
624
-
625
-
626
- def is_bytes_or_nonable_bytes_annotation(annotation: Any) -> bool:
627
- if lenient_issubclass(annotation, bytes):
628
- return True
629
- origin = get_origin(annotation)
630
- if origin is Union or origin is UnionType:
631
- for arg in get_args(annotation):
632
- if lenient_issubclass(arg, bytes):
633
- return True
634
- return False
635
-
636
-
637
- def is_uploadfile_or_nonable_uploadfile_annotation(annotation: Any) -> bool:
638
- if lenient_issubclass(annotation, UploadFile):
639
- return True
640
- origin = get_origin(annotation)
641
- if origin is Union or origin is UnionType:
642
- for arg in get_args(annotation):
643
- if lenient_issubclass(arg, UploadFile):
644
- return True
645
- return False
646
-
647
-
648
- def is_bytes_sequence_annotation(annotation: Any) -> bool:
649
- origin = get_origin(annotation)
650
- if origin is Union or origin is UnionType:
651
- at_least_one = False
652
- for arg in get_args(annotation):
653
- if is_bytes_sequence_annotation(arg):
654
- at_least_one = True
655
- continue
656
- return at_least_one
657
- return field_annotation_is_sequence(annotation) and all(
658
- is_bytes_or_nonable_bytes_annotation(sub_annotation)
659
- for sub_annotation in get_args(annotation)
660
- )
661
-
662
-
663
- def is_uploadfile_sequence_annotation(annotation: Any) -> bool:
664
- origin = get_origin(annotation)
665
- if origin is Union or origin is UnionType:
666
- at_least_one = False
667
- for arg in get_args(annotation):
668
- if is_uploadfile_sequence_annotation(arg):
669
- at_least_one = True
670
- continue
671
- return at_least_one
672
- return field_annotation_is_sequence(annotation) and all(
673
- is_uploadfile_or_nonable_uploadfile_annotation(sub_annotation)
674
- for sub_annotation in get_args(annotation)
675
- )
676
-
677
-
678
- @lru_cache
679
- def get_cached_model_fields(model: Type[BaseModel]) -> List[ModelField]:
680
- return get_model_fields(model)