ApeWorX / ApeWorX/pyrlp

RLP fields as proper object attributes.

Open
#44 33 comments 1 reaction 0 assignees View on GitHub
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!

![3r7i1c](https://user-images.githubusercontent.com/824194/28537072-e69786ea-7066-11e7-85e6-e740a62ba4ad.jpg)

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

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.