Remove py2 support
Change-Id: Ieeaeb3b8efcae004e94aea6c1d2703c9782a8650 Reviewed-on: https://forge.frm2.tum.de/review/c/sine2020/secop/playground/+/21320 Tested-by: Enrico Faulhaber <enrico.faulhaber@frm2.tum.de> Reviewed-by: Enrico Faulhaber <enrico.faulhaber@frm2.tum.de>
This commit is contained in:
parent
04032079d7
commit
70a9c42a7a
12
Makefile
12
Makefile
@ -11,7 +11,7 @@ demo:
|
||||
@ps aux|grep [s]ecop-server|awk '{print $$2}'|xargs kill
|
||||
|
||||
build:
|
||||
python setup.py build
|
||||
python3 setup.py build
|
||||
|
||||
clean:
|
||||
find . -name '*.pyc' -delete
|
||||
@ -19,20 +19,20 @@ clean:
|
||||
$(MAKE) -C doc clean
|
||||
|
||||
install: build
|
||||
python setup.py install
|
||||
python3 setup.py install
|
||||
|
||||
test:
|
||||
ifdef T
|
||||
python $(shell which pytest) -v test -l -k $(T)
|
||||
python3 $(shell which pytest) -v test -l -k $(T)
|
||||
else
|
||||
python $(shell which pytest) -v test -l
|
||||
python3 $(shell which pytest) -v test -l
|
||||
endif
|
||||
|
||||
test-verbose:
|
||||
python $(shell which pytest) -v test -s
|
||||
python3 $(shell which pytest) -v test -s
|
||||
|
||||
test-coverage:
|
||||
python $(shell which pytest) -v test --cov=secop
|
||||
python3 $(shell which pytest) -v test --cov=secop
|
||||
|
||||
doc:
|
||||
$(MAKE) -C doc html
|
||||
|
@ -21,7 +21,6 @@
|
||||
#
|
||||
# *****************************************************************************
|
||||
|
||||
from __future__ import division, print_function
|
||||
|
||||
import os
|
||||
from os import path
|
||||
|
@ -1,4 +1,4 @@
|
||||
#!/usr/bin/env python
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
# *****************************************************************************
|
||||
#
|
||||
|
@ -1,4 +1,4 @@
|
||||
#!/usr/bin/env python
|
||||
#!/usr/bin/env python3
|
||||
# pylint: disable=invalid-name
|
||||
# -*- coding: utf-8 -*-
|
||||
# *****************************************************************************
|
||||
|
@ -1,4 +1,4 @@
|
||||
#!/usr/bin/env python
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
# *****************************************************************************
|
||||
#
|
||||
|
@ -21,19 +21,11 @@
|
||||
# *****************************************************************************
|
||||
"""basic validators (for properties)"""
|
||||
|
||||
from __future__ import division, print_function
|
||||
|
||||
import re
|
||||
|
||||
from secop.errors import ProgrammingError
|
||||
|
||||
try:
|
||||
# py2
|
||||
unicode
|
||||
except NameError:
|
||||
# py3
|
||||
unicode = str # pylint: disable=redefined-builtin
|
||||
|
||||
|
||||
def FloatProperty(value):
|
||||
return float(value)
|
||||
@ -43,80 +35,80 @@ def PositiveFloatProperty(value):
|
||||
value = float(value)
|
||||
if value > 0:
|
||||
return value
|
||||
raise ValueError(u'Value must be >0 !')
|
||||
raise ValueError('Value must be >0 !')
|
||||
|
||||
|
||||
def NonNegativeFloatProperty(value):
|
||||
value = float(value)
|
||||
if value >= 0:
|
||||
return value
|
||||
raise ValueError(u'Value must be >=0 !')
|
||||
raise ValueError('Value must be >=0 !')
|
||||
|
||||
|
||||
def IntProperty(value):
|
||||
if int(value) == float(value):
|
||||
return int(value)
|
||||
raise ValueError(u'Can\'t convert %r to int!' % value)
|
||||
raise ValueError('Can\'t convert %r to int!' % value)
|
||||
|
||||
|
||||
def PositiveIntProperty(value):
|
||||
value = IntProperty(value)
|
||||
if value > 0:
|
||||
return value
|
||||
raise ValueError(u'Value must be >0 !')
|
||||
raise ValueError('Value must be >0 !')
|
||||
|
||||
|
||||
def NonNegativeIntProperty(value):
|
||||
value = IntProperty(value)
|
||||
if value >= 0:
|
||||
return value
|
||||
raise ValueError(u'Value must be >=0 !')
|
||||
raise ValueError('Value must be >=0 !')
|
||||
|
||||
|
||||
def BoolProperty(value):
|
||||
try:
|
||||
if value.lower() in [u'0', u'false', u'no', u'off',]:
|
||||
if value.lower() in ['0', 'false', 'no', 'off',]:
|
||||
return False
|
||||
if value.lower() in [u'1', u'true', u'yes', u'on', ]:
|
||||
if value.lower() in ['1', 'true', 'yes', 'on', ]:
|
||||
return True
|
||||
except AttributeError: # was no string
|
||||
if bool(value) == value:
|
||||
return value
|
||||
raise ValueError(u'%r is no valid boolean: try one of True, False, "on", "off",...' % value)
|
||||
raise ValueError('%r is no valid boolean: try one of True, False, "on", "off",...' % value)
|
||||
|
||||
|
||||
def StringProperty(value):
|
||||
return unicode(value)
|
||||
return str(value)
|
||||
|
||||
|
||||
def UnitProperty(value):
|
||||
# probably too simple!
|
||||
for s in unicode(value):
|
||||
if s.lower() not in u'°abcdefghijklmnopqrstuvwxyz':
|
||||
raise ValueError(u'%r is not a valid unit!')
|
||||
for s in str(value):
|
||||
if s.lower() not in '°abcdefghijklmnopqrstuvwxyz':
|
||||
raise ValueError('%r is not a valid unit!')
|
||||
|
||||
|
||||
def FmtStrProperty(value, regexp=re.compile(r'^%\.?\d+[efg]$')):
|
||||
value=unicode(value)
|
||||
value=str(value)
|
||||
if regexp.match(value):
|
||||
return value
|
||||
raise ValueError(u'%r is not a valid fmtstr!' % value)
|
||||
raise ValueError('%r is not a valid fmtstr!' % value)
|
||||
|
||||
|
||||
def OneOfProperty(*args):
|
||||
# literally oneof!
|
||||
if not args:
|
||||
raise ProgrammingError(u'OneOfProperty needs some argumets to check against!')
|
||||
raise ProgrammingError('OneOfProperty needs some argumets to check against!')
|
||||
def OneOfChecker(value):
|
||||
if value not in args:
|
||||
raise ValueError(u'Value must be one of %r' % list(args))
|
||||
raise ValueError('Value must be one of %r' % list(args))
|
||||
return value
|
||||
return OneOfChecker
|
||||
|
||||
|
||||
def NoneOr(checker):
|
||||
if not callable(checker):
|
||||
raise ProgrammingError(u'NoneOr needs a basic validator as Argument!')
|
||||
raise ProgrammingError('NoneOr needs a basic validator as Argument!')
|
||||
def NoneOrChecker(value):
|
||||
if value is None:
|
||||
return None
|
||||
@ -126,13 +118,13 @@ def NoneOr(checker):
|
||||
|
||||
def EnumProperty(**kwds):
|
||||
if not kwds:
|
||||
raise ProgrammingError(u'EnumProperty needs a mapping!')
|
||||
raise ProgrammingError('EnumProperty needs a mapping!')
|
||||
def EnumChecker(value):
|
||||
if value in kwds:
|
||||
return kwds[value]
|
||||
if value in kwds.values():
|
||||
return value
|
||||
raise ValueError(u'Value must be one of %r' % list(kwds))
|
||||
raise ValueError('Value must be one of %r' % list(kwds))
|
||||
return EnumChecker
|
||||
|
||||
def TupleProperty(*checkers):
|
||||
@ -140,16 +132,16 @@ def TupleProperty(*checkers):
|
||||
checkers = [None]
|
||||
for c in checkers:
|
||||
if not callable(c):
|
||||
raise ProgrammingError(u'TupleProperty needs basic validators as Arguments!')
|
||||
raise ProgrammingError('TupleProperty needs basic validators as Arguments!')
|
||||
def TupleChecker(values):
|
||||
if len(values)==len(checkers):
|
||||
return tuple(c(v) for c, v in zip(checkers, values))
|
||||
raise ValueError(u'Value needs %d elements!' % len(checkers))
|
||||
raise ValueError('Value needs %d elements!' % len(checkers))
|
||||
return TupleChecker
|
||||
|
||||
def ListOfProperty(checker):
|
||||
if not callable(checker):
|
||||
raise ProgrammingError(u'ListOfProperty needs a basic validator as Argument!')
|
||||
raise ProgrammingError('ListOfProperty needs a basic validator as Argument!')
|
||||
def ListOfChecker(values):
|
||||
return [checker(v) for v in values]
|
||||
return ListOfChecker
|
||||
|
@ -23,7 +23,6 @@
|
||||
|
||||
# nothing here yet.
|
||||
|
||||
from __future__ import print_function
|
||||
|
||||
import code
|
||||
import socket
|
||||
|
@ -21,9 +21,9 @@
|
||||
# *****************************************************************************
|
||||
"""Define Client side proxies"""
|
||||
|
||||
from __future__ import division, print_function
|
||||
|
||||
import json
|
||||
import queue
|
||||
import socket
|
||||
import threading
|
||||
import time
|
||||
@ -43,15 +43,6 @@ from secop.protocol.messages import BUFFERREQUEST, COMMANDREQUEST, \
|
||||
HEARTBEATREQUEST, HELPREQUEST, IDENTREQUEST, READREPLY, \
|
||||
READREQUEST, REQUEST2REPLY, WRITEREPLY, WRITEREQUEST
|
||||
|
||||
try:
|
||||
# py3
|
||||
import queue
|
||||
except ImportError:
|
||||
# py2
|
||||
import Queue as queue
|
||||
|
||||
|
||||
|
||||
|
||||
class TCPConnection(object):
|
||||
# disguise a TCP connection as serial one
|
||||
|
@ -23,7 +23,6 @@
|
||||
|
||||
# pylint: disable=abstract-method
|
||||
|
||||
from __future__ import division, print_function
|
||||
|
||||
from base64 import b64decode, b64encode
|
||||
|
||||
@ -31,22 +30,15 @@ from secop.errors import ProgrammingError, ProtocolError, BadValueError
|
||||
from secop.lib.enum import Enum
|
||||
from secop.parse import Parser
|
||||
|
||||
try:
|
||||
# py2
|
||||
unicode
|
||||
except NameError:
|
||||
# py3
|
||||
unicode = str # pylint: disable=redefined-builtin
|
||||
|
||||
|
||||
# Only export these classes for 'from secop.datatypes import *'
|
||||
__all__ = [
|
||||
u'DataType',
|
||||
u'FloatRange', u'IntRange',
|
||||
u'BoolType', u'EnumType',
|
||||
u'BLOBType', u'StringType',
|
||||
u'TupleOf', u'ArrayOf', u'StructOf',
|
||||
u'CommandType',
|
||||
'DataType',
|
||||
'FloatRange', 'IntRange',
|
||||
'BoolType', 'EnumType',
|
||||
'BLOBType', 'StringType',
|
||||
'TupleOf', 'ArrayOf', 'StructOf',
|
||||
'CommandType',
|
||||
]
|
||||
|
||||
# *DEFAULT* limits for IntRange/ScaledIntegers transport serialisation
|
||||
@ -58,8 +50,8 @@ Parser = Parser()
|
||||
# base class for all DataTypes
|
||||
class DataType(object):
|
||||
IS_COMMAND = False
|
||||
unit = u''
|
||||
fmtstr = u'%r'
|
||||
unit = ''
|
||||
fmtstr = '%r'
|
||||
default = None
|
||||
|
||||
def __call__(self, value):
|
||||
@ -90,7 +82,7 @@ class DataType(object):
|
||||
return value
|
||||
|
||||
def format_value(self, value, unit=None):
|
||||
"""format a value of this type into a unicode string
|
||||
"""format a value of this type into a str string
|
||||
|
||||
This is intended for 'nice' formatting for humans and is NOT
|
||||
the opposite of :meth:`from_string`
|
||||
@ -128,23 +120,23 @@ class FloatRange(DataType):
|
||||
def __init__(self, minval=None, maxval=None, unit=None, fmtstr=None,
|
||||
absolute_resolution=None, relative_resolution=None,):
|
||||
self._defaults = {}
|
||||
self.set_prop('min', minval, float(u'-inf'), float)
|
||||
self.set_prop('max', maxval, float(u'+inf'), float)
|
||||
self.set_prop('unit', unit, u'', unicode)
|
||||
self.set_prop('fmtstr', fmtstr, u'%g', unicode)
|
||||
self.set_prop('min', minval, float('-inf'), float)
|
||||
self.set_prop('max', maxval, float('+inf'), float)
|
||||
self.set_prop('unit', unit, '', str)
|
||||
self.set_prop('fmtstr', fmtstr, '%g', str)
|
||||
self.set_prop('absolute_resolution', absolute_resolution, 0.0, float)
|
||||
self.set_prop('relative_resolution', relative_resolution, 1.2e-7, float)
|
||||
self.default = 0 if self.min <= 0 <= self.max else self.min
|
||||
|
||||
# check values
|
||||
if self.min > self.max:
|
||||
raise BadValueError(u'max must be larger then min!')
|
||||
raise BadValueError('max must be larger then min!')
|
||||
if '%' not in self.fmtstr:
|
||||
raise BadValueError(u'Invalid fmtstr!')
|
||||
raise BadValueError('Invalid fmtstr!')
|
||||
if self.absolute_resolution < 0:
|
||||
raise BadValueError(u'absolute_resolution MUST be >=0')
|
||||
raise BadValueError('absolute_resolution MUST be >=0')
|
||||
if self.relative_resolution < 0:
|
||||
raise BadValueError(u'relative_resolution MUST be >=0')
|
||||
raise BadValueError('relative_resolution MUST be >=0')
|
||||
|
||||
def export_datatype(self):
|
||||
return self.get_info(type='double')
|
||||
@ -153,11 +145,11 @@ class FloatRange(DataType):
|
||||
try:
|
||||
value = float(value)
|
||||
except Exception:
|
||||
raise BadValueError(u'Can not __call__ %r to float' % value)
|
||||
raise BadValueError('Can not __call__ %r to float' % value)
|
||||
prec = max(abs(value * self.relative_resolution), self.absolute_resolution)
|
||||
if self.min - prec <= value <= self.max + prec:
|
||||
return min(max(value, self.min), self.max)
|
||||
raise BadValueError(u'%.14g should be a float between %.14g and %.14g' %
|
||||
raise BadValueError('%.14g should be a float between %.14g and %.14g' %
|
||||
(value, self.min, self.max))
|
||||
|
||||
def __repr__(self):
|
||||
@ -166,7 +158,7 @@ class FloatRange(DataType):
|
||||
hints['minval'] = hints.pop('min')
|
||||
if 'max' in hints:
|
||||
hints['maxval'] = hints.pop('max')
|
||||
return u'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"""
|
||||
@ -184,7 +176,7 @@ class FloatRange(DataType):
|
||||
if unit is None:
|
||||
unit = self.unit
|
||||
if unit:
|
||||
return u' '.join([self.fmtstr % value, unit])
|
||||
return ' '.join([self.fmtstr % value, unit])
|
||||
return self.fmtstr % value
|
||||
|
||||
|
||||
@ -196,11 +188,11 @@ class IntRange(DataType):
|
||||
self.max = DEFAULT_MAX_INT if maxval is None else int(maxval)
|
||||
self.default = 0 if self.min <= 0 <= self.max else self.min
|
||||
# a unit on an int is now allowed in SECoP, but do we need them in Frappy?
|
||||
# self.set_prop('unit', unit, u'', unicode)
|
||||
# self.set_prop('unit', unit, '', str)
|
||||
|
||||
# check values
|
||||
if self.min > self.max:
|
||||
raise BadValueError(u'Max must be larger then min!')
|
||||
raise BadValueError('Max must be larger then min!')
|
||||
|
||||
def export_datatype(self):
|
||||
return dict(type='int', min=self.min, max=self.max)
|
||||
@ -209,17 +201,17 @@ class IntRange(DataType):
|
||||
try:
|
||||
value = int(value)
|
||||
if value < self.min:
|
||||
raise BadValueError(u'%r should be an int between %d and %d' %
|
||||
raise BadValueError('%r should be an int between %d and %d' %
|
||||
(value, self.min, self.max or 0))
|
||||
if value > self.max:
|
||||
raise BadValueError(u'%r should be an int between %d and %d' %
|
||||
raise BadValueError('%r should be an int between %d and %d' %
|
||||
(value, self.min or 0, self.max))
|
||||
return value
|
||||
except Exception:
|
||||
raise BadValueError(u'Can not convert %r to int' % value)
|
||||
raise BadValueError('Can not convert %r to int' % value)
|
||||
|
||||
def __repr__(self):
|
||||
return u'IntRange(%d, %d)' % (self.min, self.max)
|
||||
return 'IntRange(%d, %d)' % (self.min, self.max)
|
||||
|
||||
def export_value(self, value):
|
||||
"""returns a python object fit for serialisation"""
|
||||
@ -234,7 +226,7 @@ class IntRange(DataType):
|
||||
return self(value)
|
||||
|
||||
def format_value(self, value, unit=None):
|
||||
return u'%d' % value
|
||||
return '%d' % value
|
||||
|
||||
|
||||
class ScaledInteger(DataType):
|
||||
@ -248,9 +240,9 @@ class ScaledInteger(DataType):
|
||||
self._defaults = {}
|
||||
self.scale = float(scale)
|
||||
if not self.scale > 0:
|
||||
raise BadValueError(u'Scale MUST be positive!')
|
||||
self.set_prop('unit', unit, u'', unicode)
|
||||
self.set_prop('fmtstr', fmtstr, u'%g', unicode)
|
||||
raise BadValueError('Scale MUST be positive!')
|
||||
self.set_prop('unit', unit, '', str)
|
||||
self.set_prop('fmtstr', fmtstr, '%g', str)
|
||||
self.set_prop('absolute_resolution', absolute_resolution, self.scale, float)
|
||||
self.set_prop('relative_resolution', relative_resolution, 1.2e-7, float)
|
||||
|
||||
@ -260,13 +252,13 @@ class ScaledInteger(DataType):
|
||||
|
||||
# check values
|
||||
if self.min > self.max:
|
||||
raise BadValueError(u'Max must be larger then min!')
|
||||
raise BadValueError('Max must be larger then min!')
|
||||
if '%' not in self.fmtstr:
|
||||
raise BadValueError(u'Invalid fmtstr!')
|
||||
raise BadValueError('Invalid fmtstr!')
|
||||
if self.absolute_resolution < 0:
|
||||
raise BadValueError(u'absolute_resolution MUST be >=0')
|
||||
raise BadValueError('absolute_resolution MUST be >=0')
|
||||
if self.relative_resolution < 0:
|
||||
raise BadValueError(u'relative_resolution MUST be >=0')
|
||||
raise BadValueError('relative_resolution MUST be >=0')
|
||||
# Remark: Datatype.copy() will round min, max to a multiple of self.scale
|
||||
# this should be o.k.
|
||||
|
||||
@ -279,13 +271,13 @@ class ScaledInteger(DataType):
|
||||
try:
|
||||
value = float(value)
|
||||
except Exception:
|
||||
raise BadValueError(u'Can not convert %r to float' % value)
|
||||
raise BadValueError('Can not convert %r to float' % value)
|
||||
prec = max(self.scale, abs(value * self.relative_resolution),
|
||||
self.absolute_resolution)
|
||||
if self.min - prec <= value <= self.max + prec:
|
||||
value = min(max(value, self.min), self.max)
|
||||
else:
|
||||
raise BadValueError(u'%g should be a float between %g and %g' %
|
||||
raise BadValueError('%g should be a float between %g and %g' %
|
||||
(value, self.min, self.max))
|
||||
intval = int((value + self.scale * 0.5) // self.scale)
|
||||
value = float(intval * self.scale)
|
||||
@ -295,7 +287,7 @@ class ScaledInteger(DataType):
|
||||
hints = self.get_info(scale='%g' % self.scale,
|
||||
min = int((self.min + self.scale * 0.5) // self.scale),
|
||||
max = int((self.max + self.scale * 0.5) // self.scale))
|
||||
return u'ScaledInteger(%s)' % (', '.join('%s=%r' % kv for kv in hints.items()))
|
||||
return 'ScaledInteger(%s)' % (', '.join('%s=%r' % kv for kv in hints.items()))
|
||||
|
||||
def export_value(self, value):
|
||||
"""returns a python object fit for serialisation"""
|
||||
@ -314,17 +306,17 @@ class ScaledInteger(DataType):
|
||||
if unit is None:
|
||||
unit = self.unit
|
||||
if unit:
|
||||
return u' '.join([self.fmtstr % value, unit])
|
||||
return ' '.join([self.fmtstr % value, unit])
|
||||
return self.fmtstr % value
|
||||
|
||||
|
||||
class EnumType(DataType):
|
||||
|
||||
def __init__(self, enum_or_name='', **kwds):
|
||||
if u'members' in kwds:
|
||||
if 'members' in kwds:
|
||||
kwds = dict(kwds)
|
||||
kwds.update(kwds[u'members'])
|
||||
kwds.pop(u'members')
|
||||
kwds.update(kwds['members'])
|
||||
kwds.pop('members')
|
||||
self._enum = Enum(enum_or_name, **kwds)
|
||||
self.default = self._enum[self._enum.members[0]]
|
||||
|
||||
@ -336,7 +328,7 @@ class EnumType(DataType):
|
||||
return {'type': 'enum', 'members':dict((m.name, m.value) for m in self._enum.members)}
|
||||
|
||||
def __repr__(self):
|
||||
return u"EnumType(%r, %s)" % (self._enum.name, ', '.join(u'%s=%d' %(m.name, m.value) for m in self._enum.members))
|
||||
return u"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"""
|
||||
@ -351,13 +343,13 @@ class EnumType(DataType):
|
||||
try:
|
||||
return self._enum[value]
|
||||
except (KeyError, TypeError): # TypeError will be raised when value is not hashable
|
||||
raise BadValueError(u'%r is not a member of enum %r' % (value, self._enum))
|
||||
raise BadValueError('%r is not a member of enum %r' % (value, self._enum))
|
||||
|
||||
def from_string(self, text):
|
||||
return self(text)
|
||||
|
||||
def format_value(self, value, unit=None):
|
||||
return u'%s<%s>' % (self._enum[value].name, self._enum[value].value)
|
||||
return '%s<%s>' % (self._enum[value].name, self._enum[value].value)
|
||||
|
||||
|
||||
class BLOBType(DataType):
|
||||
@ -373,28 +365,28 @@ class BLOBType(DataType):
|
||||
self.set_prop('minbytes', minbytes, 0, int)
|
||||
self.maxbytes = int(maxbytes)
|
||||
if self.minbytes < 0:
|
||||
raise BadValueError(u'sizes must be bigger than or equal to 0!')
|
||||
raise BadValueError('sizes must be bigger than or equal to 0!')
|
||||
elif self.minbytes > self.maxbytes:
|
||||
raise BadValueError(u'maxbytes must be bigger than or equal to minbytes!')
|
||||
raise BadValueError('maxbytes must be bigger than or equal to minbytes!')
|
||||
self.default = b'\0' * self.minbytes
|
||||
|
||||
def export_datatype(self):
|
||||
return self.get_info(type='blob', maxbytes=self.maxbytes)
|
||||
|
||||
def __repr__(self):
|
||||
return u'BLOBType(%d, %d)' % (self.minbytes, self.maxbytes)
|
||||
return 'BLOBType(%d, %d)' % (self.minbytes, self.maxbytes)
|
||||
|
||||
def __call__(self, value):
|
||||
"""return the validated (internal) value or raise"""
|
||||
if not isinstance(value, bytes):
|
||||
raise BadValueError(u'%r has the wrong type!' % value)
|
||||
raise BadValueError('%r has the wrong type!' % value)
|
||||
size = len(value)
|
||||
if size < self.minbytes:
|
||||
raise BadValueError(
|
||||
u'%r must be at least %d bytes long!' % (value, self.minbytes))
|
||||
'%r must be at least %d bytes long!' % (value, self.minbytes))
|
||||
if size > self.maxbytes:
|
||||
raise BadValueError(
|
||||
u'%r must be at most %d bytes long!' % (value, self.maxbytes))
|
||||
'%r must be at most %d bytes long!' % (value, self.maxbytes))
|
||||
return value
|
||||
|
||||
def export_value(self, value):
|
||||
@ -425,49 +417,49 @@ class StringType(DataType):
|
||||
self.set_prop('maxchars', maxchars, self.MAXCHARS, int)
|
||||
self.set_prop('isUTF8', isUTF8, False, bool)
|
||||
if self.minchars < 0:
|
||||
raise BadValueError(u'sizes must be bigger than or equal to 0!')
|
||||
raise BadValueError('sizes must be bigger than or equal to 0!')
|
||||
elif self.minchars > self.maxchars:
|
||||
raise BadValueError(u'maxchars must be bigger than or equal to minchars!')
|
||||
self.default = u' ' * self.minchars
|
||||
raise BadValueError('maxchars must be bigger than or equal to minchars!')
|
||||
self.default = ' ' * self.minchars
|
||||
|
||||
def export_datatype(self):
|
||||
return self.get_info(type='string')
|
||||
|
||||
def __repr__(self):
|
||||
return u'StringType(%s)' % (', '.join('%s=%r' % kv for kv in self.get_info().items()))
|
||||
return 'StringType(%s)' % (', '.join('%s=%r' % kv for kv in self.get_info().items()))
|
||||
|
||||
def __call__(self, value):
|
||||
"""return the validated (internal) value or raise"""
|
||||
if type(value) not in (unicode, str):
|
||||
raise BadValueError(u'%r has the wrong type!' % value)
|
||||
if not isinstance(value, str):
|
||||
raise BadValueError('%r has the wrong type!' % value)
|
||||
if not self.isUTF8:
|
||||
try:
|
||||
value.encode('ascii')
|
||||
except UnicodeEncodeError:
|
||||
raise BadValueError(u'%r contains non-ascii character!' % value)
|
||||
raise BadValueError('%r contains non-ascii character!' % value)
|
||||
size = len(value)
|
||||
if size < self.minchars:
|
||||
raise BadValueError(
|
||||
u'%r must be at least %d bytes long!' % (value, self.minchars))
|
||||
'%r must be at least %d bytes long!' % (value, self.minchars))
|
||||
if size > self.maxchars:
|
||||
raise BadValueError(
|
||||
u'%r must be at most %d bytes long!' % (value, self.maxchars))
|
||||
if u'\0' in value:
|
||||
'%r must be at most %d bytes long!' % (value, self.maxchars))
|
||||
if '\0' in value:
|
||||
raise BadValueError(
|
||||
u'Strings are not allowed to embed a \\0! Use a Blob instead!')
|
||||
'Strings are not allowed to embed a \\0! Use a Blob instead!')
|
||||
return value
|
||||
|
||||
def export_value(self, value):
|
||||
"""returns a python object fit for serialisation"""
|
||||
return u'%s' % value
|
||||
return '%s' % value
|
||||
|
||||
def import_value(self, value):
|
||||
"""returns a python object from serialisation"""
|
||||
# XXX: do we keep it as unicode str, or convert it to something else? (UTF-8 maybe?)
|
||||
return unicode(value)
|
||||
# XXX: do we keep it as str str, or convert it to something else? (UTF-8 maybe?)
|
||||
return str(value)
|
||||
|
||||
def from_string(self, text):
|
||||
value = unicode(text)
|
||||
value = str(text)
|
||||
return self(value)
|
||||
|
||||
def format_value(self, value, unit=None):
|
||||
@ -485,7 +477,7 @@ class TextType(StringType):
|
||||
super(TextType, self).__init__(0, maxchars)
|
||||
|
||||
def __repr__(self):
|
||||
return u'TextType(%d, %d)' % (self.minchars, self.maxchars)
|
||||
return 'TextType(%d, %d)' % (self.minchars, self.maxchars)
|
||||
|
||||
def copy(self):
|
||||
# DataType.copy will not work, because it is exported as 'string'
|
||||
@ -500,15 +492,15 @@ class BoolType(DataType):
|
||||
return {'type': 'bool'}
|
||||
|
||||
def __repr__(self):
|
||||
return u'BoolType()'
|
||||
return 'BoolType()'
|
||||
|
||||
def __call__(self, value):
|
||||
"""return the validated (internal) value or raise"""
|
||||
if value in [0, u'0', u'False', u'false', u'no', u'off', False]:
|
||||
if value in [0, '0', 'False', 'false', 'no', 'off', False]:
|
||||
return False
|
||||
if value in [1, u'1', u'True', u'true', u'yes', u'on', True]:
|
||||
if value in [1, '1', 'True', 'true', 'yes', 'on', True]:
|
||||
return True
|
||||
raise BadValueError(u'%r is not a boolean value!' % value)
|
||||
raise BadValueError('%r is not a boolean value!' % value)
|
||||
|
||||
def export_value(self, value):
|
||||
"""returns a python object fit for serialisation"""
|
||||
@ -539,7 +531,7 @@ class ArrayOf(DataType):
|
||||
def __init__(self, members, minlen=0, maxlen=None, unit=None):
|
||||
if not isinstance(members, DataType):
|
||||
raise BadValueError(
|
||||
u'ArrayOf only works with a DataType as first argument!')
|
||||
'ArrayOf only works with a DataType as first argument!')
|
||||
# one argument -> exactly that size
|
||||
# argument default to 100
|
||||
if maxlen is None:
|
||||
@ -551,11 +543,11 @@ class ArrayOf(DataType):
|
||||
self.minlen = int(minlen)
|
||||
self.maxlen = int(maxlen)
|
||||
if self.minlen < 0:
|
||||
raise BadValueError(u'sizes must be > 0')
|
||||
raise BadValueError('sizes must be > 0')
|
||||
elif self.maxlen < 1:
|
||||
raise BadValueError(u'Maximum size must be >= 1!')
|
||||
raise BadValueError('Maximum size must be >= 1!')
|
||||
elif self.minlen > self.maxlen:
|
||||
raise BadValueError(u'maxlen must be bigger than or equal to minlen!')
|
||||
raise BadValueError('maxlen must be bigger than or equal to minlen!')
|
||||
self.default = [members.default] * self.minlen
|
||||
|
||||
def export_datatype(self):
|
||||
@ -563,7 +555,7 @@ class ArrayOf(DataType):
|
||||
members=self.members.export_datatype())
|
||||
|
||||
def __repr__(self):
|
||||
return u'ArrayOf(%s, %s, %s)' % (
|
||||
return 'ArrayOf(%s, %s, %s)' % (
|
||||
repr(self.members), self.minlen, self.maxlen)
|
||||
|
||||
def __call__(self, value):
|
||||
@ -572,15 +564,15 @@ class ArrayOf(DataType):
|
||||
# check number of elements
|
||||
if self.minlen is not None and len(value) < self.minlen:
|
||||
raise BadValueError(
|
||||
u'Array too small, needs at least %d elements!' %
|
||||
'Array too small, needs at least %d elements!' %
|
||||
self.minlen)
|
||||
if self.maxlen is not None and len(value) > self.maxlen:
|
||||
raise BadValueError(
|
||||
u'Array too big, holds at most %d elements!' % self.minlen)
|
||||
'Array too big, holds at most %d elements!' % self.minlen)
|
||||
# apply subtype valiation to all elements and return as list
|
||||
return [self.members(elem) for elem in value]
|
||||
raise BadValueError(
|
||||
u'Can not convert %s to ArrayOf DataType!' % repr(value))
|
||||
'Can not convert %s to ArrayOf DataType!' % repr(value))
|
||||
|
||||
def export_value(self, value):
|
||||
"""returns a python object fit for serialisation"""
|
||||
@ -593,13 +585,13 @@ class ArrayOf(DataType):
|
||||
def from_string(self, text):
|
||||
value, rem = Parser.parse(text)
|
||||
if rem:
|
||||
raise ProtocolError(u'trailing garbage: %r' % rem)
|
||||
raise ProtocolError('trailing garbage: %r' % rem)
|
||||
return self(value)
|
||||
|
||||
def format_value(self, value, unit=None):
|
||||
if unit is None:
|
||||
unit = self.unit or self.members.unit
|
||||
res = u'[%s]' % (', '.join([self.members.format_value(elem, u'') for elem in value]))
|
||||
res = '[%s]' % (', '.join([self.members.format_value(elem, '') for elem in value]))
|
||||
if unit:
|
||||
return ' '.join([res, unit])
|
||||
return res
|
||||
@ -609,11 +601,11 @@ class TupleOf(DataType):
|
||||
|
||||
def __init__(self, *members):
|
||||
if not members:
|
||||
raise BadValueError(u'Empty tuples are not allowed!')
|
||||
raise BadValueError('Empty tuples are not allowed!')
|
||||
for subtype in members:
|
||||
if not isinstance(subtype, DataType):
|
||||
raise BadValueError(
|
||||
u'TupleOf only works with DataType objs as arguments!')
|
||||
'TupleOf only works with DataType objs as arguments!')
|
||||
self.members = members
|
||||
self.default = tuple(el.default for el in members)
|
||||
|
||||
@ -621,7 +613,7 @@ class TupleOf(DataType):
|
||||
return dict(type='tuple', members=[subtype.export_datatype() for subtype in self.members])
|
||||
|
||||
def __repr__(self):
|
||||
return u'TupleOf(%s)' % u', '.join([repr(st) for st in self.members])
|
||||
return 'TupleOf(%s)' % ', '.join([repr(st) for st in self.members])
|
||||
|
||||
def __call__(self, value):
|
||||
"""return the validated value or raise"""
|
||||
@ -629,13 +621,13 @@ class TupleOf(DataType):
|
||||
try:
|
||||
if len(value) != len(self.members):
|
||||
raise BadValueError(
|
||||
u'Illegal number of Arguments! Need %d arguments.' %
|
||||
'Illegal number of Arguments! Need %d arguments.' %
|
||||
(len(self.members)))
|
||||
# validate elements and return as list
|
||||
return [sub(elem)
|
||||
for sub, elem in zip(self.members, value)]
|
||||
except Exception as exc:
|
||||
raise BadValueError(u'Can not validate:', unicode(exc))
|
||||
raise BadValueError('Can not validate:', str(exc))
|
||||
|
||||
def export_value(self, value):
|
||||
"""returns a python object fit for serialisation"""
|
||||
@ -648,11 +640,11 @@ class TupleOf(DataType):
|
||||
def from_string(self, text):
|
||||
value, rem = Parser.parse(text)
|
||||
if rem:
|
||||
raise ProtocolError(u'trailing garbage: %r' % rem)
|
||||
raise ProtocolError('trailing garbage: %r' % rem)
|
||||
return self(value)
|
||||
|
||||
def format_value(self, value, unit=None):
|
||||
return u'(%s)' % (', '.join([sub.format_value(elem)
|
||||
return '(%s)' % (', '.join([sub.format_value(elem)
|
||||
for sub, elem in zip(self.members, value)]))
|
||||
|
||||
|
||||
@ -661,19 +653,19 @@ class StructOf(DataType):
|
||||
def __init__(self, optional=None, **members):
|
||||
self.members = members
|
||||
if not members:
|
||||
raise BadValueError(u'Empty structs are not allowed!')
|
||||
raise BadValueError('Empty structs are not allowed!')
|
||||
self.optional = list(optional or [])
|
||||
for name, subtype in list(members.items()):
|
||||
if not isinstance(subtype, DataType):
|
||||
raise ProgrammingError(
|
||||
u'StructOf only works with named DataType objs as keyworded arguments!')
|
||||
if not isinstance(name, (unicode, str)):
|
||||
'StructOf only works with named DataType objs as keyworded arguments!')
|
||||
if not isinstance(name, str):
|
||||
raise ProgrammingError(
|
||||
u'StructOf only works with named DataType objs as keyworded arguments!')
|
||||
'StructOf only works with named DataType objs as keyworded arguments!')
|
||||
for name in self.optional:
|
||||
if name not in members:
|
||||
raise ProgrammingError(
|
||||
u'Only members of StructOf may be declared as optional!')
|
||||
'Only members of StructOf may be declared as optional!')
|
||||
self.default = dict((k,el.default) for k, el in members.items())
|
||||
|
||||
def export_datatype(self):
|
||||
@ -685,8 +677,8 @@ class StructOf(DataType):
|
||||
|
||||
def __repr__(self):
|
||||
opt = self.optional if self.optional else ''
|
||||
return u'StructOf(%s%s)' % (u', '.join(
|
||||
[u'%s=%s' % (n, repr(st)) for n, st in list(self.members.items())]), opt)
|
||||
return 'StructOf(%s%s)' % (', '.join(
|
||||
['%s=%s' % (n, repr(st)) for n, st in list(self.members.items())]), opt)
|
||||
|
||||
def __call__(self, value):
|
||||
"""return the validated value or raise"""
|
||||
@ -694,40 +686,40 @@ class StructOf(DataType):
|
||||
# XXX: handle optional elements !!!
|
||||
if len(list(value.keys())) != len(list(self.members.keys())):
|
||||
raise BadValueError(
|
||||
u'Illegal number of Arguments! Need %d arguments.' %
|
||||
'Illegal number of Arguments! Need %d arguments.' %
|
||||
len(list(self.members.keys())))
|
||||
# validate elements and return as dict
|
||||
return dict((unicode(k), self.members[k](v))
|
||||
return dict((str(k), self.members[k](v))
|
||||
for k, v in list(value.items()))
|
||||
except Exception as exc:
|
||||
raise BadValueError(u'Can not validate %s: %s' % (repr(value), unicode(exc)))
|
||||
raise BadValueError('Can not validate %s: %s' % (repr(value), str(exc)))
|
||||
|
||||
def export_value(self, value):
|
||||
"""returns a python object fit for serialisation"""
|
||||
if len(list(value.keys())) != len(list(self.members.keys())):
|
||||
raise BadValueError(
|
||||
u'Illegal number of Arguments! Need %d arguments.' % len(
|
||||
'Illegal number of Arguments! Need %d arguments.' % len(
|
||||
list(self.members.keys())))
|
||||
return dict((unicode(k), self.members[k].export_value(v))
|
||||
return dict((str(k), self.members[k].export_value(v))
|
||||
for k, v in list(value.items()))
|
||||
|
||||
def import_value(self, value):
|
||||
"""returns a python object from serialisation"""
|
||||
if len(list(value.keys())) != len(list(self.members.keys())):
|
||||
raise BadValueError(
|
||||
u'Illegal number of Arguments! Need %d arguments.' % len(
|
||||
'Illegal number of Arguments! Need %d arguments.' % len(
|
||||
list(self.members.keys())))
|
||||
return dict((unicode(k), self.members[k].import_value(v))
|
||||
return dict((str(k), self.members[k].import_value(v))
|
||||
for k, v in list(value.items()))
|
||||
|
||||
def from_string(self, text):
|
||||
value, rem = Parser.parse(text)
|
||||
if rem:
|
||||
raise ProtocolError(u'trailing garbage: %r' % rem)
|
||||
raise ProtocolError('trailing garbage: %r' % rem)
|
||||
return self(dict(value))
|
||||
|
||||
def format_value(self, value, unit=None):
|
||||
return u'{%s}' % (', '.join(['%s=%s' % (k, self.members[k].format_value(v)) for k, v in sorted(value.items())]))
|
||||
return '{%s}' % (', '.join(['%s=%s' % (k, self.members[k].format_value(v)) for k, v in sorted(value.items())]))
|
||||
|
||||
|
||||
class CommandType(DataType):
|
||||
@ -738,10 +730,10 @@ class CommandType(DataType):
|
||||
def __init__(self, argument=None, result=None):
|
||||
if argument is not None:
|
||||
if not isinstance(argument, DataType):
|
||||
raise BadValueError(u'CommandType: Argument type must be a DataType!')
|
||||
raise BadValueError('CommandType: Argument type must be a DataType!')
|
||||
if result is not None:
|
||||
if not isinstance(result, DataType):
|
||||
raise BadValueError(u'CommandType: Result type must be a DataType!')
|
||||
raise BadValueError('CommandType: Result type must be a DataType!')
|
||||
self.argument = argument
|
||||
self.result = result
|
||||
|
||||
@ -757,23 +749,23 @@ class CommandType(DataType):
|
||||
def __repr__(self):
|
||||
argstr = repr(self.argument) if self.argument else ''
|
||||
if self.result is None:
|
||||
return u'CommandType(%s)' % argstr
|
||||
return u'CommandType(%s)->%s' % (argstr, repr(self.result))
|
||||
return 'CommandType(%s)' % argstr
|
||||
return 'CommandType(%s)->%s' % (argstr, repr(self.result))
|
||||
|
||||
def __call__(self, value):
|
||||
"""return the validated argument value or raise"""
|
||||
return self.argument(value)
|
||||
|
||||
def export_value(self, value):
|
||||
raise ProgrammingError(u'values of type command can not be transported!')
|
||||
raise ProgrammingError('values of type command can not be transported!')
|
||||
|
||||
def import_value(self, value):
|
||||
raise ProgrammingError(u'values of type command can not be transported!')
|
||||
raise ProgrammingError('values of type command can not be transported!')
|
||||
|
||||
def from_string(self, text):
|
||||
value, rem = Parser.parse(text)
|
||||
if rem:
|
||||
raise ProtocolError(u'trailing garbage: %r' % rem)
|
||||
raise ProtocolError('trailing garbage: %r' % rem)
|
||||
return self(value)
|
||||
|
||||
def format_value(self, value, unit=None):
|
||||
@ -789,7 +781,7 @@ class DataTypeType(DataType):
|
||||
returns the value or raises an appropriate exception"""
|
||||
if isinstance(value, DataType):
|
||||
return value
|
||||
raise ProgrammingError(u'%r should be a DataType!' % value)
|
||||
raise ProgrammingError('%r should be a DataType!' % value)
|
||||
|
||||
def export_value(self, value):
|
||||
"""if needed, reformat value for transport"""
|
||||
@ -872,7 +864,7 @@ class LimitsType(StructOf):
|
||||
def __call__(self, value):
|
||||
limits = StructOf.__call__(self, value)
|
||||
if limits['max'] < limits['min']:
|
||||
raise BadValueError(u'Maximum Value %s must be greater than minimum value %s!' % (limits['max'], limits['min']))
|
||||
raise BadValueError('Maximum Value %s must be greater than minimum value %s!' % (limits['max'], limits['min']))
|
||||
return limits
|
||||
|
||||
|
||||
@ -925,4 +917,4 @@ def get_datatype(json):
|
||||
try:
|
||||
return DATATYPES[base](**args)
|
||||
except (TypeError, AttributeError, KeyError):
|
||||
raise BadValueError(u'invalid data descriptor: %r' % json)
|
||||
raise BadValueError('invalid data descriptor: %r' % json)
|
||||
|
@ -21,7 +21,6 @@
|
||||
# *****************************************************************************
|
||||
"""Define (internal) SECoP Errors"""
|
||||
|
||||
from __future__ import division, print_function
|
||||
|
||||
|
||||
class SECoPError(RuntimeError):
|
||||
|
@ -21,16 +21,14 @@
|
||||
# *****************************************************************************
|
||||
"""Define Mixin Features for real Modules implemented in the server"""
|
||||
|
||||
from __future__ import division, print_function
|
||||
|
||||
from secop.datatypes import ArrayOf, BoolType, EnumType, \
|
||||
FloatRange, StringType, StructOf, TupleOf
|
||||
from secop.metaclass import ModuleMeta, add_metaclass
|
||||
from secop.metaclass import ModuleMeta
|
||||
from secop.modules import Command, Parameter
|
||||
|
||||
|
||||
@add_metaclass(ModuleMeta)
|
||||
class Feature(object):
|
||||
class Feature(object, metaclass=ModuleMeta):
|
||||
"""all things belonging to a small, predefined functionality influencing the working of a module"""
|
||||
pass
|
||||
|
||||
|
@ -20,7 +20,6 @@
|
||||
#
|
||||
# *****************************************************************************
|
||||
|
||||
from __future__ import print_function
|
||||
from os import path, listdir
|
||||
import sys
|
||||
import inspect
|
||||
|
@ -20,7 +20,6 @@
|
||||
#
|
||||
# *****************************************************************************
|
||||
|
||||
from __future__ import print_function
|
||||
|
||||
import os
|
||||
|
||||
|
@ -21,7 +21,6 @@
|
||||
#
|
||||
# *****************************************************************************
|
||||
|
||||
from __future__ import division, print_function
|
||||
|
||||
import sys
|
||||
|
||||
|
@ -22,7 +22,6 @@
|
||||
# *****************************************************************************
|
||||
|
||||
|
||||
from __future__ import division, print_function
|
||||
|
||||
from os import path
|
||||
|
||||
|
@ -22,7 +22,6 @@
|
||||
#
|
||||
# *****************************************************************************
|
||||
|
||||
from __future__ import division, print_function
|
||||
|
||||
from secop.gui.params import ParameterView
|
||||
from secop.gui.qt import QCheckBox, QDialog, QLabel, \
|
||||
@ -30,20 +29,6 @@ from secop.gui.qt import QCheckBox, QDialog, QLabel, \
|
||||
from secop.gui.util import loadUi
|
||||
from secop.gui.valuewidgets import get_widget
|
||||
|
||||
try:
|
||||
# py2
|
||||
unicode(u'')
|
||||
except NameError:
|
||||
# py3
|
||||
unicode = str # pylint: disable=redefined-builtin
|
||||
|
||||
|
||||
|
||||
#from secop.datatypes import ...
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
class CommandDialog(QDialog):
|
||||
def __init__(self, cmdname, argument, parent=None):
|
||||
@ -104,7 +89,7 @@ class ParameterGroup(QWidget):
|
||||
self._row = 0
|
||||
self._widgets = []
|
||||
|
||||
self.paramGroupBox.setTitle('Group: ' + unicode(groupname))
|
||||
self.paramGroupBox.setTitle('Group: ' + str(groupname))
|
||||
self.paramGroupBox.toggled.connect(self.on_toggle_clicked)
|
||||
self.paramGroupBox.setChecked(False)
|
||||
|
||||
@ -293,7 +278,7 @@ class ModuleCtrl(QWidget):
|
||||
label.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Preferred)
|
||||
|
||||
# make 'display' label
|
||||
view = QLabel(unicode(props[prop]))
|
||||
view = QLabel(str(props[prop]))
|
||||
view.setFont(self.font())
|
||||
view.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Preferred)
|
||||
view.setWordWrap(True)
|
||||
@ -369,7 +354,7 @@ class ModuleCtrl(QWidget):
|
||||
try:
|
||||
self._node.setParameter(module, parameter, target)
|
||||
except Exception as e:
|
||||
QMessageBox.warning(self.parent(), 'Operation failed', unicode(e))
|
||||
QMessageBox.warning(self.parent(), 'Operation failed', str(e))
|
||||
|
||||
def _updateValue(self, module, parameter, value):
|
||||
if module != self._module:
|
||||
|
@ -21,7 +21,6 @@
|
||||
#
|
||||
# *****************************************************************************
|
||||
|
||||
from __future__ import division, print_function
|
||||
|
||||
import json
|
||||
import pprint
|
||||
@ -33,15 +32,6 @@ from secop.gui.qt import QFont, QFontMetrics, QLabel, \
|
||||
QMessageBox, QTextCursor, QWidget, pyqtSlot, toHtmlEscaped
|
||||
from secop.gui.util import loadUi
|
||||
|
||||
try:
|
||||
# py2
|
||||
unicode(u'')
|
||||
except NameError:
|
||||
unicode = str # pylint: disable=redefined-builtin
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
class NodeCtrl(QWidget):
|
||||
|
||||
@ -334,6 +324,6 @@ class DrivableWidget(ReadableWidget):
|
||||
def on_targetLineEdit_returnPressed(self):
|
||||
self.target_go(self.targetLineEdit.text())
|
||||
|
||||
@pyqtSlot(unicode)
|
||||
@pyqtSlot(str)
|
||||
def on_targetComboBox_activated(self, selection):
|
||||
self.target_go(selection)
|
||||
|
@ -21,7 +21,6 @@
|
||||
#
|
||||
# *****************************************************************************
|
||||
|
||||
from __future__ import print_function
|
||||
|
||||
from secop.datatypes import EnumType, FloatRange, IntRange
|
||||
from secop.gui.qt import QPushButton as QButton
|
||||
@ -30,14 +29,6 @@ from secop.gui.qt import QCheckBox, QLabel, QLineEdit, \
|
||||
from secop.gui.util import loadUi
|
||||
from secop.lib import formatExtendedStack
|
||||
|
||||
try:
|
||||
# py2
|
||||
unicode(u'')
|
||||
except NameError:
|
||||
unicode = str # pylint: disable=redefined-builtin
|
||||
|
||||
|
||||
|
||||
|
||||
class ParameterWidget(QWidget):
|
||||
setRequested = pyqtSignal(str, str, object) # module, parameter, target
|
||||
|
@ -21,19 +21,10 @@
|
||||
#
|
||||
# *****************************************************************************
|
||||
|
||||
from __future__ import division, print_function
|
||||
|
||||
from secop.gui.qt import QLabel, QSizePolicy, QWidget
|
||||
from secop.gui.util import loadUi
|
||||
|
||||
try:
|
||||
# py2
|
||||
unicode(u'')
|
||||
except NameError:
|
||||
# py3
|
||||
unicode = str # pylint: disable=redefined-builtin
|
||||
|
||||
|
||||
|
||||
class ParameterView(QWidget):
|
||||
|
||||
|
@ -22,7 +22,6 @@
|
||||
"""Import needed stuff from PyQt4/PyQt5"""
|
||||
|
||||
# pylint: disable=unused-import
|
||||
from __future__ import division, print_function
|
||||
|
||||
import sys
|
||||
|
||||
|
@ -21,7 +21,6 @@
|
||||
#
|
||||
# *****************************************************************************
|
||||
|
||||
from __future__ import division, print_function
|
||||
|
||||
from os import path
|
||||
|
||||
|
@ -21,7 +21,6 @@
|
||||
#
|
||||
# *****************************************************************************
|
||||
|
||||
from __future__ import division, print_function
|
||||
|
||||
from secop.datatypes import ArrayOf, BLOBType, BoolType, EnumType, \
|
||||
FloatRange, IntRange, StringType, StructOf, TupleOf, TextType
|
||||
|
@ -32,21 +32,20 @@ import subprocess
|
||||
import sys
|
||||
import threading
|
||||
import traceback
|
||||
import unicodedata
|
||||
from os import path
|
||||
|
||||
repodir = path.abspath(path.join(path.dirname(__file__), u'..', u'..'))
|
||||
repodir = path.abspath(path.join(path.dirname(__file__), '..', '..'))
|
||||
|
||||
CONFIG = {
|
||||
u'piddir': os.path.join(repodir, u'pid'),
|
||||
u'logdir': os.path.join(repodir, u'log'),
|
||||
u'confdir': os.path.join(repodir, u'cfg'),
|
||||
u'basedir': repodir,
|
||||
} if os.path.exists(os.path.join(repodir, u'.git')) else {
|
||||
u'piddir': u'/var/run/secop',
|
||||
u'logdir': u'/var/log',
|
||||
u'confdir': u'/etc/secop',
|
||||
u'basedir': repodir,
|
||||
'piddir': os.path.join(repodir, 'pid'),
|
||||
'logdir': os.path.join(repodir, 'log'),
|
||||
'confdir': os.path.join(repodir, 'etc'),
|
||||
'basedir': repodir,
|
||||
} if os.path.exists(os.path.join(repodir, '.git')) else {
|
||||
'piddir': '/var/run/secop',
|
||||
'logdir': '/var/log',
|
||||
'confdir': '/etc/secop',
|
||||
'basedir': repodir,
|
||||
}
|
||||
|
||||
|
||||
|
@ -22,15 +22,9 @@
|
||||
# *****************************************************************************
|
||||
"""Enum class"""
|
||||
|
||||
from __future__ import division, print_function
|
||||
|
||||
__ALL__ = ['Enum']
|
||||
|
||||
try:
|
||||
text_type = unicode # Py2
|
||||
except NameError:
|
||||
text_type = str # Py3
|
||||
unicode = str # pylint: disable=redefined-builtin
|
||||
|
||||
class EnumMember(object):
|
||||
"""represents one member of an Enum
|
||||
@ -49,7 +43,7 @@ class EnumMember(object):
|
||||
def __cmp__(self, other):
|
||||
if isinstance(other, EnumMember):
|
||||
other = other.value
|
||||
if isinstance(other, (str, unicode)):
|
||||
if isinstance(other, str):
|
||||
if other in self.enum:
|
||||
other = self.enum[other].value
|
||||
try:
|
||||
@ -73,7 +67,7 @@ class EnumMember(object):
|
||||
if isinstance(other, int):
|
||||
return other == self.value
|
||||
# compare by name (for (in)equality only)
|
||||
if isinstance(other, (str, unicode)):
|
||||
if isinstance(other, str):
|
||||
if other in self.enum:
|
||||
return self.name == other
|
||||
return False
|
||||
@ -114,8 +108,6 @@ class EnumMember(object):
|
||||
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 __div__(self, other):
|
||||
return self.value.__div__(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):
|
||||
@ -137,8 +129,6 @@ class EnumMember(object):
|
||||
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 __rdiv__(self, other):
|
||||
return self.value.__rdiv__(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):
|
||||
@ -181,10 +171,6 @@ class EnumMember(object):
|
||||
def __float__(self):
|
||||
return self.value.__float__()
|
||||
#return NotImplemented # makes no sense
|
||||
def __oct__(self):
|
||||
return self.value.__oct__()
|
||||
def __hex__(self):
|
||||
return self.value.__hex__()
|
||||
def __index__(self):
|
||||
return self.value.__index__()
|
||||
|
||||
@ -234,7 +220,7 @@ class Enum(dict):
|
||||
name=parent.name
|
||||
# else:
|
||||
# raise TypeError('Enum instances need a name or an Enum parent!')
|
||||
if not isinstance(name, (str, text_type)):
|
||||
if not isinstance(name, str):
|
||||
raise TypeError('1st argument to Enum must be a name or an Enum!')
|
||||
|
||||
names = set()
|
||||
|
@ -1,44 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# *****************************************************************************
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify it under
|
||||
# the terms of the GNU General Public License as published by the Free Software
|
||||
# Foundation; either version 2 of the License, or (at your option) any later
|
||||
# version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||
# details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License along with
|
||||
# this program; if not, write to the Free Software Foundation, Inc.,
|
||||
# 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
#
|
||||
# Module authors:
|
||||
# Enrico Faulhaber <enrico.faulhaber@frm2.tum.de>
|
||||
#
|
||||
# *****************************************************************************
|
||||
"""Define metaclass helper"""
|
||||
|
||||
from __future__ import division, print_function
|
||||
|
||||
try:
|
||||
# pylint: disable=unused-import
|
||||
from six import add_metaclass # for py2/3 compat
|
||||
except ImportError:
|
||||
# copied from six v1.10.0
|
||||
def add_metaclass(metaclass):
|
||||
"""Class decorator for creating a class with a metaclass."""
|
||||
def wrapper(cls):
|
||||
orig_vars = cls.__dict__.copy()
|
||||
slots = orig_vars.get('__slots__')
|
||||
if slots is not None:
|
||||
if isinstance(slots, str):
|
||||
slots = [slots]
|
||||
for slots_var in slots:
|
||||
orig_vars.pop(slots_var)
|
||||
orig_vars.pop('__dict__', None)
|
||||
orig_vars.pop('__weakref__', None)
|
||||
return metaclass(cls.__name__, cls.__bases__, orig_vars)
|
||||
return wrapper
|
@ -21,7 +21,6 @@
|
||||
# *****************************************************************************
|
||||
"""Define parsing helpers"""
|
||||
|
||||
from __future__ import division, print_function
|
||||
|
||||
import re
|
||||
import time
|
||||
@ -144,7 +143,7 @@ def format_args(args):
|
||||
return ','.join(format_args(arg) for arg in args).join('[]')
|
||||
if isinstance(args, tuple):
|
||||
return ','.join(format_args(arg) for arg in args).join('()')
|
||||
if isinstance(args, (str, unicode)):
|
||||
if isinstance(args, str):
|
||||
# XXX: check for 'easy' strings only and omit the ''
|
||||
return repr(args)
|
||||
return repr(args) # for floats/ints/...
|
||||
|
@ -20,7 +20,6 @@
|
||||
#
|
||||
# *****************************************************************************
|
||||
"""Define pidfile helpers"""
|
||||
from __future__ import division, print_function
|
||||
|
||||
import atexit
|
||||
import os
|
||||
|
@ -23,7 +23,6 @@
|
||||
|
||||
"""Utilities for modules that require sequenced actions on value change."""
|
||||
|
||||
from __future__ import division, print_function
|
||||
|
||||
from time import sleep
|
||||
|
||||
@ -74,7 +73,7 @@ class SequencerMixin(object):
|
||||
self._seq_fault_on_error = fault_on_error
|
||||
self._seq_fault_on_stop = fault_on_stop
|
||||
self._seq_stopflag = False
|
||||
self._seq_phase = u''
|
||||
self._seq_phase = ''
|
||||
self._seq_error = None
|
||||
self._seq_stopped = None
|
||||
|
||||
@ -116,7 +115,7 @@ class SequencerMixin(object):
|
||||
the default is to only go into ALARM.
|
||||
"""
|
||||
if self.seq_is_alive():
|
||||
raise IsBusyError(u'move sequence already in progress')
|
||||
raise IsBusyError('move sequence already in progress')
|
||||
|
||||
self._seq_stopflag = False
|
||||
self._seq_error = self._seq_stopped = None
|
||||
@ -129,7 +128,7 @@ class SequencerMixin(object):
|
||||
|
||||
def read_status(self):
|
||||
if self.seq_is_alive():
|
||||
return self.Status.BUSY, u'moving: ' + self._seq_phase
|
||||
return self.Status.BUSY, 'moving: ' + self._seq_phase
|
||||
elif self._seq_error:
|
||||
if self._seq_fault_on_error:
|
||||
return self.Status.ERROR, self._seq_error
|
||||
@ -138,9 +137,9 @@ class SequencerMixin(object):
|
||||
if self._seq_fault_on_stop:
|
||||
return self.Status.ERROR, self._seq_stopped
|
||||
return self.Status.WARN, self._seq_stopped
|
||||
if hasattr(self, u'read_hw_status'):
|
||||
if hasattr(self, 'read_hw_status'):
|
||||
return self.read_hw_status()
|
||||
return self.Status.IDLE, u''
|
||||
return self.Status.IDLE, ''
|
||||
|
||||
def do_stop(self):
|
||||
if self.seq_is_alive():
|
||||
@ -150,7 +149,7 @@ class SequencerMixin(object):
|
||||
try:
|
||||
self._seq_thread_inner(seq, store_init)
|
||||
except Exception as e:
|
||||
self.log.exception(u'unhandled error in sequence thread: %s', e)
|
||||
self.log.exception('unhandled error in sequence thread: %s', e)
|
||||
self._seq_error = str(e)
|
||||
finally:
|
||||
self._seq_thread = None
|
||||
@ -159,11 +158,11 @@ class SequencerMixin(object):
|
||||
def _seq_thread_inner(self, seq, store_init):
|
||||
store = Namespace()
|
||||
store.__dict__.update(store_init)
|
||||
self.log.debug(u'sequence: starting, values %s', store_init)
|
||||
self.log.debug('sequence: starting, values %s', store_init)
|
||||
|
||||
for step in seq:
|
||||
self._seq_phase = step.desc
|
||||
self.log.debug(u'sequence: entering phase: %s', step.desc)
|
||||
self.log.debug('sequence: entering phase: %s', step.desc)
|
||||
try:
|
||||
i = 0
|
||||
while True:
|
||||
@ -171,10 +170,10 @@ class SequencerMixin(object):
|
||||
result = step.func(store, *step.args)
|
||||
if self._seq_stopflag:
|
||||
if result:
|
||||
self._seq_stopped = u'stopped while %s' % step.desc
|
||||
self._seq_stopped = 'stopped while %s' % step.desc
|
||||
else:
|
||||
self._seq_stopped = u'stopped after %s' % step.desc
|
||||
cleanup_func = step.kwds.get(u'cleanup', None)
|
||||
self._seq_stopped = 'stopped after %s' % step.desc
|
||||
cleanup_func = step.kwds.get('cleanup', None)
|
||||
if callable(cleanup_func):
|
||||
try:
|
||||
cleanup_func(store, result, *step.args)
|
||||
@ -188,6 +187,6 @@ class SequencerMixin(object):
|
||||
i += 1
|
||||
except Exception as e:
|
||||
self.log.exception(
|
||||
u'error in sequence step %r: %s', step.desc, e)
|
||||
self._seq_error = u'during %s: %s' % (step.desc, e)
|
||||
'error in sequence step %r: %s', step.desc, e)
|
||||
self._seq_error = 'during %s: %s' % (step.desc, e)
|
||||
break
|
||||
|
@ -21,7 +21,6 @@
|
||||
# *****************************************************************************
|
||||
"""Define Metaclass for Modules/Features"""
|
||||
|
||||
from __future__ import division, print_function
|
||||
|
||||
import time
|
||||
from collections import OrderedDict
|
||||
|
@ -21,7 +21,6 @@
|
||||
# *****************************************************************************
|
||||
"""Define Baseclasses for real Modules implemented in the server"""
|
||||
|
||||
from __future__ import division, print_function
|
||||
|
||||
import sys
|
||||
import time
|
||||
@ -33,7 +32,6 @@ from secop.errors import ConfigError, ProgrammingError
|
||||
from secop.lib import formatException, \
|
||||
formatExtendedStack, mkthread, unset_value
|
||||
from secop.lib.enum import Enum
|
||||
from secop.lib.metaclass import add_metaclass
|
||||
from secop.metaclass import ModuleMeta
|
||||
from secop.params import PREDEFINED_ACCESSIBLES, Command, Override, Parameter, Parameters, Commands
|
||||
from secop.properties import HasProperties, Property
|
||||
@ -45,8 +43,7 @@ from secop.properties import HasProperties, Property
|
||||
# from these base classes (how to do this?)
|
||||
|
||||
|
||||
@add_metaclass(ModuleMeta)
|
||||
class Module(HasProperties):
|
||||
class Module(HasProperties, metaclass=ModuleMeta):
|
||||
"""Basic Module
|
||||
|
||||
ALL secop Modules derive from this
|
||||
@ -205,7 +202,7 @@ class Module(HasProperties):
|
||||
# (self.name, k, e))
|
||||
# note: this will call write_* methods which will
|
||||
# write to the hardware, if possible!
|
||||
if k != u'value':
|
||||
if k != 'value':
|
||||
setattr(self, k, v)
|
||||
cfgdict.pop(k)
|
||||
|
||||
|
118
secop/params.py
118
secop/params.py
@ -21,7 +21,6 @@
|
||||
# *****************************************************************************
|
||||
"""Define classes for Parameters/Commands and Overriding them"""
|
||||
|
||||
from __future__ import division, print_function
|
||||
|
||||
from collections import OrderedDict
|
||||
|
||||
@ -30,11 +29,6 @@ from secop.datatypes import CommandType, DataType, StringType, BoolType, EnumTyp
|
||||
from secop.errors import ProgrammingError
|
||||
from secop.properties import HasProperties, Property
|
||||
|
||||
try:
|
||||
unicode
|
||||
except NameError:
|
||||
# pylint: disable=redefined-builtin
|
||||
unicode = str # py3 compat
|
||||
|
||||
class CountedObj(object):
|
||||
ctr = [0]
|
||||
@ -54,8 +48,8 @@ class Accessible(HasProperties, CountedObj):
|
||||
self.properties.update(kwds)
|
||||
|
||||
def __repr__(self):
|
||||
return u'%s_%d(%s)' % (self.__class__.__name__, self.ctr, ',\n\t'.join(
|
||||
[u'%s=%r' % (k, self.properties.get(k, v.default)) for k, v in sorted(self.__class__.properties.items())]))
|
||||
return '%s_%d(%s)' % (self.__class__.__name__, self.ctr, ',\n\t'.join(
|
||||
['%s=%r' % (k, self.properties.get(k, v.default)) for k, v in sorted(self.__class__.properties.items())]))
|
||||
|
||||
def copy(self):
|
||||
# return a copy of ourselfs
|
||||
@ -92,26 +86,26 @@ class Parameter(Accessible):
|
||||
"""
|
||||
|
||||
properties = {
|
||||
u'description': Property('Description of the Parameter', TextType(),
|
||||
extname=u'description', mandatory=True),
|
||||
u'datatype': Property('Datatype of the Parameter', DataTypeType(),
|
||||
extname=u'datainfo', mandatory=True),
|
||||
u'unit': Property('[legacy] unit of the parameter. This should now be on the datatype!', StringType(),
|
||||
extname=u'unit', default=''), # goodie, should be on the datatype!
|
||||
u'readonly': Property('Is the Parameter readonly? (vs. changeable via SECoP)', BoolType(),
|
||||
extname=u'readonly', default=True),
|
||||
u'group': Property('Optional parameter group this parameter belongs to', StringType(),
|
||||
extname=u'group', default=''),
|
||||
u'visibility': Property('Optional visibility hint', EnumType(u'visibility', user=1, advanced=2, expert=3),
|
||||
extname=u'visibility', default=1),
|
||||
u'constant': Property('Optional constant value for constant parameters', ValueType(),
|
||||
extname=u'constant', default=None),
|
||||
u'default': Property('Default (startup) value of this parameter if it can not be read from the hardware.',
|
||||
'description': Property('Description of the Parameter', TextType(),
|
||||
extname='description', mandatory=True),
|
||||
'datatype': Property('Datatype of the Parameter', DataTypeType(),
|
||||
extname='datainfo', mandatory=True),
|
||||
'unit': Property('[legacy] unit of the parameter. This should now be on the datatype!', StringType(),
|
||||
extname='unit', default=''), # goodie, should be on the datatype!
|
||||
'readonly': Property('Is the Parameter readonly? (vs. changeable via SECoP)', BoolType(),
|
||||
extname='readonly', default=True),
|
||||
'group': Property('Optional parameter group this parameter belongs to', StringType(),
|
||||
extname='group', default=''),
|
||||
'visibility': Property('Optional visibility hint', EnumType('visibility', user=1, advanced=2, expert=3),
|
||||
extname='visibility', default=1),
|
||||
'constant': Property('Optional constant value for constant parameters', ValueType(),
|
||||
extname='constant', default=None),
|
||||
'default': Property('Default (startup) value of this parameter if it can not be read from the hardware.',
|
||||
ValueType(), export=False, default=None, mandatory=False),
|
||||
u'export': Property('Is this parameter accessible via SECoP? (vs. internal parameter)',
|
||||
'export': Property('Is this parameter accessible via SECoP? (vs. internal parameter)',
|
||||
OrType(BoolType(), StringType()), export=False, default=True),
|
||||
u'poll': Property('Polling indicator', ValueType(), export=False, default=True), # check default value!
|
||||
u'optional': Property('[Internal] is this parameter optional?', BoolType(), export=False, default=False),
|
||||
'poll': Property('Polling indicator', ValueType(), export=False, default=True), # check default value!
|
||||
'optional': Property('[Internal] is this parameter optional?', BoolType(), export=False, default=False),
|
||||
}
|
||||
|
||||
value = None
|
||||
@ -127,27 +121,27 @@ class Parameter(Accessible):
|
||||
datatype = datatype()
|
||||
else:
|
||||
raise ValueError(
|
||||
u'datatype MUST be derived from class DataType!')
|
||||
'datatype MUST be derived from class DataType!')
|
||||
|
||||
kwds[u'description'] = description
|
||||
kwds[u'datatype'] = datatype
|
||||
kwds['description'] = description
|
||||
kwds['datatype'] = datatype
|
||||
super(Parameter, self).__init__(**kwds)
|
||||
|
||||
# note: auto-converts True/False to 1/0 which yield the expected
|
||||
# behaviour...
|
||||
self.properties[u'poll'] = int(self.poll)
|
||||
self.properties['poll'] = int(self.poll)
|
||||
|
||||
if self.constant is not None:
|
||||
self.properties[u'readonly'] = True
|
||||
self.properties['readonly'] = True
|
||||
# The value of the `constant` property should be the
|
||||
# serialised version of the constant, or unset
|
||||
constant = self.datatype(kwds[u'constant'])
|
||||
self.properties[u'constant'] = self.datatype.export_value(constant)
|
||||
constant = self.datatype(kwds['constant'])
|
||||
self.properties['constant'] = self.datatype.export_value(constant)
|
||||
|
||||
# helper: unit should be set on the datatype, not on the parameter!
|
||||
if self.unit:
|
||||
self.datatype.unit = self.unit
|
||||
self.properties[u'unit'] = ''
|
||||
self.properties['unit'] = ''
|
||||
|
||||
# internal caching: value and timestamp of last change...
|
||||
self.value = self.default
|
||||
@ -161,7 +155,7 @@ class Parameter(Accessible):
|
||||
return self.datatype.unit
|
||||
|
||||
def _set_unit_(self, unit):
|
||||
print(u'DeprecationWarning: setting unit on the parameter is going to be removed')
|
||||
print('DeprecationWarning: setting unit on the parameter is going to be removed')
|
||||
self.datatype.unit = unit
|
||||
|
||||
unit = property(_get_unit_, _set_unit_)
|
||||
@ -182,9 +176,9 @@ class Parameters(OrderedDict):
|
||||
def __setitem__(self, key, value):
|
||||
if value.export:
|
||||
if isinstance(value, PREDEFINED_ACCESSIBLES.get(key, UnusedClass)):
|
||||
value.properties[u'export'] = key
|
||||
value.properties['export'] = key
|
||||
else:
|
||||
value.properties[u'export'] = '_' + key
|
||||
value.properties['export'] = '_' + key
|
||||
self.exported[value.export] = key
|
||||
super(Parameters, self).__setitem__(key, value)
|
||||
|
||||
@ -216,22 +210,22 @@ class Override(CountedObj):
|
||||
self.reorder = reorder
|
||||
# allow to override description without keyword
|
||||
if description:
|
||||
self.kwds[u'description'] = description
|
||||
self.kwds['description'] = description
|
||||
# for now, do not use the Override ctr
|
||||
# self.kwds['ctr'] = self.ctr
|
||||
|
||||
def __repr__(self):
|
||||
return u'%s_%d(%s)' % (self.__class__.__name__, self.ctr, ', '.join(
|
||||
[u'%s=%r' % (k, v) for k, v in sorted(self.kwds.items())]))
|
||||
return '%s_%d(%s)' % (self.__class__.__name__, self.ctr, ', '.join(
|
||||
['%s=%r' % (k, v) for k, v in sorted(self.kwds.items())]))
|
||||
|
||||
def apply(self, obj):
|
||||
if isinstance(obj, Accessible):
|
||||
props = obj.properties.copy()
|
||||
if isinstance(obj, Parameter):
|
||||
if u'constant' in self.kwds:
|
||||
constant = obj.datatype(self.kwds.pop(u'constant'))
|
||||
self.kwds[u'constant'] = obj.datatype.export_value(constant)
|
||||
self.kwds[u'readonly'] = True
|
||||
if 'constant' in self.kwds:
|
||||
constant = obj.datatype(self.kwds.pop('constant'))
|
||||
self.kwds['constant'] = obj.datatype.export_value(constant)
|
||||
self.kwds['readonly'] = True
|
||||
props.update(self.kwds)
|
||||
|
||||
if self.reorder:
|
||||
@ -249,27 +243,27 @@ class Command(Accessible):
|
||||
"""
|
||||
# datatype is not listed (handled separately)
|
||||
properties = {
|
||||
u'description': Property('Description of the Command', TextType(),
|
||||
extname=u'description', export=True, mandatory=True),
|
||||
u'group': Property('Optional command group of the command.', StringType(),
|
||||
extname=u'group', export=True, default=''),
|
||||
u'visibility': Property('Optional visibility hint', EnumType(u'visibility', user=1, advanced=2, expert=3),
|
||||
extname=u'visibility', export=True, default=1),
|
||||
u'export': Property('[internal] Flag: is the command accessible via SECoP? (vs. pure internal use)',
|
||||
'description': Property('Description of the Command', TextType(),
|
||||
extname='description', export=True, mandatory=True),
|
||||
'group': Property('Optional command group of the command.', StringType(),
|
||||
extname='group', export=True, default=''),
|
||||
'visibility': Property('Optional visibility hint', EnumType('visibility', user=1, advanced=2, expert=3),
|
||||
extname='visibility', export=True, default=1),
|
||||
'export': Property('[internal] Flag: is the command accessible via SECoP? (vs. pure internal use)',
|
||||
OrType(BoolType(), StringType()), export=False, default=True),
|
||||
u'optional': Property('[internal] is The comamnd optional to implement? (vs. mandatory',
|
||||
'optional': Property('[internal] is The comamnd optional to implement? (vs. mandatory',
|
||||
BoolType(), export=False, default=False, settable=False),
|
||||
u'datatype': Property('[internal] datatype of the command, auto generated from \'argument\' and \'result\'',
|
||||
DataTypeType(), extname=u'datainfo', mandatory=True),
|
||||
u'argument': Property('Datatype of the argument to the command, or None.',
|
||||
'datatype': Property('[internal] datatype of the command, auto generated from \'argument\' and \'result\'',
|
||||
DataTypeType(), extname='datainfo', mandatory=True),
|
||||
'argument': Property('Datatype of the argument to the command, or None.',
|
||||
NoneOr(DataTypeType()), export=False, mandatory=True),
|
||||
u'result': Property('Datatype of the result from the command, or None.',
|
||||
'result': Property('Datatype of the result from the command, or None.',
|
||||
NoneOr(DataTypeType()), export=False, mandatory=True),
|
||||
}
|
||||
|
||||
def __init__(self, description, ctr=None, **kwds):
|
||||
kwds[u'description'] = description
|
||||
kwds[u'datatype'] = CommandType(kwds.get('argument', None), kwds.get('result', None))
|
||||
kwds['description'] = description
|
||||
kwds['datatype'] = CommandType(kwds.get('argument', None), kwds.get('result', None))
|
||||
super(Command, self).__init__(**kwds)
|
||||
if ctr is not None:
|
||||
self.ctr = ctr
|
||||
@ -289,10 +283,10 @@ class Command(Accessible):
|
||||
# - readonly is mandatory for serialisation, but not for declaration in classes
|
||||
r = self.exportProperties()
|
||||
# if isinstance(self, Parameter):
|
||||
# if u'readonly' not in r:
|
||||
# r[u'readonly'] = self.__class__.properties[u'readonly'].default
|
||||
# if u'datatype' in r:
|
||||
# _d = r[u'datatype']
|
||||
# if 'readonly' not in r:
|
||||
# r['readonly'] = self.__class__.properties['readonly'].default
|
||||
# if 'datatype' in r:
|
||||
# _d = r['datatype']
|
||||
# print(formatExtendedStack()) # for debug
|
||||
return r
|
||||
|
||||
|
@ -36,7 +36,6 @@ text -> string
|
||||
|
||||
further convertions are done by the validator of the datatype....
|
||||
"""
|
||||
from __future__ import division, print_function
|
||||
|
||||
from collections import OrderedDict
|
||||
|
||||
@ -55,7 +54,7 @@ class Parser(object):
|
||||
length = l
|
||||
l += 1
|
||||
except ValueError:
|
||||
if text[l - 1] in u'eE+-':
|
||||
if text[l - 1] in 'eE+-':
|
||||
l += 1
|
||||
continue
|
||||
if number is None:
|
||||
@ -69,7 +68,7 @@ class Parser(object):
|
||||
def parse_string(self, orgtext):
|
||||
# handle quoted and unquoted strings correctly
|
||||
text = orgtext.strip()
|
||||
if text[0] in (u'"', u"'"):
|
||||
if text[0] in ('"', u"'"):
|
||||
# quoted string
|
||||
quote = text[0]
|
||||
idx = 0
|
||||
@ -79,14 +78,14 @@ class Parser(object):
|
||||
if idx == -1:
|
||||
return None, orgtext
|
||||
# check escapes!
|
||||
if text[idx - 1] == u'\\':
|
||||
if text[idx - 1] == '\\':
|
||||
continue
|
||||
return text[1:idx], text[idx + 1:]
|
||||
|
||||
# unquoted strings are terminated by comma or whitespace
|
||||
idx = 0
|
||||
while idx < len(text):
|
||||
if text[idx] in u'\x09 ,.;:()[]{}<>-+*/\\!"§$%&=?#~+*\'´`^°|-':
|
||||
if text[idx] in '\x09 ,.;:()[]{}<>-+*/\\!"§$%&=?#~+*\'´`^°|-':
|
||||
break
|
||||
idx += 1
|
||||
return text[:idx] or None, text[idx:]
|
||||
@ -94,10 +93,10 @@ class Parser(object):
|
||||
def parse_tuple(self, orgtext):
|
||||
text = orgtext.strip()
|
||||
bra = text[0]
|
||||
if bra not in u'([<':
|
||||
if bra not in '([<':
|
||||
return None, orgtext
|
||||
# convert to closing bracket
|
||||
bra = u')]>'[u'([<'.index(bra)]
|
||||
bra = ')]>'['([<'.index(bra)]
|
||||
reslist = []
|
||||
# search for cosing bracket, collecting results
|
||||
text = text[1:]
|
||||
@ -111,7 +110,7 @@ class Parser(object):
|
||||
if rem[0] == bra:
|
||||
return tuple(reslist), rem[1:]
|
||||
# eat separator
|
||||
if rem[0] in u',;':
|
||||
if rem[0] in ',;':
|
||||
text = rem[1:]
|
||||
else:
|
||||
return None, rem
|
||||
@ -119,19 +118,19 @@ class Parser(object):
|
||||
|
||||
def parse_dict(self, orgtext):
|
||||
text = orgtext.strip()
|
||||
if text[0] != u'{':
|
||||
if text[0] != '{':
|
||||
return None, orgtext
|
||||
# keep ordering
|
||||
result = OrderedDict()
|
||||
# search for cosing bracket, collecting results
|
||||
# watch for key=value or key:value pairs, separated by ,
|
||||
text = text[1:]
|
||||
while u'}' in text:
|
||||
while '}' in text:
|
||||
# first part is always a string
|
||||
key, rem = self.parse_string(text)
|
||||
if not key:
|
||||
return None, orgtext
|
||||
if rem[0] not in u':=':
|
||||
if rem[0] not in ':=':
|
||||
return None, rem
|
||||
# eat separator
|
||||
text = rem[1:]
|
||||
@ -139,10 +138,10 @@ class Parser(object):
|
||||
if not value:
|
||||
return None, orgtext
|
||||
result[key] = value
|
||||
if rem[0] == u'}':
|
||||
if rem[0] == '}':
|
||||
return result, rem[1:]
|
||||
|
||||
if rem[0] not in u',;':
|
||||
if rem[0] not in ',;':
|
||||
return None, rem
|
||||
# eat separator
|
||||
text = rem[1:]
|
||||
@ -152,17 +151,17 @@ class Parser(object):
|
||||
text = orgtext.strip()
|
||||
if not text:
|
||||
return None, orgtext
|
||||
if text[0] in u'+-.0123456789':
|
||||
if text[0] in '+-.0123456789':
|
||||
return self.parse_number(orgtext)
|
||||
elif text[0] == u'{':
|
||||
elif text[0] == '{':
|
||||
return self.parse_dict(orgtext)
|
||||
elif text[0] in u'([<':
|
||||
elif text[0] in '([<':
|
||||
return self.parse_tuple(orgtext)
|
||||
return self.parse_string(orgtext)
|
||||
|
||||
def parse(self, orgtext):
|
||||
print("parsing %r" % orgtext)
|
||||
res, rem = self.parse_sub(orgtext)
|
||||
if rem and rem[0] in u',;':
|
||||
return self.parse_sub(u'[%s]' % orgtext)
|
||||
if rem and rem[0] in ',;':
|
||||
return self.parse_sub('[%s]' % orgtext)
|
||||
return res, rem
|
||||
|
@ -21,7 +21,6 @@
|
||||
# *****************************************************************************
|
||||
"""Pathes. how to find what and where..."""
|
||||
|
||||
from __future__ import division, print_function
|
||||
|
||||
import sys
|
||||
from os import path
|
||||
|
@ -21,14 +21,11 @@
|
||||
# *****************************************************************************
|
||||
"""Define validated data types."""
|
||||
|
||||
from __future__ import division, print_function
|
||||
|
||||
from collections import OrderedDict
|
||||
|
||||
from secop.datatypes import ValueType, DataType
|
||||
from secop.errors import ProgrammingError, ConfigError
|
||||
from secop.lib.metaclass import add_metaclass
|
||||
|
||||
|
||||
|
||||
# storage for 'properties of a property'
|
||||
@ -44,7 +41,7 @@ class Property(object):
|
||||
# the VALUES of the properties are on the instances!
|
||||
def __init__(self, description, datatype, default=None, extname='', export=False, mandatory=False, settable=True):
|
||||
if not callable(datatype):
|
||||
raise ValueError(u'datatype MUST be a valid DataType or a basic_validator')
|
||||
raise ValueError('datatype MUST be a valid DataType or a basic_validator')
|
||||
self.description = description
|
||||
self.default = datatype.default if default is None else datatype(default)
|
||||
self.datatype = datatype
|
||||
@ -54,7 +51,7 @@ class Property(object):
|
||||
self.settable = settable or mandatory # settable means settable from the cfg file
|
||||
|
||||
def __repr__(self):
|
||||
return u'Property(%s, %s, default=%r, extname=%r, export=%r, mandatory=%r)' % (
|
||||
return 'Property(%s, %s, default=%r, extname=%r, export=%r, mandatory=%r)' % (
|
||||
self.description, self.datatype, self.default, self.extname, self.export, self.mandatory)
|
||||
|
||||
|
||||
@ -67,16 +64,16 @@ class Properties(OrderedDict):
|
||||
"""
|
||||
def __setitem__(self, key, value):
|
||||
if not isinstance(value, Property):
|
||||
raise ProgrammingError(u'setting property %r on classes is not supported!' % key)
|
||||
raise ProgrammingError('setting property %r on classes is not supported!' % key)
|
||||
# make sure, extname is valid if export is True
|
||||
if not value.extname and value.export:
|
||||
value.extname = u'_%s' % key # generate custom kex
|
||||
value.extname = '_%s' % key # generate custom kex
|
||||
elif value.extname and not value.export:
|
||||
value.export = True
|
||||
OrderedDict.__setitem__(self, key, value)
|
||||
|
||||
def __delitem__(self, key):
|
||||
raise ProgrammingError(u'deleting Properties is not supported!')
|
||||
raise ProgrammingError('deleting Properties is not supported!')
|
||||
|
||||
|
||||
class PropertyMeta(type):
|
||||
@ -112,13 +109,12 @@ class PropertyMeta(type):
|
||||
return self.properties.get(pname, val)
|
||||
if k in attrs:
|
||||
if not isinstance(attrs[k], property):
|
||||
raise ProgrammingError(u'Name collision with property %r' % k)
|
||||
raise ProgrammingError('Name collision with property %r' % k)
|
||||
setattr(newtype, k, property(getter))
|
||||
return newtype
|
||||
|
||||
|
||||
@add_metaclass(PropertyMeta)
|
||||
class HasProperties(object):
|
||||
class HasProperties(object, metaclass=PropertyMeta):
|
||||
properties = {}
|
||||
|
||||
def __init__(self, supercall_init=True):
|
||||
|
@ -36,7 +36,6 @@ Interface to the modules:
|
||||
- remove_module(modulename_or_obj): removes the module (during shutdown)
|
||||
|
||||
"""
|
||||
from __future__ import division, print_function
|
||||
|
||||
import threading
|
||||
from collections import OrderedDict
|
||||
@ -50,12 +49,6 @@ from secop.protocol.messages import COMMANDREPLY, DESCRIPTIONREPLY, \
|
||||
DISABLEEVENTSREPLY, ENABLEEVENTSREPLY, ERRORPREFIX, EVENTREPLY, \
|
||||
HEARTBEATREPLY, IDENTREPLY, IDENTREQUEST, READREPLY, WRITEREPLY
|
||||
|
||||
try:
|
||||
unicode
|
||||
except NameError:
|
||||
# no unicode on py3
|
||||
unicode = str # pylint: disable=redefined-builtin
|
||||
|
||||
|
||||
class Dispatcher(object):
|
||||
|
||||
@ -101,7 +94,7 @@ class Dispatcher(object):
|
||||
"""called by modules param setters to notify subscribers of new values
|
||||
"""
|
||||
# argument pname is no longer used here - should we remove it?
|
||||
msg = (EVENTREPLY, u'%s:%s' % (moduleobj.name, pobj.export),
|
||||
msg = (EVENTREPLY, '%s:%s' % (moduleobj.name, pobj.export),
|
||||
[pobj.export_value(), dict(t=pobj.timestamp)])
|
||||
self.broadcast_event(msg)
|
||||
|
||||
@ -113,7 +106,7 @@ class Dispatcher(object):
|
||||
# argument pname is no longer used here - should we remove it?
|
||||
if not isinstance(err, SECoPError):
|
||||
err = InternalError(err)
|
||||
msg = (ERRORPREFIX + EVENTREPLY, u'%s:%s' % (moduleobj.name, pobj.export),
|
||||
msg = (ERRORPREFIX + EVENTREPLY, '%s:%s' % (moduleobj.name, pobj.export),
|
||||
# error-report !
|
||||
[err.name, repr(err), dict(t=currenttime())])
|
||||
self.broadcast_event(msg)
|
||||
@ -125,7 +118,7 @@ class Dispatcher(object):
|
||||
if not ':' in eventname:
|
||||
# also remove 'more specific' subscriptions
|
||||
for k, v in self._subscriptions.items():
|
||||
if k.startswith(u'%s:' % eventname):
|
||||
if k.startswith('%s:' % eventname):
|
||||
v.discard(conn)
|
||||
if eventname in self._subscriptions:
|
||||
self._subscriptions[eventname].discard(conn)
|
||||
@ -143,7 +136,7 @@ class Dispatcher(object):
|
||||
self._active_connections.discard(conn)
|
||||
|
||||
def register_module(self, moduleobj, modulename, export=True):
|
||||
self.log.debug(u'registering module %r as %s (export=%r)' %
|
||||
self.log.debug('registering module %r as %s (export=%r)' %
|
||||
(moduleobj, modulename, export))
|
||||
self._modules[modulename] = moduleobj
|
||||
if export:
|
||||
@ -154,7 +147,7 @@ class Dispatcher(object):
|
||||
return self._modules[modulename]
|
||||
elif modulename in list(self._modules.values()):
|
||||
return modulename
|
||||
raise NoSuchModuleError(u'Module does not exist on this SEC-Node!')
|
||||
raise NoSuchModuleError('Module does not exist on this SEC-Node!')
|
||||
|
||||
def remove_module(self, modulename_or_obj):
|
||||
moduleobj = self.get_module(modulename_or_obj)
|
||||
@ -163,7 +156,7 @@ class Dispatcher(object):
|
||||
self._export.remove(modulename)
|
||||
self._modules.pop(modulename)
|
||||
self._subscriptions.pop(modulename, None)
|
||||
for k in [k for k in self._subscriptions if k.startswith(u'%s:' % modulename)]:
|
||||
for k in [kk for kk in self._subscriptions if kk.startswith('%s:' % modulename)]:
|
||||
self._subscriptions.pop(k, None)
|
||||
|
||||
def list_module_names(self):
|
||||
@ -171,52 +164,52 @@ class Dispatcher(object):
|
||||
return self._export[:]
|
||||
|
||||
def export_accessibles(self, modulename):
|
||||
self.log.debug(u'export_accessibles(%r)' % modulename)
|
||||
self.log.debug('export_accessibles(%r)' % modulename)
|
||||
if modulename in self._export:
|
||||
# omit export=False params!
|
||||
res = OrderedDict()
|
||||
for aobj in self.get_module(modulename).accessibles.values():
|
||||
if aobj.export:
|
||||
res[aobj.export] = aobj.for_export()
|
||||
self.log.debug(u'list accessibles for module %s -> %r' %
|
||||
self.log.debug('list accessibles for module %s -> %r' %
|
||||
(modulename, res))
|
||||
return res
|
||||
self.log.debug(u'-> module is not to be exported!')
|
||||
self.log.debug('-> module is not to be exported!')
|
||||
return OrderedDict()
|
||||
|
||||
def get_descriptive_data(self):
|
||||
"""returns a python object which upon serialisation results in the descriptive data"""
|
||||
# XXX: be lazy and cache this?
|
||||
result = {u'modules': OrderedDict()}
|
||||
result = {'modules': OrderedDict()}
|
||||
for modulename in self._export:
|
||||
module = self.get_module(modulename)
|
||||
if not module.properties.get('export', False):
|
||||
continue
|
||||
# some of these need rework !
|
||||
mod_desc = {u'accessibles': self.export_accessibles(modulename)}
|
||||
mod_desc = {'accessibles': self.export_accessibles(modulename)}
|
||||
mod_desc.update(module.exportProperties())
|
||||
mod_desc.pop('export', False)
|
||||
result[u'modules'][modulename] = mod_desc
|
||||
result[u'equipment_id'] = self.equipment_id
|
||||
result[u'firmware'] = u'FRAPPY - The Python Framework for SECoP'
|
||||
result[u'version'] = u'2019.08'
|
||||
result['modules'][modulename] = mod_desc
|
||||
result['equipment_id'] = self.equipment_id
|
||||
result['firmware'] = 'FRAPPY - The Python Framework for SECoP'
|
||||
result['version'] = '2019.08'
|
||||
result.update(self.nodeprops)
|
||||
return result
|
||||
|
||||
def _execute_command(self, modulename, exportedname, argument=None):
|
||||
moduleobj = self.get_module(modulename)
|
||||
if moduleobj is None:
|
||||
raise NoSuchModuleError(u'Module does not exist on this SEC-Node!')
|
||||
raise NoSuchModuleError('Module does not exist on this SEC-Node!')
|
||||
|
||||
cmdname = moduleobj.commands.exported.get(exportedname, None)
|
||||
if cmdname is None:
|
||||
raise NoSuchCommandError(u'Module has no command %r on this SEC-Node!' % exportedname)
|
||||
raise NoSuchCommandError('Module has no command %r on this SEC-Node!' % exportedname)
|
||||
cmdspec = moduleobj.commands[cmdname]
|
||||
if argument is None and cmdspec.datatype.argument is not None:
|
||||
raise BadValueError(u'Command needs an argument!')
|
||||
raise BadValueError('Command needs an argument!')
|
||||
|
||||
if argument is not None and cmdspec.datatype.argument is None:
|
||||
raise BadValueError(u'Command takes no argument!')
|
||||
raise BadValueError('Command takes no argument!')
|
||||
|
||||
if cmdspec.datatype.argument:
|
||||
# validate!
|
||||
@ -224,7 +217,7 @@ class Dispatcher(object):
|
||||
|
||||
# now call func
|
||||
# note: exceptions are handled in handle_request, not here!
|
||||
func = getattr(moduleobj, u'do_' + cmdname)
|
||||
func = getattr(moduleobj, 'do_' + cmdname)
|
||||
res = func(argument) if argument else func()
|
||||
|
||||
# pipe through cmdspec.datatype.result
|
||||
@ -236,20 +229,20 @@ class Dispatcher(object):
|
||||
def _setParameterValue(self, modulename, exportedname, value):
|
||||
moduleobj = self.get_module(modulename)
|
||||
if moduleobj is None:
|
||||
raise NoSuchModuleError(u'Module does not exist on this SEC-Node!')
|
||||
raise NoSuchModuleError('Module does not exist on this SEC-Node!')
|
||||
|
||||
pname = moduleobj.parameters.exported.get(exportedname, None)
|
||||
if pname is None:
|
||||
raise NoSuchParameterError(u'Module has no parameter %r on this SEC-Node!' % exportedname)
|
||||
raise NoSuchParameterError('Module has no parameter %r on this SEC-Node!' % exportedname)
|
||||
pobj = moduleobj.parameters[pname]
|
||||
if pobj.constant is not None:
|
||||
raise ReadOnlyError(u'This parameter is constant and can not be accessed remotely.')
|
||||
raise ReadOnlyError('This parameter is constant and can not be accessed remotely.')
|
||||
if pobj.readonly:
|
||||
raise ReadOnlyError(u'This parameter can not be changed remotely.')
|
||||
raise ReadOnlyError('This parameter can not be changed remotely.')
|
||||
|
||||
# validate!
|
||||
value = pobj.datatype(value)
|
||||
writefunc = getattr(moduleobj, u'write_%s' % pname, None)
|
||||
writefunc = getattr(moduleobj, 'write_%s' % pname, None)
|
||||
# note: exceptions are handled in handle_request, not here!
|
||||
if writefunc:
|
||||
# return value is ignored here, as it is automatically set on the pobj and broadcast
|
||||
@ -261,18 +254,18 @@ class Dispatcher(object):
|
||||
def _getParameterValue(self, modulename, exportedname):
|
||||
moduleobj = self.get_module(modulename)
|
||||
if moduleobj is None:
|
||||
raise NoSuchModuleError(u'Module does not exist on this SEC-Node!')
|
||||
raise NoSuchModuleError('Module does not exist on this SEC-Node!')
|
||||
|
||||
pname = moduleobj.parameters.exported.get(exportedname, None)
|
||||
if pname is None:
|
||||
raise NoSuchParameterError(u'Module has no parameter %r on this SEC-Node!' % exportedname)
|
||||
raise NoSuchParameterError('Module has no parameter %r on this SEC-Node!' % exportedname)
|
||||
pobj = moduleobj.parameters[pname]
|
||||
if pobj.constant is not None:
|
||||
# really needed? we could just construct a readreply instead....
|
||||
#raise ReadOnlyError(u'This parameter is constant and can not be accessed remotely.')
|
||||
#raise ReadOnlyError('This parameter is constant and can not be accessed remotely.')
|
||||
return pobj.datatype.export_value(pobj.constant)
|
||||
|
||||
readfunc = getattr(moduleobj, u'read_%s' % pname, None)
|
||||
readfunc = getattr(moduleobj, 'read_%s' % pname, None)
|
||||
if readfunc:
|
||||
# should also update the pobj (via the setter from the metaclass)
|
||||
# note: exceptions are handled in handle_request, not here!
|
||||
@ -288,7 +281,7 @@ class Dispatcher(object):
|
||||
|
||||
will call 'queue_async_reply(data)' on conn or return reply
|
||||
"""
|
||||
self.log.debug(u'Dispatcher: handling msg: %s' % repr(msg))
|
||||
self.log.debug('Dispatcher: handling msg: %s' % repr(msg))
|
||||
|
||||
# play thread safe !
|
||||
# XXX: ONLY ONE REQUEST (per dispatcher) AT A TIME
|
||||
@ -298,8 +291,8 @@ class Dispatcher(object):
|
||||
if action == IDENTREQUEST:
|
||||
action, specifier, data = '_ident', None, None
|
||||
|
||||
self.log.debug(u'Looking for handle_%s' % action)
|
||||
handler = getattr(self, u'handle_%s' % action, None)
|
||||
self.log.debug('Looking for handle_%s' % action)
|
||||
handler = getattr(self, 'handle_%s' % action, None)
|
||||
|
||||
if handler:
|
||||
return handler(conn, specifier, data)
|
||||
@ -319,28 +312,28 @@ class Dispatcher(object):
|
||||
def handle_read(self, conn, specifier, data):
|
||||
if data:
|
||||
raise ProtocolError('read requests don\'t take data!')
|
||||
modulename, pname = specifier, u'value'
|
||||
modulename, pname = specifier, 'value'
|
||||
if ':' in specifier:
|
||||
modulename, pname = specifier.split(':', 1)
|
||||
# XXX: trigger polling and force sending event ???
|
||||
return (READREPLY, specifier, list(self._getParameterValue(modulename, pname)))
|
||||
|
||||
def handle_change(self, conn, specifier, data):
|
||||
modulename, pname = specifier, u'value'
|
||||
modulename, pname = specifier, 'value'
|
||||
if ':' in specifier:
|
||||
modulename, pname = specifier.split(u':', 1)
|
||||
modulename, pname = specifier.split(':', 1)
|
||||
return (WRITEREPLY, specifier, list(self._setParameterValue(modulename, pname, data)))
|
||||
|
||||
def handle_do(self, conn, specifier, data):
|
||||
# XXX: should this be done asyncron? we could just return the reply in
|
||||
# that case
|
||||
modulename, cmd = specifier.split(u':', 1)
|
||||
modulename, cmd = specifier.split(':', 1)
|
||||
return (COMMANDREPLY, specifier, list(self._execute_command(modulename, cmd, data)))
|
||||
|
||||
def handle_ping(self, conn, specifier, data):
|
||||
if data:
|
||||
raise ProtocolError('ping requests don\'t take data!')
|
||||
return (HEARTBEATREPLY, specifier, [None, {u't':currenttime()}])
|
||||
return (HEARTBEATREPLY, specifier, [None, {'t':currenttime()}])
|
||||
|
||||
def handle_activate(self, conn, specifier, data):
|
||||
if data:
|
||||
@ -348,7 +341,7 @@ class Dispatcher(object):
|
||||
if specifier:
|
||||
modulename, exportedname = specifier, None
|
||||
if ':' in specifier:
|
||||
modulename, exportedname = specifier.split(u':', 1)
|
||||
modulename, exportedname = specifier.split(':', 1)
|
||||
if modulename not in self._export:
|
||||
raise NoSuchModuleError('Module does not exist on this SEC-Node!')
|
||||
moduleobj = self.get_module(modulename)
|
||||
@ -373,7 +366,7 @@ class Dispatcher(object):
|
||||
moduleobj = self._modules.get(modulename, None)
|
||||
if pname:
|
||||
pobj = moduleobj.accessibles[pname]
|
||||
updmsg = (EVENTREPLY, u'%s:%s' % (modulename, pobj.export),
|
||||
updmsg = (EVENTREPLY, '%s:%s' % (modulename, pobj.export),
|
||||
[pobj.export_value(), dict(t=pobj.timestamp)])
|
||||
conn.queue_async_reply(updmsg)
|
||||
continue
|
||||
@ -383,7 +376,7 @@ class Dispatcher(object):
|
||||
if not pobj.export:
|
||||
continue
|
||||
# can not use announce_update here, as this will send to all clients
|
||||
updmsg = (EVENTREPLY, u'%s:%s' % (modulename, pobj.export),
|
||||
updmsg = (EVENTREPLY, '%s:%s' % (modulename, pobj.export),
|
||||
[pobj.export_value(), dict(t=pobj.timestamp)])
|
||||
conn.queue_async_reply(updmsg)
|
||||
return (ENABLEEVENTSREPLY, specifier, None) if specifier else (ENABLEEVENTSREPLY, None, None)
|
||||
|
@ -28,7 +28,7 @@ SPACE = b' '
|
||||
def encode_msg_frame(action, specifier=None, data=None):
|
||||
""" encode a msg_tripel into an msg_frame, ready to be sent
|
||||
|
||||
action (and optional specifier) are unicode strings,
|
||||
action (and optional specifier) are str strings,
|
||||
data may be an json-yfied python object"""
|
||||
action = action.encode('utf-8')
|
||||
if specifier is None:
|
||||
@ -54,7 +54,7 @@ def get_msg(_bytes):
|
||||
|
||||
|
||||
def decode_msg(msg):
|
||||
"""decode the (binary) msg into a (unicode) msg_tripel"""
|
||||
"""decode the (binary) msg into a (str) msg_tripel"""
|
||||
# check for leading/trailing CR and remove it
|
||||
res = msg.split(b' ', 2)
|
||||
action = res[0].decode('utf-8')
|
||||
|
@ -19,11 +19,11 @@
|
||||
#
|
||||
# *****************************************************************************
|
||||
"""provides tcp interface to the SECoP Server"""
|
||||
from __future__ import division, print_function
|
||||
|
||||
import collections
|
||||
import socket
|
||||
import sys
|
||||
import socket
|
||||
import collections
|
||||
import socketserver
|
||||
|
||||
from secop.datatypes import StringType, IntRange, BoolType
|
||||
from secop.errors import SECoPError
|
||||
@ -34,17 +34,10 @@ from secop.protocol.interface import decode_msg, encode_msg_frame, get_msg
|
||||
from secop.protocol.messages import ERRORPREFIX, \
|
||||
HELPREPLY, HELPREQUEST, HelpMessage
|
||||
|
||||
try:
|
||||
import socketserver # py3
|
||||
except ImportError:
|
||||
import SocketServer as socketserver # py2
|
||||
|
||||
|
||||
|
||||
DEF_PORT = 10767
|
||||
MESSAGE_READ_SIZE = 1024
|
||||
|
||||
|
||||
CR = b'\r'
|
||||
SPACE = b' '
|
||||
|
||||
|
@ -21,7 +21,6 @@
|
||||
# *****************************************************************************
|
||||
"""provide a zmq server"""
|
||||
|
||||
from __future__ import division, print_function
|
||||
|
||||
# tbd.
|
||||
|
||||
|
@ -20,50 +20,49 @@
|
||||
#
|
||||
# *****************************************************************************
|
||||
"""Define SECoP Messages"""
|
||||
from __future__ import division, print_function
|
||||
|
||||
# allowed actions:
|
||||
|
||||
IDENTREQUEST = u'*IDN?' # literal
|
||||
IDENTREQUEST = '*IDN?' # literal
|
||||
# literal! first part is fixed!
|
||||
IDENTREPLY = u'SINE2020&ISSE,SECoP,V2019-08-20,v1.0 RC2'
|
||||
IDENTREPLY = 'SINE2020&ISSE,SECoP,V2019-08-20,v1.0 RC2'
|
||||
|
||||
DESCRIPTIONREQUEST = u'describe' # literal
|
||||
DESCRIPTIONREPLY = u'describing' # +<id> +json
|
||||
DESCRIPTIONREQUEST = 'describe' # literal
|
||||
DESCRIPTIONREPLY = 'describing' # +<id> +json
|
||||
|
||||
ENABLEEVENTSREQUEST = u'activate' # literal + optional spec
|
||||
ENABLEEVENTSREPLY = u'active' # literal + optional spec, is end-of-initial-data-transfer
|
||||
ENABLEEVENTSREQUEST = 'activate' # literal + optional spec
|
||||
ENABLEEVENTSREPLY = 'active' # literal + optional spec, is end-of-initial-data-transfer
|
||||
|
||||
DISABLEEVENTSREQUEST = u'deactivate' # literal + optional spec
|
||||
DISABLEEVENTSREPLY = u'inactive' # literal + optional spec
|
||||
DISABLEEVENTSREQUEST = 'deactivate' # literal + optional spec
|
||||
DISABLEEVENTSREPLY = 'inactive' # literal + optional spec
|
||||
|
||||
COMMANDREQUEST = u'do' # +module:command +json args (if needed)
|
||||
COMMANDREQUEST = 'do' # +module:command +json args (if needed)
|
||||
# +module:command +json args (if needed) # send after the command finished !
|
||||
COMMANDREPLY = u'done'
|
||||
COMMANDREPLY = 'done'
|
||||
|
||||
# +module[:parameter] +json_value
|
||||
WRITEREQUEST = u'change'
|
||||
WRITEREQUEST = 'change'
|
||||
# +module[:parameter] +json_value # send with the read back value
|
||||
WRITEREPLY = u'changed'
|
||||
WRITEREPLY = 'changed'
|
||||
|
||||
# +module[:parameter] +json_value
|
||||
BUFFERREQUEST = u'buffer'
|
||||
BUFFERREQUEST = 'buffer'
|
||||
# +module[:parameter] +json_value # send with the read back value
|
||||
BUFFERREPLY = u'buffered'
|
||||
BUFFERREPLY = 'buffered'
|
||||
|
||||
# +module[:parameter] -> NO direct reply, calls POLL internally!
|
||||
READREQUEST = u'read'
|
||||
READREPLY = u'reply' # See Issue 54
|
||||
READREQUEST = 'read'
|
||||
READREPLY = 'reply' # See Issue 54
|
||||
|
||||
EVENTREPLY = u'update' # +module[:parameter] +json_value (value, qualifiers_as_dict)
|
||||
EVENTREPLY = 'update' # +module[:parameter] +json_value (value, qualifiers_as_dict)
|
||||
|
||||
HEARTBEATREQUEST = u'ping' # +nonce_without_space
|
||||
HEARTBEATREPLY = u'pong' # +nonce_without_space
|
||||
HEARTBEATREQUEST = 'ping' # +nonce_without_space
|
||||
HEARTBEATREPLY = 'pong' # +nonce_without_space
|
||||
|
||||
ERRORPREFIX = u'error_' # + specifier + json_extended_info(error_report)
|
||||
ERRORPREFIX = 'error_' # + specifier + json_extended_info(error_report)
|
||||
|
||||
HELPREQUEST = u'help' # literal
|
||||
HELPREPLY = u'helping' # +line number +json_text
|
||||
HELPREQUEST = 'help' # literal
|
||||
HELPREPLY = 'helping' # +line number +json_text
|
||||
|
||||
# helper mapping to find the REPLY for a REQUEST
|
||||
REQUEST2REPLY = {
|
||||
|
@ -21,12 +21,12 @@
|
||||
#
|
||||
# *****************************************************************************
|
||||
"""Define helpers"""
|
||||
from __future__ import division, print_function
|
||||
|
||||
import ast
|
||||
import os
|
||||
import threading
|
||||
import ast
|
||||
import time
|
||||
import threading
|
||||
import configparser
|
||||
from collections import OrderedDict
|
||||
|
||||
from daemon import DaemonContext
|
||||
@ -34,19 +34,12 @@ from daemon import DaemonContext
|
||||
from secop.errors import ConfigError
|
||||
from secop.lib import formatException, get_class, getGeneralConfig
|
||||
|
||||
try:
|
||||
import configparser # py3
|
||||
except ImportError:
|
||||
import ConfigParser as configparser # py2
|
||||
|
||||
try:
|
||||
import daemon.pidlockfile as pidlockfile
|
||||
except ImportError:
|
||||
import daemon.pidfile as pidlockfile
|
||||
|
||||
|
||||
|
||||
|
||||
class Server(object):
|
||||
# list allowed section prefixes
|
||||
# if mapped dict does not exist -> section need a 'class' option
|
||||
@ -66,12 +59,12 @@ class Server(object):
|
||||
if os.path.abspath(name) == name and os.path.exists(name) and \
|
||||
name.endswith('.cfg'):
|
||||
self._cfgfile = name
|
||||
self._pidfile = os.path.join(cfg[u'piddir'],
|
||||
name[:-4].replace(os.path.sep, u'_') + u'.pid')
|
||||
self._pidfile = os.path.join(cfg['piddir'],
|
||||
name[:-4].replace(os.path.sep, '_') + '.pid')
|
||||
name = os.path.basename(name[:-4])
|
||||
else:
|
||||
self._cfgfile = os.path.join(cfg[u'confdir'], name + u'.cfg')
|
||||
self._pidfile = os.path.join(cfg[u'piddir'], name + u'.pid')
|
||||
self._cfgfile = os.path.join(cfg['confdir'], name + '.cfg')
|
||||
self._pidfile = os.path.join(cfg['piddir'], name + '.pid')
|
||||
|
||||
self._name = name
|
||||
|
||||
@ -87,7 +80,7 @@ class Server(object):
|
||||
pidfile = pidlockfile.TimeoutPIDLockFile(self._pidfile)
|
||||
|
||||
if pidfile.is_locked():
|
||||
self.log.error(u'Pidfile already exists. Exiting')
|
||||
self.log.error('Pidfile already exists. Exiting')
|
||||
|
||||
with DaemonContext(
|
||||
pidfile=pidfile,
|
||||
@ -101,10 +94,10 @@ class Server(object):
|
||||
print(formatException(verbose=True))
|
||||
raise
|
||||
|
||||
self.log.info(u'startup done, handling transport messages')
|
||||
self.log.info('startup done, handling transport messages')
|
||||
self._threads = set()
|
||||
for ifname, ifobj in self.interfaces.items():
|
||||
self.log.debug(u'starting thread for interface %r' % ifname)
|
||||
self.log.debug('starting thread for interface %r' % ifname)
|
||||
t = threading.Thread(target=ifobj.serve_forever)
|
||||
t.daemon = True
|
||||
t.start()
|
||||
@ -113,78 +106,78 @@ class Server(object):
|
||||
time.sleep(1)
|
||||
for t in self._threads:
|
||||
if not t.is_alive():
|
||||
self.log.debug(u'thread %r died (%d still running)' %
|
||||
self.log.debug('thread %r died (%d still running)' %
|
||||
(t, len(self._threads)))
|
||||
t.join()
|
||||
self._threads.discard(t)
|
||||
|
||||
def _processCfg(self):
|
||||
self.log.debug(u'Parse config file %s ...' % self._cfgfile)
|
||||
self.log.debug('Parse config file %s ...' % self._cfgfile)
|
||||
|
||||
parser = configparser.SafeConfigParser()
|
||||
parser.optionxform = str
|
||||
|
||||
if not parser.read([self._cfgfile]):
|
||||
self.log.error(u'Couldn\'t read cfg file !')
|
||||
raise ConfigError(u'Couldn\'t read cfg file %r' % self._cfgfile)
|
||||
self.log.error('Couldn\'t read cfg file !')
|
||||
raise ConfigError('Couldn\'t read cfg file %r' % self._cfgfile)
|
||||
|
||||
|
||||
|
||||
for kind, devtype, classmapping in self.CFGSECTIONS:
|
||||
kinds = u'%ss' % kind
|
||||
kinds = '%ss' % kind
|
||||
objs = OrderedDict()
|
||||
self.__dict__[kinds] = objs
|
||||
for section in parser.sections():
|
||||
prefix = u'%s ' % kind
|
||||
prefix = '%s ' % kind
|
||||
if section.lower().startswith(prefix):
|
||||
name = section[len(prefix):]
|
||||
opts = dict(item for item in parser.items(section))
|
||||
if u'class' in opts:
|
||||
cls = opts.pop(u'class')
|
||||
if 'class' in opts:
|
||||
cls = opts.pop('class')
|
||||
else:
|
||||
if not classmapping:
|
||||
self.log.error(u'%s %s needs a class option!' % (kind.title(), name))
|
||||
raise ConfigError(u'cfgfile %r: %s %s needs a class option!' %
|
||||
self.log.error('%s %s needs a class option!' % (kind.title(), name))
|
||||
raise ConfigError('cfgfile %r: %s %s needs a class option!' %
|
||||
(self._cfgfile, kind.title(), name))
|
||||
type_ = opts.pop(u'type', devtype)
|
||||
type_ = opts.pop('type', devtype)
|
||||
cls = classmapping.get(type_, None)
|
||||
if not cls:
|
||||
self.log.error(u'%s %s needs a type option (select one of %s)!' %
|
||||
self.log.error('%s %s needs a type option (select one of %s)!' %
|
||||
(kind.title(), name, ', '.join(repr(r) for r in classmapping)))
|
||||
raise ConfigError(u'cfgfile %r: %s %s needs a type option (select one of %s)!' %
|
||||
raise ConfigError('cfgfile %r: %s %s needs a type option (select one of %s)!' %
|
||||
(self._cfgfile, kind.title(), name, ', '.join(repr(r) for r in classmapping)))
|
||||
# MAGIC: transform \n.\n into \n\n which are normally stripped
|
||||
# by the ini parser
|
||||
for k in opts:
|
||||
v = opts[k]
|
||||
while u'\n.\n' in v:
|
||||
v = v.replace(u'\n.\n', u'\n\n')
|
||||
while '\n.\n' in v:
|
||||
v = v.replace('\n.\n', '\n\n')
|
||||
try:
|
||||
opts[k] = ast.literal_eval(v)
|
||||
except Exception:
|
||||
opts[k] = v
|
||||
|
||||
# try to import the class, raise if this fails
|
||||
self.log.debug(u'Creating %s %s ...' % (kind.title(), name))
|
||||
self.log.debug('Creating %s %s ...' % (kind.title(), name))
|
||||
# cls.__init__ should pop all used args from options!
|
||||
logname = u'dispatcher' if kind == u'node' else u'%s_%s' % (kind, name.lower())
|
||||
logname = 'dispatcher' if kind == 'node' else '%s_%s' % (kind, name.lower())
|
||||
obj = get_class(cls)(name, self.log.getChild(logname), opts, self)
|
||||
if opts:
|
||||
raise ConfigError(u'%s %s: class %s: don\'t know how to handle option(s): %s' %
|
||||
(kind, name, cls, u', '.join(opts)))
|
||||
raise ConfigError('%s %s: class %s: don\'t know how to handle option(s): %s' %
|
||||
(kind, name, cls, ', '.join(opts)))
|
||||
|
||||
# all went well so far
|
||||
objs[name] = obj
|
||||
|
||||
# following line is the reason for 'node' beeing the first entry in CFGSECTIONS
|
||||
if len(self.nodes) != 1:
|
||||
raise ConfigError(u'cfgfile %r: needs exactly one node section!' % self._cfgfile)
|
||||
raise ConfigError('cfgfile %r: needs exactly one node section!' % self._cfgfile)
|
||||
self.dispatcher, = tuple(self.nodes.values())
|
||||
|
||||
pollTable = dict()
|
||||
# all objs created, now start them up and interconnect
|
||||
for modname, modobj in self.modules.items():
|
||||
self.log.info(u'registering module %r' % modname)
|
||||
self.log.info('registering module %r' % modname)
|
||||
self.dispatcher.register_module(modobj, modname, modobj.properties['export'])
|
||||
try:
|
||||
modobj.pollerClass.add_to_table(pollTable, modobj)
|
||||
@ -208,8 +201,8 @@ class Server(object):
|
||||
# poller.start must return either a timeout value or None (default 30 sec)
|
||||
timeout = poller.start(started_callback=event.set) or 30
|
||||
start_events.append((time.time() + timeout, repr(poller), event))
|
||||
self.log.info(u'waiting for modules and pollers being started')
|
||||
self.log.info('waiting for modules and pollers being started')
|
||||
for deadline, name, event in sorted(start_events):
|
||||
if not event.wait(timeout=max(0, deadline - time.time())):
|
||||
self.log.info('WARNING: timeout when starting %s' % name)
|
||||
self.log.info(u'all modules and pollers started')
|
||||
self.log.info('all modules and pollers started')
|
||||
|
@ -21,7 +21,6 @@
|
||||
# *****************************************************************************
|
||||
"""Define Simulation classes"""
|
||||
|
||||
from __future__ import division, print_function
|
||||
|
||||
import random
|
||||
from time import sleep
|
||||
|
@ -23,7 +23,6 @@
|
||||
#
|
||||
# *****************************************************************************
|
||||
|
||||
from __future__ import division, print_function
|
||||
|
||||
import os.path
|
||||
from subprocess import PIPE, Popen
|
||||
|
@ -20,7 +20,6 @@
|
||||
# *****************************************************************************
|
||||
"""playing implementation of a (simple) simulated cryostat"""
|
||||
|
||||
from __future__ import division, print_function
|
||||
|
||||
import random
|
||||
import time
|
||||
|
@ -20,7 +20,6 @@
|
||||
# *****************************************************************************
|
||||
"""testing devices"""
|
||||
|
||||
from __future__ import division, print_function
|
||||
|
||||
import random
|
||||
import threading
|
||||
|
@ -20,7 +20,6 @@
|
||||
# *****************************************************************************
|
||||
"""testing devices"""
|
||||
|
||||
from __future__ import division, print_function
|
||||
|
||||
import random
|
||||
|
||||
@ -28,15 +27,6 @@ from secop.datatypes import FloatRange, StringType
|
||||
from secop.modules import Communicator, Drivable, Parameter, Readable, Override
|
||||
from secop.params import Command
|
||||
|
||||
try:
|
||||
# py2
|
||||
unicode
|
||||
except NameError:
|
||||
# py3
|
||||
unicode = str # pylint: disable=redefined-builtin
|
||||
|
||||
|
||||
|
||||
|
||||
class LN2(Readable):
|
||||
"""Just a readable.
|
||||
@ -104,4 +94,4 @@ class Lower(Communicator):
|
||||
'communicate': Command('lowercase a string', argument=StringType(), result=StringType(), export='communicate'),
|
||||
}
|
||||
def do_communicate(self, request):
|
||||
return unicode(request).lower()
|
||||
return str(request).lower()
|
||||
|
@ -20,7 +20,6 @@
|
||||
#
|
||||
# *****************************************************************************
|
||||
|
||||
from __future__ import absolute_import, division, print_function
|
||||
|
||||
from secop.datatypes import EnumType, FloatRange, StringType
|
||||
from secop.modules import Drivable, Parameter, Readable
|
||||
|
@ -25,7 +25,6 @@
|
||||
|
||||
# partially borrowed from nicos
|
||||
|
||||
from __future__ import division, print_function
|
||||
|
||||
import math
|
||||
|
||||
|
@ -27,7 +27,6 @@
|
||||
Here we support devices which fulfill the official
|
||||
MLZ TANGO interface for the respective device classes.
|
||||
"""
|
||||
from __future__ import division, print_function
|
||||
|
||||
import re
|
||||
import threading
|
||||
|
1
setup.py
1
setup.py
@ -22,7 +22,6 @@
|
||||
#
|
||||
# *****************************************************************************
|
||||
|
||||
from __future__ import division, print_function
|
||||
|
||||
from glob import glob
|
||||
from os import listdir, path
|
||||
|
@ -1,5 +1,4 @@
|
||||
# content of conftest.py
|
||||
from __future__ import division, print_function
|
||||
|
||||
import pytest
|
||||
|
||||
|
@ -20,7 +20,6 @@
|
||||
#
|
||||
# *****************************************************************************
|
||||
"""test basic validators."""
|
||||
from __future__ import division, print_function
|
||||
|
||||
# no fixtures needed
|
||||
import pytest
|
||||
@ -42,9 +41,9 @@ class unprintable(object):
|
||||
[PositiveIntProperty, ['x', 1.9, '-9', '1e-4'], [1, '1']],
|
||||
[NonNegativeIntProperty, ['x', 1.9, '-9', '1e-6'], [0, '1']],
|
||||
[BoolProperty, ['x', 3], ['on', 'off', True, False]],
|
||||
[StringProperty, [unprintable()], [u'1', 1.2, [{}]]],
|
||||
[UnitProperty, [unprintable(), '3', 9], [u'mm', 'Gbarn', 'acre']],
|
||||
[FmtStrProperty, [1, None, 'a', '%f'], [u'%.0e', u'%.3f','%.1g']],
|
||||
[StringProperty, [unprintable()], ['1', 1.2, [{}]]],
|
||||
[UnitProperty, [unprintable(), '3', 9], ['mm', 'Gbarn', 'acre']],
|
||||
[FmtStrProperty, [1, None, 'a', '%f'], ['%.0e', '%.3f','%.1g']],
|
||||
])
|
||||
def test_validators(validators_args):
|
||||
v, fails, oks = validators_args
|
||||
@ -72,7 +71,7 @@ def test_checker_fails(checker_inits):
|
||||
@pytest.mark.parametrize('checker_args', [
|
||||
[OneOfProperty(1,2,3), ['x', None, 4], [1, 2, 3]],
|
||||
[NoneOr(IntProperty), ['a', 1.2, '1.2'], [None, 1, '-1', '999999999999999']],
|
||||
[EnumProperty(a=1, b=2), ['x', None, 3], [u'a', 'b', 1, 2]],
|
||||
[EnumProperty(a=1, b=2), ['x', None, 3], ['a', 'b', 1, 2]],
|
||||
[TupleProperty(IntProperty, StringProperty), [1, 'a', ('x', 2)], [(1,'x')]],
|
||||
])
|
||||
def test_checkers(checker_args):
|
||||
|
@ -20,7 +20,6 @@
|
||||
#
|
||||
# *****************************************************************************
|
||||
"""test base client."""
|
||||
from __future__ import division, print_function
|
||||
|
||||
from collections import OrderedDict
|
||||
|
||||
|
@ -21,7 +21,6 @@
|
||||
# *****************************************************************************
|
||||
"""test data types."""
|
||||
|
||||
from __future__ import division, print_function
|
||||
|
||||
# no fixtures needed
|
||||
import pytest
|
||||
@ -56,34 +55,34 @@ def test_FloatRange():
|
||||
with pytest.raises(ValueError):
|
||||
dt(-9)
|
||||
with pytest.raises(ValueError):
|
||||
dt(u'XX')
|
||||
dt('XX')
|
||||
with pytest.raises(ValueError):
|
||||
dt([19, u'X'])
|
||||
dt([19, 'X'])
|
||||
dt(1)
|
||||
dt(0)
|
||||
dt(13.14 - 10) # raises an error, if resolution is not handled correctly
|
||||
assert dt.export_value(-2.718) == -2.718
|
||||
assert dt.import_value(-2.718) == -2.718
|
||||
with pytest.raises(ValueError):
|
||||
FloatRange(u'x', u'Y')
|
||||
FloatRange('x', 'Y')
|
||||
# check that unit can be changed
|
||||
dt.unit = u'K'
|
||||
assert dt.export_datatype() == {'type': 'double', 'min':-3.14, 'max':3.14, 'unit': u'K'}
|
||||
dt.unit = 'K'
|
||||
assert dt.export_datatype() == {'type': 'double', 'min':-3.14, 'max':3.14, 'unit': 'K'}
|
||||
|
||||
dt = FloatRange()
|
||||
copytest(dt)
|
||||
assert dt.export_datatype() == {'type': 'double'}
|
||||
|
||||
dt = FloatRange(unit=u'X', fmtstr=u'%.2f', absolute_resolution=1,
|
||||
dt = FloatRange(unit='X', fmtstr='%.2f', absolute_resolution=1,
|
||||
relative_resolution=0.1)
|
||||
copytest(dt)
|
||||
assert dt.export_datatype() == {'type': 'double', 'unit':'X', 'fmtstr':'%.2f',
|
||||
'absolute_resolution':1.0,
|
||||
'relative_resolution':0.1}
|
||||
assert dt(4) == 4
|
||||
assert dt.format_value(3.14) == u'3.14 X'
|
||||
assert dt.format_value(3.14, u'') == u'3.14'
|
||||
assert dt.format_value(3.14, u'#') == u'3.14 #'
|
||||
assert dt.format_value(3.14) == '3.14 X'
|
||||
assert dt.format_value(3.14, '') == '3.14'
|
||||
assert dt.format_value(3.14, '#') == '3.14 #'
|
||||
|
||||
|
||||
def test_IntRange():
|
||||
@ -96,20 +95,20 @@ def test_IntRange():
|
||||
with pytest.raises(ValueError):
|
||||
dt(-9)
|
||||
with pytest.raises(ValueError):
|
||||
dt(u'XX')
|
||||
dt('XX')
|
||||
with pytest.raises(ValueError):
|
||||
dt([19, u'X'])
|
||||
dt([19, 'X'])
|
||||
dt(1)
|
||||
dt(0)
|
||||
with pytest.raises(ValueError):
|
||||
IntRange(u'xc', u'Yx')
|
||||
IntRange('xc', 'Yx')
|
||||
|
||||
dt = IntRange()
|
||||
copytest(dt)
|
||||
assert dt.export_datatype()['type'] == 'int'
|
||||
assert dt.export_datatype()['min'] < 0 < dt.export_datatype()['max']
|
||||
assert dt.export_datatype() == {'type': 'int', 'max': 16777216,u'min': -16777216}
|
||||
assert dt.format_value(42) == u'42'
|
||||
assert dt.export_datatype() == {'type': 'int', 'max': 16777216,'min': -16777216}
|
||||
assert dt.format_value(42) == '42'
|
||||
|
||||
def test_ScaledInteger():
|
||||
dt = ScaledInteger(0.01, -3, 3)
|
||||
@ -122,36 +121,36 @@ def test_ScaledInteger():
|
||||
with pytest.raises(ValueError):
|
||||
dt(-9)
|
||||
with pytest.raises(ValueError):
|
||||
dt(u'XX')
|
||||
dt('XX')
|
||||
with pytest.raises(ValueError):
|
||||
dt([19, u'X'])
|
||||
dt([19, 'X'])
|
||||
dt(1)
|
||||
dt(0)
|
||||
with pytest.raises(ValueError):
|
||||
ScaledInteger(u'xc', u'Yx')
|
||||
ScaledInteger('xc', 'Yx')
|
||||
with pytest.raises(ValueError):
|
||||
ScaledInteger(scale=0, minval=1, maxval=2)
|
||||
with pytest.raises(ValueError):
|
||||
ScaledInteger(scale=-10, minval=1, maxval=2)
|
||||
# check that unit can be changed
|
||||
dt.unit = u'A'
|
||||
dt.unit = 'A'
|
||||
assert dt.export_datatype() == {'type': 'scaled', 'scale':0.01, 'min':-300, 'max':300, 'unit': 'A'}
|
||||
|
||||
assert dt.export_value(0.0001) == int(0)
|
||||
assert dt.export_value(2.71819) == int(272)
|
||||
assert dt.import_value(272) == 2.72
|
||||
|
||||
dt = ScaledInteger(0.003, 0, 1, unit=u'X', fmtstr=u'%.1f',
|
||||
dt = ScaledInteger(0.003, 0, 1, unit='X', fmtstr='%.1f',
|
||||
absolute_resolution=0.001, relative_resolution=1e-5)
|
||||
copytest(dt)
|
||||
assert dt.export_datatype() == {'type': 'scaled', 'scale':0.003, 'min':0, 'max':333,
|
||||
u'unit':u'X', u'fmtstr':u'%.1f',
|
||||
u'absolute_resolution':0.001,
|
||||
u'relative_resolution':1e-5}
|
||||
'unit':'X', 'fmtstr':'%.1f',
|
||||
'absolute_resolution':0.001,
|
||||
'relative_resolution':1e-5}
|
||||
assert dt(0.4) == 0.399
|
||||
assert dt.format_value(0.4) == u'0.4 X'
|
||||
assert dt.format_value(0.4, u'') == u'0.4'
|
||||
assert dt.format_value(0.4, u'Z') == u'0.4 Z'
|
||||
assert dt.format_value(0.4) == '0.4 X'
|
||||
assert dt.format_value(0.4, '') == '0.4'
|
||||
assert dt.format_value(0.4, 'Z') == '0.4 Z'
|
||||
assert dt(1.0029) == 0.999
|
||||
with pytest.raises(ValueError):
|
||||
dt(1.004)
|
||||
@ -162,9 +161,9 @@ def test_EnumType():
|
||||
with pytest.raises(TypeError):
|
||||
EnumType(1)
|
||||
with pytest.raises(TypeError):
|
||||
EnumType([u'b', 0])
|
||||
EnumType(['b', 0])
|
||||
|
||||
dt = EnumType(u'dt', a=3, c=7, stuff=1)
|
||||
dt = EnumType('dt', a=3, c=7, stuff=1)
|
||||
copytest(dt)
|
||||
assert dt.export_datatype() == {'type': 'enum', 'members': dict(a=3, c=7, stuff=1)}
|
||||
|
||||
@ -173,28 +172,28 @@ def test_EnumType():
|
||||
with pytest.raises(ValueError):
|
||||
dt(-9)
|
||||
with pytest.raises(ValueError):
|
||||
dt(u'XX')
|
||||
dt('XX')
|
||||
with pytest.raises(ValueError):
|
||||
dt([19, u'X'])
|
||||
dt([19, 'X'])
|
||||
|
||||
assert dt(u'a') == 3
|
||||
assert dt(u'stuff') == 1
|
||||
assert dt('a') == 3
|
||||
assert dt('stuff') == 1
|
||||
assert dt(1) == 1
|
||||
with pytest.raises(ValueError):
|
||||
dt(2)
|
||||
|
||||
assert dt.export_value(u'c') == 7
|
||||
assert dt.export_value(u'stuff') == 1
|
||||
assert dt.export_value('c') == 7
|
||||
assert dt.export_value('stuff') == 1
|
||||
assert dt.export_value(1) == 1
|
||||
assert dt.import_value(u'c') == 7
|
||||
assert dt.import_value(u'a') == 3
|
||||
assert dt.import_value(u'stuff') == 1
|
||||
assert dt.import_value('c') == 7
|
||||
assert dt.import_value('a') == 3
|
||||
assert dt.import_value('stuff') == 1
|
||||
with pytest.raises(ValueError):
|
||||
dt.export_value(2)
|
||||
with pytest.raises(ValueError):
|
||||
dt.import_value(u'A')
|
||||
dt.import_value('A')
|
||||
|
||||
assert dt.format_value(3) == u'a<3>'
|
||||
assert dt.format_value(3) == 'a<3>'
|
||||
|
||||
|
||||
def test_BLOBType():
|
||||
@ -217,13 +216,13 @@ def test_BLOBType():
|
||||
with pytest.raises(ValueError):
|
||||
dt(b'abcdefghijklmno')
|
||||
with pytest.raises(ValueError):
|
||||
dt(u'abcd')
|
||||
dt('abcd')
|
||||
assert dt(b'abcd') == b'abcd'
|
||||
|
||||
assert dt.export_value(b'abcd') == u'YWJjZA=='
|
||||
assert dt.export_value(b'abcd') == u'YWJjZA=='
|
||||
# assert dt.export_value(u'abcd') == u'YWJjZA=='
|
||||
assert dt.import_value(u'YWJjZA==') == b'abcd'
|
||||
assert dt.export_value(b'abcd') == 'YWJjZA=='
|
||||
assert dt.export_value(b'abcd') == 'YWJjZA=='
|
||||
# assert dt.export_value('abcd') == 'YWJjZA=='
|
||||
assert dt.import_value('YWJjZA==') == b'abcd'
|
||||
|
||||
# XXX: right? or different format?
|
||||
# to be added after migration to py3
|
||||
@ -246,9 +245,9 @@ def test_StringType():
|
||||
with pytest.raises(ValueError):
|
||||
dt(9)
|
||||
with pytest.raises(ValueError):
|
||||
dt(u'av')
|
||||
dt('av')
|
||||
with pytest.raises(ValueError):
|
||||
dt(u'abcdefghijklmno')
|
||||
dt('abcdefghijklmno')
|
||||
with pytest.raises(ValueError):
|
||||
dt('abcdefg\0')
|
||||
assert dt('abcd') == 'abcd'
|
||||
@ -271,7 +270,7 @@ def test_TextType():
|
||||
with pytest.raises(ValueError):
|
||||
dt(9)
|
||||
with pytest.raises(ValueError):
|
||||
dt(u'abcdefghijklmno')
|
||||
dt('abcdefghijklmno')
|
||||
with pytest.raises(ValueError):
|
||||
dt('abcdefg\0')
|
||||
assert dt('ab\n\ncd\n') == 'ab\n\ncd\n'
|
||||
@ -292,20 +291,20 @@ def test_BoolType():
|
||||
with pytest.raises(ValueError):
|
||||
dt(9)
|
||||
with pytest.raises(ValueError):
|
||||
dt(u'av')
|
||||
dt('av')
|
||||
|
||||
assert dt(u'true') is True
|
||||
assert dt(u'off') is False
|
||||
assert dt('true') is True
|
||||
assert dt('off') is False
|
||||
assert dt(1) is True
|
||||
|
||||
assert dt.export_value(u'false') is False
|
||||
assert dt.export_value('false') is False
|
||||
assert dt.export_value(0) is False
|
||||
assert dt.export_value(u'on') is True
|
||||
assert dt.export_value('on') is True
|
||||
|
||||
assert dt.import_value(False) is False
|
||||
assert dt.import_value(True) is True
|
||||
with pytest.raises(ValueError):
|
||||
dt.import_value(u'av')
|
||||
dt.import_value('av')
|
||||
|
||||
assert dt.format_value(0) == u"False"
|
||||
assert dt.format_value(True) == u"True"
|
||||
@ -323,7 +322,7 @@ def test_ArrayOf():
|
||||
'members': {'type': 'int', 'min':-10,
|
||||
'max':10}}
|
||||
|
||||
dt = ArrayOf(FloatRange(-10, 10, unit=u'Z'), 1, 3)
|
||||
dt = ArrayOf(FloatRange(-10, 10, unit='Z'), 1, 3)
|
||||
copytest(dt)
|
||||
assert dt.export_datatype() == {'type': 'array', 'minlen':1, 'maxlen':3,
|
||||
'members':{'type': 'double', 'min':-10,
|
||||
@ -331,16 +330,16 @@ def test_ArrayOf():
|
||||
with pytest.raises(ValueError):
|
||||
dt(9)
|
||||
with pytest.raises(ValueError):
|
||||
dt(u'av')
|
||||
dt('av')
|
||||
|
||||
assert dt([1, 2, 3]) == [1, 2, 3]
|
||||
|
||||
assert dt.export_value([1, 2, 3]) == [1, 2, 3]
|
||||
assert dt.import_value([1, 2, 3]) == [1, 2, 3]
|
||||
|
||||
assert dt.format_value([1,2,3]) == u'[1, 2, 3] Z'
|
||||
assert dt.format_value([1,2,3], u'') == u'[1, 2, 3]'
|
||||
assert dt.format_value([1,2,3], u'Q') == u'[1, 2, 3] Q'
|
||||
assert dt.format_value([1,2,3]) == '[1, 2, 3] Z'
|
||||
assert dt.format_value([1,2,3], '') == '[1, 2, 3]'
|
||||
assert dt.format_value([1,2,3], 'Q') == '[1, 2, 3] Q'
|
||||
|
||||
|
||||
def test_TupleOf():
|
||||
@ -373,7 +372,7 @@ def test_StructOf():
|
||||
StructOf(IntRange=1)
|
||||
|
||||
dt = StructOf(a_string=StringType(0, 55), an_int=IntRange(0, 999),
|
||||
optional=[u'an_int'])
|
||||
optional=['an_int'])
|
||||
copytest(dt)
|
||||
assert dt.export_datatype() == {'type': 'struct',
|
||||
'members':{'a_string': {'type': 'string', 'maxchars':55},
|
||||
@ -383,18 +382,18 @@ def test_StructOf():
|
||||
with pytest.raises(ValueError):
|
||||
dt(9)
|
||||
with pytest.raises(ValueError):
|
||||
dt([99, u'X'])
|
||||
dt([99, 'X'])
|
||||
with pytest.raises(ValueError):
|
||||
dt(dict(a_string=u'XXX', an_int=1811))
|
||||
dt(dict(a_string='XXX', an_int=1811))
|
||||
|
||||
assert dt(dict(a_string=u'XXX', an_int=8)) == {u'a_string': u'XXX',
|
||||
u'an_int': 8}
|
||||
assert dt.export_value({u'an_int': 13, u'a_string': u'WFEC'}) == {
|
||||
u'a_string': u'WFEC', u'an_int': 13}
|
||||
assert dt.import_value({u'an_int': 13, u'a_string': u'WFEC'}) == {
|
||||
u'a_string': u'WFEC', u'an_int': 13}
|
||||
assert dt(dict(a_string='XXX', an_int=8)) == {'a_string': 'XXX',
|
||||
'an_int': 8}
|
||||
assert dt.export_value({'an_int': 13, 'a_string': 'WFEC'}) == {
|
||||
'a_string': 'WFEC', 'an_int': 13}
|
||||
assert dt.import_value({'an_int': 13, 'a_string': 'WFEC'}) == {
|
||||
'a_string': 'WFEC', 'an_int': 13}
|
||||
|
||||
assert dt.format_value({'an_int':2, u'a_string':'Z'}) == u"{a_string='Z', an_int=2}"
|
||||
assert dt.format_value({'an_int':2, 'a_string':'Z'}) == u"{a_string='Z', an_int=2}"
|
||||
|
||||
|
||||
def test_Command():
|
||||
@ -418,11 +417,11 @@ def test_get_datatype():
|
||||
with pytest.raises(ValueError):
|
||||
get_datatype(str)
|
||||
with pytest.raises(ValueError):
|
||||
get_datatype({u'undefined': {}})
|
||||
get_datatype({'undefined': {}})
|
||||
|
||||
assert isinstance(get_datatype({'type': 'bool'}), BoolType)
|
||||
with pytest.raises(ValueError):
|
||||
get_datatype([u'bool'])
|
||||
get_datatype(['bool'])
|
||||
|
||||
with pytest.raises(ValueError):
|
||||
get_datatype({'type': 'int', 'min':-10}) # missing max
|
||||
@ -444,7 +443,7 @@ def test_get_datatype():
|
||||
FloatRange)
|
||||
|
||||
with pytest.raises(ValueError):
|
||||
get_datatype([u'double'])
|
||||
get_datatype(['double'])
|
||||
with pytest.raises(ValueError):
|
||||
get_datatype({'type': 'double', 'min':10, 'max':-10})
|
||||
with pytest.raises(ValueError):
|
||||
@ -462,7 +461,7 @@ def test_get_datatype():
|
||||
assert get_datatype(dt.export_datatype()).export_datatype() == dt.export_datatype()
|
||||
|
||||
with pytest.raises(ValueError):
|
||||
get_datatype([u'scaled']) # dict missing
|
||||
get_datatype(['scaled']) # dict missing
|
||||
with pytest.raises(ValueError):
|
||||
get_datatype({'type': 'scaled', 'min':-10, 'max':10}) # no scale
|
||||
with pytest.raises(ValueError):
|
||||
@ -471,7 +470,7 @@ def test_get_datatype():
|
||||
get_datatype(['scaled', {'min':10, 'max':-10, 'scale': 1}, 2])
|
||||
|
||||
with pytest.raises(ValueError):
|
||||
get_datatype([u'enum'])
|
||||
get_datatype(['enum'])
|
||||
with pytest.raises(ValueError):
|
||||
get_datatype({'type': 'enum', 'a': -2})
|
||||
assert isinstance(get_datatype({'type': 'enum', 'members':dict(a=-2)}), EnumType)
|
||||
@ -498,13 +497,13 @@ def test_get_datatype():
|
||||
get_datatype(['string', {'maxchars':-0}, 'x'])
|
||||
|
||||
with pytest.raises(ValueError):
|
||||
get_datatype([u'array'])
|
||||
get_datatype(['array'])
|
||||
with pytest.raises(ValueError):
|
||||
get_datatype({'type': 'array', 'members': [1]})
|
||||
assert isinstance(get_datatype({'type': 'array', 'minlen':1, 'maxlen':1,
|
||||
'members':{'type': 'blob', 'maxbytes':1}}
|
||||
), ArrayOf)
|
||||
assert isinstance(get_datatype({'type': 'array', 'minlen':1, u'maxlen':1,
|
||||
assert isinstance(get_datatype({'type': 'array', 'minlen':1, 'maxlen':1,
|
||||
'members':{'type': 'blob', 'maxbytes':1}}
|
||||
).members, BLOBType)
|
||||
|
||||
@ -528,7 +527,7 @@ def test_get_datatype():
|
||||
with pytest.raises(ValueError):
|
||||
get_datatype({'type': 'tuple', 'members': {}})
|
||||
with pytest.raises(ValueError):
|
||||
get_datatype([u'tuple', 10, -10])
|
||||
get_datatype(['tuple', 10, -10])
|
||||
|
||||
assert isinstance(get_datatype({'type': 'tuple', 'members':[{'type': 'blob', 'maxbytes':1},
|
||||
{'type': 'bool'}]}), TupleOf)
|
||||
@ -538,9 +537,9 @@ def test_get_datatype():
|
||||
with pytest.raises(ValueError):
|
||||
get_datatype(['struct', [1], 2, 3])
|
||||
assert isinstance(get_datatype({'type': 'struct', 'members':
|
||||
{u'name': {'type': 'blob', 'maxbytes':1}}}), StructOf)
|
||||
{'name': {'type': 'blob', 'maxbytes':1}}}), StructOf)
|
||||
assert isinstance(get_datatype({'type': 'struct', 'members':
|
||||
{u'name': {'type': 'blob', 'maxbytes':1}}}).members[u'name'], BLOBType)
|
||||
{'name': {'type': 'blob', 'maxbytes':1}}}).members['name'], BLOBType)
|
||||
|
||||
with pytest.raises(ValueError):
|
||||
get_datatype({'type': 'struct', 'members': {}})
|
||||
|
@ -21,7 +21,6 @@
|
||||
# *****************************************************************************
|
||||
"""test Enum type."""
|
||||
|
||||
from __future__ import division, print_function
|
||||
|
||||
# no fixtures needed
|
||||
import pytest
|
||||
|
@ -20,7 +20,6 @@
|
||||
#
|
||||
# *****************************************************************************
|
||||
"""test data types."""
|
||||
from __future__ import division, print_function
|
||||
|
||||
# no fixtures needed
|
||||
#import pytest
|
||||
|
@ -21,7 +21,6 @@
|
||||
# *****************************************************************************
|
||||
"""test data types."""
|
||||
|
||||
from __future__ import division, print_function
|
||||
|
||||
# no fixtures needed
|
||||
import pytest
|
||||
@ -31,24 +30,24 @@ from secop.params import Command, Override, Parameter, Parameters
|
||||
|
||||
|
||||
def test_Command():
|
||||
cmd = Command(u'do_something')
|
||||
assert cmd.description == u'do_something'
|
||||
cmd = Command('do_something')
|
||||
assert cmd.description == 'do_something'
|
||||
assert cmd.ctr
|
||||
assert cmd.argument is None
|
||||
assert cmd.result is None
|
||||
assert cmd.for_export() == {u'datainfo': {'type': 'command'},
|
||||
u'description': u'do_something'}
|
||||
assert cmd.for_export() == {'datainfo': {'type': 'command'},
|
||||
'description': 'do_something'}
|
||||
|
||||
cmd = Command(u'do_something', argument=IntRange(-9,9), result=IntRange(-1,1))
|
||||
cmd = Command('do_something', argument=IntRange(-9,9), result=IntRange(-1,1))
|
||||
assert cmd.description
|
||||
assert isinstance(cmd.argument, IntRange)
|
||||
assert isinstance(cmd.result, IntRange)
|
||||
assert cmd.for_export() == {u'datainfo': {'type': 'command', 'argument': {'type': 'int', 'min':-9, 'max':9},
|
||||
u'result': {'type': 'int', 'min':-1, 'max':1}},
|
||||
u'description': u'do_something'}
|
||||
assert cmd.exportProperties() == {u'datainfo': {'type': 'command', 'argument': {'type': 'int', 'max': 9, 'min': -9},
|
||||
assert cmd.for_export() == {'datainfo': {'type': 'command', 'argument': {'type': 'int', 'min':-9, 'max':9},
|
||||
'result': {'type': 'int', 'min':-1, 'max':1}},
|
||||
'description': 'do_something'}
|
||||
assert cmd.exportProperties() == {'datainfo': {'type': 'command', 'argument': {'type': 'int', 'max': 9, 'min': -9},
|
||||
'result': {'type': 'int', 'max': 1, 'min': -1}},
|
||||
u'description': u'do_something'}
|
||||
'description': 'do_something'}
|
||||
|
||||
|
||||
def test_Parameter():
|
||||
|
@ -21,7 +21,6 @@
|
||||
# *****************************************************************************
|
||||
"""test data types."""
|
||||
|
||||
from __future__ import division, print_function
|
||||
|
||||
from collections import OrderedDict
|
||||
|
||||
|
@ -20,7 +20,6 @@
|
||||
#
|
||||
# *****************************************************************************
|
||||
"""test data types."""
|
||||
from __future__ import division, print_function
|
||||
|
||||
import pytest
|
||||
|
||||
@ -31,13 +30,13 @@ from secop.properties import Property, Properties, HasProperties
|
||||
|
||||
V_test_Property = [
|
||||
[(StringType(), 'default', 'extname', False, False),
|
||||
dict(default=u'default', extname=u'extname', export=True, mandatory=False)],
|
||||
dict(default='default', extname='extname', export=True, mandatory=False)],
|
||||
[(IntRange(), '42', '_extname', False, True),
|
||||
dict(default=42, extname=u'_extname', export=True, mandatory=True)],
|
||||
dict(default=42, extname='_extname', export=True, mandatory=True)],
|
||||
[(IntRange(), '42', '_extname', True, False),
|
||||
dict(default=42, extname=u'_extname', export=True, mandatory=False)],
|
||||
dict(default=42, extname='_extname', export=True, mandatory=False)],
|
||||
[(IntRange(), 42, '_extname', True, True),
|
||||
dict(default=42, extname=u'_extname', export=True, mandatory=True)],
|
||||
dict(default=42, extname='_extname', export=True, mandatory=True)],
|
||||
[(IntRange(), 0, '', True, True),
|
||||
dict(default=0, extname='', export=True, mandatory=True)],
|
||||
[(IntRange(), 0, '', True, False),
|
||||
@ -75,7 +74,7 @@ def test_Properties():
|
||||
p['a'] = Property('', IntRange(), '42', export=True)
|
||||
assert p['a'].default == 42
|
||||
assert p['a'].export is True
|
||||
assert p['a'].extname == u'_a'
|
||||
assert p['a'].extname == '_a'
|
||||
with pytest.raises(ProgrammingError):
|
||||
p['a'] = 137
|
||||
with pytest.raises(ProgrammingError):
|
||||
|
@ -1,4 +1,3 @@
|
||||
from __future__ import division, print_function
|
||||
|
||||
|
||||
def test_assert():
|
||||
|
Loading…
x
Reference in New Issue
Block a user