mirror of https://github.com/tiangolo/fastapi.git
⬆ Require Pydantic > 1.0 (#1862)
* 🔥 Remove support for Pydantic < 1.0 * 🔥 Remove deprecated skip_defaults from jsonable_encoder and set default for exclude to None, as in Pydantic * ♻️ Set default of response_model_exclude=None as in Pydantic * ⬆️ Require Pydantic >=1.0.0 in requirements
This commit is contained in:
parent
3390182fc9
commit
e1758d107e
|
|
@ -16,7 +16,6 @@ from fastapi.openapi.docs import (
|
|||
)
|
||||
from fastapi.openapi.utils import get_openapi
|
||||
from fastapi.params import Depends
|
||||
from fastapi.utils import warning_response_model_skip_defaults_deprecated
|
||||
from starlette.applications import Starlette
|
||||
from starlette.datastructures import State
|
||||
from starlette.exceptions import HTTPException
|
||||
|
|
@ -198,9 +197,8 @@ class FastAPI(Starlette):
|
|||
methods: Optional[List[str]] = None,
|
||||
operation_id: Optional[str] = None,
|
||||
response_model_include: Optional[Union[SetIntStr, DictIntStrAny]] = None,
|
||||
response_model_exclude: Union[SetIntStr, DictIntStrAny] = set(),
|
||||
response_model_exclude: Optional[Union[SetIntStr, DictIntStrAny]] = None,
|
||||
response_model_by_alias: bool = True,
|
||||
response_model_skip_defaults: Optional[bool] = None,
|
||||
response_model_exclude_unset: bool = False,
|
||||
response_model_exclude_defaults: bool = False,
|
||||
response_model_exclude_none: bool = False,
|
||||
|
|
@ -208,8 +206,6 @@ class FastAPI(Starlette):
|
|||
response_class: Optional[Type[Response]] = None,
|
||||
name: Optional[str] = None,
|
||||
) -> None:
|
||||
if response_model_skip_defaults is not None:
|
||||
warning_response_model_skip_defaults_deprecated() # pragma: nocover
|
||||
self.router.add_api_route(
|
||||
path,
|
||||
endpoint=endpoint,
|
||||
|
|
@ -227,9 +223,7 @@ class FastAPI(Starlette):
|
|||
response_model_include=response_model_include,
|
||||
response_model_exclude=response_model_exclude,
|
||||
response_model_by_alias=response_model_by_alias,
|
||||
response_model_exclude_unset=bool(
|
||||
response_model_exclude_unset or response_model_skip_defaults
|
||||
),
|
||||
response_model_exclude_unset=response_model_exclude_unset,
|
||||
response_model_exclude_defaults=response_model_exclude_defaults,
|
||||
response_model_exclude_none=response_model_exclude_none,
|
||||
include_in_schema=include_in_schema,
|
||||
|
|
@ -253,9 +247,8 @@ class FastAPI(Starlette):
|
|||
methods: Optional[List[str]] = None,
|
||||
operation_id: Optional[str] = None,
|
||||
response_model_include: Optional[Union[SetIntStr, DictIntStrAny]] = None,
|
||||
response_model_exclude: Union[SetIntStr, DictIntStrAny] = set(),
|
||||
response_model_exclude: Optional[Union[SetIntStr, DictIntStrAny]] = None,
|
||||
response_model_by_alias: bool = True,
|
||||
response_model_skip_defaults: Optional[bool] = None,
|
||||
response_model_exclude_unset: bool = False,
|
||||
response_model_exclude_defaults: bool = False,
|
||||
response_model_exclude_none: bool = False,
|
||||
|
|
@ -263,9 +256,6 @@ class FastAPI(Starlette):
|
|||
response_class: Optional[Type[Response]] = None,
|
||||
name: Optional[str] = None,
|
||||
) -> Callable:
|
||||
if response_model_skip_defaults is not None:
|
||||
warning_response_model_skip_defaults_deprecated() # pragma: nocover
|
||||
|
||||
def decorator(func: Callable) -> Callable:
|
||||
self.router.add_api_route(
|
||||
path,
|
||||
|
|
@ -284,9 +274,7 @@ class FastAPI(Starlette):
|
|||
response_model_include=response_model_include,
|
||||
response_model_exclude=response_model_exclude,
|
||||
response_model_by_alias=response_model_by_alias,
|
||||
response_model_exclude_unset=bool(
|
||||
response_model_exclude_unset or response_model_skip_defaults
|
||||
),
|
||||
response_model_exclude_unset=response_model_exclude_unset,
|
||||
response_model_exclude_defaults=response_model_exclude_defaults,
|
||||
response_model_exclude_none=response_model_exclude_none,
|
||||
include_in_schema=include_in_schema,
|
||||
|
|
@ -344,9 +332,8 @@ class FastAPI(Starlette):
|
|||
deprecated: Optional[bool] = None,
|
||||
operation_id: Optional[str] = None,
|
||||
response_model_include: Optional[Union[SetIntStr, DictIntStrAny]] = None,
|
||||
response_model_exclude: Union[SetIntStr, DictIntStrAny] = set(),
|
||||
response_model_exclude: Optional[Union[SetIntStr, DictIntStrAny]] = None,
|
||||
response_model_by_alias: bool = True,
|
||||
response_model_skip_defaults: Optional[bool] = None,
|
||||
response_model_exclude_unset: bool = False,
|
||||
response_model_exclude_defaults: bool = False,
|
||||
response_model_exclude_none: bool = False,
|
||||
|
|
@ -355,8 +342,6 @@ class FastAPI(Starlette):
|
|||
name: Optional[str] = None,
|
||||
callbacks: Optional[List[routing.APIRoute]] = None,
|
||||
) -> Callable:
|
||||
if response_model_skip_defaults is not None:
|
||||
warning_response_model_skip_defaults_deprecated() # pragma: nocover
|
||||
return self.router.get(
|
||||
path,
|
||||
response_model=response_model,
|
||||
|
|
@ -372,9 +357,7 @@ class FastAPI(Starlette):
|
|||
response_model_include=response_model_include,
|
||||
response_model_exclude=response_model_exclude,
|
||||
response_model_by_alias=response_model_by_alias,
|
||||
response_model_exclude_unset=bool(
|
||||
response_model_exclude_unset or response_model_skip_defaults
|
||||
),
|
||||
response_model_exclude_unset=response_model_exclude_unset,
|
||||
response_model_exclude_defaults=response_model_exclude_defaults,
|
||||
response_model_exclude_none=response_model_exclude_none,
|
||||
include_in_schema=include_in_schema,
|
||||
|
|
@ -398,9 +381,8 @@ class FastAPI(Starlette):
|
|||
deprecated: Optional[bool] = None,
|
||||
operation_id: Optional[str] = None,
|
||||
response_model_include: Optional[Union[SetIntStr, DictIntStrAny]] = None,
|
||||
response_model_exclude: Union[SetIntStr, DictIntStrAny] = set(),
|
||||
response_model_exclude: Optional[Union[SetIntStr, DictIntStrAny]] = None,
|
||||
response_model_by_alias: bool = True,
|
||||
response_model_skip_defaults: Optional[bool] = None,
|
||||
response_model_exclude_unset: bool = False,
|
||||
response_model_exclude_defaults: bool = False,
|
||||
response_model_exclude_none: bool = False,
|
||||
|
|
@ -409,8 +391,6 @@ class FastAPI(Starlette):
|
|||
name: Optional[str] = None,
|
||||
callbacks: Optional[List[routing.APIRoute]] = None,
|
||||
) -> Callable:
|
||||
if response_model_skip_defaults is not None:
|
||||
warning_response_model_skip_defaults_deprecated() # pragma: nocover
|
||||
return self.router.put(
|
||||
path,
|
||||
response_model=response_model,
|
||||
|
|
@ -426,9 +406,7 @@ class FastAPI(Starlette):
|
|||
response_model_include=response_model_include,
|
||||
response_model_exclude=response_model_exclude,
|
||||
response_model_by_alias=response_model_by_alias,
|
||||
response_model_exclude_unset=bool(
|
||||
response_model_exclude_unset or response_model_skip_defaults
|
||||
),
|
||||
response_model_exclude_unset=response_model_exclude_unset,
|
||||
response_model_exclude_defaults=response_model_exclude_defaults,
|
||||
response_model_exclude_none=response_model_exclude_none,
|
||||
include_in_schema=include_in_schema,
|
||||
|
|
@ -452,9 +430,8 @@ class FastAPI(Starlette):
|
|||
deprecated: Optional[bool] = None,
|
||||
operation_id: Optional[str] = None,
|
||||
response_model_include: Optional[Union[SetIntStr, DictIntStrAny]] = None,
|
||||
response_model_exclude: Union[SetIntStr, DictIntStrAny] = set(),
|
||||
response_model_exclude: Optional[Union[SetIntStr, DictIntStrAny]] = None,
|
||||
response_model_by_alias: bool = True,
|
||||
response_model_skip_defaults: Optional[bool] = None,
|
||||
response_model_exclude_unset: bool = False,
|
||||
response_model_exclude_defaults: bool = False,
|
||||
response_model_exclude_none: bool = False,
|
||||
|
|
@ -463,8 +440,6 @@ class FastAPI(Starlette):
|
|||
name: Optional[str] = None,
|
||||
callbacks: Optional[List[routing.APIRoute]] = None,
|
||||
) -> Callable:
|
||||
if response_model_skip_defaults is not None:
|
||||
warning_response_model_skip_defaults_deprecated() # pragma: nocover
|
||||
return self.router.post(
|
||||
path,
|
||||
response_model=response_model,
|
||||
|
|
@ -480,9 +455,7 @@ class FastAPI(Starlette):
|
|||
response_model_include=response_model_include,
|
||||
response_model_exclude=response_model_exclude,
|
||||
response_model_by_alias=response_model_by_alias,
|
||||
response_model_exclude_unset=bool(
|
||||
response_model_exclude_unset or response_model_skip_defaults
|
||||
),
|
||||
response_model_exclude_unset=response_model_exclude_unset,
|
||||
response_model_exclude_defaults=response_model_exclude_defaults,
|
||||
response_model_exclude_none=response_model_exclude_none,
|
||||
include_in_schema=include_in_schema,
|
||||
|
|
@ -506,9 +479,8 @@ class FastAPI(Starlette):
|
|||
deprecated: Optional[bool] = None,
|
||||
operation_id: Optional[str] = None,
|
||||
response_model_include: Optional[Union[SetIntStr, DictIntStrAny]] = None,
|
||||
response_model_exclude: Union[SetIntStr, DictIntStrAny] = set(),
|
||||
response_model_exclude: Optional[Union[SetIntStr, DictIntStrAny]] = None,
|
||||
response_model_by_alias: bool = True,
|
||||
response_model_skip_defaults: Optional[bool] = None,
|
||||
response_model_exclude_unset: bool = False,
|
||||
response_model_exclude_defaults: bool = False,
|
||||
response_model_exclude_none: bool = False,
|
||||
|
|
@ -517,8 +489,6 @@ class FastAPI(Starlette):
|
|||
name: Optional[str] = None,
|
||||
callbacks: Optional[List[routing.APIRoute]] = None,
|
||||
) -> Callable:
|
||||
if response_model_skip_defaults is not None:
|
||||
warning_response_model_skip_defaults_deprecated() # pragma: nocover
|
||||
return self.router.delete(
|
||||
path,
|
||||
response_model=response_model,
|
||||
|
|
@ -534,9 +504,7 @@ class FastAPI(Starlette):
|
|||
response_model_exclude=response_model_exclude,
|
||||
response_model_by_alias=response_model_by_alias,
|
||||
operation_id=operation_id,
|
||||
response_model_exclude_unset=bool(
|
||||
response_model_exclude_unset or response_model_skip_defaults
|
||||
),
|
||||
response_model_exclude_unset=response_model_exclude_unset,
|
||||
response_model_exclude_defaults=response_model_exclude_defaults,
|
||||
response_model_exclude_none=response_model_exclude_none,
|
||||
include_in_schema=include_in_schema,
|
||||
|
|
@ -560,9 +528,8 @@ class FastAPI(Starlette):
|
|||
deprecated: Optional[bool] = None,
|
||||
operation_id: Optional[str] = None,
|
||||
response_model_include: Optional[Union[SetIntStr, DictIntStrAny]] = None,
|
||||
response_model_exclude: Union[SetIntStr, DictIntStrAny] = set(),
|
||||
response_model_exclude: Optional[Union[SetIntStr, DictIntStrAny]] = None,
|
||||
response_model_by_alias: bool = True,
|
||||
response_model_skip_defaults: Optional[bool] = None,
|
||||
response_model_exclude_unset: bool = False,
|
||||
response_model_exclude_defaults: bool = False,
|
||||
response_model_exclude_none: bool = False,
|
||||
|
|
@ -571,8 +538,6 @@ class FastAPI(Starlette):
|
|||
name: Optional[str] = None,
|
||||
callbacks: Optional[List[routing.APIRoute]] = None,
|
||||
) -> Callable:
|
||||
if response_model_skip_defaults is not None:
|
||||
warning_response_model_skip_defaults_deprecated() # pragma: nocover
|
||||
return self.router.options(
|
||||
path,
|
||||
response_model=response_model,
|
||||
|
|
@ -588,9 +553,7 @@ class FastAPI(Starlette):
|
|||
response_model_include=response_model_include,
|
||||
response_model_exclude=response_model_exclude,
|
||||
response_model_by_alias=response_model_by_alias,
|
||||
response_model_exclude_unset=bool(
|
||||
response_model_exclude_unset or response_model_skip_defaults
|
||||
),
|
||||
response_model_exclude_unset=response_model_exclude_unset,
|
||||
response_model_exclude_defaults=response_model_exclude_defaults,
|
||||
response_model_exclude_none=response_model_exclude_none,
|
||||
include_in_schema=include_in_schema,
|
||||
|
|
@ -614,9 +577,8 @@ class FastAPI(Starlette):
|
|||
deprecated: Optional[bool] = None,
|
||||
operation_id: Optional[str] = None,
|
||||
response_model_include: Optional[Union[SetIntStr, DictIntStrAny]] = None,
|
||||
response_model_exclude: Union[SetIntStr, DictIntStrAny] = set(),
|
||||
response_model_exclude: Optional[Union[SetIntStr, DictIntStrAny]] = None,
|
||||
response_model_by_alias: bool = True,
|
||||
response_model_skip_defaults: Optional[bool] = None,
|
||||
response_model_exclude_unset: bool = False,
|
||||
response_model_exclude_defaults: bool = False,
|
||||
response_model_exclude_none: bool = False,
|
||||
|
|
@ -625,8 +587,6 @@ class FastAPI(Starlette):
|
|||
name: Optional[str] = None,
|
||||
callbacks: Optional[List[routing.APIRoute]] = None,
|
||||
) -> Callable:
|
||||
if response_model_skip_defaults is not None:
|
||||
warning_response_model_skip_defaults_deprecated() # pragma: nocover
|
||||
return self.router.head(
|
||||
path,
|
||||
response_model=response_model,
|
||||
|
|
@ -642,9 +602,7 @@ class FastAPI(Starlette):
|
|||
response_model_include=response_model_include,
|
||||
response_model_exclude=response_model_exclude,
|
||||
response_model_by_alias=response_model_by_alias,
|
||||
response_model_exclude_unset=bool(
|
||||
response_model_exclude_unset or response_model_skip_defaults
|
||||
),
|
||||
response_model_exclude_unset=response_model_exclude_unset,
|
||||
response_model_exclude_defaults=response_model_exclude_defaults,
|
||||
response_model_exclude_none=response_model_exclude_none,
|
||||
include_in_schema=include_in_schema,
|
||||
|
|
@ -668,9 +626,8 @@ class FastAPI(Starlette):
|
|||
deprecated: Optional[bool] = None,
|
||||
operation_id: Optional[str] = None,
|
||||
response_model_include: Optional[Union[SetIntStr, DictIntStrAny]] = None,
|
||||
response_model_exclude: Union[SetIntStr, DictIntStrAny] = set(),
|
||||
response_model_exclude: Optional[Union[SetIntStr, DictIntStrAny]] = None,
|
||||
response_model_by_alias: bool = True,
|
||||
response_model_skip_defaults: Optional[bool] = None,
|
||||
response_model_exclude_unset: bool = False,
|
||||
response_model_exclude_defaults: bool = False,
|
||||
response_model_exclude_none: bool = False,
|
||||
|
|
@ -679,8 +636,6 @@ class FastAPI(Starlette):
|
|||
name: Optional[str] = None,
|
||||
callbacks: Optional[List[routing.APIRoute]] = None,
|
||||
) -> Callable:
|
||||
if response_model_skip_defaults is not None:
|
||||
warning_response_model_skip_defaults_deprecated() # pragma: nocover
|
||||
return self.router.patch(
|
||||
path,
|
||||
response_model=response_model,
|
||||
|
|
@ -696,9 +651,7 @@ class FastAPI(Starlette):
|
|||
response_model_include=response_model_include,
|
||||
response_model_exclude=response_model_exclude,
|
||||
response_model_by_alias=response_model_by_alias,
|
||||
response_model_exclude_unset=bool(
|
||||
response_model_exclude_unset or response_model_skip_defaults
|
||||
),
|
||||
response_model_exclude_unset=response_model_exclude_unset,
|
||||
response_model_exclude_defaults=response_model_exclude_defaults,
|
||||
response_model_exclude_none=response_model_exclude_none,
|
||||
include_in_schema=include_in_schema,
|
||||
|
|
@ -722,9 +675,8 @@ class FastAPI(Starlette):
|
|||
deprecated: Optional[bool] = None,
|
||||
operation_id: Optional[str] = None,
|
||||
response_model_include: Optional[Union[SetIntStr, DictIntStrAny]] = None,
|
||||
response_model_exclude: Union[SetIntStr, DictIntStrAny] = set(),
|
||||
response_model_exclude: Optional[Union[SetIntStr, DictIntStrAny]] = None,
|
||||
response_model_by_alias: bool = True,
|
||||
response_model_skip_defaults: Optional[bool] = None,
|
||||
response_model_exclude_unset: bool = False,
|
||||
response_model_exclude_defaults: bool = False,
|
||||
response_model_exclude_none: bool = False,
|
||||
|
|
@ -733,8 +685,6 @@ class FastAPI(Starlette):
|
|||
name: Optional[str] = None,
|
||||
callbacks: Optional[List[routing.APIRoute]] = None,
|
||||
) -> Callable:
|
||||
if response_model_skip_defaults is not None:
|
||||
warning_response_model_skip_defaults_deprecated() # pragma: nocover
|
||||
return self.router.trace(
|
||||
path,
|
||||
response_model=response_model,
|
||||
|
|
@ -750,9 +700,7 @@ class FastAPI(Starlette):
|
|||
response_model_include=response_model_include,
|
||||
response_model_exclude=response_model_exclude,
|
||||
response_model_by_alias=response_model_by_alias,
|
||||
response_model_exclude_unset=bool(
|
||||
response_model_exclude_unset or response_model_skip_defaults
|
||||
),
|
||||
response_model_exclude_unset=response_model_exclude_unset,
|
||||
response_model_exclude_defaults=response_model_exclude_defaults,
|
||||
response_model_exclude_none=response_model_exclude_none,
|
||||
include_in_schema=include_in_schema,
|
||||
|
|
|
|||
|
|
@ -1,12 +1,7 @@
|
|||
from typing import Callable, List, Optional, Sequence
|
||||
|
||||
from fastapi.security.base import SecurityBase
|
||||
|
||||
try:
|
||||
from pydantic.fields import ModelField
|
||||
except ImportError: # pragma: nocover
|
||||
# TODO: remove when removing support for Pydantic < 1.0.0
|
||||
from pydantic.fields import Field as ModelField # type: ignore
|
||||
from pydantic.fields import ModelField
|
||||
|
||||
param_supported_types = (str, int, float, bool)
|
||||
|
||||
|
|
|
|||
|
|
@ -28,15 +28,23 @@ from fastapi.logger import logger
|
|||
from fastapi.security.base import SecurityBase
|
||||
from fastapi.security.oauth2 import OAuth2, SecurityScopes
|
||||
from fastapi.security.open_id_connect_url import OpenIdConnect
|
||||
from fastapi.utils import (
|
||||
PYDANTIC_1,
|
||||
create_response_field,
|
||||
get_field_info,
|
||||
get_path_param_names,
|
||||
)
|
||||
from pydantic import BaseConfig, BaseModel, create_model
|
||||
from fastapi.utils import create_response_field, get_path_param_names
|
||||
from pydantic import BaseModel, create_model
|
||||
from pydantic.error_wrappers import ErrorWrapper
|
||||
from pydantic.errors import MissingError
|
||||
from pydantic.fields import (
|
||||
SHAPE_LIST,
|
||||
SHAPE_SEQUENCE,
|
||||
SHAPE_SET,
|
||||
SHAPE_SINGLETON,
|
||||
SHAPE_TUPLE,
|
||||
SHAPE_TUPLE_ELLIPSIS,
|
||||
FieldInfo,
|
||||
ModelField,
|
||||
Required,
|
||||
)
|
||||
from pydantic.schema import get_annotation_from_field_info
|
||||
from pydantic.typing import ForwardRef, evaluate_forwardref
|
||||
from pydantic.utils import lenient_issubclass
|
||||
from starlette.background import BackgroundTasks
|
||||
from starlette.concurrency import run_in_threadpool
|
||||
|
|
@ -45,41 +53,6 @@ from starlette.requests import HTTPConnection, Request
|
|||
from starlette.responses import Response
|
||||
from starlette.websockets import WebSocket
|
||||
|
||||
try:
|
||||
from pydantic.fields import (
|
||||
SHAPE_LIST,
|
||||
SHAPE_SEQUENCE,
|
||||
SHAPE_SET,
|
||||
SHAPE_SINGLETON,
|
||||
SHAPE_TUPLE,
|
||||
SHAPE_TUPLE_ELLIPSIS,
|
||||
FieldInfo,
|
||||
ModelField,
|
||||
Required,
|
||||
)
|
||||
from pydantic.schema import get_annotation_from_field_info
|
||||
from pydantic.typing import ForwardRef, evaluate_forwardref
|
||||
except ImportError: # pragma: nocover
|
||||
# TODO: remove when removing support for Pydantic < 1.0.0
|
||||
from pydantic import Schema as FieldInfo # type: ignore
|
||||
from pydantic.fields import Field as ModelField # type: ignore
|
||||
from pydantic.fields import Required, Shape # type: ignore
|
||||
from pydantic.schema import get_annotation_from_schema # type: ignore
|
||||
from pydantic.utils import ForwardRef, evaluate_forwardref # type: ignore
|
||||
|
||||
SHAPE_LIST = Shape.LIST
|
||||
SHAPE_SEQUENCE = Shape.SEQUENCE
|
||||
SHAPE_SET = Shape.SET
|
||||
SHAPE_SINGLETON = Shape.SINGLETON
|
||||
SHAPE_TUPLE = Shape.TUPLE
|
||||
SHAPE_TUPLE_ELLIPSIS = Shape.TUPLE_ELLIPS
|
||||
|
||||
def get_annotation_from_field_info(
|
||||
annotation: Any, field_info: FieldInfo, field_name: str
|
||||
) -> Type[Any]:
|
||||
return get_annotation_from_schema(annotation, field_info)
|
||||
|
||||
|
||||
sequence_shapes = {
|
||||
SHAPE_LIST,
|
||||
SHAPE_SET,
|
||||
|
|
@ -113,7 +86,7 @@ multipart_incorrect_install_error = (
|
|||
|
||||
|
||||
def check_file_field(field: ModelField) -> None:
|
||||
field_info = get_field_info(field)
|
||||
field_info = field.field_info
|
||||
if isinstance(field_info, params.Form):
|
||||
try:
|
||||
# __version__ is available in both multiparts, and can be mocked
|
||||
|
|
@ -239,7 +212,7 @@ def get_flat_params(dependant: Dependant) -> List[ModelField]:
|
|||
|
||||
|
||||
def is_scalar_field(field: ModelField) -> bool:
|
||||
field_info = get_field_info(field)
|
||||
field_info = field.field_info
|
||||
if not (
|
||||
field.shape == SHAPE_SINGLETON
|
||||
and not lenient_issubclass(field.type_, BaseModel)
|
||||
|
|
@ -354,7 +327,7 @@ def get_dependant(
|
|||
) and is_scalar_sequence_field(param_field):
|
||||
add_param_to_fields(field=param_field, dependant=dependant)
|
||||
else:
|
||||
field_info = get_field_info(param_field)
|
||||
field_info = param_field.field_info
|
||||
assert isinstance(
|
||||
field_info, params.Body
|
||||
), f"Param: {param_field.name} can only be a request body, using Body(...)"
|
||||
|
|
@ -430,16 +403,13 @@ def get_param_field(
|
|||
)
|
||||
field.required = required
|
||||
if not had_schema and not is_scalar_field(field=field):
|
||||
if PYDANTIC_1:
|
||||
field.field_info = params.Body(field_info.default)
|
||||
else:
|
||||
field.schema = params.Body(field_info.default) # type: ignore # pragma: nocover
|
||||
field.field_info = params.Body(field_info.default)
|
||||
|
||||
return field
|
||||
|
||||
|
||||
def add_param_to_fields(*, field: ModelField, dependant: Dependant) -> None:
|
||||
field_info = cast(params.Param, get_field_info(field))
|
||||
field_info = cast(params.Param, field.field_info)
|
||||
if field_info.in_ == params.ParamTypes.path:
|
||||
dependant.path_params.append(field)
|
||||
elif field_info.in_ == params.ParamTypes.query:
|
||||
|
|
@ -642,26 +612,17 @@ def request_params_to_args(
|
|||
value = received_params.getlist(field.alias) or field.default
|
||||
else:
|
||||
value = received_params.get(field.alias)
|
||||
field_info = get_field_info(field)
|
||||
field_info = field.field_info
|
||||
assert isinstance(
|
||||
field_info, params.Param
|
||||
), "Params must be subclasses of Param"
|
||||
if value is None:
|
||||
if field.required:
|
||||
if PYDANTIC_1:
|
||||
errors.append(
|
||||
ErrorWrapper(
|
||||
MissingError(), loc=(field_info.in_.value, field.alias)
|
||||
)
|
||||
)
|
||||
else: # pragma: nocover
|
||||
errors.append(
|
||||
ErrorWrapper( # type: ignore
|
||||
MissingError(),
|
||||
loc=(field_info.in_.value, field.alias),
|
||||
config=BaseConfig,
|
||||
)
|
||||
errors.append(
|
||||
ErrorWrapper(
|
||||
MissingError(), loc=(field_info.in_.value, field.alias)
|
||||
)
|
||||
)
|
||||
else:
|
||||
values[field.name] = deepcopy(field.default)
|
||||
continue
|
||||
|
|
@ -685,7 +646,7 @@ async def request_body_to_args(
|
|||
errors = []
|
||||
if required_params:
|
||||
field = required_params[0]
|
||||
field_info = get_field_info(field)
|
||||
field_info = field.field_info
|
||||
embed = getattr(field_info, "embed", None)
|
||||
field_alias_omitted = len(required_params) == 1 and not embed
|
||||
if field_alias_omitted:
|
||||
|
|
@ -752,12 +713,7 @@ async def request_body_to_args(
|
|||
|
||||
|
||||
def get_missing_field_error(loc: Tuple[str, ...]) -> ErrorWrapper:
|
||||
if PYDANTIC_1:
|
||||
missing_field_error = ErrorWrapper(MissingError(), loc=loc)
|
||||
else: # pragma: no cover
|
||||
missing_field_error = ErrorWrapper( # type: ignore
|
||||
MissingError(), loc=loc, config=BaseConfig,
|
||||
)
|
||||
missing_field_error = ErrorWrapper(MissingError(), loc=loc)
|
||||
return missing_field_error
|
||||
|
||||
|
||||
|
|
@ -775,7 +731,7 @@ def get_schema_compatible_field(*, field: ModelField) -> ModelField:
|
|||
default=field.default,
|
||||
required=field.required,
|
||||
alias=field.alias,
|
||||
field_info=get_field_info(field),
|
||||
field_info=field.field_info,
|
||||
)
|
||||
return out_field
|
||||
|
||||
|
|
@ -785,7 +741,7 @@ def get_body_field(*, dependant: Dependant, name: str) -> Optional[ModelField]:
|
|||
if not flat_dependant.body_params:
|
||||
return None
|
||||
first_param = flat_dependant.body_params[0]
|
||||
field_info = get_field_info(first_param)
|
||||
field_info = first_param.field_info
|
||||
embed = getattr(field_info, "embed", None)
|
||||
body_param_names_set = {param.name for param in flat_dependant.body_params}
|
||||
if len(body_param_names_set) == 1 and not embed:
|
||||
|
|
@ -796,7 +752,7 @@ def get_body_field(*, dependant: Dependant, name: str) -> Optional[ModelField]:
|
|||
# in case a sub-dependency is evaluated with a single unique body field
|
||||
# That is combined (embedded) with other body fields
|
||||
for param in flat_dependant.body_params:
|
||||
setattr(get_field_info(param), "embed", True)
|
||||
setattr(param.field_info, "embed", True)
|
||||
model_name = "Body_" + name
|
||||
BodyModel = create_model(model_name)
|
||||
for f in flat_dependant.body_params:
|
||||
|
|
@ -804,21 +760,17 @@ def get_body_field(*, dependant: Dependant, name: str) -> Optional[ModelField]:
|
|||
required = any(True for f in flat_dependant.body_params if f.required)
|
||||
|
||||
BodyFieldInfo_kwargs: Dict[str, Any] = dict(default=None)
|
||||
if any(
|
||||
isinstance(get_field_info(f), params.File) for f in flat_dependant.body_params
|
||||
):
|
||||
if any(isinstance(f.field_info, params.File) for f in flat_dependant.body_params):
|
||||
BodyFieldInfo: Type[params.Body] = params.File
|
||||
elif any(
|
||||
isinstance(get_field_info(f), params.Form) for f in flat_dependant.body_params
|
||||
):
|
||||
elif any(isinstance(f.field_info, params.Form) for f in flat_dependant.body_params):
|
||||
BodyFieldInfo = params.Form
|
||||
else:
|
||||
BodyFieldInfo = params.Body
|
||||
|
||||
body_param_media_types = [
|
||||
getattr(get_field_info(f), "media_type")
|
||||
getattr(f.field_info, "media_type")
|
||||
for f in flat_dependant.body_params
|
||||
if isinstance(get_field_info(f), params.Body)
|
||||
if isinstance(f.field_info, params.Body)
|
||||
]
|
||||
if len(set(body_param_media_types)) == 1:
|
||||
BodyFieldInfo_kwargs["media_type"] = body_param_media_types[0]
|
||||
|
|
|
|||
|
|
@ -4,8 +4,6 @@ from pathlib import PurePath
|
|||
from types import GeneratorType
|
||||
from typing import Any, Callable, Dict, List, Optional, Set, Tuple, Union
|
||||
|
||||
from fastapi.logger import logger
|
||||
from fastapi.utils import PYDANTIC_1
|
||||
from pydantic import BaseModel
|
||||
from pydantic.json import ENCODERS_BY_TYPE
|
||||
|
||||
|
|
@ -28,21 +26,14 @@ encoders_by_class_tuples = generate_encoders_by_class_tuples(ENCODERS_BY_TYPE)
|
|||
def jsonable_encoder(
|
||||
obj: Any,
|
||||
include: Optional[Union[SetIntStr, DictIntStrAny]] = None,
|
||||
exclude: Union[SetIntStr, DictIntStrAny] = set(),
|
||||
exclude: Optional[Union[SetIntStr, DictIntStrAny]] = None,
|
||||
by_alias: bool = True,
|
||||
skip_defaults: Optional[bool] = None,
|
||||
exclude_unset: bool = False,
|
||||
exclude_defaults: bool = False,
|
||||
exclude_none: bool = False,
|
||||
custom_encoder: dict = {},
|
||||
sqlalchemy_safe: bool = True,
|
||||
) -> Any:
|
||||
if skip_defaults is not None:
|
||||
logger.warning( # pragma: nocover
|
||||
"skip_defaults in jsonable_encoder has been deprecated in favor of "
|
||||
"exclude_unset to keep in line with Pydantic v1, support for it will be "
|
||||
"removed soon."
|
||||
)
|
||||
if include is not None and not isinstance(include, set):
|
||||
include = set(include)
|
||||
if exclude is not None and not isinstance(exclude, set):
|
||||
|
|
@ -51,24 +42,14 @@ def jsonable_encoder(
|
|||
encoder = getattr(obj.__config__, "json_encoders", {})
|
||||
if custom_encoder:
|
||||
encoder.update(custom_encoder)
|
||||
if PYDANTIC_1:
|
||||
obj_dict = obj.dict(
|
||||
include=include,
|
||||
exclude=exclude,
|
||||
by_alias=by_alias,
|
||||
exclude_unset=bool(exclude_unset or skip_defaults),
|
||||
exclude_none=exclude_none,
|
||||
exclude_defaults=exclude_defaults,
|
||||
)
|
||||
else: # pragma: nocover
|
||||
if exclude_defaults:
|
||||
raise ValueError("Cannot use exclude_defaults")
|
||||
obj_dict = obj.dict(
|
||||
include=include,
|
||||
exclude=exclude,
|
||||
by_alias=by_alias,
|
||||
skip_defaults=bool(exclude_unset or skip_defaults),
|
||||
)
|
||||
obj_dict = obj.dict(
|
||||
include=include,
|
||||
exclude=exclude,
|
||||
by_alias=by_alias,
|
||||
exclude_unset=exclude_unset,
|
||||
exclude_none=exclude_none,
|
||||
exclude_defaults=exclude_defaults,
|
||||
)
|
||||
if "__root__" in obj_dict:
|
||||
obj_dict = obj_dict["__root__"]
|
||||
return jsonable_encoder(
|
||||
|
|
@ -94,7 +75,7 @@ def jsonable_encoder(
|
|||
or (not key.startswith("_sa"))
|
||||
)
|
||||
and (value is not None or not exclude_none)
|
||||
and ((include and key in include) or key not in exclude)
|
||||
and ((include and key in include) or not exclude or key not in exclude)
|
||||
):
|
||||
encoded_key = jsonable_encoder(
|
||||
key,
|
||||
|
|
|
|||
|
|
@ -1,11 +1,8 @@
|
|||
from typing import Any, Dict, Optional, Sequence
|
||||
|
||||
from fastapi.utils import PYDANTIC_1
|
||||
from pydantic import ValidationError, create_model
|
||||
from pydantic.error_wrappers import ErrorList
|
||||
from starlette.exceptions import HTTPException as StarletteHTTPException
|
||||
from starlette.requests import Request
|
||||
from starlette.websockets import WebSocket
|
||||
|
||||
|
||||
class HTTPException(StarletteHTTPException):
|
||||
|
|
@ -32,15 +29,9 @@ class FastAPIError(RuntimeError):
|
|||
class RequestValidationError(ValidationError):
|
||||
def __init__(self, errors: Sequence[ErrorList], *, body: Any = None) -> None:
|
||||
self.body = body
|
||||
if PYDANTIC_1:
|
||||
super().__init__(errors, RequestErrorModel)
|
||||
else:
|
||||
super().__init__(errors, Request) # type: ignore # pragma: nocover
|
||||
super().__init__(errors, RequestErrorModel)
|
||||
|
||||
|
||||
class WebSocketRequestValidationError(ValidationError):
|
||||
def __init__(self, errors: Sequence[ErrorList]) -> None:
|
||||
if PYDANTIC_1:
|
||||
super().__init__(errors, WebSocketErrorModel)
|
||||
else:
|
||||
super().__init__(errors, WebSocket) # type: ignore # pragma: nocover
|
||||
super().__init__(errors, WebSocketErrorModel)
|
||||
|
|
|
|||
|
|
@ -2,24 +2,13 @@ from enum import Enum
|
|||
from typing import Any, Callable, Dict, Iterable, List, Optional, Union
|
||||
|
||||
from fastapi.logger import logger
|
||||
from pydantic import BaseModel
|
||||
|
||||
try:
|
||||
from pydantic import AnyUrl, Field
|
||||
except ImportError: # pragma: nocover
|
||||
# TODO: remove when removing support for Pydantic < 1.0.0
|
||||
from pydantic import Schema as Field # type: ignore
|
||||
from pydantic import UrlStr as AnyUrl # type: ignore
|
||||
from pydantic import AnyUrl, BaseModel, Field
|
||||
|
||||
try:
|
||||
import email_validator
|
||||
|
||||
assert email_validator # make autoflake ignore the unused import
|
||||
try:
|
||||
from pydantic import EmailStr
|
||||
except ImportError: # pragma: nocover
|
||||
# TODO: remove when removing support for Pydantic < 1.0.0
|
||||
from pydantic.types import EmailStr # type: ignore
|
||||
from pydantic import EmailStr
|
||||
except ImportError: # pragma: no cover
|
||||
|
||||
class EmailStr(str): # type: ignore
|
||||
|
|
|
|||
|
|
@ -16,10 +16,10 @@ from fastapi.params import Body, Param
|
|||
from fastapi.utils import (
|
||||
deep_dict_update,
|
||||
generate_operation_id_for_path,
|
||||
get_field_info,
|
||||
get_model_definitions,
|
||||
)
|
||||
from pydantic import BaseModel
|
||||
from pydantic.fields import ModelField
|
||||
from pydantic.schema import (
|
||||
field_schema,
|
||||
get_flat_models_from_fields,
|
||||
|
|
@ -30,12 +30,6 @@ from starlette.responses import JSONResponse
|
|||
from starlette.routing import BaseRoute
|
||||
from starlette.status import HTTP_422_UNPROCESSABLE_ENTITY
|
||||
|
||||
try:
|
||||
from pydantic.fields import ModelField
|
||||
except ImportError: # pragma: nocover
|
||||
# TODO: remove when removing support for Pydantic < 1.0.0
|
||||
from pydantic.fields import Field as ModelField # type: ignore
|
||||
|
||||
validation_error_definition = {
|
||||
"title": "ValidationError",
|
||||
"type": "object",
|
||||
|
|
@ -91,7 +85,7 @@ def get_openapi_operation_parameters(
|
|||
) -> List[Dict[str, Any]]:
|
||||
parameters = []
|
||||
for param in all_route_params:
|
||||
field_info = get_field_info(param)
|
||||
field_info = param.field_info
|
||||
field_info = cast(Param, field_info)
|
||||
# ignore mypy error until enum schemas are released
|
||||
parameter = {
|
||||
|
|
@ -122,7 +116,7 @@ def get_openapi_operation_request_body(
|
|||
body_schema, _, _ = field_schema(
|
||||
body_field, model_name_map=model_name_map, ref_prefix=REF_PREFIX # type: ignore
|
||||
)
|
||||
field_info = cast(Body, get_field_info(body_field))
|
||||
field_info = cast(Body, body_field.field_info)
|
||||
request_media_type = field_info.media_type
|
||||
required = body_field.required
|
||||
request_body_oai: Dict[str, Any] = {}
|
||||
|
|
|
|||
|
|
@ -1,11 +1,7 @@
|
|||
from enum import Enum
|
||||
from typing import Any, Callable, Optional, Sequence
|
||||
|
||||
try:
|
||||
from pydantic.fields import FieldInfo
|
||||
except ImportError: # pragma: nocover
|
||||
# TODO: remove when removing support for Pydantic < 1.0.0
|
||||
from pydantic import Schema as FieldInfo # type: ignore
|
||||
from pydantic.fields import FieldInfo
|
||||
|
||||
|
||||
class ParamTypes(Enum):
|
||||
|
|
|
|||
|
|
@ -1 +1,2 @@
|
|||
from starlette.requests import HTTPConnection, Request # noqa
|
||||
from starlette.requests import HTTPConnection as HTTPConnection # noqa: F401
|
||||
from starlette.requests import Request as Request # noqa: F401
|
||||
|
|
|
|||
|
|
@ -16,15 +16,13 @@ from fastapi.encoders import DictIntStrAny, SetIntStr, jsonable_encoder
|
|||
from fastapi.exceptions import RequestValidationError, WebSocketRequestValidationError
|
||||
from fastapi.openapi.constants import STATUS_CODES_WITH_NO_BODY
|
||||
from fastapi.utils import (
|
||||
PYDANTIC_1,
|
||||
create_cloned_field,
|
||||
create_response_field,
|
||||
generate_operation_id_for_path,
|
||||
get_field_info,
|
||||
warning_response_model_skip_defaults_deprecated,
|
||||
)
|
||||
from pydantic import BaseModel
|
||||
from pydantic.error_wrappers import ErrorWrapper, ValidationError
|
||||
from pydantic.fields import ModelField
|
||||
from starlette import routing
|
||||
from starlette.concurrency import run_in_threadpool
|
||||
from starlette.exceptions import HTTPException
|
||||
|
|
@ -41,12 +39,6 @@ from starlette.status import WS_1008_POLICY_VIOLATION
|
|||
from starlette.types import ASGIApp
|
||||
from starlette.websockets import WebSocket
|
||||
|
||||
try:
|
||||
from pydantic.fields import ModelField
|
||||
except ImportError: # pragma: nocover
|
||||
# TODO: remove when removing support for Pydantic < 1.0.0
|
||||
from pydantic.fields import Field as ModelField # type: ignore
|
||||
|
||||
|
||||
def _prepare_response_content(
|
||||
res: Any,
|
||||
|
|
@ -56,17 +48,12 @@ def _prepare_response_content(
|
|||
exclude_none: bool = False,
|
||||
) -> Any:
|
||||
if isinstance(res, BaseModel):
|
||||
if PYDANTIC_1:
|
||||
return res.dict(
|
||||
by_alias=True,
|
||||
exclude_unset=exclude_unset,
|
||||
exclude_defaults=exclude_defaults,
|
||||
exclude_none=exclude_none,
|
||||
)
|
||||
else:
|
||||
return res.dict(
|
||||
by_alias=True, skip_defaults=exclude_unset,
|
||||
) # pragma: nocover
|
||||
return res.dict(
|
||||
by_alias=True,
|
||||
exclude_unset=exclude_unset,
|
||||
exclude_defaults=exclude_defaults,
|
||||
exclude_none=exclude_none,
|
||||
)
|
||||
elif isinstance(res, list):
|
||||
return [
|
||||
_prepare_response_content(
|
||||
|
|
@ -95,7 +82,7 @@ async def serialize_response(
|
|||
field: Optional[ModelField] = None,
|
||||
response_content: Any,
|
||||
include: Optional[Union[SetIntStr, DictIntStrAny]] = None,
|
||||
exclude: Union[SetIntStr, DictIntStrAny] = set(),
|
||||
exclude: Optional[Union[SetIntStr, DictIntStrAny]] = None,
|
||||
by_alias: bool = True,
|
||||
exclude_unset: bool = False,
|
||||
exclude_defaults: bool = False,
|
||||
|
|
@ -155,7 +142,7 @@ def get_request_handler(
|
|||
response_class: Type[Response] = JSONResponse,
|
||||
response_field: Optional[ModelField] = None,
|
||||
response_model_include: Optional[Union[SetIntStr, DictIntStrAny]] = None,
|
||||
response_model_exclude: Union[SetIntStr, DictIntStrAny] = set(),
|
||||
response_model_exclude: Optional[Union[SetIntStr, DictIntStrAny]] = None,
|
||||
response_model_by_alias: bool = True,
|
||||
response_model_exclude_unset: bool = False,
|
||||
response_model_exclude_defaults: bool = False,
|
||||
|
|
@ -164,7 +151,7 @@ def get_request_handler(
|
|||
) -> Callable:
|
||||
assert dependant.call is not None, "dependant.call must be a function"
|
||||
is_coroutine = asyncio.iscoroutinefunction(dependant.call)
|
||||
is_body_form = body_field and isinstance(get_field_info(body_field), params.Form)
|
||||
is_body_form = body_field and isinstance(body_field.field_info, params.Form)
|
||||
|
||||
async def app(request: Request) -> Response:
|
||||
try:
|
||||
|
|
@ -284,7 +271,7 @@ class APIRoute(routing.Route):
|
|||
methods: Optional[Union[Set[str], List[str]]] = None,
|
||||
operation_id: Optional[str] = None,
|
||||
response_model_include: Optional[Union[SetIntStr, DictIntStrAny]] = None,
|
||||
response_model_exclude: Union[SetIntStr, DictIntStrAny] = set(),
|
||||
response_model_exclude: Optional[Union[SetIntStr, DictIntStrAny]] = None,
|
||||
response_model_by_alias: bool = True,
|
||||
response_model_exclude_unset: bool = False,
|
||||
response_model_exclude_defaults: bool = False,
|
||||
|
|
@ -437,9 +424,8 @@ class APIRouter(routing.Router):
|
|||
methods: Optional[Union[Set[str], List[str]]] = None,
|
||||
operation_id: Optional[str] = None,
|
||||
response_model_include: Optional[Union[SetIntStr, DictIntStrAny]] = None,
|
||||
response_model_exclude: Union[SetIntStr, DictIntStrAny] = set(),
|
||||
response_model_exclude: Optional[Union[SetIntStr, DictIntStrAny]] = None,
|
||||
response_model_by_alias: bool = True,
|
||||
response_model_skip_defaults: Optional[bool] = None,
|
||||
response_model_exclude_unset: bool = False,
|
||||
response_model_exclude_defaults: bool = False,
|
||||
response_model_exclude_none: bool = False,
|
||||
|
|
@ -449,8 +435,6 @@ class APIRouter(routing.Router):
|
|||
route_class_override: Optional[Type[APIRoute]] = None,
|
||||
callbacks: Optional[List[APIRoute]] = None,
|
||||
) -> None:
|
||||
if response_model_skip_defaults is not None:
|
||||
warning_response_model_skip_defaults_deprecated() # pragma: nocover
|
||||
route_class = route_class_override or self.route_class
|
||||
route = route_class(
|
||||
path,
|
||||
|
|
@ -469,9 +453,7 @@ class APIRouter(routing.Router):
|
|||
response_model_include=response_model_include,
|
||||
response_model_exclude=response_model_exclude,
|
||||
response_model_by_alias=response_model_by_alias,
|
||||
response_model_exclude_unset=bool(
|
||||
response_model_exclude_unset or response_model_skip_defaults
|
||||
),
|
||||
response_model_exclude_unset=response_model_exclude_unset,
|
||||
response_model_exclude_defaults=response_model_exclude_defaults,
|
||||
response_model_exclude_none=response_model_exclude_none,
|
||||
include_in_schema=include_in_schema,
|
||||
|
|
@ -498,9 +480,8 @@ class APIRouter(routing.Router):
|
|||
methods: Optional[List[str]] = None,
|
||||
operation_id: Optional[str] = None,
|
||||
response_model_include: Optional[Union[SetIntStr, DictIntStrAny]] = None,
|
||||
response_model_exclude: Union[SetIntStr, DictIntStrAny] = set(),
|
||||
response_model_exclude: Optional[Union[SetIntStr, DictIntStrAny]] = None,
|
||||
response_model_by_alias: bool = True,
|
||||
response_model_skip_defaults: Optional[bool] = None,
|
||||
response_model_exclude_unset: bool = False,
|
||||
response_model_exclude_defaults: bool = False,
|
||||
response_model_exclude_none: bool = False,
|
||||
|
|
@ -509,9 +490,6 @@ class APIRouter(routing.Router):
|
|||
name: Optional[str] = None,
|
||||
callbacks: Optional[List[APIRoute]] = None,
|
||||
) -> Callable:
|
||||
if response_model_skip_defaults is not None:
|
||||
warning_response_model_skip_defaults_deprecated() # pragma: nocover
|
||||
|
||||
def decorator(func: Callable) -> Callable:
|
||||
self.add_api_route(
|
||||
path,
|
||||
|
|
@ -530,9 +508,7 @@ class APIRouter(routing.Router):
|
|||
response_model_include=response_model_include,
|
||||
response_model_exclude=response_model_exclude,
|
||||
response_model_by_alias=response_model_by_alias,
|
||||
response_model_exclude_unset=bool(
|
||||
response_model_exclude_unset or response_model_skip_defaults
|
||||
),
|
||||
response_model_exclude_unset=response_model_exclude_unset,
|
||||
response_model_exclude_defaults=response_model_exclude_defaults,
|
||||
response_model_exclude_none=response_model_exclude_none,
|
||||
include_in_schema=include_in_schema,
|
||||
|
|
@ -653,9 +629,8 @@ class APIRouter(routing.Router):
|
|||
deprecated: Optional[bool] = None,
|
||||
operation_id: Optional[str] = None,
|
||||
response_model_include: Optional[Union[SetIntStr, DictIntStrAny]] = None,
|
||||
response_model_exclude: Union[SetIntStr, DictIntStrAny] = set(),
|
||||
response_model_exclude: Optional[Union[SetIntStr, DictIntStrAny]] = None,
|
||||
response_model_by_alias: bool = True,
|
||||
response_model_skip_defaults: Optional[bool] = None,
|
||||
response_model_exclude_unset: bool = False,
|
||||
response_model_exclude_defaults: bool = False,
|
||||
response_model_exclude_none: bool = False,
|
||||
|
|
@ -664,8 +639,6 @@ class APIRouter(routing.Router):
|
|||
name: Optional[str] = None,
|
||||
callbacks: Optional[List[APIRoute]] = None,
|
||||
) -> Callable:
|
||||
if response_model_skip_defaults is not None:
|
||||
warning_response_model_skip_defaults_deprecated() # pragma: nocover
|
||||
return self.api_route(
|
||||
path=path,
|
||||
response_model=response_model,
|
||||
|
|
@ -682,9 +655,7 @@ class APIRouter(routing.Router):
|
|||
response_model_include=response_model_include,
|
||||
response_model_exclude=response_model_exclude,
|
||||
response_model_by_alias=response_model_by_alias,
|
||||
response_model_exclude_unset=bool(
|
||||
response_model_exclude_unset or response_model_skip_defaults
|
||||
),
|
||||
response_model_exclude_unset=response_model_exclude_unset,
|
||||
response_model_exclude_defaults=response_model_exclude_defaults,
|
||||
response_model_exclude_none=response_model_exclude_none,
|
||||
include_in_schema=include_in_schema,
|
||||
|
|
@ -708,9 +679,8 @@ class APIRouter(routing.Router):
|
|||
deprecated: Optional[bool] = None,
|
||||
operation_id: Optional[str] = None,
|
||||
response_model_include: Optional[Union[SetIntStr, DictIntStrAny]] = None,
|
||||
response_model_exclude: Union[SetIntStr, DictIntStrAny] = set(),
|
||||
response_model_exclude: Optional[Union[SetIntStr, DictIntStrAny]] = None,
|
||||
response_model_by_alias: bool = True,
|
||||
response_model_skip_defaults: Optional[bool] = None,
|
||||
response_model_exclude_unset: bool = False,
|
||||
response_model_exclude_defaults: bool = False,
|
||||
response_model_exclude_none: bool = False,
|
||||
|
|
@ -719,8 +689,6 @@ class APIRouter(routing.Router):
|
|||
name: Optional[str] = None,
|
||||
callbacks: Optional[List[APIRoute]] = None,
|
||||
) -> Callable:
|
||||
if response_model_skip_defaults is not None:
|
||||
warning_response_model_skip_defaults_deprecated() # pragma: nocover
|
||||
return self.api_route(
|
||||
path=path,
|
||||
response_model=response_model,
|
||||
|
|
@ -737,9 +705,7 @@ class APIRouter(routing.Router):
|
|||
response_model_include=response_model_include,
|
||||
response_model_exclude=response_model_exclude,
|
||||
response_model_by_alias=response_model_by_alias,
|
||||
response_model_exclude_unset=bool(
|
||||
response_model_exclude_unset or response_model_skip_defaults
|
||||
),
|
||||
response_model_exclude_unset=response_model_exclude_unset,
|
||||
response_model_exclude_defaults=response_model_exclude_defaults,
|
||||
response_model_exclude_none=response_model_exclude_none,
|
||||
include_in_schema=include_in_schema,
|
||||
|
|
@ -763,9 +729,8 @@ class APIRouter(routing.Router):
|
|||
deprecated: Optional[bool] = None,
|
||||
operation_id: Optional[str] = None,
|
||||
response_model_include: Optional[Union[SetIntStr, DictIntStrAny]] = None,
|
||||
response_model_exclude: Union[SetIntStr, DictIntStrAny] = set(),
|
||||
response_model_exclude: Optional[Union[SetIntStr, DictIntStrAny]] = None,
|
||||
response_model_by_alias: bool = True,
|
||||
response_model_skip_defaults: Optional[bool] = None,
|
||||
response_model_exclude_unset: bool = False,
|
||||
response_model_exclude_defaults: bool = False,
|
||||
response_model_exclude_none: bool = False,
|
||||
|
|
@ -774,8 +739,6 @@ class APIRouter(routing.Router):
|
|||
name: Optional[str] = None,
|
||||
callbacks: Optional[List[APIRoute]] = None,
|
||||
) -> Callable:
|
||||
if response_model_skip_defaults is not None:
|
||||
warning_response_model_skip_defaults_deprecated() # pragma: nocover
|
||||
return self.api_route(
|
||||
path=path,
|
||||
response_model=response_model,
|
||||
|
|
@ -792,9 +755,7 @@ class APIRouter(routing.Router):
|
|||
response_model_include=response_model_include,
|
||||
response_model_exclude=response_model_exclude,
|
||||
response_model_by_alias=response_model_by_alias,
|
||||
response_model_exclude_unset=bool(
|
||||
response_model_exclude_unset or response_model_skip_defaults
|
||||
),
|
||||
response_model_exclude_unset=response_model_exclude_unset,
|
||||
response_model_exclude_defaults=response_model_exclude_defaults,
|
||||
response_model_exclude_none=response_model_exclude_none,
|
||||
include_in_schema=include_in_schema,
|
||||
|
|
@ -818,9 +779,8 @@ class APIRouter(routing.Router):
|
|||
deprecated: Optional[bool] = None,
|
||||
operation_id: Optional[str] = None,
|
||||
response_model_include: Optional[Union[SetIntStr, DictIntStrAny]] = None,
|
||||
response_model_exclude: Union[SetIntStr, DictIntStrAny] = set(),
|
||||
response_model_exclude: Optional[Union[SetIntStr, DictIntStrAny]] = None,
|
||||
response_model_by_alias: bool = True,
|
||||
response_model_skip_defaults: Optional[bool] = None,
|
||||
response_model_exclude_unset: bool = False,
|
||||
response_model_exclude_defaults: bool = False,
|
||||
response_model_exclude_none: bool = False,
|
||||
|
|
@ -829,8 +789,6 @@ class APIRouter(routing.Router):
|
|||
name: Optional[str] = None,
|
||||
callbacks: Optional[List[APIRoute]] = None,
|
||||
) -> Callable:
|
||||
if response_model_skip_defaults is not None:
|
||||
warning_response_model_skip_defaults_deprecated() # pragma: nocover
|
||||
return self.api_route(
|
||||
path=path,
|
||||
response_model=response_model,
|
||||
|
|
@ -847,9 +805,7 @@ class APIRouter(routing.Router):
|
|||
response_model_include=response_model_include,
|
||||
response_model_exclude=response_model_exclude,
|
||||
response_model_by_alias=response_model_by_alias,
|
||||
response_model_exclude_unset=bool(
|
||||
response_model_exclude_unset or response_model_skip_defaults
|
||||
),
|
||||
response_model_exclude_unset=response_model_exclude_unset,
|
||||
response_model_exclude_defaults=response_model_exclude_defaults,
|
||||
response_model_exclude_none=response_model_exclude_none,
|
||||
include_in_schema=include_in_schema,
|
||||
|
|
@ -873,9 +829,8 @@ class APIRouter(routing.Router):
|
|||
deprecated: Optional[bool] = None,
|
||||
operation_id: Optional[str] = None,
|
||||
response_model_include: Optional[Union[SetIntStr, DictIntStrAny]] = None,
|
||||
response_model_exclude: Union[SetIntStr, DictIntStrAny] = set(),
|
||||
response_model_exclude: Optional[Union[SetIntStr, DictIntStrAny]] = None,
|
||||
response_model_by_alias: bool = True,
|
||||
response_model_skip_defaults: Optional[bool] = None,
|
||||
response_model_exclude_unset: bool = False,
|
||||
response_model_exclude_defaults: bool = False,
|
||||
response_model_exclude_none: bool = False,
|
||||
|
|
@ -884,8 +839,6 @@ class APIRouter(routing.Router):
|
|||
name: Optional[str] = None,
|
||||
callbacks: Optional[List[APIRoute]] = None,
|
||||
) -> Callable:
|
||||
if response_model_skip_defaults is not None:
|
||||
warning_response_model_skip_defaults_deprecated() # pragma: nocover
|
||||
return self.api_route(
|
||||
path=path,
|
||||
response_model=response_model,
|
||||
|
|
@ -902,9 +855,7 @@ class APIRouter(routing.Router):
|
|||
response_model_include=response_model_include,
|
||||
response_model_exclude=response_model_exclude,
|
||||
response_model_by_alias=response_model_by_alias,
|
||||
response_model_exclude_unset=bool(
|
||||
response_model_exclude_unset or response_model_skip_defaults
|
||||
),
|
||||
response_model_exclude_unset=response_model_exclude_unset,
|
||||
response_model_exclude_defaults=response_model_exclude_defaults,
|
||||
response_model_exclude_none=response_model_exclude_none,
|
||||
include_in_schema=include_in_schema,
|
||||
|
|
@ -928,9 +879,8 @@ class APIRouter(routing.Router):
|
|||
deprecated: Optional[bool] = None,
|
||||
operation_id: Optional[str] = None,
|
||||
response_model_include: Optional[Union[SetIntStr, DictIntStrAny]] = None,
|
||||
response_model_exclude: Union[SetIntStr, DictIntStrAny] = set(),
|
||||
response_model_exclude: Optional[Union[SetIntStr, DictIntStrAny]] = None,
|
||||
response_model_by_alias: bool = True,
|
||||
response_model_skip_defaults: Optional[bool] = None,
|
||||
response_model_exclude_unset: bool = False,
|
||||
response_model_exclude_defaults: bool = False,
|
||||
response_model_exclude_none: bool = False,
|
||||
|
|
@ -939,8 +889,6 @@ class APIRouter(routing.Router):
|
|||
name: Optional[str] = None,
|
||||
callbacks: Optional[List[APIRoute]] = None,
|
||||
) -> Callable:
|
||||
if response_model_skip_defaults is not None:
|
||||
warning_response_model_skip_defaults_deprecated() # pragma: nocover
|
||||
return self.api_route(
|
||||
path=path,
|
||||
response_model=response_model,
|
||||
|
|
@ -957,9 +905,7 @@ class APIRouter(routing.Router):
|
|||
response_model_include=response_model_include,
|
||||
response_model_exclude=response_model_exclude,
|
||||
response_model_by_alias=response_model_by_alias,
|
||||
response_model_exclude_unset=bool(
|
||||
response_model_exclude_unset or response_model_skip_defaults
|
||||
),
|
||||
response_model_exclude_unset=response_model_exclude_unset,
|
||||
response_model_exclude_defaults=response_model_exclude_defaults,
|
||||
response_model_exclude_none=response_model_exclude_none,
|
||||
include_in_schema=include_in_schema,
|
||||
|
|
@ -983,9 +929,8 @@ class APIRouter(routing.Router):
|
|||
deprecated: Optional[bool] = None,
|
||||
operation_id: Optional[str] = None,
|
||||
response_model_include: Optional[Union[SetIntStr, DictIntStrAny]] = None,
|
||||
response_model_exclude: Union[SetIntStr, DictIntStrAny] = set(),
|
||||
response_model_exclude: Optional[Union[SetIntStr, DictIntStrAny]] = None,
|
||||
response_model_by_alias: bool = True,
|
||||
response_model_skip_defaults: Optional[bool] = None,
|
||||
response_model_exclude_unset: bool = False,
|
||||
response_model_exclude_defaults: bool = False,
|
||||
response_model_exclude_none: bool = False,
|
||||
|
|
@ -994,8 +939,6 @@ class APIRouter(routing.Router):
|
|||
name: Optional[str] = None,
|
||||
callbacks: Optional[List[APIRoute]] = None,
|
||||
) -> Callable:
|
||||
if response_model_skip_defaults is not None:
|
||||
warning_response_model_skip_defaults_deprecated() # pragma: nocover
|
||||
return self.api_route(
|
||||
path=path,
|
||||
response_model=response_model,
|
||||
|
|
@ -1012,9 +955,7 @@ class APIRouter(routing.Router):
|
|||
response_model_include=response_model_include,
|
||||
response_model_exclude=response_model_exclude,
|
||||
response_model_by_alias=response_model_by_alias,
|
||||
response_model_exclude_unset=bool(
|
||||
response_model_exclude_unset or response_model_skip_defaults
|
||||
),
|
||||
response_model_exclude_unset=response_model_exclude_unset,
|
||||
response_model_exclude_defaults=response_model_exclude_defaults,
|
||||
response_model_exclude_none=response_model_exclude_none,
|
||||
include_in_schema=include_in_schema,
|
||||
|
|
@ -1038,9 +979,8 @@ class APIRouter(routing.Router):
|
|||
deprecated: Optional[bool] = None,
|
||||
operation_id: Optional[str] = None,
|
||||
response_model_include: Optional[Union[SetIntStr, DictIntStrAny]] = None,
|
||||
response_model_exclude: Union[SetIntStr, DictIntStrAny] = set(),
|
||||
response_model_exclude: Optional[Union[SetIntStr, DictIntStrAny]] = None,
|
||||
response_model_by_alias: bool = True,
|
||||
response_model_skip_defaults: Optional[bool] = None,
|
||||
response_model_exclude_unset: bool = False,
|
||||
response_model_exclude_defaults: bool = False,
|
||||
response_model_exclude_none: bool = False,
|
||||
|
|
@ -1049,8 +989,7 @@ class APIRouter(routing.Router):
|
|||
name: Optional[str] = None,
|
||||
callbacks: Optional[List[APIRoute]] = None,
|
||||
) -> Callable:
|
||||
if response_model_skip_defaults is not None:
|
||||
warning_response_model_skip_defaults_deprecated() # pragma: nocover
|
||||
|
||||
return self.api_route(
|
||||
path=path,
|
||||
response_model=response_model,
|
||||
|
|
@ -1067,9 +1006,7 @@ class APIRouter(routing.Router):
|
|||
response_model_include=response_model_include,
|
||||
response_model_exclude=response_model_exclude,
|
||||
response_model_by_alias=response_model_by_alias,
|
||||
response_model_exclude_unset=bool(
|
||||
response_model_exclude_unset or response_model_skip_defaults
|
||||
),
|
||||
response_model_exclude_unset=response_model_exclude_unset,
|
||||
response_model_exclude_defaults=response_model_exclude_defaults,
|
||||
response_model_exclude_none=response_model_exclude_none,
|
||||
include_in_schema=include_in_schema,
|
||||
|
|
|
|||
|
|
@ -5,49 +5,13 @@ from enum import Enum
|
|||
from typing import Any, Dict, Optional, Set, Type, Union, cast
|
||||
|
||||
import fastapi
|
||||
from fastapi.logger import logger
|
||||
from fastapi.openapi.constants import REF_PREFIX
|
||||
from pydantic import BaseConfig, BaseModel, create_model
|
||||
from pydantic.class_validators import Validator
|
||||
from pydantic.fields import FieldInfo, ModelField, UndefinedType
|
||||
from pydantic.schema import model_process_schema
|
||||
from pydantic.utils import lenient_issubclass
|
||||
|
||||
try:
|
||||
from pydantic.fields import FieldInfo, ModelField, UndefinedType
|
||||
|
||||
PYDANTIC_1 = True
|
||||
except ImportError: # pragma: nocover
|
||||
# TODO: remove when removing support for Pydantic < 1.0.0
|
||||
from pydantic import Schema as FieldInfo # type: ignore
|
||||
from pydantic.fields import Field as ModelField # type: ignore
|
||||
|
||||
class UndefinedType: # type: ignore
|
||||
def __repr__(self) -> str:
|
||||
return "PydanticUndefined"
|
||||
|
||||
logger.warning(
|
||||
"Pydantic versions < 1.0.0 are deprecated in FastAPI and support will be "
|
||||
"removed soon."
|
||||
)
|
||||
PYDANTIC_1 = False
|
||||
|
||||
|
||||
# TODO: remove when removing support for Pydantic < 1.0.0
|
||||
def get_field_info(field: ModelField) -> FieldInfo:
|
||||
if PYDANTIC_1:
|
||||
return field.field_info # type: ignore
|
||||
else:
|
||||
return field.schema # type: ignore # pragma: nocover
|
||||
|
||||
|
||||
# TODO: remove when removing support for Pydantic < 1.0.0
|
||||
def warning_response_model_skip_defaults_deprecated() -> None:
|
||||
logger.warning( # pragma: nocover
|
||||
"response_model_skip_defaults has been deprecated in favor of "
|
||||
"response_model_exclude_unset to keep in line with Pydantic v1, support for "
|
||||
"it will be removed soon."
|
||||
)
|
||||
|
||||
|
||||
def get_model_definitions(
|
||||
*,
|
||||
|
|
@ -98,10 +62,7 @@ def create_response_field(
|
|||
)
|
||||
|
||||
try:
|
||||
if PYDANTIC_1:
|
||||
return response_field(field_info=field_info)
|
||||
else: # pragma: nocover
|
||||
return response_field(schema=field_info)
|
||||
return response_field(field_info=field_info)
|
||||
except RuntimeError:
|
||||
raise fastapi.exceptions.FastAPIError(
|
||||
f"Invalid args for response field! Hint: check that {type_} is a valid pydantic field type"
|
||||
|
|
@ -137,10 +98,7 @@ def create_cloned_field(
|
|||
new_field.default = field.default
|
||||
new_field.required = field.required
|
||||
new_field.model_config = field.model_config
|
||||
if PYDANTIC_1:
|
||||
new_field.field_info = field.field_info
|
||||
else: # pragma: nocover
|
||||
new_field.schema = field.schema # type: ignore
|
||||
new_field.field_info = field.field_info
|
||||
new_field.allow_none = field.allow_none
|
||||
new_field.validate_always = field.validate_always
|
||||
if field.sub_fields:
|
||||
|
|
@ -153,19 +111,11 @@ def create_cloned_field(
|
|||
field.key_field, cloned_types=cloned_types
|
||||
)
|
||||
new_field.validators = field.validators
|
||||
if PYDANTIC_1:
|
||||
new_field.pre_validators = field.pre_validators
|
||||
new_field.post_validators = field.post_validators
|
||||
else: # pragma: nocover
|
||||
new_field.whole_pre_validators = field.whole_pre_validators # type: ignore
|
||||
new_field.whole_post_validators = field.whole_post_validators # type: ignore
|
||||
new_field.pre_validators = field.pre_validators
|
||||
new_field.post_validators = field.post_validators
|
||||
new_field.parse_json = field.parse_json
|
||||
new_field.shape = field.shape
|
||||
try:
|
||||
new_field.populate_validators()
|
||||
except AttributeError: # pragma: nocover
|
||||
# TODO: remove when removing support for Pydantic < 1.0.0
|
||||
new_field._populate_validators() # type: ignore
|
||||
new_field.populate_validators()
|
||||
return new_field
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -33,7 +33,7 @@ classifiers = [
|
|||
]
|
||||
requires = [
|
||||
"starlette ==0.13.6",
|
||||
"pydantic >=0.32.2,<2.0.0"
|
||||
"pydantic >=1.0.0,<2.0.0"
|
||||
]
|
||||
description-file = "README.md"
|
||||
requires-python = ">=3.6"
|
||||
|
|
|
|||
|
|
@ -5,13 +5,7 @@ from typing import Optional
|
|||
|
||||
import pytest
|
||||
from fastapi.encoders import jsonable_encoder
|
||||
from pydantic import BaseModel, ValidationError, create_model
|
||||
|
||||
try:
|
||||
from pydantic import Field
|
||||
except ImportError: # pragma: nocover
|
||||
# TODO: remove when removing support for Pydantic < 1.0.0
|
||||
from pydantic import Schema as Field
|
||||
from pydantic import BaseModel, Field, ValidationError, create_model
|
||||
|
||||
|
||||
class Person:
|
||||
|
|
|
|||
|
|
@ -3,15 +3,6 @@ from fastapi.testclient import TestClient
|
|||
|
||||
from docs_src.body_fields.tutorial001 import app
|
||||
|
||||
# TODO: remove when removing support for Pydantic < 1.0.0
|
||||
try:
|
||||
from pydantic import Field # noqa
|
||||
except ImportError: # pragma: nocover
|
||||
import pydantic
|
||||
|
||||
pydantic.Field = pydantic.Schema
|
||||
|
||||
|
||||
client = TestClient(app)
|
||||
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue