Browse Source

Merge 6caa3fc49b into 6df50d40fe

pull/11634/merge
Alex Couper 1 day ago
committed by GitHub
parent
commit
bde7492e03
No known key found for this signature in database GPG Key ID: B5690EEEBB952194
  1. 9
      .github/workflows/test.yml
  2. 41
      fastapi/_compat.py
  3. 132
      fastapi/applications.py
  4. 140
      fastapi/routing.py
  5. 66
      tests/test_serialize_response_model.py

9
.github/workflows/test.yml

@ -54,7 +54,7 @@ jobs:
- "3.10"
- "3.9"
- "3.8"
pydantic-version: ["pydantic-v1", "pydantic-v2"]
pydantic-version: ["pydantic-v1", "pydantic-v2.7", "pydantic-v2.8+"]
fail-fast: false
steps:
- name: Dump GitHub context
@ -80,8 +80,11 @@ jobs:
if: matrix.pydantic-version == 'pydantic-v1'
run: uv pip install "pydantic>=1.10.0,<2.0.0"
- name: Install Pydantic v2
if: matrix.pydantic-version == 'pydantic-v2'
run: uv pip install --upgrade "pydantic>=2.0.2,<3.0.0"
if: matrix.pydantic-version == 'pydantic-v2.7'
run: uv pip install --upgrade "pydantic>=2.0.2,<2.8"
- name: Install Pydantic v2.8+
if: matrix.pydantic-version == 'pydantic-v2.8+'
run: uv pip install --upgrade "pydantic>=2.8,<3.0"
# TODO: Remove this once Python 3.8 is no longer supported
- name: Install older AnyIO in Python 3.8
if: matrix.python-version == '3.8'

41
fastapi/_compat.py

@ -11,6 +11,7 @@ from typing import (
FrozenSet,
List,
Mapping,
Optional,
Sequence,
Set,
Tuple,
@ -69,8 +70,8 @@ if PYDANTIC_V2:
with_info_plain_validator_function as with_info_plain_validator_function,
)
except ImportError: # pragma: no cover
from pydantic_core.core_schema import (
general_plain_validator_function as with_info_plain_validator_function, # noqa: F401
from pydantic_core.core_schema import ( # noqa: F401
general_plain_validator_function as with_info_plain_validator_function,
)
RequiredParam = PydanticUndefined
@ -146,19 +147,35 @@ if PYDANTIC_V2:
exclude_unset: bool = False,
exclude_defaults: bool = False,
exclude_none: bool = False,
context: Optional[Dict[str, Any]] = None,
) -> Any:
# What calls this code passes a value that already called
# self._type_adapter.validate_python(value)
return self._type_adapter.dump_python(
value,
mode=mode,
include=include,
exclude=exclude,
by_alias=by_alias,
exclude_unset=exclude_unset,
exclude_defaults=exclude_defaults,
exclude_none=exclude_none,
)
#
# context argument was introduced in pydantic 2.8
if PYDANTIC_VERSION >= "2.8":
return self._type_adapter.dump_python(
value,
mode=mode,
include=include,
exclude=exclude,
by_alias=by_alias,
exclude_unset=exclude_unset,
exclude_defaults=exclude_defaults,
exclude_none=exclude_none,
context=context,
)
else:
return self._type_adapter.dump_python(
value,
mode=mode,
include=include,
exclude=exclude,
by_alias=by_alias,
exclude_unset=exclude_unset,
exclude_defaults=exclude_defaults,
exclude_none=exclude_none,
)
def __hash__(self) -> int:
# Each ModelField is unique for our purposes, to allow making a dict from

132
fastapi/applications.py

