class TestOptionsDict(unittest.TestCase):
    def setUp(self):
        self.dict = OptionsDictionary()

    def test_reprs(self):
        class MyComp(ExplicitComponent):
            pass

        my_comp = MyComp()

        self.dict.declare('test', values=['a', 'b'], desc='Test integer value')
        self.dict.declare('flag', default=False, types=bool)
        self.dict.declare('comp', default=my_comp, types=ExplicitComponent)
        self.dict.declare('long_desc',
                          types=str,
                          desc='This description is long and verbose, so it '
                          'takes up multiple lines in the options table.')

        self.assertEqual(repr(self.dict), repr(self.dict._dict))

        self.assertEqual(
            self.dict.__str__(width=83), '\n'.join([
                "========= ============ ================= ===================== ====================",
                "Option    Default      Acceptable Values Acceptable Types      Description         ",
                "========= ============ ================= ===================== ====================",
                "comp      MyComp       N/A               ['ExplicitComponent']                     ",
                "flag      False        [True, False]     ['bool']                                  ",
                "long_desc **Required** N/A               ['str']               This description is ",
                "                                                               long and verbose, so",
                "                                                                it takes up multipl",
                "                                                               e lines in the optio",
                "                                                               ns table.",
                "test      **Required** ['a', 'b']        N/A                   Test integer value  ",
                "========= ============ ================= ===================== ====================",
            ]))

        # if the table can't be represented in specified width, then we get the full width version
        self.assertEqual(
            self.dict.__str__(width=40), '\n'.join([
                "========= ============ ================= ===================== ====================="
                "==================================================================== ",
                "Option    Default      Acceptable Values Acceptable Types      Description          "
                "                                                                     ",
                "========= ============ ================= ===================== ====================="
                "==================================================================== ",
                "comp      MyComp       N/A               ['ExplicitComponent']                      "
                "                                                                     ",
                "flag      False        [True, False]     ['bool']                                   "
                "                                                                     ",
                "long_desc **Required** N/A               ['str']               This description is l"
                "ong and verbose, so it takes up multiple lines in the options table. ",
                "test      **Required** ['a', 'b']        N/A                   Test integer value   "
                "                                                                     ",
                "========= ============ ================= ===================== ====================="
                "==================================================================== ",
            ]))

    def test_type_checking(self):
        self.dict.declare('test', types=int, desc='Test integer value')

        self.dict['test'] = 1
        self.assertEqual(self.dict['test'], 1)

        with self.assertRaises(TypeError) as context:
            self.dict['test'] = ''

        expected_msg = "Value ('') of option 'test' has type 'str', " \
                       "but type 'int' was expected."
        self.assertEqual(expected_msg, str(context.exception))

        # multiple types are allowed
        self.dict.declare('test_multi',
                          types=(int, float),
                          desc='Test multiple types')

        self.dict['test_multi'] = 1
        self.assertEqual(self.dict['test_multi'], 1)
        self.assertEqual(type(self.dict['test_multi']), int)

        self.dict['test_multi'] = 1.0
        self.assertEqual(self.dict['test_multi'], 1.0)
        self.assertEqual(type(self.dict['test_multi']), float)

        with self.assertRaises(TypeError) as context:
            self.dict['test_multi'] = ''

        expected_msg = "Value ('') of option 'test_multi' has type 'str', " \
                       "but one of types ('int', 'float') was expected."
        self.assertEqual(expected_msg, str(context.exception))

        # make sure bools work and allowed values are populated
        self.dict.declare('flag', default=False, types=bool)
        self.assertEqual(self.dict['flag'], False)
        self.dict['flag'] = True
        self.assertEqual(self.dict['flag'], True)

        meta = self.dict._dict['flag']
        self.assertEqual(meta['values'], (True, False))

    def test_allow_none(self):
        self.dict.declare('test',
                          types=int,
                          allow_none=True,
                          desc='Test integer value')
        self.dict['test'] = None
        self.assertEqual(self.dict['test'], None)

    def test_type_and_values(self):
        # Test with only type_
        self.dict.declare('test1', types=int)
        self.dict['test1'] = 1
        self.assertEqual(self.dict['test1'], 1)

        # Test with only values
        self.dict.declare('test2', values=['a', 'b'])
        self.dict['test2'] = 'a'
        self.assertEqual(self.dict['test2'], 'a')

        # Test with both type_ and values
        with self.assertRaises(Exception) as context:
            self.dict.declare('test3', types=int, values=['a', 'b'])
        self.assertEqual(
            str(context.exception),
            "'types' and 'values' were both specified for option 'test3'.")

    def test_isvalid(self):
        self.dict.declare('even_test', types=int, check_valid=check_even)
        self.dict['even_test'] = 2
        self.dict['even_test'] = 4

        with self.assertRaises(ValueError) as context:
            self.dict['even_test'] = 3

        expected_msg = "Option 'even_test' with value 3 is not an even number."
        self.assertEqual(expected_msg, str(context.exception))

    def test_isvalid_deprecated_type(self):

        msg = "In declaration of option 'even_test' the '_type' arg is deprecated.  Use 'types' instead."

        with assert_warning(DeprecationWarning, msg):
            self.dict.declare('even_test', type_=int, check_valid=check_even)

        self.dict['even_test'] = 2
        self.dict['even_test'] = 4

        with self.assertRaises(ValueError) as context:
            self.dict['even_test'] = 3

        expected_msg = "Option 'even_test' with value 3 is not an even number."
        self.assertEqual(expected_msg, str(context.exception))

    def test_unnamed_args(self):
        with self.assertRaises(KeyError) as context:
            self.dict['test'] = 1

        # KeyError ends up with an extra set of quotes.
        expected_msg = "\"Option 'test' cannot be set because it has not been declared.\""
        self.assertEqual(expected_msg, str(context.exception))

    def test_contains(self):
        self.dict.declare('test')

        contains = 'undeclared' in self.dict
        self.assertTrue(not contains)

        contains = 'test' in self.dict
        self.assertTrue(contains)

    def test_update(self):
        self.dict.declare('test', default='Test value', types=object)

        obj = object()
        self.dict.update({'test': obj})
        self.assertIs(self.dict['test'], obj)

    def test_update_extra(self):
        with self.assertRaises(KeyError) as context:
            self.dict.update({'test': 2})

        # KeyError ends up with an extra set of quotes.
        expected_msg = "\"Option 'test' cannot be set because it has not been declared.\""
        self.assertEqual(expected_msg, str(context.exception))

    def test_get_missing(self):
        with self.assertRaises(KeyError) as context:
            self.dict['missing']

        expected_msg = "\"Option 'missing' cannot be found\""
        self.assertEqual(expected_msg, str(context.exception))

    def test_get_default(self):
        obj_def = object()
        obj_new = object()

        self.dict.declare('test', default=obj_def, types=object)

        self.assertIs(self.dict['test'], obj_def)

        self.dict['test'] = obj_new
        self.assertIs(self.dict['test'], obj_new)

    def test_values(self):
        obj1 = object()
        obj2 = object()
        self.dict.declare('test', values=[obj1, obj2])

        self.dict['test'] = obj1
        self.assertIs(self.dict['test'], obj1)

        with self.assertRaises(ValueError) as context:
            self.dict['test'] = object()

        expected_msg = (
            "Value \(<object object at 0x[0-9A-Fa-f]+>\) of option 'test' is not one of \[<object object at 0x[0-9A-Fa-f]+>,"
            " <object object at 0x[0-9A-Fa-f]+>\].")
        assertRegex(self, str(context.exception), expected_msg)

    def test_read_only(self):
        opt = OptionsDictionary(read_only=True)
        opt.declare('permanent', 3.0)

        with self.assertRaises(KeyError) as context:
            opt['permanent'] = 4.0

        expected_msg = ("Tried to set read-only option 'permanent'.")
        assertRegex(self, str(context.exception), expected_msg)

    def test_bounds(self):
        self.dict.declare('x', default=1.0, lower=0.0, upper=2.0)

        with self.assertRaises(ValueError) as context:
            self.dict['x'] = 3.0

        expected_msg = "Value (3.0) of option 'x' exceeds maximum allowed value of 2.0."
        self.assertEqual(str(context.exception), expected_msg)

        with self.assertRaises(ValueError) as context:
            self.dict['x'] = -3.0

        expected_msg = "Value (-3.0) of option 'x' is less than minimum allowed value of 0.0."
        self.assertEqual(str(context.exception), expected_msg)

    def test_undeclare(self):
        # create an entry in the dict
        self.dict.declare('test', types=int)
        self.dict['test'] = 1

        # prove it's in the dict
        self.assertEqual(self.dict['test'], 1)

        # remove entry from the dict
        self.dict.undeclare("test")

        # prove it is no longer in the dict
        with self.assertRaises(KeyError) as context:
            self.dict['test']

        expected_msg = "\"Option 'test' cannot be found\""
        self.assertEqual(expected_msg, str(context.exception))
