Source code for serde.tags
"""
This module contains tag classes for use with `Models <serde.Model>`.
"""
from collections import OrderedDict
from serde import fields, utils
from serde.exceptions import ValidationError
[docs]class Tag(fields._Base):
"""
A tag field for a `Model <serde.Model>`.
Args:
recurse (bool): whether to recurse subclasses when calculating model
variants.
serializers (list): a list of serializer functions taking the value to
serialize as an argument. The functions need to raise an `Exception`
if they fail. These serializer functions will be applied before the
primary serializer on this tag.
deserializers (list): a list of deserializer functions taking the value
to deserialize as an argument. The functions need to raise an
`Exception` if they fail. These deserializer functions will be
applied after the primary deserializer on this tag.
"""
def __init__(self, recurse=False, serializers=None, deserializers=None):
"""
Create a new `Tag`.
"""
super(Tag, self).__init__(serializers=serializers, deserializers=deserializers)
self.recurse = recurse
[docs] def variants(self):
"""
Returns a list of variants for the bound model class.
"""
base_cls = self.__model__
if self.recurse:
variants = utils.subclasses(base_cls)
else:
variants = base_cls.__subclasses__()
if not base_cls.__abstract__:
variants = [base_cls] + variants
return variants
[docs] def lookup_tag(self, variant):
"""
Get the tag value for the given model variant.
Args:
variant (Model): the model class.
Returns:
str: the corresponding tag value.
"""
return f'{variant.__module__}.{variant.__qualname__}'
[docs] def lookup_variant(self, tag):
"""
Get the variant for the given tag value.
Args:
tag: the tag value.
Returns:
Model: the corresponding Model class.
"""
for variant in self.variants():
if self.serialize(variant) == tag:
return variant
def serialize(self, value):
"""
Serialize a Model variant into a tag value.
"""
return self.lookup_tag(value)
def deserialize(self, value):
"""
Deserialize a tag value into a Model variant.
"""
variant = self.lookup_variant(value)
if not variant:
raise ValidationError('no variant found', value=value)
return variant
[docs]class External(Tag):
"""
A tag to externally tag `~serde.Model` data.
"""
def _serialize_with(self, model, d):
"""
Serialize the model variant by externally tagging the given dictionary.
"""
variant = model.__class__
d = OrderedDict([(self._serialize(variant), d)])
return d
def _deserialize_with(self, model, d):
"""
Deserialize the model variant from an externally tagged dictionary.
"""
try:
tag = next(iter(d))
except StopIteration:
raise ValidationError('missing data, expected externally tagged data')
model.__class__ = self._deserialize(tag)
return model, d[tag]
[docs]class Internal(Tag):
"""
A tag to internally tag `~serde.Model` data.
Args:
tag: the key to use when serializing the model variant's tag.
"""
def __init__(self, tag='tag', **kwargs):
"""
Create a new `Internal`.
"""
super(Internal, self).__init__(**kwargs)
self.tag = tag
def _serialize_with(self, model, d):
"""
Serialize the model variant by internally tagging the given dictionary.
"""
variant = model.__class__
d[self.tag] = self._serialize(variant)
return d
def _deserialize_with(self, model, d):
"""
Deserialize the model variant from an internally tagged dictionary.
"""
try:
tag = d[self.tag]
except KeyError:
raise ValidationError(f'missing data, expected tag {self.tag!r}')
model.__class__ = self._deserialize(tag)
return model, d
[docs]class Adjacent(Tag):
"""
A tag to adjacently tag `~serde.Model` data.
Args:
tag: the key to use when serializing the model variant's tag.
content: the key to use when serializing the model variant's data.
"""
def __init__(self, tag='tag', content='content', **kwargs):
"""
Create a new `Adjacent`.
"""
super(Adjacent, self).__init__(**kwargs)
self.tag = tag
self.content = content
def _serialize_with(self, model, d):
"""
Serialize the model variant by adjacently tagging the given dictionary.
"""
variant = model.__class__
d = OrderedDict([(self.tag, self._serialize(variant)), (self.content, d)])
return d
def _deserialize_with(self, model, d):
"""
Deserialize the model variant from an adjacently tagged dictionary.
"""
try:
tag = d[self.tag]
except KeyError:
raise ValidationError(f'missing data, expected tag {self.tag!r}')
try:
content = d[self.content]
except KeyError:
raise ValidationError(f'missing data, expected content {self.content!r}')
model.__class__ = self._deserialize(tag)
return model, content
__all__ = [name for name, obj in globals().items() if utils.is_subclass(obj, Tag)]