migrated secop_psi drivers to new syntax

- includes all changes up to 'fix inheritance order' from git_mlz
  6a32ecf342

Change-Id: Ie3ceee3dbd0a9284b47b1d5b5dbe262eebe8f283
This commit is contained in:
2021-02-24 16:15:23 +01:00
parent bc5edec06f
commit 41baf5805f
79 changed files with 2610 additions and 3952 deletions

View File

@@ -21,13 +21,13 @@
# *****************************************************************************
"""Define helpers"""
import importlib
import linecache
import socket
import sys
import threading
import traceback
import importlib
from os import path, environ
from os import environ, path
repodir = path.abspath(path.join(path.dirname(__file__), '..', '..'))
@@ -58,6 +58,7 @@ CONFIG['basedir'] = repodir
unset_value = object()
class lazy_property:
"""A property that calculates its value only once."""

View File

@@ -28,16 +28,18 @@ support for asynchronous communication, but may be used also for
synchronous IO (see secop.stringio.StringIO)
"""
import socket
import select
import time
import ast
import select
import socket
import time
from secop.errors import CommunicationFailedError, ConfigError
from secop.lib import closeSocket, parseHostPort, tcpSocket
try:
from serial import Serial
except ImportError:
Serial = None
from secop.lib import parseHostPort, tcpSocket, closeSocket
from secop.errors import ConfigError, CommunicationFailedError
class ConnectionClosed(ConnectionError):
@@ -60,10 +62,10 @@ class AsynConn:
except (ValueError, TypeError, AssertionError):
if 'COM' in uri:
raise ValueError("the correct uri for a COM port is: "
"'serial://COM<i>[?<option>=<value>[+<option>=value ...]]'" )
"'serial://COM<i>[?<option>=<value>[+<option>=value ...]]'")
if '/dev' in uri:
raise ValueError("the correct uri for a serial port is: "
"'serial:///dev/<tty>[?<option>=<value>[+<option>=value ...]]'" )
"'serial:///dev/<tty>[?<option>=<value>[+<option>=value ...]]'")
raise ValueError('invalid uri: %s' % uri)
iocls = cls.SCHEME_MAP['tcp']
uri = 'tcp://%s:%d' % host_port

View File

@@ -20,57 +20,168 @@
#
# *****************************************************************************
from inspect import cleandoc
from textwrap import indent
from secop.modules import Command, HasProperties, Module, Parameter, Property
def indent_description(p):
"""indent lines except first one"""
return indent(p.description, ' ').replace(' ', '', 1)
return indent(p.description, ' ').replace(' ', '', 1)
def append_to_doc(cls, name, title, attrname, newitems, fmtfunc):
def fmt_param(name, param):
desc = indent_description(param)
if '(' in desc[0:2]:
dtinfo = ''
else:
dtinfo = [short_doc(param.datatype), 'rd' if param.readonly else 'wr',
None if param.export else 'hidden']
dtinfo = '*(%s)* ' % ', '.join(filter(None, dtinfo))
return '- **%s** - %s%s\n' % (name, dtinfo, desc)
def fmt_command(name, command):
desc = indent_description(command)
if '(' in desc[0:2]:
dtinfo = '' # note: we expect that desc contains argument list
else:
dtinfo = '*%s*' % short_doc(command.datatype) + ' -%s ' % ('' if command.export else ' *(hidden)*')
return '- **%s**\\ %s%s\n' % (name, dtinfo, desc)
def fmt_property(name, prop):
desc = indent_description(prop)
if '(' in desc[0:2]:
dtinfo = ''
else:
dtinfo = [short_doc(prop.datatype), None if prop.export else 'hidden']
dtinfo = ', '.join(filter(None, dtinfo))
if dtinfo:
dtinfo = '*(%s)* ' % dtinfo
return '- **%s** - %s%s\n' % (name, dtinfo, desc)
SIMPLETYPES = {
'FloatRange': 'float',
'ScaledInteger': 'float',
'IntRange': 'int',
'BlobType': 'bytes',
'StringType': 'str',
'TextType': 'str',
'BoolType': 'bool',
'StructOf': 'dict',
}
def short_doc(datatype):
# pylint: disable=possibly-unused-variable
def doc_EnumType(dt):
return 'one of %s' % str(tuple(dt._enum.keys()))
def doc_ArrayOf(dt):
return 'array of %s' % short_doc(dt.members)
def doc_TupleOf(dt):
return 'tuple of (%s)' % ', '.join(short_doc(m) for m in dt.members)
def doc_CommandType(dt):
argument = short_doc(dt.argument) if dt.argument else ''
result = ' -> %s' % short_doc(dt.result) if dt.result else ''
return '(%s)%s' % (argument, result) # return argument list only
def doc_NoneOr(dt):
other = short_doc(dt.other)
return '%s or None' % other if other else None
def doc_OrType(dt):
types = [short_doc(t) for t in dt.types]
if None in types: # type is anyway broad: no doc
return None
return ' or '.join(types)
def doc_Stub(dt):
return dt.name.replace('Type', '').replace('Range', '').lower()
clsname = datatype.__class__.__name__
result = SIMPLETYPES.get(clsname)
if result:
return result
fun = locals().get('doc_' + clsname)
if fun:
return fun(datatype)
return None # broad type like ValueType: no doc
def append_to_doc(cls, lines, itemcls, name, attrname, fmtfunc):
"""add information about some items to the doc
:param cls: the class with the doc string to be extended
:param name: the name of the attribute dict to be used
:param title: the title to be used
:param newitems: the set of new items defined for this class
:param lines: content of the docstring, as lines
:param itemcls: the class of the attribute to be collected, a tuple of classes is also allowed.
:param attrname: the name of the attribute dict to look for
:param name: the name of the items to be collected (used for the title and for the tags)
:param fmtfunc: a function returning a formatted item to be displayed, including line feed at end
or an empty string to suppress output for this item
:type fmtfunc: function(key, value)
rules, assuming name='properties':
- if the docstring contains ``{properties}``, new properties are inserted here
- if the docstring contains ``{all properties}``, all properties are inserted here
- if the docstring contains ``{no properties}``, no properties are inserted
only the first appearance of a tag above is considered
"""
doc = cleandoc(cls.__doc__ or '')
doc = '\n'.join(lines)
title = 'SECoP %s' % name.title()
allitems = getattr(cls, attrname, {})
fmtdict = {n: fmtfunc(n, p) or ' - **%s** *removed*\n' % n for n, p in allitems.items()}
fmtdict = {n: fmtfunc(n, p) for n, p in allitems.items() if isinstance(p, itemcls)}
head, _, tail = doc.partition('{all %s}' % name)
clsset = set()
if tail: # take all
inherited = set()
fmted = ''.join(fmtdict.values())
fmted = fmtdict.values()
else:
inherited = {n: p for n, p in allitems.items() if fmtdict.get(n) and n not in newitems}
fmted = ''.join(' ' + v for k, v in fmtdict.items() if k in newitems)
head, _, tail = doc.partition('{%s}' % name)
if not tail:
head, _, tail = doc.partition('{no %s}' % name)
if tail: # add no information
return
# no tag found: append to the end
if fmted:
clsset = set()
for name in inherited:
p = allitems[name]
refcls = cls
fmted = []
for key, formatted_item in fmtdict.items():
if not formatted_item:
continue
# find where item is defined or modified
refcls = None
for base in cls.__mro__:
dp = getattr(base, attrname, {}).get(name)
if dp:
if dp == p:
p = getattr(base, attrname, {}).get(key)
if isinstance(p, itemcls):
if fmtfunc(key, p) == formatted_item:
refcls = base
else:
break
clsset.add(refcls)
clsset.discard(cls)
if refcls == cls:
# definition in cls is new or modified
fmted.append(formatted_item)
else:
# definition of last modification in refcls
clsset.add(refcls)
if fmted:
if clsset:
fmted += ' - see also %s\n' % (', '.join(':class:`%s.%s`' % (c.__module__, c.__name__)
for c in cls.__mro__ if c in clsset))
cls.__doc__ = '%s\n\n:%s: %s\n%s' % (head, title, fmted, tail)
fmted.append('- see also %s\n' % (', '.join(':class:`%s.%s`' % (c.__module__, c.__name__)
for c in cls.__mro__ if c in clsset)))
doc = '%s\n\n:%s: %s\n\n%s' % (head, title, ' '.join(fmted), tail)
lines[:] = doc.split('\n')
def class_doc_handler(app, what, name, cls, options, lines):
if what == 'class':
if issubclass(cls, HasProperties):
append_to_doc(cls, lines, Property, 'properties', 'propertyDict', fmt_property)
if issubclass(cls, Module):
append_to_doc(cls, lines, Parameter, 'parameters', 'accessibles', fmt_param)
append_to_doc(cls, lines, Command, 'commands', 'accessibles', fmt_command)

View File

@@ -32,6 +32,7 @@ class EnumMember:
has an int-type value and attributes 'name' and 'value'
"""
__slots__ = ['name', 'value', 'enum']
def __init__(self, enum, name, value):
if not isinstance(enum, Enum):
raise TypeError('1st Argument must be an instance of class Enum()')
@@ -49,7 +50,7 @@ class EnumMember:
try:
other = int(other)
except Exception:
#raise TypeError('%r can not be compared to %r!' %(other, self))
# raise TypeError('%r can not be compared to %r!' %(other, self))
return -1 # XXX:!
if self.value < other:
return -1
@@ -59,10 +60,12 @@ class EnumMember:
def __lt__(self, other):
return self.__cmp__(other.value if isinstance(other, EnumMember) else other) == -1
def __le__(self, other):
return self.__cmp__(other.value if isinstance(other, EnumMember) else other) < 1
def __eq__(self, other):
if isinstance(other, (EnumMember)):
if isinstance(other, EnumMember):
return other.value == self.value
if isinstance(other, int):
return other == self.value
@@ -72,10 +75,13 @@ class EnumMember:
return self.name == other
return False
return self.__cmp__(other.value if isinstance(other, EnumMember) else other) == 0
def __ne__(self, other):
return not self.__eq__(other)
def __ge__(self, other):
return self.__cmp__(other.value if isinstance(other, EnumMember) else other) > -1
def __gt__(self, other):
return self.__cmp__(other.value if isinstance(other, EnumMember) else other) == 1
@@ -100,77 +106,105 @@ class EnumMember:
def __repr__(self):
return '<%s%s (%d)>' % (self.enum.name + '.' if self.enum.name else '', self.name, self.value)
# numeric operations: delegate to int. Do we really need any of those?
def __add__(self, other):
return self.value.__add__(other.value if isinstance(other, EnumMember) else other)
def __sub__(self, other):
return self.value.__sub__(other.value if isinstance(other, EnumMember) else other)
def __mul__(self, other):
return self.value.__mul__(other.value if isinstance(other, EnumMember) else other)
def __truediv__(self, other):
return self.value.__truediv__(other.value if isinstance(other, EnumMember) else other)
def __floordiv__(self, other):
return self.value.__floordiv__(other.value if isinstance(other, EnumMember) else other)
def __mod__(self, other):
return self.value.__mod__(other.value if isinstance(other, EnumMember) else other)
def __divmod__(self, other):
return self.value.__divmod__(other.value if isinstance(other, EnumMember) else other)
def __pow__(self, other, *args):
return self.value.__pow__(other, *args)
def __lshift__(self, other):
return self.value.__lshift__(other.value if isinstance(other, EnumMember) else other)
def __rshift__(self, other):
return self.value.__rshift__(other.value if isinstance(other, EnumMember) else other)
def __radd__(self, other):
return self.value.__radd__(other.value if isinstance(other, EnumMember) else other)
def __rsub__(self, other):
return self.value.__rsub__(other.value if isinstance(other, EnumMember) else other)
def __rmul__(self, other):
return self.value.__rmul__(other.value if isinstance(other, EnumMember) else other)
def __rtruediv__(self, other):
return self.value.__rtruediv__(other.value if isinstance(other, EnumMember) else other)
def __rfloordiv__(self, other):
return self.value.__rfloordiv__(other.value if isinstance(other, EnumMember) else other)
def __rmod__(self, other):
return self.value.__rmod__(other.value if isinstance(other, EnumMember) else other)
def __rdivmod__(self, other):
return self.value.__rdivmod__(other.value if isinstance(other, EnumMember) else other)
def __rpow__(self, other, *args):
return self.value.__rpow__(other, *args)
def __rlshift__(self, other):
return self.value.__rlshift__(other.value if isinstance(other, EnumMember) else other)
def __rrshift__(self, other):
return self.value.__rrshift__(other.value if isinstance(other, EnumMember) else other)
# logical operations
def __and__(self, other):
return self.value.__and__(other.value if isinstance(other, EnumMember) else other)
def __xor__(self, other):
return self.value.__xor__(other.value if isinstance(other, EnumMember) else other)
def __or__(self, other):
return self.value.__or__(other.value if isinstance(other, EnumMember) else other)
def __rand__(self, other):
return self.value.__rand__(other.value if isinstance(other, EnumMember) else other)
def __rxor__(self, other):
return self.value.__rxor__(other.value if isinstance(other, EnumMember) else other)
def __ror__(self, other):
return self.value.__ror__(other.value if isinstance(other, EnumMember) else other)
# other stuff
def __neg__(self):
return self.value.__neg__()
def __pos__(self):
return self.value.__pos__()
def __abs__(self):
return self.value.__abs__()
def __invert__(self):
return self.value.__invert__()
def __int__(self):
return self.value.__int__()
def __float__(self):
return self.value.__float__()
#return NotImplemented # makes no sense
def __index__(self):
return self.value.__index__()
@@ -206,6 +240,7 @@ class Enum(dict):
You only can create an extended Enum.
"""
name = ''
def __init__(self, name='', parent=None, **kwds):
super(Enum, self).__init__()
if isinstance(name, (dict, Enum)) and parent is None:
@@ -217,7 +252,7 @@ class Enum(dict):
# if name was not given, use that of the parent
# this means, an extended Enum behaves like the parent
# THIS MAY BE CONFUSING SOMETIMES!
name=parent.name
name = parent.name
# else:
# raise TypeError('Enum instances need a name or an Enum parent!')
if not isinstance(name, str):
@@ -225,8 +260,9 @@ class Enum(dict):
names = set()
values = set()
# pylint: disable=dangerous-default-value
def add(self, k, v, names = names, value = values):
def add(self, k, v, names=names, value=values):
"""helper for creating the enum members"""
if v is None:
# sugar: take the next free number if value was None
@@ -237,7 +273,7 @@ class Enum(dict):
if v in names:
v = self[v].value
while v in values:
v +=1
v += 1
# check that the value is an int
_v = int(v)
@@ -290,7 +326,6 @@ class Enum(dict):
def __repr__(self):
return 'Enum(%r, %s)' % (self.name, ', '.join('%s=%d' % (m.name, m.value) for m in self.members))
# return '<Enum %r (%d values)>' % (self.name, len(self)//2)
def __call__(self, key):
return self[key]

View File

@@ -21,6 +21,7 @@
# *****************************************************************************
"""Define parsing helpers"""
# TODO: remove, as currently not used
import re
import time

View File

@@ -141,7 +141,7 @@ class SequencerMixin:
return self.read_hw_status()
return self.Status.IDLE, ''
def do_stop(self):
def stop(self):
if self.seq_is_alive():
self._seq_stopflag = True