Module xtelligent_serial.registry

Expand source code
from collections.abc import Sequence, Iterable, Mapping
from functools import singledispatch
from inspect import ismethod, isfunction, signature
from typing import Any, Type

from .signatures import JSONSerializable, Serializer, Deserializer

# pylint: disable=redefined-builtin, unused-argument

def iscallable(v):
    return ismethod(v) or isfunction(v)

def props(target):
    return {k: v for k, v in [(k, getattr(target, k)) for k in dir(target) if not k.startswith('_')] if not iscallable(v)}

@singledispatch
def serialize(target) -> JSONSerializable:
    '''Serializes an instance of `t` into raw data (dict, list, str, int, etc.).
    Uses the single dispatch pattern to find implementations of the function for
    return None if target is None else serialize(props(target))
    specific types.'''
    return None if target is None else serialize(props(target))

@serialize.register
def _(target: Mapping) -> JSONSerializable:
    return {k: serialize(v) for k, v in target.items()}

@serialize.register
def _(target: Sequence) -> JSONSerializable:
    return [serialize(item) for item in list(target)]

@serialize.register
def _(target: Iterable) -> JSONSerializable:
    return [serialize(item) for item in target]

@serialize.register
def _(target: int) -> JSONSerializable:
    return target

@serialize.register
def _(target: float) -> JSONSerializable:
    return target

@serialize.register
def _(target: bool) -> JSONSerializable:
    return target

@serialize.register
def _(target: str) -> JSONSerializable:
    return target

TYPEKEY = '?__type__?'

@singledispatch
def fjd(raw_data: JSONSerializable, **kwargs) -> Any:
    if raw_data is None:
        return None
    raise ValueError('No deserialization method for {0}'.format(kwargs.get('type') or type(raw_data)))


def deserialize(t: Type, raw_data: JSONSerializable) -> Any:
    '''Creates an instance of `t` from raw data (dict, list, str, int, etc.).
    Uses a modified single dispatch pattern to find deserializers.
    '''
    if not t:
        raise ValueError('Expected t to be a type')
    func = fjd.dispatch(type(raw_data) if t == object else t)
    if not func:
        raise ValueError(f'No deserialization handler for type {t}')
    if isinstance(raw_data, Mapping): # Assume dict:object, never list!
        return func({**raw_data, TYPEKEY: t}, type=t)
    return func(raw_data, type=t)

@fjd.register
def _(i: int, **kwargs):
    return i
@fjd.register
def _(f: float, **kwargs):
    return f
@fjd.register
def _(s: str, **kwargs):
    return s
@fjd.register
def _(b: bool, **kwargs):
    return b


def register_serializer(type: Type, func: Serializer):
    assert isfunction(func)
    @serialize.register
    def _(target: type) -> JSONSerializable:
        return func(target)

def register_deserializer(type: Type, func: Deserializer):
    assert isfunction(func)
    sig = signature(func)
    def f1(raw_data, **kwargs): # pylint: disable=invalid-name
        return func(raw_data)
    f = func if len(sig.parameters) == 2 else f1
    @fjd.register
    def _ds(raw_data, **kwargs) -> type:
        return f(raw_data, **kwargs)

Functions

def deserialize(t: Type, raw_data: Union[int, float, bool, str, collections.abc.Sequence, collections.abc.Iterable, collections.abc.Mapping]) ‑> Any

Creates an instance of t from raw data (dict, list, str, int, etc.). Uses a modified single dispatch pattern to find deserializers.

Expand source code
def deserialize(t: Type, raw_data: JSONSerializable) -> Any:
    '''Creates an instance of `t` from raw data (dict, list, str, int, etc.).
    Uses a modified single dispatch pattern to find deserializers.
    '''
    if not t:
        raise ValueError('Expected t to be a type')
    func = fjd.dispatch(type(raw_data) if t == object else t)
    if not func:
        raise ValueError(f'No deserialization handler for type {t}')
    if isinstance(raw_data, Mapping): # Assume dict:object, never list!
        return func({**raw_data, TYPEKEY: t}, type=t)
    return func(raw_data, type=t)
def fjd(raw_data: Union[int, float, bool, str, collections.abc.Sequence, collections.abc.Iterable, collections.abc.Mapping], **kwargs) ‑> Any
Expand source code
@singledispatch
def fjd(raw_data: JSONSerializable, **kwargs) -> Any:
    if raw_data is None:
        return None
    raise ValueError('No deserialization method for {0}'.format(kwargs.get('type') or type(raw_data)))
def iscallable(v)
Expand source code
def iscallable(v):
    return ismethod(v) or isfunction(v)
def props(target)
Expand source code
def props(target):
    return {k: v for k, v in [(k, getattr(target, k)) for k in dir(target) if not k.startswith('_')] if not iscallable(v)}
def register_deserializer(type: Type, func: Callable[[Union[int, float, bool, str, collections.abc.Sequence, collections.abc.Iterable, collections.abc.Mapping]], Any])
Expand source code
def register_deserializer(type: Type, func: Deserializer):
    assert isfunction(func)
    sig = signature(func)
    def f1(raw_data, **kwargs): # pylint: disable=invalid-name
        return func(raw_data)
    f = func if len(sig.parameters) == 2 else f1
    @fjd.register
    def _ds(raw_data, **kwargs) -> type:
        return f(raw_data, **kwargs)
def register_serializer(type: Type, func: Callable[[Any], Union[int, float, bool, str, collections.abc.Sequence, collections.abc.Iterable, collections.abc.Mapping]])
Expand source code
def register_serializer(type: Type, func: Serializer):
    assert isfunction(func)
    @serialize.register
    def _(target: type) -> JSONSerializable:
        return func(target)
def serialize(target) ‑> Union[int, float, bool, str, collections.abc.Sequence, collections.abc.Iterable, collections.abc.Mapping]

Serializes an instance of t into raw data (dict, list, str, int, etc.). Uses the single dispatch pattern to find implementations of the function for return None if target is None else serialize(props(target)) specific types.

Expand source code
@singledispatch
def serialize(target) -> JSONSerializable:
    '''Serializes an instance of `t` into raw data (dict, list, str, int, etc.).
    Uses the single dispatch pattern to find implementations of the function for
    return None if target is None else serialize(props(target))
    specific types.'''
    return None if target is None else serialize(props(target))