diff --git a/secop/datatypes.py b/secop/datatypes.py index 2b16e3b..ded628a 100644 --- a/secop/datatypes.py +++ b/secop/datatypes.py @@ -108,24 +108,24 @@ class FloatRange(DataType): """Restricted float type""" def __init__(self, minval=None, maxval=None, unit=None, fmtstr=None, - absolute_precision=None, relative_precision=None,): + absolute_resolution=None, relative_resolution=None,): self._defaults = {} self.setprop('min', minval, float(u'-inf'), float) self.setprop('max', maxval, float(u'+inf'), float) self.setprop('unit', unit, u'', unicode) self.setprop('fmtstr', fmtstr, u'%g', unicode) - self.setprop('absolute_precision', absolute_precision, 0.0, float) - self.setprop('relative_precision', relative_precision, 1.2e-7, float) + self.setprop('absolute_resolution', absolute_resolution, 0.0, float) + self.setprop('relative_resolution', relative_resolution, 1.2e-7, float) # check values if self.min > self.max: - raise ValueError(u'Max must be larger then min!') + raise ValueError(u'max must be larger then min!') if '%' not in self.fmtstr: raise ValueError(u'Invalid fmtstr!') - if self.absolute_precision < 0: - raise ValueError(u'absolute_precision MUST be >=0') - if self.relative_precision < 0: - raise ValueError(u'relative_precision MUST be >=0') + if self.absolute_resolution < 0: + raise ValueError(u'absolute_resolution MUST be >=0') + if self.relative_resolution < 0: + raise ValueError(u'relative_resolution MUST be >=0') @property def as_json(self): @@ -137,17 +137,10 @@ class FloatRange(DataType): value = float(value) except Exception: raise ValueError(u'Can not validate %r to float' % value) - if self.min is not None and value < self.min: - raise ValueError(u'%r should not be less then %s' % - (value, self.min)) - if self.max is not None and value > self.max: - raise ValueError(u'%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(u'%r should be an float between %.3f and %.3f' % + prec = max(abs(value * self.relative_resolution), self.absolute_resolution) + if self.min - prec <= value <= self.max + prec: + return min(max(value, self.min), self.max) + raise ValueError(u'%g should be a float between %g and %g' % (value, self.min, self.max)) def __repr__(self): @@ -228,15 +221,15 @@ class ScaledInteger(DataType): the scale is only used for calculating to/from transport serialisation""" def __init__(self, scale, minval=None, maxval=None, unit=None, fmtstr=None, - absolute_precision=None, relative_precision=None,): + absolute_resolution=None, relative_resolution=None,): self._defaults = {} self.scale = float(scale) if not self.scale > 0: raise ValueError(u'Scale MUST be positive!') self.setprop('unit', unit, u'', unicode) self.setprop('fmtstr', fmtstr, u'%g', unicode) - self.setprop('absolute_precision', absolute_precision, self.scale, float) - self.setprop('relative_precision', relative_precision, 1.2e-7, float) + self.setprop('absolute_resolution', absolute_resolution, self.scale, float) + self.setprop('relative_resolution', relative_resolution, 1.2e-7, float) self.min = DEFAULT_MIN_INT * self.scale if minval is None else float(minval) self.max = DEFAULT_MAX_INT * self.scale if maxval is None else float(maxval) @@ -246,10 +239,10 @@ class ScaledInteger(DataType): raise ValueError(u'Max must be larger then min!') if '%' not in self.fmtstr: raise ValueError(u'Invalid fmtstr!') - if self.absolute_precision < 0: - raise ValueError(u'absolute_precision MUST be >=0') - if self.relative_precision < 0: - raise ValueError(u'relative_precision MUST be >=0') + if self.absolute_resolution < 0: + raise ValueError(u'absolute_resolution MUST be >=0') + if self.relative_resolution < 0: + raise ValueError(u'relative_resolution MUST be >=0') @property def as_json(self): @@ -265,11 +258,12 @@ class ScaledInteger(DataType): value = float(value) except Exception: raise ValueError(u'Can not validate %r to float' % value) - if value < self.min: - raise ValueError(u'%r should be a float between %d and %d' % - (value, self.min, self.max)) - if value > self.max: - raise ValueError(u'%r should be a float between %d and %d' % + prec = max(self.scale, abs(value * self.relative_resolution), + self.absolute_resolution) + if self.min - prec <= value <= self.max + prec: + value = min(max(value, self.min), self.max) + else: + raise ValueError(u'%g should be a float between %g and %g' % (value, self.min, self.max)) intval = int((value + self.scale * 0.5) // self.scale) value = float(intval * self.scale) diff --git a/test/test_datatypes.py b/test/test_datatypes.py index 81a59f3..2ff1272 100644 --- a/test/test_datatypes.py +++ b/test/test_datatypes.py @@ -56,6 +56,7 @@ def test_FloatRange(): dt.validate([19, u'X']) dt.validate(1) dt.validate(0) + dt.validate(13.14 - 10) # raises an error, if resolution is not handled correctly assert dt.export_value(-2.718) == -2.718 assert dt.import_value(-2.718) == -2.718 with pytest.raises(ValueError): @@ -64,11 +65,11 @@ def test_FloatRange(): dt = FloatRange() assert dt.as_json == [u'double', {}] - dt = FloatRange(unit=u'X', fmtstr=u'%r', absolute_precision=1, - relative_precision=0.1) + dt = FloatRange(unit=u'X', fmtstr=u'%r', absolute_resolution=1, + relative_resolution=0.1) assert dt.as_json == [u'double', {u'unit':u'X', u'fmtstr':u'%r', - u'absolute_precision':1, - u'relative_precision':0.1}] + u'absolute_resolution':1, + u'relative_resolution':0.1}] assert dt.validate(4) == 4 assert dt.format_value(3.14) == u'3.14 X' assert dt.format_value(3.14, u'') == u'3.14' @@ -126,15 +127,18 @@ def test_ScaledInteger(): assert dt.import_value(272) == 2.72 dt = ScaledInteger(0.003, 0, 1, unit=u'X', fmtstr=u'%r', - absolute_precision=1, relative_precision=0.1) + absolute_resolution=0.001, relative_resolution=1e-5) assert dt.as_json == [u'scaled', {u'scale':0.003,u'min':0,u'max':333, u'unit':u'X', u'fmtstr':u'%r', - u'absolute_precision':1, - u'relative_precision':0.1}] + u'absolute_resolution':0.001, + u'relative_resolution':1e-5}] assert dt.validate(0.4) == 0.399 assert dt.format_value(0.4) == u'0.4 X' assert dt.format_value(0.4, u'') == u'0.4' assert dt.format_value(0.4, u'Z') == u'0.4 Z' + assert dt.validate(1.0029) == 0.999 + with pytest.raises(ValueError): + dt.validate(1.004) def test_EnumType():