-
Notifications
You must be signed in to change notification settings - Fork 286
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
7ee1abf
commit 7ef3ef3
Showing
2 changed files
with
404 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,115 @@ | ||
import abc | ||
import collections | ||
|
||
from rlp.exceptions import ( | ||
ListSerializationError, | ||
ObjectSerializationError, | ||
ListDeserializationError, | ||
ObjectDeserializationError, | ||
) | ||
|
||
from .lists import ( | ||
List, | ||
) | ||
|
||
|
||
class FieldsMeta: | ||
fields = None | ||
|
||
@classmethod | ||
def get_field_names(cls): | ||
if cls.fields: | ||
names, _ = zip(*cls.fields) | ||
else: | ||
names = tuple() | ||
return names | ||
|
||
@classmethod | ||
def get_sedes(cls): | ||
if cls.fields: | ||
_, sedes = zip(*cls.fields) | ||
else: | ||
sedes = tuple() | ||
return List(sedes) | ||
|
||
|
||
class SerializableMeta(abc.ABCMeta): | ||
def __new__(cls, name, bases, attrs): | ||
fields = attrs.pop('fields', tuple()) | ||
|
||
fields_meta = type('FieldsMeta', (FieldsMeta,), {'fields': fields}) | ||
attrs['_meta'] = fields_meta | ||
return super(SerializableMeta, cls).__new__(cls, name, bases, attrs) | ||
|
||
|
||
class Serializable(collections.Sequence, metaclass=SerializableMeta): | ||
def __init__(self, *args, **kwargs): | ||
|
||
# check keyword arguments are known | ||
field_set = set(self._meta.get_field_names()) | ||
|
||
# set positional arguments | ||
for field, arg in zip(self._meta.get_field_names(), args): | ||
setattr(self, field, arg) | ||
field_set.remove(field) | ||
|
||
# set keyword arguments, if not already set | ||
for field, value in kwargs.items(): | ||
if field in field_set: | ||
setattr(self, field, value) | ||
field_set.remove(field) | ||
else: | ||
raise AttributeError("Attempt to set unknown field: {0}".format(field)) | ||
|
||
if len(field_set) != 0: | ||
raise TypeError('Not all fields initialized. Missing: %r' % field_set) | ||
|
||
def __iter__(self): | ||
for field in self._meta.get_field_names(): | ||
yield getattr(self, field) | ||
|
||
def __getitem__(self, idx): | ||
if isinstance(idx, int): | ||
field = self._meta.get_field_names()[idx] | ||
return getattr(self, field) | ||
else: | ||
raise NotImplementedError("not implemented") | ||
|
||
def __len__(self): | ||
return len(self._meta.fields) | ||
|
||
@classmethod | ||
def get_sedes(cls): | ||
return cls._meta.get_sedes() | ||
|
||
@classmethod | ||
def serialize(cls, obj): | ||
try: | ||
result = cls.get_sedes().serialize(obj) | ||
except ListSerializationError as e: | ||
raise ObjectSerializationError(obj=obj, sedes=cls, list_exception=e) | ||
else: | ||
return result | ||
|
||
@classmethod | ||
def deserialize(cls, serial, exclude=None, mutable=False, **kwargs): | ||
try: | ||
values = cls.get_sedes().deserialize(serial) | ||
except ListDeserializationError as e: | ||
raise ObjectDeserializationError(serial=serial, sedes=cls, list_exception=e) | ||
|
||
params = { | ||
field: value | ||
for field, value | ||
in zip(cls._meta.get_field_names(), values) | ||
if exclude is None or field not in exclude | ||
} | ||
obj = cls(**dict(list(params.items()) + list(kwargs.items()))) | ||
|
||
# TODO: mutability | ||
return obj | ||
|
||
@classmethod | ||
def exclude(cls, excluded_fields): | ||
"""Create a new sedes considering only a reduced set of fields.""" | ||
raise NotImplementedError("Not yet implemented") |
Oops, something went wrong.