diff --git a/tests/API1/testbooleanparam.py b/tests/API1/testbooleanparam.py new file mode 100644 index 000000000..221bac189 --- /dev/null +++ b/tests/API1/testbooleanparam.py @@ -0,0 +1,155 @@ +""" +Unit test for Boolean parameters. +""" +import datetime as dt + +import param + +from . import API1TestCase +from .utils import check_defaults + + +class TestBooleanParameters(API1TestCase): + + def setUp(self): + super(TestBooleanParameters, self).setUp() + class P(param.Parameterized): + e = param.Boolean() + f = param.Boolean(default=None) + + self.P = P + + def _check_defaults(self, p): + assert p.default is False + assert p.allow_None is False + assert p.bounds == (0, 1) + + def test_defaults_class(self): + class A(param.Parameterized): + b = param.Boolean() + + check_defaults(A.param.b, label='B') + self._check_defaults(A.param.b) + + def test_defaults_inst(self): + class A(param.Parameterized): + b = param.Boolean() + + a = A() + + check_defaults(a.param.b, label='B') + self._check_defaults(a.param.b) + + def test_defaults_unbound(self): + b = param.Boolean() + + check_defaults(b, label=None) + self._check_defaults(b) + + def test_default_is_None(self): + p = self.P() + assert p.f is None + assert p.param.f.allow_None is True + + p.f = True + p.f = None + assert p.f is None + + def test_raise_None_when_not_allowed(self): + p = self.P() + + msg = r"Boolean parameter 'e' must be True or False, not None" + with self.assertRaisesRegex(ValueError, msg): + p.e = None + + with self.assertRaisesRegex(ValueError, msg): + self.P.e = None + + def test_bad_type(self): + msg = r"Boolean parameter 'e' must be True or False, not test" + + with self.assertRaisesRegex(ValueError, msg): + self.P.e = 'test' + + with self.assertRaisesRegex(ValueError, msg): + self.P(e='test') + + p = self.P() + + with self.assertRaisesRegex(ValueError, msg): + p.e = 'test' + + +class TestEventParameters(API1TestCase): + + def setUp(self): + super(TestEventParameters, self).setUp() + class P(param.Parameterized): + e = param.Event() + f = param.Event(default=None) + + self.P = P + + def _check_defaults(self, p): + assert p.default is False + assert p.allow_None is False + assert p.bounds == (0, 1) + + def test_defaults_class(self): + class A(param.Parameterized): + b = param.Event() + + check_defaults(A.param.b, label='B') + self._check_defaults(A.param.b) + + def test_defaults_inst(self): + class A(param.Parameterized): + b = param.Event() + + a = A() + + check_defaults(a.param.b, label='B') + self._check_defaults(a.param.b) + + def test_defaults_unbound(self): + b = param.Event() + + check_defaults(b, label=None) + self._check_defaults(b) + + def test_resets_to_false(self): + p = self.P() + p.e = True + assert p.e is False + + def test_default_is_None(self): + p = self.P() + assert p.f is None + assert p.param.f.allow_None is True + + p.f = None + assert p.f is False + + def test_raise_None_when_not_allowed(self): + p = self.P() + + msg = r"Boolean parameter 'e' must be True or False, not None" + with self.assertRaisesRegex(ValueError, msg): + p.e = None + + with self.assertRaisesRegex(ValueError, msg): + self.P.e = None + + def test_bad_type(self): + msg = r"Boolean parameter 'e' must be True or False, not test" + + with self.assertRaisesRegex(ValueError, msg): + self.P.e = 'test' + + with self.assertRaisesRegex(ValueError, msg): + self.P(e='test') + + p = self.P() + + with self.assertRaisesRegex(ValueError, msg): + p.e = 'test' diff --git a/tests/API1/testbytesparam.py b/tests/API1/testbytesparam.py index 4cbdfa18b..efbac4e31 100644 --- a/tests/API1/testbytesparam.py +++ b/tests/API1/testbytesparam.py @@ -6,6 +6,7 @@ import pytest from . import API1TestCase +from .utils import check_defaults import param @@ -14,6 +15,33 @@ class TestBytesParameters(API1TestCase): + def _check_defaults(self, p): + assert p.default == b'' + assert p.allow_None is False + assert p.regex is None + + def test_defaults_class(self): + class A(param.Parameterized): + b = param.Bytes() + + check_defaults(A.param.b, label='B') + self._check_defaults(A.param.b) + + def test_defaults_inst(self): + class A(param.Parameterized): + b = param.Bytes() + + a = A() + + check_defaults(a.param.b, label='B') + self._check_defaults(a.param.b) + + def test_defaults_unbound(self): + b = param.Bytes() + + check_defaults(b, label=None) + self._check_defaults(b) + def test_bytes_default_type(self): if sys.version_info.major < 3: pytest.skip() diff --git a/tests/API1/testcalendardateparam.py b/tests/API1/testcalendardateparam.py index 6eb9dcbd0..166d2e733 100644 --- a/tests/API1/testcalendardateparam.py +++ b/tests/API1/testcalendardateparam.py @@ -7,10 +7,41 @@ import pytest import param from . import API1TestCase +from .utils import check_defaults class TestDateTimeParameters(API1TestCase): + def _check_defaults(self, p): + assert p.default is None + assert p.allow_None is True + assert p.bounds is None + assert p.softbounds is None + assert p.inclusive_bounds == (True, True) + assert p.step is None + + def test_defaults_class(self): + class A(param.Parameterized): + d = param.CalendarDate() + + check_defaults(A.param.d, label='D') + self._check_defaults(A.param.d) + + def test_defaults_inst(self): + class A(param.Parameterized): + d = param.CalendarDate() + + a = A() + + check_defaults(a.param.d, label='D') + self._check_defaults(a.param.d) + + def test_defaults_unbound(self): + d = param.CalendarDate() + + check_defaults(d, label=None) + self._check_defaults(d) + def test_initialization_out_of_bounds(self): try: class Q(param.Parameterized): @@ -57,3 +88,8 @@ def test_get_soft_bounds(self): def test_datetime_not_accepted(self): with pytest.raises(ValueError): param.CalendarDate(dt.datetime(2021, 8, 16, 10)) + + def test_step_invalid_type_parameter(self): + exception = "Step can only be None or a date type" + with self.assertRaisesRegex(ValueError, exception): + param.CalendarDate(dt.date(2017,2,27), step=3.2) diff --git a/tests/API1/testcalendardaterangeparam.py b/tests/API1/testcalendardaterangeparam.py index 01984eead..3bb005e38 100644 --- a/tests/API1/testcalendardaterangeparam.py +++ b/tests/API1/testcalendardaterangeparam.py @@ -11,6 +11,34 @@ class TestDateTimeRange(API1TestCase): + def _check_defaults(self, p): + assert p.default is None + assert p.allow_None is True + assert p.length == 2 + assert p.bounds is None + assert p.softbounds is None + assert p.inclusive_bounds == (True, True) + assert p.step is None + + def test_defaults_class(self): + class P(param.Parameterized): + r = param.CalendarDateRange() + + self._check_defaults(P.param.r) + + def test_defaults_inst(self): + class P(param.Parameterized): + r = param.CalendarDateRange() + + p = P() + + self._check_defaults(p.param.r) + + def test_defaults_unbound(self): + r = param.CalendarDateRange() + + self._check_defaults(r) + bad_range = (dt.date(2017,2,27),dt.date(2017,2,26)) def test_wrong_type_default(self): diff --git a/tests/API1/testclassselector.py b/tests/API1/testclassselector.py index 69382df04..e477f8638 100644 --- a/tests/API1/testclassselector.py +++ b/tests/API1/testclassselector.py @@ -7,6 +7,7 @@ import param from . import API1TestCase +from .utils import check_defaults class TestClassSelectorParameters(API1TestCase): @@ -20,6 +21,37 @@ class P(param.Parameterized): self.P = P + def _check_defaults(self, p): + assert p.default is None + assert p.allow_None is True + assert p.instantiate is True + assert p.is_instance is True + + def test_defaults_class(self): + class P(param.Parameterized): + s = param.ClassSelector(int) + + check_defaults(P.param.s, label='S', skip=['instantiate']) + self._check_defaults(P.param.s) + assert P.param.s.class_ is int + + def test_defaults_inst(self): + class P(param.Parameterized): + s = param.ClassSelector(int) + + p = P() + + check_defaults(p.param.s, label='S', skip=['instantiate']) + self._check_defaults(p.param.s) + assert p.param.s.class_ is int + + def test_defaults_unbound(self): + s = param.ClassSelector(int) + + check_defaults(s, label=None, skip=['instantiate']) + self._check_defaults(s) + assert s.class_ is int + def test_single_class_instance_constructor(self): p = self.P(e=6) self.assertEqual(p.e, 6) @@ -73,6 +105,35 @@ def test_multiple_class_type_error(self): class TestDictParameters(API1TestCase): + def _check_defaults(self, p): + assert p.default is None + assert p.allow_None is True + assert p.instantiate is True + assert p.is_instance is True + assert p.class_ == dict + + def test_defaults_class(self): + class P(param.Parameterized): + s = param.Dict() + + check_defaults(P.param.s, label='S', skip=['instantiate']) + self._check_defaults(P.param.s) + + def test_defaults_inst(self): + class P(param.Parameterized): + s = param.Dict() + + p = P() + + check_defaults(p.param.s, label='S', skip=['instantiate']) + self._check_defaults(p.param.s) + + def test_defaults_unbound(self): + s = param.Dict() + + check_defaults(s, label=None, skip=['instantiate']) + self._check_defaults(s) + def test_valid_dict_parameter(self): valid_dict = {1:2, 3:3} diff --git a/tests/API1/testcolorparameter.py b/tests/API1/testcolorparameter.py index 233694c9d..949e31973 100644 --- a/tests/API1/testcolorparameter.py +++ b/tests/API1/testcolorparameter.py @@ -3,9 +3,37 @@ """ import param from . import API1TestCase +from .utils import check_defaults class TestColorParameters(API1TestCase): + def _check_defaults(self, p): + assert p.default is None + assert p.allow_None is True + assert p.allow_named is True + + def test_defaults_class(self): + class A(param.Parameterized): + c = param.Color() + + check_defaults(A.param.c, label='C') + self._check_defaults(A.param.c) + + def test_defaults_inst(self): + class A(param.Parameterized): + c = param.Color() + + a = A() + + check_defaults(a.param.c, label='C') + self._check_defaults(a.param.c) + + def test_defaults_unbound(self): + c = param.Color() + + check_defaults(c, label=None) + self._check_defaults(c) + def test_initialization_invalid_string(self): try: class Q(param.Parameterized): diff --git a/tests/API1/testcompositeparams.py b/tests/API1/testcompositeparams.py index 5cf3aac9d..0e8662d46 100644 --- a/tests/API1/testcompositeparams.py +++ b/tests/API1/testcompositeparams.py @@ -7,6 +7,7 @@ import param from . import API1TestCase +from .utils import check_defaults class TestCompositeParameters(API1TestCase): @@ -33,13 +34,42 @@ def __call__(self): self.SomeSequence = SomeSequence + def _check_defaults(self, p): + assert p.default is None + assert p.allow_None is True + assert p.attribs == [] + + def test_defaults_class(self): + class P(param.Parameterized): + c = param.Composite() + + check_defaults(P.param.c, label='C') + self._check_defaults(P.param.c) + assert P.param.c.objtype is P + + def test_defaults_inst(self): + class P(param.Parameterized): + c = param.Composite() + + p = P() + + check_defaults(p.param.c, label='C') + self._check_defaults(p.param.c) + assert p.param.c.objtype is P + + def test_defaults_unbound(self): + c = param.Composite() + + check_defaults(c, label=None) + self._check_defaults(c) + assert not hasattr(c, 'objtype') + def test_initialization(self): "Make an instance and do default checks" self.assertEqual(self.a.x, 0) self.assertEqual(self.a.y, 0) self.assertEqual(self.a.xy, [0,0]) - def test_set_component(self): self.a.x = 1 self.assertEqual(self.a.xy, [1,0]) diff --git a/tests/API1/testdateparam.py b/tests/API1/testdateparam.py index 318c078b2..dc81e71bc 100644 --- a/tests/API1/testdateparam.py +++ b/tests/API1/testdateparam.py @@ -6,9 +6,40 @@ import datetime as dt import param from . import API1TestCase +from .utils import check_defaults class TestDateParameters(API1TestCase): + def _check_defaults(self, p): + assert p.default is None + assert p.allow_None is True + assert p.bounds is None + assert p.softbounds is None + assert p.inclusive_bounds == (True, True) + assert p.step is None + + def test_defaults_class(self): + class A(param.Parameterized): + d = param.Date() + + check_defaults(A.param.d, label='D') + self._check_defaults(A.param.d) + + def test_defaults_inst(self): + class A(param.Parameterized): + d = param.Date() + + a = A() + + check_defaults(a.param.d, label='D') + self._check_defaults(a.param.d) + + def test_defaults_unbound(self): + d = param.Date() + + check_defaults(d, label=None) + self._check_defaults(d) + def test_initialization_out_of_bounds(self): try: class Q(param.Parameterized): @@ -52,6 +83,12 @@ def test_get_soft_bounds(self): self.assertEqual(q.get_soft_bounds(), (dt.datetime(2017,2,1), dt.datetime(2017,2,25))) + def test_step_invalid_type_datetime_parameter(self): + exception = "Step can only be None, a datetime or datetime type" + with self.assertRaisesRegex(ValueError, exception): + param.Date(dt.datetime(2017,2,27), step=3.2) + + def test_date_serialization(): class User(param.Parameterized): A = param.Date(default=None) diff --git a/tests/API1/testdaterangeparam.py b/tests/API1/testdaterangeparam.py index 01a805fc4..94a56bd79 100644 --- a/tests/API1/testdaterangeparam.py +++ b/tests/API1/testdaterangeparam.py @@ -8,6 +8,7 @@ import pytest from . import API1TestCase +from .utils import check_defaults try: import numpy as np @@ -19,6 +20,34 @@ class TestDateRange(API1TestCase): + def _check_defaults(self, p): + assert p.default is None + assert p.allow_None is True + assert p.length == 2 + assert p.bounds is None + assert p.softbounds is None + assert p.inclusive_bounds == (True, True) + assert p.step is None + + def test_defaults_class(self): + class P(param.Parameterized): + r = param.DateRange() + + self._check_defaults(P.param.r) + + def test_defaults_inst(self): + class P(param.Parameterized): + r = param.DateRange() + + p = P() + + self._check_defaults(p.param.r) + + def test_defaults_unbound(self): + r = param.DateRange() + + self._check_defaults(r) + bad_range = (dt.datetime(2017,2,27),dt.datetime(2017,2,26)) def test_wrong_type_default(self): diff --git a/tests/API1/testdefaults.py b/tests/API1/testdefaults.py index 7c62256ae..2fff70146 100644 --- a/tests/API1/testdefaults.py +++ b/tests/API1/testdefaults.py @@ -3,6 +3,8 @@ """ import pytest +import param + from param.parameterized import add_metaclass from param import concrete_descendents, Parameter @@ -10,6 +12,7 @@ from param import * # noqa from param import ClassSelector from . import API1TestCase +from .utils import check_defaults positional_args = { # ClassSelector: (object,) @@ -50,3 +53,80 @@ def test(self): @add_metaclass(DefaultsMetaclassTest) class TestDefaults(API1TestCase): pass + + +def test_defaults_parameter_inst(): + class A(param.Parameterized): + s = param.Parameter() + + a = A() + + check_defaults(a.param.s, label='S') + assert a.param.s.default is None + assert a.param.s.allow_None is True + +def test_defaults_parameter_class(): + class A(param.Parameterized): + s = param.Parameter() + + check_defaults(A.param.s, label='S') + assert A.param.s.default is None + assert A.param.s.allow_None is True + +def test_defaults_parameter_unbound(): + s = param.Parameter() + + check_defaults(s, label=None) + assert s.default is None + assert s.allow_None is True + +def test_defaults_parameter_inst_allow_None(): + class A(param.Parameterized): + s1 = param.Parameter(default='not None') + s2 = param.Parameter(default='not None', allow_None=False) + s3 = param.Parameter(default='not None', allow_None=True) + s4 = param.Parameter(default=None) + s5 = param.Parameter(default=None, allow_None=False) + s6 = param.Parameter(default=None, allow_None=True) + + a = A() + + assert a.param.s1.allow_None is False + assert a.param.s2.allow_None is False + assert a.param.s3.allow_None is True + assert a.param.s4.allow_None is True + assert a.param.s5.allow_None is True + assert a.param.s6.allow_None is True + + +def test_defaults_parameter_class_allow_None(): + class A(param.Parameterized): + s1 = param.Parameter(default='not None') + s2 = param.Parameter(default='not None', allow_None=False) + s3 = param.Parameter(default='not None', allow_None=True) + s4 = param.Parameter(default=None) + s5 = param.Parameter(default=None, allow_None=False) + s6 = param.Parameter(default=None, allow_None=True) + + assert A.param.s1.allow_None is False + assert A.param.s2.allow_None is False + assert A.param.s3.allow_None is True + assert A.param.s4.allow_None is True + assert A.param.s5.allow_None is True + assert A.param.s6.allow_None is True + + +def test_defaults_parameter_unbound_allow_None(): + s1 = param.Parameter(default='not None') + s2 = param.Parameter(default='not None', allow_None=False) + s3 = param.Parameter(default='not None', allow_None=True) + s4 = param.Parameter(default=None) + s5 = param.Parameter(default=None, allow_None=False) + s6 = param.Parameter(default=None, allow_None=True) + + assert s1.allow_None is False + assert s2.allow_None is False + assert s3.allow_None is True + assert s4.allow_None is True + assert s5.allow_None is True + assert s6.allow_None is True diff --git a/tests/API1/testfileselector.py b/tests/API1/testfileselector.py new file mode 100644 index 000000000..792911edd --- /dev/null +++ b/tests/API1/testfileselector.py @@ -0,0 +1,138 @@ +import os +import shutil +import tempfile + +import param + +from . import API1TestCase +from .utils import check_defaults + + +class TestFileSelectorParameters(API1TestCase): + + def setUp(self): + super(TestFileSelectorParameters, self).setUp() + + tmpdir1 = tempfile.mkdtemp() + fa = os.path.join(tmpdir1, 'a.txt') + fb = os.path.join(tmpdir1, 'b.txt') + glob1 = os.path.join(tmpdir1, '*') + open(fa, 'w').close() + open(fb, 'w').close() + tmpdir2 = tempfile.mkdtemp() + fc = os.path.join(tmpdir2, 'c.txt') + fd = os.path.join(tmpdir2, 'd.txt') + glob2 = os.path.join(tmpdir2, '*') + open(fc, 'w').close() + open(fd, 'w').close() + + self.tmpdir1 = tmpdir1 + self.tmpdir2 = tmpdir2 + self.fa = fa + self.fb = fb + self.fc = fc + self.fd = fd + self.glob1 = glob1 + self.glob2 = glob2 + + class P(param.Parameterized): + a = param.FileSelector(path=glob1) + b = param.FileSelector(default=fa, path=glob1) + + self.P = P + + def tearDown(self): + shutil.rmtree(self.tmpdir1) + shutil.rmtree(self.tmpdir2) + + def _check_defaults(self, p): + assert p.default is None + assert p.allow_None is None + assert p.objects == [] + assert p.compute_default_fn is None + assert p.check_on_set is False + assert p.names is None + assert p.path == "" + + def test_defaults_class(self): + class P(param.Parameterized): + s = param.FileSelector() + + check_defaults(P.param.s, label='S') + self._check_defaults(P.param.s) + + def test_defaults_inst(self): + class P(param.Parameterized): + s = param.FileSelector() + + p = P() + + check_defaults(p.param.s, label='S') + self._check_defaults(p.param.s) + + def test_defaults_unbound(self): + s = param.FileSelector() + + check_defaults(s, label=None) + self._check_defaults(s) + + def test_default_is_None(self): + p = self.P() + assert p.a is None + assert p.param.a.default is None + + def test_default_is_honored(self): + p = self.P() + assert p.b == self.fa + assert p.param.b.default in [self.fa, self.fb] + + def test_allow_default_None(self): + class P(param.Parameterized): + a = param.FileSelector(default=None) + + def test_default_not_in_glob(self): + with self.assertRaises(ValueError): + class P(param.Parameterized): + a = param.FileSelector(default='not/in/glob', path=self.glob1) + + def test_objects_auto_set(self): + p = self.P() + assert p.param.a.objects == [self.fa, self.fb] + + def test_set_object_constructor(self): + p = self.P(a=self.fb) + assert p.a == self.fb + + def test_set_object_outside_bounds(self): + p = self.P() + with self.assertRaises(ValueError): + p.a = '/not/in/glob' + + def test_set_path_and_update(self): + p = self.P() + p.param.b.path = self.glob2 + p.param.b.update() + assert p.param.b.objects == [self.fc, self.fd] + assert p.param.b.default in [self.fc, self.fd] + # Default updated but not the value itself + assert p.b == self.fa + + def test_get_range(self): + p = self.P() + r = p.param.a.get_range() + assert r['a.txt'] == self.fa + assert r['b.txt'] == self.fb + p.param.a.path = self.glob2 + p.param.a.update() + r = p.param.a.get_range() + assert r['c.txt'] == self.fc + assert r['d.txt'] == self.fd + + def test_update_file_removed(self): + p = self.P() + assert p.param.b.objects == [self.fa, self.fb] + assert p.param.b.default in [self.fa, self.fb] + os.remove(self.fa) + p.param.b.update() + assert p.param.b.objects == [self.fb] + assert p.param.b.default == self.fb diff --git a/tests/API1/testlist.py b/tests/API1/testlist.py index 17dcab770..3692b722c 100644 --- a/tests/API1/testlist.py +++ b/tests/API1/testlist.py @@ -1,5 +1,6 @@ import param from . import API1TestCase +from .utils import check_defaults # TODO: I copied the tests from testobjectselector, although I # struggled to understand some of them. Both files should be reviewed # and cleaned up together. @@ -17,6 +18,36 @@ class P(param.Parameterized): self.P = P + def _check_defaults(self, p): + assert p.default == [] + assert p.allow_None is False + assert p.class_ is None + assert p.item_type is None + assert p.bounds == (0, None) + assert p.instantiate is True + + def test_defaults_class(self): + class P(param.Parameterized): + l = param.List() + + check_defaults(P.param.l, label='L', skip=['instantiate']) + self._check_defaults(P.param.l) + + def test_defaults_inst(self): + class P(param.Parameterized): + l = param.List() + + p = P() + + check_defaults(p.param.l, label='L', skip=['instantiate']) + self._check_defaults(p.param.l) + + def test_defaults_unbound(self): + l = param.List() + + check_defaults(l, label=None, skip=['instantiate']) + self._check_defaults(l) + def test_default_None(self): class Q(param.Parameterized): r = param.List(default=[]) # Also check None) @@ -51,3 +82,79 @@ def test_set_object_not_None(self): pass else: raise AssertionError("Object set outside range.") + + +class TestHookListParameters(API1TestCase): + + def setUp(self): + super(TestHookListParameters, self).setUp() + class P(param.Parameterized): + e = param.HookList([abs]) + l = param.HookList(bounds=(0,10)) + + self.P = P + + def _check_defaults(self, p): + assert p.default == [] + assert p.allow_None is False + assert p.class_ is None + assert p.item_type is None + assert p.bounds == (0, None) + assert p.instantiate is True + + def test_defaults_class(self): + class P(param.Parameterized): + l = param.HookList() + + check_defaults(P.param.l, label='L', skip=['instantiate']) + self._check_defaults(P.param.l) + + def test_defaults_inst(self): + class P(param.Parameterized): + l = param.HookList() + + p = P() + + check_defaults(p.param.l, label='L', skip=['instantiate']) + self._check_defaults(p.param.l) + + def test_defaults_unbound(self): + l = param.HookList() + + check_defaults(l, label=None, skip=['instantiate']) + self._check_defaults(l) + + def test_default_None(self): + class Q(param.Parameterized): + r = param.HookList(default=[]) # Also check None) + + def test_set_object_constructor(self): + p = self.P(e=[abs]) + self.assertEqual(p.e, [abs]) + + def test_set_object_outside_bounds(self): + p = self.P() + try: + p.l = [abs]*11 + except ValueError: + pass + else: + raise AssertionError("Object set outside range.") + + def test_set_object_wrong_type_foo(self): + p = self.P() + try: + p.e = ['s'] + except ValueError: + pass + else: + raise AssertionError("Object allowed of wrong type.") + + def test_set_object_not_None(self): + p = self.P() + try: + p.e = None + except ValueError: + pass + else: + raise AssertionError("Object set outside range.") diff --git a/tests/API1/testlistselector.py b/tests/API1/testlistselector.py index d3316b5f4..cb382ed6e 100644 --- a/tests/API1/testlistselector.py +++ b/tests/API1/testlistselector.py @@ -1,5 +1,6 @@ import param from . import API1TestCase +from .utils import check_defaults # TODO: I copied the tests from testobjectselector, although I # struggled to understand some of them. Both files should be reviewed # and cleaned up together. @@ -20,6 +21,36 @@ class P(param.Parameterized): self.P = P + def _check_defaults(self, p): + assert p.default is None + assert p.allow_None is None + assert p.objects == [] + assert p.compute_default_fn is None + assert p.check_on_set is False + assert p.names is None + + def test_defaults_class(self): + class P(param.Parameterized): + s = param.ListSelector() + + check_defaults(P.param.s, label='S') + self._check_defaults(P.param.s) + + def test_defaults_inst(self): + class P(param.Parameterized): + s = param.ListSelector() + + p = P() + + check_defaults(p.param.s, label='S') + self._check_defaults(p.param.s) + + def test_defaults_unbound(self): + s = param.ListSelector() + + check_defaults(s, label=None) + self._check_defaults(s) + def test_default_None(self): class Q(param.Parameterized): r = param.ListSelector(default=None) @@ -28,6 +59,15 @@ def test_set_object_constructor(self): p = self.P(e=[6]) self.assertEqual(p.e, [6]) + def test_allow_None_is_None(self): + p = self.P() + assert p.param.e.allow_None is None + assert p.param.f.allow_None is None + assert p.param.g.allow_None is None + assert p.param.h.allow_None is None + assert p.param.i.allow_None is None + + def test_set_object_outside_bounds(self): p = self.P(e=[6]) try: diff --git a/tests/API1/testmultifileselector.py b/tests/API1/testmultifileselector.py new file mode 100644 index 000000000..59e482db3 --- /dev/null +++ b/tests/API1/testmultifileselector.py @@ -0,0 +1,142 @@ +import os +import shutil +import tempfile + +import param + +from . import API1TestCase +from .utils import check_defaults + + +class TestMultiFileSelectorParameters(API1TestCase): + + def setUp(self): + super(TestMultiFileSelectorParameters, self).setUp() + + tmpdir1 = tempfile.mkdtemp() + fa = os.path.join(tmpdir1, 'a.txt') + fb = os.path.join(tmpdir1, 'b.txt') + glob1 = os.path.join(tmpdir1, '*') + open(fa, 'w').close() + open(fb, 'w').close() + tmpdir2 = tempfile.mkdtemp() + fc = os.path.join(tmpdir2, 'c.txt') + fd = os.path.join(tmpdir2, 'd.txt') + glob2 = os.path.join(tmpdir2, '*') + open(fc, 'w').close() + open(fd, 'w').close() + + self.tmpdir1 = tmpdir1 + self.tmpdir2 = tmpdir2 + self.fa = fa + self.fb = fb + self.fc = fc + self.fd = fd + self.glob1 = glob1 + self.glob2 = glob2 + + class P(param.Parameterized): + a = param.MultiFileSelector(path=glob1) + b = param.MultiFileSelector(default=[fa], path=glob1) + + self.P = P + + def tearDown(self): + shutil.rmtree(self.tmpdir1) + shutil.rmtree(self.tmpdir2) + + def _check_defaults(self, p): + assert p.default is None + assert p.allow_None is None + assert p.objects == [] + assert p.compute_default_fn is None + assert p.check_on_set is False + assert p.names is None + assert p.path == '' + + def test_defaults_class(self): + class P(param.Parameterized): + s = param.MultiFileSelector() + + check_defaults(P.param.s, label='S') + self._check_defaults(P.param.s) + + def test_defaults_inst(self): + class P(param.Parameterized): + s = param.MultiFileSelector() + + p = P() + + check_defaults(p.param.s, label='S') + self._check_defaults(p.param.s) + + def test_defaults_unbound(self): + s = param.MultiFileSelector() + + check_defaults(s, label=None) + self._check_defaults(s) + + def test_default_is_None(self): + p = self.P() + assert p.a is None + assert p.param.a.default is None + + def test_default_is_honored(self): + p = self.P() + assert p.b == [self.fa] + assert p.param.b.default ==[self.fa] + + def test_allow_default_None(self): + class P(param.Parameterized): + a = param.MultiFileSelector(default=None) + + def test_objects_auto_set(self): + p = self.P() + assert p.param.a.objects == [self.fa, self.fb] + + def test_default_not_in_glob(self): + with self.assertRaises(ValueError): + class P(param.Parameterized): + a = param.MultiFileSelector(default=['not/in/glob'], path=self.glob1) + + def test_objects_auto_set(self): + p = self.P() + assert sorted(p.param.a.objects) == sorted([self.fa, self.fb]) + + def test_set_object_constructor(self): + p = self.P(a=[self.fb]) + assert p.a == [self.fb] + + def test_set_object_outside_bounds(self): + p = self.P() + with self.assertRaises(ValueError): + p.a = ['/not/in/glob'] + + def test_set_path_and_update(self): + p = self.P() + p.param.b.path = self.glob2 + p.param.b.update() + assert sorted(p.param.b.objects) == sorted([self.fc, self.fd]) + assert sorted(p.param.b.default) == sorted([self.fc, self.fd]) + # Default updated but not the value itself + assert p.b == [self.fa] + + def test_get_range(self): + p = self.P() + r = p.param.a.get_range() + assert r['a.txt'] == self.fa + assert r['b.txt'] == self.fb + p.param.a.path = self.glob2 + p.param.a.update() + r = p.param.a.get_range() + assert r['c.txt'] == self.fc + assert r['d.txt'] == self.fd + + def test_update_file_removed(self): + p = self.P() + assert p.param.b.objects == [self.fa, self.fb] + assert p.param.b.default == [self.fa] + os.remove(self.fa) + p.param.b.update() + assert p.param.b.objects == [self.fb] + assert p.param.b.default == [self.fb] diff --git a/tests/API1/testnumberparameter.py b/tests/API1/testnumberparameter.py index 62a28fd07..85f02c25e 100644 --- a/tests/API1/testnumberparameter.py +++ b/tests/API1/testnumberparameter.py @@ -2,54 +2,575 @@ Unit test for Number parameters and their subclasses. """ import param -import datetime as dt from . import API1TestCase +from .utils import check_defaults class TestNumberParameters(API1TestCase): - def test_initialization_without_step_class(self): - class Q(param.Parameterized): - q = param.Number(default=1) + def setUp(self): + super(TestNumberParameters, self).setUp() + class P(param.Parameterized): + b = param.Number(allow_None=False) + c = param.Number(default=1, allow_None=True) + d = param.Number(default=None) + e = param.Number(default=1) + f = param.Number(default=1, step=0.5) + g = param.Number(default=lambda: 1) + h = param.Number(default=1, bounds=(0, 2)) + i = param.Number(bounds=(-1, 1)) + j = param.Number(bounds=(-1, 1), inclusive_bounds=(False, True)) + k = param.Number(bounds=(-1, 1), inclusive_bounds=(True, False)) + l = param.Number(bounds=(-1, 1), inclusive_bounds=(False, False)) + m = param.Number(bounds=(-1, None)) + n = param.Number(bounds=(None, 1)) - self.assertEqual(Q.param['q'].step, None) + self.P = P + + def _check_defaults(self, p): + assert p.default == 0.0 + assert p.allow_None is False + assert p.bounds is None + assert p.softbounds is None + assert p.inclusive_bounds == (True, True) + assert p.step is None + + def test_defaults_class(self): + class A(param.Parameterized): + n = param.Number() + + check_defaults(A.param.n, label='N') + self._check_defaults(A.param.n) + + def test_defaults_inst(self): + class A(param.Parameterized): + n = param.Number() + + a = A() + + check_defaults(a.param.n, label='N') + self._check_defaults(a.param.n) + + def test_defaults_unbound(self): + n = param.Number() + + check_defaults(n, label=None) + self._check_defaults(n) + + def test_allow_None_class(self): + self.P.c = None + assert self.P.c is None + self.P.d = None + assert self.P.d is None + + exception = "Parameter 'b' only takes numeric values, not type <(class|type) 'NoneType'>." + with self.assertRaisesRegex(ValueError, exception): + self.P.b = None + + def test_allow_None_inst(self): + p = self.P() + p.c = None + assert p.c is None + p.d = None + assert p.d is None + + exception = "Parameter 'b' only takes numeric values, not type <(class|type) 'NoneType'>." + with self.assertRaisesRegex(ValueError, exception): + p.b = None + + def test_initialization_without_step_class(self): + self.assertEqual(self.P.param['e'].step, None) def test_initialization_with_step_class(self): + self.assertEqual(self.P.param['f'].step, 0.5) + + def test_initialization_without_step_instance(self): + p = self.P() + self.assertEqual(p.param['e'].step, None) + + def test_initialization_with_step_instance(self): + p = self.P() + self.assertEqual(p.param['f'].step, 0.5) + + def test_step_invalid_type_number_parameter(self): + exception = "Step can only be None or a numeric value" + with self.assertRaisesRegex(ValueError, exception): + param.Number(step='invalid value') + + def test_outside_bounds(self): + exception = "Parameter 'h' must be at most 2, not 10." + with self.assertRaisesRegex(ValueError, exception): + self.P.h = 10 + + p = self.P() + + with self.assertRaisesRegex(ValueError, exception): + p.h = 10 + + def test_unbounded_side_class(self): + self.P.m = 10 + assert self.P.m == 10 + + exception = "Parameter 'm' must be at least -1, not -10." + with self.assertRaisesRegex(ValueError, exception): + self.P.m = -10 + + self.P.n = -10 + assert self.P.n == -10 + + exception = "Parameter 'n' must be at most 1, not 10." + with self.assertRaisesRegex(ValueError, exception): + self.P.n = 10 + + def test_unbounded_side_inst(self): + p = self.P() + + p.m = 10 + assert p.m == 10 + + exception = "Parameter 'm' must be at least -1, not -10." + with self.assertRaisesRegex(ValueError, exception): + p.m = -10 + + p.n = -10 + assert p.n == -10 + + exception = "Parameter 'n' must be at most 1, not 10." + with self.assertRaisesRegex(ValueError, exception): + p.n = 10 + + def test_inclusive_bounds_no_error_class(self): + self.P.i = -1 + assert self.P.i == -1 + self.P.i = 1 + assert self.P.i == 1 + + self.P.j = 1 + assert self.P.j == 1 + + self.P.k = -1 + assert self.P.k == -1 + + def test_inclusive_bounds_no_error_inst(self): + p = self.P() + p.i = -1 + assert p.i == -1 + p.i = 1 + assert p.i == 1 + + p.j = 1 + assert p.j == 1 + + p.k = -1 + assert p.k == -1 + + def test_inclusive_bounds_error_on_bounds(self): + p = self.P() + exception = "Parameter 'j' must be greater than -1, not -1." + with self.assertRaisesRegex(ValueError, exception): + self.P.j = -1 + with self.assertRaisesRegex(ValueError, exception): + p.j = -1 + + exception = "Parameter 'k' must be less than 1, not 1." + with self.assertRaisesRegex(ValueError, exception): + self.P.k = 1 + with self.assertRaisesRegex(ValueError, exception): + p.k = 1 + + exception = "Parameter 'l' must be greater than -1, not -1." + with self.assertRaisesRegex(ValueError, exception): + self.P.l = -1 + with self.assertRaisesRegex(ValueError, exception): + p.l = -1 + exception = "Parameter 'l' must be less than 1, not 1." + with self.assertRaisesRegex(ValueError, exception): + self.P.l = 1 + with self.assertRaisesRegex(ValueError, exception): + p.l = 1 + + def test_inclusive_bounds_error_on_bounds_post(self): + exception = "Parameter None must be greater than -1, not -1." + with self.assertRaisesRegex(ValueError, exception): + class P(param.Parameterized): + j = param.Number(default=-1, bounds=(-1, 1), inclusive_bounds=(False, True)) + + exception = "Parameter None must be less than 1, not 1" + with self.assertRaisesRegex(ValueError, exception): + class P(param.Parameterized): + j = param.Number(default=1, bounds=(-1, 1), inclusive_bounds=(True, False)) + + exception = "Parameter None must be greater than -1, not -1." + with self.assertRaisesRegex(ValueError, exception): + class P(param.Parameterized): + j = param.Number(default=-1, bounds=(-1, 1), inclusive_bounds=(False, False)) + + exception = "Parameter None must be less than 1, not 1." + with self.assertRaisesRegex(ValueError, exception): + class P(param.Parameterized): + j = param.Number(default=1, bounds=(-1, 1), inclusive_bounds=(False, False)) + + def test_invalid_default_for_bounds(self): + exception = "Parameter None must be at least 10, not 0.0." + with self.assertRaisesRegex(ValueError, exception): + class P(param.Parameterized): + n = param.Number(bounds=(10, 20)) + + def test_callable(self): + assert self.P.g == 1 + p = self.P() + assert p.g == 1 + + def test_callable_wrong_type(self): class Q(param.Parameterized): - q = param.Number(default=1, step=0.5) + q = param.Number(default=lambda: 'test') - self.assertEqual(Q.param['q'].step, 0.5) + exception = "Parameter 'q' only takes numeric values, not type <(class|type) 'str'>." + with self.assertRaisesRegex(ValueError, exception): + Q.q - def test_initialization_without_step_instance(self): + q = Q() + + with self.assertRaisesRegex(ValueError, exception): + q.q + + def test_callable_outside_bounds(self): class Q(param.Parameterized): - q = param.Number(default=1) + q = param.Number(default=lambda: 2, bounds=(0, 1)) - qobj = Q() - self.assertEqual(qobj.param['q'].step, None) + exception = "Parameter 'q' must be at most 1, not 2." + with self.assertRaisesRegex(ValueError, exception): + Q.q - def test_initialization_with_step_instance(self): + q = Q() + + with self.assertRaisesRegex(ValueError, exception): + q.q + + def test_crop_to_bounds(self): + p = self.P() + + # when allow_None is True + assert p.param.d.crop_to_bounds(None) is None + + # no bounds + assert p.param.e.crop_to_bounds(10000) == 10000 + + # with concrete bounds + assert p.param.h.crop_to_bounds(10) == 2 + assert p.param.h.crop_to_bounds(-10) == 0 + + # return default if non numerical + assert p.param.e.crop_to_bounds('test') == 1 + + # Unbound + assert p.param.m.crop_to_bounds(10) == 10 + assert p.param.n.crop_to_bounds(-10) == -10 + + +class TestIntegerParameters(API1TestCase): + + def setUp(self): + super(TestIntegerParameters, self).setUp() + class P(param.Parameterized): + b = param.Integer(allow_None=False) + c = param.Integer(default=1, allow_None=True) + d = param.Integer(default=None) + e = param.Integer(default=1) + f = param.Integer(default=1, step=1) + g = param.Integer(default=lambda: 1) + h = param.Integer(default=1, bounds=(0, 2)) + i = param.Integer(bounds=(-1, 1)) + j = param.Integer(bounds=(-1, 1), inclusive_bounds=(False, True)) + k = param.Integer(bounds=(-1, 1), inclusive_bounds=(True, False)) + l = param.Integer(bounds=(-1, 1), inclusive_bounds=(False, False)) + m = param.Integer(bounds=(-1, None)) + n = param.Integer(bounds=(None, 1)) + + self.P = P + + def _check_defaults(self, p): + assert isinstance(p.default, int) + assert p.default == 0 + assert p.allow_None is False + assert p.bounds is None + assert p.softbounds is None + assert p.inclusive_bounds == (True, True) + assert p.step is None + + def test_defaults_class(self): + class A(param.Parameterized): + n = param.Integer() + + check_defaults(A.param.n, label='N') + self._check_defaults(A.param.n) + + def test_defaults_inst(self): + class A(param.Parameterized): + n = param.Integer() + + a = A() + + check_defaults(a.param.n, label='N') + self._check_defaults(a.param.n) + + def test_defaults_unbound(self): + n = param.Integer() + + check_defaults(n, label=None) + self._check_defaults(n) + + def test_allow_None_class(self): + self.P.c = None + assert self.P.c is None + self.P.d = None + assert self.P.d is None + + exception = "Integer parameter 'b' must be an integer, not type <(class|type) 'NoneType'>." + with self.assertRaisesRegex(ValueError, exception): + self.P.b = None + + def test_allow_None_inst(self): + p = self.P() + p.c = None + assert p.c is None + p.d = None + assert p.d is None + + exception = "Integer parameter 'b' must be an integer, not type <(class|type) 'NoneType'>." + with self.assertRaisesRegex(ValueError, exception): + p.b = None + + def test_initialization_without_step_class(self): class Q(param.Parameterized): - q = param.Number(default=1, step=0.5) + q = param.Integer(default=1) - qobj = Q() - self.assertEqual(qobj.param['q'].step, 0.5) + self.assertEqual(Q.param['q'].step, None) + + + def test_initialization_without_step_class(self): + self.assertEqual(self.P.param['e'].step, None) + + def test_initialization_with_step_class(self): + self.assertEqual(self.P.param['f'].step, 1) + + def test_initialization_without_step_instance(self): + p = self.P() + self.assertEqual(p.param['e'].step, None) + + def test_initialization_with_step_instance(self): + p = self.P() + self.assertEqual(p.param['f'].step, 1) def test_step_invalid_type_number_parameter(self): - exception = "Step can only be None or a numeric value" + exception = "Step can only be None or an integer value" with self.assertRaisesRegex(ValueError, exception): - param.Number(step='invalid value') + param.Integer(step='invalid value') def test_step_invalid_type_integer_parameter(self): exception = "Step can only be None or an integer value" with self.assertRaisesRegex(ValueError, exception): param.Integer(step=3.4) - def test_step_invalid_type_datetime_parameter(self): - exception = "Step can only be None, a datetime or datetime type" + def test_outside_bounds(self): + exception = "Parameter 'h' must be at most 2, not 10." with self.assertRaisesRegex(ValueError, exception): - param.Date(dt.datetime(2017,2,27), step=3.2) + self.P.h = 10 - def test_step_invalid_type_date_parameter(self): - exception = "Step can only be None or a date type" + p = self.P() + + with self.assertRaisesRegex(ValueError, exception): + p.h = 10 + + def test_unbounded_side_class(self): + self.P.m = 10 + assert self.P.m == 10 + + exception = "Parameter 'm' must be at least -1, not -10." + with self.assertRaisesRegex(ValueError, exception): + self.P.m = -10 + + self.P.n = -10 + assert self.P.n == -10 + + exception = "Parameter 'n' must be at most 1, not 10." + with self.assertRaisesRegex(ValueError, exception): + self.P.n = 10 + + def test_unbounded_side_inst(self): + p = self.P() + + p.m = 10 + assert p.m == 10 + + exception = "Parameter 'm' must be at least -1, not -10." + with self.assertRaisesRegex(ValueError, exception): + p.m = -10 + + p.n = -10 + assert p.n == -10 + + exception = "Parameter 'n' must be at most 1, not 10." + with self.assertRaisesRegex(ValueError, exception): + p.n = 10 + + def test_inclusive_bounds_no_error_class(self): + self.P.i = -1 + assert self.P.i == -1 + self.P.i = 1 + assert self.P.i == 1 + + self.P.j = 1 + assert self.P.j == 1 + + self.P.k = -1 + assert self.P.k == -1 + + def test_inclusive_bounds_no_error_inst(self): + p = self.P() + p.i = -1 + assert p.i == -1 + p.i = 1 + assert p.i == 1 + + p.j = 1 + assert p.j == 1 + + p.k = -1 + assert p.k == -1 + + def test_inclusive_bounds_error_on_bounds(self): + p = self.P() + exception = "Parameter 'j' must be greater than -1, not -1." + with self.assertRaisesRegex(ValueError, exception): + self.P.j = -1 with self.assertRaisesRegex(ValueError, exception): - param.CalendarDate(dt.date(2017,2,27), step=3.2) + p.j = -1 + + exception = "Parameter 'k' must be less than 1, not 1." + with self.assertRaisesRegex(ValueError, exception): + self.P.k = 1 + with self.assertRaisesRegex(ValueError, exception): + p.k = 1 + + exception = "Parameter 'l' must be greater than -1, not -1." + with self.assertRaisesRegex(ValueError, exception): + self.P.l = -1 + with self.assertRaisesRegex(ValueError, exception): + p.l = -1 + exception = "Parameter 'l' must be less than 1, not 1." + with self.assertRaisesRegex(ValueError, exception): + self.P.l = 1 + with self.assertRaisesRegex(ValueError, exception): + p.l = 1 + + def test_inclusive_bounds_error_on_bounds_post(self): + exception = "Parameter None must be greater than -1, not -1." + with self.assertRaisesRegex(ValueError, exception): + class P(param.Parameterized): + j = param.Integer(default=-1, bounds=(-1, 1), inclusive_bounds=(False, True)) + + exception = "Parameter None must be less than 1, not 1" + with self.assertRaisesRegex(ValueError, exception): + class P(param.Parameterized): + j = param.Integer(default=1, bounds=(-1, 1), inclusive_bounds=(True, False)) + + exception = "Parameter None must be greater than -1, not -1." + with self.assertRaisesRegex(ValueError, exception): + class P(param.Parameterized): + j = param.Integer(default=-1, bounds=(-1, 1), inclusive_bounds=(False, False)) + + exception = "Parameter None must be less than 1, not 1." + with self.assertRaisesRegex(ValueError, exception): + class P(param.Parameterized): + j = param.Integer(default=1, bounds=(-1, 1), inclusive_bounds=(False, False)) + + def test_invalid_default_for_bounds(self): + exception = "Parameter None must be at least 10, not 0." + with self.assertRaisesRegex(ValueError, exception): + class P(param.Parameterized): + n = param.Integer(bounds=(10, 20)) + + def test_callable(self): + assert self.P.g == 1 + p = self.P() + assert p.g == 1 + + def test_callable_wrong_type(self): + class Q(param.Parameterized): + q = param.Integer(default=lambda: 'test') + + exception = "Integer parameter 'q' must be an integer, not type <(class|type) 'str'>." + with self.assertRaisesRegex(ValueError, exception): + Q.q + + q = Q() + + with self.assertRaisesRegex(ValueError, exception): + q.q + + def test_callable_outside_bounds(self): + class Q(param.Parameterized): + q = param.Integer(default=lambda: 2, bounds=(0, 1)) + + exception = "Parameter 'q' must be at most 1, not 2." + with self.assertRaisesRegex(ValueError, exception): + Q.q + + q = Q() + + with self.assertRaisesRegex(ValueError, exception): + q.q + + def test_crop_to_bounds(self): + p = self.P() + + # when allow_None is True + assert p.param.d.crop_to_bounds(None) is None + + # no bounds + assert p.param.e.crop_to_bounds(10000) == 10000 + + # with concrete bounds + assert p.param.h.crop_to_bounds(10) == 2 + assert p.param.h.crop_to_bounds(-10) == 0 + + # return default if non numerical + assert p.param.e.crop_to_bounds('test') == 1 + + # Unbound + assert p.param.m.crop_to_bounds(10) == 10 + assert p.param.n.crop_to_bounds(-10) == -10 + + +class TestMagnitudeParameters(API1TestCase): + + def _check_defaults(self, p): + assert p.default == 1.0 + assert p.allow_None is False + assert p.bounds == (0.0, 1.0) + assert p.softbounds is None + assert p.inclusive_bounds == (True, True) + assert p.step is None + + def test_defaults_class(self): + class A(param.Parameterized): + n = param.Magnitude() + + check_defaults(A.param.n, label='N') + self._check_defaults(A.param.n) + + def test_defaults_inst(self): + class A(param.Parameterized): + n = param.Magnitude() + + a = A() + + check_defaults(a.param.n, label='N') + self._check_defaults(a.param.n) + + def test_defaults_unbound(self): + n = param.Magnitude() + + check_defaults(n, label=None) + self._check_defaults(n) diff --git a/tests/API1/testnumpy.py b/tests/API1/testnumpy.py index 98b3d1f0a..f351bbcd7 100644 --- a/tests/API1/testnumpy.py +++ b/tests/API1/testnumpy.py @@ -6,6 +6,7 @@ import param from . import API1TestCase +from .utils import check_defaults try: import numpy @@ -24,6 +25,36 @@ def _is_array_and_equal(test,ref): # TODO: incomplete class TestNumpy(API1TestCase): + + def _check_defaults(self, p): + assert p.default is None + assert p.allow_None is True + assert p.instantiate is True + assert p.is_instance is True + assert p.class_ == numpy.ndarray + + def test_defaults_class(self): + class P(param.Parameterized): + s = param.Array() + + check_defaults(P.param.s, label='S', skip=['instantiate']) + self._check_defaults(P.param.s) + + def test_defaults_inst(self): + class P(param.Parameterized): + s = param.Array() + + p = P() + + check_defaults(p.param.s, label='S', skip=['instantiate']) + self._check_defaults(p.param.s) + + def test_defaults_unbound(self): + s = param.Array() + + check_defaults(s, label=None, skip=['instantiate']) + self._check_defaults(s) + def test_array_param(self): class Z(param.Parameterized): z = param.Array(default=numpy.array([1])) diff --git a/tests/API1/testobjectselector.py b/tests/API1/testobjectselector.py index a4a3c8c83..cd6c086f2 100644 --- a/tests/API1/testobjectselector.py +++ b/tests/API1/testobjectselector.py @@ -7,6 +7,7 @@ import param from . import API1TestCase +from .utils import check_defaults from collections import OrderedDict @@ -28,10 +29,50 @@ class P(param.Parameterized): self.P = P + def _check_defaults(self, p): + assert p.default is None + assert p.allow_None is None + assert p.objects == [] + assert p.compute_default_fn is None + assert p.check_on_set is False + assert p.names is None + + def test_defaults_class(self): + class P(param.Parameterized): + s = param.ObjectSelector() + + check_defaults(P.param.s, label='S') + self._check_defaults(P.param.s) + + def test_defaults_inst(self): + class P(param.Parameterized): + s = param.ObjectSelector() + + p = P() + + check_defaults(p.param.s, label='S') + self._check_defaults(p.param.s) + + def test_defaults_unbound(self): + s = param.ObjectSelector() + + check_defaults(s, label=None) + self._check_defaults(s) + def test_set_object_constructor(self): p = self.P(e=6) self.assertEqual(p.e, 6) + def test_allow_None_is_None(self): + p = self.P() + assert p.param.e.allow_None is None + assert p.param.f.allow_None is None + assert p.param.g.allow_None is None + assert p.param.h.allow_None is None + assert p.param.i.allow_None is None + assert p.param.s.allow_None is None + assert p.param.d.allow_None is None + def test_get_range_list(self): r = self.P.param.params("g").get_range() self.assertEqual(r['7'],7) diff --git a/tests/API1/testpandas.py b/tests/API1/testpandas.py index e31fc9642..aa24d1057 100644 --- a/tests/API1/testpandas.py +++ b/tests/API1/testpandas.py @@ -6,6 +6,7 @@ import param from . import API1TestCase +from .utils import check_defaults try: import pandas @@ -18,6 +19,38 @@ class TestDataFrame(API1TestCase): + def _check_defaults(self, p): + assert p.default is None + assert p.allow_None is True + assert p.instantiate is True + assert p.is_instance is True + assert p.rows is None + assert p.columns is None + assert p.ordered is None + assert p.class_ == pandas.DataFrame + + def test_defaults_class(self): + class P(param.Parameterized): + s = param.DataFrame() + + check_defaults(P.param.s, label='S', skip=['instantiate']) + self._check_defaults(P.param.s) + + def test_defaults_inst(self): + class P(param.Parameterized): + s = param.DataFrame() + + p = P() + + check_defaults(p.param.s, label='S', skip=['instantiate']) + self._check_defaults(p.param.s) + + def test_defaults_unbound(self): + s = param.DataFrame() + + check_defaults(s, label=None, skip=['instantiate']) + self._check_defaults(s) + def test_dataframe_positional_argument(self): valid_df = pandas.DataFrame({'a':[1,2], 'b':[2,3], 'c':[4,5]}, columns=['b', 'a', 'c']) @@ -163,6 +196,36 @@ class Test(param.Parameterized): class TestSeries(API1TestCase): + def _check_defaults(self, p): + assert p.default is None + assert p.allow_None is True + assert p.instantiate is True + assert p.is_instance is True + assert p.rows is None + assert p.class_ == pandas.Series + + def test_defaults_class(self): + class P(param.Parameterized): + s = param.Series() + + check_defaults(P.param.s, label='S', skip=['instantiate']) + self._check_defaults(P.param.s) + + def test_defaults_inst(self): + class P(param.Parameterized): + s = param.Series() + + p = P() + + check_defaults(p.param.s, label='S', skip=['instantiate']) + self._check_defaults(p.param.s) + + def test_defaults_unbound(self): + s = param.Series() + + check_defaults(s, label=None, skip=['instantiate']) + self._check_defaults(s) + def test_series_positional_argument(self): valid_series = pandas.Series([1,2]) class Test(param.Parameterized): diff --git a/tests/API1/testpathparam.py b/tests/API1/testpathparam.py new file mode 100644 index 000000000..637eaadc5 --- /dev/null +++ b/tests/API1/testpathparam.py @@ -0,0 +1,251 @@ +import os +import shutil +import tempfile + +import param + +from . import API1TestCase +from .utils import check_defaults + + +class TestPathParameters(API1TestCase): + + def setUp(self): + super(TestPathParameters, self).setUp() + + tmpdir1 = tempfile.mkdtemp() + fa = os.path.join(tmpdir1, 'a.txt') + fb = os.path.join(tmpdir1, 'b.txt') + open(fa, 'w').close() + open(fb, 'w').close() + + self.tmpdir1 = tmpdir1 + self.fa = fa + self.fb = fb + + class P(param.Parameterized): + a = param.Path() + b = param.Path(self.fb) + c = param.Path('a.txt', search_paths=[tmpdir1]) + + self.P = P + + def tearDown(self): + shutil.rmtree(self.tmpdir1) + + def _check_defaults(self, p): + assert p.default is None + assert p.allow_None is True + assert p.search_paths == [] + + def test_defaults_class(self): + class P(param.Parameterized): + p = param.Path() + + check_defaults(P.param.p, label='P') + self._check_defaults(P.param.p) + + def test_defaults_inst(self): + class P(param.Parameterized): + p = param.Path() + + p = P() + + check_defaults(p.param.p, label='P') + self._check_defaults(p.param.p) + + def test_defaults_unbound(self): + p = param.Path() + + check_defaults(p, label=None) + self._check_defaults(p) + + def test_no_path_class(self): + assert self.P.a is None + + def test_no_path_class(self): + p = self.P() + assert p.a is None + + def test_inst_with_path(self): + p = self.P(a=self.fa) + assert isinstance(p.a, str) + assert os.path.isfile(p.a) + assert os.path.isabs(p.a) + assert p.a == self.fa + + def test_set_to_None_allowed(self): + p = self.P() + + assert p.param.b.allow_None is False + # This should probably raise an error (#708) + p.b = None + + def test_search_paths(self): + p = self.P() + + assert isinstance(p.c, str) + assert os.path.isfile(p.c) + assert os.path.isabs(p.c) + assert p.c == self.fa + + +class TestFilenameParameters(API1TestCase): + + def setUp(self): + super(TestFilenameParameters, self).setUp() + + tmpdir1 = tempfile.mkdtemp() + fa = os.path.join(tmpdir1, 'a.txt') + fb = os.path.join(tmpdir1, 'b.txt') + open(fa, 'w').close() + open(fb, 'w').close() + + self.tmpdir1 = tmpdir1 + self.fa = fa + self.fb = fb + + class P(param.Parameterized): + a = param.Filename() + b = param.Filename(self.fb) + c = param.Filename('a.txt', search_paths=[tmpdir1]) + + self.P = P + + def tearDown(self): + shutil.rmtree(self.tmpdir1) + + def _check_defaults(self, p): + assert p.default is None + assert p.allow_None is True + assert p.search_paths == [] + + def test_defaults_class(self): + class P(param.Parameterized): + p = param.Filename() + + check_defaults(P.param.p, label='P') + self._check_defaults(P.param.p) + + def test_defaults_inst(self): + class P(param.Parameterized): + p = param.Filename() + + p = P() + + check_defaults(p.param.p, label='P') + self._check_defaults(p.param.p) + + def test_defaults_unbound(self): + p = param.Filename() + + check_defaults(p, label=None) + self._check_defaults(p) + + def test_no_path_class(self): + assert self.P.a is None + + def test_no_path_class(self): + p = self.P() + assert p.a is None + + def test_inst_with_path(self): + p = self.P(a=self.fa) + assert isinstance(p.a, str) + assert os.path.isfile(p.a) + assert os.path.isabs(p.a) + assert p.a == self.fa + + def test_set_to_None_allowed(self): + p = self.P() + + assert p.param.b.allow_None is False + # This should probably raise an error (#708) + p.b = None + + def test_search_paths(self): + p = self.P() + + assert isinstance(p.c, str) + assert os.path.isfile(p.c) + assert os.path.isabs(p.c) + assert p.c == self.fa + + +class TestFoldernameParameters(API1TestCase): + + def setUp(self): + super(TestFoldernameParameters, self).setUp() + + tmpdir1 = tempfile.mkdtemp() + da = os.path.join(tmpdir1, 'da') + os.mkdir(da) + + self.tmpdir1 = tmpdir1 + self.da = da + + class P(param.Parameterized): + a = param.Foldername() + b = param.Foldername(tmpdir1) + c = param.Path('da', search_paths=[tmpdir1]) + + self.P = P + + def tearDown(self): + shutil.rmtree(self.tmpdir1) + + def _check_defaults(self, p): + assert p.default is None + assert p.allow_None is True + assert p.search_paths == [] + + def test_defaults_class(self): + class P(param.Parameterized): + p = param.Foldername() + + check_defaults(P.param.p, label='P') + self._check_defaults(P.param.p) + + def test_defaults_inst(self): + class P(param.Parameterized): + p = param.Foldername() + + p = P() + + check_defaults(p.param.p, label='P') + self._check_defaults(p.param.p) + + def test_defaults_unbound(self): + p = param.Foldername() + + check_defaults(p, label=None) + self._check_defaults(p) + + def test_no_path_class(self): + assert self.P.a is None + + def test_no_path_class(self): + p = self.P() + assert p.a is None + + def test_inst_with_path(self): + p = self.P(a=self.da) + assert isinstance(p.a, str) + assert os.path.isdir(p.a) + assert os.path.isabs(p.a) + assert p.a == self.da + + def test_set_to_None_allowed(self): + p = self.P() + + assert p.param.b.allow_None is False + # This should probably raise an error (#708) + p.b = None + + def test_search_paths(self): + p = self.P() + + assert isinstance(p.c, str) + assert os.path.isdir(p.c) + assert os.path.isabs(p.c) + assert p.c == self.da diff --git a/tests/API1/testrangeparameter.py b/tests/API1/testrangeparameter.py index 45ecaebf5..9e98547bc 100644 --- a/tests/API1/testrangeparameter.py +++ b/tests/API1/testrangeparameter.py @@ -7,6 +7,111 @@ class TestRangeParameters(API1TestCase): + def setUp(self): + super(TestRangeParameters, self).setUp() + class P(param.Parameterized): + e = param.Range() + f = param.Range(default=(0, 1), allow_None=True) + g = param.Range(default=(0, 1)) + + self.P = P + + def _check_defaults(self, p): + assert p.default is None + assert p.allow_None is True + assert p.length == 2 + assert p.bounds is None + assert p.softbounds is None + assert p.inclusive_bounds == (True, True) + assert p.step is None + + def test_defaults_class(self): + class P(param.Parameterized): + r = param.Range() + + self._check_defaults(P.param.r) + + def test_defaults_inst(self): + class P(param.Parameterized): + r = param.Range() + + p = P() + + self._check_defaults(p.param.r) + + def test_defaults_unbound(self): + r = param.Range() + + self._check_defaults(r) + + def test_set_object_constructor(self): + p = self.P(e=(0, 20)) + assert p.e == (0, 20) + + def test_raise_not_2_tuple(self): + p = self.P() + msg = r"Tuple parameter 'e' is not of the correct length \(3 instead of 2\)" + with self.assertRaisesRegex(ValueError, msg): + p.e = (1, 2, 3) + + def test_raise_if_value_bad_length_constructor(self): + msg = r"Tuple parameter 'e' is not of the correct length \(3 instead of 2\)" + with self.assertRaisesRegex(ValueError, msg): + self.P(e=(1, 1, 1)) + + def test_raise_if_value_bad_length_setattr(self): + p = self.P() + msg = r"Tuple parameter 'e' is not of the correct length \(3 instead of 2\)" + with self.assertRaisesRegex(ValueError, msg): + p.e = (1, 1, 1) + + def test_raise_if_default_is_None_and_no_length(self): + msg = "length must be specified if no default is supplied" + with self.assertRaisesRegex(ValueError, msg): + class P(param.Parameterized): + t = param.NumericTuple(default=None) + + def test_bad_type(self): + msg = r"Tuple parameter 'e' only takes a tuple value, not <(class|type) 'str'>." + + with self.assertRaisesRegex(ValueError, msg): + self.P.e = 'test' + + with self.assertRaisesRegex(ValueError, msg): + self.P(e='test') + + p = self.P() + + with self.assertRaisesRegex(ValueError, msg): + p.e = 'test' + + msg = r"Tuple parameter None only takes a tuple value, not <(class|type) 'str'>." + with self.assertRaisesRegex(ValueError, msg): + class P(param.Parameterized): + e = param.NumericTuple(default='test') + + def test_support_allow_None_True(self): + p = self.P() + assert p.f == (0, 1) + p.f = None + assert p.f is None + + class P(param.Parameterized): + f = param.Range(default=(0, 1), allow_None=True) + + P.f = None + assert P.f is None + + def test_support_allow_None_False(self): + p = self.P() + msg = "Tuple parameter 'g' only takes a tuple value, not <(class|type) 'NoneType'>." + with self.assertRaisesRegex(ValueError, msg): + p.g = None + + msg = "Tuple parameter 'g' only takes a tuple value, not <(class|type) 'NoneType'>." + with self.assertRaisesRegex(ValueError, msg): + self.P.g = None + def test_initialization_out_of_bounds(self): try: class Q(param.Parameterized): diff --git a/tests/API1/testselector.py b/tests/API1/testselector.py index dd6d85bdf..bffae371c 100644 --- a/tests/API1/testselector.py +++ b/tests/API1/testselector.py @@ -7,6 +7,7 @@ import param from . import API1TestCase +from.utils import check_defaults from collections import OrderedDict @@ -28,10 +29,50 @@ class P(param.Parameterized): self.P = P + def _check_defaults(self, p): + assert p.default is None + assert p.allow_None is None + assert p.objects == [] + assert p.compute_default_fn is None + assert p.check_on_set is False + assert p.names is None + + def test_defaults_class(self): + class P(param.Parameterized): + s = param.Selector() + + check_defaults(P.param.s, label='S') + self._check_defaults(P.param.s) + + def test_defaults_inst(self): + class P(param.Parameterized): + s = param.Selector() + + p = P() + + check_defaults(p.param.s, label='S') + self._check_defaults(p.param.s) + + def test_defaults_unbound(self): + s = param.Selector() + + check_defaults(s, label=None) + self._check_defaults(s) + def test_set_object_constructor(self): p = self.P(e=6) self.assertEqual(p.e, 6) + def test_allow_None_is_None(self): + p = self.P() + assert p.param.e.allow_None is None + assert p.param.f.allow_None is None + assert p.param.g.allow_None is None + assert p.param.h.allow_None is None + assert p.param.i.allow_None is None + assert p.param.s.allow_None is None + assert p.param.d.allow_None is None + def test_get_range_list(self): r = self.P.param.params("g").get_range() self.assertEqual(r['7'],7) diff --git a/tests/API1/teststringparam.py b/tests/API1/teststringparam.py index 06864c3e8..ce12561fa 100644 --- a/tests/API1/teststringparam.py +++ b/tests/API1/teststringparam.py @@ -4,6 +4,7 @@ import sys from . import API1TestCase +from .utils import check_defaults import param @@ -12,6 +13,33 @@ class TestStringParameters(API1TestCase): + def _check_defaults(self, p): + assert p.default == '' + assert p.allow_None is False + assert p.regex is None + + def test_defaults_class(self): + class A(param.Parameterized): + s = param.String() + + check_defaults(A.param.s, label='S') + self._check_defaults(A.param.s) + + def test_defaults_inst(self): + class A(param.Parameterized): + s = param.String() + + a = A() + + check_defaults(a.param.s, label='S') + self._check_defaults(a.param.s) + + def test_defaults_unbound(self): + s = param.String() + + check_defaults(s, label=None) + self._check_defaults(s) + def test_regex_ok(self): class A(param.Parameterized): s = param.String('0.0.0.0', ip_regex) diff --git a/tests/API1/testtupleparam.py b/tests/API1/testtupleparam.py new file mode 100644 index 000000000..33589d6e3 --- /dev/null +++ b/tests/API1/testtupleparam.py @@ -0,0 +1,374 @@ +from . import API1TestCase +from .utils import check_defaults + +import param +import pytest + +try: + import numpy as np +except: + np = None + + +class TestTupleParameters(API1TestCase): + + def setUp(self): + super(TestTupleParameters, self).setUp() + class P(param.Parameterized): + e = param.Tuple(default=(1, 1)) + f = param.Tuple(default=(0, 0, 0)) + g = param.Tuple(default=None, length=3) + h = param.Tuple(length=2, allow_None=True) + + self.P = P + + def _check_defaults(self, p): + assert p.default == (0, 0) + assert p.length == 2 + assert p.allow_None is False + + def test_defaults_class(self): + class P(param.Parameterized): + t = param.Tuple() + + check_defaults(P.param.t, label='T') + self._check_defaults(P.param.t) + + def test_defaults_inst(self): + class P(param.Parameterized): + t = param.Tuple() + + p = P() + + check_defaults(p.param.t, label='T') + self._check_defaults(p.param.t) + + def test_defaults_unbound(self): + t = param.Tuple() + + check_defaults(t, label=None) + self._check_defaults(t) + + def test_set_object_constructor(self): + p = self.P(e=(2, 2)) + self.assertEqual(p.e, (2, 2)) + + def test_length_inferred_from_default(self): + p = self.P() + assert p.param.f.length == 3 + + def test_raise_if_value_bad_length_constructor(self): + msg = r"Tuple parameter 'e' is not of the correct length \(3 instead of 2\)" + with self.assertRaisesRegex(ValueError, msg): + self.P(e=(1, 1, 'extra')) + + def test_raise_if_value_bad_length_setattr(self): + p = self.P() + msg = r"Tuple parameter 'e' is not of the correct length \(3 instead of 2\)" + with self.assertRaisesRegex(ValueError, msg): + p.e = (1, 1, 'extra') + + def test_raise_if_default_is_None_and_no_length(self): + msg = "length must be specified if no default is supplied" + with self.assertRaisesRegex(ValueError, msg): + class P(param.Parameterized): + t = param.Tuple(default=None) + + def test_None_default(self): + p = self.P() + assert p.g is None + assert p.param.g.length == 3 + assert p.param.g.allow_None + + def test_raise_if_default_is_None_and_bad_length(self): + msg = r"Tuple parameter 'g' is not of the correct length \(2 instead of 3\)." + with self.assertRaisesRegex(ValueError, msg): + p = self.P(g=(0, 0)) + + p = self.P() + with self.assertRaisesRegex(ValueError, msg): + p.g = (0, 0) + + def test_bad_type(self): + msg = r"Tuple parameter 'e' only takes a tuple value, not <(class|type) 'str'>." + + with self.assertRaisesRegex(ValueError, msg): + self.P.e = 'test' + + with self.assertRaisesRegex(ValueError, msg): + self.P(e='test') + + p = self.P() + + with self.assertRaisesRegex(ValueError, msg): + p.e = 'test' + + msg = r"Tuple parameter None only takes a tuple value, not <(class|type) 'str'>." + with self.assertRaisesRegex(ValueError, msg): + class P(param.Parameterized): + e = param.Tuple(default='test') + + def test_support_allow_None(self): + p = self.P() + assert p.h == (0, 0) + p.h = None + p.h = (1, 1) + assert p.h == (1, 1) + + class P(param.Parameterized): + h = param.Tuple(length=2, allow_None=True) + + P.h = None + P.h = (1, 1) + assert P.h == (1, 1) + + +class TestNumericTupleParameters(API1TestCase): + + def setUp(self): + super(TestNumericTupleParameters, self).setUp() + class P(param.Parameterized): + e = param.NumericTuple(default=(1, 1)) + f = param.NumericTuple(default=(0, 0, 0)) + g = param.NumericTuple(default=None, length=3) + h = param.NumericTuple(length=2, allow_None=True) + + self.P = P + + def _check_defaults(self, p): + assert p.default == (0, 0) + assert p.length == 2 + assert p.allow_None is False + + def test_defaults_class(self): + class P(param.Parameterized): + t = param.NumericTuple() + + check_defaults(P.param.t, label='T') + self._check_defaults(P.param.t) + + def test_defaults_inst(self): + class P(param.Parameterized): + t = param.NumericTuple() + + p = P() + + check_defaults(p.param.t, label='T') + self._check_defaults(p.param.t) + + def test_defaults_unbound(self): + t = param.NumericTuple() + + check_defaults(t, label=None) + self._check_defaults(t) + + def test_set_object_constructor(self): + p = self.P(e=(2, 2)) + self.assertEqual(p.e, (2, 2)) + + def test_length_inferred_from_default(self): + p = self.P() + assert p.param.f.length == 3 + + def test_raise_if_value_bad_length_constructor(self): + msg = r"Tuple parameter 'e' is not of the correct length \(3 instead of 2\)" + with self.assertRaisesRegex(ValueError, msg): + self.P(e=(1, 1, 1)) + + def test_raise_if_value_bad_length_setattr(self): + p = self.P() + msg = r"Tuple parameter 'e' is not of the correct length \(3 instead of 2\)" + with self.assertRaisesRegex(ValueError, msg): + p.e = (1, 1, 1) + + def test_raise_if_default_is_None_and_no_length(self): + msg = "length must be specified if no default is supplied" + with self.assertRaisesRegex(ValueError, msg): + class P(param.Parameterized): + t = param.NumericTuple(default=None) + + def test_None_default(self): + p = self.P() + assert p.g is None + assert p.param.g.length == 3 + assert p.param.g.allow_None + + def test_raise_if_default_is_None_and_bad_length(self): + msg = r"Tuple parameter 'g' is not of the correct length \(2 instead of 3\)." + with self.assertRaisesRegex(ValueError, msg): + p = self.P(g=(0, 0)) + + p = self.P() + with self.assertRaisesRegex(ValueError, msg): + p.g = (0, 0) + + def test_bad_type(self): + msg = r"Tuple parameter 'e' only takes a tuple value, not <(class|type) 'str'>." + + with self.assertRaisesRegex(ValueError, msg): + self.P.e = 'test' + + with self.assertRaisesRegex(ValueError, msg): + self.P(e='test') + + p = self.P() + + with self.assertRaisesRegex(ValueError, msg): + p.e = 'test' + + msg = r"Tuple parameter None only takes a tuple value, not <(class|type) 'str'>." + with self.assertRaisesRegex(ValueError, msg): + class P(param.Parameterized): + e = param.NumericTuple(default='test') + + def test_support_allow_None(self): + p = self.P() + assert p.h == (0, 0) + p.h = None + p.h = (1, 1) + assert p.h == (1, 1) + + class P(param.Parameterized): + h = param.NumericTuple(length=2, allow_None=True) + + P.h = None + P.h = (1, 1) + assert P.h == (1, 1) + + def test_raise_on_non_numeric_values(self): + msg = r"NumericTuple parameter 'e' only takes numeric values, not type <(class|type) 'str'>." + + with self.assertRaisesRegex(ValueError, msg): + self.P.e = ('bad', 1) + + with self.assertRaisesRegex(ValueError, msg): + self.P(e=('bad', 1)) + + p = self.P() + + with self.assertRaisesRegex(ValueError, msg): + p.e = ('bad', 1) + + msg = r"NumericTuple parameter None only takes numeric values, not type <(class|type) 'str'>." + with self.assertRaisesRegex(ValueError, msg): + class P(param.Parameterized): + e = param.NumericTuple(default=('bad', 1)) + + @pytest.mark.skipif(np is None, reason='NumPy is not available') + def test_support_numpy_values(self): + self.P(e=(np.int64(1), np.float32(2))) + + +class TestXYCoordinatesParameters(API1TestCase): + + def setUp(self): + super(TestXYCoordinatesParameters, self).setUp() + class P(param.Parameterized): + e = param.XYCoordinates(default=(1, 1)) + f = param.XYCoordinates(default=(0, 1), allow_None=True) + g = param.XYCoordinates(default=(1, 2)) + + self.P = P + + def _check_defaults(self, p): + assert p.default == (0.0, 0.0) + assert p.length == 2 + assert p.allow_None is False + + def test_defaults_class(self): + class P(param.Parameterized): + t = param.XYCoordinates() + + self._check_defaults(P.param.t) + + def test_defaults_inst(self): + class P(param.Parameterized): + t = param.XYCoordinates() + + p = P() + + self._check_defaults(p.param.t) + + def test_defaults_unbound(self): + t = param.XYCoordinates() + + self._check_defaults(t) + + def test_set_object_constructor(self): + p = self.P(e=(2, 2)) + self.assertEqual(p.e, (2, 2)) + + def test_raise_if_value_bad_length_constructor(self): + msg = r"Tuple parameter 'e' is not of the correct length \(3 instead of 2\)" + with self.assertRaisesRegex(ValueError, msg): + self.P(e=(1, 1, 1)) + + def test_raise_if_value_bad_length_setattr(self): + p = self.P() + msg = r"Tuple parameter 'e' is not of the correct length \(3 instead of 2\)" + with self.assertRaisesRegex(ValueError, msg): + p.e = (1, 1, 1) + + def test_bad_type(self): + msg = r"Tuple parameter 'e' only takes a tuple value, not <(class|type) 'str'>." + + with self.assertRaisesRegex(ValueError, msg): + self.P.e = 'test' + + with self.assertRaisesRegex(ValueError, msg): + self.P(e='test') + + p = self.P() + + with self.assertRaisesRegex(ValueError, msg): + p.e = 'test' + + msg = r"Tuple parameter None only takes a tuple value, not <(class|type) 'str'>." + with self.assertRaisesRegex(ValueError, msg): + class P(param.Parameterized): + e = param.NumericTuple(default='test') + + def test_support_allow_None_True(self): + p = self.P() + assert p.f == (0, 1) + p.f = None + assert p.f is None + + class P(param.Parameterized): + f = param.Range(default=(0, 1), allow_None=True) + + P.f = None + assert P.f is None + + def test_support_allow_None_False(self): + p = self.P() + msg = "Tuple parameter 'g' only takes a tuple value, not <(class|type) 'NoneType'>." + with self.assertRaisesRegex(ValueError, msg): + p.g = None + + msg = "Tuple parameter 'g' only takes a tuple value, not <(class|type) 'NoneType'>." + with self.assertRaisesRegex(ValueError, msg): + self.P.g = None + + def test_raise_on_non_numeric_values(self): + msg = r"NumericTuple parameter 'e' only takes numeric values, not type <(class|type) 'str'>." + + with self.assertRaisesRegex(ValueError, msg): + self.P.e = ('bad', 1) + + with self.assertRaisesRegex(ValueError, msg): + self.P(e=('bad', 1)) + + p = self.P() + + with self.assertRaisesRegex(ValueError, msg): + p.e = ('bad', 1) + + msg = r"NumericTuple parameter None only takes numeric values, not type <(class|type) 'str'>." + with self.assertRaisesRegex(ValueError, msg): + class P(param.Parameterized): + e = param.NumericTuple(default=('bad', 1)) + + @pytest.mark.skipif(np is None, reason='NumPy is not available') + def test_support_numpy_values(self): + self.P(e=(np.int64(1), np.float32(2))) diff --git a/tests/API1/testutils.py b/tests/API1/testutils.py index ace822f6e..7e2c707ee 100644 --- a/tests/API1/testutils.py +++ b/tests/API1/testutils.py @@ -1,9 +1,10 @@ import datetime as dt +import os import param import pytest -from param import guess_param_types +from param import guess_param_types, resolve_path try: import numpy as np @@ -56,3 +57,265 @@ def test_guess_param_types(val, p): if not type(out_param) == param.Parameter: assert out_param.default is val assert out_param.constant + +@pytest.fixture +def reset_search_paths(): + # The default is [os.getcwd()] which doesn't play well with the testing + # framework where every test creates a new temporary directory. + # This fixture sets it temporarily to []. + original = resolve_path.search_paths + try: + resolve_path.search_paths = [] + yield + finally: + resolve_path.search_paths = original + + +def test_resolve_path_file_default(): + assert resolve_path.path_to_file is True + assert resolve_path.search_paths == [os.getcwd()] + + +def test_resolve_path_file_not_found(): + with pytest.raises(IOError, match='File surelyyoudontexist was not found in the following'): + resolve_path('surelyyoudontexist') + + +@pytest.mark.usefixtures('reset_search_paths') +def test_resolve_path_file_not_found(tmpdir): + cdir = os.getcwd() + os.chdir(str(tmpdir)) + try: + with pytest.raises(IOError, match='File notthere was not found in the following'): + resolve_path('notthere') + finally: + os.chdir(cdir) + + +@pytest.mark.usefixtures('reset_search_paths') +def test_resolve_path_folder_not_found(tmpdir): + cdir = os.getcwd() + os.chdir(str(tmpdir)) + try: + with pytest.raises(IOError, match='Folder notthere was not found in the following'): + resolve_path('notthere', path_to_file=False) + finally: + os.chdir(cdir) + + +@pytest.mark.usefixtures('reset_search_paths') +def test_resolve_path_either_not_found(tmpdir): + cdir = os.getcwd() + os.chdir(str(tmpdir)) + try: + with pytest.raises(IOError, match='Path notthere was not found in the following'): + resolve_path('notthere', path_to_file=None) + finally: + os.chdir(cdir) + + +@pytest.mark.usefixtures('reset_search_paths') +@pytest.mark.parametrize('path_to_file', [True, False, None]) +def test_resolve_path_abs_not_found(tmpdir, path_to_file): + cdir = os.getcwd() + fp = os.path.join(str(tmpdir), 'foo') + os.chdir(str(tmpdir)) + try: + with pytest.raises(IOError, match='not found'): + resolve_path(fp, path_to_file=path_to_file) + finally: + os.chdir(cdir) + + +@pytest.mark.usefixtures('reset_search_paths') +def test_resolve_path_cwd_file(tmpdir): + cdir = os.getcwd() + fp = os.path.join(str(tmpdir), 'foo') + open(fp, 'w').close() + os.chdir(str(tmpdir)) + try: + p = resolve_path('foo') + assert os.path.isfile(p) + assert os.path.basename(p) == 'foo' + assert os.path.isabs(p) + assert p == fp + finally: + os.chdir(cdir) + + +@pytest.mark.usefixtures('reset_search_paths') +def test_resolve_path_cwd_folder(tmpdir): + cdir = os.getcwd() + fp = os.path.join(str(tmpdir), 'foo') + os.mkdir(fp) + os.chdir(str(tmpdir)) + try: + p = resolve_path('foo', path_to_file=False) + assert os.path.isdir(p) + assert os.path.basename(p) == 'foo' + assert os.path.isabs(p) + assert p == fp + finally: + os.chdir(cdir) + + +@pytest.mark.usefixtures('reset_search_paths') +def test_resolve_path_cwd_either_file(tmpdir): + cdir = os.getcwd() + fp = os.path.join(str(tmpdir), 'foo') + open(fp, 'w').close() + os.chdir(str(tmpdir)) + try: + p = resolve_path('foo', path_to_file=None) + assert os.path.isfile(p) + assert os.path.basename(p) == 'foo' + assert os.path.isabs(p) + assert p == fp + finally: + os.chdir(cdir) + + +@pytest.mark.usefixtures('reset_search_paths') +def test_resolve_path_cwd_either_folder(tmpdir): + cdir = os.getcwd() + fp = os.path.join(str(tmpdir), 'foo') + os.mkdir(fp) + os.chdir(str(tmpdir)) + try: + p = resolve_path('foo', path_to_file=None) + assert os.path.isdir(p) + assert os.path.basename(p) == 'foo' + assert os.path.isabs(p) + assert p == fp + finally: + os.chdir(cdir) + + +@pytest.mark.usefixtures('reset_search_paths') +def test_resolve_path_abs_file(tmpdir): + cdir = os.getcwd() + fp = os.path.join(str(tmpdir), 'foo') + open(fp, 'w').close() + os.chdir(str(tmpdir)) + try: + p = resolve_path(fp) + assert os.path.isfile(p) + assert os.path.basename(p) == 'foo' + assert os.path.isabs(p) + assert p == fp + finally: + os.chdir(cdir) + + +@pytest.mark.usefixtures('reset_search_paths') +def test_resolve_path_abs_folder(tmpdir): + cdir = os.getcwd() + fp = os.path.join(str(tmpdir), 'foo') + os.mkdir(fp) + os.chdir(str(tmpdir)) + try: + p = resolve_path(fp, path_to_file=False) + assert os.path.isdir(p) + assert os.path.basename(p) == 'foo' + assert os.path.isabs(p) + assert p == fp + finally: + os.chdir(cdir) + + +@pytest.mark.usefixtures('reset_search_paths') +def test_resolve_path_abs_either_file(tmpdir): + cdir = os.getcwd() + fp = os.path.join(str(tmpdir), 'foo') + open(fp, 'w').close() + os.chdir(str(tmpdir)) + try: + p = resolve_path(fp, path_to_file=None) + assert os.path.isfile(p) + assert os.path.basename(p) == 'foo' + assert os.path.isabs(p) + assert p == fp + finally: + os.chdir(cdir) + + +@pytest.mark.usefixtures('reset_search_paths') +def test_resolve_path_abs_either_folder(tmpdir): + cdir = os.getcwd() + fp = os.path.join(str(tmpdir), 'foo') + os.mkdir(fp) + os.chdir(str(tmpdir)) + try: + p = resolve_path(fp, path_to_file=None) + assert os.path.isdir(p) + assert os.path.basename(p) == 'foo' + assert os.path.isabs(p) + assert p == fp + finally: + os.chdir(cdir) + + +def test_resolve_path_search_paths_file(tmpdir): + fp = os.path.join(str(tmpdir), 'foo') + open(fp, 'w').close() + p = resolve_path('foo', search_paths=[str(tmpdir)]) + assert os.path.isfile(p) + assert os.path.basename(p) == 'foo' + assert os.path.isabs(p) + assert p == fp + + +@pytest.mark.usefixtures('reset_search_paths') +def test_resolve_path_search_paths_folder(tmpdir): + fp = os.path.join(str(tmpdir), 'foo') + os.mkdir(fp) + p = resolve_path('foo', search_paths=[str(tmpdir)], path_to_file=False) + assert os.path.isdir(p) + assert os.path.basename(p) == 'foo' + assert os.path.isabs(p) + assert p == fp + + +@pytest.mark.usefixtures('reset_search_paths') +def test_resolve_path_search_paths_either_file(tmpdir): + fp = os.path.join(str(tmpdir), 'foo') + open(fp, 'w').close() + p = resolve_path('foo', search_paths=[str(tmpdir)], path_to_file=None) + assert os.path.isfile(p) + assert os.path.basename(p) == 'foo' + assert os.path.isabs(p) + assert p == fp + + +@pytest.mark.usefixtures('reset_search_paths') +def test_resolve_path_search_paths_either_folder(tmpdir): + fp = os.path.join(str(tmpdir), 'foo') + os.mkdir(fp) + p = resolve_path('foo', search_paths=[str(tmpdir)], path_to_file=None) + assert os.path.isdir(p) + assert os.path.basename(p) == 'foo' + assert os.path.isabs(p) + assert p == fp + + +@pytest.mark.usefixtures('reset_search_paths') +def test_resolve_path_search_paths_multiple_file(tmpdir): + d1 = os.path.join(str(tmpdir), 'd1') + d2 = os.path.join(str(tmpdir), 'd2') + os.mkdir(d1) + os.mkdir(d2) + fp1 = os.path.join(d1, 'foo1') + open(fp1, 'w').close() + fp2 = os.path.join(d2, 'foo2') + open(fp2, 'w').close() + p = resolve_path('foo1', search_paths=[d1, d2]) + assert os.path.isfile(p) + assert os.path.basename(p) == 'foo1' + assert os.path.isabs(p) + assert p == fp1 + + p = resolve_path('foo2', search_paths=[d1, d2]) + assert os.path.isfile(p) + assert os.path.basename(p) == 'foo2' + assert os.path.isabs(p) + assert p == fp2 diff --git a/tests/API1/utils.py b/tests/API1/utils.py index 8380c3d8b..3000f684c 100644 --- a/tests/API1/utils.py +++ b/tests/API1/utils.py @@ -73,3 +73,23 @@ def assertContains(self, level, substring): raise AssertionError(msg.format(level=level, last_line=repr(last_line[0]), substring=repr(substring))) + + +def check_defaults(parameter, label, skip=[]): + # ! Not testing default and allow_None + if 'doc' not in skip: + assert parameter.doc is None + if 'precedence' not in skip: + assert parameter.precedence is None + if 'instantiate' not in skip: + assert parameter.instantiate is False + if 'constant' not in skip: + assert parameter.constant is False + if 'readonly' not in skip: + assert parameter.readonly is False + if 'pickle_default_value' not in skip: + assert parameter.pickle_default_value is True + if 'per_instance' not in skip: + assert parameter.per_instance is True + if 'label' not in skip: + assert parameter.label == label