forked from M-Labs/artiq
language/environment: cast argument processor default values early
Fixes #1434. Also add unit tests for some argument processors. Signed-off-by: Etienne Wodey <wodey@iqo.uni-hannover.de>
This commit is contained in:
parent
371d923385
commit
9b03a365ed
@ -32,7 +32,7 @@ class _SimpleArgProcessor:
|
|||||||
if isinstance(default, list):
|
if isinstance(default, list):
|
||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
if default is not NoDefault:
|
if default is not NoDefault:
|
||||||
self.default_value = default
|
self.default_value = self.process(default)
|
||||||
|
|
||||||
def default(self):
|
def default(self):
|
||||||
if not hasattr(self, "default_value"):
|
if not hasattr(self, "default_value"):
|
||||||
@ -54,6 +54,7 @@ class PYONValue(_SimpleArgProcessor):
|
|||||||
def __init__(self, default=NoDefault):
|
def __init__(self, default=NoDefault):
|
||||||
# Override the _SimpleArgProcessor init, as list defaults are valid
|
# Override the _SimpleArgProcessor init, as list defaults are valid
|
||||||
# PYON values
|
# PYON values
|
||||||
|
# default stays decoded
|
||||||
if default is not NoDefault:
|
if default is not NoDefault:
|
||||||
self.default_value = default
|
self.default_value = default
|
||||||
|
|
||||||
@ -69,7 +70,13 @@ class PYONValue(_SimpleArgProcessor):
|
|||||||
|
|
||||||
class BooleanValue(_SimpleArgProcessor):
|
class BooleanValue(_SimpleArgProcessor):
|
||||||
"""A boolean argument."""
|
"""A boolean argument."""
|
||||||
pass
|
def process(self, x):
|
||||||
|
if x is True:
|
||||||
|
return True
|
||||||
|
elif x is False:
|
||||||
|
return False
|
||||||
|
else:
|
||||||
|
raise ValueError("Invalid BooleanValue value")
|
||||||
|
|
||||||
|
|
||||||
class EnumerationValue(_SimpleArgProcessor):
|
class EnumerationValue(_SimpleArgProcessor):
|
||||||
@ -80,15 +87,20 @@ class EnumerationValue(_SimpleArgProcessor):
|
|||||||
argument.
|
argument.
|
||||||
"""
|
"""
|
||||||
def __init__(self, choices, default=NoDefault):
|
def __init__(self, choices, default=NoDefault):
|
||||||
_SimpleArgProcessor.__init__(self, default)
|
|
||||||
assert default is NoDefault or default in choices
|
|
||||||
self.choices = choices
|
self.choices = choices
|
||||||
|
super().__init__(default)
|
||||||
|
|
||||||
|
def process(self, x):
|
||||||
|
if x not in self.choices:
|
||||||
|
raise ValueError("Invalid EnumerationValue value")
|
||||||
|
return x
|
||||||
|
|
||||||
def describe(self):
|
def describe(self):
|
||||||
d = _SimpleArgProcessor.describe(self)
|
d = _SimpleArgProcessor.describe(self)
|
||||||
d["choices"] = self.choices
|
d["choices"] = self.choices
|
||||||
return d
|
return d
|
||||||
|
|
||||||
|
|
||||||
class NumberValue(_SimpleArgProcessor):
|
class NumberValue(_SimpleArgProcessor):
|
||||||
"""An argument that can take a numerical value.
|
"""An argument that can take a numerical value.
|
||||||
|
|
||||||
@ -132,8 +144,6 @@ class NumberValue(_SimpleArgProcessor):
|
|||||||
"the scale manually".format(unit))
|
"the scale manually".format(unit))
|
||||||
if step is None:
|
if step is None:
|
||||||
step = scale/10.0
|
step = scale/10.0
|
||||||
if default is not NoDefault:
|
|
||||||
self.default_value = default
|
|
||||||
self.unit = unit
|
self.unit = unit
|
||||||
self.scale = scale
|
self.scale = scale
|
||||||
self.step = step
|
self.step = step
|
||||||
@ -141,19 +151,13 @@ class NumberValue(_SimpleArgProcessor):
|
|||||||
self.max = max
|
self.max = max
|
||||||
self.ndecimals = ndecimals
|
self.ndecimals = ndecimals
|
||||||
|
|
||||||
|
super().__init__(default)
|
||||||
|
|
||||||
def _is_int(self):
|
def _is_int(self):
|
||||||
return (self.ndecimals == 0
|
return (self.ndecimals == 0
|
||||||
and int(self.step) == self.step
|
and int(self.step) == self.step
|
||||||
and self.scale == 1)
|
and self.scale == 1)
|
||||||
|
|
||||||
def default(self):
|
|
||||||
if not hasattr(self, "default_value"):
|
|
||||||
raise DefaultMissing
|
|
||||||
if self._is_int():
|
|
||||||
return int(self.default_value)
|
|
||||||
else:
|
|
||||||
return float(self.default_value)
|
|
||||||
|
|
||||||
def process(self, x):
|
def process(self, x):
|
||||||
if self._is_int():
|
if self._is_int():
|
||||||
return int(x)
|
return int(x)
|
||||||
|
79
artiq/test/test_arguments.py
Normal file
79
artiq/test/test_arguments.py
Normal file
@ -0,0 +1,79 @@
|
|||||||
|
import unittest
|
||||||
|
import numbers
|
||||||
|
|
||||||
|
|
||||||
|
from artiq.language.environment import BooleanValue, EnumerationValue, \
|
||||||
|
NumberValue, DefaultMissing
|
||||||
|
|
||||||
|
|
||||||
|
class NumberValueCase(unittest.TestCase):
|
||||||
|
def setUp(self):
|
||||||
|
self.default_value = NumberValue()
|
||||||
|
self.int_value = NumberValue(42, step=1, ndecimals=0)
|
||||||
|
self.float_value = NumberValue(42)
|
||||||
|
|
||||||
|
def test_invalid_default(self):
|
||||||
|
with self.assertRaises(ValueError):
|
||||||
|
_ = NumberValue("invalid")
|
||||||
|
|
||||||
|
with self.assertRaises(TypeError):
|
||||||
|
_ = NumberValue(1.+1j)
|
||||||
|
|
||||||
|
def test_no_default(self):
|
||||||
|
with self.assertRaises(DefaultMissing):
|
||||||
|
self.default_value.default()
|
||||||
|
|
||||||
|
def test_integer_default(self):
|
||||||
|
self.assertIsInstance(self.int_value.default(), numbers.Integral)
|
||||||
|
|
||||||
|
def test_default_to_float(self):
|
||||||
|
self.assertIsInstance(self.float_value.default(), numbers.Real)
|
||||||
|
self.assertNotIsInstance(self.float_value.default(), numbers.Integral)
|
||||||
|
|
||||||
|
def test_invalid_unit(self):
|
||||||
|
with self.assertRaises(KeyError):
|
||||||
|
_ = NumberValue(unit="invalid")
|
||||||
|
|
||||||
|
def test_default_scale(self):
|
||||||
|
self.assertEqual(self.default_value.scale, 1.)
|
||||||
|
|
||||||
|
|
||||||
|
class BooleanValueCase(unittest.TestCase):
|
||||||
|
def setUp(self):
|
||||||
|
self.default_value = BooleanValue()
|
||||||
|
self.true_value = BooleanValue(True)
|
||||||
|
self.false_value = BooleanValue(False)
|
||||||
|
|
||||||
|
def test_default(self):
|
||||||
|
self.assertIs(self.true_value.default(), True)
|
||||||
|
self.assertIs(self.false_value.default(), False)
|
||||||
|
|
||||||
|
def test_no_default(self):
|
||||||
|
with self.assertRaises(DefaultMissing):
|
||||||
|
self.default_value.default()
|
||||||
|
|
||||||
|
def test_invalid_default(self):
|
||||||
|
with self.assertRaises(ValueError):
|
||||||
|
_ = BooleanValue(1)
|
||||||
|
|
||||||
|
with self.assertRaises(ValueError):
|
||||||
|
_ = BooleanValue("abc")
|
||||||
|
|
||||||
|
|
||||||
|
class EnumerationValueCase(unittest.TestCase):
|
||||||
|
def setUp(self):
|
||||||
|
self.default_value = EnumerationValue(["abc"])
|
||||||
|
|
||||||
|
def test_no_default(self):
|
||||||
|
with self.assertRaises(DefaultMissing):
|
||||||
|
self.default_value.default()
|
||||||
|
|
||||||
|
def test_invalid_default(self):
|
||||||
|
with self.assertRaises(ValueError):
|
||||||
|
_ = EnumerationValue("abc", "d")
|
||||||
|
|
||||||
|
def test_valid_default(self):
|
||||||
|
try:
|
||||||
|
_ = EnumerationValue("abc", "a")
|
||||||
|
except ValueError:
|
||||||
|
self.fail("Unexpected ValueError")
|
Loading…
Reference in New Issue
Block a user