fetched mlz version

- before some chamges in the gerrit pipline

Change-Id: I33eb2d75f83345a7039d0fb709e66defefb1c3e0
This commit is contained in:
2023-05-02 11:31:30 +02:00
parent b19a8c2e5c
commit da15df076a
765 changed files with 35890 additions and 59302 deletions

View File

@ -25,12 +25,12 @@
# no fixtures needed
import pytest
from secop.datatypes import ArrayOf, BLOBType, BoolType, \
CommandType, ConfigError, DataType, Enum, EnumType, FloatRange, \
IntRange, ProgrammingError, ScaledInteger, StatusType, \
StringType, StructOf, TextType, TupleOf, get_datatype, \
DiscouragedConversion
from secop.lib import generalConfig
from frappy.datatypes import ArrayOf, BLOBType, BoolType, CommandType, \
ConfigError, DataType, EnumType, FloatRange, \
IntRange, ProgrammingError, ScaledInteger, StatusType, StringType, \
StructOf, TextType, TupleOf, ValueType, get_datatype
from frappy.errors import BadValueError, RangeError, WrongTypeError
from frappy.lib import generalConfig
def copytest(dt):
@ -54,13 +54,15 @@ def test_FloatRange():
copytest(dt)
assert dt.export_datatype() == {'type': 'double', 'min':-3.14, 'max':3.14}
with pytest.raises(ValueError):
dt(9)
with pytest.raises(ValueError):
dt(-9)
with pytest.raises(ValueError):
with pytest.raises(RangeError):
dt.validate(9)
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(WrongTypeError):
dt('XX')
with pytest.raises(ValueError):
with pytest.raises(WrongTypeError):
dt([19, 'X'])
dt(1)
dt(0)
@ -105,17 +107,19 @@ def test_IntRange():
copytest(dt)
assert dt.export_datatype() == {'type': 'int', 'min':-3, 'max':3}
with pytest.raises(ValueError):
dt(9)
with pytest.raises(ValueError):
dt(-9)
with pytest.raises(ValueError):
with pytest.raises(RangeError):
dt.validate(9)
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(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)
@ -141,17 +145,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):
dt(9)
with pytest.raises(ValueError):
dt(-9)
with pytest.raises(ValueError):
with pytest.raises(RangeError):
dt.validate(9)
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(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)
@ -171,27 +177,30 @@ def test_ScaledInteger():
'unit':'A'}
assert dt.absolute_resolution == dt.scale
dt = ScaledInteger(0.003, 0, 1, unit='X', fmtstr='%.1f',
dt = ScaledInteger(0.003, 0.4, 1, unit='X', fmtstr='%.1f',
absolute_resolution=0.001, relative_resolution=1e-5)
copytest(dt)
assert dt.export_datatype() == {'type': 'scaled', 'scale':0.003, 'min':0, 'max':333,
assert dt.export_datatype() == {'type': 'scaled', 'scale':0.003, 'min':133, 'max':333,
'unit':'X', 'fmtstr':'%.1f',
'absolute_resolution':0.001,
'relative_resolution':1e-5}
assert dt(0.4) == 0.399
assert dt.format_value(0.4) == '0.4 X'
assert dt.format_value(0.4, '') == '0.4'
assert dt.format_value(0.4, 'Z') == '0.4 Z'
assert dt(1.0029) == 0.999
with pytest.raises(ValueError):
dt(1.004)
assert round(dt(0.7), 5) == 0.699
assert dt.format_value(0.6) == '0.6 X'
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(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(RangeError):
dt.validate(0.395) # rounded value outside limit
dt.setProperty('min', 1)
dt.setProperty('max', 0)
with pytest.raises(ConfigError):
dt.checkProperties()
with pytest.raises(ValueError):
with pytest.raises(WrongTypeError):
dt.setProperty('scale', None)
@ -205,21 +214,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
@ -228,9 +237,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>'
@ -249,13 +258,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'
@ -287,13 +296,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
@ -317,11 +326,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'
@ -338,9 +347,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
@ -353,7 +362,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"
@ -366,9 +375,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)
@ -381,9 +390,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)
@ -410,19 +419,22 @@ def test_ArrayOf():
dt = ArrayOf(EnumType('myenum', single=0), 5)
copytest(dt)
dt = ArrayOf(ArrayOf(FloatRange(unit='m')))
assert dt.format_value([[0, 1], [2, 3]]) == '[[0, 1], [2, 3]] m'
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)
@ -438,7 +450,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)
@ -451,21 +463,21 @@ 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(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'}) == {
'a_string': 'WFEC', 'an_int': 13}
assert dt.format_value({'an_int':2, 'a_string':'Z'}) == "{a_string='Z', an_int=2}"
assert dt.format_value({'an_int': 2, 'a_string': 'Z'}) == "{an_int=2, a_string='Z'}"
dt = StructOf(['optionalmember'], optionalmember=EnumType('myenum', single=0))
copytest(dt)
@ -485,38 +497,50 @@ def test_Command():
def test_StatusType():
status_codes = Enum('Status', IDLE=100, WARN=200, BUSY=300, ERROR=400)
dt = StatusType(status_codes)
assert dt.IDLE == status_codes.IDLE
assert dt.ERROR == status_codes.ERROR
assert dt._enum == status_codes
dt = StatusType('IDLE', 'WARN', 'ERROR', 'DISABLED')
assert dt.IDLE == StatusType.IDLE == 100
assert dt.ERROR == StatusType.ERROR == 400
dt2 = StatusType(None, IDLE=100, WARN=200, ERROR=400, DISABLED=0)
assert dt2.export_datatype() == dt.export_datatype()
dt3 = StatusType(dt.enum)
assert dt3.export_datatype() == dt.export_datatype()
with pytest.raises(ProgrammingError):
StatusType('__init__') # built in attribute of StatusType
with pytest.raises(ProgrammingError):
StatusType(dt.enum, 'custom') # not a standard attribute
StatusType(dt.enum, custom=499) # o.k., if value is given
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)
@ -525,16 +549,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)
@ -543,45 +567,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}}
@ -590,43 +614,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]})
@ -638,11 +662,11 @@ def test_get_datatype():
(StringType(10, 10), StringType()),
(ArrayOf(StringType(), 3, 5), ArrayOf(StringType(), 3, 6)),
(TupleOf(StringType(), BoolType()), TupleOf(StringType(), IntRange())),
(StructOf(a=FloatRange(-1,1)), StructOf(a=FloatRange(), b=BoolType(), optional=['b'])),
(StructOf(a=FloatRange(-1,1), b=BoolType()), StructOf(a=FloatRange(), b=BoolType(), optional=['b'])),
])
def test_oneway_compatible(dt, contained_in):
dt.compatible(contained_in)
with pytest.raises(ValueError):
with pytest.raises(BadValueError):
contained_in.compatible(dt)
@ -667,9 +691,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)
@ -678,5 +702,58 @@ def test_lazy_validation(dt):
generalConfig.defaults['lazy_number_validation'] = True
dt('0')
generalConfig.defaults['lazy_number_validation'] = False
with pytest.raises(DiscouragedConversion):
with pytest.raises(WrongTypeError):
dt('0')
mytuple = TupleOf(ScaledInteger(0.1, 0, 10, unit='$'), FloatRange(unit='$/min'))
myarray = ArrayOf(mytuple)
@pytest.mark.parametrize('unit, dt', [
('m', FloatRange(unit='$/sec')),
('A', mytuple),
('V', myarray),
('X', StructOf(a=myarray, b=mytuple)),
])
def test_main_unit(unit, dt):
fixed_dt = dt.copy()
fixed_dt.set_main_unit(unit)
before = repr(dt.export_datatype())
after = repr(fixed_dt.export_datatype())
assert '$' in before
assert before != 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)