Ejemplo n.º 2
0
class TestOptionsDict(unittest.TestCase):
    def setUp(self):
        self.dict = OptionsDictionary()

    @unittest.skipIf(tabulate is None,
                     reason="package 'tabulate' is not installed")
    def test_reprs(self):
        class MyComp(ExplicitComponent):
            pass

        my_comp = MyComp()

        self.dict.declare('test', values=['a', 'b'], desc='Test integer value')
        self.dict.declare('flag', default=False, types=bool)
        self.dict.declare('comp', default=my_comp, types=ExplicitComponent)
        self.dict.declare('long_desc',
                          types=str,
                          desc='This description is long and verbose, so it '
                          'takes up multiple lines in the options table.')

        self.assertEqual(repr(self.dict), repr(self.dict._dict))

        self.assertEqual(
            self.dict.__str__(width=89), '\n'.join([
                "=========  ============  ===================  =====================  ====================",
                "Option     Default       Acceptable Values    Acceptable Types       Description",
                "=========  ============  ===================  =====================  ====================",
                "comp       MyComp        N/A                  ['ExplicitComponent']",
                "flag       False         [True, False]        ['bool']",
                "long_desc  **Required**  N/A                  ['str']                This description is ",
                "                                                                     long and verbose, so",
                "                                                                      it takes up multipl",
                "                                                                     e lines in the optio",
                "                                                                     ns table.",
                "test       **Required**  ['a', 'b']           N/A                    Test integer value",
                "=========  ============  ===================  =====================  ====================",
            ]))

        # if the table can't be represented in specified width, then we get the full width version
        self.assertEqual(
            self.dict.__str__(width=40), '\n'.join([
                "=========  ============  ===================  =====================  ====================="
                "====================================================================",
                "Option     Default       Acceptable Values    Acceptable Types       Description",
                "=========  ============  ===================  =====================  ====================="
                "====================================================================",
                "comp       MyComp        N/A                  ['ExplicitComponent']",
                "flag       False         [True, False]        ['bool']",
                "long_desc  **Required**  N/A                  ['str']                This description is l"
                "ong and verbose, so it takes up multiple lines in the options table.",
                "test       **Required**  ['a', 'b']           N/A                    Test integer value",
                "=========  ============  ===================  =====================  ====================="
                "====================================================================",
            ]))

    @unittest.skipIf(tabulate is None,
                     reason="package 'tabulate' is not installed")
    def test_to_table(self):
        class MyComp(ExplicitComponent):
            pass

        my_comp = MyComp()

        self.dict.declare('test', values=['a', 'b'], desc='Test integer value')
        self.dict.declare('flag', default=False, types=bool)
        self.dict.declare('comp', default=my_comp, types=ExplicitComponent)
        self.dict.declare('long_desc',
                          types=str,
                          desc='This description is long and verbose, so it '
                          'takes up multiple lines in the options table.')

        expected = "| Option    | Default      | Acceptable Values   | Acceptable Types      " \
                   "| Description                                                            " \
                   "                   |\n" \
                   "|-----------|--------------|---------------------|-----------------------|--" \
                   "----------------------------------------------------------------------------" \
                   "-------------|\n" \
                   "| comp      | MyComp       | N/A                 | ['ExplicitComponent'] |   " \
                   "                                                                             " \
                   "           |\n" \
                   "| flag      | False        | [True, False]       | ['bool']              |   " \
                   "                                                                             " \
                   "           |\n" \
                   "| long_desc | **Required** | N/A                 | ['str']               | Th" \
                   "is description is long and verbose, so it takes up multiple lines in the opti" \
                   "ons table. |\n" \
                   "| test      | **Required** | ['a', 'b']          | N/A                   | Te" \
                   "st integer value                                                             " \
                   "           |"

        self.assertEqual(self.dict.to_table(fmt='github'), expected)

    @unittest.skipIf(tabulate is None,
                     reason="package 'tabulate' is not installed")
    def test_deprecation_col(self):
        class MyComp(ExplicitComponent):
            pass

        my_comp = MyComp()

        self.dict.declare('test', values=['a', 'b'], desc='Test integer value')
        self.dict.declare('flag', default=False, types=bool)
        self.dict.declare('comp', default=my_comp, types=ExplicitComponent)
        self.dict.declare('long_desc',
                          types=str,
                          desc='This description is long and verbose, so it '
                          'takes up multiple lines in the options table.',
                          deprecation='This option is deprecated')

        expected = "|Option|Default|AcceptableValues|AcceptableTypes|Description|Deprecation|\n|" \
        "-----------|--------------|---------------------|-----------------------|-----------------" \
        "--------------------------------------------------------------------------|---------------" \
        "------------|\n|comp|MyComp|N/A|['ExplicitComponent']||N/A|\n|flag|False|[True,False]|" \
        "['bool']||N/A|\n|long_desc|**Required**|N/A|['str']|Thisdescriptionislongandverbose,soit" \
        "takesupmultiplelinesintheoptionstable.|Thisoptionisdeprecated|\n|test|**Required**|" \
        "['a','b']|N/A|Testintegervalue|N/A|"

        self.assertEqual(
            self.dict.to_table(fmt='github').replace(" ", ""), expected)

        my_comp = MyComp()

        self.dict.declare('test', values=['a', 'b'], desc='Test integer value')
        self.dict.declare('flag', default=False, types=bool)
        self.dict.declare('comp', default=my_comp, types=ExplicitComponent)
        self.dict.declare('long_desc',
                          types=str,
                          desc='This description is long and verbose, so it '
                          'takes up multiple lines in the options table.')

        expected = "|Option|Default|AcceptableValues|AcceptableTypes|Description|\n|-----------|----" \
        "----------|---------------------|-----------------------|-----------------------------------" \
        "--------------------------------------------------------|\n|comp|MyComp|N/A|" \
        "['ExplicitComponent']||\n|flag|False|[True,False]|['bool']||\n|long_desc|**Required**|N/A|" \
        "['str']|Thisdescriptionislongandverbose,soittakesupmultiplelinesintheoptionstable.|\n|test|" \
        "**Required**|['a','b']|N/A|Testintegervalue|"

        self.assertEqual(
            self.dict.to_table(fmt='github').replace(" ", ""), expected)

    def test_type_checking(self):
        self.dict.declare('test', types=int, desc='Test integer value')

        self.dict['test'] = 1
        self.assertEqual(self.dict['test'], 1)

        with self.assertRaises(TypeError) as context:
            self.dict['test'] = ''

        expected_msg = "Value ('') of option 'test' has type 'str', " \
                       "but type 'int' was expected."
        self.assertEqual(expected_msg, str(context.exception))

        # multiple types are allowed
        self.dict.declare('test_multi',
                          types=(int, float),
                          desc='Test multiple types')

        self.dict['test_multi'] = 1
        self.assertEqual(self.dict['test_multi'], 1)
        self.assertEqual(type(self.dict['test_multi']), int)

        self.dict['test_multi'] = 1.0
        self.assertEqual(self.dict['test_multi'], 1.0)
        self.assertEqual(type(self.dict['test_multi']), float)

        with self.assertRaises(TypeError) as context:
            self.dict['test_multi'] = ''

        expected_msg = "Value ('') of option 'test_multi' has type 'str', " \
                       "but one of types ('int', 'float') was expected."
        self.assertEqual(expected_msg, str(context.exception))

        # make sure bools work and allowed values are populated
        self.dict.declare('flag', default=False, types=bool)
        self.assertEqual(self.dict['flag'], False)
        self.dict['flag'] = True
        self.assertEqual(self.dict['flag'], True)

        meta = self.dict._dict['flag']
        self.assertEqual(meta['values'], (True, False))

    def test_allow_none(self):
        self.dict.declare('test',
                          types=int,
                          allow_none=True,
                          desc='Test integer value')
        self.dict['test'] = None
        self.assertEqual(self.dict['test'], None)

    def test_type_and_values(self):
        # Test with only type_
        self.dict.declare('test1', types=int)
        self.dict['test1'] = 1
        self.assertEqual(self.dict['test1'], 1)

        # Test with only values
        self.dict.declare('test2', values=['a', 'b'])
        self.dict['test2'] = 'a'
        self.assertEqual(self.dict['test2'], 'a')

        # Test with both type_ and values
        with self.assertRaises(Exception) as context:
            self.dict.declare('test3', types=int, values=['a', 'b'])
        self.assertEqual(
            str(context.exception),
            "'types' and 'values' were both specified for option 'test3'.")

    def test_check_valid_template(self):
        # test the template 'check_valid' function
        from openmdao.utils.options_dictionary import check_valid
        self.dict.declare('test', check_valid=check_valid)

        with self.assertRaises(ValueError) as context:
            self.dict['test'] = 1

        expected_msg = "Option 'test' with value 1 is not valid."
        self.assertEqual(expected_msg, str(context.exception))

    def test_isvalid(self):
        self.dict.declare('even_test', types=int, check_valid=check_even)
        self.dict['even_test'] = 2
        self.dict['even_test'] = 4

        with self.assertRaises(ValueError) as context:
            self.dict['even_test'] = 3

        expected_msg = "Option 'even_test' with value 3 is not an even number."
        self.assertEqual(expected_msg, str(context.exception))

    def test_unnamed_args(self):
        with self.assertRaises(KeyError) as context:
            self.dict['test'] = 1

        # KeyError ends up with an extra set of quotes.
        expected_msg = "\"Option 'test' cannot be set because it has not been declared.\""
        self.assertEqual(expected_msg, str(context.exception))

    def test_contains(self):
        self.dict.declare('test')

        contains = 'undeclared' in self.dict
        self.assertTrue(not contains)

        contains = 'test' in self.dict
        self.assertTrue(contains)

    def test_update(self):
        self.dict.declare('test', default='Test value', types=object)

        obj = object()
        self.dict.update({'test': obj})
        self.assertIs(self.dict['test'], obj)

    def test_update_extra(self):
        with self.assertRaises(KeyError) as context:
            self.dict.update({'test': 2})

        # KeyError ends up with an extra set of quotes.
        expected_msg = "\"Option 'test' cannot be set because it has not been declared.\""
        self.assertEqual(expected_msg, str(context.exception))

    def test_get_missing(self):
        with self.assertRaises(KeyError) as context:
            self.dict['missing']

        expected_msg = "Option 'missing' has not been declared."
        self.assertEqual(expected_msg, context.exception.args[0])

    def test_get_default(self):
        obj_def = object()
        obj_new = object()

        self.dict.declare('test', default=obj_def, types=object)

        self.assertIs(self.dict['test'], obj_def)

        self.dict['test'] = obj_new
        self.assertIs(self.dict['test'], obj_new)

    def test_values(self):
        obj1 = object()
        obj2 = object()
        self.dict.declare('test', values=[obj1, obj2])

        self.dict['test'] = obj1
        self.assertIs(self.dict['test'], obj1)

        with self.assertRaises(ValueError) as context:
            self.dict['test'] = object()

        expected_msg = (
            "Value \(<object object at 0x[0-9A-Fa-f]+>\) of option 'test' is not one of \[<object object at 0x[0-9A-Fa-f]+>,"
            " <object object at 0x[0-9A-Fa-f]+>\].")
        self.assertRegex(str(context.exception), expected_msg)

    def test_read_only(self):
        opt = OptionsDictionary(read_only=True)
        opt.declare('permanent', 3.0)

        with self.assertRaises(KeyError) as context:
            opt['permanent'] = 4.0

        expected_msg = ("Tried to set read-only option 'permanent'.")
        self.assertRegex(str(context.exception), expected_msg)

    def test_bounds(self):
        self.dict.declare('x', default=1.0, lower=0.0, upper=2.0)

        with self.assertRaises(ValueError) as context:
            self.dict['x'] = 3.0

        expected_msg = "Value (3.0) of option 'x' exceeds maximum allowed value of 2.0."
        self.assertEqual(str(context.exception), expected_msg)

        with self.assertRaises(ValueError) as context:
            self.dict['x'] = -3.0

        expected_msg = "Value (-3.0) of option 'x' is less than minimum allowed value of 0.0."
        self.assertEqual(str(context.exception), expected_msg)

    def test_undeclare(self):
        # create an entry in the dict
        self.dict.declare('test', types=int)
        self.dict['test'] = 1

        # prove it's in the dict
        self.assertEqual(self.dict['test'], 1)

        # remove entry from the dict
        self.dict.undeclare("test")

        # prove it is no longer in the dict
        with self.assertRaises(KeyError) as context:
            self.dict['test']

        expected_msg = "Option 'test' has not been declared."
        self.assertEqual(expected_msg, context.exception.args[0])

    def test_deprecated_option(self):
        msg = 'Option "test1" is deprecated.'
        self.dict.declare('test1', deprecation=msg)

        # test double set
        with assert_warning(OMDeprecationWarning, msg):
            self.dict['test1'] = None
        # Should only generate warning first time
        with assert_no_warning(OMDeprecationWarning, msg):
            self.dict['test1'] = None

        # Also test set and then get
        msg = 'Option "test2" is deprecated.'
        self.dict.declare('test2', deprecation=msg)

        with assert_warning(OMDeprecationWarning, msg):
            self.dict['test2'] = None
        # Should only generate warning first time
        with assert_no_warning(OMDeprecationWarning, msg):
            option = self.dict['test2']

    def test_deprecated_tuple_option(self):
        msg = 'Option "test1" is deprecated. Use "foo" instead.'
        self.dict.declare('test1', deprecation=(msg, 'foo'))
        self.dict.declare('foo')

        # test double set
        with assert_warning(OMDeprecationWarning, msg):
            self.dict['test1'] = 'xyz'
        # Should only generate warning first time
        with assert_no_warning(OMDeprecationWarning, msg):
            self.dict['test1'] = 'zzz'

        with assert_no_warning(OMDeprecationWarning, msg):
            option = self.dict['test1']
        with assert_no_warning(OMDeprecationWarning):
            option2 = self.dict['foo']
        self.assertEqual(option, option2)

        # Also test set and then get
        msg = 'Option "test2" is deprecated. Use "foo2" instead.'
        self.dict.declare('test2', deprecation=(msg, 'foo2'))
        self.dict.declare('foo2')

        with assert_warning(OMDeprecationWarning, msg):
            self.dict['test2'] = 'abcd'
        # Should only generate warning first time
        with assert_no_warning(OMDeprecationWarning, msg):
            option = self.dict['test2']
        with assert_no_warning(OMDeprecationWarning):
            option2 = self.dict['foo2']
        self.assertEqual(option, option2)

        # test bad alias
        msg = 'Option "test3" is deprecated. Use "foo3" instead.'
        self.dict.declare('test3', deprecation=(msg, 'foo3'))

        with self.assertRaises(KeyError) as context:
            self.dict['test3'] = 'abcd'

        expected_msg = "Can't find aliased option 'foo3' for deprecated option 'test3'."
        self.assertEqual(context.exception.args[0], expected_msg)

    def test_bad_option_name(self):
        opt = OptionsDictionary()
        msg = "'foo:bar' is not a valid python name and will become an invalid option name in a future release. You can prevent this warning (and future exceptions) by declaring this option using a valid python name."

        with assert_warning(OMDeprecationWarning, msg):
            opt.declare('foo:bar', 1.0)
