Browse Source

Update now that pydantic has been released

pull/11634/head
Alex Couper 12 months ago
parent
commit
25716e308d
  1. 64
      fastapi/_compat.py
  2. 37
      fastapi/applications.py
  3. 52
      fastapi/routing.py
  4. 9
      tests/test_serialize_response_model.py

64
fastapi/_compat.py

@ -18,13 +18,14 @@ from typing import (
Union, Union,
) )
from fastapi.exceptions import RequestErrorModel
from fastapi.types import IncEx, ModelNameMap, UnionType
from pydantic import BaseModel, create_model from pydantic import BaseModel, create_model
from pydantic.version import VERSION as P_VERSION from pydantic.version import VERSION as P_VERSION
from starlette.datastructures import UploadFile from starlette.datastructures import UploadFile
from typing_extensions import Annotated, Literal, get_args, get_origin from typing_extensions import Annotated, Literal, get_args, get_origin
from fastapi.exceptions import RequestErrorModel
from fastapi.types import IncEx, ModelNameMap, UnionType
# Reassign variable to make it reexported for mypy # Reassign variable to make it reexported for mypy
PYDANTIC_VERSION = P_VERSION PYDANTIC_VERSION = P_VERSION
PYDANTIC_V2 = PYDANTIC_VERSION.startswith("2.") PYDANTIC_V2 = PYDANTIC_VERSION.startswith("2.")
@ -50,8 +51,8 @@ if PYDANTIC_V2:
from pydantic import PydanticSchemaGenerationError as PydanticSchemaGenerationError from pydantic import PydanticSchemaGenerationError as PydanticSchemaGenerationError
from pydantic import TypeAdapter from pydantic import TypeAdapter
from pydantic import ValidationError as ValidationError from pydantic import ValidationError as ValidationError
from pydantic._internal._schema_generation_shared import ( # type: ignore[attr-defined] from pydantic._internal._schema_generation_shared import (
GetJsonSchemaHandler as GetJsonSchemaHandler, GetJsonSchemaHandler as GetJsonSchemaHandler, # type: ignore[attr-defined]
) )
from pydantic._internal._typing_extra import eval_type_lenient from pydantic._internal._typing_extra import eval_type_lenient
from pydantic._internal._utils import lenient_issubclass as lenient_issubclass from pydantic._internal._utils import lenient_issubclass as lenient_issubclass
@ -67,8 +68,8 @@ if PYDANTIC_V2:
with_info_plain_validator_function as with_info_plain_validator_function, with_info_plain_validator_function as with_info_plain_validator_function,
) )
except ImportError: # pragma: no cover except ImportError: # pragma: no cover
from pydantic_core.core_schema import ( from pydantic_core.core_schema import ( # noqa: F401
general_plain_validator_function as with_info_plain_validator_function, # noqa: F401 general_plain_validator_function as with_info_plain_validator_function,
) )
Required = PydanticUndefined Required = PydanticUndefined
@ -149,8 +150,8 @@ if PYDANTIC_V2:
# What calls this code passes a value that already called # What calls this code passes a value that already called
# self._type_adapter.validate_python(value) # self._type_adapter.validate_python(value)
# #
# context argument was introduced in pydantic 2.7.3 # context argument was introduced in pydantic 2.8
kwargs = {"context": context} if PYDANTIC_VERSION >= "2.7.3" else {} kwargs = {"context": context} if PYDANTIC_VERSION >= "2.8" else {}
return self._type_adapter.dump_python( return self._type_adapter.dump_python(
value, value,
@ -287,17 +288,16 @@ if PYDANTIC_V2:
return BodyModel return BodyModel
else: else:
from fastapi.openapi.constants import REF_PREFIX as REF_PREFIX
from pydantic import AnyUrl as Url # noqa: F401 from pydantic import AnyUrl as Url # noqa: F401
from pydantic import ( # type: ignore[assignment] from pydantic import ( # type: ignore[assignment]; noqa: F401
BaseConfig as BaseConfig, # noqa: F401 BaseConfig as BaseConfig,
) )
from pydantic import ValidationError as ValidationError # noqa: F401 from pydantic import ValidationError as ValidationError # noqa: F401
from pydantic.class_validators import ( # type: ignore[no-redef] from pydantic.class_validators import ( # type: ignore[no-redef]; noqa: F401
Validator as Validator, # noqa: F401 Validator as Validator,
) )
from pydantic.error_wrappers import ( # type: ignore[no-redef] from pydantic.error_wrappers import ( # type: ignore[no-redef]; noqa: F401
ErrorWrapper as ErrorWrapper, # noqa: F401 ErrorWrapper as ErrorWrapper,
) )
from pydantic.errors import MissingError from pydantic.errors import MissingError
from pydantic.fields import ( # type: ignore[attr-defined] from pydantic.fields import ( # type: ignore[attr-defined]
@ -310,34 +310,36 @@ else:
SHAPE_TUPLE_ELLIPSIS, SHAPE_TUPLE_ELLIPSIS,
) )
from pydantic.fields import FieldInfo as FieldInfo from pydantic.fields import FieldInfo as FieldInfo
from pydantic.fields import ( # type: ignore[no-redef,attr-defined] from pydantic.fields import ( # type: ignore[no-redef,attr-defined]; noqa: F401
ModelField as ModelField, # noqa: F401 ModelField as ModelField,
) )
from pydantic.fields import ( # type: ignore[no-redef,attr-defined] from pydantic.fields import ( # type: ignore[no-redef,attr-defined]; noqa: F401
Required as Required, # noqa: F401 Required as Required,
) )
from pydantic.fields import ( # type: ignore[no-redef,attr-defined] from pydantic.fields import (
Undefined as Undefined, Undefined as Undefined, # type: ignore[no-redef,attr-defined]
) )
from pydantic.fields import ( # type: ignore[no-redef, attr-defined] from pydantic.fields import ( # type: ignore[no-redef, attr-defined]; noqa: F401
UndefinedType as UndefinedType, # noqa: F401 UndefinedType as UndefinedType,
)
from pydantic.schema import field_schema
from pydantic.schema import ( # type: ignore[no-redef] # noqa: F401
get_annotation_from_field_info as get_annotation_from_field_info,
) )
from pydantic.schema import ( from pydantic.schema import (
field_schema,
get_flat_models_from_fields, get_flat_models_from_fields,
get_model_name_map, get_model_name_map,
model_process_schema, model_process_schema,
) )
from pydantic.schema import ( # type: ignore[no-redef] # noqa: F401 from pydantic.typing import ( # type: ignore[no-redef]; noqa: F401
get_annotation_from_field_info as get_annotation_from_field_info, evaluate_forwardref as evaluate_forwardref,
) )
from pydantic.typing import ( # type: ignore[no-redef] from pydantic.utils import ( # type: ignore[no-redef]; noqa: F401
evaluate_forwardref as evaluate_forwardref, # noqa: F401 lenient_issubclass as lenient_issubclass,
)
from pydantic.utils import ( # type: ignore[no-redef]
lenient_issubclass as lenient_issubclass, # noqa: F401
) )
from fastapi.openapi.constants import REF_PREFIX as REF_PREFIX
GetJsonSchemaHandler = Any # type: ignore[assignment,misc] GetJsonSchemaHandler = Any # type: ignore[assignment,misc]
JsonSchemaValue = Dict[str, Any] # type: ignore[misc] JsonSchemaValue = Dict[str, Any] # type: ignore[misc]
CoreSchema = Any # type: ignore[assignment,misc] CoreSchema = Any # type: ignore[assignment,misc]

