Override does not change the order of inherited accessibles.

except when explicitely mentioned with reorder=True
improved test_modules for unique accessibles, as this
was related with Accessible.ctr

Change-Id: I61877de9300bb0297c88a6c44bb265c634937856
Reviewed-on: https://forge.frm2.tum.de/review/19693
Tested-by: JenkinsCodeReview <bjoern_pedersen@frm2.tum.de>
Reviewed-by: Enrico Faulhaber <enrico.faulhaber@frm2.tum.de>
This commit is contained in:
zolliker 2019-01-08 14:44:06 +01:00 committed by Enrico Faulhaber
parent 1c33a41748
commit 4f83bc42cc
4 changed files with 75 additions and 32 deletions

View File

@ -214,10 +214,10 @@ class Module(object):
def early_init(self): def early_init(self):
# may be overriden in derived classes to init stuff # may be overriden in derived classes to init stuff
self.log.debug('empty early_init()') self.log.debug('empty %s.early_init()' % self.__class__.__name__)
def init_module(self): def init_module(self):
self.log.debug('empty init_module()') self.log.debug('empty %s.init_module()' % self.__class__.__name__)
def start_module(self, started_callback): def start_module(self, started_callback):
'''runs after init of all modules '''runs after init of all modules
@ -226,7 +226,7 @@ class Module(object):
or, if not implmemented, immediately or, if not implmemented, immediately
''' '''
self.log.debug('empty start_module()') self.log.debug('empty %s.start_module()' % self.__class__.__name__)
started_callback(self) started_callback(self)

View File

