diff --git a/secop/client/__init__.py b/secop/client/__init__.py index 9bfa570..36e7d55 100644 --- a/secop/client/__init__.py +++ b/secop/client/__init__.py @@ -30,7 +30,7 @@ from collections import defaultdict from secop.lib import mkthread, formatExtendedTraceback, formatExtendedStack from secop.lib.asynconn import AsynConn, ConnectionClosed -from secop.datatypes import get_datatype +from secop.datatypes import get_datatype, EnumType from secop.protocol.interface import encode_msg_frame, decode_msg from secop.protocol.messages import REQUEST2REPLY, ERRORPREFIX, EVENTREPLY, WRITEREQUEST, WRITEREPLY, \ READREQUEST, READREPLY, IDENTREQUEST, IDENTPREFIX, ENABLEEVENTSREQUEST, COMMANDREQUEST, DESCRIPTIONREQUEST @@ -428,12 +428,13 @@ class SecopClient(ProxyClient): commands = {} accessibles = moddescr['accessibles'] for aname, aentry in accessibles.items(): - aentry = dict(aentry, datatype=get_datatype(aentry['datainfo'])) iname = self.internalize_name(aname) + datatype = get_datatype(aentry['datainfo'], iname) + aentry = dict(aentry, datatype=datatype) ident = '%s:%s' % (modname, aname) self.identifier[modname, iname] = ident self.internal[ident] = modname, iname - if aentry['datainfo']['type'] == 'command': + if datatype.IS_COMMAND: commands[iname] = aentry else: parameters[iname] = aentry diff --git a/secop/datatypes.py b/secop/datatypes.py index 4418561..bcff35a 100644 --- a/secop/datatypes.py +++ b/secop/datatypes.py @@ -1066,41 +1066,72 @@ class StatusType(TupleOf): return TupleOf.__getattr__(self, key) +def floatargs(kwds): + return {k: v for k, v in kwds.items() if k in + {'unit', 'fmtstr', 'absolute_resolution', 'relative_resolution'}} + + # argumentnames to lambda from spec! +# **kwds at the end are for must-ignore policy DATATYPES = dict( - bool =BoolType, - int =lambda min, max, **kwds: IntRange(minval=min, maxval=max, **kwds), - scaled =lambda scale, min, max, **kwds: ScaledInteger(scale=scale, minval=min*scale, maxval=max*scale, **kwds), - double =lambda min=None, max=None, **kwds: FloatRange(minval=min, maxval=max, **kwds), - blob =lambda maxbytes, minbytes=0: BLOBType(minbytes=minbytes, maxbytes=maxbytes), - string =lambda minchars=0, maxchars=None, isUTF8=False: StringType(minchars=minchars, maxchars=maxchars, isUTF8=isUTF8), - array =lambda maxlen, members, minlen=0: ArrayOf(get_datatype(members), minlen=minlen, maxlen=maxlen), - tuple =lambda members: TupleOf(*tuple(map(get_datatype, members))), - enum =lambda members: EnumType('', members=members), - struct =lambda members, optional=None: StructOf(optional, - **dict((n, get_datatype(t)) for n, t in list(members.items()))), - command = lambda argument=None, result=None: CommandType(get_datatype(argument), get_datatype(result)), - limit = lambda members: LimitsType(get_datatype(members)), + bool = lambda **kwds: + BoolType(), + + int = lambda min, max, **kwds: + IntRange(minval=min, maxval=max), + + scaled = lambda scale, min, max, **kwds: + ScaledInteger(scale=scale, minval=min*scale, maxval=max*scale, **floatargs(kwds)), + + double = lambda min=None, max=None, **kwds: + FloatRange(minval=min, maxval=max, **floatargs(kwds)), + + blob = lambda maxbytes, minbytes=0, **kwds: + BLOBType(minbytes=minbytes, maxbytes=maxbytes), + + string = lambda minchars=0, maxchars=None, isUTF8=False, **kwds: + StringType(minchars=minchars, maxchars=maxchars, isUTF8=isUTF8), + + array = lambda maxlen, members, minlen=0, pname='', **kwds: + ArrayOf(get_datatype(members, pname), minlen=minlen, maxlen=maxlen), + + tuple = lambda members, pname='', **kwds: + TupleOf(*tuple((get_datatype(t, pname) for t in members))), + + enum = lambda members, pname='', **kwds: + EnumType(pname, members=members), + + struct = lambda members, optional=None, pname='', **kwds: + StructOf(optional, **dict((n, get_datatype(t, pname)) for n, t in list(members.items()))), + + command = lambda argument=None, result=None, pname='', **kwds: + CommandType(get_datatype(argument, pname), get_datatype(result)), + + limit = lambda members, pname='', **kwds: + LimitsType(get_datatype(members, pname)), ) # important for getting the right datatype from formerly jsonified descr. -def get_datatype(json): +def get_datatype(json, pname=''): """returns a DataType object from description inverse of .export_datatype() + the pname argument, if given, is used to name EnumTypes from the parameter name """ if json is None: return json if isinstance(json, list) and len(json) == 2: - base, args = json # still allow old syntax + base, kwargs = json # still allow old syntax else: try: - args = json.copy() - base = args.pop('type') + kwargs = json.copy() + base = kwargs.pop('type') except (TypeError, KeyError, AttributeError): raise BadValueError('a data descriptor must be a dict containing a "type" key, not %r' % json) + try: - return DATATYPES[base](**args) + return DATATYPES[base](pname=pname, **kwargs) except Exception as e: + print(base, kwargs, repr(e)) raise BadValueError('invalid data descriptor: %r (%s)' % (json, str(e))) diff --git a/secop/lib/enum.py b/secop/lib/enum.py index 3bc7d45..591dd13 100755 --- a/secop/lib/enum.py +++ b/secop/lib/enum.py @@ -98,7 +98,7 @@ class EnumMember: # be human readable (for debugging) def __repr__(self): - return '<%s.%s (%d)>' % (self.enum.name, self.name, self.value) + 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? diff --git a/secop/modules.py b/secop/modules.py index c971a64..0ff3bc4 100644 --- a/secop/modules.py +++ b/secop/modules.py @@ -176,7 +176,7 @@ class Module(HasProperties, metaclass=ModuleMeta): # paramobj might also be a command (not sure if this is needed) if paramobj: if propname == 'datatype': - paramobj.setProperty('datatype', get_datatype(cfgdict.pop(k))) + paramobj.setProperty('datatype', get_datatype(cfgdict.pop(k), k)) elif propname in paramobj.getProperties(): paramobj.setProperty(propname, cfgdict.pop(k)) else: