From 2d98fe881243137f005db829c627b5179d2088c1 Mon Sep 17 00:00:00 2001 From: Markus Zolliker Date: Fri, 10 Jan 2020 10:56:56 +0100 Subject: [PATCH] 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 Reviewed-by: Markus Zolliker --- secop/properties.py | 26 ++++++++++++-------------- secop_psi/ppms.py | 7 +++++-- test/test_properties.py | 9 --------- 3 files changed, 17 insertions(+), 25 deletions(-) diff --git a/secop/properties.py b/secop/properties.py index 4d1db43..bd048b1 100644 --- a/secop/properties.py +++ b/secop/properties.py @@ -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): diff --git a/secop_psi/ppms.py b/secop_psi/ppms.py index 7932e68..05f41d0 100755 --- a/secop_psi/ppms.py +++ b/secop_psi/ppms.py @@ -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 diff --git a/test/test_properties.py b/test/test_properties.py index e274961..6c2d226 100644 --- a/test/test_properties.py +++ b/test/test_properties.py @@ -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'