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:
@@ -28,13 +28,13 @@
|
||||
import sys
|
||||
from base64 import b64decode, b64encode
|
||||
|
||||
from secop.errors import ProgrammingError, ProtocolError, BadValueError, ConfigError
|
||||
from secop.errors import BadValueError, \
|
||||
ConfigError, ProgrammingError, ProtocolError
|
||||
from secop.lib import clamp
|
||||
from secop.lib.enum import Enum
|
||||
from secop.parse import Parser
|
||||
from secop.properties import HasProperties, Property
|
||||
|
||||
|
||||
# Only export these classes for 'from secop.datatypes import *'
|
||||
__all__ = [
|
||||
'DataType', 'get_datatype',
|
||||
@@ -53,6 +53,7 @@ UNLIMITED = 1 << 64 # internal limit for integers, is probably high enough for
|
||||
Parser = Parser()
|
||||
|
||||
|
||||
# base class for all DataTypes
|
||||
class DataType(HasProperties):
|
||||
"""base class for all data types"""
|
||||
IS_COMMAND = False
|
||||
@@ -97,7 +98,7 @@ class DataType(HasProperties):
|
||||
def set_properties(self, **kwds):
|
||||
"""init datatype properties"""
|
||||
try:
|
||||
for k,v in kwds.items():
|
||||
for k, v in kwds.items():
|
||||
self.setProperty(k, v)
|
||||
self.checkProperties()
|
||||
except Exception as e:
|
||||
@@ -126,10 +127,6 @@ class DataType(HasProperties):
|
||||
"""
|
||||
raise NotImplementedError
|
||||
|
||||
def short_doc(self):
|
||||
"""short description for automatic extension of doc strings"""
|
||||
return None
|
||||
|
||||
|
||||
class Stub(DataType):
|
||||
"""incomplete datatype, to be replaced with a proper one later during module load
|
||||
@@ -154,42 +151,35 @@ class Stub(DataType):
|
||||
"""
|
||||
for dtcls in globals().values():
|
||||
if isinstance(dtcls, type) and issubclass(dtcls, DataType):
|
||||
for prop in dtcls.properties.values():
|
||||
for prop in dtcls.propertyDict.values():
|
||||
stub = prop.datatype
|
||||
if isinstance(stub, cls):
|
||||
prop.datatype = globals()[stub.name](*stub.args)
|
||||
|
||||
def short_doc(self):
|
||||
return self.name.replace('Type', '').replace('Range', '').lower()
|
||||
|
||||
|
||||
# SECoP types:
|
||||
|
||||
|
||||
class FloatRange(DataType):
|
||||
"""(restricted) float type
|
||||
|
||||
:param minval: (property **min**)
|
||||
:param maxval: (property **max**)
|
||||
:param properties: any of the properties below
|
||||
:param kwds: any of the properties below
|
||||
"""
|
||||
min = Property('low limit', Stub('FloatRange'), extname='min', default=-sys.float_info.max)
|
||||
max = Property('high limit', Stub('FloatRange'), extname='max', default=sys.float_info.max)
|
||||
unit = Property('physical unit', Stub('StringType'), extname='unit', default='')
|
||||
fmtstr = Property('format string', Stub('StringType'), extname='fmtstr', default='%g')
|
||||
absolute_resolution = Property('absolute resolution', Stub('FloatRange', 0),
|
||||
extname='absolute_resolution', default=0.0)
|
||||
relative_resolution = Property('relative resolution', Stub('FloatRange', 0),
|
||||
extname='relative_resolution', default=1.2e-7)
|
||||
|
||||
properties = {
|
||||
'min': Property('low limit', Stub('FloatRange'), extname='min', default=-sys.float_info.max),
|
||||
'max': Property('high limit', Stub('FloatRange'), extname='max', default=sys.float_info.max),
|
||||
'unit': Property('physical unit', Stub('StringType'), extname='unit', default=''),
|
||||
'fmtstr': Property('format string', Stub('StringType'), extname='fmtstr', default='%g'),
|
||||
'absolute_resolution': Property('absolute resolution', Stub('FloatRange', 0),
|
||||
extname='absolute_resolution', default=0.0),
|
||||
'relative_resolution': Property('relative resolution', Stub('FloatRange', 0),
|
||||
extname='relative_resolution', default=1.2e-7),
|
||||
}
|
||||
|
||||
def __init__(self, minval=None, maxval=None, **properties):
|
||||
def __init__(self, minval=None, maxval=None, **kwds):
|
||||
super().__init__()
|
||||
properties['min'] = minval if minval is not None else -sys.float_info.max
|
||||
properties['max'] = maxval if maxval is not None else sys.float_info.max
|
||||
self.set_properties(**properties)
|
||||
kwds['min'] = minval if minval is not None else -sys.float_info.max
|
||||
kwds['max'] = maxval if maxval is not None else sys.float_info.max
|
||||
self.set_properties(**kwds)
|
||||
|
||||
def checkProperties(self):
|
||||
self.default = 0 if self.min <= 0 <= self.max else self.min
|
||||
@@ -213,7 +203,7 @@ class FloatRange(DataType):
|
||||
if self.min - prec <= value <= self.max + prec:
|
||||
return min(max(value, self.min), self.max)
|
||||
raise BadValueError('%.14g should be a float between %.14g and %.14g' %
|
||||
(value, self.min, self.max))
|
||||
(value, self.min, self.max))
|
||||
|
||||
def __repr__(self):
|
||||
hints = self.get_info()
|
||||
@@ -221,7 +211,7 @@ class FloatRange(DataType):
|
||||
hints['minval'] = hints.pop('min')
|
||||
if 'max' in hints:
|
||||
hints['maxval'] = hints.pop('max')
|
||||
return 'FloatRange(%s)' % (', '.join('%s=%r' % (k,v) for k,v in hints.items()))
|
||||
return 'FloatRange(%s)' % (', '.join('%s=%r' % (k, v) for k, v in hints.items()))
|
||||
|
||||
def export_value(self, value):
|
||||
"""returns a python object fit for serialisation"""
|
||||
@@ -249,9 +239,6 @@ class FloatRange(DataType):
|
||||
other(max(sys.float_info.min, self.min))
|
||||
other(min(sys.float_info.max, self.max))
|
||||
|
||||
def short_doc(self):
|
||||
return 'float'
|
||||
|
||||
|
||||
class IntRange(DataType):
|
||||
"""restricted int type
|
||||
@@ -259,12 +246,10 @@ class IntRange(DataType):
|
||||
:param minval: (property **min**)
|
||||
:param maxval: (property **max**)
|
||||
"""
|
||||
properties = {
|
||||
'min': Property('minimum value', Stub('IntRange', -UNLIMITED, UNLIMITED), extname='min', mandatory=True),
|
||||
'max': Property('maximum value', Stub('IntRange', -UNLIMITED, UNLIMITED), extname='max', mandatory=True),
|
||||
# a unit on an int is now allowed in SECoP, but do we need them in Frappy?
|
||||
# 'unit': Property('physical unit', StringType(), extname='unit', default=''),
|
||||
}
|
||||
min = Property('minimum value', Stub('IntRange', -UNLIMITED, UNLIMITED), extname='min', mandatory=True)
|
||||
max = Property('maximum value', Stub('IntRange', -UNLIMITED, UNLIMITED), extname='max', mandatory=True)
|
||||
# a unit on an int is now allowed in SECoP, but do we need them in Frappy?
|
||||
# unit = Property('physical unit', StringType(), extname='unit', default='')
|
||||
|
||||
def __init__(self, minval=None, maxval=None):
|
||||
super().__init__()
|
||||
@@ -290,7 +275,12 @@ class IntRange(DataType):
|
||||
raise BadValueError('Can not convert %r to int' % value)
|
||||
|
||||
def __repr__(self):
|
||||
return 'IntRange(%d, %d)' % (self.min, self.max)
|
||||
args = (self.min, self.max)
|
||||
if args[1] == DEFAULT_MAX_INT:
|
||||
args = args[:1]
|
||||
if args[0] == DEFAULT_MIN_INT:
|
||||
args = ()
|
||||
return 'IntRange%s' % repr(args)
|
||||
|
||||
def export_value(self, value):
|
||||
"""returns a python object fit for serialisation"""
|
||||
@@ -316,48 +306,38 @@ class IntRange(DataType):
|
||||
for i in range(self.min, self.max + 1):
|
||||
other(i)
|
||||
|
||||
def short_doc(self):
|
||||
return 'int'
|
||||
|
||||
|
||||
class ScaledInteger(DataType):
|
||||
"""scaled integer (= fixed resolution float) type
|
||||
|
||||
| In general *ScaledInteger* is needed only in special cases,
|
||||
e.g. when the a SEC node is running on very limited hardware
|
||||
without floating point support.
|
||||
| Please use *FloatRange* instead.
|
||||
|
||||
:param minval: (property **min**)
|
||||
:param maxval: (property **max**)
|
||||
:param properties: any of the properties below
|
||||
:param kwds: any of the properties below
|
||||
|
||||
{properties}
|
||||
:note: - limits are for the scaled float value
|
||||
- the scale is only used for calculating to/from transport serialisation
|
||||
note: limits are for the scaled float value
|
||||
the scale is only used for calculating to/from transport serialisation
|
||||
"""
|
||||
properties = {
|
||||
'scale': Property('scale factor', FloatRange(sys.float_info.min), extname='scale', mandatory=True),
|
||||
'min': Property('low limit', FloatRange(), extname='min', mandatory=True),
|
||||
'max': Property('high limit', FloatRange(), extname='max', mandatory=True),
|
||||
'unit': Property('physical unit', Stub('StringType'), extname='unit', default=''),
|
||||
'fmtstr': Property('format string', Stub('StringType'), extname='fmtstr', default='%g'),
|
||||
'absolute_resolution': Property('absolute resolution', FloatRange(0),
|
||||
extname='absolute_resolution', default=0.0),
|
||||
'relative_resolution': Property('relative resolution', FloatRange(0),
|
||||
extname='relative_resolution', default=1.2e-7),
|
||||
}
|
||||
scale = Property('scale factor', FloatRange(sys.float_info.min), extname='scale', mandatory=True)
|
||||
min = Property('low limit', FloatRange(), extname='min', mandatory=True)
|
||||
max = Property('high limit', FloatRange(), extname='max', mandatory=True)
|
||||
unit = Property('physical unit', Stub('StringType'), extname='unit', default='')
|
||||
fmtstr = Property('format string', Stub('StringType'), extname='fmtstr', default='%g')
|
||||
absolute_resolution = Property('absolute resolution', FloatRange(0),
|
||||
extname='absolute_resolution', default=0.0)
|
||||
relative_resolution = Property('relative resolution', FloatRange(0),
|
||||
extname='relative_resolution', default=1.2e-7)
|
||||
|
||||
def __init__(self, scale, minval=None, maxval=None, absolute_resolution=None, **properties):
|
||||
def __init__(self, scale, minval=None, maxval=None, absolute_resolution=None, **kwds):
|
||||
super().__init__()
|
||||
scale = float(scale)
|
||||
if absolute_resolution is None:
|
||||
absolute_resolution = scale
|
||||
self.set_properties(scale=scale,
|
||||
self.set_properties(
|
||||
scale=scale,
|
||||
min=DEFAULT_MIN_INT * scale if minval is None else float(minval),
|
||||
max=DEFAULT_MAX_INT * scale if maxval is None else float(maxval),
|
||||
absolute_resolution=absolute_resolution,
|
||||
**properties)
|
||||
**kwds)
|
||||
|
||||
def checkProperties(self):
|
||||
self.default = 0 if self.min <= 0 <= self.max else self.min
|
||||
@@ -384,8 +364,8 @@ class ScaledInteger(DataType):
|
||||
|
||||
def export_datatype(self):
|
||||
return self.get_info(type='scaled',
|
||||
min = int((self.min + self.scale * 0.5) // self.scale),
|
||||
max = int((self.max + self.scale * 0.5) // self.scale))
|
||||
min=int((self.min + self.scale * 0.5) // self.scale),
|
||||
max=int((self.max + self.scale * 0.5) // self.scale))
|
||||
|
||||
def __call__(self, value):
|
||||
try:
|
||||
@@ -398,15 +378,15 @@ class ScaledInteger(DataType):
|
||||
value = min(max(value, self.min), self.max)
|
||||
else:
|
||||
raise BadValueError('%g should be a float between %g and %g' %
|
||||
(value, self.min, self.max))
|
||||
(value, self.min, self.max))
|
||||
intval = int((value + self.scale * 0.5) // self.scale)
|
||||
value = float(intval * self.scale)
|
||||
return value # return 'actual' value (which is more discrete than a float)
|
||||
|
||||
def __repr__(self):
|
||||
hints = self.get_info(scale=float('%g' % self.scale),
|
||||
min = int((self.min + self.scale * 0.5) // self.scale),
|
||||
max = int((self.max + self.scale * 0.5) // self.scale))
|
||||
min=int((self.min + self.scale * 0.5) // self.scale),
|
||||
max=int((self.max + self.scale * 0.5) // self.scale))
|
||||
return 'ScaledInteger(%s)' % (', '.join('%s=%r' % kv for kv in hints.items()))
|
||||
|
||||
def export_value(self, value):
|
||||
@@ -435,25 +415,19 @@ class ScaledInteger(DataType):
|
||||
other(self.min)
|
||||
other(self.max)
|
||||
|
||||
def short_doc(self):
|
||||
return 'float'
|
||||
|
||||
|
||||
class EnumType(DataType):
|
||||
"""enumeration
|
||||
|
||||
:param enum_or_name: the name of the Enum or an Enum to inherit from
|
||||
:param members: each argument denotes <member name>=<member int value>
|
||||
|
||||
exception: use members=<member dict> to add members from a dict
|
||||
:param members: members dict or None when using kwds only
|
||||
:param kwds: (additional) members
|
||||
"""
|
||||
def __init__(self, enum_or_name='', **members):
|
||||
def __init__(self, enum_or_name='', *, members=None, **kwds):
|
||||
super().__init__()
|
||||
if 'members' in members:
|
||||
members = dict(members)
|
||||
members.update(members['members'])
|
||||
members.pop('members')
|
||||
self._enum = Enum(enum_or_name, **members)
|
||||
if members is not None:
|
||||
kwds.update(members)
|
||||
self._enum = Enum(enum_or_name, **kwds)
|
||||
self.default = self._enum[self._enum.members[0]]
|
||||
|
||||
def copy(self):
|
||||
@@ -461,10 +435,11 @@ class EnumType(DataType):
|
||||
return EnumType(self._enum)
|
||||
|
||||
def export_datatype(self):
|
||||
return {'type': 'enum', 'members':dict((m.name, m.value) for m in self._enum.members)}
|
||||
return {'type': 'enum', 'members': dict((m.name, m.value) for m in self._enum.members)}
|
||||
|
||||
def __repr__(self):
|
||||
return "EnumType(%r, %s)" % (self._enum.name, ', '.join('%s=%d' %(m.name, m.value) for m in self._enum.members))
|
||||
return "EnumType(%r, %s)" % (self._enum.name,
|
||||
', '.join('%s=%d' % (m.name, m.value) for m in self._enum.members))
|
||||
|
||||
def export_value(self, value):
|
||||
"""returns a python object fit for serialisation"""
|
||||
@@ -478,7 +453,7 @@ class EnumType(DataType):
|
||||
"""return the validated (internal) value or raise"""
|
||||
try:
|
||||
return self._enum[value]
|
||||
except (KeyError, TypeError): # TypeError will be raised when value is not hashable
|
||||
except (KeyError, TypeError): # TypeError will be raised when value is not hashable
|
||||
raise BadValueError('%r is not a member of enum %r' % (value, self._enum))
|
||||
|
||||
def from_string(self, text):
|
||||
@@ -487,25 +462,24 @@ class EnumType(DataType):
|
||||
def format_value(self, value, unit=None):
|
||||
return '%s<%s>' % (self._enum[value].name, self._enum[value].value)
|
||||
|
||||
def set_name(self, name):
|
||||
self._enum.name = name
|
||||
|
||||
def compatible(self, other):
|
||||
for m in self._enum.members:
|
||||
other(m)
|
||||
|
||||
def short_doc(self):
|
||||
return 'one of %s' % str(tuple(self._enum.keys()))
|
||||
|
||||
|
||||
class BLOBType(DataType):
|
||||
"""binary large object
|
||||
|
||||
internally treated as bytes
|
||||
"""
|
||||
properties = {
|
||||
'minbytes': Property('minimum number of bytes', IntRange(0), extname='minbytes',
|
||||
default=0),
|
||||
'maxbytes': Property('maximum number of bytes', IntRange(0), extname='maxbytes',
|
||||
mandatory=True),
|
||||
}
|
||||
|
||||
minbytes = Property('minimum number of bytes', IntRange(0), extname='minbytes',
|
||||
default=0)
|
||||
maxbytes = Property('maximum number of bytes', IntRange(0), extname='maxbytes',
|
||||
mandatory=True)
|
||||
|
||||
def __init__(self, minbytes=0, maxbytes=None):
|
||||
super().__init__()
|
||||
@@ -565,21 +539,20 @@ class BLOBType(DataType):
|
||||
class StringType(DataType):
|
||||
"""string
|
||||
|
||||
for parameters see properties below
|
||||
"""
|
||||
properties = {
|
||||
'minchars': Property('minimum number of character points', IntRange(0, UNLIMITED),
|
||||
extname='minchars', default=0),
|
||||
'maxchars': Property('maximum number of character points', IntRange(0, UNLIMITED),
|
||||
extname='maxchars', default=UNLIMITED),
|
||||
'isUTF8': Property('flag telling whether encoding is UTF-8 instead of ASCII',
|
||||
Stub('BoolType'), extname='isUTF8', default=False),
|
||||
}
|
||||
minchars = Property('minimum number of character points', IntRange(0, UNLIMITED),
|
||||
extname='minchars', default=0)
|
||||
maxchars = Property('maximum number of character points', IntRange(0, UNLIMITED),
|
||||
extname='maxchars', default=UNLIMITED)
|
||||
isUTF8 = Property('flag telling whether encoding is UTF-8 instead of ASCII',
|
||||
Stub('BoolType'), extname='isUTF8', default=False)
|
||||
|
||||
def __init__(self, minchars=0, maxchars=None, isUTF8=False):
|
||||
def __init__(self, minchars=0, maxchars=None, **kwds):
|
||||
super().__init__()
|
||||
if maxchars is None:
|
||||
maxchars = minchars or UNLIMITED
|
||||
self.set_properties(minchars=minchars, maxchars=maxchars, isUTF8=isUTF8)
|
||||
self.set_properties(minchars=minchars, maxchars=maxchars, **kwds)
|
||||
|
||||
def checkProperties(self):
|
||||
self.default = ' ' * self.minchars
|
||||
@@ -635,24 +608,13 @@ class StringType(DataType):
|
||||
except AttributeError:
|
||||
raise BadValueError('incompatible datatypes')
|
||||
|
||||
def short_doc(self):
|
||||
return 'str'
|
||||
|
||||
|
||||
# TextType is a special StringType intended for longer texts (i.e. embedding \n),
|
||||
# whereas StringType is supposed to not contain '\n'
|
||||
# unfortunately, SECoP makes no distinction here....
|
||||
# note: content is supposed to follow the format of a git commit message, i.e. a line of text, 2 '\n' + a longer explanation
|
||||
# note: content is supposed to follow the format of a git commit message,
|
||||
# i.e. a line of text, 2 '\n' + a longer explanation
|
||||
class TextType(StringType):
|
||||
"""special string type, intended for longer texts
|
||||
|
||||
:param maxchars: maximum number of characters
|
||||
|
||||
whereas StringType is supposed to not contain '\n'
|
||||
unfortunately, SECoP makes no distinction here....
|
||||
note: content is supposed to follow the format of a git commit message,
|
||||
i.e. a line of text, 2 '\n' + a longer explanation
|
||||
"""
|
||||
def __init__(self, maxchars=None):
|
||||
if maxchars is None:
|
||||
maxchars = UNLIMITED
|
||||
@@ -661,7 +623,7 @@ class TextType(StringType):
|
||||
def __repr__(self):
|
||||
if self.maxchars == UNLIMITED:
|
||||
return 'TextType()'
|
||||
return 'TextType(%d)' % (self.maxchars)
|
||||
return 'TextType(%d)' % self.maxchars
|
||||
|
||||
def copy(self):
|
||||
# DataType.copy will not work, because it is exported as 'string'
|
||||
@@ -669,9 +631,7 @@ class TextType(StringType):
|
||||
|
||||
|
||||
class BoolType(DataType):
|
||||
"""boolean
|
||||
|
||||
"""
|
||||
"""boolean"""
|
||||
default = False
|
||||
|
||||
def export_datatype(self):
|
||||
@@ -707,9 +667,6 @@ class BoolType(DataType):
|
||||
other(False)
|
||||
other(True)
|
||||
|
||||
def short_doc(self):
|
||||
return 'bool'
|
||||
|
||||
|
||||
Stub.fix_datatypes()
|
||||
|
||||
@@ -721,14 +678,12 @@ Stub.fix_datatypes()
|
||||
class ArrayOf(DataType):
|
||||
"""data structure with fields of homogeneous type
|
||||
|
||||
:param members: the datatype for all elements
|
||||
:param members: the datatype of the elements
|
||||
"""
|
||||
properties = {
|
||||
'minlen': Property('minimum number of elements', IntRange(0), extname='minlen',
|
||||
default=0),
|
||||
'maxlen': Property('maximum number of elements', IntRange(0), extname='maxlen',
|
||||
mandatory=True),
|
||||
}
|
||||
minlen = Property('minimum number of elements', IntRange(0), extname='minlen',
|
||||
default=0)
|
||||
maxlen = Property('maximum number of elements', IntRange(0), extname='maxlen',
|
||||
mandatory=True)
|
||||
|
||||
def __init__(self, members, minlen=0, maxlen=None):
|
||||
super().__init__()
|
||||
@@ -759,14 +714,14 @@ class ArrayOf(DataType):
|
||||
|
||||
def setProperty(self, key, value):
|
||||
"""set also properties of members"""
|
||||
if key in self.__class__.properties:
|
||||
if key in self.propertyDict:
|
||||
super().setProperty(key, value)
|
||||
else:
|
||||
self.members.setProperty(key, value)
|
||||
|
||||
def export_datatype(self):
|
||||
return dict(type='array', minlen=self.minlen, maxlen=self.maxlen,
|
||||
members=self.members.export_datatype())
|
||||
members=self.members.export_datatype())
|
||||
|
||||
def __repr__(self):
|
||||
return 'ArrayOf(%s, %s, %s)' % (
|
||||
@@ -818,16 +773,12 @@ class ArrayOf(DataType):
|
||||
except AttributeError:
|
||||
raise BadValueError('incompatible datatypes')
|
||||
|
||||
def short_doc(self):
|
||||
return 'array of %s' % self.members.short_doc()
|
||||
|
||||
|
||||
class TupleOf(DataType):
|
||||
"""data structure with fields of inhomogeneous type
|
||||
|
||||
:param members: each argument is a datatype of an element
|
||||
types are given as positional arguments
|
||||
"""
|
||||
|
||||
def __init__(self, *members):
|
||||
super().__init__()
|
||||
if not members:
|
||||
@@ -855,11 +806,10 @@ class TupleOf(DataType):
|
||||
try:
|
||||
if len(value) != len(self.members):
|
||||
raise BadValueError(
|
||||
'Illegal number of Arguments! Need %d arguments.' %
|
||||
(len(self.members)))
|
||||
'Illegal number of Arguments! Need %d arguments.' % len(self.members))
|
||||
# validate elements and return as list
|
||||
return tuple(sub(elem)
|
||||
for sub, elem in zip(self.members, value))
|
||||
for sub, elem in zip(self.members, value))
|
||||
except Exception as exc:
|
||||
raise BadValueError('Can not validate:', str(exc))
|
||||
|
||||
@@ -879,19 +829,16 @@ class TupleOf(DataType):
|
||||
|
||||
def format_value(self, value, unit=None):
|
||||
return '(%s)' % (', '.join([sub.format_value(elem)
|
||||
for sub, elem in zip(self.members, value)]))
|
||||
for sub, elem in zip(self.members, value)]))
|
||||
|
||||
def compatible(self, other):
|
||||
if not isinstance(other, TupleOf):
|
||||
raise BadValueError('incompatible datatypes')
|
||||
if len(self.members) != len(other.members) :
|
||||
if len(self.members) != len(other.members):
|
||||
raise BadValueError('incompatible datatypes')
|
||||
for a, b in zip(self.members, other.members):
|
||||
a.compatible(b)
|
||||
|
||||
def short_doc(self):
|
||||
return 'tuple of (%s)' % ', '.join(m.short_doc() for m in self.members)
|
||||
|
||||
|
||||
class ImmutableDict(dict):
|
||||
def _no(self, *args, **kwds):
|
||||
@@ -902,8 +849,8 @@ class ImmutableDict(dict):
|
||||
class StructOf(DataType):
|
||||
"""data structure with named fields
|
||||
|
||||
:param optional: (*sequence*) optional members
|
||||
:param members: each argument denotes <member name>=<member data type>
|
||||
:param optional: a list of optional members
|
||||
:param members: names as keys and types as values for all members
|
||||
"""
|
||||
def __init__(self, optional=None, **members):
|
||||
super().__init__()
|
||||
@@ -919,15 +866,15 @@ class StructOf(DataType):
|
||||
if name not in members:
|
||||
raise ProgrammingError(
|
||||
'Only members of StructOf may be declared as optional!')
|
||||
self.default = dict((k,el.default) for k, el in members.items())
|
||||
self.default = dict((k, el.default) for k, el in members.items())
|
||||
|
||||
def copy(self):
|
||||
"""DataType.copy does not work when members contain enums"""
|
||||
return StructOf(self.optional, **{k: v.copy() for k,v in self.members.items()})
|
||||
return StructOf(self.optional, **{k: v.copy() for k, v in self.members.items()})
|
||||
|
||||
def export_datatype(self):
|
||||
res = dict(type='struct', members=dict((n, s.export_datatype())
|
||||
for n, s in list(self.members.items())))
|
||||
for n, s in list(self.members.items())))
|
||||
if self.optional:
|
||||
res['optional'] = self.optional
|
||||
return res
|
||||
@@ -979,18 +926,11 @@ class StructOf(DataType):
|
||||
except (AttributeError, TypeError, KeyError):
|
||||
raise BadValueError('incompatible datatypes')
|
||||
|
||||
def short_doc(self):
|
||||
return 'dict'
|
||||
|
||||
|
||||
class CommandType(DataType):
|
||||
"""command
|
||||
|
||||
a pseudo datatype for commands with arguments and return values
|
||||
|
||||
:param argument: None or the data type of the argument. multiple arguments may be simulated
|
||||
by TupleOf or StructOf
|
||||
:param result: None or the data type of the result
|
||||
"""
|
||||
IS_COMMAND = True
|
||||
|
||||
@@ -1049,16 +989,10 @@ class CommandType(DataType):
|
||||
except AttributeError:
|
||||
raise BadValueError('incompatible datatypes')
|
||||
|
||||
def short_doc(self):
|
||||
argument = self.argument.short_doc() if self.argument else ''
|
||||
result = ' -> %s' % self.argument.short_doc() if self.result else ''
|
||||
return '(%s)%s' % (argument, result) # return argument list only
|
||||
|
||||
|
||||
# internally used datatypes (i.e. only for programming the SEC-node)
|
||||
|
||||
class DataTypeType(DataType):
|
||||
"""DataType type"""
|
||||
def __call__(self, value):
|
||||
"""check if given value (a python obj) is a valid datatype
|
||||
|
||||
@@ -1102,9 +1036,7 @@ class ValueType(DataType):
|
||||
|
||||
|
||||
class NoneOr(DataType):
|
||||
"""validates a None or other
|
||||
|
||||
:param other: the other datatype"""
|
||||
"""validates a None or smth. else"""
|
||||
default = None
|
||||
|
||||
def __init__(self, other):
|
||||
@@ -1119,16 +1051,8 @@ class NoneOr(DataType):
|
||||
return None
|
||||
return self.other.export_value(value)
|
||||
|
||||
def short_doc(self):
|
||||
other = self.other.short_doc()
|
||||
return '%s or None' % other if other else None
|
||||
|
||||
|
||||
class OrType(DataType):
|
||||
"""validates one of the
|
||||
|
||||
:param types: each argument denotes one allowed type
|
||||
"""
|
||||
def __init__(self, *types):
|
||||
super().__init__()
|
||||
self.types = types
|
||||
@@ -1142,12 +1066,6 @@ class OrType(DataType):
|
||||
pass
|
||||
raise BadValueError("Invalid Value, must conform to one of %s" % (', '.join((str(t) for t in self.types))))
|
||||
|
||||
def short_doc(self):
|
||||
types = [t.short_doc() for t in self.types]
|
||||
if None in types:
|
||||
return None
|
||||
return ' or '.join(types)
|
||||
|
||||
|
||||
Int8 = IntRange(-(1 << 7), (1 << 7) - 1)
|
||||
Int16 = IntRange(-(1 << 15), (1 << 15) - 1)
|
||||
@@ -1161,12 +1079,6 @@ UInt64 = IntRange(0, (1 << 64) - 1)
|
||||
|
||||
# Goodie: Convenience Datatypes for Programming
|
||||
class LimitsType(TupleOf):
|
||||
"""limit (min, max) tuple
|
||||
|
||||
:param members: the type of both members
|
||||
|
||||
checks for min <= max
|
||||
"""
|
||||
def __init__(self, members):
|
||||
TupleOf.__init__(self, members, members)
|
||||
|
||||
@@ -1178,22 +1090,13 @@ class LimitsType(TupleOf):
|
||||
|
||||
|
||||
class StatusType(TupleOf):
|
||||
"""SECoP status type
|
||||
|
||||
:param enum: the status code enum type
|
||||
|
||||
allows to access enum members directly
|
||||
"""
|
||||
|
||||
# shorten initialisation and allow access to status enumMembers from status values
|
||||
def __init__(self, enum):
|
||||
TupleOf.__init__(self, EnumType(enum), StringType())
|
||||
self.enum = enum
|
||||
self._enum = enum
|
||||
|
||||
def __getattr__(self, key):
|
||||
enum = TupleOf.__getattr__(self, 'enum')
|
||||
if hasattr(enum, key):
|
||||
return getattr(enum, key)
|
||||
return TupleOf.__getattr__(self, key)
|
||||
return getattr(self._enum, key)
|
||||
|
||||
|
||||
def floatargs(kwds):
|
||||
|
||||
Reference in New Issue
Block a user