|
|
@@ -21,13 +21,29 @@ class Component(ABC):
|
|
|
|
|
|
class ObjectDetailsPanelMeta(ABCMeta):
|
|
|
|
|
|
- def __new__(mcls, name, bases, attrs):
|
|
|
- # Collect all declared attributes
|
|
|
- attrs['_attrs'] = {}
|
|
|
- for key, val in list(attrs.items()):
|
|
|
- if isinstance(val, Attr):
|
|
|
- attrs['_attrs'][key] = val
|
|
|
- return super().__new__(mcls, name, bases, attrs)
|
|
|
+ def __new__(mcls, name, bases, namespace, **kwargs):
|
|
|
+ declared = {}
|
|
|
+
|
|
|
+ # Walk MRO parents (excluding `object`) for declared attributes
|
|
|
+ for base in reversed([b for b in bases if hasattr(b, "_attrs")]):
|
|
|
+ for key, attr in getattr(base, '_attrs', {}).items():
|
|
|
+ if key not in declared:
|
|
|
+ declared[key] = attr
|
|
|
+
|
|
|
+ # Add local declarations in the order they appear in the class body
|
|
|
+ for key, attr in namespace.items():
|
|
|
+ if isinstance(attr, Attr):
|
|
|
+ declared[key] = attr
|
|
|
+
|
|
|
+ namespace['_attrs'] = declared
|
|
|
+
|
|
|
+ # Remove Attrs from the class namespace to keep things tidy
|
|
|
+ local_items = [key for key, attr in namespace.items() if isinstance(attr, Attr)]
|
|
|
+ for key in local_items:
|
|
|
+ namespace.pop(key)
|
|
|
+
|
|
|
+ cls = super().__new__(mcls, name, bases, namespace, **kwargs)
|
|
|
+ return cls
|
|
|
|
|
|
|
|
|
class ObjectPanel(Component, metaclass=ObjectDetailsPanelMeta):
|
|
|
@@ -56,7 +72,7 @@ class ObjectPanel(Component, metaclass=ObjectDetailsPanelMeta):
|
|
|
return self.render()
|
|
|
|
|
|
|
|
|
-class NestedGroupObjectPanel(ObjectPanel):
|
|
|
+class NestedGroupObjectPanel(ObjectPanel, metaclass=ObjectDetailsPanelMeta):
|
|
|
name = attrs.TextAttr('name', label=_('Name'))
|
|
|
description = attrs.TextAttr('description', label=_('Description'))
|
|
|
parent = attrs.NestedObjectAttr('parent', label=_('Parent'), linkify=True)
|