moved some code from iohandler.py to metaclass.py
- code for calling write_<param> before write function from handler is moved to the metaclass - moved some methods from IOBaseHandler to IOHandler Change-Id: I733c7fe8d3d59d9013e7b5a33e170c4b3e386921 Reviewed-on: https://forge.frm2.tum.de/review/c/sine2020/secop/playground/+/22098 Tested-by: JenkinsCodeReview <bjoern_pedersen@frm2.tum.de> Reviewed-by: Markus Zolliker <markus.zolliker@psi.ch>
This commit is contained in:
parent
f0a3306f9c
commit
859bf5e1a2
@ -20,7 +20,8 @@
|
||||
# *****************************************************************************
|
||||
"""IO handler
|
||||
|
||||
Utility class for cases, where multiple parameters are treated with a common command.
|
||||
Utility class for cases, where multiple parameters are treated with a common command,
|
||||
or in cases, where IO can be parametrized.
|
||||
The support for LakeShore and similar protocols is already included.
|
||||
|
||||
For read, instead of the methods read_<parameter> we write one method analyze_<group>
|
||||
@ -62,7 +63,7 @@ class CmdParser:
|
||||
"""helper for parsing replies
|
||||
|
||||
using a subset of old style python formatting.
|
||||
The same format can be used or formatting command arguments
|
||||
The same format can be used for formatting command arguments
|
||||
"""
|
||||
|
||||
# make a map of cast functions
|
||||
@ -74,14 +75,12 @@ class CmdParser:
|
||||
('xX', lambda x: int(x, 16)),
|
||||
('eEfFgG', float),
|
||||
) for letter in letters}
|
||||
# pattern for chacaters to be escaped
|
||||
ESC_PAT = re.compile('([\\%s])' % '\\'.join('|^$-.+*?()[]{}<>'))
|
||||
# pattern for characters to be escaped
|
||||
ESC_PAT = re.compile(r'([\|\^\$\-\.\+\*\?\(\)\[\]\{\}\<\>])')
|
||||
# format pattern
|
||||
FMT_PAT = re.compile('(%%|%[^diouxXfFgGeEcrsa]*(?:.|$))')
|
||||
|
||||
def __init__(self, argformat):
|
||||
# replace named patterns
|
||||
|
||||
self.fmt = argformat
|
||||
spl = self.FMT_PAT.split(argformat)
|
||||
spl_iter = iter(spl)
|
||||
@ -178,30 +177,66 @@ class Change:
|
||||
|
||||
|
||||
class IOHandlerBase:
|
||||
"""generic command handler"""
|
||||
"""abstract IO handler
|
||||
|
||||
def __init__(self, group):
|
||||
# group is used for calling the proper analyze_<group> and change_<group> methods
|
||||
IO handlers for parametrized access should inherit from this
|
||||
"""
|
||||
|
||||
def get_read_func(self, modclass, pname):
|
||||
"""get the read function for parameter pname"""
|
||||
raise NotImplementedError
|
||||
|
||||
def get_write_func(self, pname):
|
||||
"""get the write function for parameter pname"""
|
||||
raise NotImplementedError
|
||||
|
||||
|
||||
class IOHandler(IOHandlerBase):
|
||||
"""IO handler for cases, where multiple parameters are treated with a common command
|
||||
|
||||
This IO handler works for a syntax, where the reply of a query command has
|
||||
the same format as the arguments for the change command.
|
||||
Examples: devices from LakeShore, PPMS
|
||||
|
||||
implementing classes may override the following class variables
|
||||
"""
|
||||
CMDARGS = [] # list of properties or parameters to be used for building some of the the query and change commands
|
||||
CMDSEPARATOR = None # if not None, it is possible to join a command and a query with the given separator
|
||||
|
||||
def __init__(self, group, querycmd, replyfmt, changecmd=None):
|
||||
"""initialize the IO handler
|
||||
|
||||
group: the handler group (used for analyze_<group> and change_<group>)
|
||||
querycmd: the command for a query, may contain named formats for cmdargs
|
||||
replyfmt: the format for reading the reply with some scanf like behaviour
|
||||
changecmd: the first part of the change command (without values), may be
|
||||
omitted if no write happens
|
||||
"""
|
||||
self.group = group
|
||||
self.parameters = set()
|
||||
self._module_class = None
|
||||
self.querycmd = querycmd
|
||||
self.replyfmt = CmdParser(replyfmt)
|
||||
self.changecmd = changecmd
|
||||
|
||||
def parse_reply(self, reply):
|
||||
"""return values from a raw reply"""
|
||||
raise NotImplementedError
|
||||
return self.replyfmt.parse(reply)
|
||||
|
||||
def make_query(self, module):
|
||||
"""make a query"""
|
||||
raise NotImplementedError
|
||||
return self.querycmd % {k: getattr(module, k, None) for k in self.CMDARGS}
|
||||
|
||||
def make_change(self, module, *values):
|
||||
"""make a change command from values"""
|
||||
raise NotImplementedError
|
||||
"""make a change command"""
|
||||
changecmd = self.changecmd % {k: getattr(module, k, None) for k in self.CMDARGS}
|
||||
return changecmd + self.replyfmt.format(*values)
|
||||
|
||||
def send_command(self, module, changecmd=''):
|
||||
"""send a command (query or change+query) and parse the reply into a list
|
||||
|
||||
If changecmd is given, it is prepended before the query.
|
||||
If changecmd is given, it is prepended before the query. changecmd must
|
||||
contain the command separator at the end.
|
||||
"""
|
||||
querycmd = self.make_query(module)
|
||||
reply = module.sendRecv(changecmd + querycmd)
|
||||
@ -210,13 +245,14 @@ class IOHandlerBase:
|
||||
def send_change(self, module, *values):
|
||||
"""compose and send a command from values
|
||||
|
||||
and send a query afterwards. This method might be overriden, if the change command
|
||||
can be combined with a query command, or if the change command already includes
|
||||
a reply.
|
||||
and send a query afterwards, or combine with a query command.
|
||||
Override this method, if the change command already includes a reply.
|
||||
"""
|
||||
changecmd = self.make_change(module, *values)
|
||||
module.sendRecv(changecmd) # ignore result
|
||||
return self.send_command(module)
|
||||
if self.CMDSEPARATOR is None:
|
||||
module.sendRecv(changecmd) # ignore result
|
||||
return self.send_command(module)
|
||||
return self.send_command(module, changecmd + self.CMDSEPARATOR)
|
||||
|
||||
def get_read_func(self, modclass, pname):
|
||||
"""returns the read function passed to the metaclass
|
||||
@ -253,7 +289,7 @@ class IOHandlerBase:
|
||||
raise
|
||||
return Done
|
||||
|
||||
def get_write_func(self, pname, pre_wfunc=None):
|
||||
def get_write_func(self, pname):
|
||||
"""returns the write function passed to the metaclass
|
||||
|
||||
If pre_wfunc is given, it is to be called before change_<group>.
|
||||
@ -261,20 +297,9 @@ class IOHandlerBase:
|
||||
"""
|
||||
self.change = getattr(self._module_class, 'change_' + self.group)
|
||||
|
||||
if pre_wfunc:
|
||||
|
||||
def wfunc(module, value, hdl=self, pname=pname, wfunc=pre_wfunc):
|
||||
value = wfunc(module, value)
|
||||
if value is None or value is Done:
|
||||
return value
|
||||
hdl.write(module, pname, value)
|
||||
return Done
|
||||
|
||||
else:
|
||||
|
||||
def wfunc(module, value, hdl=self, pname=pname):
|
||||
hdl.write(module, pname, value)
|
||||
return Done
|
||||
def wfunc(module, value, hdl=self, pname=pname):
|
||||
hdl.write(module, pname, value)
|
||||
return Done
|
||||
|
||||
return wfunc
|
||||
|
||||
@ -300,49 +325,3 @@ class IOHandlerBase:
|
||||
result = self.analyze(module, *reply)
|
||||
for k, v in result.items():
|
||||
setattr(module, k, v)
|
||||
|
||||
|
||||
class IOHandler(IOHandlerBase):
|
||||
"""more evolved command handler
|
||||
|
||||
This command handler works for a syntax, where the reply of a query command has
|
||||
the same format as the arguments for the change command.
|
||||
Examples: devices from LakeShore, PPMS
|
||||
|
||||
implementing classes have to define/override the following:
|
||||
"""
|
||||
CMDARGS = [] # list of properties or parameters to be used for building some of the the query and change commands
|
||||
CMDSEPARATOR = ';' # if given, it is valid to join a command a a query with the given separator
|
||||
|
||||
def __init__(self, group, querycmd, replyfmt, changecmd=None):
|
||||
"""initialize the command handler
|
||||
|
||||
group: the handler group (used for analyze_<group> and change_<group>)
|
||||
querycmd: the command for a query, may contain named formats for cmdargs
|
||||
replyfmt: the format for reading the reply with some scanf like behaviour
|
||||
changecmd: the first part of the change command (without values), may be
|
||||
omitted if no write happens
|
||||
"""
|
||||
super().__init__(group)
|
||||
self.querycmd = querycmd
|
||||
self.replyfmt = CmdParser(replyfmt)
|
||||
self.changecmd = changecmd
|
||||
|
||||
def parse_reply(self, reply):
|
||||
"""return values from a raw reply"""
|
||||
return self.replyfmt.parse(reply)
|
||||
|
||||
def make_query(self, module):
|
||||
"""make a query"""
|
||||
return self.querycmd % {k: getattr(module, k, None) for k in self.CMDARGS}
|
||||
|
||||
def make_change(self, module, *values):
|
||||
"""make a change command"""
|
||||
changecmd = self.changecmd % {k: getattr(module, k, None) for k in self.CMDARGS}
|
||||
return changecmd + self.replyfmt.format(*values)
|
||||
|
||||
def send_change(self, module, *values):
|
||||
"""join change and query commands"""
|
||||
if self.CMDSEPARATOR is None:
|
||||
return super().send_change(module, *values)
|
||||
return self.send_command(module, self.make_change(module, *values) + self.CMDSEPARATOR)
|
||||
|
@ -136,6 +136,7 @@ class ModuleMeta(PropertyMeta):
|
||||
break
|
||||
rfunc = getattr(base, 'read_' + pname, None)
|
||||
|
||||
# create wrapper except when read function is already wrapped
|
||||
if rfunc is None or getattr(rfunc, '__wrapped__', False) is False:
|
||||
|
||||
def wrapped_rfunc(self, pname=pname, rfunc=rfunc):
|
||||
@ -157,37 +158,41 @@ class ModuleMeta(PropertyMeta):
|
||||
setattr(self, pname, value) # important! trigger the setter
|
||||
return value
|
||||
|
||||
wrapped_rfunc.__doc__ = rfunc.__doc__
|
||||
if rfunc:
|
||||
wrapped_rfunc.__doc__ = rfunc.__doc__
|
||||
setattr(newtype, 'read_' + pname, wrapped_rfunc)
|
||||
wrapped_rfunc.__wrapped__ = True
|
||||
|
||||
if not pobj.readonly:
|
||||
wfunc = attrs.get('write_' + pname, None)
|
||||
# if a handler and write_<param> is present, wfunc will be called
|
||||
# by the handler first
|
||||
wfunc = pobj.handler.get_write_func(pname, wfunc) if pobj.handler else wfunc
|
||||
for base in bases:
|
||||
if wfunc is not None:
|
||||
break
|
||||
wfunc = getattr(base, 'write_' + pname, None)
|
||||
|
||||
# create wrapper except when write function is already wrapped
|
||||
if wfunc is None or getattr(wfunc, '__wrapped__', False) is False:
|
||||
|
||||
def wrapped_wfunc(self, value, pname=pname, wfunc=wfunc):
|
||||
# append write function from handler, to be called after wfunc
|
||||
wfuncs = (wfunc, pobj.handler.get_write_func(pname) if pobj.handler else None)
|
||||
|
||||
def wrapped_wfunc(self, value, pname=pname, wfuncs=wfuncs):
|
||||
self.log.debug("check validity of %s = %r" % (pname, value))
|
||||
pobj = self.accessibles[pname]
|
||||
value = pobj.datatype(value)
|
||||
if wfunc:
|
||||
self.log.debug('calling %r(%r)' % (wfunc, value))
|
||||
for wfunc in filter(None, wfuncs):
|
||||
self.log.debug('calling %s %r(%r)' % (wfunc.__name__, wfunc, value))
|
||||
returned_value = wfunc(self, value)
|
||||
if returned_value is Done: # the setter is already triggered
|
||||
return getattr(self, pname)
|
||||
if returned_value is not None:
|
||||
value = returned_value
|
||||
if returned_value is None: # goodie: accept missing return value
|
||||
break # handler is not called in this case
|
||||
value = returned_value
|
||||
setattr(self, pname, value)
|
||||
return value
|
||||
|
||||
wrapped_wfunc.__doc__ = wfunc.__doc__
|
||||
if wfunc:
|
||||
wrapped_wfunc.__doc__ = wfunc.__doc__
|
||||
setattr(newtype, 'write_' + pname, wrapped_wfunc)
|
||||
wrapped_wfunc.__wrapped__ = True
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user