merge until "support write_ method on readonly param and more"

from gerrit

Change-Id: I8d2ad2a381d3a37947d8afc5e17be0428d94df36
This commit is contained in:
2022-03-21 14:00:12 +01:00
parent 7c9296fe2e
commit b1ddc01fbb
24 changed files with 604 additions and 436 deletions

View File

@ -161,8 +161,8 @@ class HasAccessibles(HasProperties):
new_rfunc.wrapped = True # indicate to subclasses that no more wrapping is needed
setattr(cls, 'read_' + pname, new_rfunc)
if not pobj.readonly:
wfunc = getattr(cls, 'write_' + pname, None)
wfunc = getattr(cls, 'write_' + pname, None)
if not pobj.readonly or wfunc: # allow write_ method even when pobj is not readonly
wrapped = getattr(wfunc, 'wrapped', False) # meaning: wrapped or auto generated
if (wfunc is None or wrapped) and pobj.handler:
# ignore the handler, if a write function is present
@ -181,14 +181,14 @@ class HasAccessibles(HasProperties):
self.log.debug('validate %r for %r', value, pname)
# we do not need to handle errors here, we do not
# want to make a parameter invalid, when a write failed
value = pobj.datatype(value)
returned_value = wfunc(self, value)
self.log.debug('write_%s(%r) returned %r', pname, value, returned_value)
if returned_value is Done:
new_value = pobj.datatype(value)
new_value = wfunc(self, new_value)
self.log.debug('write_%s(%r) returned %r', pname, value, new_value)
if new_value is Done:
# setattr(self, pname, getattr(self, pname))
return getattr(self, pname)
setattr(self, pname, value) # important! trigger the setter
return value
setattr(self, pname, new_value) # important! trigger the setter
return new_value
else:
def new_wfunc(self, value, pname=pname):
@ -201,14 +201,14 @@ class HasAccessibles(HasProperties):
setattr(cls, 'write_' + pname, new_wfunc)
# check for programming errors
for attrname, attrvalue in cls.__dict__.items():
for attrname in dir(cls):
prefix, _, pname = attrname.partition('_')
if not pname:
continue
if prefix == 'do':
raise ProgrammingError('%r: old style command %r not supported anymore'
% (cls.__name__, attrname))
if prefix in ('read', 'write') and not getattr(attrvalue, 'wrapped', False):
if prefix in ('read', 'write') and not getattr(getattr(cls, attrname), 'wrapped', False):
raise ProgrammingError('%s.%s defined, but %r is no parameter'
% (cls.__name__, attrname, pname))
@ -280,6 +280,7 @@ class Module(HasAccessibles):
# reference to the dispatcher (used for sending async updates)
DISPATCHER = None
attachedModules = None
def __init__(self, name, logger, cfgdict, srv):
# remember the dispatcher object (for the async callbacks)
@ -391,9 +392,8 @@ class Module(HasAccessibles):
continue
if pname in cfgdict:
if not pobj.readonly and pobj.initwrite is not False:
if pobj.initwrite is not False and hasattr(self, 'write_' + pname):
# parameters given in cfgdict have to call write_<pname>
# TODO: not sure about readonly (why not a parameter which can only be written from config?)
try:
pobj.value = pobj.datatype(cfgdict[pname])
self.writeDict[pname] = pobj.value
@ -416,10 +416,8 @@ class Module(HasAccessibles):
except BadValueError as e:
# this should not happen, as the default is already checked in Parameter
raise ProgrammingError('bad default for %s:%s: %s' % (name, pname, e)) from None
if pobj.initwrite and not pobj.readonly:
if pobj.initwrite and hasattr(self, 'write_' + pname):
# we will need to call write_<pname>
# if this is not desired, the default must not be given
# TODO: not sure about readonly (why not a parameter which can only be written from config?)
pobj.value = value
self.writeDict[pname] = value
else:
@ -649,11 +647,11 @@ class Module(HasAccessibles):
self.log.info('recovered after %d calls to doPoll (%r)', error_count, last_error)
last_error = None
except Exception as e:
if type(e) != last_error:
if repr(e) != last_error:
error_count = 0
self.log.error('error in doPoll: %r', e)
error_count += 1
last_error = e
last_error = repr(e)
now = time.time()
# find ONE due slow poll and call it
loop = True
@ -803,14 +801,15 @@ class Attached(Property):
assign a module name to this property in the cfg file,
and the server will create an attribute with this module
"""
module = None
def __init__(self, description='attached module'):
super().__init__(description, StringType(), mandatory=False)
def __init__(self, basecls=Module, description='attached module', mandatory=True):
self.basecls = basecls
super().__init__(description, StringType(), mandatory=mandatory)
def __get__(self, obj, owner):
if obj is None:
return self
if self.module is None:
self.module = obj.DISPATCHER.get_module(super().__get__(obj, owner))
return self.module
if obj.attachedModules is None:
# return the name of the module (called from Server on startup)
return super().__get__(obj, owner)
# return the module (called after startup)
return obj.attachedModules.get(self.name) # return None if not given