pythonasyncioapiasyncfastapiframeworkjsonjson-schemaopenapiopenapi3pydanticpython-typespython3redocreststarletteswaggerswagger-uiuvicornweb
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
363 lines
13 KiB
363 lines
13 KiB
# mypy: ignore-errors
|
|
from functools import lru_cache
|
|
from typing import (
|
|
Any,
|
|
Dict,
|
|
List,
|
|
Sequence,
|
|
Tuple,
|
|
Type,
|
|
)
|
|
|
|
from fastapi._compat.shared import PYDANTIC_V2
|
|
from fastapi._compat.lazy_import import get_v1_if_loaded, v1_isinstance, v1_lenient_issubclass
|
|
from fastapi.types import ModelNameMap
|
|
from pydantic import BaseModel
|
|
from typing_extensions import Literal
|
|
|
|
from .model_field import ModelField
|
|
|
|
if PYDANTIC_V2:
|
|
from .v2 import BaseConfig as BaseConfig
|
|
from .v2 import FieldInfo as FieldInfo
|
|
from .v2 import PydanticSchemaGenerationError as PydanticSchemaGenerationError
|
|
from .v2 import RequiredParam as RequiredParam
|
|
from .v2 import Undefined as Undefined
|
|
from .v2 import UndefinedType as UndefinedType
|
|
from .v2 import Url as Url
|
|
from .v2 import Validator as Validator
|
|
from .v2 import _get_model_config as _get_model_config
|
|
from .v2 import _model_dump as _model_dump
|
|
from .v2 import _model_rebuild as _model_rebuild
|
|
from .v2 import copy_field_info as copy_field_info
|
|
from .v2 import create_body_model as create_body_model
|
|
from .v2 import evaluate_forwardref as evaluate_forwardref
|
|
from .v2 import get_annotation_from_field_info as get_annotation_from_field_info
|
|
from .v2 import get_definitions as get_definitions
|
|
from .v2 import get_missing_field_error as get_missing_field_error
|
|
from .v2 import get_schema_from_model_field as get_schema_from_model_field
|
|
from .v2 import is_bytes_field as is_bytes_field
|
|
from .v2 import is_bytes_sequence_field as is_bytes_sequence_field
|
|
from .v2 import is_scalar_field as is_scalar_field
|
|
from .v2 import is_scalar_sequence_field as is_scalar_sequence_field
|
|
from .v2 import is_sequence_field as is_sequence_field
|
|
from .v2 import serialize_sequence_value as serialize_sequence_value
|
|
from .v2 import (
|
|
with_info_plain_validator_function as with_info_plain_validator_function,
|
|
)
|
|
else:
|
|
from .v1 import BaseConfig as BaseConfig
|
|
from .v1 import FieldInfo as FieldInfo
|
|
from .v1 import PydanticSchemaGenerationError as PydanticSchemaGenerationError
|
|
from .v1 import RequiredParam as RequiredParam
|
|
from .v1 import Undefined as Undefined
|
|
from .v1 import UndefinedType as UndefinedType
|
|
from .v1 import Url as Url
|
|
from .v1 import Validator as Validator
|
|
from .v1 import _get_model_config as _get_model_config
|
|
from .v1 import _model_dump as _model_dump
|
|
from .v1 import _model_rebuild as _model_rebuild
|
|
from .v1 import copy_field_info as copy_field_info
|
|
from .v1 import create_body_model as create_body_model
|
|
from .v1 import evaluate_forwardref as evaluate_forwardref
|
|
from .v1 import get_annotation_from_field_info as get_annotation_from_field_info
|
|
from .v1 import get_definitions as get_definitions
|
|
from .v1 import get_missing_field_error as get_missing_field_error
|
|
from .v1 import get_schema_from_model_field as get_schema_from_model_field
|
|
from .v1 import is_bytes_field as is_bytes_field
|
|
from .v1 import is_bytes_sequence_field as is_bytes_sequence_field
|
|
from .v1 import is_scalar_field as is_scalar_field
|
|
from .v1 import is_scalar_sequence_field as is_scalar_sequence_field
|
|
from .v1 import is_sequence_field as is_sequence_field
|
|
from .v1 import serialize_sequence_value as serialize_sequence_value
|
|
from .v1 import (
|
|
with_info_plain_validator_function as with_info_plain_validator_function,
|
|
)
|
|
|
|
|
|
@lru_cache
|
|
def get_cached_model_fields(model: Type[BaseModel]) -> List[ModelField]:
|
|
if v1_lenient_issubclass(model, "BaseModel"):
|
|
v1 = get_v1_if_loaded()
|
|
return v1.get_model_fields(model)
|
|
else:
|
|
from . import v2
|
|
return v2.get_model_fields(model) # type: ignore[return-value]
|
|
|
|
|
|
def _is_undefined(value: object) -> bool:
|
|
if v1_isinstance(value, "UndefinedType"):
|
|
return True
|
|
elif PYDANTIC_V2:
|
|
from pydantic_core import PydanticUndefined
|
|
return value is PydanticUndefined
|
|
else:
|
|
return False
|
|
|
|
|
|
def _get_model_config(model: BaseModel) -> Any:
|
|
if v1_isinstance(model, "BaseModel"):
|
|
v1 = get_v1_if_loaded()
|
|
return v1._get_model_config(model)
|
|
elif PYDANTIC_V2:
|
|
from . import v2
|
|
return v2._get_model_config(model)
|
|
else:
|
|
return getattr(model, "__config__", None)
|
|
|
|
|
|
def _model_dump(
|
|
model: BaseModel, mode: Literal["json", "python"] = "json", **kwargs: Any
|
|
) -> Any:
|
|
if v1_isinstance(model, "BaseModel"):
|
|
v1 = get_v1_if_loaded()
|
|
return v1._model_dump(model, mode=mode, **kwargs)
|
|
if PYDANTIC_V2:
|
|
from . import v2
|
|
return v2._model_dump(model, mode=mode, **kwargs)
|
|
else:
|
|
return model.dict(**kwargs)
|
|
|
|
|
|
def _is_error_wrapper(exc: Exception) -> bool:
|
|
if v1_isinstance(exc, "ErrorWrapper"):
|
|
return True
|
|
elif PYDANTIC_V2:
|
|
from . import v2
|
|
return v2._is_error_wrapper(exc)
|
|
else:
|
|
return False
|
|
|
|
|
|
def copy_field_info(*, field_info: FieldInfo, annotation: Any) -> FieldInfo:
|
|
if v1_isinstance(field_info, "FieldInfo"):
|
|
v1 = get_v1_if_loaded()
|
|
return v1.copy_field_info(field_info=field_info, annotation=annotation)
|
|
else:
|
|
from . import v2
|
|
return v2.copy_field_info(field_info=field_info, annotation=annotation)
|
|
|
|
|
|
def create_body_model(
|
|
*, fields: List[ModelField], model_name: str
|
|
) -> Type[BaseModel]:
|
|
if fields and v1_isinstance(fields[0], "ModelField"):
|
|
v1 = get_v1_if_loaded()
|
|
return v1.create_body_model(fields=fields, model_name=model_name)
|
|
else:
|
|
from . import v2
|
|
return v2.create_body_model(fields=fields, model_name=model_name)
|
|
|
|
|
|
def get_annotation_from_field_info(
|
|
annotation: Any, field_info: FieldInfo, field_name: str
|
|
) -> Any:
|
|
if v1_isinstance(field_info, "FieldInfo"):
|
|
v1 = get_v1_if_loaded()
|
|
return v1.get_annotation_from_field_info(
|
|
annotation=annotation, field_info=field_info, field_name=field_name
|
|
)
|
|
else:
|
|
from . import v2
|
|
return v2.get_annotation_from_field_info(
|
|
annotation=annotation, field_info=field_info, field_name=field_name
|
|
)
|
|
|
|
|
|
def is_bytes_field(field: ModelField) -> bool:
|
|
if v1_isinstance(field, "ModelField"):
|
|
v1 = get_v1_if_loaded()
|
|
return v1.is_bytes_field(field)
|
|
else:
|
|
from . import v2
|
|
return v2.is_bytes_field(field)
|
|
|
|
|
|
def is_bytes_sequence_field(field: ModelField) -> bool:
|
|
if v1_isinstance(field, "ModelField"):
|
|
v1 = get_v1_if_loaded()
|
|
return v1.is_bytes_sequence_field(field)
|
|
else:
|
|
from . import v2
|
|
return v2.is_bytes_sequence_field(field)
|
|
|
|
|
|
def is_scalar_field(field: ModelField) -> bool:
|
|
if v1_isinstance(field, "ModelField"):
|
|
v1 = get_v1_if_loaded()
|
|
return v1.is_scalar_field(field)
|
|
else:
|
|
from . import v2
|
|
return v2.is_scalar_field(field)
|
|
|
|
|
|
def is_scalar_sequence_field(field: ModelField) -> bool:
|
|
if v1_isinstance(field, "ModelField"):
|
|
v1 = get_v1_if_loaded()
|
|
return v1.is_scalar_sequence_field(field)
|
|
else:
|
|
from . import v2
|
|
return v2.is_scalar_sequence_field(field)
|
|
|
|
|
|
def is_sequence_field(field: ModelField) -> bool:
|
|
if v1_isinstance(field, "ModelField"):
|
|
v1 = get_v1_if_loaded()
|
|
return v1.is_sequence_field(field)
|
|
else:
|
|
from . import v2
|
|
return v2.is_sequence_field(field)
|
|
|
|
|
|
def serialize_sequence_value(*, field: ModelField, value: Any) -> Sequence[Any]:
|
|
if v1_isinstance(field, "ModelField"):
|
|
v1 = get_v1_if_loaded()
|
|
return v1.serialize_sequence_value(field=field, value=value)
|
|
else:
|
|
from . import v2
|
|
return v2.serialize_sequence_value(field=field, value=value)
|
|
|
|
|
|
def get_compat_model_name_map(fields: List[ModelField]) -> ModelNameMap:
|
|
v1 = get_v1_if_loaded()
|
|
v1_model_fields = [field for field in fields if v1_isinstance(field, "ModelField")] if v1 else []
|
|
v1_flat_models = v1.get_flat_models_from_fields(v1_model_fields, known_models=set()) if v1 and v1_model_fields else set()
|
|
all_flat_models = v1_flat_models
|
|
if PYDANTIC_V2:
|
|
from . import v2
|
|
v2_model_fields = [field for field in fields if not v1_isinstance(field, "ModelField")]
|
|
v2_flat_models = v2.get_flat_models_from_fields(v2_model_fields, known_models=set())
|
|
all_flat_models = v1_flat_models | v2_flat_models
|
|
model_name_map = v2.get_model_name_map(all_flat_models)
|
|
return model_name_map
|
|
model_name_map = v1.get_model_name_map(all_flat_models) if v1 else {}
|
|
return model_name_map
|
|
|
|
|
|
def get_definitions(
|
|
*,
|
|
fields: List[ModelField],
|
|
model_name_map: ModelNameMap,
|
|
separate_input_output_schemas: bool = True,
|
|
) -> Tuple[
|
|
Dict[Tuple[ModelField, Literal["validation", "serialization"]], Dict[str, Any]],
|
|
Dict[str, Dict[str, Any]],
|
|
]:
|
|
v1 = get_v1_if_loaded()
|
|
v1_fields = [field for field in fields if v1_isinstance(field, "ModelField")] if v1 else []
|
|
if v1_fields and v1:
|
|
v1_field_maps, v1_definitions = v1.get_definitions(
|
|
fields=v1_fields,
|
|
model_name_map=model_name_map,
|
|
separate_input_output_schemas=separate_input_output_schemas,
|
|
)
|
|
else:
|
|
v1_field_maps: Dict[Tuple[ModelField, Literal["validation", "serialization"]], Dict[str, Any]] = {}
|
|
v1_definitions: Dict[str, Dict[str, Any]] = {}
|
|
if PYDANTIC_V2:
|
|
from . import v2
|
|
v2_fields = [field for field in fields if not v1_isinstance(field, "ModelField")]
|
|
v2_field_maps, v2_definitions = v2.get_definitions(
|
|
fields=v2_fields,
|
|
model_name_map=model_name_map,
|
|
separate_input_output_schemas=separate_input_output_schemas,
|
|
)
|
|
all_field_maps = {**v1_field_maps, **v2_field_maps}
|
|
all_definitions = {**v1_definitions, **v2_definitions}
|
|
return all_field_maps, all_definitions
|
|
return v1_field_maps, v1_definitions
|
|
|
|
|
|
def get_schema_from_model_field(
|
|
*,
|
|
field: ModelField,
|
|
model_name_map: ModelNameMap,
|
|
field_mapping: Dict[
|
|
Tuple[ModelField, Literal["validation", "serialization"]], Dict[str, Any]
|
|
],
|
|
separate_input_output_schemas: bool = True,
|
|
) -> Dict[str, Any]:
|
|
if v1_isinstance(field, "ModelField"):
|
|
v1 = get_v1_if_loaded()
|
|
return v1.get_schema_from_model_field(
|
|
field=field,
|
|
model_name_map=model_name_map,
|
|
field_mapping=field_mapping,
|
|
separate_input_output_schemas=separate_input_output_schemas,
|
|
)
|
|
else:
|
|
from . import v2
|
|
return v2.get_schema_from_model_field(
|
|
field=field,
|
|
model_name_map=model_name_map,
|
|
field_mapping=field_mapping,
|
|
separate_input_output_schemas=separate_input_output_schemas,
|
|
)
|
|
|
|
|
|
def _is_model_field(value: Any) -> bool:
|
|
if v1_isinstance(value, "ModelField"):
|
|
return True
|
|
elif PYDANTIC_V2:
|
|
from . import v2
|
|
return v2._is_model_field(value)
|
|
else:
|
|
return False
|
|
|
|
|
|
def _is_model_class(value: Any) -> bool:
|
|
if v1_lenient_issubclass(value, "BaseModel"):
|
|
return True
|
|
elif PYDANTIC_V2:
|
|
from . import v2
|
|
return v2._is_model_class(value)
|
|
else:
|
|
return False
|
|
|
|
|
|
def get_missing_field_error(loc: Tuple[str, ...], field: ModelField) -> Dict[str, Any]:
|
|
if v1_isinstance(field, "ModelField"):
|
|
v1 = get_v1_if_loaded()
|
|
return v1.get_missing_field_error(loc=loc, field=field)
|
|
else:
|
|
from . import v2
|
|
return v2.get_missing_field_error(loc=loc, field=field)
|
|
|
|
|
|
def evaluate_forwardref(type_: Any, globalns: Dict[str, Any], localns: Dict[str, Any]) -> Any:
|
|
if PYDANTIC_V2:
|
|
from . import v2
|
|
return v2.evaluate_forwardref(type_, globalns, localns)
|
|
else:
|
|
v1 = get_v1_if_loaded()
|
|
return v1.evaluate_forwardref(type_, globalns, localns)
|
|
|
|
|
|
def with_info_plain_validator_function(
|
|
func: Any,
|
|
info_argname: str = "info",
|
|
) -> Any:
|
|
if PYDANTIC_V2:
|
|
try:
|
|
from pydantic_core.core_schema import (
|
|
with_info_plain_validator_function as pydantic_core_with_info,
|
|
)
|
|
except ImportError: # pragma: no cover
|
|
from pydantic_core.core_schema import (
|
|
general_plain_validator_function as pydantic_core_with_info,
|
|
)
|
|
return pydantic_core_with_info(func)
|
|
else:
|
|
v1 = get_v1_if_loaded()
|
|
return v1.with_info_plain_validator_function(func=func, info_argname=info_argname)
|
|
|
|
|
|
def _model_rebuild(model) -> None:
|
|
if v1_lenient_issubclass(model, "BaseModel"):
|
|
v1 = get_v1_if_loaded()
|
|
v1._model_rebuild(model)
|
|
elif PYDANTIC_V2:
|
|
from . import v2
|
|
v2._model_rebuild(model)
|
|
else:
|
|
model.update_forward_refs()
|
|
|