| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235 |
- """Collection of functions for building custom `json_default` functions.
- In general functions come in pairs of `use_x_default` and `x_default`, where the former is used
- to determine if you should call the latter.
- Most `use_x_default` functions also act as a [`TypeGuard`](https://mypy.readthedocs.io/en/stable/type_narrowing.html#user-defined-type-guards).
- """
- ### IMPORTS
- ### ============================================================================
- ## Future
- from __future__ import annotations
- ## Standard Library
- import base64
- import dataclasses
- import datetime
- import enum
- from types import TracebackType
- from typing import Any, TypeGuard
- import traceback
- import uuid
- ## Installed
- ## Application
- ### FUNCTIONS
- ### ============================================================================
- def unknown_default(obj: Any) -> str:
- """Backup default function for any object type.
- Will attempt to use `str` or `repr`. If both functions error will return
- the string `"__could_not_encode__"`.
- Args:
- obj: object to handle
- """
- try:
- return str(obj)
- except Exception: # pylint: disable=broad-exception-caught
- pass
- try:
- return repr(obj)
- except Exception: # pylint: disable=broad-exception-caught
- pass
- return "__could_not_encode__"
- ## Types
- ## -----------------------------------------------------------------------------
- def use_type_default(obj: Any) -> TypeGuard[type]:
- """Default check function for `type` objects (aka classes)."""
- return isinstance(obj, type)
- def type_default(obj: type) -> str:
- """Default function for `type` objects.
- Args:
- obj: object to handle
- """
- return obj.__name__
- ## Dataclasses
- ## -----------------------------------------------------------------------------
- def use_dataclass_default(obj: Any) -> bool:
- """Default check function for dataclass instances"""
- return dataclasses.is_dataclass(obj) and not isinstance(obj, type)
- def dataclass_default(obj) -> dict[str, Any]:
- """Default function for dataclass instances
- Args:
- obj: object to handle
- """
- return dataclasses.asdict(obj)
- ## Dates and Times
- ## -----------------------------------------------------------------------------
- def use_time_default(obj: Any) -> TypeGuard[datetime.time]:
- """Default check function for `datetime.time` instances"""
- return isinstance(obj, datetime.time)
- def time_default(obj: datetime.time) -> str:
- """Default function for `datetime.time` instances
- Args:
- obj: object to handle
- """
- return obj.isoformat()
- def use_date_default(obj: Any) -> TypeGuard[datetime.date]:
- """Default check function for `datetime.date` instances"""
- return isinstance(obj, datetime.date)
- def date_default(obj: datetime.date) -> str:
- """Default function for `datetime.date` instances
- Args:
- obj: object to handle
- """
- return obj.isoformat()
- def use_datetime_default(obj: Any) -> TypeGuard[datetime.datetime]:
- """Default check function for `datetime.datetime` instances"""
- return isinstance(obj, datetime.datetime)
- def datetime_default(obj: datetime.datetime) -> str:
- """Default function for `datetime.datetime` instances
- Args:
- obj: object to handle
- """
- return obj.isoformat()
- def use_datetime_any(obj: Any) -> TypeGuard[datetime.time | datetime.date | datetime.datetime]:
- """Default check function for `datetime` related instances"""
- return isinstance(obj, (datetime.time, datetime.date, datetime.datetime))
- def datetime_any(obj: datetime.time | datetime.date | datetime.date) -> str:
- """Default function for `datetime` related instances
- Args:
- obj: object to handle
- """
- return obj.isoformat()
- ## Exception and Tracebacks
- ## -----------------------------------------------------------------------------
- def use_exception_default(obj: Any) -> TypeGuard[BaseException]:
- """Default check function for exception instances.
- Exception classes are not treated specially and should be handled by the
- `[use_]type_default` functions.
- """
- return isinstance(obj, BaseException)
- def exception_default(obj: BaseException) -> str:
- """Default function for exception instances
- Args:
- obj: object to handle
- """
- return f"{obj.__class__.__name__}: {obj}"
- def use_traceback_default(obj: Any) -> TypeGuard[TracebackType]:
- """Default check function for tracebacks"""
- return isinstance(obj, TracebackType)
- def traceback_default(obj: TracebackType) -> str:
- """Default function for tracebacks
- Args:
- obj: object to handle
- """
- return "".join(traceback.format_tb(obj)).strip()
- ## Enums
- ## -----------------------------------------------------------------------------
- def use_enum_default(obj: Any) -> TypeGuard[enum.Enum | enum.EnumMeta]:
- """Default check function for enums.
- Supports both enum classes and enum values.
- """
- return isinstance(obj, (enum.Enum, enum.EnumMeta))
- def enum_default(obj: enum.Enum | enum.EnumMeta) -> Any | list[Any]:
- """Default function for enums.
- Supports both enum classes and enum values.
- Args:
- obj: object to handle
- """
- if isinstance(obj, enum.Enum):
- return obj.value
- return [e.value for e in obj] # type: ignore[var-annotated]
- ## UUIDs
- ## -----------------------------------------------------------------------------
- def use_uuid_default(obj: Any) -> TypeGuard[uuid.UUID]:
- """Default check function for `uuid.UUID` instances"""
- return isinstance(obj, uuid.UUID)
- def uuid_default(obj: uuid.UUID) -> str:
- """Default function for `uuid.UUID` instances
- Formats the UUID using "hyphen" format.
- Args:
- obj: object to handle
- """
- return str(obj)
- ## Bytes
- ## -----------------------------------------------------------------------------
- def use_bytes_default(obj: Any) -> TypeGuard[bytes | bytearray]:
- """Default check function for bytes"""
- return isinstance(obj, (bytes, bytearray))
- def bytes_default(obj: bytes | bytearray, url_safe: bool = True) -> str:
- """Default function for bytes
- Args:
- obj: object to handle
- url_safe: use URL safe base 64 character set.
- Returns:
- The byte data as a base 64 string.
- """
- if url_safe:
- return base64.urlsafe_b64encode(obj).decode("utf8")
- return base64.b64encode(obj).decode("utf8")
|