schon/evibes/utils/parsers.py

70 lines
2.2 KiB
Python

from typing import Any, Optional
import orjson
from django.conf import settings
from drf_orjson_renderer.parsers import ORJSONParser
from rest_framework.exceptions import ParseError
def _underscoreize_key(key: str, no_underscore_before_number: bool = False) -> str:
if not isinstance(key, str) or not key:
return key
out = []
prev_lower = False
for ch in key:
if ch.isupper():
if out and (
prev_lower or (not no_underscore_before_number and out[-1].isdigit())
):
out.append("_")
out.append(ch.lower())
prev_lower = False
elif ch.isdigit():
if out and not no_underscore_before_number and not out[-1].isdigit():
out.append("_")
out.append(ch)
prev_lower = False
else:
out.append(ch)
prev_lower = True
return "".join(out)
def underscoreize(obj: Any, no_underscore_before_number: bool = False) -> Any:
if isinstance(obj, dict):
return {
(
_underscoreize_key(k, no_underscore_before_number)
if isinstance(k, str)
else k
): underscoreize(v, no_underscore_before_number)
for k, v in obj.items()
}
if isinstance(obj, (list, tuple)):
t = type(obj)
return t(underscoreize(v, no_underscore_before_number) for v in obj)
return obj
class CamelCaseParser(ORJSONParser):
def parse(
self, stream, media_type: Optional[Any] = None, parser_context: Any = None
) -> Any:
parser_context = parser_context or {}
encoding: str = parser_context.get("encoding", settings.DEFAULT_CHARSET)
try:
raw = stream.read().decode(encoding)
data = orjson.loads(raw)
except ValueError as exc:
raise ParseError(f"JSON parse error - {exc}") from exc
no_us_before_number = (
getattr(settings, "REST_FRAMEWORK", {})
.get("JSON_UNDERSCOREIZE", {})
.get("no_underscore_before_number", False)
)
return underscoreize(data, no_underscore_before_number=no_us_before_number)
__all__ = ["CamelCaseParser", "underscoreize"]