add optional validation to ValueType
* add optional parameter for ValueType: validator used for checking, if a value meets a criteria (e.g. is dict) + InternalParameter, which is not exported and can hold any python value Change-Id: If39a7a4a8019f2aa1a930e42cbef4fca59163b78 Reviewed-on: https://forge.frm2.tum.de/review/c/secop/frappy/+/30787 Tested-by: Jenkins Automated Tests <pedersen+jenkins@frm2.tum.de> Reviewed-by: Alexander Zaft <a.zaft@fz-juelich.de>
This commit is contained in:
parent
7baacb0f9e
commit
c101af99d3
@ -28,8 +28,8 @@
|
|||||||
import sys
|
import sys
|
||||||
from base64 import b64decode, b64encode
|
from base64 import b64decode, b64encode
|
||||||
|
|
||||||
from frappy.errors import WrongTypeError, RangeError, \
|
from frappy.errors import ConfigError, DiscouragedConversion, \
|
||||||
ConfigError, ProgrammingError, ProtocolError, DiscouragedConversion
|
ProgrammingError, ProtocolError, RangeError, WrongTypeError
|
||||||
from frappy.lib import clamp, generalConfig
|
from frappy.lib import clamp, generalConfig
|
||||||
from frappy.lib.enum import Enum
|
from frappy.lib.enum import Enum
|
||||||
from frappy.parse import Parser
|
from frappy.parse import Parser
|
||||||
@ -1161,11 +1161,35 @@ class DataTypeType(DataType):
|
|||||||
|
|
||||||
|
|
||||||
class ValueType(DataType):
|
class ValueType(DataType):
|
||||||
"""validates any python value"""
|
"""Can take any python value.
|
||||||
|
|
||||||
|
The optional (callable) validator can be used to restrict values to a
|
||||||
|
certain type.
|
||||||
|
For example using `ValueType(dict)` would ensure only values that can be
|
||||||
|
turned into a dictionary can be used in this instance, as the conversion
|
||||||
|
`dict(value)` is called for validation.
|
||||||
|
|
||||||
|
Notes:
|
||||||
|
The validator must either accept a value by returning it or the converted value,
|
||||||
|
or raise an error.
|
||||||
|
"""
|
||||||
|
def __init__(self, validator=None):
|
||||||
|
super().__init__()
|
||||||
|
self.validator = validator
|
||||||
|
|
||||||
def __call__(self, value):
|
def __call__(self, value):
|
||||||
"""accepts any type -> no conversion"""
|
"""accepts any type -> default is no conversion"""
|
||||||
|
if self.validator:
|
||||||
|
try:
|
||||||
|
return self.validator(value)
|
||||||
|
except Exception as e:
|
||||||
|
raise ConfigError('Validator %s raised %r for value %s' \
|
||||||
|
% (self.validator, e, value)) from e
|
||||||
return value
|
return value
|
||||||
|
|
||||||
|
def copy(self):
|
||||||
|
return ValueType(self.validator)
|
||||||
|
|
||||||
def export_value(self, value):
|
def export_value(self, value):
|
||||||
"""if needed, reformat value for transport"""
|
"""if needed, reformat value for transport"""
|
||||||
return value
|
return value
|
||||||
|
@ -163,7 +163,7 @@ class Parameter(Accessible):
|
|||||||
export=False, default=False)
|
export=False, default=False)
|
||||||
update_unchanged = Property(
|
update_unchanged = Property(
|
||||||
'''[internal] updates of unchanged values
|
'''[internal] updates of unchanged values
|
||||||
|
|
||||||
- one of the values 'always', 'never', 'default'
|
- one of the values 'always', 'never', 'default'
|
||||||
or the minimum time between updates of equal values [sec]''',
|
or the minimum time between updates of equal values [sec]''',
|
||||||
OrType(FloatRange(0), EnumType(always=0, never=999999999, default=-1)),
|
OrType(FloatRange(0), EnumType(always=0, never=999999999, default=-1)),
|
||||||
|
@ -25,13 +25,12 @@
|
|||||||
# no fixtures needed
|
# no fixtures needed
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from frappy.datatypes import ArrayOf, BLOBType, BoolType, \
|
from frappy.datatypes import ArrayOf, BLOBType, BoolType, CommandType, \
|
||||||
CommandType, ConfigError, DataType, EnumType, FloatRange, \
|
ConfigError, DataType, DiscouragedConversion, EnumType, FloatRange, \
|
||||||
IntRange, ProgrammingError, ScaledInteger, StatusType, \
|
IntRange, ProgrammingError, ScaledInteger, StatusType, StringType, \
|
||||||
StringType, StructOf, TextType, TupleOf, get_datatype, \
|
StructOf, TextType, TupleOf, ValueType, get_datatype
|
||||||
DiscouragedConversion
|
from frappy.errors import BadValueError, RangeError, WrongTypeError
|
||||||
from frappy.lib import generalConfig
|
from frappy.lib import generalConfig
|
||||||
from frappy.errors import WrongTypeError, RangeError, BadValueError
|
|
||||||
|
|
||||||
|
|
||||||
def copytest(dt):
|
def copytest(dt):
|
||||||
@ -725,3 +724,36 @@ def test_main_unit(unit, dt):
|
|||||||
assert '$' in before
|
assert '$' in before
|
||||||
assert before != after
|
assert before != after
|
||||||
assert before.replace('$', unit) == after
|
assert before.replace('$', unit) == after
|
||||||
|
|
||||||
|
def ex_validator(i):
|
||||||
|
if i > 10:
|
||||||
|
raise RuntimeError('too large')
|
||||||
|
return i
|
||||||
|
|
||||||
|
@pytest.mark.parametrize('validator, value, result', [
|
||||||
|
(dict, [('a', 1)], {'a': 1}),
|
||||||
|
(ex_validator, 5, 5),
|
||||||
|
# pylint: disable=unnecessary-lambda
|
||||||
|
(lambda x: dict(x), {'a': 1}, {'a': 1}),
|
||||||
|
# pylint: disable=unnecessary-lambda
|
||||||
|
(lambda i: ex_validator(i) * 3, 3, 9),
|
||||||
|
])
|
||||||
|
def test_value_type(validator, value, result):
|
||||||
|
t = ValueType()
|
||||||
|
tv = ValueType(validator)
|
||||||
|
assert t(value) == value
|
||||||
|
assert tv(value) == result
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize('validator, value', [
|
||||||
|
(dict, 'strinput'),
|
||||||
|
(ex_validator, 20),
|
||||||
|
# pylint: disable=unnecessary-lambda
|
||||||
|
(lambda i: list(i), 1),
|
||||||
|
])
|
||||||
|
def test_value_type_rejecting(validator, value):
|
||||||
|
t = ValueType()
|
||||||
|
tv = ValueType(validator)
|
||||||
|
assert t(value) == value
|
||||||
|
with pytest.raises(ConfigError):
|
||||||
|
tv(value)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user