Ejemplo n.º 3
0
class TestOptionsDict(unittest.TestCase):

    def setUp(self):
        self.dict = OptionsDictionary()

    def test_type_checking(self):
        self.dict.declare('test', types=int, desc='Test integer value')

        self.dict['test'] = 1
        self.assertEqual(self.dict['test'], 1)

        with self.assertRaises(TypeError) as context:
            self.dict['test'] = ''

        class_or_type = 'class' if PY3 else 'type'
        expected_msg = "Option 'test' has the wrong type (<{} 'int'>)".format(class_or_type)
        self.assertEqual(expected_msg, str(context.exception))

        # make sure bools work
        self.dict.declare('flag', default=False, types=bool)
        self.assertEqual(self.dict['flag'], False)
        self.dict['flag'] = True
        self.assertEqual(self.dict['flag'], True)

    def test_allow_none(self):
        self.dict.declare('test', types=int, allow_none=True, desc='Test integer value')
        self.dict['test'] = None
        self.assertEqual(self.dict['test'], None)

    def test_type_and_values(self):
        # Test with only type_
        self.dict.declare('test1', types=int)
        self.dict['test1'] = 1
        self.assertEqual(self.dict['test1'], 1)

        # Test with only values
        self.dict.declare('test2', values=['a', 'b'])
        self.dict['test2'] = 'a'
        self.assertEqual(self.dict['test2'], 'a')

        # Test with both type_ and values
        with self.assertRaises(Exception) as context:
            self.dict.declare('test3', types=int, values=['a', 'b'])
        self.assertEqual(str(context.exception),
                         "'types' and 'values' were both specified for option 'test3'.")

    def test_isvalid(self):
        self.dict.declare('even_test', types=int, is_valid=lambda x: x%2 == 0)
        self.dict['even_test'] = 2
        self.dict['even_test'] = 4

        with self.assertRaises(ValueError) as context:
            self.dict['even_test'] = 3

        expected_msg = "Function is_valid returns False for {}.".format('even_test')
        self.assertEqual(expected_msg, str(context.exception))

    def test_isvalid_deprecated_type(self):

        with warnings.catch_warnings(record=True) as w:
            warnings.simplefilter("always")
            self.dict.declare('even_test', type_=int, is_valid=lambda x: x%2 == 0)
            self.assertEqual(len(w), 1)
            self.assertEqual(str(w[-1].message), "In declaration of option 'even_test' the '_type' arg is deprecated.  Use 'types' instead.")

        self.dict['even_test'] = 2
        self.dict['even_test'] = 4

        with self.assertRaises(ValueError) as context:
            self.dict['even_test'] = 3

        expected_msg = "Function is_valid returns False for {}.".format('even_test')
        self.assertEqual(expected_msg, str(context.exception))

    def test_unnamed_args(self):
        with self.assertRaises(KeyError) as context:
            self.dict['test'] = 1

        # KeyError ends up with an extra set of quotes.
        expected_msg = "\"Key 'test' cannot be set because it has not been declared.\""
        self.assertEqual(expected_msg, str(context.exception))

    def test_contains(self):
        self.dict.declare('test')

        contains = 'undeclared' in self.dict
        self.assertTrue(not contains)

        contains = 'test' in self.dict
        self.assertTrue(contains)

    def test_update(self):
        self.dict.declare('test', default='Test value', types=object)

        obj = object()
        self.dict.update({'test': obj})
        self.assertIs(self.dict['test'], obj)

    def test_update_extra(self):
        with self.assertRaises(KeyError) as context:
            self.dict.update({'test': 2})

        # KeyError ends up with an extra set of quotes.
        expected_msg = "\"Key 'test' cannot be set because it has not been declared.\""
        self.assertEqual(expected_msg, str(context.exception))

    def test_get_missing(self):
        with self.assertRaises(KeyError) as context:
            self.dict['missing']

        expected_msg = "\"Option 'missing' cannot be found\""
        self.assertEqual(expected_msg, str(context.exception))

    def test_get_default(self):
        obj_def = object()
        obj_new = object()

        self.dict.declare('test', default=obj_def, types=object)

        self.assertIs(self.dict['test'], obj_def)

        self.dict['test'] = obj_new
        self.assertIs(self.dict['test'], obj_new)

    def test_values(self):
        obj1 = object()
        obj2 = object()
        self.dict.declare('test', values=[obj1, obj2])

        self.dict['test'] = obj1
        self.assertIs(self.dict['test'], obj1)

        with self.assertRaises(ValueError) as context:
            self.dict['test'] = object()

        expected_msg = ("Option 'test''s value is not one of \[<object object at 0x[0-9A-Fa-f]+>,"
                        " <object object at 0x[0-9A-Fa-f]+>\]")
        assertRegex(self, str(context.exception), expected_msg)

    def test_read_only(self):
        opt = OptionsDictionary(read_only=True)
        opt.declare('permanent', 3.0)

        with self.assertRaises(KeyError) as context:
            opt['permanent'] = 4.0

        expected_msg = ("Tried to set 'permanent' on a read-only OptionsDictionary")
        assertRegex(self, str(context.exception), expected_msg)

    def test_bounds(self):
        self.dict.declare('x', default=1.0, lower=0.0, upper=2.0)

        with self.assertRaises(ValueError) as context:
            self.dict['x'] = 3.0

        expected_msg = ("Value of 3.0 exceeds maximum of 2.0 for option 'x'")
        assertRegex(self, str(context.exception), expected_msg)

        with self.assertRaises(ValueError) as context:
            self.dict['x'] = -3.0

        expected_msg = ("Value of -3.0 exceeds minimum of 0.0 for option 'x'")
        assertRegex(self, str(context.exception), expected_msg)

    def test_undeclare(self):
        # create an entry in the dict
        self.dict.declare('test', types=int)
        self.dict['test'] = 1

        # prove it's in the dict
        self.assertEqual(self.dict['test'], 1)

        # remove entry from the dict
        self.dict.undeclare("test")

        # prove it is no longer in the dict
        with self.assertRaises(KeyError) as context:
            self.dict['test']

        expected_msg = "\"Option 'test' cannot be found\""
        self.assertEqual(expected_msg, str(context.exception))
