fix property inheritance

Change-Id: If01b98fc38c730e73799b43c78838afee6cf6058
This commit is contained in:
2021-10-06 08:33:52 +02:00
parent e38cd11bfe
commit 929f55055e

View File

@ -50,15 +50,6 @@ class HasDescriptors(metaclass=HasDescriptorMeta):
if bad: if bad:
raise ProgrammingError('misplaced trailing comma after %s.%s' % (cls.__name__, '/'.join(bad))) raise ProgrammingError('misplaced trailing comma after %s.%s' % (cls.__name__, '/'.join(bad)))
@classmethod
def filterDescriptors(cls, filter_type):
res = {}
for name in dir(cls):
desc = getattr(cls, name, None)
if isinstance(desc, filter_type):
res[name] = desc
return res
UNSET = object() # an unset value, not even None UNSET = object() # an unset value, not even None
@ -110,9 +101,9 @@ class Property:
self.name = name self.name = name
if self.export and not self.extname: if self.export and not self.extname:
self.extname = '_' + name self.extname = '_' + name
if self.description == '_':
# the programmer indicates, that the name is already speaking for itself def copy(self):
self.description = name.replace('_', ' ') return type(self)(**self.__dict__)
def __repr__(self): def __repr__(self):
extras = ['default=%s' % repr(self.default)] extras = ['default=%s' % repr(self.default)]
@ -131,6 +122,12 @@ class Property:
class HasProperties(HasDescriptors): class HasProperties(HasDescriptors):
"""mixin for classes with properties
- properties are collected in cls.propertyDict
- bare values overriding properties should be kept as properties
- include also attributes of type Property on base classes not inheriting HasProperties
"""
propertyValues = None propertyValues = None
def __init__(self): def __init__(self):
@ -152,25 +149,24 @@ class HasProperties(HasDescriptors):
if bad: if bad:
raise ProgrammingError('misplaced trailing comma after %s.%s' % (cls.__name__, '/'.join(bad))) raise ProgrammingError('misplaced trailing comma after %s.%s' % (cls.__name__, '/'.join(bad)))
properties = {} properties = {}
for base in cls.__bases__: # using cls.__bases__ and base.propertyDict for this would fail on some multiple inheritance cases
properties.update(getattr(base, 'propertyDict', {})) for base in reversed(cls.__mro__):
properties.update(cls.filterDescriptors(Property)) properties.update({k: v for k, v in base.__dict__.items() if isinstance(v, Property)})
cls.propertyDict = properties cls.propertyDict = properties
# treat overriding properties with bare values # treat overriding properties with bare values
for pn, po in properties.items(): for pn, po in properties.items():
value = getattr(cls, pn, po) value = getattr(cls, pn, po)
if not isinstance(value, Property): # attribute is a bare value if not isinstance(value, Property): # attribute is a bare value
po = Property(**po.__dict__) po = po.copy()
try: try:
po.value = po.datatype(value) po.value = po.datatype(value)
except BadValueError: except BadValueError:
for base in cls.__bases__: if pn in properties:
if pn in getattr(base, 'propertyDict', {}): if callable(value):
if callable(value): raise ProgrammingError('method %s.%s collides with property of %s' %
raise ProgrammingError('method %s.%s collides with property of %s' % (cls.__name__, pn, base.__name__)) from None
(cls.__name__, pn, base.__name__)) from None raise ProgrammingError('can not set property %s.%s to %r' %
raise ProgrammingError('can not set property %s.%s to %r' % (cls.__name__, pn, value)) from None
(cls.__name__, pn, value)) from None
cls.propertyDict[pn] = po cls.propertyDict[pn] = po
def checkProperties(self): def checkProperties(self):
@ -180,8 +176,7 @@ class HasProperties(HasDescriptors):
try: try:
self.propertyValues[pn] = po.datatype(self.propertyValues[pn]) self.propertyValues[pn] = po.datatype(self.propertyValues[pn])
except (KeyError, BadValueError): except (KeyError, BadValueError):
name = getattr(self, 'name', self.__class__.__name__) raise ConfigError('%s needs a value of type %r!' % (pn, po.datatype)) from None
raise ConfigError('%s.%s needs a value of type %r!' % (name, pn, po.datatype)) from None
for pn, po in self.propertyDict.items(): for pn, po in self.propertyDict.items():
if pn.startswith('min'): if pn.startswith('min'):
maxname = 'max' + pn[3:] maxname = 'max' + pn[3:]