Browse Source

Better jsonable encoder for iterables

pull/13803/head
Pedro Lobato 4 weeks ago
parent
commit
5961a8c61c
  1. 22
      fastapi/encoders.py
  2. 17
      tests/test_jsonable_encoder.py

22
fastapi/encoders.py

@ -14,14 +14,26 @@ from ipaddress import (
from pathlib import Path, PurePath
from re import Pattern
from types import GeneratorType
from typing import Any, Callable, Dict, List, Optional, Tuple, Type, Union
from typing import (
Any,
Callable,
Dict,
List,
Mapping,
Optional,
Sequence,
Tuple,
Type,
Union,
)
from uuid import UUID
from fastapi.types import IncEx
from pydantic import BaseModel
from pydantic.color import Color
from pydantic.networks import AnyUrl, NameEmail
from pydantic.types import SecretBytes, SecretStr
from pydantic_extra_types.color import Color
from pydantic_extra_types.coordinate import Coordinate
from typing_extensions import Annotated, Doc
from ._compat import PYDANTIC_V2, UndefinedType, Url, _model_dump
@ -58,6 +70,7 @@ def decimal_encoder(dec_value: Decimal) -> Union[int, float]:
ENCODERS_BY_TYPE: Dict[Type[Any], Callable[[Any], Any]] = {
bytes: lambda o: o.decode(),
Color: str,
Coordinate: str,
datetime.date: isoformat,
datetime.datetime: isoformat,
datetime.time: isoformat,
@ -261,7 +274,7 @@ def jsonable_encoder(
return obj
if isinstance(obj, UndefinedType):
return None
if isinstance(obj, dict):
if isinstance(obj, Mapping):
encoded_dict = {}
allowed_keys = set(obj.keys())
if include is not None:
@ -296,7 +309,8 @@ def jsonable_encoder(
)
encoded_dict[encoded_key] = encoded_value
return encoded_dict
if isinstance(obj, (list, set, frozenset, GeneratorType, tuple, deque)):
if isinstance(obj, (Sequence, GeneratorType)):
encoded_list = []
for item in obj:
encoded_list.append(

17
tests/test_jsonable_encoder.py

@ -4,7 +4,7 @@ from datetime import datetime, timezone
from decimal import Decimal
from enum import Enum
from pathlib import PurePath, PurePosixPath, PureWindowsPath
from typing import Optional
from typing import Optional, Sequence, Union
import pytest
from fastapi._compat import PYDANTIC_V2, Undefined
@ -316,3 +316,18 @@ def test_encode_deque_encodes_child_models():
def test_encode_pydantic_undefined():
data = {"value": Undefined}
assert jsonable_encoder(data) == {"value": None}
def test_encode_sequence():
class SequenceModel(Sequence[str]):
def __init__(self, items: list[str]):
self._items = items
def __getitem__(self, index: Union[int, slice]) -> Union[str, Sequence[str]]:
return self._items[index]
def __len__(self) -> int:
return len(self._items)
seq = SequenceModel(["item1", "item2", "item3"])
assert jsonable_encoder(seq) == ["item1", "item2", "item3"]

Loading…
Cancel
Save