simple possiblity for overriding properties

When a class attribute is assigned with the same name as a property
from an inherited class, the property default is overridden.
If the type does not match, a ProgrammingError is raised.

Change-Id: Ifdab5c8bbdaae008370a9b297c770f5c04db9311
Reviewed-on: https://forge.frm2.tum.de/review/c/sine2020/secop/playground/+/21989
Reviewed-by: Markus Zolliker <markus.zolliker@psi.ch>
Tested-by: JenkinsCodeReview <bjoern_pedersen@frm2.tum.de>
This commit is contained in:
2019-12-10 17:25:57 +01:00
parent 1521e0a34b
commit f5af074015
2 changed files with 53 additions and 7 deletions

View File

@ -24,7 +24,7 @@
from collections import OrderedDict
from secop.errors import ProgrammingError, ConfigError
from secop.errors import ProgrammingError, ConfigError, BadValueError
# storage for 'properties of a property'
@ -52,8 +52,9 @@ class Property:
self.settable = settable or mandatory # settable means settable from the cfg file
def __repr__(self):
return 'Property(%r, %s, default=%r, extname=%r, export=%r, mandatory=%r)' % (
self.description, self.datatype, self.default, self.extname, self.export, self.mandatory)
return 'Property(%r, %s, default=%r, extname=%r, export=%r, mandatory=%r, settable=%r)' % (
self.description, self.datatype, self.default, self.extname, self.export,
self.mandatory, self.settable)
class Properties(OrderedDict):
@ -100,7 +101,8 @@ class PropertyMeta(type):
for base in reversed(bases):
properties.update(getattr(base, "properties", {}))
# update with properties from new class
properties.update(attrs.get('properties', {}))
aprops = attrs.get('properties', {})
properties.update(aprops)
newtype.properties = properties
# generate getters
@ -108,9 +110,23 @@ class PropertyMeta(type):
def getter(self, pname=k):
val = self.__class__.properties[pname].default
return self.properties.get(pname, val)
if k in attrs:
if not isinstance(attrs[k], property):
raise ProgrammingError('Name collision with property %r' % k)
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
try:
# make a copy first!
prop = Property(**newtype.properties[k].__dict__)
prop.default = prop.datatype(attrs[k])
newtype.properties[k] = prop
except BadValueError:
raise ProgrammingError('%r: property %r can not be set to %r'
% (newtype, k, attrs[k]))
setattr(newtype, k, property(getter))
return newtype

View File

@ -114,6 +114,36 @@ def test_Property_checks():
o.checkProperties()
o = cl()
o.checkProperties()
# test for min/max check
o.setProperty('maxabc', 1)
with pytest.raises(ConfigError):
o.checkProperties()
def test_Property_override():
o1 = c()
class co(c):
a = 3
o2 = co()
assert o1.a == 1
assert o2.a == 3
with pytest.raises(ProgrammingError) as e:
class cx(c): # pylint: disable=unused-variable
def a(self):
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'
assert 'can not be set to' in str(e.value)