Source code for cattrs.strategies._unions
from collections import defaultdict
from typing import Any, Callable, Dict, Optional, Type
from attrs import NOTHING
from cattrs import Converter
__all__ = ["default_tag_generator", "configure_tagged_union"]
def default_tag_generator(typ: Type) -> str:
"""Return the class name."""
return typ.__name__
[docs]def configure_tagged_union(
union: Any,
converter: Converter,
tag_generator: Callable[[Type], str] = default_tag_generator,
tag_name: str = "_type",
default: Optional[Type] = NOTHING,
) -> None:
"""
Configure the converter so that `union` (which should be a union) is
un/structured with the help of an additional piece of data in the
unstructured payload, the tag.
:param converter: The converter to apply the strategy to.
:param tag_generator: A `tag_generator` function is used to map each
member of the union to a tag, which is then included in the
unstructured payload. The default tag generator returns the name of
the class.
:param tag_name: The key under which the tag will be set in the
unstructured payload. By default, `'_type'`.
:param default: An optional class to be used if the tag information
is not present when structuring.
The tagged union strategy currently only works with the dict
un/structuring base strategy.
.. versionadded:: 23.1.0
"""
args = union.__args__
tag_to_hook = {}
exact_cl_unstruct_hooks = {}
for cl in args:
tag = tag_generator(cl)
struct_handler = converter._structure_func.dispatch(cl)
unstruct_handler = converter._unstructure_func.dispatch(cl)
def structure_union_member(val: dict, _cl=cl, _h=struct_handler) -> cl:
return _h(val, _cl)
def unstructure_union_member(val: union, _h=unstruct_handler) -> dict:
return _h(val)
tag_to_hook[tag] = structure_union_member
exact_cl_unstruct_hooks[cl] = unstructure_union_member
cl_to_tag = {cl: tag_generator(cl) for cl in args}
if default is not NOTHING:
default_handler = converter._structure_func.dispatch(default)
def structure_default(val: dict, _cl=default, _h=default_handler):
return _h(val, _cl)
tag_to_hook = defaultdict(lambda: structure_default, tag_to_hook)
cl_to_tag = defaultdict(lambda: default, cl_to_tag)
def unstructure_tagged_union(
val: union,
_exact_cl_unstruct_hooks=exact_cl_unstruct_hooks,
_cl_to_tag=cl_to_tag,
_tag_name=tag_name,
) -> Dict:
res = _exact_cl_unstruct_hooks[val.__class__](val)
res[_tag_name] = _cl_to_tag[val.__class__]
return res
if default is NOTHING:
def structure_tagged_union(
val: dict, _, _tag_to_cl=tag_to_hook, _tag_name=tag_name
) -> union:
return _tag_to_cl[val[_tag_name]](val)
else:
def structure_tagged_union(
val: dict,
_,
_tag_to_hook=tag_to_hook,
_tag_name=tag_name,
_dh=default_handler,
_default=default,
) -> union:
if _tag_name in val:
return _tag_to_hook[val[_tag_name]](val)
else:
return _dh(val, _default)
converter.register_unstructure_hook(union, unstructure_tagged_union)
converter.register_structure_hook(union, structure_tagged_union)