Ejemplo n.º 4
0
class TestOptionsDict(unittest.TestCase):

    def setUp(self):
        self.dict = OptionsDictionary()

    def test_reprs(self):
        class MyComp(ExplicitComponent):
            pass

        my_comp = MyComp()

        self.dict.declare('test', values=['a', 'b'], desc='Test integer value')
        self.dict.declare('flag', default=False, types=bool)
        self.dict.declare('comp', default=my_comp, types=ExplicitComponent)
        self.dict.declare('long_desc', types=str,
                          desc='This description is long and verbose, so it '
                               'takes up multiple lines in the options table.')

        self.assertEqual(repr(self.dict), repr(self.dict._dict))

        self.assertEqual(self.dict.__str__(width=83), '\n'.join([
            "========= ============ ================= ===================== ====================",
            "Option    Default      Acceptable Values Acceptable Types      Description         ",
            "========= ============ ================= ===================== ====================",
            "comp      MyComp       N/A               ['ExplicitComponent']                     ",
            "flag      False        [True, False]     ['bool']                                  ",
            "long_desc **Required** N/A               ['str']               This description is ",
            "                                                               long and verbose, so",
            "                                                                it takes up multipl",
            "                                                               e lines in the optio",
            "                                                               ns table.",
            "test      **Required** ['a', 'b']        N/A                   Test integer value  ",
            "========= ============ ================= ===================== ====================",
        ]))

        # if the table can't be represented in specified width, then we get the full width version
        self.assertEqual(self.dict.__str__(width=40), '\n'.join([
            "========= ============ ================= ===================== ====================="
            "==================================================================== ",
            "Option    Default      Acceptable Values Acceptable Types      Description          "
            "                                                                     ",
            "========= ============ ================= ===================== ====================="
            "==================================================================== ",
            "comp      MyComp       N/A               ['ExplicitComponent']                      "
            "                                                                     ",
            "flag      False        [True, False]     ['bool']                                   "
            "                                                                     ",
            "long_desc **Required** N/A               ['str']               This description is l"
            "ong and verbose, so it takes up multiple lines in the options table. ",
            "test      **Required** ['a', 'b']        N/A                   Test integer value   "
            "                                                                     ",
            "========= ============ ================= ===================== ====================="
            "==================================================================== ",
        ]))

    def test_type_checking(self):
        self.dict.declare('test', types=int, desc='Test integer value')

        self.dict['test'] = 1
        self.assertEqual(self.dict['test'], 1)

        with self.assertRaises(TypeError) as context:
            self.dict['test'] = ''

        expected_msg = "Value ('') of option 'test' has type 'str', " \
                       "but type 'int' was expected."
        self.assertEqual(expected_msg, str(context.exception))

        # multiple types are allowed
        self.dict.declare('test_multi', types=(int, float), desc='Test multiple types')

        self.dict['test_multi'] = 1
        self.assertEqual(self.dict['test_multi'], 1)
        self.assertEqual(type(self.dict['test_multi']), int)

        self.dict['test_multi'] = 1.0
        self.assertEqual(self.dict['test_multi'], 1.0)
        self.assertEqual(type(self.dict['test_multi']), float)

        with self.assertRaises(TypeError) as context:
            self.dict['test_multi'] = ''

        expected_msg = "Value ('') of option 'test_multi' has type 'str', " \
                       "but one of types ('int', 'float') was expected."
        self.assertEqual(expected_msg, str(context.exception))

        # make sure bools work and allowed values are populated
        self.dict.declare('flag', default=False, types=bool)
        self.assertEqual(self.dict['flag'], False)
        self.dict['flag'] = True
        self.assertEqual(self.dict['flag'], True)

        meta = self.dict._dict['flag']
        self.assertEqual(meta['values'], (True, False))

    def test_allow_none(self):
        self.dict.declare('test', types=int, allow_none=True, desc='Test integer value')
        self.dict['test'] = None
        self.assertEqual(self.dict['test'], None)

    def test_type_and_values(self):
        # Test with only type_
        self.dict.declare('test1', types=int)
        self.dict['test1'] = 1
        self.assertEqual(self.dict['test1'], 1)

        # Test with only values
        self.dict.declare('test2', values=['a', 'b'])
        self.dict['test2'] = 'a'
        self.assertEqual(self.dict['test2'], 'a')

        # Test with both type_ and values
        with self.assertRaises(Exception) as context:
            self.dict.declare('test3', types=int, values=['a', 'b'])
        self.assertEqual(str(context.exception),
                         "'types' and 'values' were both specified for option 'test3'.")

    def test_isvalid(self):
        self.dict.declare('even_test', types=int, check_valid=check_even)
        self.dict['even_test'] = 2
        self.dict['even_test'] = 4

        with self.assertRaises(ValueError) as context:
            self.dict['even_test'] = 3

        expected_msg = "Option 'even_test' with value 3 is not an even number."
        self.assertEqual(expected_msg, str(context.exception))

    def test_isvalid_deprecated_type(self):

        msg = "In declaration of option 'even_test' the '_type' arg is deprecated.  Use 'types' instead."

        with assert_warning(DeprecationWarning, msg):
            self.dict.declare('even_test', type_=int, check_valid=check_even)

        self.dict['even_test'] = 2
        self.dict['even_test'] = 4

        with self.assertRaises(ValueError) as context:
            self.dict['even_test'] = 3

        expected_msg = "Option 'even_test' with value 3 is not an even number."
        self.assertEqual(expected_msg, str(context.exception))

    def test_unnamed_args(self):
        with self.assertRaises(KeyError) as context:
            self.dict['test'] = 1

        # KeyError ends up with an extra set of quotes.
        expected_msg = "\"Option 'test' cannot be set because it has not been declared.\""
        self.assertEqual(expected_msg, str(context.exception))

    def test_contains(self):
        self.dict.declare('test')

        contains = 'undeclared' in self.dict
        self.assertTrue(not contains)

        contains = 'test' in self.dict
        self.assertTrue(contains)

    def test_update(self):
        self.dict.declare('test', default='Test value', types=object)

        obj = object()
        self.dict.update({'test': obj})
        self.assertIs(self.dict['test'], obj)

    def test_update_extra(self):
        with self.assertRaises(KeyError) as context:
            self.dict.update({'test': 2})

        # KeyError ends up with an extra set of quotes.
        expected_msg = "\"Option 'test' cannot be set because it has not been declared.\""
        self.assertEqual(expected_msg, str(context.exception))

    def test_get_missing(self):
        with self.assertRaises(KeyError) as context:
            self.dict['missing']

        expected_msg = "\"Option 'missing' cannot be found\""
        self.assertEqual(expected_msg, str(context.exception))

    def test_get_default(self):
        obj_def = object()
        obj_new = object()

        self.dict.declare('test', default=obj_def, types=object)

        self.assertIs(self.dict['test'], obj_def)

        self.dict['test'] = obj_new
        self.assertIs(self.dict['test'], obj_new)

    def test_values(self):
        obj1 = object()
        obj2 = object()
        self.dict.declare('test', values=[obj1, obj2])

        self.dict['test'] = obj1
        self.assertIs(self.dict['test'], obj1)

        with self.assertRaises(ValueError) as context:
            self.dict['test'] = object()

        expected_msg = ("Value \(<object object at 0x[0-9A-Fa-f]+>\) of option 'test' is not one of \[<object object at 0x[0-9A-Fa-f]+>,"
                        " <object object at 0x[0-9A-Fa-f]+>\].")
        assertRegex(self, str(context.exception), expected_msg)

    def test_read_only(self):
        opt = OptionsDictionary(read_only=True)
        opt.declare('permanent', 3.0)

        with self.assertRaises(KeyError) as context:
            opt['permanent'] = 4.0

        expected_msg = ("Tried to set read-only option 'permanent'.")
        assertRegex(self, str(context.exception), expected_msg)

    def test_bounds(self):
        self.dict.declare('x', default=1.0, lower=0.0, upper=2.0)

        with self.assertRaises(ValueError) as context:
            self.dict['x'] = 3.0

        expected_msg = "Value (3.0) of option 'x' exceeds maximum allowed value of 2.0."
        self.assertEqual(str(context.exception), expected_msg)

        with self.assertRaises(ValueError) as context:
            self.dict['x'] = -3.0

        expected_msg = "Value (-3.0) of option 'x' is less than minimum allowed value of 0.0."
        self.assertEqual(str(context.exception), expected_msg)

    def test_undeclare(self):
        # create an entry in the dict
        self.dict.declare('test', types=int)
        self.dict['test'] = 1

        # prove it's in the dict
        self.assertEqual(self.dict['test'], 1)

        # remove entry from the dict
        self.dict.undeclare("test")

        # prove it is no longer in the dict
        with self.assertRaises(KeyError) as context:
            self.dict['test']

        expected_msg = "\"Option 'test' cannot be found\""
        self.assertEqual(expected_msg, str(context.exception))