37
fastapi/applications.py

@ -13,6 +13,17 @@ from typing import (
Union, Union,
) )
from starlette.applications import Starlette
from starlette.datastructures import State
from starlette.exceptions import HTTPException
from starlette.middleware import Middleware
from starlette.middleware.base import BaseHTTPMiddleware
from starlette.requests import Request
from starlette.responses import HTMLResponse, JSONResponse, Response
from starlette.routing import BaseRoute
from starlette.types import ASGIApp, Lifespan, Receive, Scope, Send
from typing_extensions import Annotated, Doc, deprecated
from fastapi import routing from fastapi import routing
from fastapi.datastructures import Default, DefaultPlaceholder from fastapi.datastructures import Default, DefaultPlaceholder
from fastapi.exception_handlers import ( from fastapi.exception_handlers import (
@ -31,16 +42,6 @@ from fastapi.openapi.utils import get_openapi
from fastapi.params import Depends from fastapi.params import Depends
from fastapi.types import DecoratedCallable, IncEx from fastapi.types import DecoratedCallable, IncEx
from fastapi.utils import generate_unique_id from fastapi.utils import generate_unique_id
from starlette.applications import Starlette
from starlette.datastructures import State
from starlette.exceptions import HTTPException
from starlette.middleware import Middleware
from starlette.middleware.base import BaseHTTPMiddleware
from starlette.requests import Request
from starlette.responses import HTMLResponse, JSONResponse, Response
from starlette.routing import BaseRoute
from starlette.types import ASGIApp, Lifespan, Receive, Scope, Send
from typing_extensions import Annotated, Doc, deprecated
AppType = TypeVar("AppType", bound="FastAPI") AppType = TypeVar("AppType", bound="FastAPI")
@ -1723,7 +1724,7 @@ class FastAPI(Starlette):
This will be passed in as serialization context to the response model. This will be passed in as serialization context to the response model.
Note: This feature is a noop on pydantic < 2.7.2 Note: This feature is a noop on pydantic < 2.8
Read more about serialization context in the Read more about serialization context in the
[Pydantic documentation](https://docs.pydantic.dev/latest/concepts/serialization/#serialization-context) [Pydantic documentation](https://docs.pydantic.dev/latest/concepts/serialization/#serialization-context)
@ -2112,7 +2113,7 @@ class FastAPI(Starlette):
This will be passed in as serialization context to the response model. This will be passed in as serialization context to the response model.
Note: This feature is a noop on pydantic < 2.7.2 Note: This feature is a noop on pydantic < 2.8
Read more about serialization context in the Read more about serialization context in the
[Pydantic documentation](https://docs.pydantic.dev/latest/concepts/serialization/#serialization-context) [Pydantic documentation](https://docs.pydantic.dev/latest/concepts/serialization/#serialization-context)
@ -2506,7 +2507,7 @@ class FastAPI(Starlette):
This will be passed in as serialization context to the response model. This will be passed in as serialization context to the response model.
Note: This feature is a noop on pydantic < 2.7.2 Note: This feature is a noop on pydantic < 2.8
Read more about serialization context in the Read more about serialization context in the
[Pydantic documentation](https://docs.pydantic.dev/latest/concepts/serialization/#serialization-context) [Pydantic documentation](https://docs.pydantic.dev/latest/concepts/serialization/#serialization-context)
@ -2900,7 +2901,7 @@ class FastAPI(Starlette):
This will be passed in as serialization context to the response model. This will be passed in as serialization context to the response model.
Note: This feature is a noop on pydantic < 2.7.2 Note: This feature is a noop on pydantic < 2.8
Read more about serialization context in the Read more about serialization context in the
[Pydantic documentation](https://docs.pydantic.dev/latest/concepts/serialization/#serialization-context) [Pydantic documentation](https://docs.pydantic.dev/latest/concepts/serialization/#serialization-context)
@ -3289,7 +3290,7 @@ class FastAPI(Starlette):
This will be passed in as serialization context to the response model. This will be passed in as serialization context to the response model.
Note: This feature is a noop on pydantic < 2.7.2 Note: This feature is a noop on pydantic < 2.8
Read more about serialization context in the Read more about serialization context in the
[Pydantic documentation](https://docs.pydantic.dev/latest/concepts/serialization/#serialization-context) [Pydantic documentation](https://docs.pydantic.dev/latest/concepts/serialization/#serialization-context)
@ -3678,7 +3679,7 @@ class FastAPI(Starlette):
This will be passed in as serialization context to the response model. This will be passed in as serialization context to the response model.
Note: This feature is a noop on pydantic < 2.7.2 Note: This feature is a noop on pydantic < 2.8
Read more about serialization context in the Read more about serialization context in the
[Pydantic documentation](https://docs.pydantic.dev/latest/concepts/serialization/#serialization-context) [Pydantic documentation](https://docs.pydantic.dev/latest/concepts/serialization/#serialization-context)
@ -4067,7 +4068,7 @@ class FastAPI(Starlette):
This will be passed in as serialization context to the response model. This will be passed in as serialization context to the response model.
Note: This feature is a noop on pydantic < 2.7.2 Note: This feature is a noop on pydantic < 2.8
Read more about serialization context in the Read more about serialization context in the
[Pydantic documentation](https://docs.pydantic.dev/latest/concepts/serialization/#serialization-context) [Pydantic documentation](https://docs.pydantic.dev/latest/concepts/serialization/#serialization-context)
@ -4461,7 +4462,7 @@ class FastAPI(Starlette):
This will be passed in as serialization context to the response model. This will be passed in as serialization context to the response model.
Note: This feature is a noop on pydantic < 2.7.2 Note: This feature is a noop on pydantic < 2.8
Read more about serialization context in the Read more about serialization context in the
[Pydantic documentation](https://docs.pydantic.dev/latest/concepts/serialization/#serialization-context) [Pydantic documentation](https://docs.pydantic.dev/latest/concepts/serialization/#serialization-context)

52
fastapi/routing.py

@ -19,6 +19,24 @@ from typing import (
Union, Union,
) )
from pydantic import BaseModel
from starlette import routing
from starlette.concurrency import run_in_threadpool
from starlette.exceptions import HTTPException
from starlette.requests import Request
from starlette.responses import JSONResponse, Response
from starlette.routing import BaseRoute, Match
from starlette.routing import Mount as Mount # noqa
from starlette.routing import (
compile_path,
get_name,
request_response,
websocket_session,
)
from starlette.types import ASGIApp, Lifespan, Scope
from starlette.websockets import WebSocket
from typing_extensions import Annotated, Doc, deprecated
from fastapi import params from fastapi import params
from fastapi._compat import ( from fastapi._compat import (
ModelField, ModelField,
@ -52,24 +70,6 @@ from fastapi.utils import (
get_value_or_default, get_value_or_default,
is_body_allowed_for_status_code, is_body_allowed_for_status_code,
) )
from pydantic import BaseModel
from starlette import routing
from starlette.concurrency import run_in_threadpool
from starlette.exceptions import HTTPException
from starlette.requests import Request
from starlette.responses import JSONResponse, Response
from starlette.routing import (
BaseRoute,
Match,
compile_path,
get_name,
request_response,
websocket_session,
)
from starlette.routing import Mount as Mount # noqa
from starlette.types import ASGIApp, Lifespan, Scope
from starlette.websockets import WebSocket
from typing_extensions import Annotated, Doc, deprecated
def _prepare_response_content( def _prepare_response_content(
@ -1583,7 +1583,7 @@ class APIRouter(routing.Router):
This will be passed in as serialization context to the response model. This will be passed in as serialization context to the response model.
Note: This feature is a noop on pydantic < 2.7.2 Note: This feature is a noop on pydantic < 2.8
Read more about serialization context in the Read more about serialization context in the
[Pydantic documentation](https://docs.pydantic.dev/latest/concepts/serialization/#serialization-context) [Pydantic documentation](https://docs.pydantic.dev/latest/concepts/serialization/#serialization-context)
@ -1976,7 +1976,7 @@ class APIRouter(routing.Router):
This will be passed in as serialization context to the response model. This will be passed in as serialization context to the response model.
Note: This feature is a noop on pydantic < 2.7.2 Note: This feature is a noop on pydantic < 2.8
Read more about serialization context in the Read more about serialization context in the
[Pydantic documentation](https://docs.pydantic.dev/latest/concepts/serialization/#serialization-context) [Pydantic documentation](https://docs.pydantic.dev/latest/concepts/serialization/#serialization-context)
@ -2374,7 +2374,7 @@ class APIRouter(routing.Router):
This will be passed in as serialization context to the response model. This will be passed in as serialization context to the response model.
Note: This feature is a noop on pydantic < 2.7.2 Note: This feature is a noop on pydantic < 2.8
Read more about serialization context in the Read more about serialization context in the
[Pydantic documentation](https://docs.pydantic.dev/latest/concepts/serialization/#serialization-context) [Pydantic documentation](https://docs.pydantic.dev/latest/concepts/serialization/#serialization-context)
@ -2772,7 +2772,7 @@ class APIRouter(routing.Router):
This will be passed in as serialization context to the response model. This will be passed in as serialization context to the response model.
Note: This feature is a noop on pydantic < 2.7.2 Note: This feature is a noop on pydantic < 2.8
Read more about serialization context in the Read more about serialization context in the
[Pydantic documentation](https://docs.pydantic.dev/latest/concepts/serialization/#serialization-context) [Pydantic documentation](https://docs.pydantic.dev/latest/concepts/serialization/#serialization-context)
@ -3165,7 +3165,7 @@ class APIRouter(routing.Router):
This will be passed in as serialization context to the response model. This will be passed in as serialization context to the response model.
Note: This feature is a noop on pydantic < 2.7.2 Note: This feature is a noop on pydantic < 2.8
Read more about serialization context in the Read more about serialization context in the
[Pydantic documentation](https://docs.pydantic.dev/latest/concepts/serialization/#serialization-context) [Pydantic documentation](https://docs.pydantic.dev/latest/concepts/serialization/#serialization-context)
@ -3558,7 +3558,7 @@ class APIRouter(routing.Router):
This will be passed in as serialization context to the response model. This will be passed in as serialization context to the response model.
Note: This feature is a noop on pydantic < 2.7.2 Note: This feature is a noop on pydantic < 2.8
Read more about serialization context in the Read more about serialization context in the
[Pydantic documentation](https://docs.pydantic.dev/latest/concepts/serialization/#serialization-context) [Pydantic documentation](https://docs.pydantic.dev/latest/concepts/serialization/#serialization-context)
@ -3956,7 +3956,7 @@ class APIRouter(routing.Router):
This will be passed in as serialization context to the response model. This will be passed in as serialization context to the response model.
Note: This feature is a noop on pydantic < 2.7.2 Note: This feature is a noop on pydantic < 2.8
Read more about serialization context in the Read more about serialization context in the
[Pydantic documentation](https://docs.pydantic.dev/latest/concepts/serialization/#serialization-context) [Pydantic documentation](https://docs.pydantic.dev/latest/concepts/serialization/#serialization-context)
@ -4354,7 +4354,7 @@ class APIRouter(routing.Router):
This will be passed in as serialization context to the response model. This will be passed in as serialization context to the response model.
Note: This feature is a noop on pydantic < 2.7.2 Note: This feature is a noop on pydantic < 2.8
Read more about serialization context in the Read more about serialization context in the
[Pydantic documentation](https://docs.pydantic.dev/latest/concepts/serialization/#serialization-context) [Pydantic documentation](https://docs.pydantic.dev/latest/concepts/serialization/#serialization-context)

9
tests/test_serialize_response_model.py

@ -1,11 +1,12 @@
from typing import Dict, List, Optional from typing import Dict, List, Optional
import pytest import pytest
from fastapi import FastAPI
from fastapi._compat import PYDANTIC_V2, PYDANTIC_VERSION
from pydantic import BaseModel, Field from pydantic import BaseModel, Field
from starlette.testclient import TestClient from starlette.testclient import TestClient
from fastapi import FastAPI
from fastapi._compat import PYDANTIC_V2, PYDANTIC_VERSION
app = FastAPI() app = FastAPI()
@ -188,7 +189,7 @@ if PYDANTIC_V2:
client_v2 = TestClient(app_v2) client_v2 = TestClient(app_v2)
@pytest.mark.skipif(PYDANTIC_VERSION < "2.7.3", reason="requires Pydantic v2.7.3+") @pytest.mark.skipif(PYDANTIC_VERSION < "2.8", reason="requires Pydantic v2.8+")
def test_validdict_with_context__pydantic_supported(): def test_validdict_with_context__pydantic_supported():
response = client_v2.get("/items/validdict-with-context") response = client_v2.get("/items/validdict-with-context")
response.raise_for_status() response.raise_for_status()
@ -202,7 +203,7 @@ if PYDANTIC_V2:
assert response.json() == expected_response assert response.json() == expected_response
@pytest.mark.skipif( @pytest.mark.skipif(
PYDANTIC_VERSION >= "2.7.3", PYDANTIC_VERSION >= "2.8",
reason="Pydantic supports the feature from this point on", reason="Pydantic supports the feature from this point on",
) )
def test_validdict_with_context__pre_pydantic_support(): def test_validdict_with_context__pre_pydantic_support():

Loading…
Cancel
Save