improve and fix errors with parameter limits

- in order to work properly, readonly=True in limit parameters
  has to be set before creating the write_* method
- more explicit: Use e.g. target_max=Limit()
- fix an error in the loop over the base classes when creating
  the check_* method
- more concise error message when a limit is violated
+ fix an error in playground when using persistent parameters

Change-Id: Ibd557b55d6c0d9a2612cda4460b16e3c70e1bc9e
Reviewed-on: https://forge.frm2.tum.de/review/c/secop/frappy/+/31017
Tested-by: Jenkins Automated Tests <pedersen+jenkins@frm2.tum.de>
Reviewed-by: Markus Zolliker <markus.zolliker@psi.ch>
This commit is contained in:
2023-05-04 12:50:25 +02:00
parent 85166344d2
commit 3b95013b69
6 changed files with 73 additions and 42 deletions

View File

@@ -34,7 +34,7 @@ from frappy.errors import BadValueError, CommunicationFailedError, ConfigError,
ProgrammingError, SECoPError, secop_error, RangeError
from frappy.lib import formatException, mkthread, UniqueObject
from frappy.lib.enum import Enum
from frappy.params import Accessible, Command, Parameter
from frappy.params import Accessible, Command, Parameter, Limit
from frappy.properties import HasProperties, Property
from frappy.logging import RemoteLogHandler, HasComlog
@@ -161,7 +161,7 @@ class HasAccessibles(HasProperties):
# find the base class, where the parameter <limname> is defined first.
# we have to check all bases, as they may not be treated yet when
# not inheriting from HasAccessibles
base = next(b for b in reversed(base.__mro__) if limname in b.__dict__)
base = next(b for b in reversed(cls.__mro__) if limname in b.__dict__)
if cname not in base.__dict__:
# there is no check method yet at this class
# add check function to the class where the limit was defined
@@ -431,28 +431,19 @@ class Module(HasAccessibles):
self.valueCallbacks[pname] = []
self.errorCallbacks[pname] = []
if not pobj.hasDatatype():
head, _, postfix = pname.rpartition('_')
if postfix not in ('min', 'max', 'limits'):
errors.append(f'{pname} needs a datatype')
continue
# when datatype is not given, properties are set automagically
pobj.setProperty('readonly', False)
baseparam = self.parameters.get(head)
if isinstance(pobj, Limit):
basepname = pname.rpartition('_')[0]
baseparam = self.parameters.get(basepname)
if not baseparam:
errors.append(f'parameter {pname!r} is given, but not {head!r}')
errors.append(f'limit {pname!r} is given, but not {basepname!r}')
continue
dt = baseparam.datatype
if dt is None:
if baseparam.datatype is None:
continue # an error will be reported on baseparam
if postfix == 'limits':
pobj.setProperty('datatype', TupleOf(dt, dt))
pobj.setProperty('default', (dt.min, dt.max))
else:
pobj.setProperty('datatype', dt)
pobj.setProperty('default', getattr(dt, postfix))
if not pobj.description:
pobj.setProperty('description', f'limit for {pname}')
pobj.set_datatype(baseparam.datatype)
if not pobj.hasDatatype():
errors.append(f'{pname} needs a datatype')
continue
if pobj.value is None:
if pobj.needscfg:
@@ -805,11 +796,11 @@ class Module(HasAccessibles):
raise ValueError('remote handler not found')
self.remoteLogHandler.set_conn_level(self, conn, level)
def checkLimits(self, value, parametername='target'):
def checkLimits(self, value, pname='target'):
"""check for limits
:param value: the value to be checked for <parametername>_min <= value <= <parametername>_max
:param parametername: parameter name, default is 'target'
:param value: the value to be checked for <pname>_min <= value <= <pname>_max
:param pname: parameter name, default is 'target'
raises RangeError in case the value is not valid
@@ -818,14 +809,20 @@ class Module(HasAccessibles):
when no automatic super call is desired.
"""
try:
min_, max_ = getattr(self, parametername + '_limits')
min_, max_ = getattr(self, pname + '_limits')
if not min_ <= value <= max_:
raise RangeError(f'{pname} outside {pname}_limits')
return
except AttributeError:
min_ = getattr(self, parametername + '_min', float('-inf'))
max_ = getattr(self, parametername + '_max', float('inf'))
if not min_ <= value <= max_:
if min_ > max_:
raise RangeError(f'invalid limits: [{min_:g}, {max_:g}]')
raise RangeError(f'limits violation: {value:g} outside [{min_:g}, {max_:g}]')
pass
min_ = getattr(self, pname + '_min', float('-inf'))
max_ = getattr(self, pname + '_max', float('inf'))
if min_ > max_:
raise RangeError(f'invalid limits: {pname}_min > {pname}_max')
if value < min_:
raise RangeError(f'{pname} below {pname}_min')
if value > max_:
raise RangeError(f'{pname} above {pname}_max')
class Readable(Module):