-
Notifications
You must be signed in to change notification settings - Fork 286
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Rework Serializable class. #62
Rework Serializable class. #62
Conversation
7ef3ef3
to
29cc832
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks good overall, and should allow me to get rid of gsalgado/py-evm@28b9f69#diff-18b46e5ecb111d05094ba0820424e8c7R28
rlp/sedes/serializable.py
Outdated
} | ||
obj = cls(**dict(list(params.items()) + list(kwargs.items()))) | ||
|
||
# TODO: mutability |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is there anything really left todo here?
rlp/sedes/serializable.py
Outdated
raise NotImplementedError("Not yet implemented") | ||
|
||
@property | ||
def is_immutable(self): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We probably don't need this as we can just inline the not self.is_mutable
, no?
rlp/sedes/serializable.py
Outdated
is_mutable = True | ||
|
||
_cached_rlp_value = None | ||
_cached_rlp_key = None |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There's something about having two separate attributes for this that makes me uncomfortable... Did you consider using a dict for that? Maybe _cached_snapshot = {}
, or maybe a _get_cached_rlp(snapshot_key)
method decorated with a lru_cache
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Alright, I've changed to use a class level cache. Each subclass of Serializable
now has it's own cache which is shared across instances of those objects as well as across both the mutable and immutable versions of those objects.
rlp/sedes/serializable.py
Outdated
field = self._meta.field_names[idx] | ||
return getattr(self, field) | ||
else: | ||
raise NotImplementedError("not implemented") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
elif isinstance(idx, str):
return getattr(self, idx)
Seems like a straightforward and nice thing to do here.
rlp/sedes/serializable.py
Outdated
def __eq__(self, other): | ||
if not hasattr(other, 'serialize'): | ||
return False | ||
return self.serialize(self) == other.serialize(other) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not sure this is enough. An rlp with type (bytes32, bytes32) and one with type (int, int) can be trivially made to be equal here, which feels wrong to me.
Maybe test the deserialized values?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Agreed.
Note that this is the current way that
rlp.sedes.Serializable
determines equality:Lines 204 to 208 in 188abd6
def __eq__(self, other): """Two objects are equal, if they are equal after serialization.""" if not hasattr(other.__class__, 'serialize'): return False return self.serialize(self) == other.serialize(other)
I may go back to using a version of the following:
return len(self) == len(other) and all(
compare_eq(left, right)
for left, right
in zip(self, other)
)
Where compare_eq
is a function which handles things like equality between tuples
and lists
with the same values which a naive ==
comparison will return False
.
Performance Numbers: This Branch
Master @ f30fe1e
|
For block importing (i.e. |
66b5d4a
to
738d9da
Compare
738d9da
to
fd14033
Compare
@gsalgado looking at stats, it looks like we have 2x calls to |
@@ -218,7 +222,6 @@ def decode(rlp, sedes=None, strict=True, **kwargs): | |||
obj = sedes.deserialize(item, **kwargs) | |||
if hasattr(obj, '_cached_rlp'): | |||
obj._cached_rlp = rlp | |||
assert not isinstance(obj, Serializable) or not obj.is_mutable() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This was removed in favor of having mutable Serializable
instances automatically invalidate their cache.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sweet
tests/test_serializable.py
Outdated
@@ -29,185 +71,209 @@ class RLPType2(Serializable): | |||
(RLPType2, [RLPType1(8, b'a', (7, ''))], 'field2_2'), | |||
), | |||
) | |||
def test_validation(rlptype, fields, exception_includes): | |||
def test_serializable_initialization_validation(rlptype, fields, exception_includes): | |||
# TODO: expand tests to combine *args and **kwargs instantiation |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Time to do?
assert rlp_obj.is_mutable is True | ||
|
||
# cache should be populated now. | ||
assert encode(rlp_obj, cache=True) == rlp_code |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Does it make sense to turn on caching by default?
Use updated CircleCI images
What was wrong
The
Serializable
class was ready for some cleanup.How was it fixed
__setattr_
and__getattr__
with metaclass mechanics andproperty
based setters and getters.cls.exclude
API.is_mutable
to be property based.make_mutable
API in favor of a copy based API.__iter__
__eq__
implementation to only return true on equality of rich values rather than serialized values.obj[field_name]
andobj[0]
based field lookups.cls.fields
in favor ofcls._meta
which holds field names, sedes and some extra meta info.__init__
argument handling to be a bit more explicit in how it handlesargs
andkwargs
.