2022-11-05 13:45:16 +04:00
|
|
|
import codecs
|
2020-10-16 16:55:33 +08:00
|
|
|
import datetime
|
2020-08-26 18:04:57 +08:00
|
|
|
import json
|
|
|
|
|
import pickle # nosec:B403
|
2020-10-16 16:55:33 +08:00
|
|
|
from decimal import Decimal
|
2022-11-05 13:45:16 +04:00
|
|
|
from typing import Any
|
2020-08-26 18:04:57 +08:00
|
|
|
|
2021-09-17 10:19:56 +08:00
|
|
|
import pendulum
|
2021-07-26 16:33:22 +08:00
|
|
|
from fastapi.encoders import jsonable_encoder
|
2022-09-28 17:37:05 +08:00
|
|
|
from starlette.templating import _TemplateResponse as TemplateResponse
|
2020-10-16 16:55:33 +08:00
|
|
|
|
|
|
|
|
CONVERTERS = {
|
2021-10-09 16:51:05 +08:00
|
|
|
"date": lambda x: pendulum.parse(x, exact=True),
|
2021-09-17 10:19:56 +08:00
|
|
|
"datetime": lambda x: pendulum.parse(x, exact=True),
|
2020-10-16 16:55:33 +08:00
|
|
|
"decimal": Decimal,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class JsonEncoder(json.JSONEncoder):
|
2022-10-22 20:59:37 +04:00
|
|
|
def default(self, obj: Any) -> Any:
|
2020-10-16 16:55:33 +08:00
|
|
|
if isinstance(obj, datetime.datetime):
|
2021-10-09 16:51:05 +08:00
|
|
|
return {"val": str(obj), "_spec_type": "datetime"}
|
2020-10-16 16:55:33 +08:00
|
|
|
elif isinstance(obj, datetime.date):
|
2021-10-09 16:51:05 +08:00
|
|
|
return {"val": str(obj), "_spec_type": "date"}
|
2020-10-16 16:55:33 +08:00
|
|
|
elif isinstance(obj, Decimal):
|
|
|
|
|
return {"val": str(obj), "_spec_type": "decimal"}
|
|
|
|
|
else:
|
2021-01-11 03:11:35 -08:00
|
|
|
return jsonable_encoder(obj)
|
2020-10-16 16:55:33 +08:00
|
|
|
|
|
|
|
|
|
2022-10-22 20:59:37 +04:00
|
|
|
def object_hook(obj: Any) -> Any:
|
2020-10-16 16:55:33 +08:00
|
|
|
_spec_type = obj.get("_spec_type")
|
|
|
|
|
if not _spec_type:
|
|
|
|
|
return obj
|
|
|
|
|
|
|
|
|
|
if _spec_type in CONVERTERS:
|
2022-10-22 20:59:37 +04:00
|
|
|
return CONVERTERS[_spec_type](obj["val"]) # type: ignore
|
2020-10-16 16:55:33 +08:00
|
|
|
else:
|
|
|
|
|
raise TypeError("Unknown {}".format(_spec_type))
|
|
|
|
|
|
2020-08-26 18:04:57 +08:00
|
|
|
|
|
|
|
|
class Coder:
|
|
|
|
|
@classmethod
|
2022-10-22 21:06:38 +04:00
|
|
|
def encode(cls, value: Any) -> str:
|
2020-08-26 18:04:57 +08:00
|
|
|
raise NotImplementedError
|
|
|
|
|
|
|
|
|
|
@classmethod
|
2022-11-05 13:45:16 +04:00
|
|
|
def decode(cls, value: str) -> Any:
|
2020-08-26 18:04:57 +08:00
|
|
|
raise NotImplementedError
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class JsonCoder(Coder):
|
|
|
|
|
@classmethod
|
2022-10-22 20:59:37 +04:00
|
|
|
def encode(cls, value: Any) -> str:
|
2020-10-16 16:55:33 +08:00
|
|
|
return json.dumps(value, cls=JsonEncoder)
|
2020-08-26 18:04:57 +08:00
|
|
|
|
|
|
|
|
@classmethod
|
2022-11-05 13:45:16 +04:00
|
|
|
def decode(cls, value: str) -> str:
|
2020-10-16 16:55:33 +08:00
|
|
|
return json.loads(value, object_hook=object_hook)
|
2020-08-26 18:04:57 +08:00
|
|
|
|
|
|
|
|
|
|
|
|
|
class PickleCoder(Coder):
|
|
|
|
|
@classmethod
|
2022-10-22 21:06:38 +04:00
|
|
|
def encode(cls, value: Any) -> str:
|
2022-09-28 17:37:05 +08:00
|
|
|
if isinstance(value, TemplateResponse):
|
|
|
|
|
value = value.body
|
2022-11-05 13:45:16 +04:00
|
|
|
return codecs.encode(pickle.dumps(value), "base64").decode()
|
2020-08-26 18:04:57 +08:00
|
|
|
|
|
|
|
|
@classmethod
|
2022-11-05 13:45:16 +04:00
|
|
|
def decode(cls, value: str) -> Any:
|
|
|
|
|
return pickle.loads(codecs.decode(value.encode(), "base64")) # nosec:B403,B301
|