Skip to content
This repository has been archived by the owner on Apr 22, 2024. It is now read-only.

Commit

Permalink
Proposed solution for part of the issue #157.
Browse files Browse the repository at this point in the history
This commit alters the metaclass "MetaStruct". Now it will consider the
existance of a `__ordered__` attribute of the base classes being used
and also will evaluate three new `private` class attributes:
  - remove_attributes;
  - rename_attributes;
  - insert_before.

With these three new attributes now we can have control of class
attributes modifications while inheriting from another class (based on
GenericStruct or GenericMessage).

Usage:

```python
    class MyClassA(GenericStruct):
        attr_0 = UBInt8()
        attr_a = UBInt16()
        attr_z = UBInt8()
        attr_c = UBInt8()

    class MyClassB(MyClassA):
        attr_c = UBInt32()  # This will update the attr_c from parent class
        attr_d = UBInt8()   # This will add a new attribute positioned befor attr_a
                            # according to the defined below
        attr_e = 8          # This will add a new attribute at the end of the list.

        _remove_attributes = ['attr_z']
        _rename_attributes = [('attr_0', 'attr_new_name')]
        _insert_attributes_before = {'attr_d': 'attr_a'}
```

The resulting dictionary of 'MyClassB' will be:

```python
    OrderedDict([('attr_new_name', pyof.foundation.basic_types.UBInt8),
                 ('attr_d', pyof.foundation.basic_types.UBInt8),
                 ('attr_a', pyof.foundation.basic_types.UBInt16),
                 ('attr_c', pyof.foundation.basic_types.UBInt32),
                 ('attr_e', int)])
```

Signed-off-by: Diego Rabatone Oliveira <[email protected]>
  • Loading branch information
Diego Rabatone Oliveira committed Jan 11, 2017
1 parent 558038f commit cd39026
Showing 1 changed file with 92 additions and 5 deletions.
97 changes: 92 additions & 5 deletions pyof/foundation/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -246,11 +246,98 @@ def __prepare__(mcs, name, bases): # pylint: disable=unused-argument

def __new__(mcs, name, bases, classdict):
"""Add ``__ordered__`` attribute with attributes in declared order."""
# Skip methods and private attributes
classdict['__ordered__'] = OrderedDict([(key, type(value)) for
key, value in classdict.items()
if key[0] != '_' and not
hasattr(value, '__call__')])
ordered = None

#: Recovering __ordered__ from the first parent class that have it,
#: if any
for base in bases:
if hasattr(base, '__ordered__'):
# TODO: How to do a "copy from current of version" that get the
# class (value) from the correct pyof version (the same
# as the class being edited/created)?
# This is where we need to do some magic!
#: Try to get the __ordered__ dict from the base (parent) class.
#: If it fails (there is no __ordered__) then an exception is
#: raised
base_ordered = base.__ordered__.copy()

#: List with attributes names to be removed.
remove_attributes = classdict.get('_remove_attributes')

#: List of tuples like (old_name, new_name)
rename_attributes = classdict.get('_rename_attributes')

#: A dict with new_attribute_name as key and valued with the
#: name of the attribute that will be preceeded by this new
#: attribute.
insert_before = classdict.get('_insert_attributes_before')

# Remove attributes marked to be removed, if any to do so
if remove_attributes is not None:
for attr in remove_attributes:
try:
base_ordered.pop(attr)
except KeyError:
pass

# Renaming attributes copied from the parent class
if rename_attributes is not None:
for old_name, new_name in rename_attributes:
if old_name in classdict:
classdict[new_name] = classdict.pop(old_name)
else:
classdict[new_name] = deepcopy(base.__dict__[old_name])
base_ordered = OrderedDict([(new_name, v) if
k == old_name else (k, v)
for k, v in
base_ordered.items()])

# Now let's get the new class attributes.
new_attrs = OrderedDict([(key, type(value)) for
key, value in classdict.items()
if key[0] != '_' and not
hasattr(value, '__call__')])

attrs = list(base_ordered.keys())

# And now lets add these new attributes to the ordered dict,
# considering the insert_before item data.
for attr in list(new_attrs.keys()):
#: If the attribute was redefined, by default we will
#: keep it at the same place it was before. So, we do not
#: need to add it again.
if attr not in attrs:
if insert_before and attr in insert_before:
#: If the attribute must be added before some other
#: attribute, do so.
attrs.insert(attrs.index(insert_before[attr]),
attr)
else:
#: Otherwise append the new attribute into the end
#: of the list
attrs.append(attr)

#: finally creating the ordered dict that will be added on the
#: class.
ordered = OrderedDict()
for attr in attrs:
ordered[attr] = new_attrs.get(attr, base_ordered.get(attr))

#: break the for loop, we are just interested on the first
#: base class that have the __ordered__ dict.
break

if ordered is None:
#: If there was no __ordered__ dict on the parent class, create
#: one with the current class attributes, skipping methods and
#: private attributes
ordered = OrderedDict([(key, type(value)) for
key, value in classdict.items()
if key[0] != '_' and not
hasattr(value, '__call__')])

classdict['__ordered__'] = ordered

return type.__new__(mcs, name, bases, classdict)


Expand Down

1 comment on commit cd39026

@cemsbr
Copy link
Contributor

@cemsbr cemsbr commented on cd39026 Jan 11, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can create a class to manage the ordered attribute updates as well as Enum updates. Thus, the metaclass won't excessive responsibility and it will be easier to test.

Please sign in to comment.