@ -48,7 +48,6 @@ class Accessible(CountedObj):
def copy(self): def copy(self):
# return a copy of ourselfs # return a copy of ourselfs
props = self.__dict__.copy() props = self.__dict__.copy()
props.pop('ctr')
return type(self)(**props) return type(self)(**props)
def exported_properties(self): def exported_properties(self):
@ -155,10 +154,12 @@ class Override(CountedObj):
"""Stores the overrides to be applied to a Parameter """Stores the overrides to be applied to a Parameter
note: overrides are applied by the metaclass during class creating note: overrides are applied by the metaclass during class creating
reorder= True: use position of Override instead of inherited for the order
""" """
def __init__(self, description="", **kwds): def __init__(self, description="", reorder=False, **kwds):
super(Override, self).__init__() super(Override, self).__init__()
self.kwds = kwds self.kwds = kwds
self.reorder = reorder
# allow to override description without keyword # allow to override description without keyword
if description: if description:
self.kwds['description'] = description self.kwds['description'] = description
@ -177,7 +178,8 @@ class Override(CountedObj):
raise ProgrammingError( "%s is not a valid %s property" % raise ProgrammingError( "%s is not a valid %s property" %
(key, type(obj).__name__)) (key, type(obj).__name__))
props.update(self.kwds) props.update(self.kwds)
props['ctr'] = self.ctr if self.reorder:
props['ctr'] = self.ctr
return type(obj)(**props) return type(obj)(**props)
else: else:
raise ProgrammingError( raise ProgrammingError(

View File

@ -25,9 +25,9 @@ from __future__ import division, print_function
# no fixtures needed # no fixtures needed
import pytest import pytest
from secop.datatypes import BoolType, EnumType from secop.datatypes import BoolType, EnumType, FloatRange
from secop.metaclass import ModuleMeta from secop.metaclass import ModuleMeta
from secop.modules import Communicator, Drivable, Module, Readable, Writable from secop.modules import Communicator, Drivable, Module
from secop.params import Command, Override, Parameter from secop.params import Command, Override, Parameter
try: try:
@ -60,8 +60,9 @@ def test_Communicator():
q.get() q.get()
def test_ModuleMeta(): def test_ModuleMeta():
newclass = ModuleMeta.__new__(ModuleMeta, 'TestReadable', (Drivable, Writable, Readable, Module), { newclass1 = ModuleMeta.__new__(ModuleMeta, 'TestDrivable', (Drivable,), {
"parameters" : { "parameters" : {
'pollinterval': Override(reorder=True),
'param1' : Parameter('param1', datatype=BoolType(), default=False), 'param1' : Parameter('param1', datatype=BoolType(), default=False),
'param2': Parameter('param2', datatype=BoolType(), default=True), 'param2': Parameter('param2', datatype=BoolType(), default=True),
}, },
@ -83,12 +84,19 @@ def test_ModuleMeta():
"read_value": lambda self, *args: True, "read_value": lambda self, *args: True,
"init": lambda self, *args: [None for self.accessibles['value'].datatype in [EnumType('value', OK=1, Bad=2)]], "init": lambda self, *args: [None for self.accessibles['value'].datatype in [EnumType('value', OK=1, Bad=2)]],
}) })
# every cmd/param has to be collected to accessibles # first inherited accessibles, then Overrides with reorder=True and new accessibles
assert newclass.accessibles sortcheck1 = ['value', 'status', 'target', 'pollinterval',
with pytest.raises(AttributeError): 'param1', 'param2', 'cmd', 'a1', 'a2', 'cmd2']
assert newclass.commands
with pytest.raises(AttributeError): newclass2 = ModuleMeta.__new__(ModuleMeta, 'UpperClass', (newclass1,), {
assert newclass.parameters "accessibles": {
'cmd2': Override('another stuff'),
'a1': Override(datatype=FloatRange(), reorder=True),
'b2': Parameter('a2', datatype=BoolType(), default=True),
},
})
sortcheck2 = ['value', 'status', 'target', 'pollinterval',
'param1', 'param2', 'cmd', 'a2', 'cmd2', 'a1', 'b2']
logger = type('LoggerStub', (object,), dict( logger = type('LoggerStub', (object,), dict(
debug = lambda self, *a: print(*a), debug = lambda self, *a: print(*a),
@ -103,21 +111,45 @@ def test_ModuleMeta():
dispatcher = dispatcher, dispatcher = dispatcher,
))() ))()
o1 = newclass('o1', logger, {}, srv) params_found = set() # set of instance accessibles
o2 = newclass('o2', logger, {}, srv) objects = []
params_found= set()
ctr_found = set() for newclass, sortcheck in [(newclass1, sortcheck1), (newclass2, sortcheck2)]:
for obj in [o1, o2]: o1 = newclass('o1', logger, {}, srv)
for n, o in obj.accessibles.items(): o2 = newclass('o2', logger, {}, srv)
print(n) for obj in [o1, o2]:
objects.append(obj)
ctr_found = set()
for n, o in obj.accessibles.items():
# 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)
# check on the level of classes
# this checks newclass1 too, as it is inherited by newclass2
for baseclass in newclass2.__mro__:
# every cmd/param has to be collected to accessibles
acs = getattr(baseclass, 'accessibles', None)
if issubclass(baseclass, Module):
assert acs is not None
else: # do not check object or mixin
acs = {}
with pytest.raises(AttributeError):
assert baseclass.commands
with pytest.raises(AttributeError):
assert baseclass.parameters
for n, o in acs.items():
# check that class accessibles are not reused as instance accessibles
assert o not in params_found assert o not in params_found
params_found.add(o)
assert o.ctr not in ctr_found for o in objects:
ctr_found.add(o.ctr) o.early_init()
o1.early_init() for o in objects:
o2.early_init() o.init_module()
o1.init_module()
o2.init_module()
q = queue.Queue() q = queue.Queue()
o1.start_module(q.put) for o in objects:
o2.start_module(q.put) o.start_module(q.put)

View File

@ -49,10 +49,19 @@ def test_Parameter():
def test_Override(): def test_Override():
p = Parameter('description1', datatype=BoolType, default=False) p = Parameter('description1', datatype=BoolType, default=False)
o = Override(default=True) o = Override(default=True, reorder=True)
assert o.ctr != p.ctr assert o.ctr != p.ctr
q = o.apply(p) q = o.apply(p)
assert q.ctr == o.ctr # override shall be useable to influence the order, hence copy the ctr value assert q.ctr == o.ctr # override shall be useable to influence the order, hence copy the ctr value
assert q.ctr != p.ctr assert q.ctr != p.ctr
assert o.ctr != p.ctr assert o.ctr != p.ctr
assert q != p assert q != p
p2 = Parameter('description2', datatype=BoolType, default=False)
o2 = Override(default=True)
assert o2.ctr != p2.ctr
q2 = o2.apply(p2)
assert q2.ctr != o2.ctr
assert q2.ctr == p2.ctr
assert o2.ctr != p2.ctr
assert q2 != p2