@ -1075,6 +1075,7 @@ class FastAPI(Starlette):
response_model_exclude_unset: bool = False,
response_model_exclude_defaults: bool = False,
response_model_exclude_none: bool = False,
response_model_context: Optional[Dict[str, Any]] = None,
include_in_schema: bool = True,
response_class: Union[Type[Response], DefaultPlaceholder] = Default(
JSONResponse
@ -1105,6 +1106,7 @@ class FastAPI(Starlette):
response_model_exclude_unset=response_model_exclude_unset,
response_model_exclude_defaults=response_model_exclude_defaults,
response_model_exclude_none=response_model_exclude_none,
response_model_context=response_model_context,
include_in_schema=include_in_schema,
response_class=response_class,
name=name,
@ -1133,6 +1135,7 @@ class FastAPI(Starlette):
response_model_exclude_unset: bool = False,
response_model_exclude_defaults: bool = False,
response_model_exclude_none: bool = False,
response_model_context: Optional[Dict[str, Any]] = None,
include_in_schema: bool = True,
response_class: Type[Response] = Default(JSONResponse),
name: Optional[str] = None,
@ -1162,6 +1165,7 @@ class FastAPI(Starlette):
response_model_exclude_unset=response_model_exclude_unset,
response_model_exclude_defaults=response_model_exclude_defaults,
response_model_exclude_none=response_model_exclude_none,
response_model_context=response_model_context,
include_in_schema=include_in_schema,
response_class=response_class,
name=name,
@ -1711,6 +1715,21 @@ class FastAPI(Starlette):
"""
),
] = False,
response_model_context: Annotated[
Optional[Dict[str, Any]],
Doc(
"""
Additional context to pass to Pydantic when creating the response.
This will be passed in as serialization context to the response model.
Note: This feature is a noop on pydantic < 2.8
Read more about serialization context in the
[Pydantic documentation](https://docs.pydantic.dev/latest/concepts/serialization/#serialization-context)
"""
),
] = None,
include_in_schema: Annotated[
bool,
Doc(
@ -1822,6 +1841,7 @@ class FastAPI(Starlette):
response_model_exclude_unset=response_model_exclude_unset,
response_model_exclude_defaults=response_model_exclude_defaults,
response_model_exclude_none=response_model_exclude_none,
response_model_context=response_model_context,
include_in_schema=include_in_schema,
response_class=response_class,
name=name,
@ -2084,6 +2104,21 @@ class FastAPI(Starlette):
"""
),
] = False,
response_model_context: Annotated[
Optional[Dict[str, Any]],
Doc(
"""
Additional context to pass to Pydantic when creating the response.
This will be passed in as serialization context to the response model.
Note: This feature is a noop on pydantic < 2.8
Read more about serialization context in the
[Pydantic documentation](https://docs.pydantic.dev/latest/concepts/serialization/#serialization-context)
"""
),
] = None,
include_in_schema: Annotated[
bool,
Doc(
@ -2200,6 +2235,7 @@ class FastAPI(Starlette):
response_model_exclude_unset=response_model_exclude_unset,
response_model_exclude_defaults=response_model_exclude_defaults,
response_model_exclude_none=response_model_exclude_none,
response_model_context=response_model_context,
include_in_schema=include_in_schema,
response_class=response_class,
name=name,
@ -2462,6 +2498,21 @@ class FastAPI(Starlette):
"""
),
] = False,
response_model_context: Annotated[
Optional[Dict[str, Any]],
Doc(
"""
Additional context to pass to Pydantic when creating the response.
This will be passed in as serialization context to the response model.
Note: This feature is a noop on pydantic < 2.8
Read more about serialization context in the
[Pydantic documentation](https://docs.pydantic.dev/latest/concepts/serialization/#serialization-context)
"""
),
] = None,
include_in_schema: Annotated[
bool,
Doc(
@ -2578,6 +2629,7 @@ class FastAPI(Starlette):
response_model_exclude_unset=response_model_exclude_unset,
response_model_exclude_defaults=response_model_exclude_defaults,
response_model_exclude_none=response_model_exclude_none,
response_model_context=response_model_context,
include_in_schema=include_in_schema,
response_class=response_class,
name=name,
@ -2840,6 +2892,21 @@ class FastAPI(Starlette):
"""
),
] = False,
response_model_context: Annotated[
Optional[Dict[str, Any]],
Doc(
"""
Additional context to pass to Pydantic when creating the response.
This will be passed in as serialization context to the response model.
Note: This feature is a noop on pydantic < 2.8
Read more about serialization context in the
[Pydantic documentation](https://docs.pydantic.dev/latest/concepts/serialization/#serialization-context)
"""
),
] = None,
include_in_schema: Annotated[
bool,
Doc(
@ -2951,6 +3018,7 @@ class FastAPI(Starlette):
response_model_exclude_unset=response_model_exclude_unset,
response_model_exclude_defaults=response_model_exclude_defaults,
response_model_exclude_none=response_model_exclude_none,
response_model_context=response_model_context,
include_in_schema=include_in_schema,
response_class=response_class,
name=name,
@ -3213,6 +3281,21 @@ class FastAPI(Starlette):
"""
),
] = False,
response_model_context: Annotated[
Optional[Dict[str, Any]],
Doc(
"""
Additional context to pass to Pydantic when creating the response.
This will be passed in as serialization context to the response model.
Note: This feature is a noop on pydantic < 2.8
Read more about serialization context in the
[Pydantic documentation](https://docs.pydantic.dev/latest/concepts/serialization/#serialization-context)
"""
),
] = None,
include_in_schema: Annotated[
bool,
Doc(
@ -3324,6 +3407,7 @@ class FastAPI(Starlette):
response_model_exclude_unset=response_model_exclude_unset,
response_model_exclude_defaults=response_model_exclude_defaults,
response_model_exclude_none=response_model_exclude_none,
response_model_context=response_model_context,
include_in_schema=include_in_schema,
response_class=response_class,
name=name,
@ -3586,6 +3670,21 @@ class FastAPI(Starlette):
"""
),
] = False,
response_model_context: Annotated[
Optional[Dict[str, Any]],
Doc(
"""
Additional context to pass to Pydantic when creating the response.
This will be passed in as serialization context to the response model.
Note: This feature is a noop on pydantic < 2.8
Read more about serialization context in the
[Pydantic documentation](https://docs.pydantic.dev/latest/concepts/serialization/#serialization-context)
"""
),
] = None,
include_in_schema: Annotated[
bool,
Doc(
@ -3697,6 +3796,7 @@ class FastAPI(Starlette):
response_model_exclude_unset=response_model_exclude_unset,
response_model_exclude_defaults=response_model_exclude_defaults,
response_model_exclude_none=response_model_exclude_none,
response_model_context=response_model_context,
include_in_schema=include_in_schema,
response_class=response_class,
name=name,
@ -3959,6 +4059,21 @@ class FastAPI(Starlette):
"""
),
] = False,
response_model_context: Annotated[
Optional[Dict[str, Any]],
Doc(
"""
Additional context to pass to Pydantic when creating the response.
This will be passed in as serialization context to the response model.
Note: This feature is a noop on pydantic < 2.8
Read more about serialization context in the
[Pydantic documentation](https://docs.pydantic.dev/latest/concepts/serialization/#serialization-context)
"""
),
] = None,
include_in_schema: Annotated[
bool,
Doc(
@ -4075,6 +4190,7 @@ class FastAPI(Starlette):
response_model_exclude_unset=response_model_exclude_unset,
response_model_exclude_defaults=response_model_exclude_defaults,
response_model_exclude_none=response_model_exclude_none,
response_model_context=response_model_context,
include_in_schema=include_in_schema,
response_class=response_class,
name=name,
@ -4337,6 +4453,21 @@ class FastAPI(Starlette):
"""
),
] = False,
response_model_context: Annotated[
Optional[Dict[str, Any]],
Doc(
"""
Additional context to pass to Pydantic when creating the response.
This will be passed in as serialization context to the response model.
Note: This feature is a noop on pydantic < 2.8
Read more about serialization context in the
[Pydantic documentation](https://docs.pydantic.dev/latest/concepts/serialization/#serialization-context)
"""
),
] = None,
include_in_schema: Annotated[
bool,
Doc(
@ -4448,6 +4579,7 @@ class FastAPI(Starlette):
response_model_exclude_unset=response_model_exclude_unset,
response_model_exclude_defaults=response_model_exclude_defaults,
response_model_exclude_none=response_model_exclude_none,
response_model_context=response_model_context,
include_in_schema=include_in_schema,
response_class=response_class,
name=name,

140
fastapi/routing.py

@ -152,6 +152,7 @@ async def serialize_response(
exclude_defaults: bool = False,
exclude_none: bool = False,
is_coroutine: bool = True,
context: Optional[Dict[str, Any]] = None,
) -> Any:
if field:
errors = []
@ -187,6 +188,7 @@ async def serialize_response(
exclude_unset=exclude_unset,
exclude_defaults=exclude_defaults,
exclude_none=exclude_none,
context=context,
)
return jsonable_encoder(
@ -227,6 +229,7 @@ def get_request_handler(
response_model_exclude_unset: bool = False,
response_model_exclude_defaults: bool = False,
response_model_exclude_none: bool = False,
response_model_context: Optional[Dict[str, Any]] = None,
dependency_overrides_provider: Optional[Any] = None,
embed_body_fields: bool = False,
) -> Callable[[Request], Coroutine[Any, Any, Response]]:
@ -335,6 +338,7 @@ def get_request_handler(
exclude_defaults=response_model_exclude_defaults,
exclude_none=response_model_exclude_none,
is_coroutine=is_coroutine,
context=response_model_context,
)
response = actual_response_class(content, **response_args)
if not is_body_allowed_for_status_code(response.status_code):
@ -450,6 +454,7 @@ class APIRoute(routing.Route):
response_model_exclude_unset: bool = False,
response_model_exclude_defaults: bool = False,
response_model_exclude_none: bool = False,
response_model_context: Optional[Dict[str, Any]] = None,
include_in_schema: bool = True,
response_class: Union[Type[Response], DefaultPlaceholder] = Default(
JSONResponse
@ -480,6 +485,7 @@ class APIRoute(routing.Route):
self.response_model_exclude_unset = response_model_exclude_unset
self.response_model_exclude_defaults = response_model_exclude_defaults
self.response_model_exclude_none = response_model_exclude_none
self.response_model_context = response_model_context
self.include_in_schema = include_in_schema
self.response_class = response_class
self.dependency_overrides_provider = dependency_overrides_provider
@ -582,6 +588,7 @@ class APIRoute(routing.Route):
response_model_exclude_unset=self.response_model_exclude_unset,
response_model_exclude_defaults=self.response_model_exclude_defaults,
response_model_exclude_none=self.response_model_exclude_none,
response_model_context=self.response_model_context,
dependency_overrides_provider=self.dependency_overrides_provider,
embed_body_fields=self._embed_body_fields,
)
@ -901,6 +908,7 @@ class APIRouter(routing.Router):
response_model_exclude_unset: bool = False,
response_model_exclude_defaults: bool = False,
response_model_exclude_none: bool = False,
response_model_context: Optional[Dict[str, Any]] = None,
include_in_schema: bool = True,
response_class: Union[Type[Response], DefaultPlaceholder] = Default(
JSONResponse
@ -951,6 +959,7 @@ class APIRouter(routing.Router):
response_model_exclude_unset=response_model_exclude_unset,
response_model_exclude_defaults=response_model_exclude_defaults,
response_model_exclude_none=response_model_exclude_none,
response_model_context=response_model_context,
include_in_schema=include_in_schema and self.include_in_schema,
response_class=current_response_class,
name=name,
@ -982,6 +991,7 @@ class APIRouter(routing.Router):
response_model_exclude_unset: bool = False,
response_model_exclude_defaults: bool = False,
response_model_exclude_none: bool = False,
response_model_context: Optional[Dict[str, Any]] = None,
include_in_schema: bool = True,
response_class: Type[Response] = Default(JSONResponse),
name: Optional[str] = None,
@ -1012,6 +1022,7 @@ class APIRouter(routing.Router):
response_model_exclude_unset=response_model_exclude_unset,
response_model_exclude_defaults=response_model_exclude_defaults,
response_model_exclude_none=response_model_exclude_none,
response_model_context=response_model_context,
include_in_schema=include_in_schema,
response_class=response_class,
name=name,
@ -1320,6 +1331,7 @@ class APIRouter(routing.Router):
response_model_exclude_unset=route.response_model_exclude_unset,
response_model_exclude_defaults=route.response_model_exclude_defaults,
response_model_exclude_none=route.response_model_exclude_none,
response_model_context=route.response_model_context,
include_in_schema=route.include_in_schema
and self.include_in_schema
and include_in_schema,
@ -1618,6 +1630,21 @@ class APIRouter(routing.Router):
"""
),
] = False,
response_model_context: Annotated[
Optional[Dict[str, Any]],
Doc(
"""
Additional context to pass to Pydantic when creating the response.
This will be passed in as serialization context to the response model.
Note: This feature is a noop on pydantic < 2.8
Read more about serialization context in the
[Pydantic documentation](https://docs.pydantic.dev/latest/concepts/serialization/#serialization-context)
"""
),
] = None,
include_in_schema: Annotated[
bool,
Doc(
@ -1733,6 +1760,7 @@ class APIRouter(routing.Router):
response_model_exclude_unset=response_model_exclude_unset,
response_model_exclude_defaults=response_model_exclude_defaults,
response_model_exclude_none=response_model_exclude_none,
response_model_context=response_model_context,
include_in_schema=include_in_schema,
response_class=response_class,
name=name,
@ -1995,6 +2023,21 @@ class APIRouter(routing.Router):
"""
),
] = False,
response_model_context: Annotated[
Optional[Dict[str, Any]],
Doc(
"""
Additional context to pass to Pydantic when creating the response.
This will be passed in as serialization context to the response model.
Note: This feature is a noop on pydantic < 2.8
Read more about serialization context in the
[Pydantic documentation](https://docs.pydantic.dev/latest/concepts/serialization/#serialization-context)
"""
),
] = None,
include_in_schema: Annotated[
bool,
Doc(
@ -2115,6 +2158,7 @@ class APIRouter(routing.Router):
response_model_exclude_unset=response_model_exclude_unset,
response_model_exclude_defaults=response_model_exclude_defaults,
response_model_exclude_none=response_model_exclude_none,
response_model_context=response_model_context,
include_in_schema=include_in_schema,
response_class=response_class,
name=name,
@ -2377,6 +2421,21 @@ class APIRouter(routing.Router):
"""
),
] = False,
response_model_context: Annotated[
Optional[Dict[str, Any]],
Doc(
"""
Additional context to pass to Pydantic when creating the response.
This will be passed in as serialization context to the response model.
Note: This feature is a noop on pydantic < 2.8
Read more about serialization context in the
[Pydantic documentation](https://docs.pydantic.dev/latest/concepts/serialization/#serialization-context)
"""
),
] = None,
include_in_schema: Annotated[
bool,
Doc(
@ -2497,6 +2556,7 @@ class APIRouter(routing.Router):
response_model_exclude_unset=response_model_exclude_unset,
response_model_exclude_defaults=response_model_exclude_defaults,
response_model_exclude_none=response_model_exclude_none,
response_model_context=response_model_context,
include_in_schema=include_in_schema,
response_class=response_class,
name=name,
@ -2759,6 +2819,21 @@ class APIRouter(routing.Router):
"""
),
] = False,
response_model_context: Annotated[
Optional[Dict[str, Any]],
Doc(
"""
Additional context to pass to Pydantic when creating the response.
This will be passed in as serialization context to the response model.
Note: This feature is a noop on pydantic < 2.8
Read more about serialization context in the
[Pydantic documentation](https://docs.pydantic.dev/latest/concepts/serialization/#serialization-context)
"""
),
] = None,
include_in_schema: Annotated[
bool,
Doc(
@ -2874,6 +2949,7 @@ class APIRouter(routing.Router):
response_model_exclude_unset=response_model_exclude_unset,
response_model_exclude_defaults=response_model_exclude_defaults,
response_model_exclude_none=response_model_exclude_none,
response_model_context=response_model_context,
include_in_schema=include_in_schema,
response_class=response_class,
name=name,
@ -3136,6 +3212,21 @@ class APIRouter(routing.Router):
"""
),
] = False,
response_model_context: Annotated[
Optional[Dict[str, Any]],
Doc(
"""
Additional context to pass to Pydantic when creating the response.
This will be passed in as serialization context to the response model.
Note: This feature is a noop on pydantic < 2.8
Read more about serialization context in the
[Pydantic documentation](https://docs.pydantic.dev/latest/concepts/serialization/#serialization-context)
"""
),
] = None,
include_in_schema: Annotated[
bool,
Doc(
@ -3251,6 +3342,7 @@ class APIRouter(routing.Router):
response_model_exclude_unset=response_model_exclude_unset,
response_model_exclude_defaults=response_model_exclude_defaults,
response_model_exclude_none=response_model_exclude_none,
response_model_context=response_model_context,
include_in_schema=include_in_schema,
response_class=response_class,
name=name,
@ -3513,6 +3605,21 @@ class APIRouter(routing.Router):
"""
),
] = False,
response_model_context: Annotated[
Optional[Dict[str, Any]],
Doc(
"""
Additional context to pass to Pydantic when creating the response.
This will be passed in as serialization context to the response model.
Note: This feature is a noop on pydantic < 2.8
Read more about serialization context in the
[Pydantic documentation](https://docs.pydantic.dev/latest/concepts/serialization/#serialization-context)
"""
),
] = None,
include_in_schema: Annotated[
bool,
Doc(
@ -3633,6 +3740,7 @@ class APIRouter(routing.Router):
response_model_exclude_unset=response_model_exclude_unset,
response_model_exclude_defaults=response_model_exclude_defaults,
response_model_exclude_none=response_model_exclude_none,
response_model_context=response_model_context,
include_in_schema=include_in_schema,
response_class=response_class,
name=name,
@ -3895,6 +4003,21 @@ class APIRouter(routing.Router):
"""
),
] = False,
response_model_context: Annotated[
Optional[Dict[str, Any]],
Doc(
"""
Additional context to pass to Pydantic when creating the response.
This will be passed in as serialization context to the response model.
Note: This feature is a noop on pydantic < 2.8
Read more about serialization context in the
[Pydantic documentation](https://docs.pydantic.dev/latest/concepts/serialization/#serialization-context)
"""
),
] = None,
include_in_schema: Annotated[
bool,
Doc(
@ -4015,6 +4138,7 @@ class APIRouter(routing.Router):
response_model_exclude_unset=response_model_exclude_unset,
response_model_exclude_defaults=response_model_exclude_defaults,
response_model_exclude_none=response_model_exclude_none,
response_model_context=response_model_context,
include_in_schema=include_in_schema,
response_class=response_class,
name=name,
@ -4277,6 +4401,21 @@ class APIRouter(routing.Router):
"""
),
] = False,
response_model_context: Annotated[
Optional[Dict[str, Any]],
Doc(
"""
Additional context to pass to Pydantic when creating the response.
This will be passed in as serialization context to the response model.
Note: This feature is a noop on pydantic < 2.8
Read more about serialization context in the
[Pydantic documentation](https://docs.pydantic.dev/latest/concepts/serialization/#serialization-context)
"""
),
] = None,
include_in_schema: Annotated[
bool,
Doc(
@ -4397,6 +4536,7 @@ class APIRouter(routing.Router):
response_model_exclude_unset=response_model_exclude_unset,
response_model_exclude_defaults=response_model_exclude_defaults,
response_model_exclude_none=response_model_exclude_none,
response_model_context=response_model_context,
include_in_schema=include_in_schema,
response_class=response_class,
name=name,

66
tests/test_serialize_response_model.py

@ -1,6 +1,8 @@
from typing import Dict, List, Optional
import pytest
from fastapi import FastAPI
from fastapi._compat import PYDANTIC_V2, PYDANTIC_VERSION
from pydantic import BaseModel, Field
from starlette.testclient import TestClient
@ -152,3 +154,67 @@ def test_validdict_exclude_unset():
"k2": {"aliased_name": "bar", "price": 1.0},
"k3": {"aliased_name": "baz", "price": 2.0, "owner_ids": [1, 2, 3]},
}
if PYDANTIC_V2:
from pydantic import SerializationInfo, model_serializer
class MultiUseItem(BaseModel):
name: str = Field(alias="aliased_name")
secret: Optional[str] = None
owner_ids: Optional[List[int]] = None
@model_serializer(mode="wrap")
def _serialize(self, handler, info: SerializationInfo):
data = handler(self)
if info.context and info.context.get("mode") == "FASTAPI":
if "secret" in data:
data.pop("secret")
return data
app_v2 = FastAPI()
@app_v2.get(
"/items/validdict-with-context",
response_model=Dict[str, MultiUseItem],
response_model_context={"mode": "FASTAPI"},
)
async def get_validdict_with_context():
return {
"k1": MultiUseItem(aliased_name="foo"),
"k2": MultiUseItem(aliased_name="bar", secret="sEcReT"),
"k3": MultiUseItem(
aliased_name="baz", secret="sEcReT", owner_ids=[1, 2, 3]
),
}
client_v2 = TestClient(app_v2)
@pytest.mark.skipif(PYDANTIC_VERSION < "2.8", reason="requires Pydantic v2.8+")
def test_validdict_with_context__pydantic_supported():
response = client_v2.get("/items/validdict-with-context")
response.raise_for_status()
expected_response = {
"k1": {"aliased_name": "foo", "owner_ids": None},
"k2": {"aliased_name": "bar", "owner_ids": None},
"k3": {"aliased_name": "baz", "owner_ids": [1, 2, 3]},
}
assert response.json() == expected_response
@pytest.mark.skipif(
PYDANTIC_VERSION >= "2.8",
reason="Pydantic supports the feature from this point on",
)
def test_validdict_with_context__pre_pydantic_support():
response = client_v2.get("/items/validdict-with-context")
response.raise_for_status()
expected_response = {
"k1": {"aliased_name": "foo", "owner_ids": None, "secret": None},
"k2": {"aliased_name": "bar", "owner_ids": None, "secret": "sEcReT"},
"k3": {"aliased_name": "baz", "owner_ids": [1, 2, 3], "secret": "sEcReT"},
}
assert response.json() == expected_response

Loading…
Cancel
Save