Ejemplo n.º 5
0
class TestOptionsDict(unittest.TestCase):

    def setUp(self):
        self.dict = OptionsDictionary()

    def test_reprs(self):
        class MyComp(ExplicitComponent):
            pass

        my_comp = MyComp()

        self.dict.declare('test', values=['a', 'b'], desc='Test integer value')
        self.dict.declare('flag', default=False, types=bool)
        self.dict.declare('comp', default=my_comp, types=ExplicitComponent)
        self.dict.declare('long_desc', types=str,
                          desc='This description is long and verbose, so it '
                               'takes up multiple lines in the options table.')

        self.assertEqual(self.dict.__repr__(), self.dict._dict)

        self.assertEqual(self.dict.__str__(width=83), '\n'.join([
            "========= ============ ================= ===================== ====================",
            "Option    Default      Acceptable Values Acceptable Types      Description         ",
            "========= ============ ================= ===================== ====================",
            "comp      MyComp       N/A               ['ExplicitComponent']                     ",
            "flag      False        N/A               ['bool']                                  ",
            "long_desc **Required** N/A               ['str']               This description is ",
            "                                                               long and verbose, so",
            "                                                                it takes up multipl",
            "                                                               e lines in the optio",
            "                                                               ns table.",
            "test      **Required** ['a', 'b']        N/A                   Test integer value  ",
            "========= ============ ================= ===================== ====================",
        ]))

        # if the table can't be represented in specified width, then we get the full width version
        self.assertEqual(self.dict.__str__(width=40), '\n'.join([
            "========= ============ ================= ===================== ====================="
            "==================================================================== ",
            "Option    Default      Acceptable Values Acceptable Types      Description          "
            "                                                                     ",
            "========= ============ ================= ===================== ====================="
            "==================================================================== ",
            "comp      MyComp       N/A               ['ExplicitComponent']                      "
            "                                                                     ",
            "flag      False        N/A               ['bool']                                   "
            "                                                                     ",
            "long_desc **Required** N/A               ['str']               This description is l"
            "ong and verbose, so it takes up multiple lines in the options table. ",
            "test      **Required** ['a', 'b']        N/A                   Test integer value   "
            "                                                                     ",
            "========= ============ ================= ===================== ====================="
            "==================================================================== ",
        ]))


    def test_type_checking(self):
        self.dict.declare('test', types=int, desc='Test integer value')

        self.dict['test'] = 1
        self.assertEqual(self.dict['test'], 1)

        with self.assertRaises(TypeError) as context:
            self.dict['test'] = ''

        class_or_type = 'class' if PY3 else 'type'
        expected_msg = "Option 'test' has the wrong type (<{} 'int'>)".format(class_or_type)
        self.assertEqual(expected_msg, str(context.exception))

        # make sure bools work
        self.dict.declare('flag', default=False, types=bool)
        self.assertEqual(self.dict['flag'], False)
        self.dict['flag'] = True
        self.assertEqual(self.dict['flag'], True)

    def test_allow_none(self):
        self.dict.declare('test', types=int, allow_none=True, desc='Test integer value')
        self.dict['test'] = None
        self.assertEqual(self.dict['test'], None)

    def test_type_and_values(self):
        # Test with only type_
        self.dict.declare('test1', types=int)
        self.dict['test1'] = 1
        self.assertEqual(self.dict['test1'], 1)

        # Test with only values
        self.dict.declare('test2', values=['a', 'b'])
        self.dict['test2'] = 'a'
        self.assertEqual(self.dict['test2'], 'a')

        # Test with both type_ and values
        with self.assertRaises(Exception) as context:
            self.dict.declare('test3', types=int, values=['a', 'b'])
        self.assertEqual(str(context.exception),
                         "'types' and 'values' were both specified for option 'test3'.")

    def test_isvalid(self):
        self.dict.declare('even_test', types=int, is_valid=lambda x: x % 2 == 0)
        self.dict['even_test'] = 2
        self.dict['even_test'] = 4

        with self.assertRaises(ValueError) as context:
            self.dict['even_test'] = 3

        expected_msg = "Function is_valid returns False for {}.".format('even_test')
        self.assertEqual(expected_msg, str(context.exception))

    def test_isvalid_deprecated_type(self):

        with warnings.catch_warnings(record=True) as w:
            warnings.simplefilter("always")
            self.dict.declare('even_test', type_=int, is_valid=lambda x: x % 2 == 0)
            self.assertEqual(len(w), 1)
            self.assertEqual(str(w[-1].message), "In declaration of option 'even_test' the '_type' arg is deprecated.  Use 'types' instead.")

        self.dict['even_test'] = 2
        self.dict['even_test'] = 4

        with self.assertRaises(ValueError) as context:
            self.dict['even_test'] = 3

        expected_msg = "Function is_valid returns False for {}.".format('even_test')
        self.assertEqual(expected_msg, str(context.exception))

    def test_unnamed_args(self):
        with self.assertRaises(KeyError) as context:
            self.dict['test'] = 1

        # KeyError ends up with an extra set of quotes.
        expected_msg = "\"Key 'test' cannot be set because it has not been declared.\""
        self.assertEqual(expected_msg, str(context.exception))

    def test_contains(self):
        self.dict.declare('test')

        contains = 'undeclared' in self.dict
        self.assertTrue(not contains)

        contains = 'test' in self.dict
        self.assertTrue(contains)

    def test_update(self):
        self.dict.declare('test', default='Test value', types=object)

        obj = object()
        self.dict.update({'test': obj})
        self.assertIs(self.dict['test'], obj)

    def test_update_extra(self):
        with self.assertRaises(KeyError) as context:
            self.dict.update({'test': 2})

        # KeyError ends up with an extra set of quotes.
        expected_msg = "\"Key 'test' cannot be set because it has not been declared.\""
        self.assertEqual(expected_msg, str(context.exception))

    def test_get_missing(self):
        with self.assertRaises(KeyError) as context:
            self.dict['missing']

        expected_msg = "\"Option 'missing' cannot be found\""
        self.assertEqual(expected_msg, str(context.exception))

    def test_get_default(self):
        obj_def = object()
        obj_new = object()

        self.dict.declare('test', default=obj_def, types=object)

        self.assertIs(self.dict['test'], obj_def)

        self.dict['test'] = obj_new
        self.assertIs(self.dict['test'], obj_new)

    def test_values(self):
        obj1 = object()
        obj2 = object()
        self.dict.declare('test', values=[obj1, obj2])

        self.dict['test'] = obj1
        self.assertIs(self.dict['test'], obj1)

        with self.assertRaises(ValueError) as context:
            self.dict['test'] = object()

        expected_msg = ("Option 'test''s value is not one of \[<object object at 0x[0-9A-Fa-f]+>,"
                        " <object object at 0x[0-9A-Fa-f]+>\]")
        assertRegex(self, str(context.exception), expected_msg)

    def test_read_only(self):
        opt = OptionsDictionary(read_only=True)
        opt.declare('permanent', 3.0)

        with self.assertRaises(KeyError) as context:
            opt['permanent'] = 4.0

        expected_msg = ("Tried to set 'permanent' on a read-only OptionsDictionary")
        assertRegex(self, str(context.exception), expected_msg)

    def test_bounds(self):
        self.dict.declare('x', default=1.0, lower=0.0, upper=2.0)

        with self.assertRaises(ValueError) as context:
            self.dict['x'] = 3.0

        expected_msg = ("Value of 3.0 exceeds maximum of 2.0 for option 'x'")
        assertRegex(self, str(context.exception), expected_msg)

        with self.assertRaises(ValueError) as context:
            self.dict['x'] = -3.0

        expected_msg = ("Value of -3.0 exceeds minimum of 0.0 for option 'x'")
        assertRegex(self, str(context.exception), expected_msg)

    def test_undeclare(self):
        # create an entry in the dict
        self.dict.declare('test', types=int)
        self.dict['test'] = 1

        # prove it's in the dict
        self.assertEqual(self.dict['test'], 1)

        # remove entry from the dict
        self.dict.undeclare("test")

        # prove it is no longer in the dict
        with self.assertRaises(KeyError) as context:
            self.dict['test']

        expected_msg = "\"Option 'test' cannot be found\""
        self.assertEqual(expected_msg, str(context.exception))