allow to set exported properties in code

Actually, only property values set in the configuration can
be exported, as values equal to the default are not exported.
For this, the mechanism of overwriting properties by class attributes
has to be modified.

Change-Id: I4388d1fbb36393e863556fbbc8df800dd4800c87
Reviewed-on: https://forge.frm2.tum.de/review/c/sine2020/secop/playground/+/22161
Tested-by: JenkinsCodeReview <bjoern_pedersen@frm2.tum.de>
Reviewed-by: Markus Zolliker <markus.zolliker@psi.ch>
This commit is contained in:
2020-01-10 10:56:56 +01:00
parent 8466a159fe
commit 2d98fe8812
3 changed files with 17 additions and 25 deletions

View File

@ -37,7 +37,7 @@ class Property:
otherwise, this is optional in which case the default value is applied.
All values MUST pass the datatype.
'''
# note: this is inteded to be used on base classes.
# note: this is intended to be used on base classes.
# the VALUES of the properties are on the instances!
def __init__(self, description, datatype, default=None, extname='', export=False, mandatory=None, settable=True):
if not callable(datatype):
@ -102,29 +102,24 @@ class PropertyMeta(type):
for base in reversed(bases):
properties.update(getattr(base, "properties", {}))
# update with properties from new class
aprops = attrs.get('properties', {})
properties.update(aprops)
properties.update(attrs.get('properties', {}))
newtype.properties = properties
# generate getters
for k in properties:
for k, po in properties.items():
def getter(self, pname=k):
val = self.__class__.properties[pname].default
return self.properties.get(pname, val)
if k in attrs and not isinstance(attrs[k], property):
if callable(attrs[k]):
raise ProgrammingError('%r: property %r collides with method'
% (newtype, k))
if k in aprops:
# this property was defined in the same class
raise ProgrammingError('%r: name collision with property %r'
% (newtype, k))
# assume the attribute value should be assigned to the property
# store the attribute value for putting on the instance later
try:
# make a copy first!
prop = Property(**newtype.properties[k].__dict__)
prop.default = prop.datatype(attrs[k])
newtype.properties[k] = prop
# for inheritance reasons, it seems best to store it as a renamed attribute
setattr(newtype, '_initProp_' + k, po.datatype(attrs[k]))
except BadValueError:
raise ProgrammingError('%r: property %r can not be set to %r'
% (newtype, k, attrs[k]))
@ -144,7 +139,10 @@ class HasProperties(metaclass=PropertyMeta):
self.properties = {}
# pre-init with properties default value (if any)
for pn, po in self.__class__.properties.items():
if not po.mandatory:
value = getattr(self, '_initProp_' + pn, self)
if value is not self: # property value was given as attribute
self.properties[pn] = value
elif not po.mandatory:
self.properties[pn] = po.default
def checkProperties(self):

View File

@ -127,8 +127,6 @@ class Main(Communicator):
class PpmsMixin(HasIodev, Module):
properties = {
'iodev': Attached(),
'general_stop': Property('respect general stop', datatype=BoolType(),
export=True, default=True)
}
pollerClass = Poller
@ -439,6 +437,11 @@ class Temp(PpmsMixin, Drivable):
14: [Status.ERROR, 'can not complete'],
15: [Status.ERROR, 'general failure'],
}
properties = {
'iodev': Attached(),
'general_stop': Property('respect general stop', datatype=BoolType(),
export=True, default=True)
}
channel = 'temp'
_stopped = False

View File

@ -133,15 +133,6 @@ def test_Property_override():
pass
assert 'collides with method' in str(e.value)
with pytest.raises(ProgrammingError) as e:
class cy(c): # pylint: disable=unused-variable
properties = {
'b' : Property('', FloatRange(), 3.14),
}
b = 1.5
assert 'name collision with property' in str(e.value)
with pytest.raises(ProgrammingError) as e:
class cz(c): # pylint: disable=unused-variable
a = 's'