fix and improved Attached

- Attached checks now for proper base class
- fixes an error in Attached: attached saved in attachedModules
  dict instead on the Attached object (which sits on the class!)
+ fix: in testonly mode errors must be logged before returning
+ fix: use repr of exception in poll to check for repeated errors

Change-Id: I141fa107fed48e58b55ddf1e071987656c0f618f
Reviewed-on: https://forge.frm2.tum.de/review/c/sine2020/secop/playground/+/27913
Tested-by: Jenkins Automated Tests <pedersen+jenkins@frm2.tum.de>
Reviewed-by: Markus Zolliker <markus.zolliker@psi.ch>
This commit is contained in:
zolliker 2022-03-01 14:37:23 +01:00
parent 8767be2aac
commit fda1939324
3 changed files with 43 additions and 20 deletions

View File

@ -280,6 +280,7 @@ class Module(HasAccessibles):
# reference to the dispatcher (used for sending async updates) # reference to the dispatcher (used for sending async updates)
DISPATCHER = None DISPATCHER = None
attachedModules = None
def __init__(self, name, logger, cfgdict, srv): def __init__(self, name, logger, cfgdict, srv):
# remember the dispatcher object (for the async callbacks) # remember the dispatcher object (for the async callbacks)
@ -649,11 +650,11 @@ class Module(HasAccessibles):
self.log.info('recovered after %d calls to doPoll (%r)', error_count, last_error) self.log.info('recovered after %d calls to doPoll (%r)', error_count, last_error)
last_error = None last_error = None
except Exception as e: except Exception as e:
if type(e) != last_error: if repr(e) != last_error:
error_count = 0 error_count = 0
self.log.error('error in doPoll: %r', e) self.log.error('error in doPoll: %r', e)
error_count += 1 error_count += 1
last_error = e last_error = repr(e)
now = time.time() now = time.time()
# find ONE due slow poll and call it # find ONE due slow poll and call it
loop = True loop = True
@ -803,14 +804,15 @@ class Attached(Property):
assign a module name to this property in the cfg file, assign a module name to this property in the cfg file,
and the server will create an attribute with this module and the server will create an attribute with this module
""" """
module = None def __init__(self, basecls=Module, description='attached module'):
self.basecls = basecls
def __init__(self, description='attached module'):
super().__init__(description, StringType(), mandatory=False) super().__init__(description, StringType(), mandatory=False)
def __get__(self, obj, owner): def __get__(self, obj, owner):
if obj is None: if obj is None:
return self return self
if self.module is None: if obj.attachedModules is None:
self.module = obj.DISPATCHER.get_module(super().__get__(obj, owner)) # return the name of the module (called from Server on startup)
return self.module return super().__get__(obj, owner)
# return the module (called after startup)
return obj.attachedModules[self.name]

View File

@ -30,10 +30,11 @@ import sys
import traceback import traceback
from collections import OrderedDict from collections import OrderedDict
from secop.errors import ConfigError from secop.errors import ConfigError, SECoPError
from secop.lib import formatException, get_class, generalConfig from secop.lib import formatException, get_class, generalConfig
from secop.lib.multievent import MultiEvent from secop.lib.multievent import MultiEvent
from secop.params import PREDEFINED_ACCESSIBLES from secop.params import PREDEFINED_ACCESSIBLES
from secop.modules import Attached
try: try:
from daemon import DaemonContext from daemon import DaemonContext
@ -275,6 +276,23 @@ class Server:
missing_super.add('%s was not called, probably missing super call' missing_super.add('%s was not called, probably missing super call'
% modobj.earlyInit.__qualname__) % modobj.earlyInit.__qualname__)
# handle attached modules
for modname, modobj in self.modules.items():
attached_modules = {}
for propname, propobj in modobj.propertyDict.items():
if isinstance(propobj, Attached):
try:
attname = getattr(modobj, propname)
attobj = self.dispatcher.get_module(attname)
if isinstance(attobj, propobj.basecls):
attached_modules[propname] = attobj
else:
errors.append('attached module %s=%r must inherit from %r'
% (propname, attname, propobj.basecls.__qualname__))
except SECoPError as e:
errors.append('module %s, attached %s: %s' % (modname, propname, str(e)))
modobj.attachedModules = attached_modules
# call init on each module after registering all # call init on each module after registering all
for modname, modobj in self.modules.items(): for modname, modobj in self.modules.items():
try: try:
@ -287,8 +305,7 @@ class Server:
failure_traceback = traceback.format_exc() failure_traceback = traceback.format_exc()
errors.append('error initializing %s: %r' % (modname, e)) errors.append('error initializing %s: %r' % (modname, e))
if self._testonly: if not self._testonly:
return
start_events = MultiEvent(default_timeout=30) start_events = MultiEvent(default_timeout=30)
for modname, modobj in self.modules.items(): for modname, modobj in self.modules.items():
# startModule must return either a timeout value or None (default 30 sec) # startModule must return either a timeout value or None (default 30 sec)
@ -297,8 +314,8 @@ class Server:
if not modobj.startModuleDone: if not modobj.startModuleDone:
missing_super.add('%s was not called, probably missing super call' missing_super.add('%s was not called, probably missing super call'
% modobj.startModule.__qualname__) % modobj.startModule.__qualname__)
errors.extend(missing_super) errors.extend(missing_super)
if errors: if errors:
for errtxt in errors: for errtxt in errors:
for line in errtxt.split('\n'): for line in errtxt.split('\n'):
@ -310,6 +327,8 @@ class Server:
sys.stderr.write(failure_traceback) sys.stderr.write(failure_traceback)
sys.exit(1) sys.exit(1)
if self._testonly:
return
self.log.info('waiting for modules being started') self.log.info('waiting for modules being started')
start_events.name = None start_events.name = None
if not start_events.wait(): if not start_events.wait():

View File

@ -75,4 +75,6 @@ def test_attach():
assert m.propertyValues['att'] == 'a' assert m.propertyValues['att'] == 'a'
srv.dispatcher.register_module(a, 'a') srv.dispatcher.register_module(a, 'a')
srv.dispatcher.register_module(m, 'm') srv.dispatcher.register_module(m, 'm')
assert m.att == 'a'
m.attachedModules = {'att': a}
assert m.att == a assert m.att == a