replace validators with datatypes
Change-Id: I446c4e14e24afa3f65e79c8b6e07eec3271532b0
This commit is contained in:
parent
a87e568b55
commit
bc3253a01a
@ -30,8 +30,8 @@ import Queue
|
||||
|
||||
import mlzlog
|
||||
|
||||
from secop.validators import validator_from_str
|
||||
from secop.lib import mkthread
|
||||
from secop.datatypes import get_datatype
|
||||
from secop.lib import mkthread, formatException
|
||||
from secop.lib.parsing import parse_time, format_time
|
||||
from secop.protocol.encoding import ENCODERS
|
||||
from secop.protocol.framing import FRAMERS
|
||||
@ -271,6 +271,7 @@ class Client(object):
|
||||
except ValueError:
|
||||
# keep as string
|
||||
data = json_data
|
||||
# print formatException()
|
||||
return msgtype, spec, data
|
||||
|
||||
def _handle_event(self, spec, data):
|
||||
@ -310,9 +311,9 @@ class Client(object):
|
||||
|
||||
for module, moduleData in self.describing_data['modules'].items():
|
||||
for parameter, parameterData in moduleData['parameters'].items():
|
||||
validator = validator_from_str(parameterData['validator'])
|
||||
datatype = get_datatype(parameterData['datatype'])
|
||||
self.describing_data['modules'][module]['parameters'] \
|
||||
[parameter]['validator'] = validator
|
||||
[parameter]['datatype'] = datatype
|
||||
|
||||
def register_callback(self, module, parameter, cb):
|
||||
self.log.debug('registering callback %r for %s:%s' %
|
||||
@ -440,10 +441,10 @@ class Client(object):
|
||||
return self.communicate('read', '%s:%s' % (module, parameter))
|
||||
|
||||
def setParameter(self, module, parameter, value):
|
||||
validator = self._getDescribingParameterData(module,
|
||||
parameter)['validator']
|
||||
datatype = self._getDescribingParameterData(module,
|
||||
parameter)['datatype']
|
||||
|
||||
value = validator(value)
|
||||
value = datatype.export(datatype.validate(value))
|
||||
self.communicate('change', '%s:%s' % (module, parameter), value)
|
||||
|
||||
@property
|
||||
|
@ -21,14 +21,6 @@
|
||||
# *****************************************************************************
|
||||
"""Define validated data types."""
|
||||
|
||||
# a Validator returns a validated object or raises an ValueError
|
||||
# also validators should have a __repr__ returning a 'python' string
|
||||
# which recreates them
|
||||
|
||||
# if a validator does a mapping, it normally maps to the
|
||||
# internal representation with method :meth:`validate`
|
||||
# to get the external representation (aöso for logging),
|
||||
# call method :meth:`export`
|
||||
|
||||
|
||||
from .errors import ProgrammingError
|
||||
@ -56,10 +48,10 @@ class FloatRange(DataType):
|
||||
"""Restricted float type"""
|
||||
|
||||
def __init__(self, min=None, max=None):
|
||||
self.min = float('-Inf') if min is None else float(min)
|
||||
self.max = float('+Inf') if max is None else float(max)
|
||||
self.min = None if min is None else float(min)
|
||||
self.max = None if max is None else float(max)
|
||||
# note: as we may compare to Inf all comparisons would be false
|
||||
if self.min <= self.max:
|
||||
if (self.min or float('-inf')) <= (self.max or float('+inf')):
|
||||
self.as_json = ['double', min, max]
|
||||
else:
|
||||
raise ValueError('Max must be larger then min!')
|
||||
@ -67,15 +59,25 @@ class FloatRange(DataType):
|
||||
def validate(self, value):
|
||||
try:
|
||||
value = float(value)
|
||||
if self.min <= value <= self.max:
|
||||
return value
|
||||
raise ValueError('%r should be an float between %.3f and %.3f' %
|
||||
(value, self.min, self.max))
|
||||
except:
|
||||
raise ValueError('Can not validate %r to float' % value)
|
||||
if self.min is not None and value < self.min:
|
||||
raise ValueError('%r should not be less then %s' % (value, self.min))
|
||||
if self.max is not None and value > self.max:
|
||||
raise ValueError('%r should not be greater than %s' % (value, self.max))
|
||||
if None in (self.min, self.max):
|
||||
return value
|
||||
if self.min <= value <= self.max:
|
||||
return value
|
||||
raise ValueError('%r should be an float between %.3f and %.3f' %
|
||||
(value, self.min, self.max))
|
||||
|
||||
def __repr__(self):
|
||||
return "FloatRange(%f, %f)" % (self.min, self.max)
|
||||
if self.max != None:
|
||||
return "FloatRange(%r, %r)" % (float('-inf') if self.min is None else self.min, self.max)
|
||||
if self.min != None:
|
||||
return "FloatRange(%r)" % self.min
|
||||
return "FloatRange()"
|
||||
|
||||
def export(self, value):
|
||||
"""returns a python object fit for serialisation"""
|
||||
@ -105,7 +107,11 @@ class IntRange(DataType):
|
||||
raise ValueError('Can not validate %r to int' % value)
|
||||
|
||||
def __repr__(self):
|
||||
return "IntRange(%d, %d)" % (self.min, self.max)
|
||||
if self.max is not None:
|
||||
return "IntRange(%d, %d)" % (self.min, self.max)
|
||||
if self.min is not None:
|
||||
return "IntRange(%d)" % self.min
|
||||
return "IntRange(%d)" % self.min
|
||||
|
||||
def export(self, value):
|
||||
"""returns a python object fit for serialisation"""
|
||||
@ -119,7 +125,8 @@ class EnumType(DataType):
|
||||
self.entries = {}
|
||||
num = 0
|
||||
for arg in args:
|
||||
if type(args) != str:
|
||||
if type(arg) != str:
|
||||
print arg, type(arg)
|
||||
raise ValueError('EnumType entries MUST be strings!')
|
||||
self.entries[num] = arg
|
||||
num += 1
|
||||
@ -138,7 +145,7 @@ class EnumType(DataType):
|
||||
self.as_json = ['enum', self.reversed.copy()]
|
||||
|
||||
def __repr__(self):
|
||||
return "EnumType(%s)" % ', '.join(['%r=%d' % (v,k) for k,v in self.entries.items()])
|
||||
return "EnumType(%s)" % ', '.join(['%s=%d' % (v,k) for k,v in self.entries.items()])
|
||||
|
||||
def export(self, value):
|
||||
"""returns a python object fit for serialisation"""
|
||||
@ -169,8 +176,10 @@ class BLOBType(DataType):
|
||||
raise ValueError('maxsize must be bigger than minsize!')
|
||||
|
||||
def __repr__(self):
|
||||
if self.minsize or self.maxsize:
|
||||
return 'BLOB(%d, %s)' % (self.minsize, self.maxsize)
|
||||
if self.maxsize:
|
||||
return 'BLOB(%s, %s)' % (str(self.minsize), str(self.maxsize))
|
||||
if self.minsize:
|
||||
return 'BLOB(%d)' % self.minsize
|
||||
return 'BLOB()'
|
||||
|
||||
def validate(self, value):
|
||||
@ -203,7 +212,11 @@ class StringType(DataType):
|
||||
raise ValueError('maxsize must be bigger than minsize!')
|
||||
|
||||
def __repr__(self):
|
||||
return 'StringType(%d, %s)' % (self.minsize, self.maxsize)
|
||||
if self.maxsize:
|
||||
return 'StringType(%s, %s)' % (str(self.minsize), str(self.maxsize))
|
||||
if self.minsize:
|
||||
return 'StringType(%d)' % str(self.minsize)
|
||||
return 'StringType()'
|
||||
|
||||
def validate(self, value):
|
||||
"""return the validated (internal) value or raise"""
|
||||
@ -223,6 +236,7 @@ class StringType(DataType):
|
||||
"""returns a python object fit for serialisation"""
|
||||
return '%s' % value
|
||||
|
||||
|
||||
# Bool is a special enum
|
||||
class BoolType(DataType):
|
||||
as_json = ['bool']
|
||||
@ -265,6 +279,9 @@ class ArrayOf(DataType):
|
||||
if self.minsize is not None and self.maxsize is not None and self.minsize > self.maxsize:
|
||||
raise ValueError('Maximum size must be >= Minimum size')
|
||||
|
||||
def __repr__(self):
|
||||
return 'ArrayOf(%s, %s, %s)' % (repr(self.subtype), self.minsize, self.maxsize)
|
||||
|
||||
def validate(self, value):
|
||||
"""validate a external representation to an internal one"""
|
||||
if isinstance(value, (tuple, list)):
|
||||
@ -292,6 +309,9 @@ class TupleOf(DataType):
|
||||
self.subtypes = subtypes
|
||||
self.as_json = ['tuple', [subtype.as_json for subtype in subtypes]]
|
||||
|
||||
def __repr__(self):
|
||||
return 'TupleOf(%s)' % ', '.join([repr(st) for st in self.subtypes])
|
||||
|
||||
def validate(self, value):
|
||||
"""return the validated value or raise"""
|
||||
# keep the ordering!
|
||||
@ -320,6 +340,9 @@ class StructOf(DataType):
|
||||
self.named_subtypes = named_subtypes
|
||||
self.as_json = ['struct', dict((n,s.as_json) for n,s in named_subtypes.items())]
|
||||
|
||||
def __repr__(self):
|
||||
return 'StructOf(%s)' % ', '.join(['%s=%s'%(n,repr(st)) for n,st in self.named_subtypes.iteritems()])
|
||||
|
||||
def validate(self, value):
|
||||
"""return the validated value or raise"""
|
||||
try:
|
||||
@ -358,10 +381,14 @@ DATATYPES = dict(
|
||||
|
||||
# probably not needed...
|
||||
def export_datatype(datatype):
|
||||
if datatype is None:
|
||||
return datatype
|
||||
return datatype.as_json
|
||||
|
||||
# important for getting the right datatype from formerly jsonified descr.
|
||||
def get_datatype(json):
|
||||
if json is None:
|
||||
return json
|
||||
if not isinstance(json, list):
|
||||
raise ValueError('Argument must be a properly formatted list!')
|
||||
if len(json)<1:
|
||||
|
@ -34,7 +34,8 @@ import threading
|
||||
from secop.lib.parsing import format_time
|
||||
from secop.errors import ConfigError, ProgrammingError
|
||||
from secop.protocol import status
|
||||
from secop.validators import enum, vector, floatrange, validator_to_str
|
||||
from secop.datatypes import DataType, EnumType, TupleOf, StringType, FloatRange, export_datatype
|
||||
|
||||
|
||||
EVENT_ONLY_ON_CHANGED_VALUES = False
|
||||
|
||||
@ -49,7 +50,7 @@ class PARAM(object):
|
||||
|
||||
def __init__(self,
|
||||
description,
|
||||
validator=float,
|
||||
datatype=None,
|
||||
default=Ellipsis,
|
||||
unit=None,
|
||||
readonly=True,
|
||||
@ -59,8 +60,14 @@ class PARAM(object):
|
||||
# make a copy of a PARAM object
|
||||
self.__dict__.update(description.__dict__)
|
||||
return
|
||||
if not isinstance(datatype, DataType):
|
||||
if issubclass(datatype, DataType):
|
||||
# goodie: make an instance from a class (forgotten ()???)
|
||||
datatype = datatype()
|
||||
else:
|
||||
raise ValueError('Datatype MUST be from datatypes!')
|
||||
self.description = description
|
||||
self.validator = validator
|
||||
self.datatype = datatype
|
||||
self.default = default
|
||||
self.unit = unit
|
||||
self.readonly = readonly
|
||||
@ -79,7 +86,7 @@ class PARAM(object):
|
||||
res = dict(
|
||||
description=self.description,
|
||||
readonly=self.readonly,
|
||||
validator=validator_to_str(self.validator),
|
||||
datatype=export_datatype(self.datatype),
|
||||
)
|
||||
if self.unit:
|
||||
res['unit'] = self.unit
|
||||
@ -98,9 +105,9 @@ class CMD(object):
|
||||
def __init__(self, description, arguments, result):
|
||||
# descriptive text for humans
|
||||
self.description = description
|
||||
# list of validators for arguments
|
||||
# list of datatypes for arguments
|
||||
self.arguments = arguments
|
||||
# validator for result
|
||||
# datatype for result
|
||||
self.resulttype = result
|
||||
|
||||
def __repr__(self):
|
||||
@ -111,8 +118,8 @@ class CMD(object):
|
||||
# used for serialisation only
|
||||
return dict(
|
||||
description=self.description,
|
||||
arguments=repr(self.arguments),
|
||||
resulttype=repr(self.resulttype), )
|
||||
arguments=map(export_datatype, self.arguments),
|
||||
resulttype=export_datatype(self.resulttype), )
|
||||
|
||||
# Meta class
|
||||
# warning: MAGIC!
|
||||
@ -163,7 +170,7 @@ class DeviceMeta(type):
|
||||
def wrapped_wfunc(self, value, pname=pname, wfunc=wfunc):
|
||||
self.log.debug("wfunc: set %s to %r" % (pname, value))
|
||||
pobj = self.PARAMS[pname]
|
||||
value = pobj.validator(value) if pobj.validator else value
|
||||
value = pobj.datatype.validate(value) if pobj.datatype else value
|
||||
if wfunc:
|
||||
value = wfunc(self, value) or value
|
||||
# XXX: use setattr or direct manipulation
|
||||
@ -180,7 +187,7 @@ class DeviceMeta(type):
|
||||
|
||||
def setter(self, value, pname=pname):
|
||||
pobj = self.PARAMS[pname]
|
||||
value = pobj.validator(value) if pobj.validator else value
|
||||
value = pobj.datatype.validate(value) if pobj.datatype else value
|
||||
pobj.timestamp = time.time()
|
||||
if not EVENT_ONLY_ON_CHANGED_VALUES or (value != pobj.value):
|
||||
pobj.value = value
|
||||
@ -309,14 +316,14 @@ class Device(object):
|
||||
self.PARAMS[k] = param_type(self.PARAMS[k])
|
||||
|
||||
# now 'apply' config:
|
||||
# pass values through the validators and store as attributes
|
||||
# pass values through the datatypes and store as attributes
|
||||
for k, v in cfgdict.items():
|
||||
# apply validator, complain if type does not fit
|
||||
validator = self.PARAMS[k].validator
|
||||
if validator is not None:
|
||||
# only check if validator given
|
||||
# apply datatype, complain if type does not fit
|
||||
datatype = self.PARAMS[k].datatype
|
||||
if datatype is not None:
|
||||
# only check if datatype given
|
||||
try:
|
||||
v = validator(v)
|
||||
v = datatype.validate(v)
|
||||
except (ValueError, TypeError) as e:
|
||||
raise ConfigError('Device %s: config parameter %r:\n%r' %
|
||||
(self.name, k, e))
|
||||
@ -338,19 +345,20 @@ class Readable(Device):
|
||||
providing the readonly parameter 'value' and 'status'
|
||||
"""
|
||||
PARAMS = {
|
||||
'value': PARAM('current value of the device', readonly=True, default=0.),
|
||||
'value': PARAM('current value of the device', readonly=True, default=0.,
|
||||
datatype=FloatRange()),
|
||||
'pollinterval': PARAM('sleeptime between polls', default=5,
|
||||
readonly=False, validator=floatrange(0.1, 120), ),
|
||||
readonly=False, datatype=FloatRange(0.1, 120), ),
|
||||
'status': PARAM('current status of the device', default=(status.OK, ''),
|
||||
validator=vector(
|
||||
enum(**{
|
||||
datatype=TupleOf(
|
||||
EnumType(**{
|
||||
'IDLE': status.OK,
|
||||
'BUSY': status.BUSY,
|
||||
'WARN': status.WARN,
|
||||
'UNSTABLE': status.UNSTABLE,
|
||||
'ERROR': status.ERROR,
|
||||
'UNKNOWN': status.UNKNOWN
|
||||
}), str),
|
||||
}), StringType() ),
|
||||
readonly=True),
|
||||
}
|
||||
|
||||
@ -378,6 +386,7 @@ class Driveable(Readable):
|
||||
"""
|
||||
PARAMS = {
|
||||
'target': PARAM('target value of the device', default=0., readonly=False,
|
||||
datatype=FloatRange(),
|
||||
),
|
||||
}
|
||||
# XXX: CMDS ???? auto deriving working well enough?
|
||||
|
@ -27,7 +27,7 @@ import threading
|
||||
|
||||
from secop.devices.core import Driveable, CMD, PARAM
|
||||
from secop.protocol import status
|
||||
from secop.validators import floatrange, positive, enum, nonnegative, vector
|
||||
from secop.datatypes import FloatRange, EnumType, TupleOf
|
||||
from secop.lib import clamp, mkthread
|
||||
|
||||
|
||||
@ -44,81 +44,82 @@ class Cryostat(CryoBase):
|
||||
"""
|
||||
PARAMS = dict(
|
||||
jitter=PARAM("amount of random noise on readout values",
|
||||
validator=floatrange(0, 1), unit="K",
|
||||
datatype=FloatRange(0, 1), unit="K",
|
||||
default=0.1, readonly=False, export=False,
|
||||
),
|
||||
T_start=PARAM("starting temperature for simulation",
|
||||
validator=positive, default=10,
|
||||
datatype=FloatRange(0), default=10,
|
||||
export=False,
|
||||
),
|
||||
looptime=PARAM("timestep for simulation",
|
||||
validator=floatrange(0.01, 10), unit="s", default=1,
|
||||
datatype=FloatRange(0.01, 10), unit="s", default=1,
|
||||
readonly=False, export=False,
|
||||
),
|
||||
ramp=PARAM("ramping speed of the setpoint",
|
||||
validator=floatrange(0, 1e3), unit="K/min", default=1,
|
||||
datatype=FloatRange(0, 1e3), unit="K/min", default=1,
|
||||
readonly=False,
|
||||
),
|
||||
setpoint=PARAM("current setpoint during ramping else target",
|
||||
validator=float, default=1, unit='K',
|
||||
datatype=FloatRange(), default=1, unit='K',
|
||||
),
|
||||
maxpower=PARAM("Maximum heater power",
|
||||
validator=nonnegative, default=1, unit="W",
|
||||
datatype=FloatRange(0), default=1, unit="W",
|
||||
readonly=False,
|
||||
group='heater_settings',
|
||||
),
|
||||
heater=PARAM("current heater setting",
|
||||
validator=floatrange(0, 100), default=0, unit="%",
|
||||
datatype=FloatRange(0, 100), default=0, unit="%",
|
||||
group='heater_settings',
|
||||
),
|
||||
heaterpower=PARAM("current heater power",
|
||||
validator=nonnegative, default=0, unit="W",
|
||||
datatype=FloatRange(0), default=0, unit="W",
|
||||
group='heater_settings',
|
||||
),
|
||||
target=PARAM("target temperature",
|
||||
validator=nonnegative, default=0, unit="K",
|
||||
datatype=FloatRange(0), default=0, unit="K",
|
||||
readonly=False,
|
||||
),
|
||||
value=PARAM("regulation temperature",
|
||||
validator=nonnegative, default=0, unit="K",
|
||||
datatype=FloatRange(0), default=0, unit="K",
|
||||
),
|
||||
pid=PARAM("regulation coefficients",
|
||||
validator=vector(nonnegative, floatrange(
|
||||
0, 100), floatrange(0, 100)),
|
||||
datatype=TupleOf(FloatRange(0), FloatRange(0, 100),
|
||||
FloatRange(0, 100)),
|
||||
default=(40, 10, 2), readonly=False,
|
||||
group='pid',
|
||||
),
|
||||
p=PARAM("regulation coefficient 'p'",
|
||||
validator=nonnegative, default=40, unit="%/K", readonly=False,
|
||||
datatype=FloatRange(0), default=40, unit="%/K", readonly=False,
|
||||
group='pid',
|
||||
),
|
||||
i=PARAM("regulation coefficient 'i'",
|
||||
validator=floatrange(0, 100), default=10, readonly=False,
|
||||
datatype=FloatRange(0, 100), default=10, readonly=False,
|
||||
group='pid',
|
||||
),
|
||||
d=PARAM("regulation coefficient 'd'",
|
||||
validator=floatrange(0, 100), default=2, readonly=False,
|
||||
datatype=FloatRange(0, 100), default=2, readonly=False,
|
||||
group='pid',
|
||||
),
|
||||
mode=PARAM("mode of regulation",
|
||||
validator=enum('ramp', 'pid', 'openloop'), default='ramp',
|
||||
datatype=EnumType('ramp', 'pid', 'openloop'),
|
||||
default='ramp',
|
||||
readonly=False,
|
||||
),
|
||||
pollinterval=PARAM("polling interval",
|
||||
validator=positive, default=5,
|
||||
datatype=FloatRange(0), default=5,
|
||||
),
|
||||
tolerance=PARAM("temperature range for stability checking",
|
||||
validator=floatrange(0, 100), default=0.1, unit='K',
|
||||
datatype=FloatRange(0, 100), default=0.1, unit='K',
|
||||
readonly=False,
|
||||
group='stability',
|
||||
),
|
||||
window=PARAM("time window for stability checking",
|
||||
validator=floatrange(1, 900), default=30, unit='s',
|
||||
datatype=FloatRange(1, 900), default=30, unit='s',
|
||||
readonly=False,
|
||||
group='stability',
|
||||
),
|
||||
timeout=PARAM("max waiting time for stabilisation check",
|
||||
validator=floatrange(1, 36000), default=900, unit='s',
|
||||
datatype=FloatRange(1, 36000), default=900, unit='s',
|
||||
readonly=False,
|
||||
group='stability',
|
||||
),
|
||||
|
@ -25,7 +25,7 @@ import random
|
||||
import threading
|
||||
|
||||
from secop.devices.core import Readable, Driveable, PARAM
|
||||
from secop.validators import *
|
||||
from secop.datatypes import EnumType, FloatRange, IntRange, ArrayOf, StringType, TupleOf, StructOf, BoolType
|
||||
from secop.protocol import status
|
||||
|
||||
|
||||
@ -34,18 +34,18 @@ class Switch(Driveable):
|
||||
"""
|
||||
PARAMS = {
|
||||
'value': PARAM('current state (on or off)',
|
||||
validator=enum(on=1, off=0), default=0,
|
||||
datatype=EnumType(on=1, off=0), default=0,
|
||||
),
|
||||
'target': PARAM('wanted state (on or off)',
|
||||
validator=enum(on=1, off=0), default=0,
|
||||
datatype=EnumType(on=1, off=0), default=0,
|
||||
readonly=False,
|
||||
),
|
||||
'switch_on_time': PARAM('seconds to wait after activating the switch',
|
||||
validator=floatrange(0, 60), unit='s',
|
||||
datatype=FloatRange(0, 60), unit='s',
|
||||
default=10, export=False,
|
||||
),
|
||||
'switch_off_time': PARAM('cool-down time in seconds',
|
||||
validator=floatrange(0, 60), unit='s',
|
||||
datatype=FloatRange(0, 60), unit='s',
|
||||
default=10, export=False,
|
||||
),
|
||||
}
|
||||
@ -99,22 +99,22 @@ class MagneticField(Driveable):
|
||||
"""
|
||||
PARAMS = {
|
||||
'value': PARAM('current field in T',
|
||||
unit='T', validator=floatrange(-15, 15), default=0,
|
||||
unit='T', datatype=FloatRange(-15, 15), default=0,
|
||||
),
|
||||
'target': PARAM('target field in T',
|
||||
unit='T', validator=floatrange(-15, 15), default=0,
|
||||
unit='T', datatype=FloatRange(-15, 15), default=0,
|
||||
readonly=False,
|
||||
),
|
||||
'ramp': PARAM('ramping speed',
|
||||
unit='T/min', validator=floatrange(0, 1), default=0.1,
|
||||
unit='T/min', datatype=FloatRange(0, 1), default=0.1,
|
||||
readonly=False,
|
||||
),
|
||||
'mode': PARAM('what to do after changing field',
|
||||
default=1, validator=enum(persistent=1, hold=0),
|
||||
default=1, datatype=EnumType(persistent=1, hold=0),
|
||||
readonly=False,
|
||||
),
|
||||
'heatswitch': PARAM('name of heat switch device',
|
||||
validator=str, export=False,
|
||||
datatype=StringType(), export=False,
|
||||
),
|
||||
}
|
||||
|
||||
@ -183,10 +183,10 @@ class CoilTemp(Readable):
|
||||
"""
|
||||
PARAMS = {
|
||||
'value': PARAM('Coil temperatur',
|
||||
unit='K', validator=float, default=0,
|
||||
unit='K', datatype=FloatRange(), default=0,
|
||||
),
|
||||
'sensor': PARAM("Sensor number or calibration id",
|
||||
validator=str, readonly=True,
|
||||
datatype=StringType(), readonly=True,
|
||||
),
|
||||
}
|
||||
|
||||
@ -199,13 +199,13 @@ class SampleTemp(Driveable):
|
||||
"""
|
||||
PARAMS = {
|
||||
'value': PARAM('Sample temperature',
|
||||
unit='K', validator=float, default=10,
|
||||
unit='K', datatype=FloatRange(), default=10,
|
||||
),
|
||||
'sensor': PARAM("Sensor number or calibration id",
|
||||
validator=str, readonly=True,
|
||||
datatype=StringType(), readonly=True,
|
||||
),
|
||||
'ramp': PARAM('moving speed in K/min',
|
||||
validator=floatrange(0, 100), unit='K/min', default=0.1,
|
||||
datatype=FloatRange(0, 100), unit='K/min', default=0.1,
|
||||
readonly=False,
|
||||
),
|
||||
}
|
||||
@ -243,16 +243,16 @@ class Label(Readable):
|
||||
"""
|
||||
PARAMS = {
|
||||
'system': PARAM("Name of the magnet system",
|
||||
validator=str, export=False,
|
||||
datatype=StringType, export=False,
|
||||
),
|
||||
'subdev_mf': PARAM("name of subdevice for magnet status",
|
||||
validator=str, export=False,
|
||||
datatype=StringType, export=False,
|
||||
),
|
||||
'subdev_ts': PARAM("name of subdevice for sample temp",
|
||||
validator=str, export=False,
|
||||
datatype=StringType, export=False,
|
||||
),
|
||||
'value': PARAM("final value of label string",
|
||||
validator=str,
|
||||
datatype=StringType,
|
||||
),
|
||||
}
|
||||
|
||||
@ -283,25 +283,22 @@ class Label(Readable):
|
||||
return '; '.join(strings)
|
||||
|
||||
|
||||
class ValidatorTest(Readable):
|
||||
class DatatypesTest(Readable):
|
||||
"""
|
||||
"""
|
||||
PARAMS = {
|
||||
'oneof': PARAM('oneof',
|
||||
validator=oneof(int, 'X', 2.718), readonly=False, default=4.0),
|
||||
'enum': PARAM('enum',
|
||||
validator=enum('boo', 'faar', z=9), readonly=False, default=1),
|
||||
'vector': PARAM('vector of int, float and str',
|
||||
validator=vector(int, float, str), readonly=False, default=(1, 2.3, 'a')),
|
||||
'array': PARAM('array: 2..3 times oneof(0,1)',
|
||||
validator=array(oneof(2, 3), oneof(0, 1)), readonly=False, default=[1, 0, 1]),
|
||||
'nonnegative': PARAM('nonnegative',
|
||||
validator=nonnegative, readonly=False, default=0),
|
||||
'positive': PARAM('positive',
|
||||
validator=positive, readonly=False, default=1),
|
||||
datatype=EnumType('boo', 'faar', z=9), readonly=False, default=1),
|
||||
'tupleof': PARAM('tuple of int, float and str',
|
||||
datatype=TupleOf(IntRange(), FloatRange(), StringType()), readonly=False, default=(1, 2.3, 'a')),
|
||||
'arrayof': PARAM('array: 2..3 times bool',
|
||||
datatype=ArrayOf(BoolType(), 2, 3), readonly=False, default=[1, 0, 1]),
|
||||
'intrange': PARAM('intrange',
|
||||
validator=intrange(2, 9), readonly=False, default=4),
|
||||
datatype=IntRange(2, 9), readonly=False, default=4),
|
||||
'floatrange': PARAM('floatrange',
|
||||
validator=floatrange(-1, 1), readonly=False, default=0,
|
||||
datatype=FloatRange(-1, 1), readonly=False, default=0,
|
||||
),
|
||||
'struct': PARAM('struct(a=str, b=int, c=bool)',
|
||||
datatype=StructOf(a=StringType(), b=IntRange(), c=BoolType()),
|
||||
),
|
||||
}
|
||||
|
@ -23,7 +23,7 @@
|
||||
import random
|
||||
|
||||
from secop.lib.parsing import format_time
|
||||
from secop.validators import enum, vector, floatrange, validator_to_str
|
||||
from secop.datatypes import EnumType, TupleOf, FloatRange, get_datatype, StringType
|
||||
from secop.devices.core import Readable, Device, Driveable, PARAM
|
||||
from secop.protocol import status
|
||||
|
||||
@ -62,14 +62,17 @@ class EpicsReadable(Readable):
|
||||
"""EpicsDriveable handles a Driveable interfacing to EPICS v4"""
|
||||
# Commmon PARAMS for all EPICS devices
|
||||
PARAMS = {
|
||||
'value': PARAM('EPICS generic value', validator=float,
|
||||
'value': PARAM('EPICS generic value',
|
||||
datatype=FloatRange(),
|
||||
default=300.0,),
|
||||
'epics_version': PARAM("EPICS version used, v3 or v4",
|
||||
validator=str,),
|
||||
datatype=EnumType(v3=3, v4=4),),
|
||||
# 'private' parameters: not remotely accessible
|
||||
'value_pv': PARAM('EPICS pv_name of value', validator=str,
|
||||
'value_pv': PARAM('EPICS pv_name of value',
|
||||
datatype=StringType(),
|
||||
default="unset", export=False),
|
||||
'status_pv': PARAM('EPICS pv_name of status', validator=str,
|
||||
'status_pv': PARAM('EPICS pv_name of status',
|
||||
datatype=StringType(),
|
||||
default="unset", export=False),
|
||||
}
|
||||
|
||||
@ -119,18 +122,18 @@ class EpicsDriveable(Driveable):
|
||||
"""EpicsDriveable handles a Driveable interfacing to EPICS v4"""
|
||||
# Commmon PARAMS for all EPICS devices
|
||||
PARAMS = {
|
||||
'target': PARAM('EPICS generic target', validator=float,
|
||||
'target': PARAM('EPICS generic target', datatype=FloatRange(),
|
||||
default=300.0, readonly=False),
|
||||
'value': PARAM('EPICS generic value', validator=float,
|
||||
'value': PARAM('EPICS generic value', datatype=FloatRange(),
|
||||
default=300.0,),
|
||||
'epics_version': PARAM("EPICS version used, v3 or v4",
|
||||
validator=str,),
|
||||
datatype=StringType(),),
|
||||
# 'private' parameters: not remotely accessible
|
||||
'target_pv': PARAM('EPICS pv_name of target', validator=str,
|
||||
'target_pv': PARAM('EPICS pv_name of target', datatype=StringType(),
|
||||
default="unset", export=False),
|
||||
'value_pv': PARAM('EPICS pv_name of value', validator=str,
|
||||
'value_pv': PARAM('EPICS pv_name of value', datatype=StringType(),
|
||||
default="unset", export=False),
|
||||
'status_pv': PARAM('EPICS pv_name of status', validator=str,
|
||||
'status_pv': PARAM('EPICS pv_name of status', datatype=StringType(),
|
||||
default="unset", export=False),
|
||||
}
|
||||
|
||||
@ -191,15 +194,15 @@ class EpicsDriveable(Driveable):
|
||||
class EpicsTempCtrl(EpicsDriveable):
|
||||
|
||||
PARAMS = {
|
||||
# TODO: restrict possible values with oneof validator
|
||||
'heaterrange': PARAM('Heater range', validator=str,
|
||||
# TODO: restrict possible values with oneof datatype
|
||||
'heaterrange': PARAM('Heater range', datatype=StringType(),
|
||||
default='Off', readonly=False,),
|
||||
'tolerance': PARAM('allowed deviation between value and target',
|
||||
validator=floatrange(1e-6, 1e6), default=0.1,
|
||||
datatype=FloatRange(1e-6, 1e6), default=0.1,
|
||||
readonly=False,),
|
||||
# 'private' parameters: not remotely accessible
|
||||
'heaterrange_pv': PARAM('EPICS pv_name of heater range',
|
||||
validator=str, default="unset", export=False,),
|
||||
datatype=StringType(), default="unset", export=False,),
|
||||
}
|
||||
|
||||
def read_target(self, maxage=0):
|
||||
|
@ -23,8 +23,7 @@
|
||||
import random
|
||||
|
||||
from secop.devices.core import Readable, Driveable, PARAM
|
||||
from secop.validators import floatrange, positive
|
||||
|
||||
from secop.datatypes import FloatRange, StringType
|
||||
|
||||
class LN2(Readable):
|
||||
"""Just a readable.
|
||||
@ -45,7 +44,7 @@ class Heater(Driveable):
|
||||
"""
|
||||
PARAMS = {
|
||||
'maxheaterpower': PARAM('maximum allowed heater power',
|
||||
validator=floatrange(0, 100), unit='W',
|
||||
datatype=FloatRange(0, 100), unit='W',
|
||||
),
|
||||
}
|
||||
|
||||
@ -64,10 +63,10 @@ class Temp(Driveable):
|
||||
"""
|
||||
PARAMS = {
|
||||
'sensor': PARAM("Sensor number or calibration id",
|
||||
validator=str, readonly=True,
|
||||
datatype=StringType(8,16), readonly=True,
|
||||
),
|
||||
'target': PARAM("Target temperature",
|
||||
default=300.0, validator=positive, readonly=False, unit='K',
|
||||
default=300.0, datatype=FloatRange(0), readonly=False, unit='K',
|
||||
),
|
||||
}
|
||||
|
||||
|
@ -25,7 +25,7 @@ from PyQt4.QtGui import QWidget, QLabel, QSizePolicy
|
||||
from PyQt4.QtCore import pyqtSignature as qtsig, Qt, pyqtSignal
|
||||
|
||||
from secop.gui.util import loadUi
|
||||
from secop.validators import validator_to_str
|
||||
from secop.datatypes import get_datatype
|
||||
|
||||
|
||||
class ParameterView(QWidget):
|
||||
@ -59,10 +59,7 @@ class ParameterView(QWidget):
|
||||
label.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Preferred)
|
||||
|
||||
# make 'display' label
|
||||
if prop == 'validator':
|
||||
view = QLabel(validator_to_str(props[prop]))
|
||||
else:
|
||||
view = QLabel(str(props[prop]))
|
||||
view = QLabel(str(props[prop]))
|
||||
view.setFont(self.font())
|
||||
view.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Preferred)
|
||||
view.setWordWrap(True)
|
||||
|
@ -64,6 +64,73 @@ def mkthread(func, *args, **kwds):
|
||||
return t
|
||||
|
||||
|
||||
import sys
|
||||
import linecache
|
||||
import traceback
|
||||
|
||||
def formatExtendedFrame(frame):
|
||||
ret = []
|
||||
for key, value in frame.f_locals.iteritems():
|
||||
try:
|
||||
valstr = repr(value)[:256]
|
||||
except Exception:
|
||||
valstr = '<cannot be displayed>'
|
||||
ret.append(' %-20s = %s\n' % (key, valstr))
|
||||
ret.append('\n')
|
||||
return ret
|
||||
|
||||
def formatExtendedTraceback(etype, value, tb):
|
||||
ret = ['Traceback (most recent call last):\n']
|
||||
while tb is not None:
|
||||
frame = tb.tb_frame
|
||||
filename = frame.f_code.co_filename
|
||||
item = ' File "%s", line %d, in %s\n' % (filename, tb.tb_lineno,
|
||||
frame.f_code.co_name)
|
||||
linecache.checkcache(filename)
|
||||
line = linecache.getline(filename, tb.tb_lineno, frame.f_globals)
|
||||
if line:
|
||||
item = item + ' %s\n' % line.strip()
|
||||
ret.append(item)
|
||||
if filename not in ('<script>', '<string>'):
|
||||
ret += formatExtendedFrame(tb.tb_frame)
|
||||
tb = tb.tb_next
|
||||
ret += traceback.format_exception_only(etype, value)
|
||||
return ''.join(ret).rstrip('\n')
|
||||
|
||||
def formatExtendedStack(level=1):
|
||||
f = sys._getframe(level)
|
||||
ret = ['Stack trace (most recent call last):\n\n']
|
||||
while f is not None:
|
||||
lineno = f.f_lineno
|
||||
co = f.f_code
|
||||
filename = co.co_filename
|
||||
name = co.co_name
|
||||
item = ' File "%s", line %d, in %s\n' % (filename, lineno, name)
|
||||
linecache.checkcache(filename)
|
||||
line = linecache.getline(filename, lineno, f.f_globals)
|
||||
if line:
|
||||
item = item + ' %s\n' % line.strip()
|
||||
ret.insert(1, item)
|
||||
if filename != '<script>':
|
||||
ret[2:2] = formatExtendedFrame(f)
|
||||
f = f.f_back
|
||||
return ''.join(ret).rstrip('\n')
|
||||
|
||||
def formatException(cut=0, exc_info=None):
|
||||
"""Format an exception with traceback, but leave out the first `cut`
|
||||
number of frames.
|
||||
"""
|
||||
if exc_info is None:
|
||||
typ, val, tb = sys.exc_info()
|
||||
else:
|
||||
typ, val, tb = exc_info
|
||||
res = ['Traceback (most recent call last):\n']
|
||||
tbres = traceback.format_tb(tb, sys.maxsize)
|
||||
res += tbres[cut:]
|
||||
res += traceback.format_exception_only(typ, val)
|
||||
return ''.join(res)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
print "minimal testing: lib"
|
||||
d = attrdict(a=1, b=2)
|
||||
|
@ -43,6 +43,7 @@ import threading
|
||||
from messages import *
|
||||
from errors import *
|
||||
from secop.lib.parsing import format_time
|
||||
from secop.lib import formatExtendedStack, formatException
|
||||
|
||||
|
||||
class Dispatcher(object):
|
||||
@ -88,6 +89,7 @@ class Dispatcher(object):
|
||||
errorclass=err.__class__.__name__,
|
||||
errorinfo=[repr(err), str(msg)])
|
||||
except (ValueError, TypeError) as err:
|
||||
self.log.exception(err)
|
||||
reply = msg.get_error(
|
||||
errorclass='BadValue',
|
||||
errorinfo=[repr(err), str(msg)])
|
||||
@ -95,7 +97,7 @@ class Dispatcher(object):
|
||||
self.log.exception(err)
|
||||
reply = msg.get_error(
|
||||
errorclass='InternalError',
|
||||
errorinfo=[repr(err), str(msg)])
|
||||
errorinfo=[formatException(), str(msg), formatExtendedStack()])
|
||||
else:
|
||||
self.log.debug('Can not handle msg %r' % msg)
|
||||
reply = self.unhandled(conn, msg)
|
||||
|
@ -1,308 +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 validators."""
|
||||
|
||||
# a Validator returns a validated object or raises an ValueError
|
||||
# easy python validators: int(), float(), str()
|
||||
# also validators should have a __repr__ returning a 'python' string
|
||||
# which recreates them
|
||||
|
||||
# if a validator does a mapping, it normally maps to the external representation (used for print/log/protocol/...)
|
||||
# to get the internal representation (for the code), call method convert
|
||||
|
||||
from errors import ProgrammingError
|
||||
|
||||
|
||||
class Validator(object):
|
||||
# list of tuples: (name, converter)
|
||||
params = []
|
||||
valuetype = float
|
||||
argstr = ''
|
||||
|
||||
def __init__(self, *args, **kwds):
|
||||
plist = self.params[:]
|
||||
if len(args) > len(plist):
|
||||
raise ProgrammingError('%s takes %d parameters only (%d given)' % (
|
||||
self.__class__.__name__, len(plist), len(args)))
|
||||
for pval in args:
|
||||
pname, pconv = plist.pop(0)
|
||||
if pname in kwds:
|
||||
raise ProgrammingError('%s: positional parameter %s is given '
|
||||
'as keyword!' %
|
||||
(self.__class__.__name__, pname))
|
||||
self.__dict__[pname] = pconv(pval)
|
||||
for pname, pconv in plist:
|
||||
if pname in kwds:
|
||||
pval = kwds.pop(pname)
|
||||
self.__dict__[pname] = pconv(pval)
|
||||
else:
|
||||
raise ProgrammingError('%s: param %s left unspecified!' %
|
||||
(self.__class__.__name__, pname))
|
||||
|
||||
if kwds:
|
||||
raise ProgrammingError('%s got unknown arguments: %s' % (
|
||||
self.__class__.__name__, ', '.join(list(kwds.keys()))))
|
||||
params = []
|
||||
for pn, pt in self.params:
|
||||
pv = getattr(self, pn)
|
||||
if callable(pv):
|
||||
params.append('%s=%s' % (pn, validator_to_str(pv)))
|
||||
else:
|
||||
params.append('%s=%r' % (pn, pv))
|
||||
self.argstr = ', '.join(params)
|
||||
|
||||
def __call__(self, value):
|
||||
return self.check(self.valuetype(value))
|
||||
|
||||
def __repr__(self):
|
||||
return self.to_string()
|
||||
|
||||
def to_string(self):
|
||||
return ('%s(%s)' % (self.__class__.__name__, self.argstr))
|
||||
|
||||
|
||||
class floatrange(Validator):
|
||||
params = [('lower', float), ('upper', float)]
|
||||
|
||||
def check(self, value):
|
||||
try:
|
||||
value = float(value)
|
||||
if self.lower <= value <= self.upper:
|
||||
return value
|
||||
raise ValueError(
|
||||
'Floatrange: value %r must be a float within %f and %f' %
|
||||
(value, self.lower, self.upper))
|
||||
except TypeError:
|
||||
raise ValueError(
|
||||
'Floatrange: value %r must be a float within %f and %f' %
|
||||
(value, self.lower, self.upper))
|
||||
|
||||
|
||||
class intrange(Validator):
|
||||
params = [('lower', int), ('upper', int)]
|
||||
valuetype = int
|
||||
|
||||
def check(self, value):
|
||||
if self.lower <= int(value) <= self.upper:
|
||||
return value
|
||||
raise ValueError(
|
||||
'Intrange: value %r must be an integer within %f and %f' %
|
||||
(value, self.lower, self.upper))
|
||||
|
||||
|
||||
class array(Validator):
|
||||
"""integral amount of data-elements which are described by the SAME validator
|
||||
|
||||
The size of the array can also be described by an validator
|
||||
"""
|
||||
valuetype = list
|
||||
params = [('size', lambda x: x), ('datatype', lambda x: x)]
|
||||
|
||||
def check(self, values):
|
||||
requested_size = len(values)
|
||||
if callable(self.size):
|
||||
try:
|
||||
allowed_size = self.size(requested_size)
|
||||
except ValueError as e:
|
||||
raise ValueError('illegal number of elements %d, need %r: (%s)'
|
||||
% (requested_size, self.size, e))
|
||||
else:
|
||||
allowed_size = self.size
|
||||
if requested_size != allowed_size:
|
||||
raise ValueError('need %d elements (got %d)' %
|
||||
(allowed_size, requested_size))
|
||||
# apply data-type validator to all elements and return
|
||||
res = []
|
||||
for idx, el in enumerate(values):
|
||||
try:
|
||||
res.append(self.datatype(el))
|
||||
except ValueError as e:
|
||||
raise ValueError(
|
||||
'Array Element %s (=%r) not conforming to %r: (%s)' %
|
||||
(idx, el, self.datatype, e))
|
||||
return res
|
||||
|
||||
|
||||
# more complicated validator may not be able to use validator base class
|
||||
class vector(Validator):
|
||||
"""fixed length, eache element has its own validator"""
|
||||
valuetype = tuple
|
||||
|
||||
def __init__(self, *args):
|
||||
self.validators = args
|
||||
self.argstr = ', '.join([validator_to_str(e) for e in args])
|
||||
|
||||
def __call__(self, args):
|
||||
if type(args) in (str, unicode):
|
||||
args = eval(args)
|
||||
if len(args) != len(self.validators):
|
||||
raise ValueError('Vector: need exactly %d elementes (got %d)' %
|
||||
len(self.validators), len(args))
|
||||
res = tuple(v(e) for v, e in zip(self.validators, args))
|
||||
return res
|
||||
|
||||
|
||||
# XXX: fixme!
|
||||
class record(Validator):
|
||||
"""fixed length, eache element has its own name and validator"""
|
||||
|
||||
def __init__(self, **kwds):
|
||||
self.validators = kwds
|
||||
self.argstr = ', '.join(
|
||||
['%s=%s' % (e[0], validator_to_str(e[1])) for e in kwds.items()])
|
||||
|
||||
def __call__(self, **args):
|
||||
if len(args) != len(self.validators):
|
||||
raise ValueError('Vector: need exactly %d elementes (got %d)' %
|
||||
len(self.validators), len(args))
|
||||
return tuple(v(e) for v, e in zip(self.validators, args))
|
||||
|
||||
|
||||
class oneof(Validator):
|
||||
"""needs to comply with one of the given validators/values"""
|
||||
|
||||
def __init__(self, *args):
|
||||
self.oneof = args
|
||||
self.argstr = ', '.join(
|
||||
[validator_to_str(e) if callable(e) else repr(e) for e in args])
|
||||
|
||||
def __call__(self, arg):
|
||||
for v in self.oneof:
|
||||
if callable(v):
|
||||
try:
|
||||
if (v == int) and (float(arg) != int(arg)):
|
||||
continue
|
||||
return v(arg)
|
||||
except ValueError:
|
||||
pass # try next validator
|
||||
elif v == arg:
|
||||
return v
|
||||
raise ValueError('Oneof: %r should be one of: %s' % (arg, self.argstr))
|
||||
|
||||
|
||||
class enum(Validator):
|
||||
|
||||
def __init__(self, *args, **kwds):
|
||||
self.mapping = {}
|
||||
# use given kwds directly
|
||||
self.mapping.update(kwds)
|
||||
# enumerate args
|
||||
i = -1
|
||||
args = list(args)
|
||||
while args:
|
||||
i += 1
|
||||
if i in self.mapping:
|
||||
continue
|
||||
self.mapping[args.pop(0)] = i
|
||||
# generate reverse mapping too for use by protocol
|
||||
self.revmapping = {}
|
||||
params = []
|
||||
for k, v in sorted(self.mapping.items(), key=lambda x: x[1]):
|
||||
self.revmapping[v] = k
|
||||
params.append('%s=%r' % (k, v))
|
||||
self.argstr = ', '.join(params)
|
||||
|
||||
def __call__(self, obj):
|
||||
try:
|
||||
obj = int(obj)
|
||||
except ValueError:
|
||||
pass
|
||||
if obj in self.mapping:
|
||||
return obj
|
||||
if obj in self.revmapping:
|
||||
return self.revmapping[obj]
|
||||
raise ValueError("%r should be one of %s" %
|
||||
(obj, ', '.join(map(repr, self.mapping.keys()))))
|
||||
|
||||
def convert(self, arg):
|
||||
return self.mapping.get(arg, arg)
|
||||
|
||||
|
||||
# Validators without parameters:
|
||||
def positive(value=Ellipsis):
|
||||
if value != Ellipsis:
|
||||
if value > 0:
|
||||
return float(value)
|
||||
raise ValueError('Value %r must be > 0!' % value)
|
||||
return -1e-38 # small number > 0
|
||||
|
||||
|
||||
positive.__repr__ = lambda x: validator_to_str(x)
|
||||
|
||||
|
||||
def nonnegative(value=Ellipsis):
|
||||
if value != Ellipsis:
|
||||
if value >= 0:
|
||||
return float(value)
|
||||
raise ValueError('Value %r must be >= 0!' % value)
|
||||
return 0.0
|
||||
|
||||
|
||||
nonnegative.__repr__ = lambda x: validator_to_str(x)
|
||||
|
||||
# helpers
|
||||
|
||||
|
||||
def validator_to_str(validator):
|
||||
if isinstance(validator, Validator):
|
||||
return validator.to_string()
|
||||
if hasattr(validator, 'func_name'):
|
||||
return getattr(validator, 'func_name')
|
||||
for s in 'int str float'.split(' '):
|
||||
t = eval(s)
|
||||
if validator == t or isinstance(validator, t):
|
||||
return s
|
||||
print "##########", type(validator), repr(validator)
|
||||
|
||||
|
||||
# XXX: better use a mapping here!
|
||||
def validator_from_str(validator_str):
|
||||
validator_str = validator_str.replace("<type 'str'>", "str")
|
||||
validator_str = validator_str.replace("<type 'float'>", "float")
|
||||
return eval(validator_str)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
print "minimal testing: validators"
|
||||
for val, good, bad in [
|
||||
(floatrange(3.09, 5.47), 4.13, 9.27),
|
||||
(intrange(3, 5), 4, 8),
|
||||
(array(
|
||||
size=3, datatype=int), (1, 2, 3), (1, 2, 3, 4)),
|
||||
(vector(int, int), (12, 6), (1.23, 'X')),
|
||||
(oneof('a', 'b', 'c', 1), 'b', 'x'),
|
||||
#(record(a=int, b=float), dict(a=2,b=3.97), dict(c=9,d='X')),
|
||||
(positive, 2, 0),
|
||||
(nonnegative, 0, -1),
|
||||
(enum(
|
||||
a=1, b=20), 1, 12),
|
||||
]:
|
||||
print validator_to_str(val), repr(
|
||||
validator_from_str(validator_to_str(val)))
|
||||
print val(good), 'OK'
|
||||
try:
|
||||
val(bad)
|
||||
print "FAIL"
|
||||
raise ProgrammingError
|
||||
except Exception as e:
|
||||
print bad, e, 'OK'
|
||||
print
|
Loading…
x
Reference in New Issue
Block a user