Skip to content

Instantly share code, notes, and snippets.

@aodag
Created February 11, 2020 08:42
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save aodag/3c8facc9ffcf51f1c56bd144cd112682 to your computer and use it in GitHub Desktop.
Save aodag/3c8facc9ffcf51f1c56bd144cd112682 to your computer and use it in GitHub Desktop.
extension to convert dataclasses with cattrs.
import dataclasses
from .converters import Converter
def _is_dataclasses_class(obj):
return dataclasses.is_dataclass(obj)
class DataclassesConverter(Converter):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.register_unstructure_hook_func(
_is_dataclasses_class, self._unstructure_dataclasses)
self.register_structure_hook_func(
_is_dataclasses_class, self._structure_dataclasses)
def _unstructure_dataclasses(self, obj):
"""Our version of `attrs.asdict`, so we can call back to us."""
fields = dataclasses.fields(obj)
dispatch = self._unstructure_func.dispatch
rv = self._dict_factory()
for f in fields:
name = f.name
v = getattr(obj, name)
rv[name] = dispatch(v.__class__)(v)
return rv
def _structure_dataclasses(self, obj, cl):
"""Instantiate an attrs class from a mapping (dict)."""
# For public use.
conv_obj = {} # Start with a fresh dict, to ignore extra keys.
dispatch = self._structure_func.dispatch
for f in dataclasses.fields(cl): # type: ignore
# We detect the type by metadata.
type_ = f.type
name = f.name
try:
val = obj[name]
except KeyError:
continue
if name[0] == "_":
name = name[1:]
conv_obj[name] = (
dispatch(type_)(val, type_) if type_ is not None else val
)
return cl(**conv_obj) # type: ignore
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment