mirror of
https://github.com/long2ice/fastapi-cache.git
synced 2026-03-25 04:57:54 +00:00
Cache pydantic model fields for faster decoding
In `timeit` tests, 10.000 calls to `ModelField()` could take up to half a second on my Macbook Pro M1, depending on the type annotation used. Given that the method is called for every cache hit, this can really add up. The number of different return types for endpoints is very much finite however, so caching is a definite win here.
This commit is contained in:
@@ -3,7 +3,7 @@ import datetime
|
|||||||
import json
|
import json
|
||||||
import pickle # nosec:B403
|
import pickle # nosec:B403
|
||||||
from decimal import Decimal
|
from decimal import Decimal
|
||||||
from typing import Any, Callable, TypeVar, overload
|
from typing import Any, Callable, ClassVar, Dict, TypeVar, overload
|
||||||
|
|
||||||
import pendulum
|
import pendulum
|
||||||
from fastapi.encoders import jsonable_encoder
|
from fastapi.encoders import jsonable_encoder
|
||||||
@@ -53,6 +53,13 @@ class Coder:
|
|||||||
def decode(cls, value: str) -> Any:
|
def decode(cls, value: str) -> Any:
|
||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
|
|
||||||
|
# (Shared) cache for endpoint return types to Pydantic model fields.
|
||||||
|
# Note that subclasses share this cache! If a subclass overrides the
|
||||||
|
# decode_as_type method and then stores a different kind of field for a
|
||||||
|
# given type, do make sure that the subclass provides its own class
|
||||||
|
# attribute for this cache.
|
||||||
|
_type_field_cache: ClassVar[Dict[Any, fields.ModelField]] = {}
|
||||||
|
|
||||||
@overload
|
@overload
|
||||||
@classmethod
|
@classmethod
|
||||||
def decode_as_type(cls, value: str, type_: _T) -> _T:
|
def decode_as_type(cls, value: str, type_: _T) -> _T:
|
||||||
@@ -72,9 +79,12 @@ class Coder:
|
|||||||
"""
|
"""
|
||||||
result = cls.decode(value)
|
result = cls.decode(value)
|
||||||
if type_ is not None:
|
if type_ is not None:
|
||||||
field = fields.ModelField(
|
try:
|
||||||
name="body", type_=type_, class_validators=None, model_config=BaseConfig
|
field = cls._type_field_cache[type_]
|
||||||
)
|
except KeyError:
|
||||||
|
field = cls._type_field_cache[type_] = fields.ModelField(
|
||||||
|
name="body", type_=type_, class_validators=None, model_config=BaseConfig
|
||||||
|
)
|
||||||
result, errors = field.validate(result, {}, loc=())
|
result, errors = field.validate(result, {}, loc=())
|
||||||
if errors is not None:
|
if errors is not None:
|
||||||
if not isinstance(errors, list):
|
if not isinstance(errors, list):
|
||||||
|
|||||||
Reference in New Issue
Block a user