From e1758d107ea5eede358cfdbf69ae7829c8e65a92 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebasti=C3=A1n=20Ram=C3=ADrez?= Date: Sun, 9 Aug 2020 22:17:08 +0200 Subject: [PATCH] =?UTF-8?q?=E2=AC=86=20Require=20Pydantic=20>=201.0=20(#18?= =?UTF-8?q?62)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * 🔥 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 --- fastapi/applications.py | 92 +++---------- fastapi/dependencies/models.py | 7 +- fastapi/dependencies/utils.py | 116 +++++----------- fastapi/encoders.py | 39 ++---- fastapi/exceptions.py | 13 +- fastapi/openapi/models.py | 15 +-- fastapi/openapi/utils.py | 12 +- fastapi/params.py | 6 +- fastapi/requests.py | 3 +- fastapi/routing.py | 127 +++++------------- fastapi/utils.py | 62 +-------- pyproject.toml | 2 +- tests/test_jsonable_encoder.py | 8 +- .../test_body_fields/test_tutorial001.py | 9 -- 14 files changed, 115 insertions(+), 396 deletions(-) diff --git a/fastapi/applications.py b/fastapi/applications.py index 27a72fe31..f3ce08e9f 100644 --- a/fastapi/applications.py +++ b/fastapi/applications.py @@ -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, diff --git a/fastapi/dependencies/models.py b/fastapi/dependencies/models.py index 8e0c7830a..5aff1de3b 100644 --- a/fastapi/dependencies/models.py +++ b/fastapi/dependencies/models.py @@ -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) diff --git a/fastapi/dependencies/utils.py b/fastapi/dependencies/utils.py index 6c49941ad..7c9f7e847 100644 --- a/fastapi/dependencies/utils.py +++ b/fastapi/dependencies/utils.py @@ -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] diff --git a/fastapi/encoders.py b/fastapi/encoders.py index d0d094b6a..1255b7497 100644 --- a/fastapi/encoders.py +++ b/fastapi/encoders.py @@ -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, diff --git a/fastapi/exceptions.py b/fastapi/exceptions.py index d7da2030f..8d92311d4 100644 --- a/fastapi/exceptions.py +++ b/fastapi/exceptions.py @@ -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) diff --git a/fastapi/openapi/models.py b/fastapi/openapi/models.py index 809286327..3b716766d 100644 --- a/fastapi/openapi/models.py +++ b/fastapi/openapi/models.py @@ -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 diff --git a/fastapi/openapi/utils.py b/fastapi/openapi/utils.py index c5e38e993..cd1b1baad 100644 --- a/fastapi/openapi/utils.py +++ b/fastapi/openapi/utils.py @@ -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] = {} diff --git a/fastapi/params.py b/fastapi/params.py index aaa64ceba..f53e2dba9 100644 --- a/fastapi/params.py +++ b/fastapi/params.py @@ -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): diff --git a/fastapi/requests.py b/fastapi/requests.py index 06d8f01cc..d16552c0a 100644 --- a/fastapi/requests.py +++ b/fastapi/requests.py @@ -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 diff --git a/fastapi/routing.py b/fastapi/routing.py index 0128b33ce..e455f81c9 100644 --- a/fastapi/routing.py +++ b/fastapi/routing.py @@ -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, diff --git a/fastapi/utils.py b/fastapi/utils.py index 08b3e75f5..d5ace9240 100644 --- a/fastapi/utils.py +++ b/fastapi/utils.py @@ -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 diff --git a/pyproject.toml b/pyproject.toml index 2eb8c9842..516b3e855 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -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" diff --git a/tests/test_jsonable_encoder.py b/tests/test_jsonable_encoder.py index 3205c4cbf..87b2466e8 100644 --- a/tests/test_jsonable_encoder.py +++ b/tests/test_jsonable_encoder.py @@ -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: diff --git a/tests/test_tutorial/test_body_fields/test_tutorial001.py b/tests/test_tutorial/test_body_fields/test_tutorial001.py index 458bd70d5..9de4907c2 100644 --- a/tests/test_tutorial/test_body_fields/test_tutorial001.py +++ b/tests/test_tutorial/test_body_fields/test_tutorial001.py @@ -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)