Source code for dataclass_wizard.loader_selection

from typing import Callable, Collection, Optional

from .class_helper import (get_meta, CLASS_TO_LOAD_FUNC,
                           CLASS_TO_LOADER, CLASS_TO_V1_LOADER,
                           set_class_loader, create_new_class, CLASS_TO_DUMP_FUNC, CLASS_TO_V1_DUMPER, set_class_dumper,
                           CLASS_TO_DUMPER)
from .constants import _LOAD_HOOKS, _DUMP_HOOKS
from .type_def import T, JSONObject


[docs] def asdict(o: T, *, cls=None, dict_factory=dict, exclude: 'Collection[str] | None' = None, **kwargs) -> JSONObject: # noinspection PyUnresolvedReferences """Return the fields of a dataclass instance as a new dictionary mapping field names to field values. Example usage: @dataclass class C: x: int y: int c = C(1, 2) assert asdict(c) == {'x': 1, 'y': 2} When directly invoking this function, an optional Meta configuration for the dataclass can be specified via ``DumpMeta``; by default, this will apply recursively to any nested dataclasses. Here's a sample usage of this below:: >>> DumpMeta(key_transform='CAMEL').bind_to(MyClass) >>> asdict(MyClass(my_str="value")) If given, 'dict_factory' will be used instead of built-in dict. The function applies recursively to field values that are dataclass instances. This will also look into built-in containers: tuples, lists, and dicts. """ # This likely won't be needed, as ``dataclasses.fields`` already has this # check. # if not _is_dataclass_instance(obj): # raise TypeError("asdict() should be called on dataclass instances") cls = cls or type(o) try: dump = CLASS_TO_DUMP_FUNC[cls] except KeyError: dump = _get_dump_fn_for_dataclass(cls) return dump(o, dict_factory, exclude, **kwargs)
[docs] def fromdict(cls: type[T], d: JSONObject) -> T: """ Converts a Python dictionary object to a dataclass instance. Iterates over each dataclass field recursively; lists, dicts, and nested dataclasses will likewise be initialized as expected. When directly invoking this function, an optional Meta configuration for the dataclass can be specified via ``LoadMeta``; by default, this will apply recursively to any nested dataclasses. Here's a sample usage of this below:: >>> LoadMeta(key_transform='CAMEL').bind_to(MyClass) >>> fromdict(MyClass, {"myStr": "value"}) """ try: load = CLASS_TO_LOAD_FUNC[cls] except KeyError: load = _get_load_fn_for_dataclass(cls) return load(d)
[docs] def fromlist(cls: type[T], list_of_dict: list[JSONObject]) -> list[T]: """ Converts a Python list object to a list of dataclass instances. Iterates over each dataclass field recursively; lists, dicts, and nested dataclasses will likewise be initialized as expected. """ try: load = CLASS_TO_LOAD_FUNC[cls] except KeyError: load = _get_load_fn_for_dataclass(cls) return [load(d) for d in list_of_dict]
def _get_load_fn_for_dataclass(cls: type[T], v1=None) -> Callable[[JSONObject], T]: meta = get_meta(cls) if v1 is None: v1 = getattr(meta, 'v1', False) if v1: from .v1.loaders import load_func_for_dataclass as V1_load_func_for_dataclass # noinspection PyTypeChecker load = V1_load_func_for_dataclass(cls) else: from .loaders import load_func_for_dataclass load = load_func_for_dataclass(cls) # noinspection PyTypeChecker return load def _get_dump_fn_for_dataclass(cls: type[T], v1=None) -> Callable[[JSONObject], T]: if v1 is None: v1 = getattr(get_meta(cls), 'v1', False) if v1: from .v1.dumpers import dump_func_for_dataclass as V1_dump_func_for_dataclass # noinspection PyTypeChecker dump = V1_dump_func_for_dataclass(cls) else: from .dumpers import dump_func_for_dataclass dump = dump_func_for_dataclass(cls) # noinspection PyTypeChecker return dump
[docs] def get_dumper(class_or_instance=None, create=True, base_cls: T = None, v1: Optional[bool] = None) -> type[T]: """ Get the dumper for the class, using the following logic: * Return the class if it's already a sub-class of :class:`DumpMixin` * If `create` is enabled (which is the default), a new sub-class of :class:`DumpMixin` for the class will be generated and cached on the initial run. * Otherwise, we will return the base loader, :class:`DumpMixin`, which can potentially be shared by more than one dataclass. """ if v1 is None: v1 = getattr(get_meta(class_or_instance), 'v1', False) if v1: cls_to_dumper = CLASS_TO_V1_DUMPER if base_cls is None: from .v1.dumpers import DumpMixin as V1_DumpMixin base_cls = V1_DumpMixin else: cls_to_dumper = CLASS_TO_DUMPER if base_cls is None: from .dumpers import DumpMixin base_cls = DumpMixin try: return cls_to_dumper[class_or_instance] except KeyError: # TODO figure out type errors if hasattr(class_or_instance, _DUMP_HOOKS): return set_class_dumper( cls_to_dumper, class_or_instance, class_or_instance) elif create: cls_loader = create_new_class(class_or_instance, (base_cls, )) return set_class_dumper( cls_to_dumper, class_or_instance, cls_loader) return set_class_dumper( cls_to_dumper, class_or_instance, base_cls)
[docs] def get_loader(class_or_instance=None, create=True, base_cls: T = None, v1: Optional[bool] = None, env: bool = False) -> type[T]: """ Get the loader for the class, using the following logic: * Return the class if it's already a sub-class of :class:`LoadMixin` * If `create` is enabled (which is the default), a new sub-class of :class:`LoadMixin` for the class will be generated and cached on the initial run. * Otherwise, we will return the base loader, :class:`LoadMixin`, which can potentially be shared by more than one dataclass. """ if v1 is None: v1 = getattr(get_meta(class_or_instance), 'v1', False) if v1: cls_to_loader = CLASS_TO_V1_LOADER if base_cls is None: if env: from .v1._env import LoadMixin as V1_EnvLoadMixin base_cls = V1_EnvLoadMixin else: from .v1.loaders import LoadMixin as V1_LoadMixin base_cls = V1_LoadMixin else: cls_to_loader = CLASS_TO_LOADER if base_cls is None: if env: from .environ.loaders import EnvLoader base_cls = EnvLoader else: from .loaders import LoadMixin base_cls = LoadMixin try: return cls_to_loader[class_or_instance] except KeyError: if hasattr(class_or_instance, _LOAD_HOOKS): return set_class_loader( cls_to_loader, class_or_instance, class_or_instance) elif create: cls_loader = create_new_class(class_or_instance, (base_cls, )) return set_class_loader( cls_to_loader, class_or_instance, cls_loader) return set_class_loader( cls_to_loader, class_or_instance, base_cls)