diff --git a/param/__init__.py b/param/__init__.py index a3885ed74..016d72405 100644 --- a/param/__init__.py +++ b/param/__init__.py @@ -2803,6 +2803,32 @@ def __init__(self, default=Undefined, *, bounds=Undefined, softbounds=Undefined, def _validate(self, val): super()._validate(val) self._validate_bounds(val, self.bounds, self.inclusive_bounds) + self._validate_step(val, self.step) + self._validate_order(val, self.step, allow_None=self.allow_None) + + def _validate_step(self, val, step): + if step is not None: + if not _is_number(step): + raise ValueError("Step can only be None or a " + "numeric value, not type %r." % type(step)) + elif step == 0: + raise ValueError("Step cannot be 0.") + + def _validate_order(self, val, step, allow_None): + if val is None and allow_None: + return + elif val is not None and (val[0] is None or val[1] is None): + return + + start, end = val + if step is not None and step > 0 and not start <= end: + name = "" if self.name is None else " %rs" % self.name + raise ValueError("Range parameter%s end %s is less than its start %s with positive step %s." + % (name, end, start, step)) + elif step is not None and step < 0 and not start >= end: + name = "" if self.name is None else " %rs" % self.name + raise ValueError("Range parameter%s start %s is less than its end %s with negative step %s." + % (name, start, end, step)) def _validate_bounds(self, val, bounds, inclusive_bounds): if bounds is None or (val is None and self.allow_None): diff --git a/tests/testrangeparameter.py b/tests/testrangeparameter.py index 74e33eb2d..c01cf3c0a 100644 --- a/tests/testrangeparameter.py +++ b/tests/testrangeparameter.py @@ -155,3 +155,36 @@ class Q(param.Parameterized): def test_get_soft_bounds(self): q = param.Range((1,3), bounds=(0, 10), softbounds=(1, 9)) self.assertEqual(q.get_soft_bounds(), (1, 9)) + + def test_validate_step(self): + msg = r"Step can only be None or a numeric value, not type ." + + p = param.Range((1, 2), bounds=(0, 10), step=1) + assert p.step == 1 + + with self.assertRaisesRegex(ValueError, msg): + param.Range((1, 2), bounds=(0, 10), step="1") + + def test_validate_order_on_val_with_positive_step(self): + msg = r"Range parameter 'q's end 1 is less than its start 2 with positive step 1." + + class Q(param.Parameterized): + q = param.Range(bounds=(0, 10), step=1) + + with self.assertRaisesRegex(ValueError, msg): + Q.q = (2, 1) + + def test_validate_order_on_val_with_negative_step(self): + msg = r"Range parameter 'q's start -4 is less than its end -2 with negative step -1." + + class Q(param.Parameterized): + q = param.Range(bounds=(-5, -1), step=-1) + + with self.assertRaisesRegex(ValueError, msg): + Q.q = (-4, -2) + + def test_validate_step_order_cannot_be_0(self): + msg = r"Step cannot be 0." + + with self.assertRaisesRegex(ValueError, msg): + param.Range(bounds=(0, 10), step=0)