You cannot select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
	
	
		
			242 lines
		
	
	
		
			6.4 KiB
		
	
	
	
		
			Python
		
	
			
		
		
	
	
			242 lines
		
	
	
		
			6.4 KiB
		
	
	
	
		
			Python
		
	
"""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
 | 
						|
import sys
 | 
						|
from types import TracebackType
 | 
						|
from typing import Any
 | 
						|
import traceback
 | 
						|
import uuid
 | 
						|
 | 
						|
if sys.version_info >= (3, 10):
 | 
						|
    from typing import TypeGuard
 | 
						|
else:
 | 
						|
    from typing_extensions import TypeGuard
 | 
						|
 | 
						|
## 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")
 |