removed old style syntax
- removed secop/metaclass.py - moved code from ModuleMeta to modules.HasAccessibles.__init_subclass__ - reworked properties: assignment obj.property = value now always allowed - reworked Parameters and Command to be true descriptors - Command must now be solely used as decorator - renamed 'usercommand' to 'Command' - command methods no longer start with 'do_' - reworked mechanism to determine accessible order: the attribute paramOrder, if given, determines order of accessibles + fixed some issues makeing the IDE more happy + simplified code for StatusType and added a test for it Change-Id: I8045cf38ee6f4d4862428272df0b12a7c8abaca7 Reviewed-on: https://forge.frm2.tum.de/review/c/sine2020/secop/playground/+/25049 Tested-by: Jenkins Automated Tests <pedersen+jenkins@frm2.tum.de> Reviewed-by: Enrico Faulhaber <enrico.faulhaber@frm2.tum.de> Reviewed-by: Markus Zolliker <markus.zolliker@psi.ch>
This commit is contained in:
@ -25,7 +25,7 @@
|
||||
# no fixtures needed
|
||||
import pytest
|
||||
|
||||
from secop.datatypes import ArrayOf, BLOBType, BoolType, \
|
||||
from secop.datatypes import ArrayOf, BLOBType, BoolType, Enum, StatusType, \
|
||||
DataType, EnumType, FloatRange, IntRange, ProgrammingError, ConfigError, \
|
||||
ScaledInteger, StringType, TextType, StructOf, TupleOf, get_datatype, CommandType
|
||||
|
||||
@ -359,6 +359,7 @@ def test_BoolType():
|
||||
# pylint: disable=unexpected-keyword-arg
|
||||
BoolType(unit='K')
|
||||
|
||||
|
||||
def test_ArrayOf():
|
||||
# test constructor catching illegal arguments
|
||||
with pytest.raises(ValueError):
|
||||
@ -478,6 +479,14 @@ def test_Command():
|
||||
'result':{'type': 'int', 'min':-3, 'max':3}}
|
||||
|
||||
|
||||
def test_StatusType():
|
||||
status_codes = Enum('Status', IDLE=100, WARN=200, BUSY=300, ERROR=400)
|
||||
dt = StatusType(status_codes)
|
||||
assert dt.IDLE == status_codes.IDLE
|
||||
assert dt.ERROR == status_codes.ERROR
|
||||
assert dt._enum == status_codes
|
||||
|
||||
|
||||
def test_get_datatype():
|
||||
with pytest.raises(ValueError):
|
||||
get_datatype(1)
|
||||
|
@ -107,15 +107,11 @@ def test_IOHandler():
|
||||
|
||||
|
||||
class Module1(Module):
|
||||
properties = {
|
||||
'channel': Property('the channel', IntRange(), default=3),
|
||||
'loop': Property('the loop', IntRange(), default=2),
|
||||
}
|
||||
parameters = {
|
||||
'simple': Parameter('a readonly', FloatRange(), default=0.77, handler=group1),
|
||||
'real': Parameter('a float value', FloatRange(), default=12.3, handler=group2, readonly=False),
|
||||
'text': Parameter('a string value', StringType(), default='x', handler=group2, readonly=False),
|
||||
}
|
||||
channel = Property('the channel', IntRange(), default=3)
|
||||
loop = Property('the loop', IntRange(), default=2)
|
||||
simple = Parameter('a readonly', FloatRange(), default=0.77, handler=group1)
|
||||
real = Parameter('a float value', FloatRange(), default=12.3, handler=group2, readonly=False)
|
||||
text = Parameter('a string value', StringType(), default='x', handler=group2, readonly=False)
|
||||
|
||||
def sendRecv(self, command):
|
||||
assert data.pop('command') == command
|
||||
@ -196,6 +192,4 @@ def test_IOHandler():
|
||||
with pytest.raises(ProgrammingError): # can not use a handler for different modules
|
||||
# pylint: disable=unused-variable
|
||||
class Module2(Module):
|
||||
parameters = {
|
||||
'simple': Parameter('a readonly', FloatRange(), default=0.77, handler=group1),
|
||||
}
|
||||
simple = Parameter('a readonly', FloatRange(), default=0.77, handler=group1)
|
||||
|
@ -22,14 +22,14 @@
|
||||
# *****************************************************************************
|
||||
"""test data types."""
|
||||
|
||||
# no fixtures needed
|
||||
#import pytest
|
||||
|
||||
import threading
|
||||
import pytest
|
||||
|
||||
from secop.datatypes import BoolType, FloatRange, StringType
|
||||
from secop.modules import Communicator, Drivable, Module
|
||||
from secop.params import Command, Override, Parameter, usercommand
|
||||
from secop.params import Command, Parameter
|
||||
from secop.poller import BasicPoller
|
||||
from secop.errors import ProgrammingError
|
||||
|
||||
|
||||
class DispatcherStub:
|
||||
@ -64,30 +64,27 @@ def test_Communicator():
|
||||
assert event.is_set() # event should be set immediately
|
||||
|
||||
|
||||
def test_ModuleMeta():
|
||||
def test_ModuleMagic():
|
||||
class Newclass1(Drivable):
|
||||
parameters = {
|
||||
'pollinterval': Override(reorder=True),
|
||||
'param1' : Parameter('param1', datatype=BoolType(), default=False),
|
||||
'param2': Parameter('param2', datatype=FloatRange(unit='Ohm'), default=True),
|
||||
"cmd": Command('stuff', argument=BoolType(), result=BoolType())
|
||||
}
|
||||
commands = {
|
||||
# intermixing parameters with commands is not recommended,
|
||||
# but acceptable for influencing the order
|
||||
'a1': Parameter('a1', datatype=BoolType(), default=False),
|
||||
'a2': Parameter('a2', datatype=BoolType(), default=True),
|
||||
'value': Override(datatype=StringType(), default='first'),
|
||||
'cmd2': Command('another stuff', argument=BoolType(), result=BoolType()),
|
||||
}
|
||||
param1 = Parameter('param1', datatype=BoolType(), default=False)
|
||||
param2 = Parameter('param2', datatype=FloatRange(unit='Ohm'), default=True)
|
||||
|
||||
@Command(argument=BoolType(), result=BoolType())
|
||||
def cmd(self, arg):
|
||||
"""stuff"""
|
||||
return not arg
|
||||
|
||||
a1 = Parameter('a1', datatype=BoolType(), default=False)
|
||||
a2 = Parameter('a2', datatype=BoolType(), default=True)
|
||||
value = Parameter(datatype=StringType(), default='first')
|
||||
|
||||
@Command(argument=BoolType(), result=BoolType())
|
||||
def cmd2(self, arg):
|
||||
"""another stuff"""
|
||||
return not arg
|
||||
|
||||
pollerClass = BasicPoller
|
||||
|
||||
def do_cmd(self, arg):
|
||||
return not arg
|
||||
|
||||
def do_cmd2(self, arg):
|
||||
return not arg
|
||||
|
||||
def read_param1(self):
|
||||
return True
|
||||
|
||||
@ -103,19 +100,31 @@ def test_ModuleMeta():
|
||||
def read_value(self):
|
||||
return 'second'
|
||||
|
||||
with pytest.raises(ProgrammingError):
|
||||
class Mod1(Module): # pylint: disable=unused-variable
|
||||
def do_this(self): # old style command
|
||||
pass
|
||||
|
||||
# first inherited accessibles, then Overrides with reorder=True and new accessibles
|
||||
sortcheck1 = ['value', 'status', 'target', 'pollinterval',
|
||||
with pytest.raises(ProgrammingError):
|
||||
class Mod2(Module): # pylint: disable=unused-variable
|
||||
param = Parameter(), # pylint: disable=trailing-comma-tuple
|
||||
|
||||
|
||||
# first inherited accessibles
|
||||
sortcheck1 = ['value', 'status', 'pollinterval', 'target', 'stop',
|
||||
'param1', 'param2', 'cmd', 'a1', 'a2', 'cmd2']
|
||||
|
||||
class Newclass2(Newclass1):
|
||||
parameters = {
|
||||
'cmd2': Override('another stuff'),
|
||||
'value': Override(datatype=FloatRange(unit='deg'), reorder=True),
|
||||
'a1': Override(datatype=FloatRange(unit='$/s'), reorder=True, readonly=False),
|
||||
'b2': Parameter('<b2>', datatype=BoolType(), default=True,
|
||||
poll=True, readonly=False, initwrite=True),
|
||||
}
|
||||
paramOrder = 'param1', 'param2', 'cmd', 'value'
|
||||
|
||||
@Command(description='another stuff')
|
||||
def cmd2(self, arg):
|
||||
return arg
|
||||
|
||||
value = Parameter(datatype=FloatRange(unit='deg'))
|
||||
a1 = Parameter(datatype=FloatRange(unit='$/s'), readonly=False)
|
||||
b2 = Parameter('<b2>', datatype=BoolType(), default=True,
|
||||
poll=True, readonly=False, initwrite=True)
|
||||
|
||||
def write_a1(self, value):
|
||||
self._a1_written = value
|
||||
@ -128,47 +137,15 @@ def test_ModuleMeta():
|
||||
def read_value(self):
|
||||
return 0
|
||||
|
||||
sortcheck2 = ['status', 'target', 'pollinterval',
|
||||
'param1', 'param2', 'cmd', 'a2', 'cmd2', 'value', 'a1', 'b2']
|
||||
|
||||
# check consistency of new syntax:
|
||||
class Testclass1(Drivable):
|
||||
pollinterval = Parameter(reorder=True)
|
||||
param1 = Parameter('param1', datatype=BoolType(), default=False)
|
||||
param2 = Parameter('param2', datatype=FloatRange(unit='Ohm'), default=True)
|
||||
|
||||
@usercommand(BoolType(), BoolType())
|
||||
def cmd(self, arg):
|
||||
"""stuff"""
|
||||
return not arg
|
||||
|
||||
a1 = Parameter('a1', datatype=BoolType(), default=False)
|
||||
a2 = Parameter('a2', datatype=BoolType(), default=True)
|
||||
value = Parameter(datatype=StringType(), default='first')
|
||||
|
||||
@usercommand(BoolType(), BoolType())
|
||||
def cmd2(self, arg):
|
||||
"""another stuff"""
|
||||
return not arg
|
||||
|
||||
class Testclass2(Testclass1):
|
||||
cmd2 = Command('another stuff')
|
||||
value = Parameter(datatype=FloatRange(unit='deg'), reorder=True)
|
||||
a1 = Parameter(datatype=FloatRange(unit='$/s'), reorder=True, readonly=False)
|
||||
b2 = Parameter('<b2>', datatype=BoolType(), default=True,
|
||||
poll=True, readonly=False, initwrite=True)
|
||||
|
||||
for old, new in (Newclass1, Testclass1), (Newclass2, Testclass2):
|
||||
assert len(old.accessibles) == len(new.accessibles)
|
||||
for (oname, oobj), (nname, nobj) in zip(old.accessibles.items(), new.accessibles.items()):
|
||||
assert oname == nname
|
||||
assert oobj.for_export() == nobj.for_export()
|
||||
# first inherited items not mentioned, then the ones mentioned in paramOrder, then the other new ones
|
||||
sortcheck2 = ['status', 'pollinterval', 'target', 'stop',
|
||||
'a1', 'a2', 'cmd2', 'param1', 'param2', 'cmd', 'value', 'b2']
|
||||
|
||||
logger = LoggerStub()
|
||||
updates = {}
|
||||
srv = ServerStub(updates)
|
||||
|
||||
params_found = set() # set of instance accessibles
|
||||
params_found = set() # set of instance accessibles
|
||||
objects = []
|
||||
|
||||
for newclass, sortcheck in [(Newclass1, sortcheck1), (Newclass2, sortcheck2)]:
|
||||
@ -176,15 +153,11 @@ def test_ModuleMeta():
|
||||
o2 = newclass('o2', logger, {'.description':''}, srv)
|
||||
for obj in [o1, o2]:
|
||||
objects.append(obj)
|
||||
ctr_found = set()
|
||||
for n, o in obj.accessibles.items():
|
||||
for o in obj.accessibles.values():
|
||||
# check that instance accessibles are unique objects
|
||||
assert o not in params_found
|
||||
params_found.add(o)
|
||||
assert o.ctr not in ctr_found
|
||||
ctr_found.add(o.ctr)
|
||||
check_order = [(obj.accessibles[n].ctr, n) for n in sortcheck]
|
||||
assert check_order == sorted(check_order)
|
||||
assert list(obj.accessibles) == sortcheck
|
||||
|
||||
# check for inital updates working properly
|
||||
o1 = Newclass1('o1', logger, {'.description':''}, srv)
|
||||
@ -246,7 +219,7 @@ def test_ModuleMeta():
|
||||
assert acs is not None
|
||||
else: # do not check object or mixin
|
||||
acs = {}
|
||||
for n, o in acs.items():
|
||||
for o in acs.values():
|
||||
# check that class accessibles are not reused as instance accessibles
|
||||
assert o not in params_found
|
||||
|
||||
|
@ -25,68 +25,78 @@
|
||||
# no fixtures needed
|
||||
import pytest
|
||||
|
||||
from secop.datatypes import BoolType, IntRange
|
||||
from secop.params import Command, Override, Parameter, Parameters
|
||||
from secop.datatypes import BoolType, IntRange, FloatRange
|
||||
from secop.params import Command, Parameter
|
||||
from secop.modules import HasAccessibles
|
||||
from secop.errors import ProgrammingError
|
||||
|
||||
|
||||
def test_Command():
|
||||
cmd = Command('do_something')
|
||||
assert cmd.description == 'do_something'
|
||||
assert cmd.ctr
|
||||
assert cmd.argument is None
|
||||
assert cmd.result is None
|
||||
assert cmd.for_export() == {'datainfo': {'type': 'command'},
|
||||
'description': 'do_something'}
|
||||
class Mod(HasAccessibles):
|
||||
@Command()
|
||||
def cmd(self):
|
||||
"""do something"""
|
||||
@Command(IntRange(-9,9), result=IntRange(-1,1), description='do some other thing')
|
||||
def cmd2(self):
|
||||
pass
|
||||
|
||||
cmd = Command('do_something', argument=IntRange(-9,9), result=IntRange(-1,1))
|
||||
assert cmd.description
|
||||
assert isinstance(cmd.argument, IntRange)
|
||||
assert isinstance(cmd.result, IntRange)
|
||||
assert cmd.for_export() == {'datainfo': {'type': 'command', 'argument': {'type': 'int', 'min':-9, 'max':9},
|
||||
'result': {'type': 'int', 'min':-1, 'max':1}},
|
||||
'description': 'do_something'}
|
||||
assert cmd.exportProperties() == {'datainfo': {'type': 'command', 'argument': {'type': 'int', 'max': 9, 'min': -9},
|
||||
'result': {'type': 'int', 'max': 1, 'min': -1}},
|
||||
'description': 'do_something'}
|
||||
assert Mod.cmd.description == 'do something'
|
||||
assert Mod.cmd.argument is None
|
||||
assert Mod.cmd.result is None
|
||||
assert Mod.cmd.for_export() == {'datainfo': {'type': 'command'},
|
||||
'description': 'do something'}
|
||||
|
||||
assert Mod.cmd2.description == 'do some other thing'
|
||||
assert isinstance(Mod.cmd2.argument, IntRange)
|
||||
assert isinstance(Mod.cmd2.result, IntRange)
|
||||
assert Mod.cmd2.for_export() == {'datainfo': {'type': 'command', 'argument': {'type': 'int', 'min': -9, 'max': 9},
|
||||
'result': {'type': 'int', 'min': -1, 'max': 1}},
|
||||
'description': 'do some other thing'}
|
||||
assert Mod.cmd2.exportProperties() == {'datainfo': {'type': 'command', 'argument': {'type': 'int', 'max': 9, 'min': -9},
|
||||
'result': {'type': 'int', 'max': 1, 'min': -1}},
|
||||
'description': 'do some other thing'}
|
||||
|
||||
|
||||
def test_Parameter():
|
||||
p1 = Parameter('description1', datatype=IntRange(), default=0)
|
||||
p2 = Parameter('description2', datatype=IntRange(), constant=1)
|
||||
assert p1 != p2
|
||||
assert p1.ctr != p2.ctr
|
||||
class Mod(HasAccessibles):
|
||||
p1 = Parameter('desc1', datatype=FloatRange(), default=0)
|
||||
p2 = Parameter('desc2', datatype=FloatRange(), default=0, readonly=True)
|
||||
p3 = Parameter('desc3', datatype=FloatRange(), default=0, readonly=False)
|
||||
p4 = Parameter('desc4', datatype=FloatRange(), constant=1)
|
||||
assert repr(Mod.p1) != repr(Mod.p3)
|
||||
assert id(Mod.p1.datatype) != id(Mod.p2.datatype)
|
||||
assert Mod.p1.exportProperties() == {'datainfo': {'type': 'double'}, 'description': 'desc1', 'readonly': True}
|
||||
assert Mod.p2.exportProperties() == {'datainfo': {'type': 'double'}, 'description': 'desc2', 'readonly': True}
|
||||
assert Mod.p3.exportProperties() == {'datainfo': {'type': 'double'}, 'description': 'desc3', 'readonly': False}
|
||||
assert Mod.p4.exportProperties() == {'datainfo': {'type': 'double'}, 'description': 'desc4', 'readonly': True,
|
||||
'constant': 1.0}
|
||||
p3 = Mod.p1.copy()
|
||||
assert id(p3) != id(Mod.p1)
|
||||
assert repr(Mod.p1) == repr(p3)
|
||||
|
||||
with pytest.raises(ProgrammingError):
|
||||
Parameter(None, datatype=float, inherit=False)
|
||||
p3 = p1.copy()
|
||||
assert p1.ctr == p3.ctr
|
||||
p3.ctr = p1.ctr # manipulate ctr for next line
|
||||
assert repr(p1) == repr(p3)
|
||||
assert p1.datatype != p2.datatype
|
||||
|
||||
|
||||
def test_Override():
|
||||
p = Parameter('description1', datatype=BoolType, default=False)
|
||||
class Base(HasAccessibles):
|
||||
p1 = Parameter('description1', datatype=BoolType, default=False)
|
||||
p2 = Parameter('description1', datatype=BoolType, default=False)
|
||||
p3 = Parameter('description1', datatype=BoolType, default=False)
|
||||
|
||||
o = Override(default=True, reorder=True)
|
||||
q = o.apply(p)
|
||||
qctr = q.ctr
|
||||
assert q.ctr > p.ctr # reorder=True: take ctr from override object
|
||||
assert q != p
|
||||
assert qctr == o.apply(p).ctr # do not create a new ctr when applied again
|
||||
class Mod(Base):
|
||||
p1 = Parameter(default=True)
|
||||
p2 = Parameter() # override without change
|
||||
|
||||
o2 = Override(default=True)
|
||||
q2 = o2.apply(p)
|
||||
assert q2.ctr == p.ctr # reorder=False: take ctr from inherited param
|
||||
assert q2 != p
|
||||
assert repr(q2) != repr(p)
|
||||
assert Mod.p1 != Base.p1
|
||||
assert Mod.p2 != Base.p2
|
||||
assert Mod.p3 == Base.p3
|
||||
|
||||
q3 = Override().apply(p) # Override without change
|
||||
assert id(q2) != id(p) # must be a new object
|
||||
assert repr(q3) == repr(p) # but must be a clone
|
||||
assert id(Mod.p2) != id(Base.p2) # must be a new object
|
||||
assert repr(Mod.p2) == repr(Base.p2) # but must be a clone
|
||||
|
||||
|
||||
def test_Parameters():
|
||||
ps = Parameters(dict(p1=Parameter('p1', datatype=BoolType, default=True)))
|
||||
ps['p2'] = Parameter('p2', datatype=BoolType, default=True, export=True)
|
||||
assert ps['_p2'].export == '_p2'
|
||||
def test_Export():
|
||||
class Mod:
|
||||
param = Parameter('description1', datatype=BoolType, default=False)
|
||||
assert Mod.param.export == '_param'
|
||||
|
@ -24,38 +24,58 @@
|
||||
import pytest
|
||||
|
||||
from secop.datatypes import IntRange, StringType, FloatRange, ValueType
|
||||
from secop.errors import ProgrammingError, ConfigError
|
||||
from secop.properties import Property, Properties, HasProperties
|
||||
from secop.errors import ProgrammingError, ConfigError, BadValueError
|
||||
from secop.properties import Property, HasProperties
|
||||
|
||||
# args are: datatype, default, extname, export, mandatory, settable
|
||||
|
||||
def Prop(*args, name=None, **kwds):
|
||||
# collect the args for Property
|
||||
return name, args, kwds
|
||||
|
||||
|
||||
# Property(description, datatype, default, ...)
|
||||
V_test_Property = [
|
||||
[(StringType(), 'default', 'extname', False, False),
|
||||
dict(default='default', extname='extname', export=True, mandatory=False)],
|
||||
[(IntRange(), '42', '_extname', False, True),
|
||||
dict(default=42, extname='_extname', export=True, mandatory=True)],
|
||||
[(IntRange(), '42', '_extname', True, False),
|
||||
dict(default=42, extname='_extname', export=True, mandatory=False)],
|
||||
[(IntRange(), 42, '_extname', True, True),
|
||||
dict(default=42, extname='_extname', export=True, mandatory=True)],
|
||||
[(IntRange(), 0, '', True, True),
|
||||
dict(default=0, extname='', export=True, mandatory=True)],
|
||||
[(IntRange(), 0, '', True, False),
|
||||
dict(default=0, extname='', export=True, mandatory=False)],
|
||||
[(IntRange(), 0, '', False, True),
|
||||
dict(default=0, extname='', export=False, mandatory=True)],
|
||||
[(IntRange(), 0, '', False, False),
|
||||
dict(default=0, extname='', export=False, mandatory=False)],
|
||||
[(IntRange(), None, '', None),
|
||||
dict(default=0, extname='', export=False, mandatory=True)], # mandatory not given, no default -> mandatory
|
||||
[(ValueType(), 1, '', False),
|
||||
dict(default=1, extname='', export=False, mandatory=False)], # mandatory not given, default given -> NOT mandatory
|
||||
[Prop(StringType(), 'default', extname='extname', mandatory=False),
|
||||
dict(default='default', extname='extname', export=True, mandatory=False)
|
||||
],
|
||||
[Prop(IntRange(), '42', export=True, name='custom', mandatory=True),
|
||||
dict(default=42, extname='_custom', export=True, mandatory=True),
|
||||
],
|
||||
[Prop(IntRange(), '42', export=True, name='name'),
|
||||
dict(default=42, extname='_name', export=True, mandatory=False)
|
||||
],
|
||||
[Prop(IntRange(), 42, '_extname', mandatory=True),
|
||||
dict(default=42, extname='_extname', export=True, mandatory=True)
|
||||
],
|
||||
[Prop(IntRange(), 0, export=True, mandatory=True),
|
||||
dict(default=0, extname='', export=True, mandatory=True)
|
||||
],
|
||||
[Prop(IntRange(), 0, export=True, mandatory=False),
|
||||
dict(default=0, extname='', export=True, mandatory=False)
|
||||
],
|
||||
[Prop(IntRange(), 0, export=False, mandatory=True),
|
||||
dict(default=0, extname='', export=False, mandatory=True)
|
||||
],
|
||||
[Prop(IntRange(), 0, export=False, mandatory=False),
|
||||
dict(default=0, extname='', export=False, mandatory=False)
|
||||
],
|
||||
[Prop(IntRange()),
|
||||
dict(default=0, extname='', export=False, mandatory=True) # mandatory not given, no default -> mandatory
|
||||
],
|
||||
[Prop(ValueType(), 1),
|
||||
dict(default=1, extname='', export=False, mandatory=False) # mandatory not given, default given -> NOT mandatory
|
||||
],
|
||||
]
|
||||
@pytest.mark.parametrize('args, check', V_test_Property)
|
||||
def test_Property(args, check):
|
||||
p = Property('', *args)
|
||||
@pytest.mark.parametrize('propargs, check', V_test_Property)
|
||||
def test_Property(propargs, check):
|
||||
name, args, kwds = propargs
|
||||
p = Property('', *args, **kwds)
|
||||
if name:
|
||||
p.__set_name__(None, name)
|
||||
result = {k: getattr(p, k) for k in check}
|
||||
assert result == check
|
||||
|
||||
|
||||
def test_Property_basic():
|
||||
with pytest.raises(TypeError):
|
||||
# pylint: disable=no-value-for-parameter
|
||||
@ -67,47 +87,47 @@ def test_Property_basic():
|
||||
Property('', 1)
|
||||
Property('', IntRange(), '42', 'extname', False, False)
|
||||
|
||||
|
||||
def test_Properties():
|
||||
p = Properties()
|
||||
with pytest.raises(ProgrammingError):
|
||||
p[1] = 2
|
||||
p['a'] = Property('', IntRange(), '42', export=True)
|
||||
assert p['a'].default == 42
|
||||
assert p['a'].export is True
|
||||
assert p['a'].extname == '_a'
|
||||
with pytest.raises(ProgrammingError):
|
||||
p['a'] = 137
|
||||
with pytest.raises(ProgrammingError):
|
||||
del p[1]
|
||||
with pytest.raises(ProgrammingError):
|
||||
del p['a']
|
||||
p['a'] = Property('', IntRange(), 0, export=False)
|
||||
assert p['a'].default == 0
|
||||
assert p['a'].export is False
|
||||
assert p['a'].extname == ''
|
||||
class Cls(HasProperties):
|
||||
aa = Property('', IntRange(0, 99), '42', export=True)
|
||||
bb = Property('', IntRange(), 0, export=False)
|
||||
|
||||
assert Cls.aa.default == 42
|
||||
assert Cls.aa.export is True
|
||||
assert Cls.aa.extname == '_aa'
|
||||
|
||||
cc = Cls()
|
||||
with pytest.raises(BadValueError):
|
||||
cc.aa = 137
|
||||
|
||||
assert Cls.bb.default == 0
|
||||
assert Cls.bb.export is False
|
||||
assert Cls.bb.extname == ''
|
||||
|
||||
|
||||
class c(HasProperties):
|
||||
properties = {
|
||||
'a' : Property('', IntRange(), 1),
|
||||
}
|
||||
# properties
|
||||
a = Property('', IntRange(), 1)
|
||||
|
||||
|
||||
class cl(c):
|
||||
properties = {
|
||||
'a' : Property('', IntRange(), 3),
|
||||
'b' : Property('', FloatRange(), 3.14),
|
||||
'minabc': Property('', IntRange(), 8),
|
||||
'maxabc': Property('', IntRange(), 9),
|
||||
'minx': Property('', IntRange(), 2),
|
||||
'maxy': Property('', IntRange(), 1),
|
||||
}
|
||||
# properties
|
||||
a = Property('', IntRange(), 3)
|
||||
b = Property('', FloatRange(), 3.14)
|
||||
minabc = Property('', IntRange(), 8)
|
||||
maxabc = Property('', IntRange(), 9)
|
||||
minx = Property('', IntRange(), 2)
|
||||
maxy = Property('', IntRange(), 1)
|
||||
|
||||
|
||||
def test_HasProperties():
|
||||
o = c()
|
||||
assert o.properties['a'] == 1
|
||||
assert o.a == 1
|
||||
o = cl()
|
||||
assert o.properties['a'] == 3
|
||||
assert o.properties['b'] == 3.14
|
||||
assert o.a == 3
|
||||
assert o.b == 3.14
|
||||
|
||||
|
||||
def test_Property_checks():
|
||||
o = c()
|
||||
@ -119,6 +139,7 @@ def test_Property_checks():
|
||||
with pytest.raises(ConfigError):
|
||||
o.checkProperties()
|
||||
|
||||
|
||||
def test_Property_override():
|
||||
o1 = c()
|
||||
class co(c):
|
||||
@ -131,10 +152,10 @@ def test_Property_override():
|
||||
class cx(c): # pylint: disable=unused-variable
|
||||
def a(self):
|
||||
pass
|
||||
assert 'collides with method' in str(e.value)
|
||||
assert 'collides with' 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)
|
||||
assert 'can not set' in str(e.value)
|
||||
|
Reference in New Issue
Block a user