RLP fields as proper object attributes.
- Dominant language
- Python
- Stars
- 103
- Forks
- 69
- Avg merge
- 54m
- Merged PRs (30d)
- 1
Description
### What is the problem
RLP objects are not very good at inheritance.
```python
class MyBaseObject(rlp.Serializable):
fields = (
('field_a', sedes.Binary),
)
class InheritsFromBase(MyBaseObject):
fields = (
('field_a', sedes.Binary), # not DRY, repeated from base class
('another_field', sedes.Binary),
)
```
There is no good *pythonic* way to extend a base class's fields.
### How can we fix it.
Metaclass wizardry!

Much the same way that django models perform magical metaclass voodoo, we can do the same. Implement a metaclass for use that finds all attributes which are valid `sedes` types and does all the fancy stuff that happens within the `__init__` method of `Serializable`. In general, doing away with `Serializable.fields` seems like a nice cleanup.
The example above would look something like:
```python
class MyBaseObject(rlp.Serializable):
field_a = sedes.Binary()
class InheritsFromBase(MyBaseObject):
field_a = sedes.Binary() # required to maintain explicit field ordering.
another_field = sedes.Binary()
```
### Implementation specifics.
#### Field class
We need a `Field` class which we can use to define RLP fields. This should be a thin wrapper of sorts around a `rlp.sedes` object. Ideally, we can have some sort of thin shortcut for converting a `sedes` object to a `Field` object using something like a classmethod `Field.from_sedes(...)`
##### Metaclass
Unless an alternate solution is presented, the `rlp.Serializable` class needs to be converted to use a metaclass. This metaclass will need to introspect the current fields as well as all of the fields for the base class to construct the class.
I suggest this part be done in two steps.
- **Step 1**
The first step would be to have the metaclass construct an `rlp.Serializable` instance with the proper `fields` property, and to allow the existing internals of `rlp.Serializable` to populate the actual attribute setters and getters. Under this approach accessing the field properties from the class would likely result in an `AttributeError` as they would not be constructed until class instantiation.
- **Step 2**
The second step is to invert this, and have the metaclass (or the underlying `Field` class) create these attributes at the class level, likely using descriptors (which the `Field` class could implement). Then, the `fields` property on the class would only be present for backwards compatibility.
#### Field ordering
The field ordering for RLP objects is important. Luckily, this is easy to do using metaclasses.
https://docs.python.org/3/reference/datamodel.html#metaclass-example
However, this gets complicated in cases of inheritance. From the discussion on this issue, we've decided that the best solution to fixing this is to required that either **all** fields or **zero** field be overridden when a class is sub classed.
#### Documentation
The documentation will need to be updated to reflect this new API.
Contributor guide
Assessment
This issue has not been assessed yet.