Skip to content

Commit

Permalink
Compare objects MRO with encoders at runtime
Browse files Browse the repository at this point in the history
The previous implementation doesn't handle subclass instances when
pydantic.json.ENCODERS_BY_TYPE is modified after fastapi.encoders import.

This diff makes it easier for developers to add custom encoders that also work
with subclass instances (and it simplifies the code, as well).
  • Loading branch information
ramnes committed Apr 3, 2024
1 parent 9490491 commit d1d89e7
Showing 1 changed file with 5 additions and 21 deletions.
26 changes: 5 additions & 21 deletions fastapi/encoders.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import dataclasses
import datetime
from collections import defaultdict, deque
from collections import deque
from decimal import Decimal
from enum import Enum
from ipaddress import (
Expand All @@ -14,7 +14,7 @@
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, Optional, Type, Union
from uuid import UUID

from fastapi.types import IncEx
Expand Down Expand Up @@ -85,20 +85,6 @@ def decimal_encoder(dec_value: Decimal) -> Union[int, float]:
}


def generate_encoders_by_class_tuples(
type_encoder_map: Dict[Any, Callable[[Any], Any]],
) -> Dict[Callable[[Any], Any], Tuple[Any, ...]]:
encoders_by_class_tuples: Dict[Callable[[Any], Any], Tuple[Any, ...]] = defaultdict(
tuple
)
for type_, encoder in type_encoder_map.items():
encoders_by_class_tuples[encoder] += (type_,)
return encoders_by_class_tuples


encoders_by_class_tuples = generate_encoders_by_class_tuples(ENCODERS_BY_TYPE)


def jsonable_encoder(
obj: Annotated[
Any,
Expand Down Expand Up @@ -312,11 +298,9 @@ def jsonable_encoder(
)
return encoded_list

if type(obj) in ENCODERS_BY_TYPE:
return ENCODERS_BY_TYPE[type(obj)](obj)
for encoder, classes_tuple in encoders_by_class_tuples.items():
if isinstance(obj, classes_tuple):
return encoder(obj)
for base in obj.__class__.__mro__[:-1]:
if base in ENCODERS_BY_TYPE:
return ENCODERS_BY_TYPE[base](obj)

try:
data = dict(obj)
Expand Down

0 comments on commit d1d89e7

Please sign in to comment.