split BadValue into WrongType and RangeError
in order to match SECoP specification fixes #4668 Change-Id: Ica73a8171536ccc324cf8db915347a6263c2d736 Reviewed-on: https://forge.frm2.tum.de/review/c/secop/frappy/+/30625 Tested-by: Jenkins Automated Tests <pedersen+jenkins@frm2.tum.de> Reviewed-by: Enrico Faulhaber <enrico.faulhaber@frm2.tum.de> Reviewed-by: Markus Zolliker <markus.zolliker@psi.ch>
This commit is contained in:
parent
58ff438f46
commit
c0704b3d4f
@ -573,7 +573,7 @@ class SecopClient(ProxyClient):
|
||||
argument = datatype.export_value(argument)
|
||||
else:
|
||||
if argument is not None:
|
||||
raise frappy.errors.BadValueError('command has no argument')
|
||||
raise frappy.errors.WrongTypeError('command has no argument')
|
||||
# pylint: disable=unsubscriptable-object
|
||||
data, qualifiers = self.request(COMMANDREQUEST, self.identifier[module, command], argument)[2]
|
||||
datatype = self.modules[module]['commands'][command]['datatype'].result
|
||||
|
@ -28,7 +28,7 @@
|
||||
import sys
|
||||
from base64 import b64decode, b64encode
|
||||
|
||||
from frappy.errors import BadValueError, \
|
||||
from frappy.errors import WrongTypeError, RangeError, \
|
||||
ConfigError, ProgrammingError, ProtocolError
|
||||
from frappy.lib import clamp, generalConfig
|
||||
from frappy.lib.enum import Enum
|
||||
@ -45,7 +45,7 @@ UNLIMITED = 1 << 64 # internal limit for integers, is probably high enough for
|
||||
Parser = Parser()
|
||||
|
||||
|
||||
class DiscouragedConversion(BadValueError):
|
||||
class DiscouragedConversion(WrongTypeError):
|
||||
"""the discouraged conversion string - > float happened"""
|
||||
log_message = True
|
||||
|
||||
@ -230,7 +230,7 @@ class FloatRange(HasUnit, DataType):
|
||||
try:
|
||||
value = float(value)
|
||||
except Exception:
|
||||
raise BadValueError('can not convert %s to a float' % shortrepr(value)) from None
|
||||
raise WrongTypeError('can not convert %s to a float' % shortrepr(value)) from None
|
||||
if not generalConfig.lazy_number_validation:
|
||||
raise DiscouragedConversion('automatic string to float conversion no longer supported') from None
|
||||
|
||||
@ -245,8 +245,8 @@ class FloatRange(HasUnit, DataType):
|
||||
if self.min - prec <= value <= self.max + prec:
|
||||
# silently clamp when outside by not more than prec
|
||||
return clamp(self.min, value, self.max)
|
||||
raise BadValueError('%.14g must be between %d and %d' %
|
||||
(value, self.min, self.max))
|
||||
raise RangeError('%.14g must be between %d and %d' %
|
||||
(value, self.min, self.max))
|
||||
|
||||
def __repr__(self):
|
||||
hints = self.get_info()
|
||||
@ -277,7 +277,7 @@ class FloatRange(HasUnit, DataType):
|
||||
|
||||
def compatible(self, other):
|
||||
if not isinstance(other, (FloatRange, ScaledInteger)):
|
||||
raise BadValueError('incompatible datatypes')
|
||||
raise WrongTypeError('incompatible datatypes')
|
||||
# avoid infinity
|
||||
other.validate(max(sys.float_info.min, self.min))
|
||||
other.validate(min(sys.float_info.max, self.max))
|
||||
@ -316,11 +316,11 @@ class IntRange(DataType):
|
||||
fvalue = float(value)
|
||||
value = int(value)
|
||||
except Exception:
|
||||
raise BadValueError('can not convert %s to an int' % shortrepr(value)) from None
|
||||
raise WrongTypeError('can not convert %s to an int' % shortrepr(value)) from None
|
||||
if not generalConfig.lazy_number_validation:
|
||||
raise DiscouragedConversion('automatic string to float conversion no longer supported') from None
|
||||
if round(fvalue) != fvalue:
|
||||
raise BadValueError('%r should be an int')
|
||||
raise WrongTypeError('%r should be an int')
|
||||
return value
|
||||
|
||||
def validate(self, value, previous=None):
|
||||
@ -329,8 +329,8 @@ class IntRange(DataType):
|
||||
# check the limits
|
||||
if self.min <= value <= self.max:
|
||||
return value
|
||||
raise BadValueError('%r must be between %d and %d' %
|
||||
(value, self.min, self.max))
|
||||
raise RangeError('%r must be between %d and %d' %
|
||||
(value, self.min, self.max))
|
||||
|
||||
def __repr__(self):
|
||||
args = (self.min, self.max)
|
||||
@ -364,7 +364,7 @@ class IntRange(DataType):
|
||||
# the following loop will not cycle more than the number of Enum elements
|
||||
for i in range(self.min, self.max + 1):
|
||||
other(i)
|
||||
raise BadValueError('incompatible datatypes')
|
||||
raise WrongTypeError('incompatible datatypes')
|
||||
|
||||
|
||||
class ScaledInteger(HasUnit, DataType):
|
||||
@ -388,7 +388,10 @@ class ScaledInteger(HasUnit, DataType):
|
||||
|
||||
def __init__(self, scale, minval=None, maxval=None, absolute_resolution=None, **kwds):
|
||||
super().__init__()
|
||||
scale = float(scale)
|
||||
try:
|
||||
scale = float(scale)
|
||||
except (ValueError, TypeError) as e:
|
||||
raise ProgrammingError(e) from None
|
||||
if absolute_resolution is None:
|
||||
absolute_resolution = scale
|
||||
self.set_properties(
|
||||
@ -404,7 +407,7 @@ class ScaledInteger(HasUnit, DataType):
|
||||
|
||||
# check values
|
||||
if '%' not in self.fmtstr:
|
||||
raise BadValueError('Invalid fmtstr!')
|
||||
raise ConfigError('Invalid fmtstr!')
|
||||
# Remark: Datatype.copy() will round min, max to a multiple of self.scale
|
||||
# this should be o.k.
|
||||
|
||||
@ -434,7 +437,7 @@ class ScaledInteger(HasUnit, DataType):
|
||||
try:
|
||||
value = float(value)
|
||||
except Exception:
|
||||
raise BadValueError('can not convert %s to float' % shortrepr(value)) from None
|
||||
raise WrongTypeError('can not convert %s to float' % shortrepr(value)) from None
|
||||
if not generalConfig.lazy_number_validation:
|
||||
raise DiscouragedConversion('automatic string to float conversion no longer supported') from None
|
||||
intval = int(round(value / self.scale))
|
||||
@ -446,8 +449,8 @@ class ScaledInteger(HasUnit, DataType):
|
||||
if self.min - self.scale < value < self.max + self.scale:
|
||||
# silently clamp when outside by not more than self.scale
|
||||
return clamp(self(self.min), result, self(self.max))
|
||||
raise BadValueError('%.14g must be between between %g and %g' %
|
||||
(value, self.min, self.max))
|
||||
raise RangeError('%.14g must be between between %g and %g' %
|
||||
(value, self.min, self.max))
|
||||
|
||||
def __repr__(self):
|
||||
hints = self.get_info(scale=float('%g' % self.scale),
|
||||
@ -476,7 +479,7 @@ class ScaledInteger(HasUnit, DataType):
|
||||
|
||||
def compatible(self, other):
|
||||
if not isinstance(other, (FloatRange, ScaledInteger)):
|
||||
raise BadValueError('incompatible datatypes')
|
||||
raise WrongTypeError('incompatible datatypes')
|
||||
other.validate(self.min)
|
||||
other.validate(self.max)
|
||||
|
||||
@ -522,7 +525,9 @@ class EnumType(DataType):
|
||||
try:
|
||||
return self._enum[value]
|
||||
except (KeyError, TypeError): # TypeError will be raised when value is not hashable
|
||||
raise BadValueError('%s is not a member of enum %r' % (shortrepr(value), self._enum)) from None
|
||||
if isinstance(value, (int, str)):
|
||||
raise RangeError('%s is not a member of enum %r' % (shortrepr(value), self._enum)) from None
|
||||
raise WrongTypeError('%s must be either int or str for an enum value' % (shortrepr(value))) from None
|
||||
|
||||
def from_string(self, text):
|
||||
return self(text)
|
||||
@ -570,13 +575,13 @@ class BLOBType(DataType):
|
||||
def __call__(self, value):
|
||||
"""accepts bytes only"""
|
||||
if not isinstance(value, bytes):
|
||||
raise BadValueError('%s must be of type bytes' % shortrepr(value))
|
||||
raise WrongTypeError('%s must be of type bytes' % shortrepr(value))
|
||||
size = len(value)
|
||||
if size < self.minbytes:
|
||||
raise BadValueError(
|
||||
raise RangeError(
|
||||
'%r must be at least %d bytes long!' % (value, self.minbytes))
|
||||
if size > self.maxbytes:
|
||||
raise BadValueError(
|
||||
raise RangeError(
|
||||
'%r must be at most %d bytes long!' % (value, self.maxbytes))
|
||||
return value
|
||||
|
||||
@ -599,9 +604,9 @@ class BLOBType(DataType):
|
||||
def compatible(self, other):
|
||||
try:
|
||||
if self.minbytes < other.minbytes or self.maxbytes > other.maxbytes:
|
||||
raise BadValueError('incompatible datatypes')
|
||||
raise RangeError('incompatible datatypes')
|
||||
except AttributeError:
|
||||
raise BadValueError('incompatible datatypes') from None
|
||||
raise WrongTypeError('incompatible datatypes') from None
|
||||
|
||||
|
||||
class StringType(DataType):
|
||||
@ -635,21 +640,21 @@ class StringType(DataType):
|
||||
def __call__(self, value):
|
||||
"""accepts strings only"""
|
||||
if not isinstance(value, str):
|
||||
raise BadValueError('%s has the wrong type!' % shortrepr(value))
|
||||
raise WrongTypeError('%s has the wrong type!' % shortrepr(value))
|
||||
if not self.isUTF8:
|
||||
try:
|
||||
value.encode('ascii')
|
||||
except UnicodeEncodeError:
|
||||
raise BadValueError('%s contains non-ascii character!' % shortrepr(value)) from None
|
||||
raise RangeError('%s contains non-ascii character!' % shortrepr(value)) from None
|
||||
size = len(value)
|
||||
if size < self.minchars:
|
||||
raise BadValueError(
|
||||
raise RangeError(
|
||||
'%s must be at least %d chars long!' % (shortrepr(value), self.minchars))
|
||||
if size > self.maxchars:
|
||||
raise BadValueError(
|
||||
raise RangeError(
|
||||
'%s must be at most %d chars long!' % (shortrepr(value), self.maxchars))
|
||||
if '\0' in value:
|
||||
raise BadValueError(
|
||||
raise RangeError(
|
||||
'Strings are not allowed to embed a \\0! Use a Blob instead!')
|
||||
return value
|
||||
|
||||
@ -672,9 +677,9 @@ class StringType(DataType):
|
||||
try:
|
||||
if self.minchars < other.minchars or self.maxchars > other.maxchars or \
|
||||
self.isUTF8 > other.isUTF8:
|
||||
raise BadValueError('incompatible datatypes')
|
||||
raise RangeError('incompatible datatypes')
|
||||
except AttributeError:
|
||||
raise BadValueError('incompatible datatypes') from None
|
||||
raise WrongTypeError('incompatible datatypes') from None
|
||||
|
||||
|
||||
# TextType is a special StringType intended for longer texts (i.e. embedding \n),
|
||||
@ -715,7 +720,7 @@ class BoolType(DataType):
|
||||
return False
|
||||
if value in [1, '1', 'True', 'true', 'yes', 'on', True]:
|
||||
return True
|
||||
raise BadValueError('%s is not a boolean value!' % shortrepr(value))
|
||||
raise WrongTypeError('%s is not a boolean value!' % shortrepr(value))
|
||||
|
||||
def export_value(self, value):
|
||||
"""returns a python object fit for serialisation"""
|
||||
@ -757,7 +762,7 @@ class ArrayOf(DataType):
|
||||
def __init__(self, members, minlen=0, maxlen=None):
|
||||
super().__init__()
|
||||
if not isinstance(members, DataType):
|
||||
raise BadValueError(
|
||||
raise ProgrammingError(
|
||||
'ArrayOf only works with a DataType as first argument!')
|
||||
# one argument -> exactly that size
|
||||
# argument default to 100
|
||||
@ -800,15 +805,15 @@ class ArrayOf(DataType):
|
||||
try:
|
||||
# check number of elements
|
||||
if self.minlen is not None and len(value) < self.minlen:
|
||||
raise BadValueError(
|
||||
raise RangeError(
|
||||
'array too small, needs at least %d elements!' %
|
||||
self.minlen)
|
||||
if self.maxlen is not None and len(value) > self.maxlen:
|
||||
raise BadValueError(
|
||||
raise RangeError(
|
||||
'array too big, holds at most %d elements!' % self.maxlen)
|
||||
except TypeError:
|
||||
raise BadValueError('%s can not be converted to ArrayOf DataType!'
|
||||
% type(value).__name__) from None
|
||||
raise WrongTypeError('%s can not be converted to ArrayOf DataType!'
|
||||
% type(value).__name__) from None
|
||||
|
||||
def __call__(self, value):
|
||||
"""accepts any sequence, converts to tuple (immutable!)"""
|
||||
@ -816,7 +821,8 @@ class ArrayOf(DataType):
|
||||
try:
|
||||
return tuple(self.members(v) for v in value)
|
||||
except Exception as e:
|
||||
raise BadValueError('can not convert some array elements') from e
|
||||
errcls = RangeError if isinstance(e, RangeError) else WrongTypeError
|
||||
raise errcls('can not convert some array elements') from e
|
||||
|
||||
def validate(self, value, previous=None):
|
||||
self.check_type(value)
|
||||
@ -825,7 +831,8 @@ class ArrayOf(DataType):
|
||||
return tuple(self.members.validate(v, p) for v, p in zip(value, previous))
|
||||
return tuple(self.members.validate(v) for v in value)
|
||||
except Exception as e:
|
||||
raise BadValueError('some array elements are invalid') from e
|
||||
errcls = RangeError if isinstance(e, RangeError) else WrongTypeError
|
||||
raise errcls('some array elements are invalid') from e
|
||||
|
||||
def export_value(self, value):
|
||||
"""returns a python object fit for serialisation"""
|
||||
@ -852,10 +859,10 @@ class ArrayOf(DataType):
|
||||
def compatible(self, other):
|
||||
try:
|
||||
if self.minlen < other.minlen or self.maxlen > other.maxlen:
|
||||
raise BadValueError('incompatible datatypes')
|
||||
raise RangeError('incompatible datatypes')
|
||||
self.members.compatible(other.members)
|
||||
except AttributeError:
|
||||
raise BadValueError('incompatible datatypes') from None
|
||||
raise WrongTypeError('incompatible datatypes') from None
|
||||
|
||||
def set_main_unit(self, unit):
|
||||
self.members.set_main_unit(unit)
|
||||
@ -869,10 +876,10 @@ class TupleOf(DataType):
|
||||
def __init__(self, *members):
|
||||
super().__init__()
|
||||
if not members:
|
||||
raise BadValueError('Empty tuples are not allowed!')
|
||||
raise ProgrammingError('Empty tuples are not allowed!')
|
||||
for subtype in members:
|
||||
if not isinstance(subtype, DataType):
|
||||
raise BadValueError(
|
||||
raise ProgrammingError(
|
||||
'TupleOf only works with DataType objs as arguments!')
|
||||
self.members = members
|
||||
self.default = tuple(el.default for el in members)
|
||||
@ -890,11 +897,11 @@ class TupleOf(DataType):
|
||||
def check_type(self, value):
|
||||
try:
|
||||
if len(value) != len(self.members):
|
||||
raise BadValueError(
|
||||
raise WrongTypeError(
|
||||
'tuple needs %d elements' % len(self.members))
|
||||
except TypeError:
|
||||
raise BadValueError('%s can not be converted to TupleOf DataType!'
|
||||
% type(value).__name__) from None
|
||||
raise WrongTypeError('%s can not be converted to TupleOf DataType!'
|
||||
% type(value).__name__) from None
|
||||
|
||||
def __call__(self, value):
|
||||
"""accepts any sequence, converts to tuple"""
|
||||
@ -902,7 +909,8 @@ class TupleOf(DataType):
|
||||
try:
|
||||
return tuple(sub(elem) for sub, elem in zip(self.members, value))
|
||||
except Exception as e:
|
||||
raise BadValueError('can not convert some tuple elements') from e
|
||||
errcls = RangeError if isinstance(e, RangeError) else WrongTypeError
|
||||
raise errcls('can not convert some tuple elements') from e
|
||||
|
||||
def validate(self, value, previous=None):
|
||||
self.check_type(value)
|
||||
@ -911,7 +919,8 @@ class TupleOf(DataType):
|
||||
return tuple(sub.validate(elem) for sub, elem in zip(self.members, value))
|
||||
return tuple(sub.validate(v, p) for sub, v, p in zip(self.members, value, previous))
|
||||
except Exception as e:
|
||||
raise BadValueError('some tuple elements are invalid') from e
|
||||
errcls = RangeError if isinstance(e, RangeError) else WrongTypeError
|
||||
raise errcls('some tuple elements are invalid') from e
|
||||
|
||||
def export_value(self, value):
|
||||
"""returns a python object fit for serialisation"""
|
||||
@ -933,9 +942,9 @@ class TupleOf(DataType):
|
||||
|
||||
def compatible(self, other):
|
||||
if not isinstance(other, TupleOf):
|
||||
raise BadValueError('incompatible datatypes')
|
||||
raise WrongTypeError('incompatible datatypes')
|
||||
if len(self.members) != len(other.members):
|
||||
raise BadValueError('incompatible datatypes')
|
||||
raise WrongTypeError('incompatible datatypes')
|
||||
for a, b in zip(self.members, other.members):
|
||||
a.compatible(b)
|
||||
|
||||
@ -962,7 +971,7 @@ class StructOf(DataType):
|
||||
super().__init__()
|
||||
self.members = members
|
||||
if not members:
|
||||
raise BadValueError('Empty structs are not allowed!')
|
||||
raise ProgrammingError('Empty structs are not allowed!')
|
||||
self.optional = list(members if optional is None else optional)
|
||||
for name, subtype in list(members.items()):
|
||||
if not isinstance(subtype, DataType):
|
||||
@ -994,27 +1003,28 @@ class StructOf(DataType):
|
||||
"""accepts any mapping, returns an immutable dict"""
|
||||
try:
|
||||
if set(dict(value)) != set(self.members):
|
||||
raise BadValueError('member names do not match') from None
|
||||
raise WrongTypeError('member names do not match') from None
|
||||
except TypeError:
|
||||
raise BadValueError('%s can not be converted a StructOf'
|
||||
% type(value).__name__) from None
|
||||
raise WrongTypeError('%s can not be converted a StructOf'
|
||||
% type(value).__name__) from None
|
||||
try:
|
||||
return ImmutableDict((str(k), self.members[k](v))
|
||||
for k, v in list(value.items()))
|
||||
except Exception as e:
|
||||
raise BadValueError('can not convert some struct element') from e
|
||||
errcls = RangeError if isinstance(e, RangeError) else WrongTypeError
|
||||
raise errcls('can not convert some struct element') from e
|
||||
|
||||
def validate(self, value, previous=None):
|
||||
try:
|
||||
superfluous = set(dict(value)) - set(self.members)
|
||||
except TypeError:
|
||||
raise BadValueError('%s can not be converted a StructOf'
|
||||
% type(value).__name__) from None
|
||||
raise WrongTypeError('%s can not be converted a StructOf'
|
||||
% type(value).__name__) from None
|
||||
if superfluous - set(self.optional):
|
||||
raise BadValueError('struct contains superfluous members: %s' % ', '.join(superfluous))
|
||||
raise WrongTypeError('struct contains superfluous members: %s' % ', '.join(superfluous))
|
||||
missing = set(self.members) - set(value) - set(self.optional)
|
||||
if missing:
|
||||
raise BadValueError('missing struct elements: %s' % ', '.join(missing))
|
||||
raise WrongTypeError('missing struct elements: %s' % ', '.join(missing))
|
||||
try:
|
||||
if previous is None:
|
||||
return ImmutableDict((str(k), self.members[k].validate(v))
|
||||
@ -1023,7 +1033,8 @@ class StructOf(DataType):
|
||||
result.update(((k, self.members[k].validate(v, previous[k])) for k, v in value.items()))
|
||||
return ImmutableDict(result)
|
||||
except Exception as e:
|
||||
raise BadValueError('some struct elements are invalid') from e
|
||||
errcls = RangeError if isinstance(e, RangeError) else WrongTypeError
|
||||
raise errcls('some struct elements are invalid') from e
|
||||
|
||||
def export_value(self, value):
|
||||
"""returns a python object fit for serialisation"""
|
||||
@ -1051,9 +1062,9 @@ class StructOf(DataType):
|
||||
m.compatible(other.members[k])
|
||||
mandatory.discard(k)
|
||||
if mandatory:
|
||||
raise BadValueError('incompatible datatypes')
|
||||
raise WrongTypeError('incompatible datatypes')
|
||||
except (AttributeError, TypeError, KeyError):
|
||||
raise BadValueError('incompatible datatypes') from None
|
||||
raise WrongTypeError('incompatible datatypes') from None
|
||||
|
||||
def set_main_unit(self, unit):
|
||||
for member in self.members.values():
|
||||
@ -1071,10 +1082,10 @@ class CommandType(DataType):
|
||||
super().__init__()
|
||||
if argument is not None:
|
||||
if not isinstance(argument, DataType):
|
||||
raise BadValueError('CommandType: Argument type must be a DataType!')
|
||||
raise ProgrammingError('CommandType: Argument type must be a DataType!')
|
||||
if result is not None:
|
||||
if not isinstance(result, DataType):
|
||||
raise BadValueError('CommandType: Result type must be a DataType!')
|
||||
raise ProgrammingError('CommandType: Result type must be a DataType!')
|
||||
self.argument = argument
|
||||
self.result = result
|
||||
|
||||
@ -1118,7 +1129,7 @@ class CommandType(DataType):
|
||||
if self.result != other.result: # not both are None
|
||||
other.result.compatible(self.result)
|
||||
except AttributeError:
|
||||
raise BadValueError('incompatible datatypes') from None
|
||||
raise WrongTypeError('incompatible datatypes') from None
|
||||
|
||||
|
||||
# internally used datatypes (i.e. only for programming the SEC-node)
|
||||
@ -1204,7 +1215,7 @@ class OrType(DataType):
|
||||
return t(value)
|
||||
except Exception:
|
||||
pass
|
||||
raise BadValueError("Invalid Value, must conform to one of %s" % (', '.join((str(t) for t in self.types))))
|
||||
raise WrongTypeError("Invalid Value, must conform to one of %s" % (', '.join((str(t) for t in self.types))))
|
||||
|
||||
|
||||
Int8 = IntRange(-(1 << 7), (1 << 7) - 1)
|
||||
@ -1226,7 +1237,7 @@ class LimitsType(TupleOf):
|
||||
"""accepts an ordered tuple of numeric member types"""
|
||||
limits = TupleOf.validate(self, value)
|
||||
if limits[1] < limits[0]:
|
||||
raise BadValueError('Maximum Value %s must be greater than minimum value %s!' % (limits[1], limits[0]))
|
||||
raise RangeError('Maximum Value %s must be greater than minimum value %s!' % (limits[1], limits[0]))
|
||||
return limits
|
||||
|
||||
|
||||
@ -1294,9 +1305,9 @@ def get_datatype(json, pname=''):
|
||||
kwargs = json.copy()
|
||||
base = kwargs.pop('type')
|
||||
except (TypeError, KeyError, AttributeError):
|
||||
raise BadValueError('a data descriptor must be a dict containing a "type" key, not %r' % json) from None
|
||||
raise WrongTypeError('a data descriptor must be a dict containing a "type" key, not %r' % json) from None
|
||||
|
||||
try:
|
||||
return DATATYPES[base](pname=pname, **kwargs)
|
||||
except Exception as e:
|
||||
raise BadValueError('invalid data descriptor: %r (%s)' % (json, str(e))) from None
|
||||
raise WrongTypeError('invalid data descriptor: %r (%s)' % (json, str(e))) from None
|
||||
|
@ -92,7 +92,15 @@ class ReadOnlyError(SECoPError):
|
||||
pass
|
||||
|
||||
|
||||
class BadValueError(ValueError, SECoPError):
|
||||
class BadValueError(SECoPError):
|
||||
"""do not raise, but might used for instance checks (WrongTypeError, RangeError)"""
|
||||
|
||||
|
||||
class RangeError(ValueError, BadValueError):
|
||||
name = 'RangeError'
|
||||
|
||||
|
||||
class WrongTypeError(TypeError, BadValueError):
|
||||
pass
|
||||
|
||||
|
||||
@ -154,23 +162,28 @@ def secop_error(exception):
|
||||
return InternalError(repr(exception))
|
||||
|
||||
|
||||
EXCEPTIONS = dict(
|
||||
NoSuchModule=NoSuchModuleError,
|
||||
NoSuchParameter=NoSuchParameterError,
|
||||
NoSuchCommand=NoSuchCommandError,
|
||||
CommandFailed=CommandFailedError,
|
||||
CommandRunning=CommandRunningError,
|
||||
ReadOnly=ReadOnlyError,
|
||||
BadValue=BadValueError,
|
||||
CommunicationFailed=CommunicationFailedError,
|
||||
HardwareError=HardwareError,
|
||||
IsBusy=IsBusyError,
|
||||
IsError=IsErrorError,
|
||||
Disabled=DisabledError,
|
||||
EXCEPTIONS = {e().name: e for e in [
|
||||
NoSuchModuleError,
|
||||
NoSuchParameterError,
|
||||
NoSuchCommandError,
|
||||
CommandFailedError,
|
||||
CommandRunningError,
|
||||
ReadOnlyError,
|
||||
BadValueError,
|
||||
RangeError,
|
||||
WrongTypeError,
|
||||
CommunicationFailedError,
|
||||
HardwareError,
|
||||
IsBusyError,
|
||||
IsErrorError,
|
||||
DisabledError,
|
||||
ProtocolError,
|
||||
NotImplementedError,
|
||||
InternalError]}
|
||||
|
||||
# TODO: check if these are really needed:
|
||||
EXCEPTIONS.update(
|
||||
SyntaxError=ProtocolError,
|
||||
NotImplemented=NotImplementedError,
|
||||
ProtocolError=ProtocolError,
|
||||
InternalError=InternalError,
|
||||
# internal short versions (candidates for spec)
|
||||
Protocol=ProtocolError,
|
||||
Internal=InternalError,
|
||||
|
@ -22,9 +22,12 @@
|
||||
"""Define Mixin Features for real Modules implemented in the server"""
|
||||
|
||||
|
||||
from frappy.datatypes import FloatRange, TupleOf
|
||||
from frappy.core import Feature, Parameter, PersistentParam
|
||||
from frappy.errors import BadValueError
|
||||
from frappy.datatypes import ArrayOf, BoolType, EnumType, \
|
||||
FloatRange, StringType, StructOf, TupleOf
|
||||
from frappy.core import Command, Done, Drivable, Feature, \
|
||||
Parameter, Property, PersistentParam, Readable
|
||||
from frappy.errors import RangeError, ConfigError
|
||||
from frappy.lib import clamp
|
||||
|
||||
|
||||
class HasSimpleOffset(Feature):
|
||||
@ -45,12 +48,48 @@ class HasTargetLimits(Feature):
|
||||
"""
|
||||
target_limits = PersistentParam('user limits', readonly=False, default=(-9e99, 9e99),
|
||||
datatype=TupleOf(FloatRange(unit='deg'), FloatRange(unit='deg')))
|
||||
_limits = None
|
||||
|
||||
def apply_offset(self, sign, *values):
|
||||
if isinstance(self, HasOffset):
|
||||
return tuple(v + sign * self.offset for v in values)
|
||||
return values
|
||||
|
||||
def earlyInit(self):
|
||||
super().earlyInit()
|
||||
# make limits valid
|
||||
_limits = self.apply_offset(1, *self.limits)
|
||||
self._limits = tuple(clamp(self.abslimits[0], v, self.abslimits[1]) for v in _limits)
|
||||
self.read_limits()
|
||||
|
||||
def checkProperties(self):
|
||||
pname = 'target' if isinstance(self, Drivable) else 'value'
|
||||
dt = self.parameters[pname].datatype
|
||||
min_, max_ = self.abslimits
|
||||
t_min, t_max = self.apply_offset(1, dt.min, dt.max)
|
||||
if t_min > max_ or t_max < min_:
|
||||
raise ConfigError('abslimits not within %s range' % pname)
|
||||
self.abslimits = clamp(t_min, min_, t_max), clamp(t_min, max_, t_max)
|
||||
super().checkProperties()
|
||||
|
||||
def read_limits(self):
|
||||
return self.apply_offset(-1, *self._limits)
|
||||
|
||||
def write_limits(self, value):
|
||||
min_, max_ = self.apply_offset(-1, *self.abslimits)
|
||||
if not min_ <= value[0] <= value[1] <= max_:
|
||||
if value[0] > value[1]:
|
||||
raise RangeError('invalid interval: %r' % value)
|
||||
raise RangeError('limits not within abs limits [%g, %g]' % (min_, max_))
|
||||
self.limits = value
|
||||
self.saveParameters()
|
||||
return Done
|
||||
|
||||
def check_limits(self, value):
|
||||
"""check if value is valid"""
|
||||
min_, max_ = self.target_limits
|
||||
if not min_ <= value <= max_:
|
||||
raise BadValueError('limits violation: %g outside [%g, %g]' % (value, min_, max_))
|
||||
raise RangeError('limits violation: %g outside [%g, %g]' % (value, min_, max_))
|
||||
|
||||
|
||||
# --- legacy mixins, not agreed as standard ---
|
||||
|
@ -28,7 +28,7 @@ import inspect
|
||||
from frappy.datatypes import BoolType, CommandType, DataType, \
|
||||
DataTypeType, EnumType, NoneOr, OrType, \
|
||||
StringType, StructOf, TextType, TupleOf, ValueType
|
||||
from frappy.errors import BadValueError, ProgrammingError
|
||||
from frappy.errors import BadValueError, WrongTypeError, ProgrammingError
|
||||
from frappy.properties import HasProperties, Property
|
||||
from frappy.lib import generalConfig
|
||||
|
||||
@ -304,7 +304,7 @@ class Parameter(Accessible):
|
||||
except KeyError:
|
||||
raise ProgrammingError('cannot set %s on parameter with datatype %s'
|
||||
% (key, type(self.datatype).__name__)) from None
|
||||
except ValueError as e:
|
||||
except BadValueError as e:
|
||||
raise ProgrammingError('property %s: %s' % (key, str(e))) from None
|
||||
|
||||
def checkProperties(self):
|
||||
@ -489,7 +489,7 @@ class Command(Accessible):
|
||||
res = func(argument)
|
||||
else:
|
||||
if argument is not None:
|
||||
raise BadValueError('%s.%s takes no arguments' % (module_obj.__class__.__name__, self.name))
|
||||
raise WrongTypeError('%s.%s takes no arguments' % (module_obj.__class__.__name__, self.name))
|
||||
res = func()
|
||||
if self.result:
|
||||
return self.result(res)
|
||||
|
@ -29,7 +29,7 @@ from frappy.core import BoolType, Command, EnumType, FloatRange, IntRange, \
|
||||
HasIO, Parameter, Property, Drivable, PersistentMixin, PersistentParam, Done, \
|
||||
IDLE, BUSY, ERROR
|
||||
from frappy.io import BytesIO
|
||||
from frappy.errors import CommunicationFailedError, HardwareError, BadValueError, IsBusyError
|
||||
from frappy.errors import CommunicationFailedError, HardwareError, RangeError, IsBusyError
|
||||
from frappy.rwhandler import ReadHandler, WriteHandler
|
||||
from frappy.lib import formatStatusBits
|
||||
|
||||
@ -345,8 +345,8 @@ class Motor(PersistentMixin, HasIO, Drivable):
|
||||
abs(target - self.encoder) > self.move_limit + self.tolerance):
|
||||
# pylint: disable=bad-string-format-type
|
||||
# pylint wrongly does not recognise encoder as a descriptor
|
||||
raise BadValueError('can not move more than %g deg (%g -> %g)' %
|
||||
(self.move_limit, self.encoder, target))
|
||||
raise RangeError('can not move more than %g deg (%g -> %g)' %
|
||||
(self.move_limit, self.encoder, target))
|
||||
diff = self.encoder - self.steppos
|
||||
if self._need_reset:
|
||||
if self.auto_reset:
|
||||
|
@ -31,6 +31,7 @@ from frappy.datatypes import ArrayOf, BLOBType, BoolType, \
|
||||
StringType, StructOf, TextType, TupleOf, get_datatype, \
|
||||
DiscouragedConversion
|
||||
from frappy.lib import generalConfig
|
||||
from frappy.errors import WrongTypeError, RangeError, BadValueError
|
||||
|
||||
|
||||
def copytest(dt):
|
||||
@ -54,15 +55,15 @@ def test_FloatRange():
|
||||
copytest(dt)
|
||||
assert dt.export_datatype() == {'type': 'double', 'min':-3.14, 'max':3.14}
|
||||
|
||||
with pytest.raises(ValueError):
|
||||
with pytest.raises(RangeError):
|
||||
dt.validate(9)
|
||||
with pytest.raises(ValueError):
|
||||
with pytest.raises(RangeError):
|
||||
dt.validate(-9)
|
||||
dt(9) # convert, but do not check limits
|
||||
dt(-9) # convert, but do not check limits
|
||||
with pytest.raises(ValueError):
|
||||
with pytest.raises(WrongTypeError):
|
||||
dt('XX')
|
||||
with pytest.raises(ValueError):
|
||||
with pytest.raises(WrongTypeError):
|
||||
dt([19, 'X'])
|
||||
dt(1)
|
||||
dt(0)
|
||||
@ -107,19 +108,19 @@ def test_IntRange():
|
||||
copytest(dt)
|
||||
assert dt.export_datatype() == {'type': 'int', 'min':-3, 'max':3}
|
||||
|
||||
with pytest.raises(ValueError):
|
||||
with pytest.raises(RangeError):
|
||||
dt.validate(9)
|
||||
with pytest.raises(ValueError):
|
||||
with pytest.raises(RangeError):
|
||||
dt.validate(-9)
|
||||
dt(9) # convert, but do not check limits
|
||||
dt(-9) # convert, but do not check limits
|
||||
with pytest.raises(ValueError):
|
||||
with pytest.raises(WrongTypeError):
|
||||
dt('XX')
|
||||
with pytest.raises(ValueError):
|
||||
with pytest.raises(WrongTypeError):
|
||||
dt([19, 'X'])
|
||||
with pytest.raises(ValueError):
|
||||
with pytest.raises(WrongTypeError):
|
||||
dt(1.3)
|
||||
with pytest.raises(ValueError):
|
||||
with pytest.raises(WrongTypeError):
|
||||
dt('1.3')
|
||||
dt(1)
|
||||
dt(0)
|
||||
@ -145,19 +146,19 @@ def test_ScaledInteger():
|
||||
# serialisation of datatype contains limits on the 'integer' value
|
||||
assert dt.export_datatype() == {'type': 'scaled', 'scale':0.01, 'min':-300, 'max':300}
|
||||
|
||||
with pytest.raises(ValueError):
|
||||
with pytest.raises(RangeError):
|
||||
dt.validate(9)
|
||||
with pytest.raises(ValueError):
|
||||
with pytest.raises(RangeError):
|
||||
dt.validate(-9)
|
||||
dt(9) # convert, but do not check limits
|
||||
dt(-9) # convert, but do not check limits
|
||||
with pytest.raises(ValueError):
|
||||
with pytest.raises(WrongTypeError):
|
||||
dt('XX')
|
||||
with pytest.raises(ValueError):
|
||||
with pytest.raises(WrongTypeError):
|
||||
dt([19, 'X'])
|
||||
dt(1)
|
||||
dt(0)
|
||||
with pytest.raises(ValueError):
|
||||
with pytest.raises(ProgrammingError):
|
||||
ScaledInteger('xc', 'Yx')
|
||||
with pytest.raises(ProgrammingError):
|
||||
ScaledInteger(scale=0, minval=1, maxval=2)
|
||||
@ -189,10 +190,10 @@ def test_ScaledInteger():
|
||||
assert dt.format_value(0.6, '') == '0.6'
|
||||
assert dt.format_value(0.6, 'Z') == '0.6 Z'
|
||||
assert round(dt.validate(1.0004), 5) == 0.999 # rounded value within limit
|
||||
with pytest.raises(ValueError):
|
||||
with pytest.raises(RangeError):
|
||||
dt.validate(1.006) # rounded value outside limit
|
||||
assert round(dt.validate(0.398), 5) == 0.399 # rounded value within rounded limit
|
||||
with pytest.raises(ValueError):
|
||||
with pytest.raises(RangeError):
|
||||
dt.validate(0.395) # rounded value outside limit
|
||||
|
||||
dt.setProperty('min', 1)
|
||||
@ -200,7 +201,7 @@ def test_ScaledInteger():
|
||||
with pytest.raises(ConfigError):
|
||||
dt.checkProperties()
|
||||
|
||||
with pytest.raises(ValueError):
|
||||
with pytest.raises(WrongTypeError):
|
||||
dt.setProperty('scale', None)
|
||||
|
||||
|
||||
@ -214,21 +215,21 @@ def test_EnumType():
|
||||
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)}
|
||||
assert dt.export_datatype() == {'type': 'enum', 'members': {'a': 3, 'c': 7, 'stuff': 1}}
|
||||
|
||||
with pytest.raises(ValueError):
|
||||
with pytest.raises(RangeError):
|
||||
dt(9)
|
||||
with pytest.raises(ValueError):
|
||||
with pytest.raises(RangeError):
|
||||
dt(-9)
|
||||
with pytest.raises(ValueError):
|
||||
with pytest.raises(RangeError):
|
||||
dt('XX')
|
||||
with pytest.raises(ValueError):
|
||||
with pytest.raises(WrongTypeError):
|
||||
dt([19, 'X'])
|
||||
|
||||
assert dt('a') == 3
|
||||
assert dt('stuff') == 1
|
||||
assert dt(1) == 1
|
||||
with pytest.raises(ValueError):
|
||||
with pytest.raises(RangeError):
|
||||
dt(2)
|
||||
|
||||
assert dt.export_value('c') == 7
|
||||
@ -237,9 +238,9 @@ def test_EnumType():
|
||||
assert dt.import_value('c') == 7
|
||||
assert dt.import_value('a') == 3
|
||||
assert dt.import_value('stuff') == 1
|
||||
with pytest.raises(ValueError):
|
||||
with pytest.raises(RangeError):
|
||||
dt.export_value(2)
|
||||
with pytest.raises(ValueError):
|
||||
with pytest.raises(RangeError):
|
||||
dt.import_value('A')
|
||||
|
||||
assert dt.format_value(3) == 'a<3>'
|
||||
@ -258,13 +259,13 @@ def test_BLOBType():
|
||||
copytest(dt)
|
||||
assert dt.export_datatype() == {'type': 'blob', 'minbytes':3, 'maxbytes':10}
|
||||
|
||||
with pytest.raises(ValueError):
|
||||
with pytest.raises(WrongTypeError):
|
||||
dt(9)
|
||||
with pytest.raises(ValueError):
|
||||
with pytest.raises(RangeError):
|
||||
dt(b'av')
|
||||
with pytest.raises(ValueError):
|
||||
with pytest.raises(RangeError):
|
||||
dt(b'abcdefghijklmno')
|
||||
with pytest.raises(ValueError):
|
||||
with pytest.raises(WrongTypeError):
|
||||
dt('abcd')
|
||||
assert dt(b'abcd') == b'abcd'
|
||||
|
||||
@ -296,13 +297,13 @@ def test_StringType():
|
||||
copytest(dt)
|
||||
assert dt.export_datatype() == {'type': 'string', 'minchars':4, 'maxchars':11}
|
||||
|
||||
with pytest.raises(ValueError):
|
||||
with pytest.raises(WrongTypeError):
|
||||
dt(9)
|
||||
with pytest.raises(ValueError):
|
||||
with pytest.raises(RangeError):
|
||||
dt('av')
|
||||
with pytest.raises(ValueError):
|
||||
with pytest.raises(RangeError):
|
||||
dt('abcdefghijklmno')
|
||||
with pytest.raises(ValueError):
|
||||
with pytest.raises(RangeError):
|
||||
dt('abcdefg\0')
|
||||
assert dt('abcd') == 'abcd'
|
||||
# tests with bytes have to be added after migration to py3
|
||||
@ -326,11 +327,11 @@ def test_TextType():
|
||||
copytest(dt)
|
||||
assert dt.export_datatype() == {'type': 'string', 'maxchars':12}
|
||||
|
||||
with pytest.raises(ValueError):
|
||||
with pytest.raises(WrongTypeError):
|
||||
dt(9)
|
||||
with pytest.raises(ValueError):
|
||||
with pytest.raises(RangeError):
|
||||
dt('abcdefghijklmno')
|
||||
with pytest.raises(ValueError):
|
||||
with pytest.raises(RangeError):
|
||||
dt('abcdefg\0')
|
||||
assert dt('ab\n\ncd\n') == 'ab\n\ncd\n'
|
||||
# assert dt(b'ab\n\ncd\n') == 'ab\n\ncd\n'
|
||||
@ -347,9 +348,9 @@ def test_BoolType():
|
||||
copytest(dt)
|
||||
assert dt.export_datatype() == {'type': 'bool'}
|
||||
|
||||
with pytest.raises(ValueError):
|
||||
with pytest.raises(WrongTypeError):
|
||||
dt(9)
|
||||
with pytest.raises(ValueError):
|
||||
with pytest.raises(WrongTypeError):
|
||||
dt('av')
|
||||
|
||||
assert dt('true') is True
|
||||
@ -362,7 +363,7 @@ def test_BoolType():
|
||||
|
||||
assert dt.import_value(False) is False
|
||||
assert dt.import_value(True) is True
|
||||
with pytest.raises(ValueError):
|
||||
with pytest.raises(WrongTypeError):
|
||||
dt.import_value('av')
|
||||
|
||||
assert dt.format_value(0) == "False"
|
||||
@ -375,9 +376,9 @@ def test_BoolType():
|
||||
|
||||
def test_ArrayOf():
|
||||
# test constructor catching illegal arguments
|
||||
with pytest.raises(ValueError):
|
||||
with pytest.raises(ProgrammingError):
|
||||
ArrayOf(int)
|
||||
with pytest.raises(ValueError):
|
||||
with pytest.raises(ProgrammingError):
|
||||
ArrayOf(-3, IntRange(-10,10))
|
||||
dt = ArrayOf(IntRange(-10, 10), 5)
|
||||
copytest(dt)
|
||||
@ -390,9 +391,9 @@ def test_ArrayOf():
|
||||
assert dt.export_datatype() == {'type': 'array', 'minlen':1, 'maxlen':3,
|
||||
'members':{'type': 'double', 'min':-10,
|
||||
'max':10, 'unit': 'Z'}}
|
||||
with pytest.raises(ValueError):
|
||||
with pytest.raises(WrongTypeError):
|
||||
dt(9)
|
||||
with pytest.raises(ValueError):
|
||||
with pytest.raises(WrongTypeError):
|
||||
dt('av')
|
||||
|
||||
assert dt([1, 2, 3]) == (1, 2, 3)
|
||||
@ -422,16 +423,16 @@ def test_ArrayOf():
|
||||
|
||||
def test_TupleOf():
|
||||
# test constructor catching illegal arguments
|
||||
with pytest.raises(ValueError):
|
||||
with pytest.raises(ProgrammingError):
|
||||
TupleOf(2)
|
||||
|
||||
dt = TupleOf(IntRange(-10, 10), BoolType())
|
||||
copytest(dt)
|
||||
assert dt.export_datatype() == {'type': 'tuple',
|
||||
'members':[{'type': 'int', 'min':-10, 'max':10}, {'type': 'bool'}]}
|
||||
with pytest.raises(ValueError):
|
||||
with pytest.raises(WrongTypeError):
|
||||
dt(9)
|
||||
with pytest.raises(ValueError):
|
||||
with pytest.raises(WrongTypeError):
|
||||
dt([99, 'X'])
|
||||
|
||||
assert dt([1, True]) == (1, True)
|
||||
@ -447,7 +448,7 @@ def test_TupleOf():
|
||||
|
||||
def test_StructOf():
|
||||
# test constructor catching illegal arguments
|
||||
with pytest.raises(ValueError):
|
||||
with pytest.raises(ProgrammingError):
|
||||
StructOf(IntRange) # pylint: disable=E1121
|
||||
with pytest.raises(ProgrammingError):
|
||||
StructOf(IntRange=1)
|
||||
@ -460,15 +461,15 @@ def test_StructOf():
|
||||
'an_int': {'type': 'int', 'min':0, 'max':999}},
|
||||
'optional':['an_int']}
|
||||
|
||||
with pytest.raises(ValueError):
|
||||
with pytest.raises(WrongTypeError):
|
||||
dt(9)
|
||||
with pytest.raises(ValueError):
|
||||
with pytest.raises(WrongTypeError):
|
||||
dt([99, 'X'])
|
||||
with pytest.raises(ValueError):
|
||||
dt.validate(dict(a_string='XXX', an_int=1811))
|
||||
with pytest.raises(RangeError):
|
||||
dt.validate({'a_string': 'XXX', 'an_int': 1811})
|
||||
|
||||
assert dt(dict(a_string='XXX', an_int=8)) == {'a_string': 'XXX',
|
||||
'an_int': 8}
|
||||
assert dt({'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'}) == {
|
||||
@ -502,30 +503,30 @@ def test_StatusType():
|
||||
|
||||
|
||||
def test_get_datatype():
|
||||
with pytest.raises(ValueError):
|
||||
with pytest.raises(WrongTypeError):
|
||||
get_datatype(1)
|
||||
with pytest.raises(ValueError):
|
||||
with pytest.raises(WrongTypeError):
|
||||
get_datatype(True)
|
||||
with pytest.raises(ValueError):
|
||||
with pytest.raises(WrongTypeError):
|
||||
get_datatype(str)
|
||||
with pytest.raises(ValueError):
|
||||
with pytest.raises(WrongTypeError):
|
||||
get_datatype({'undefined': {}})
|
||||
|
||||
assert isinstance(get_datatype({'type': 'bool'}), BoolType)
|
||||
with pytest.raises(ValueError):
|
||||
with pytest.raises(WrongTypeError):
|
||||
get_datatype(['bool'])
|
||||
|
||||
with pytest.raises(ValueError):
|
||||
with pytest.raises(WrongTypeError):
|
||||
get_datatype({'type': 'int', 'min':-10}) # missing max
|
||||
with pytest.raises(ValueError):
|
||||
with pytest.raises(WrongTypeError):
|
||||
get_datatype({'type': 'int', 'max':10}) # missing min
|
||||
assert isinstance(get_datatype({'type': 'int', 'min':-10, 'max':10}), IntRange)
|
||||
|
||||
with pytest.raises(ValueError):
|
||||
with pytest.raises(WrongTypeError):
|
||||
get_datatype({'type': 'int', 'min':10, 'max':-10}) # min > max
|
||||
with pytest.raises(ValueError):
|
||||
with pytest.raises(WrongTypeError):
|
||||
get_datatype({'type': 'int'}) # missing limits
|
||||
with pytest.raises(ValueError):
|
||||
with pytest.raises(WrongTypeError):
|
||||
get_datatype({'type': 'int', 'x': 2})
|
||||
|
||||
assert isinstance(get_datatype({'type': 'double'}), FloatRange)
|
||||
@ -534,16 +535,16 @@ def test_get_datatype():
|
||||
assert isinstance(get_datatype({'type': 'double', 'min':-9.9, 'max':11.1}),
|
||||
FloatRange)
|
||||
|
||||
with pytest.raises(ValueError):
|
||||
with pytest.raises(WrongTypeError):
|
||||
get_datatype(['double'])
|
||||
with pytest.raises(ValueError):
|
||||
with pytest.raises(WrongTypeError):
|
||||
get_datatype({'type': 'double', 'min':10, 'max':-10})
|
||||
with pytest.raises(ValueError):
|
||||
with pytest.raises(WrongTypeError):
|
||||
get_datatype(['double', {}, 2])
|
||||
|
||||
with pytest.raises(ValueError):
|
||||
with pytest.raises(WrongTypeError):
|
||||
get_datatype({'type': 'scaled', 'scale':0.01, 'min':-2.718})
|
||||
with pytest.raises(ValueError):
|
||||
with pytest.raises(WrongTypeError):
|
||||
get_datatype({'type': 'scaled', 'scale':0.02, 'max':3.14})
|
||||
assert isinstance(get_datatype(
|
||||
{'type': 'scaled', 'scale':0.03, 'min':-99, 'max':111}), ScaledInteger)
|
||||
@ -552,45 +553,45 @@ def test_get_datatype():
|
||||
assert dt.export_datatype() == {'type': 'scaled', 'max':330, 'min':0, 'scale':0.03}
|
||||
assert get_datatype(dt.export_datatype()).export_datatype() == dt.export_datatype()
|
||||
|
||||
with pytest.raises(ValueError):
|
||||
with pytest.raises(WrongTypeError):
|
||||
get_datatype(['scaled']) # dict missing
|
||||
with pytest.raises(ValueError):
|
||||
with pytest.raises(WrongTypeError):
|
||||
get_datatype({'type': 'scaled', 'min':-10, 'max':10}) # no scale
|
||||
with pytest.raises(ValueError):
|
||||
with pytest.raises(WrongTypeError):
|
||||
get_datatype({'type': 'scaled', 'min':10, 'max':-10, 'scale': 1}) # limits reversed
|
||||
with pytest.raises(ValueError):
|
||||
with pytest.raises(WrongTypeError):
|
||||
get_datatype(['scaled', {'min':10, 'max':-10, 'scale': 1}, 2])
|
||||
|
||||
with pytest.raises(ValueError):
|
||||
with pytest.raises(WrongTypeError):
|
||||
get_datatype(['enum'])
|
||||
with pytest.raises(ValueError):
|
||||
with pytest.raises(WrongTypeError):
|
||||
get_datatype({'type': 'enum', 'a': -2})
|
||||
assert isinstance(get_datatype({'type': 'enum', 'members':dict(a=-2)}), EnumType)
|
||||
assert isinstance(get_datatype({'type': 'enum', 'members': {'a': -2}}), EnumType)
|
||||
|
||||
assert isinstance(get_datatype({'type': 'blob', 'maxbytes':1}), BLOBType)
|
||||
assert isinstance(get_datatype({'type': 'blob', 'minbytes':1, 'maxbytes':10}), BLOBType)
|
||||
|
||||
with pytest.raises(ValueError):
|
||||
with pytest.raises(WrongTypeError):
|
||||
get_datatype({'type': 'blob', 'minbytes':10, 'maxbytes':1})
|
||||
with pytest.raises(ValueError):
|
||||
with pytest.raises(WrongTypeError):
|
||||
get_datatype({'type': 'blob', 'minbytes':10, 'maxbytes':-10})
|
||||
with pytest.raises(ValueError):
|
||||
with pytest.raises(WrongTypeError):
|
||||
get_datatype(['blob', {'maxbytes':10}, 'x'])
|
||||
|
||||
assert isinstance(get_datatype({'type': 'string', 'maxchars':1}), StringType)
|
||||
assert isinstance(get_datatype({'type': 'string', 'maxchars':1}), StringType)
|
||||
assert isinstance(get_datatype({'type': 'string', 'minchars':1, 'maxchars':10}), StringType)
|
||||
|
||||
with pytest.raises(ValueError):
|
||||
with pytest.raises(WrongTypeError):
|
||||
get_datatype({'type': 'string', 'minchars':10, 'maxchars':1})
|
||||
with pytest.raises(ValueError):
|
||||
with pytest.raises(WrongTypeError):
|
||||
get_datatype({'type': 'string', 'minchars':10, 'maxchars':-10})
|
||||
with pytest.raises(ValueError):
|
||||
with pytest.raises(WrongTypeError):
|
||||
get_datatype(['string', {'maxchars':-0}, 'x'])
|
||||
|
||||
with pytest.raises(ValueError):
|
||||
with pytest.raises(WrongTypeError):
|
||||
get_datatype(['array'])
|
||||
with pytest.raises(ValueError):
|
||||
with pytest.raises(WrongTypeError):
|
||||
get_datatype({'type': 'array', 'members': [1]})
|
||||
assert isinstance(get_datatype({'type': 'array', 'minlen':1, 'maxlen':1,
|
||||
'members':{'type': 'blob', 'maxbytes':1}}
|
||||
@ -599,43 +600,43 @@ def test_get_datatype():
|
||||
'members':{'type': 'blob', 'maxbytes':1}}
|
||||
).members, BLOBType)
|
||||
|
||||
with pytest.raises(ValueError):
|
||||
with pytest.raises(WrongTypeError):
|
||||
get_datatype({'type': 'array', 'members':{'type': 'blob', 'maxbytes':1}, 'minbytes':-10})
|
||||
with pytest.raises(ValueError):
|
||||
with pytest.raises(WrongTypeError):
|
||||
get_datatype({'type': 'array', 'members':{'type': 'blob', 'maxbytes':1},
|
||||
'min':10, 'max':1})
|
||||
with pytest.raises(ValueError):
|
||||
get_datatype({'type': 'array', 'blob': dict(max=4), 'maxbytes': 10})
|
||||
with pytest.raises(WrongTypeError):
|
||||
get_datatype({'type': 'array', 'blob': {'max': 4}, 'maxbytes': 10})
|
||||
|
||||
with pytest.raises(ValueError):
|
||||
with pytest.raises(WrongTypeError):
|
||||
get_datatype(['tuple'])
|
||||
with pytest.raises(ValueError):
|
||||
with pytest.raises(WrongTypeError):
|
||||
get_datatype(['tuple', [1], 2, 3])
|
||||
assert isinstance(get_datatype(
|
||||
{'type': 'tuple', 'members':[{'type': 'blob', 'maxbytes':1}]}), TupleOf)
|
||||
assert isinstance(get_datatype(
|
||||
{'type': 'tuple', 'members':[{'type': 'blob', 'maxbytes':1}]}).members[0], BLOBType)
|
||||
|
||||
with pytest.raises(ValueError):
|
||||
with pytest.raises(WrongTypeError):
|
||||
get_datatype({'type': 'tuple', 'members': {}})
|
||||
with pytest.raises(ValueError):
|
||||
with pytest.raises(WrongTypeError):
|
||||
get_datatype(['tuple', 10, -10])
|
||||
|
||||
assert isinstance(get_datatype({'type': 'tuple', 'members':[{'type': 'blob', 'maxbytes':1},
|
||||
{'type': 'bool'}]}), TupleOf)
|
||||
|
||||
with pytest.raises(ValueError):
|
||||
with pytest.raises(WrongTypeError):
|
||||
get_datatype(['struct'])
|
||||
with pytest.raises(ValueError):
|
||||
with pytest.raises(WrongTypeError):
|
||||
get_datatype(['struct', [1], 2, 3])
|
||||
assert isinstance(get_datatype({'type': 'struct', 'members':
|
||||
{'name': {'type': 'blob', 'maxbytes':1}}}), StructOf)
|
||||
assert isinstance(get_datatype({'type': 'struct', 'members':
|
||||
{'name': {'type': 'blob', 'maxbytes':1}}}).members['name'], BLOBType)
|
||||
|
||||
with pytest.raises(ValueError):
|
||||
with pytest.raises(WrongTypeError):
|
||||
get_datatype({'type': 'struct', 'members': {}})
|
||||
with pytest.raises(ValueError):
|
||||
with pytest.raises(WrongTypeError):
|
||||
get_datatype({'type': 'struct', 'members':[1,2,3]})
|
||||
|
||||
|
||||
@ -651,7 +652,7 @@ def test_get_datatype():
|
||||
])
|
||||
def test_oneway_compatible(dt, contained_in):
|
||||
dt.compatible(contained_in)
|
||||
with pytest.raises(ValueError):
|
||||
with pytest.raises(BadValueError):
|
||||
contained_in.compatible(dt)
|
||||
|
||||
|
||||
@ -676,9 +677,9 @@ def test_twoway_compatible(dt1, dt2):
|
||||
(StructOf(a=FloatRange(-1, 1), b=StringType()), StructOf(a=FloatRange(), b=BoolType())),
|
||||
])
|
||||
def test_incompatible(dt1, dt2):
|
||||
with pytest.raises(ValueError):
|
||||
with pytest.raises(BadValueError):
|
||||
dt1.compatible(dt2)
|
||||
with pytest.raises(ValueError):
|
||||
with pytest.raises(BadValueError):
|
||||
dt2.compatible(dt1)
|
||||
|
||||
|
||||
|
@ -22,9 +22,15 @@
|
||||
"""test data types."""
|
||||
|
||||
import frappy.errors
|
||||
from frappy.errors import EXCEPTIONS, SECoPError
|
||||
|
||||
|
||||
def test_errors():
|
||||
"""check consistence of frappy.errors.EXCEPTIONS"""
|
||||
for e in frappy.errors.EXCEPTIONS.values():
|
||||
assert frappy.errors.EXCEPTIONS[e().name] == e
|
||||
for e in EXCEPTIONS.values():
|
||||
assert EXCEPTIONS[e().name] == e
|
||||
# check that all defined secop errors are in EXCEPTIONS
|
||||
for cls in frappy.errors.__dict__.values():
|
||||
if isinstance(cls, type) and issubclass(cls, SECoPError):
|
||||
if cls != SECoPError:
|
||||
assert cls().name in EXCEPTIONS
|
||||
|
@ -24,7 +24,7 @@
|
||||
import pytest
|
||||
|
||||
from frappy.datatypes import FloatRange, IntRange, StringType, ValueType
|
||||
from frappy.errors import BadValueError, ConfigError, ProgrammingError
|
||||
from frappy.errors import RangeError, ConfigError, ProgrammingError
|
||||
from frappy.properties import HasProperties, Property
|
||||
from frappy.core import Parameter
|
||||
|
||||
@ -35,6 +35,7 @@ def Prop(*args, name=None, **kwds):
|
||||
|
||||
|
||||
# Property(description, datatype, default, ...)
|
||||
# pylint: disable=use-dict-literal
|
||||
V_test_Property = [
|
||||
[Prop(StringType(), 'default', extname='extname', mandatory=False),
|
||||
dict(default='default', extname='extname', export=True, mandatory=False)
|
||||
@ -99,7 +100,7 @@ def test_Properties():
|
||||
assert Cls.aa.extname == '_aa'
|
||||
|
||||
cc = Cls()
|
||||
with pytest.raises(BadValueError):
|
||||
with pytest.raises(RangeError):
|
||||
cc.aa = 137
|
||||
|
||||
assert Cls.bb.default == 0
|
||||
|
Loading…
x
Reference in New Issue
Block a user