예제 #1
0
    def test_parser_set_with_encoding(self):
        """Test schemaconfigglue override an option with a non-ascii value."""
        class MySchema(Schema):
            foo = StringOption()

        parser = SchemaConfigParser(MySchema())
        op, options, args = schemaconfigglue(parser, argv=['--foo', 'fóobâr'])
        self.assertEqual(parser.get('__main__', 'foo', parse=False), 'fóobâr')
        self.assertEqual(parser.get('__main__', 'foo'), 'fóobâr')
예제 #2
0
    def test_parser_set_with_encoding(self):
        """Test schemaconfigglue override an option with a non-ascii value."""
        class MySchema(Schema):
            foo = StringOption()

        parser = SchemaConfigParser(MySchema())
        op, options, args = schemaconfigglue(
            parser, argv=['--foo', 'fóobâr'])
        self.assertEqual(parser.get('__main__', 'foo', parse=False),
            'fóobâr')
        self.assertEqual(parser.get('__main__', 'foo'), 'fóobâr')
예제 #3
0
    def test_option_specified_twice(self):
        """Test schemaconfigglue with option name specified twice."""
        class MySchema(Schema):
            foo = IntOption(short_name='f')

        parser = SchemaConfigParser(MySchema())
        op, options, args = schemaconfigglue(
            parser, argv=['-f', '42', '--foo', '24'])
        self.assertEqual(parser.get('__main__', 'foo'), 24)
        op, options, args = schemaconfigglue(
            parser, argv=['-f', '24', '--foo', '42'])
        self.assertEqual(parser.get('__main__', 'foo'), 42)
예제 #4
0
    def test_option_specified_twice(self):
        """Test schemaconfigglue with option name specified twice."""
        class MySchema(Schema):
            foo = IntOption(short_name='f')

        parser = SchemaConfigParser(MySchema())
        op, options, args = schemaconfigglue(parser,
                                             argv=['-f', '42', '--foo', '24'])
        self.assertEqual(parser.get('__main__', 'foo'), 24)
        op, options, args = schemaconfigglue(parser,
                                             argv=['-f', '24', '--foo', '42'])
        self.assertEqual(parser.get('__main__', 'foo'), 42)
    def test_set_non_string(self):
        """Test parser.set with a non-string value."""
        class MySchema(Schema):
            foo = IntOption()
            bar = BoolOption()
        parser = SchemaConfigParser(MySchema())
        parser.parse_all()

        parser.set('__main__', 'foo', 2)
        parser.set('__main__', 'bar', False)
        self.assertEqual(parser.get('__main__', 'foo'), 2)
        self.assertEqual(parser._sections['__main__']['foo'], '2')
        self.assertEqual(parser.get('__main__', 'bar'), False)
        self.assertEqual(parser._sections['__main__']['bar'], 'False')
예제 #6
0
    def test_set_non_string(self):
        """Test parser.set with a non-string value."""
        class MySchema(Schema):
            foo = IntOption()
            bar = BoolOption()
        parser = SchemaConfigParser(MySchema())
        parser.parse_all()

        parser.set('__main__', 'foo', 2)
        parser.set('__main__', 'bar', False)
        self.assertEqual(parser.get('__main__', 'foo'), 2)
        self.assertEqual(parser._sections['__main__']['foo'], '2')
        self.assertEqual(parser.get('__main__', 'bar'), False)
        self.assertEqual(parser._sections['__main__']['bar'], 'False')
    def test_get_without_environment_var(self):
        class MySchema(Schema):
            foo = IntOption()

        config = StringIO("[__main__]\nfoo=$FOO")
        parser = SchemaConfigParser(MySchema())
        parser.readfp(config)
        self.assertEqual(parser.get('__main__', 'foo'), 0)
예제 #8
0
    def test_get_without_environment_var(self):
        class MySchema(Schema):
            foo = IntOption()

        config = StringIO("[__main__]\nfoo=$FOO")
        parser = SchemaConfigParser(MySchema())
        parser.readfp(config)
        self.assertEqual(parser.get('__main__', 'foo'), 0)
예제 #9
0
    def test_option_short_name(self):
        """Test schemaconfigglue support for short option names."""
        class MySchema(Schema):
            foo = IntOption(short_name='f')

        parser = SchemaConfigParser(MySchema())
        op, options, args = schemaconfigglue(parser, argv=['-f', '42'])
        self.assertEqual(parser.get('__main__', 'foo'), 42)
    def test_get_with_environment_var(self, mock_os):
        mock_os.environ = {'FOO': '42'}
        class MySchema(Schema):
            foo = IntOption()

        config = StringIO("[__main__]\nfoo=$FOO")
        parser = SchemaConfigParser(MySchema())
        parser.readfp(config)
        self.assertEqual(parser.get('__main__', 'foo'), 42)
예제 #11
0
    def test_option_short_name(self):
        """Test schemaconfigglue support for short option names."""
        class MySchema(Schema):
            foo = IntOption(short_name='f')

        parser = SchemaConfigParser(MySchema())
        op, options, args = schemaconfigglue(
            parser, argv=['-f', '42'])
        self.assertEqual(parser.get('__main__', 'foo'), 42)
예제 #12
0
    def test_get_with_environment_var(self, mock_os):
        mock_os.environ = {'FOO': '42'}
        class MySchema(Schema):
            foo = IntOption()

        config = StringIO("[__main__]\nfoo=$FOO")
        parser = SchemaConfigParser(MySchema())
        parser.readfp(config)
        self.assertEqual(parser.get('__main__', 'foo'), 42)
    def test_get_with_raw_value(self):
        """Test get using a raw value."""
        class MySchema(Schema):
            foo = StringOption(raw=True)
        config = StringIO('[__main__]\nfoo=blah%(asd)##$@@dddf2kjhkjs')
        expected_value = 'blah%(asd)##$@@dddf2kjhkjs'

        parser = SchemaConfigParser(MySchema())
        parser.readfp(config)
        value = parser.get('__main__', 'foo')
        self.assertEqual(value, expected_value)
예제 #14
0
    def test_extra_sections_dict_default_value(self):
        """Test parse dict with default value."""
        class MySchema(Schema):
            foo = DictOption(spec={
                'bar': IntOption(),
                'baz': BoolOption()})

        parser = SchemaConfigParser(MySchema())
        self.assertEqual(parser.get('__main__', 'foo'),
            {'bar': 0, 'baz': False})
        self.assertEqual(parser.extra_sections, set([]))
    def test_extra_sections_dict_default_value(self):
        """Test parse dict with default value."""
        class MySchema(Schema):
            foo = DictOption(spec={
                'bar': IntOption(),
                'baz': BoolOption()})

        parser = SchemaConfigParser(MySchema())
        self.assertEqual(parser.get('__main__', 'foo'),
            {'bar': 0, 'baz': False})
        self.assertEqual(parser.extra_sections, set([]))
예제 #16
0
    def test_get_with_raw_value(self):
        """Test get using a raw value."""
        class MySchema(Schema):
            foo = StringOption(raw=True)
        config = StringIO('[__main__]\nfoo=blah%(asd)##$@@dddf2kjhkjs')
        expected_value = 'blah%(asd)##$@@dddf2kjhkjs'

        parser = SchemaConfigParser(MySchema())
        parser.readfp(config)
        value = parser.get('__main__', 'foo')
        self.assertEqual(value, expected_value)
예제 #17
0
    def test_parse_option(self):
        """Test parser parses option."""
        class MyOtherSchema(Schema):
            class foo(Section):
                bar = StringOption()

        expected_value = 'baz'
        config = StringIO("[foo]\nbar = baz")
        parser = SchemaConfigParser(MyOtherSchema())
        parser.readfp(config)
        value = parser.get('foo', 'bar')
        self.assertEqual(value, expected_value)
    def test_parse_option(self):
        """Test parser parses option."""
        class MyOtherSchema(Schema):
            class foo(Section):
                bar = StringOption()

        expected_value = 'baz'
        config = StringIO("[foo]\nbar = baz")
        parser = SchemaConfigParser(MyOtherSchema())
        parser.readfp(config)
        value = parser.get('foo', 'bar')
        self.assertEqual(value, expected_value)
예제 #19
0
    def test_glue_environ_precedence_null_and_fatal_option(self):
        class MySchema(Schema):
            foo = StringOption(null=True, fatal=True)

        parser = SchemaConfigParser(MySchema())

        with patch.object(os, 'environ', {'CONFIGGLUE_FOO': '42'}):
            _argv, sys.argv = sys.argv, ['prognam']
            try:
                op, options, args = schemaconfigglue(parser)
                self.assertEqual(parser.get('__main__', 'foo'), '42')
            finally:
                sys.argv = _argv
예제 #20
0
    def test_glue_environ_precedence_null_and_fatal_option(self):
        class MySchema(Schema):
            foo = StringOption(null=True, fatal=True)

        parser = SchemaConfigParser(MySchema())

        with patch.object(os, 'environ', {'CONFIGGLUE_FOO': '42'}):
            _argv, sys.argv = sys.argv, ['prognam']
            try:
                op, options, args = schemaconfigglue(parser)
                self.assertEqual(parser.get('__main__', 'foo'), '42')
            finally:
                sys.argv = _argv
예제 #21
0
class TestSchemaConfigGlue(unittest.TestCase):
    def setUp(self):
        class MySchema(Schema):
            class foo(Section):
                bar = IntOption()

            baz = IntOption(help='The baz option')

        self.parser = SchemaConfigParser(MySchema())

    def test_glue_no_op(self):
        """Test schemaconfigglue with the default OptionParser value."""
        config = StringIO("[__main__]\nbaz=1")
        self.parser.readfp(config)
        self.assertEqual(self.parser.values(), {
            'foo': {
                'bar': 0
            },
            '__main__': {
                'baz': 1
            }
        })

        op, options, args = schemaconfigglue(self.parser, argv=['--baz', '2'])
        self.assertEqual(self.parser.values(), {
            'foo': {
                'bar': 0
            },
            '__main__': {
                'baz': 2
            }
        })

    def test_glue_no_argv(self):
        """Test schemaconfigglue with the default argv value."""
        config = StringIO("[__main__]\nbaz=1")
        self.parser.readfp(config)
        self.assertEqual(self.parser.values(), {
            'foo': {
                'bar': 0
            },
            '__main__': {
                'baz': 1
            }
        })

        _argv, sys.argv = sys.argv, []
        try:
            op, options, args = schemaconfigglue(self.parser)
            self.assertEqual(self.parser.values(), {
                'foo': {
                    'bar': 0
                },
                '__main__': {
                    'baz': 1
                }
            })
        finally:
            sys.argv = _argv

    def test_glue_section_option(self):
        """Test schemaconfigglue overriding one option."""
        config = StringIO("[foo]\nbar=1")
        self.parser.readfp(config)
        self.assertEqual(self.parser.values(), {
            'foo': {
                'bar': 1
            },
            '__main__': {
                'baz': 0
            }
        })

        op, options, args = schemaconfigglue(self.parser,
                                             argv=['--foo_bar', '2'])
        self.assertEqual(self.parser.values(), {
            'foo': {
                'bar': 2
            },
            '__main__': {
                'baz': 0
            }
        })

    def test_glue_missing_section(self):
        """Test schemaconfigglue with missing section."""
        class MySchema(Schema):
            foo = DictOption()

        config = StringIO("[__main__]\nfoo = bar")
        parser = SchemaConfigParser(MySchema())
        parser.readfp(config)

        # hitting the parser directly raises an exception
        self.assertRaises(NoSectionError, parser.values)
        self.assertFalse(parser.is_valid())

        # which is nicely handled by the glue code, so as not to crash it
        op, options, args = schemaconfigglue(parser)

        # there is no value for 'foo' due to the missing section
        self.assertEqual(options, {'foo': None})

    def test_glue_json_dict(self):
        class MySchema(Schema):
            foo = DictOption()

        parser = SchemaConfigParser(MySchema())
        op, options, args = schemaconfigglue(parser,
                                             argv=['--foo', '{"bar": "baz"}'])

        self.assertEqual(options, {'foo': '{"bar": "baz"}'})
        self.assertEqual(parser.values(),
                         {'__main__': {
                             'foo': {
                                 'bar': 'baz'
                             }
                         }})

    @patch('configglue.glue.os')
    def test_glue_environ(self, mock_os):
        mock_os.environ = {'CONFIGGLUE_FOO_BAR': '42', 'CONFIGGLUE_BAZ': 3}
        config = StringIO("[foo]\nbar=1")
        self.parser.readfp(config)

        _argv, sys.argv = sys.argv, ['prognam']
        try:
            op, options, args = schemaconfigglue(self.parser)
            self.assertEqual(self.parser.values(), {
                'foo': {
                    'bar': 42
                },
                '__main__': {
                    'baz': 3
                }
            })
        finally:
            sys.argv = _argv

    @patch('configglue.glue.os')
    def test_glue_environ_bad_name(self, mock_os):
        mock_os.environ = {'FOO_BAR': 2, 'BAZ': 3}
        config = StringIO("[foo]\nbar=1")
        self.parser.readfp(config)

        _argv, sys.argv = sys.argv, ['prognam']
        try:
            op, options, args = schemaconfigglue(self.parser)
            self.assertEqual(self.parser.values(), {
                'foo': {
                    'bar': 1
                },
                '__main__': {
                    'baz': 0
                }
            })
        finally:
            sys.argv = _argv

    def test_glue_environ_precedence(self):
        with patch.object(os, 'environ', {
                'CONFIGGLUE_FOO_BAR': '42',
                'BAR': '1'
        }):

            config = StringIO("[foo]\nbar=$BAR")
            self.parser.readfp(config)

            _argv, sys.argv = sys.argv, ['prognam']
            try:
                op, options, args = schemaconfigglue(self.parser)
                self.assertEqual(self.parser.get('foo', 'bar'), 42)
            finally:
                sys.argv = _argv

    def test_glue_environ_precedence_fatal_option(self):
        class MySchema(Schema):
            foo = IntOption(fatal=True)

        parser = SchemaConfigParser(MySchema())

        with patch.object(os, 'environ', {'CONFIGGLUE_FOO': '42'}):
            _argv, sys.argv = sys.argv, ['prognam']
            try:
                op, options, args = schemaconfigglue(parser)
                self.assertEqual(parser.get('__main__', 'foo'), 42)
            finally:
                sys.argv = _argv

    def test_glue_environ_precedence_null_option(self):
        class MySchema(Schema):
            foo = StringOption(null=True)

        parser = SchemaConfigParser(MySchema())

        with patch.object(os, 'environ', {'CONFIGGLUE_FOO': '42'}):
            _argv, sys.argv = sys.argv, ['prognam']
            try:
                op, options, args = schemaconfigglue(parser)
                self.assertEqual(parser.get('__main__', 'foo'), '42')
            finally:
                sys.argv = _argv

    def test_glue_environ_precedence_null_and_fatal_option(self):
        class MySchema(Schema):
            foo = StringOption(null=True, fatal=True)

        parser = SchemaConfigParser(MySchema())

        with patch.object(os, 'environ', {'CONFIGGLUE_FOO': '42'}):
            _argv, sys.argv = sys.argv, ['prognam']
            try:
                op, options, args = schemaconfigglue(parser)
                self.assertEqual(parser.get('__main__', 'foo'), '42')
            finally:
                sys.argv = _argv

    def test_ambiguous_option(self):
        """Test schemaconfigglue when an ambiguous option is specified."""
        class MySchema(Schema):
            class foo(Section):
                baz = IntOption()

            class bar(Section):
                baz = IntOption()

        config = StringIO("[foo]\nbaz=1")
        parser = SchemaConfigParser(MySchema())
        parser.readfp(config)
        self.assertEqual(parser.values('foo'), {'baz': 1})
        self.assertEqual(parser.values('bar'), {'baz': 0})

        op, options, args = schemaconfigglue(parser, argv=['--bar_baz', '2'])
        self.assertEqual(parser.values('foo'), {'baz': 1})
        self.assertEqual(parser.values('bar'), {'baz': 2})

    def test_help(self):
        """Test schemaconfigglue with --help."""
        config = StringIO("[foo]\nbar=1")
        self.parser.readfp(config)
        self.assertEqual(self.parser.values(), {
            'foo': {
                'bar': 1
            },
            '__main__': {
                'baz': 0
            }
        })

        # replace stdout to capture its value
        stdout = StringIO()
        _stdout = sys.stdout
        sys.stdout = stdout
        # call the method and assert its value
        self.assertRaises(SystemExit,
                          schemaconfigglue,
                          self.parser,
                          argv=['--help'])
        # replace stdout again to cleanup
        sys.stdout = _stdout

        # assert the value of stdout is correct
        stdout.seek(0)
        output = stdout.read()
        self.assertTrue(output.startswith('Usage:'))

    def test_help_with_fatal(self):
        """Test schemaconfigglue with --help and an undefined fatal option."""
        class MySchema(Schema):
            foo = IntOption(fatal=True)

        self.parser = SchemaConfigParser(MySchema())

        # replace stdout to capture its value
        stdout = StringIO()
        _stdout = sys.stdout
        sys.stdout = stdout
        # call the method and assert its value
        self.assertRaises(SystemExit,
                          schemaconfigglue,
                          self.parser,
                          argv=['--help'])
        # replace stdout again to cleanup
        sys.stdout = _stdout

        # assert the value of stdout is correct
        stdout.seek(0)
        output = stdout.read()
        self.assertTrue(output.startswith('Usage:'))

    def test_parser_set_with_encoding(self):
        """Test schemaconfigglue override an option with a non-ascii value."""
        class MySchema(Schema):
            foo = StringOption()

        parser = SchemaConfigParser(MySchema())
        op, options, args = schemaconfigglue(parser, argv=['--foo', 'fóobâr'])
        self.assertEqual(parser.get('__main__', 'foo', parse=False), 'fóobâr')
        self.assertEqual(parser.get('__main__', 'foo'), 'fóobâr')

    def test_option_short_name(self):
        """Test schemaconfigglue support for short option names."""
        class MySchema(Schema):
            foo = IntOption(short_name='f')

        parser = SchemaConfigParser(MySchema())
        op, options, args = schemaconfigglue(parser, argv=['-f', '42'])
        self.assertEqual(parser.get('__main__', 'foo'), 42)

    def test_option_conflicting_short_name(self):
        """Test schemaconfigglue with conflicting short option names."""
        class MySchema(Schema):
            foo = IntOption(short_name='f')
            flup = StringOption(short_name='f')

        parser = SchemaConfigParser(MySchema())
        self.assertRaises(OptionConflictError,
                          schemaconfigglue,
                          parser,
                          argv=['-f', '42'])

    def test_option_specified_twice(self):
        """Test schemaconfigglue with option name specified twice."""
        class MySchema(Schema):
            foo = IntOption(short_name='f')

        parser = SchemaConfigParser(MySchema())
        op, options, args = schemaconfigglue(parser,
                                             argv=['-f', '42', '--foo', '24'])
        self.assertEqual(parser.get('__main__', 'foo'), 24)
        op, options, args = schemaconfigglue(parser,
                                             argv=['-f', '24', '--foo', '42'])
        self.assertEqual(parser.get('__main__', 'foo'), 42)

    def test_fatal_option_with_config(self):
        class MySchema(Schema):
            foo = IntOption(fatal=True)

        config = StringIO("[__main__]\nfoo=1")
        parser = SchemaConfigParser(MySchema())
        parser.readfp(config)

        op, options, args = schemaconfigglue(parser)
        self.assertEqual(parser.values(), {'__main__': {'foo': 1}})
예제 #22
0
class TestSchemaConfigParser(unittest.TestCase):
    def setUp(self):
        class MySchema(Schema):
            foo = StringOption()
        self.schema = MySchema()
        self.parser = SchemaConfigParser(self.schema)
        self.config = StringIO("[__main__]\nfoo = bar")

    def test_init_no_args(self):
        self.assertRaises(TypeError, SchemaConfigParser)

    def test_init_valid_schema(self):
        self.assertEqual(self.parser.schema, self.schema)

    def test_init_invalid_schema(self):
        class MyInvalidSchema(Schema):
            class __main__(Section):
                pass

        self.assertRaises(SchemaValidationError, SchemaConfigParser,
                          MyInvalidSchema())

    def test_items(self):
        self.parser.readfp(self.config)
        items = self.parser.items('__main__')
        self.assertEqual(set(items), set([('foo', 'bar')]))

    def test_items_no_section(self):
        self.assertRaises(NoSectionError, self.parser.items, '__main__')

    def test_items_raw(self):
        config = StringIO('[__main__]\nfoo=%(baz)s')
        self.parser.readfp(config)
        items = self.parser.items('__main__', raw=True)
        self.assertEqual(set(items), set([('foo', '%(baz)s')]))

    def test_items_vars(self):
        config = StringIO('[__main__]\nfoo=%(baz)s')
        self.parser.readfp(config)
        items = self.parser.items('__main__', vars={'baz': '42'})
        self.assertEqual(set(items), set([('foo', '42'), ('baz', '42')]))

    def test_items_interpolate(self):
        """Test parser.items with interpolated values."""
        class MySchema(Schema):
            foo = StringOption()

            class baz(Section):
                bar = StringOption()

        parser = SchemaConfigParser(MySchema())
        config = StringIO('[__main__]\nfoo=%(bar)s\n[baz]\nbar=42')
        parser.readfp(config)
        # test interpolate
        items = parser.items('baz')
        self.assertEqual(items, {'bar': '42'}.items())

    def test_items_interpolate_error(self):
        config = StringIO('[__main__]\nfoo=%(bar)s')
        self.parser.readfp(config)
        self.assertRaises(InterpolationMissingOptionError, self.parser.items,
                          '__main__')

    def test_values_empty_parser(self):
        values = self.parser.values()
        self.assertEqual(values, {'__main__': {'foo': ''}})

    def test_values_full_parser(self):
        expected_values = {'__main__': {'foo': 'bar'}}

        self.parser.readfp(self.config)
        values = self.parser.values()
        self.assertEqual(expected_values, values)
        values = self.parser.values(section='__main__')
        self.assertEqual(expected_values['__main__'], values)

    def test_values_many_sections_same_option(self):
        """Test parser.values for many section with the same option."""
        class MySchema(Schema):
            class foo(Section):
                bar = IntOption()

            class baz(Section):
                bar = IntOption()

        config = StringIO("[foo]\nbar=3\n[baz]\nbar=4")
        expected_values = {'foo': {'bar': 3}, 'baz': {'bar': 4}}

        schema = MySchema()
        parser = SchemaConfigParser(schema)
        parser.readfp(config)
        values = parser.values()
        self.assertEqual(values, expected_values)

    def test_values_many_sections_different_options(self):
        """Test parser.values for many sections with different options."""
        class MySchema(Schema):
            class foo(Section):
                bar = IntOption()

            class bar(Section):
                baz = IntOption()

        config = StringIO("[foo]\nbar=3\n[bar]\nbaz=4")
        expected_values = {'foo': {'bar': 3}, 'bar': {'baz': 4}}

        schema = MySchema()
        parser = SchemaConfigParser(schema)
        parser.readfp(config)
        values = parser.values()
        self.assertEqual(values, expected_values)

    def test_parse_option(self):
        """Test parser parses option."""
        class MyOtherSchema(Schema):
            class foo(Section):
                bar = StringOption()

        expected_value = 'baz'
        config = StringIO("[foo]\nbar = baz")
        parser = SchemaConfigParser(MyOtherSchema())
        parser.readfp(config)
        value = parser.get('foo', 'bar')
        self.assertEqual(value, expected_value)

    def test_parse_invalid_section(self):
        self.assertRaises(NoSectionError, self.parser.parse,
            'bar', 'baz', '1')

    def test_default_values(self):
        """Test parser reads default option values."""
        class MySchema(Schema):
            foo = BoolOption(default=True)

            class bar(Section):
                baz = IntOption()
                bla = StringOption(default='hello')

        schema = MySchema()
        config = StringIO("[bar]\nbaz=123")
        expected_values = {'__main__': {'foo': True},
                           'bar': {'baz': 123, 'bla': 'hello'}}
        parser = SchemaConfigParser(schema)
        parser.readfp(config)
        self.assertEquals(expected_values, parser.values())

        config = StringIO("[bar]\nbla=123")
        expected = {
            '__main__': {'foo': True},
            'bar': {'baz': 0, 'bla': '123'}}
        parser = SchemaConfigParser(schema)
        parser.readfp(config)
        self.assertEquals(expected, parser.values())

    def test_fatal_options(self):
        """Test parsing non-provided options marked as fatal."""
        class MySchema(Schema):
            foo = IntOption(fatal=True)
            bar = IntOption()
        schema = MySchema()
        config = StringIO("[__main__]\nfoo=123")
        expected = {'__main__': {'foo': 123, 'bar': 0}}
        parser = SchemaConfigParser(schema)
        parser.readfp(config)
        self.assertEquals(expected, parser.values())

        config = StringIO("[__main__]\nbar=123")
        parser = SchemaConfigParser(schema)
        parser.readfp(config)
        self.assertRaises(NoOptionError, parser.values)

    def test_extra_sections(self):
        """Test extra_sections."""
        class MySchema(Schema):
            foo = DictOption(spec={'bar': IntOption()})

        config = StringIO("[__main__]\nfoo=mydict\n[mydict]\nbar=1")
        parser = SchemaConfigParser(MySchema())
        parser.readfp(config)
        parser.parse_all()

        expected_sections = set(['mydict'])
        extra_sections = parser.extra_sections
        self.assertEqual(expected_sections, extra_sections)

    def test_extra_sections_dict_default_value(self):
        """Test parse dict with default value."""
        class MySchema(Schema):
            foo = DictOption(spec={
                'bar': IntOption(),
                'baz': BoolOption()})

        parser = SchemaConfigParser(MySchema())
        self.assertEqual(parser.get('__main__', 'foo'),
            {'bar': 0, 'baz': False})
        self.assertEqual(parser.extra_sections, set([]))

    def test_extra_sections_missing_section(self):
        """Test parse dict with missing referenced section."""
        class MySchema(Schema):
            foo = DictOption()

        config = StringIO(textwrap.dedent("""
            [__main__]
            foo = dict1
            """))
        parser = SchemaConfigParser(MySchema())
        parser.readfp(config)
        parser.parse_all()

        self.assertEqual(parser.extra_sections, set(['dict1']))

    def test_multiple_extra_sections(self):
        """Test parsing multiple extra sections."""
        class MySchema(Schema):
            foo = ListOption(
                item=DictOption(spec={'bar': IntOption()}))

        config = StringIO('[__main__]\nfoo=d1\n    d2\n    d3\n'
                          '[d1]\nbar=1\n[d2]\nbar=2\n[d3]\nbar=3')
        parser = SchemaConfigParser(MySchema())
        parser.readfp(config)
        parser.parse_all()

        expected_sections = set(['d1', 'd2', 'd3'])
        extra_sections = parser.extra_sections
        self.assertEqual(expected_sections, extra_sections)

    def test_get_default(self):
        config = StringIO("[__main__]\n")
        expected = ''
        self.parser.readfp(config)
        default = self.parser._get_default('__main__', 'foo')
        self.assertEqual(default, expected)

    def test_get_default_noschema(self):
        config = StringIO("[__noschema__]\nbar=1\n[__main__]\n")
        expected = '1'
        self.parser.readfp(config)
        default = self.parser._get_default('__noschema__', 'bar')
        self.assertEqual(default, expected)

    def test_get_default_from_section(self):
        """Test parser._get_default for a section/option pair."""
        class MySchema(Schema):
            class foo(Section):
                bar = IntOption()
        config = StringIO("[__main__]\n")
        expected = 0

        parser = SchemaConfigParser(MySchema())
        parser.readfp(config)
        default = parser._get_default('foo', 'bar')
        self.assertEqual(default, expected)

    def test_get_default_no_option(self):
        self.assertRaises(NoOptionError, self.parser._get_default,
            '__main__', 'bar')

    def test_get_default_no_section(self):
        self.assertRaises(NoSectionError, self.parser._get_default,
            'foo', 'bar')

    def test_multi_file_dict_config(self):
        """Test parsing a dict option spanning multiple files."""
        class MySchema(Schema):
            foo = DictOption(spec={
                'bar': IntOption(),
                'baz': IntOption(),
            }, strict=True)
        config1 = StringIO('[__main__]\nfoo=mydict\n[mydict]\nbar=1\nbaz=1')
        config2 = StringIO('[mydict]\nbaz=2')
        expected_values = {'__main__': {'foo': {'bar': 1, 'baz': 2}}}

        parser = SchemaConfigParser(MySchema())
        parser.readfp(config1)
        parser.readfp(config2)
        self.assertEqual(parser.values(), expected_values)

    def test_multi_file_dict_list_config(self):
        """Test parsing a list of dicts option spanning multiple files."""
        class MySchema(Schema):
            foo = ListOption(
                item=DictOption(spec={
                    'bar': IntOption(),
                    'baz': IntOption(),
                }, strict=True))

        config1 = StringIO('[__main__]\nfoo=mydict\n[mydict]\nbar=1\nbaz=1')
        expected_values = {'__main__': {'foo': [{'bar': 1, 'baz': 1}]}}

        parser = SchemaConfigParser(MySchema())
        parser.readfp(config1)
        self.assertEqual(parser.values(), expected_values)

        # override used dictionaries
        config2 = StringIO('[__main__]\nfoo=otherdict\n[otherdict]\nbar=2')
        expected_values = {'__main__': {'foo': [{'bar': 2, 'baz': 0}]}}
        parser.readfp(config2)
        self.assertEqual(parser.values(), expected_values)

        # override existing dictionaries
        config3 = StringIO('[otherdict]\nbaz=3')
        expected_values = {'__main__': {'foo': [{'bar': 2, 'baz': 3}]}}
        parser.readfp(config3)
        self.assertEqual(parser.values(), expected_values)

        # reuse existing dict
        config4 = StringIO('[__main__]\nfoo=mydict\n    otherdict')
        expected_values = {'__main__': {'foo': [{'bar': 1, 'baz': 1},
                                               {'bar': 2, 'baz': 3}]}}
        parser.readfp(config4)
        self.assertEqual(parser.values(), expected_values)

    def test_read_multiple_files(self):
        def setup_config():
            folder = tempfile.mkdtemp()

            f = open("%s/first.cfg" % folder, 'w')
            f.write("[__main__]\nfoo=foo")
            f.close()

            f = open("%s/second.cfg" % folder, 'w')
            f.write("[__main__]\nfoo=bar")
            f.close()

            files = ["%s/first.cfg" % folder, "%s/second.cfg" % folder]
            return files, folder

        files, folder = setup_config()
        self.parser.read(files)
        self.assertEqual(self.parser.values(), {'__main__': {'foo': 'bar'}})

        # silently remove any created files
        try:
            shutil.rmtree(folder)
        except:
            pass

    def test_read_utf8_encoded_file(self):
        # create config file
        fp, filename = tempfile.mkstemp()

        try:
            f = open(filename, 'w')
            f.write(u'[__main__]\nfoo=€'.encode(CONFIG_FILE_ENCODING))
            f.close()

            self.parser.read(filename)
            self.assertEqual(self.parser.values(),
                {'__main__': {'foo': u'€'}})
        finally:
            # destroy config file
            os.remove(filename)

    def test_readfp_with_utf8_encoded_text(self):
        config = StringIO(u'[__main__]\nfoo=€'.encode(CONFIG_FILE_ENCODING))
        self.parser.readfp(config)
        self.assertEqual(self.parser.values(), {'__main__': {'foo': u'€'}})

    def test_set(self):
        with tempfile.NamedTemporaryFile() as f:
            f.write('[__main__]\nfoo=1')
            f.flush()

            self.parser.read(f.name)
            self.assertEqual(self.parser._dirty, {})
            self.assertEqual(self.parser.get('__main__', 'foo'), '1')
            self.parser.set('__main__', 'foo', '2')
            self.assertEqual(self.parser.get('__main__', 'foo'), '2')
            self.assertEqual(self.parser._dirty,
                {f.name: {'__main__': {'foo': '2'}}})

    def test_set_non_string(self):
        """Test parser.set with a non-string value."""
        class MySchema(Schema):
            foo = IntOption()
            bar = BoolOption()
        parser = SchemaConfigParser(MySchema())
        parser.parse_all()

        parser.set('__main__', 'foo', 2)
        parser.set('__main__', 'bar', False)
        self.assertEqual(parser.get('__main__', 'foo'), 2)
        self.assertEqual(parser._sections['__main__']['foo'], '2')
        self.assertEqual(parser.get('__main__', 'bar'), False)
        self.assertEqual(parser._sections['__main__']['bar'], 'False')

    def test_set_invalid_type(self):
        self.parser.parse_all()
        self.assertRaises(TypeError, self.parser.set, '__main__', 'foo', 2)

    def test_write(self):
        """Test parser write config to a file."""
        class MySchema(Schema):
            foo = StringOption()

            class DEFAULTSECT(Section):
                pass

        parser = SchemaConfigParser(MySchema())
        expected = u"[{0}]\nbaz = 2\n\n[__main__]\nfoo = bar".format(
            DEFAULTSECT)
        config = StringIO(expected)
        parser.readfp(config)

        # create config file
        fp, filename = tempfile.mkstemp()
        try:
            parser.write(open(filename, 'w'))
            result = open(filename, 'r').read().strip()
            self.assertEqual(result, expected)
        finally:
            # remove the file
            os.unlink(filename)

    def test_write_prefill_parser(self):
        """Test parser write config to a file."""
        class MySchema(Schema):
            foo = IntOption()

        parser = SchemaConfigParser(MySchema())
        expected = u"[__main__]\nfoo = 0"

        # create config file
        fp, filename = tempfile.mkstemp()
        try:
            parser.write(open(filename, 'w'))
            result = open(filename, 'r').read().strip()
            self.assertEqual(result, expected)
        finally:
            # remove the file
            os.unlink(filename)

    def test_save_config(self):
        expected = u'[__main__]\nfoo = 42'
        self._check_save_file(expected)

    def test_save_config_non_ascii(self):
        expected = u'[__main__]\nfoo = fóobâr'
        self._check_save_file(expected)

    def _check_save_file(self, expected, read_config=True):
        config = StringIO(expected.encode(CONFIG_FILE_ENCODING))
        if read_config:
            self.parser.readfp(config)

        # create config file
        fp, filename = tempfile.mkstemp()
        try:
            self.parser.save(open(filename, 'w'))
            result = open(filename, 'r').read().strip()
            self.assertEqual(result.decode(CONFIG_FILE_ENCODING), expected)

            self.parser.save(filename)
            result = open(filename, 'r').read().strip()
            self.assertEqual(result.decode(CONFIG_FILE_ENCODING), expected)
        finally:
            # remove the file
            os.unlink(filename)

    def test_save_config_prefill_parser(self):
        """Test parser save config when no config files read."""
        expected = u'[__main__]\nfoo ='
        self._check_save_file(expected, read_config=False)

    def test_save_no_config_same_files(self):
        class MySchema(Schema):
            foo = IntOption()

        parser = SchemaConfigParser(MySchema())
        parser.set('__main__', 'foo', 2)
        self.assertRaises(ValueError, parser.save)

    def test_save_config_same_files(self):
        """Test parser save config values to original files."""
        def setup_config():
            folder = tempfile.mkdtemp()

            f = open("%s/first.cfg" % folder, 'w')
            f.write("[__main__]\nfoo=1")
            f.close()

            f = open("%s/second.cfg" % folder, 'w')
            f.write("[__main__]\nbar=2")
            f.close()

            files = ["%s/first.cfg" % folder, "%s/second.cfg" % folder]
            return files, folder

        class MySchema(Schema):
            foo = StringOption()
            bar = StringOption()
            baz = IntOption()

        self.parser = SchemaConfigParser(MySchema())

        files, folder = setup_config()
        self.parser.read(files)
        self.parser.set('__main__', 'foo', '42')
        self.parser.set('__main__', 'bar', '42')
        self.parser.set('__main__', 'baz', 42)
        self.parser.save()

        # test the changes were correctly saved
        data = open("%s/first.cfg" % folder).read()
        self.assertTrue('foo = 42' in data)
        self.assertFalse('bar = 42' in data)
        # new value goes into last read config file
        self.assertFalse('baz = 42' in data)
        data = open("%s/second.cfg" % folder).read()
        self.assertFalse('foo = 42' in data)
        self.assertTrue('bar = 42' in data)
        # new value goes into last read config file
        self.assertTrue('baz = 42' in data)

        # silently remove any created files
        try:
            shutil.rmtree(folder)
        except:
            pass

    def test_save_config_last_location_nested_includes(self):
        def setup_config():
            folder = tempfile.mkdtemp()

            f = open("%s/first.cfg" % folder, 'w')
            f.write("[__main__]\nfoo=1")
            f.close()

            f = open("%s/second.cfg" % folder, 'w')
            f.write("[__main__]\nbar=2\nincludes = third.cfg")
            f.close()

            f = open("%s/third.cfg" % folder, 'w')
            f.write("[__main__]\nfoo=3")
            f.close()

            files = ["%s/first.cfg" % folder, "%s/second.cfg" % folder]
            return files, folder

        class MySchema(Schema):
            foo = StringOption()
            bar = StringOption()
            baz = IntOption()

        self.parser = SchemaConfigParser(MySchema())

        files, folder = setup_config()
        self.parser.read(files)
        self.parser.set('__main__', 'foo', '42')
        self.parser.set('__main__', 'bar', '42')
        self.parser.set('__main__', 'baz', 42)
        self.parser.save()

        # test the changes were correctly saved
        data = open("%s/first.cfg" % folder).read()
        self.assertEqual(data.strip(), '[__main__]\nfoo=1')
        data = open("%s/third.cfg" % folder).read()
        self.assertEqual(data.strip(), '[__main__]\nfoo = 42')
        data = open("%s/second.cfg" % folder).read()
        self.assertTrue('bar = 42' in data)
        # new value goes into last read config file
        # not in the last included config file
        self.assertTrue('baz = 42' in data)

        # silently remove any created files
        try:
            shutil.rmtree(folder)
        except:
            pass
예제 #23
0
class TestSchemaConfigGlue(unittest.TestCase):
    def setUp(self):
        class MySchema(Schema):
            class foo(Section):
                bar = IntOption()

            baz = IntOption(help='The baz option')

        self.parser = SchemaConfigParser(MySchema())

    def test_glue_no_op(self):
        """Test schemaconfigglue with the default OptionParser value."""
        config = StringIO("[__main__]\nbaz=1")
        self.parser.readfp(config)
        self.assertEqual(self.parser.values(),
            {'foo': {'bar': 0}, '__main__': {'baz': 1}})

        op, options, args = schemaconfigglue(self.parser, argv=['--baz', '2'])
        self.assertEqual(self.parser.values(),
            {'foo': {'bar': 0}, '__main__': {'baz': 2}})

    def test_glue_no_argv(self):
        """Test schemaconfigglue with the default argv value."""
        config = StringIO("[__main__]\nbaz=1")
        self.parser.readfp(config)
        self.assertEqual(self.parser.values(),
            {'foo': {'bar': 0}, '__main__': {'baz': 1}})

        _argv, sys.argv = sys.argv, []
        try:
            op, options, args = schemaconfigglue(self.parser)
            self.assertEqual(self.parser.values(),
                {'foo': {'bar': 0}, '__main__': {'baz': 1}})
        finally:
            sys.argv = _argv

    def test_glue_section_option(self):
        """Test schemaconfigglue overriding one option."""
        config = StringIO("[foo]\nbar=1")
        self.parser.readfp(config)
        self.assertEqual(self.parser.values(),
            {'foo': {'bar': 1}, '__main__': {'baz': 0}})

        op, options, args = schemaconfigglue(self.parser,
                                             argv=['--foo_bar', '2'])
        self.assertEqual(self.parser.values(),
                         {'foo': {'bar': 2}, '__main__': {'baz': 0}})

    def test_glue_missing_section(self):
        """Test schemaconfigglue with missing section."""
        class MySchema(Schema):
            foo = DictOption()

        config = StringIO("[__main__]\nfoo = bar")
        parser = SchemaConfigParser(MySchema())
        parser.readfp(config)

        # hitting the parser directly raises an exception
        self.assertRaises(NoSectionError, parser.values)
        self.assertFalse(parser.is_valid())

        # which is nicely handled by the glue code, so as not to crash it
        op, options, args = schemaconfigglue(parser)

        # there is no value for 'foo' due to the missing section
        self.assertEqual(options, {'foo': None})

    def test_glue_json_dict(self):
        class MySchema(Schema):
            foo = DictOption()

        parser = SchemaConfigParser(MySchema())
        op, options, args = schemaconfigglue(parser,
            argv=['--foo', '{"bar": "baz"}'])

        self.assertEqual(options, {'foo': '{"bar": "baz"}'})
        self.assertEqual(parser.values(),
            {'__main__': {'foo': {'bar': 'baz'}}})

    @patch('configglue.glue.os')
    def test_glue_environ(self, mock_os):
        mock_os.environ = {'CONFIGGLUE_FOO_BAR': '42', 'CONFIGGLUE_BAZ': 3}
        config = StringIO("[foo]\nbar=1")
        self.parser.readfp(config)

        _argv, sys.argv = sys.argv, ['prognam']
        try:
            op, options, args = schemaconfigglue(self.parser)
            self.assertEqual(self.parser.values(),
                {'foo': {'bar': 42}, '__main__': {'baz': 3}})
        finally:
            sys.argv = _argv

    @patch('configglue.glue.os')
    def test_glue_environ_bad_name(self, mock_os):
        mock_os.environ = {'FOO_BAR': 2, 'BAZ': 3}
        config = StringIO("[foo]\nbar=1")
        self.parser.readfp(config)

        _argv, sys.argv = sys.argv, ['prognam']
        try:
            op, options, args = schemaconfigglue(self.parser)
            self.assertEqual(self.parser.values(),
                {'foo': {'bar': 1}, '__main__': {'baz': 0}})
        finally:
            sys.argv = _argv

    def test_glue_environ_precedence(self):
        with patch.object(os, 'environ',
            {'CONFIGGLUE_FOO_BAR': '42', 'BAR': '1'}):

            config = StringIO("[foo]\nbar=$BAR")
            self.parser.readfp(config)

            _argv, sys.argv = sys.argv, ['prognam']
            try:
                op, options, args = schemaconfigglue(self.parser)
                self.assertEqual(self.parser.get('foo', 'bar'), 42)
            finally:
                sys.argv = _argv

    def test_glue_environ_precedence_fatal_option(self):
        class MySchema(Schema):
            foo = IntOption(fatal=True)

        parser = SchemaConfigParser(MySchema())

        with patch.object(os, 'environ', {'CONFIGGLUE_FOO': '42'}):
            _argv, sys.argv = sys.argv, ['prognam']
            try:
                op, options, args = schemaconfigglue(parser)
                self.assertEqual(parser.get('__main__', 'foo'), 42)
            finally:
                sys.argv = _argv

    def test_glue_environ_precedence_null_option(self):
        class MySchema(Schema):
            foo = StringOption(null=True)

        parser = SchemaConfigParser(MySchema())

        with patch.object(os, 'environ', {'CONFIGGLUE_FOO': '42'}):
            _argv, sys.argv = sys.argv, ['prognam']
            try:
                op, options, args = schemaconfigglue(parser)
                self.assertEqual(parser.get('__main__', 'foo'), '42')
            finally:
                sys.argv = _argv

    def test_glue_environ_precedence_null_and_fatal_option(self):
        class MySchema(Schema):
            foo = StringOption(null=True, fatal=True)

        parser = SchemaConfigParser(MySchema())

        with patch.object(os, 'environ', {'CONFIGGLUE_FOO': '42'}):
            _argv, sys.argv = sys.argv, ['prognam']
            try:
                op, options, args = schemaconfigglue(parser)
                self.assertEqual(parser.get('__main__', 'foo'), '42')
            finally:
                sys.argv = _argv

    def test_ambiguous_option(self):
        """Test schemaconfigglue when an ambiguous option is specified."""
        class MySchema(Schema):
            class foo(Section):
                baz = IntOption()

            class bar(Section):
                baz = IntOption()

        config = StringIO("[foo]\nbaz=1")
        parser = SchemaConfigParser(MySchema())
        parser.readfp(config)
        self.assertEqual(parser.values('foo'), {'baz': 1})
        self.assertEqual(parser.values('bar'), {'baz': 0})

        op, options, args = schemaconfigglue(
            parser, argv=['--bar_baz', '2'])
        self.assertEqual(parser.values('foo'), {'baz': 1})
        self.assertEqual(parser.values('bar'), {'baz': 2})

    def test_help(self):
        """Test schemaconfigglue with --help."""
        config = StringIO("[foo]\nbar=1")
        self.parser.readfp(config)
        self.assertEqual(self.parser.values(),
            {'foo': {'bar': 1}, '__main__': {'baz': 0}})

        # replace stdout to capture its value
        stdout = StringIO()
        _stdout = sys.stdout
        sys.stdout = stdout
        # call the method and assert its value
        self.assertRaises(SystemExit, schemaconfigglue, self.parser,
            argv=['--help'])
        # replace stdout again to cleanup
        sys.stdout = _stdout

        # assert the value of stdout is correct
        stdout.seek(0)
        output = stdout.read()
        self.assertTrue(output.startswith('Usage:'))

    def test_help_with_fatal(self):
        """Test schemaconfigglue with --help and an undefined fatal option."""
        class MySchema(Schema):
            foo = IntOption(fatal=True)

        self.parser = SchemaConfigParser(MySchema())

        # replace stdout to capture its value
        stdout = StringIO()
        _stdout = sys.stdout
        sys.stdout = stdout
        # call the method and assert its value
        self.assertRaises(SystemExit, schemaconfigglue, self.parser,
            argv=['--help'])
        # replace stdout again to cleanup
        sys.stdout = _stdout

        # assert the value of stdout is correct
        stdout.seek(0)
        output = stdout.read()
        self.assertTrue(output.startswith('Usage:'))

    def test_parser_set_with_encoding(self):
        """Test schemaconfigglue override an option with a non-ascii value."""
        class MySchema(Schema):
            foo = StringOption()

        parser = SchemaConfigParser(MySchema())
        op, options, args = schemaconfigglue(
            parser, argv=['--foo', 'fóobâr'])
        self.assertEqual(parser.get('__main__', 'foo', parse=False),
            'fóobâr')
        self.assertEqual(parser.get('__main__', 'foo'), 'fóobâr')

    def test_option_short_name(self):
        """Test schemaconfigglue support for short option names."""
        class MySchema(Schema):
            foo = IntOption(short_name='f')

        parser = SchemaConfigParser(MySchema())
        op, options, args = schemaconfigglue(
            parser, argv=['-f', '42'])
        self.assertEqual(parser.get('__main__', 'foo'), 42)

    def test_option_conflicting_short_name(self):
        """Test schemaconfigglue with conflicting short option names."""
        class MySchema(Schema):
            foo = IntOption(short_name='f')
            flup = StringOption(short_name='f')

        parser = SchemaConfigParser(MySchema())
        self.assertRaises(OptionConflictError, schemaconfigglue, parser,
            argv=['-f', '42'])

    def test_option_specified_twice(self):
        """Test schemaconfigglue with option name specified twice."""
        class MySchema(Schema):
            foo = IntOption(short_name='f')

        parser = SchemaConfigParser(MySchema())
        op, options, args = schemaconfigglue(
            parser, argv=['-f', '42', '--foo', '24'])
        self.assertEqual(parser.get('__main__', 'foo'), 24)
        op, options, args = schemaconfigglue(
            parser, argv=['-f', '24', '--foo', '42'])
        self.assertEqual(parser.get('__main__', 'foo'), 42)

    def test_fatal_option_with_config(self):
        class MySchema(Schema):
            foo = IntOption(fatal=True)

        config = StringIO("[__main__]\nfoo=1")
        parser = SchemaConfigParser(MySchema())
        parser.readfp(config)

        op, options, args = schemaconfigglue(parser)
        self.assertEqual(parser.values(), {'__main__': {'foo': 1}})
class TestSchemaConfigParser(unittest.TestCase):
    def setUp(self):
        class MySchema(Schema):
            foo = StringOption()
        self.schema = MySchema()
        self.parser = SchemaConfigParser(self.schema)
        self.config = StringIO("[__main__]\nfoo = bar")

    def test_init_no_args(self):
        self.assertRaises(TypeError, SchemaConfigParser)

    def test_init_valid_schema(self):
        self.assertEqual(self.parser.schema, self.schema)

    def test_init_invalid_schema(self):
        class MyInvalidSchema(Schema):
            class __main__(Section):
                pass

        self.assertRaises(SchemaValidationError, SchemaConfigParser,
                          MyInvalidSchema())

    def test_items(self):
        self.parser.readfp(self.config)
        items = self.parser.items('__main__')
        self.assertEqual(set(items), set([('foo', 'bar')]))

    def test_items_no_section(self):
        self.assertRaises(NoSectionError, self.parser.items, '__main__')

    def test_items_raw(self):
        config = StringIO('[__main__]\nfoo=%(baz)s')
        self.parser.readfp(config)
        items = self.parser.items('__main__', raw=True)
        self.assertEqual(set(items), set([('foo', '%(baz)s')]))

    def test_items_vars(self):
        config = StringIO('[__main__]\nfoo=%(baz)s')
        self.parser.readfp(config)
        items = self.parser.items('__main__', vars={'baz': '42'})
        self.assertEqual(set(items), set([('foo', '42'), ('baz', '42')]))

    def test_items_interpolate(self):
        """Test parser.items with interpolated values."""
        class MySchema(Schema):
            foo = StringOption()

            class baz(Section):
                bar = StringOption()

        parser = SchemaConfigParser(MySchema())
        config = StringIO('[__main__]\nfoo=%(bar)s\n[baz]\nbar=42')
        parser.readfp(config)
        # test interpolate
        items = parser.items('baz')
        self.assertEqual(items, {'bar': '42'}.items())

    def test_items_interpolate_error(self):
        config = StringIO('[__main__]\nfoo=%(bar)s')
        self.parser.readfp(config)
        self.assertRaises(InterpolationMissingOptionError, self.parser.items,
                          '__main__')

    def test_values_empty_parser(self):
        values = self.parser.values()
        self.assertEqual(values, {'__main__': {'foo': ''}})

    def test_values_full_parser(self):
        expected_values = {'__main__': {'foo': 'bar'}}

        self.parser.readfp(self.config)
        values = self.parser.values()
        self.assertEqual(expected_values, values)
        values = self.parser.values(section='__main__')
        self.assertEqual(expected_values['__main__'], values)

    def test_values_many_sections_same_option(self):
        """Test parser.values for many section with the same option."""
        class MySchema(Schema):
            class foo(Section):
                bar = IntOption()

            class baz(Section):
                bar = IntOption()

        config = StringIO("[foo]\nbar=3\n[baz]\nbar=4")
        expected_values = {'foo': {'bar': 3}, 'baz': {'bar': 4}}

        schema = MySchema()
        parser = SchemaConfigParser(schema)
        parser.readfp(config)
        values = parser.values()
        self.assertEqual(values, expected_values)

    def test_values_many_sections_different_options(self):
        """Test parser.values for many sections with different options."""
        class MySchema(Schema):
            class foo(Section):
                bar = IntOption()

            class bar(Section):
                baz = IntOption()

        config = StringIO("[foo]\nbar=3\n[bar]\nbaz=4")
        expected_values = {'foo': {'bar': 3}, 'bar': {'baz': 4}}

        schema = MySchema()
        parser = SchemaConfigParser(schema)
        parser.readfp(config)
        values = parser.values()
        self.assertEqual(values, expected_values)

    def test_parse_option(self):
        """Test parser parses option."""
        class MyOtherSchema(Schema):
            class foo(Section):
                bar = StringOption()

        expected_value = 'baz'
        config = StringIO("[foo]\nbar = baz")
        parser = SchemaConfigParser(MyOtherSchema())
        parser.readfp(config)
        value = parser.get('foo', 'bar')
        self.assertEqual(value, expected_value)

    def test_parse_invalid_section(self):
        self.assertRaises(NoSectionError, self.parser.parse,
            'bar', 'baz', '1')

    def test_default_values(self):
        """Test parser reads default option values."""
        class MySchema(Schema):
            foo = BoolOption(default=True)

            class bar(Section):
                baz = IntOption()
                bla = StringOption(default='hello')

        schema = MySchema()
        config = StringIO("[bar]\nbaz=123")
        expected_values = {'__main__': {'foo': True},
                           'bar': {'baz': 123, 'bla': 'hello'}}
        parser = SchemaConfigParser(schema)
        parser.readfp(config)
        self.assertEquals(expected_values, parser.values())

        config = StringIO("[bar]\nbla=123")
        expected = {
            '__main__': {'foo': True},
            'bar': {'baz': 0, 'bla': '123'}}
        parser = SchemaConfigParser(schema)
        parser.readfp(config)
        self.assertEquals(expected, parser.values())

    def test_fatal_options(self):
        """Test parsing non-provided options marked as fatal."""
        class MySchema(Schema):
            foo = IntOption(fatal=True)
            bar = IntOption()
        schema = MySchema()
        config = StringIO("[__main__]\nfoo=123")
        expected = {'__main__': {'foo': 123, 'bar': 0}}
        parser = SchemaConfigParser(schema)
        parser.readfp(config)
        self.assertEquals(expected, parser.values())

        config = StringIO("[__main__]\nbar=123")
        parser = SchemaConfigParser(schema)
        parser.readfp(config)
        self.assertRaises(NoOptionError, parser.values)

    def test_extra_sections(self):
        """Test extra_sections."""
        class MySchema(Schema):
            foo = DictOption(spec={'bar': IntOption()})

        config = StringIO("[__main__]\nfoo=mydict\n[mydict]\nbar=1")
        parser = SchemaConfigParser(MySchema())
        parser.readfp(config)
        parser.parse_all()

        expected_sections = set(['mydict'])
        extra_sections = parser.extra_sections
        self.assertEqual(expected_sections, extra_sections)

    def test_extra_sections_dict_default_value(self):
        """Test parse dict with default value."""
        class MySchema(Schema):
            foo = DictOption(spec={
                'bar': IntOption(),
                'baz': BoolOption()})

        parser = SchemaConfigParser(MySchema())
        self.assertEqual(parser.get('__main__', 'foo'),
            {'bar': 0, 'baz': False})
        self.assertEqual(parser.extra_sections, set([]))

    def test_extra_sections_missing_section(self):
        """Test parse dict with missing referenced section."""
        class MySchema(Schema):
            foo = DictOption()

        config = StringIO(textwrap.dedent("""
            [__main__]
            foo = dict1
            """))
        parser = SchemaConfigParser(MySchema())
        parser.readfp(config)
        parser.parse_all()

        self.assertEqual(parser.extra_sections, set(['dict1']))

    def test_multiple_extra_sections(self):
        """Test parsing multiple extra sections."""
        class MySchema(Schema):
            foo = ListOption(
                item=DictOption(spec={'bar': IntOption()}))

        config = StringIO('[__main__]\nfoo=d1\n    d2\n    d3\n'
                          '[d1]\nbar=1\n[d2]\nbar=2\n[d3]\nbar=3')
        parser = SchemaConfigParser(MySchema())
        parser.readfp(config)
        parser.parse_all()

        expected_sections = set(['d1', 'd2', 'd3'])
        extra_sections = parser.extra_sections
        self.assertEqual(expected_sections, extra_sections)

    def test_get_default(self):
        config = StringIO("[__main__]\n")
        expected = ''
        self.parser.readfp(config)
        default = self.parser._get_default('__main__', 'foo')
        self.assertEqual(default, expected)

    def test_get_default_noschema(self):
        config = StringIO("[__noschema__]\nbar=1\n[__main__]\n")
        expected = '1'
        self.parser.readfp(config)
        default = self.parser._get_default('__noschema__', 'bar')
        self.assertEqual(default, expected)

    def test_get_default_from_section(self):
        """Test parser._get_default for a section/option pair."""
        class MySchema(Schema):
            class foo(Section):
                bar = IntOption()
        config = StringIO("[__main__]\n")
        expected = 0

        parser = SchemaConfigParser(MySchema())
        parser.readfp(config)
        default = parser._get_default('foo', 'bar')
        self.assertEqual(default, expected)

    def test_get_default_no_option(self):
        self.assertRaises(NoOptionError, self.parser._get_default,
            '__main__', 'bar')

    def test_get_default_no_section(self):
        self.assertRaises(NoSectionError, self.parser._get_default,
            'foo', 'bar')

    def test_multi_file_dict_config(self):
        """Test parsing a dict option spanning multiple files."""
        class MySchema(Schema):
            foo = DictOption(spec={
                'bar': IntOption(),
                'baz': IntOption(),
            }, strict=True)
        config1 = StringIO('[__main__]\nfoo=mydict\n[mydict]\nbar=1\nbaz=1')
        config2 = StringIO('[mydict]\nbaz=2')
        expected_values = {'__main__': {'foo': {'bar': 1, 'baz': 2}}}

        parser = SchemaConfigParser(MySchema())
        parser.readfp(config1)
        parser.readfp(config2)
        self.assertEqual(parser.values(), expected_values)

    def test_multi_file_dict_list_config(self):
        """Test parsing a list of dicts option spanning multiple files."""
        class MySchema(Schema):
            foo = ListOption(
                item=DictOption(spec={
                    'bar': IntOption(),
                    'baz': IntOption(),
                }, strict=True))

        config1 = StringIO('[__main__]\nfoo=mydict\n[mydict]\nbar=1\nbaz=1')
        expected_values = {'__main__': {'foo': [{'bar': 1, 'baz': 1}]}}

        parser = SchemaConfigParser(MySchema())
        parser.readfp(config1)
        self.assertEqual(parser.values(), expected_values)

        # override used dictionaries
        config2 = StringIO('[__main__]\nfoo=otherdict\n[otherdict]\nbar=2')
        expected_values = {'__main__': {'foo': [{'bar': 2, 'baz': 0}]}}
        parser.readfp(config2)
        self.assertEqual(parser.values(), expected_values)

        # override existing dictionaries
        config3 = StringIO('[otherdict]\nbaz=3')
        expected_values = {'__main__': {'foo': [{'bar': 2, 'baz': 3}]}}
        parser.readfp(config3)
        self.assertEqual(parser.values(), expected_values)

        # reuse existing dict
        config4 = StringIO('[__main__]\nfoo=mydict\n    otherdict')
        expected_values = {'__main__': {'foo': [{'bar': 1, 'baz': 1},
                                               {'bar': 2, 'baz': 3}]}}
        parser.readfp(config4)
        self.assertEqual(parser.values(), expected_values)

    def test_read_multiple_files(self):
        def setup_config():
            folder = tempfile.mkdtemp()

            f = open("%s/first.cfg" % folder, 'w')
            f.write("[__main__]\nfoo=foo")
            f.close()

            f = open("%s/second.cfg" % folder, 'w')
            f.write("[__main__]\nfoo=bar")
            f.close()

            files = ["%s/first.cfg" % folder, "%s/second.cfg" % folder]
            return files, folder

        files, folder = setup_config()
        self.parser.read(files)
        self.assertEqual(self.parser.values(), {'__main__': {'foo': 'bar'}})

        # silently remove any created files
        try:
            shutil.rmtree(folder)
        except:
            pass

    def test_read_utf8_encoded_file(self):
        # create config file
        fp, filename = tempfile.mkstemp()

        try:
            f = open(filename, 'w')
            f.write(u'[__main__]\nfoo=€'.encode(CONFIG_FILE_ENCODING))
            f.close()

            self.parser.read(filename)
            self.assertEqual(self.parser.values(),
                {'__main__': {'foo': u'€'}})
        finally:
            # destroy config file
            os.remove(filename)

    def test_readfp_with_utf8_encoded_text(self):
        config = StringIO(u'[__main__]\nfoo=€'.encode(CONFIG_FILE_ENCODING))
        self.parser.readfp(config)
        self.assertEqual(self.parser.values(), {'__main__': {'foo': u'€'}})

    def test_set(self):
        with tempfile.NamedTemporaryFile() as f:
            f.write('[__main__]\nfoo=1')
            f.flush()

            self.parser.read(f.name)
            self.assertEqual(self.parser._dirty, {})
            self.assertEqual(self.parser.get('__main__', 'foo'), '1')
            self.parser.set('__main__', 'foo', '2')
            self.assertEqual(self.parser.get('__main__', 'foo'), '2')
            self.assertEqual(self.parser._dirty,
                {f.name: {'__main__': {'foo': '2'}}})

    def test_set_non_string(self):
        """Test parser.set with a non-string value."""
        class MySchema(Schema):
            foo = IntOption()
            bar = BoolOption()
        parser = SchemaConfigParser(MySchema())
        parser.parse_all()

        parser.set('__main__', 'foo', 2)
        parser.set('__main__', 'bar', False)
        self.assertEqual(parser.get('__main__', 'foo'), 2)
        self.assertEqual(parser._sections['__main__']['foo'], '2')
        self.assertEqual(parser.get('__main__', 'bar'), False)
        self.assertEqual(parser._sections['__main__']['bar'], 'False')

    def test_set_invalid_type(self):
        self.parser.parse_all()
        self.assertRaises(TypeError, self.parser.set, '__main__', 'foo', 2)

    def test_write(self):
        """Test parser write config to a file."""
        class MySchema(Schema):
            foo = StringOption()

            class DEFAULTSECT(Section):
                pass

        parser = SchemaConfigParser(MySchema())
        expected = u"[{0}]\nbaz = 2\n\n[__main__]\nfoo = bar".format(
            DEFAULTSECT)
        config = StringIO(expected)
        parser.readfp(config)

        # create config file
        fp, filename = tempfile.mkstemp()
        try:
            parser.write(open(filename, 'w'))
            result = open(filename, 'r').read().strip()
            self.assertEqual(result, expected)
        finally:
            # remove the file
            os.unlink(filename)

    def test_write_prefill_parser(self):
        """Test parser write config to a file."""
        class MySchema(Schema):
            foo = IntOption()

        parser = SchemaConfigParser(MySchema())
        expected = u"[__main__]\nfoo = 0"

        # create config file
        fp, filename = tempfile.mkstemp()
        try:
            parser.write(open(filename, 'w'))
            result = open(filename, 'r').read().strip()
            self.assertEqual(result, expected)
        finally:
            # remove the file
            os.unlink(filename)

    def test_save_config(self):
        expected = u'[__main__]\nfoo = 42'
        self._check_save_file(expected)

    def test_save_config_non_ascii(self):
        expected = u'[__main__]\nfoo = fóobâr'
        self._check_save_file(expected)

    def _check_save_file(self, expected, read_config=True):
        config = StringIO(expected.encode(CONFIG_FILE_ENCODING))
        if read_config:
            self.parser.readfp(config)

        # create config file
        fp, filename = tempfile.mkstemp()
        try:
            self.parser.save(open(filename, 'w'))
            result = open(filename, 'r').read().strip()
            self.assertEqual(result.decode(CONFIG_FILE_ENCODING), expected)

            self.parser.save(filename)
            result = open(filename, 'r').read().strip()
            self.assertEqual(result.decode(CONFIG_FILE_ENCODING), expected)
        finally:
            # remove the file
            os.unlink(filename)

    def test_save_config_prefill_parser(self):
        """Test parser save config when no config files read."""
        expected = u'[__main__]\nfoo ='
        self._check_save_file(expected, read_config=False)

    def test_save_no_config_same_files(self):
        class MySchema(Schema):
            foo = IntOption()

        parser = SchemaConfigParser(MySchema())
        parser.set('__main__', 'foo', 2)
        self.assertRaises(ValueError, parser.save)

    def test_save_config_same_files(self):
        """Test parser save config values to original files."""
        def setup_config():
            folder = tempfile.mkdtemp()

            f = open("%s/first.cfg" % folder, 'w')
            f.write("[__main__]\nfoo=1")
            f.close()

            f = open("%s/second.cfg" % folder, 'w')
            f.write("[__main__]\nbar=2")
            f.close()

            files = ["%s/first.cfg" % folder, "%s/second.cfg" % folder]
            return files, folder

        class MySchema(Schema):
            foo = StringOption()
            bar = StringOption()
            baz = IntOption()

        self.parser = SchemaConfigParser(MySchema())

        files, folder = setup_config()
        self.parser.read(files)
        self.parser.set('__main__', 'foo', '42')
        self.parser.set('__main__', 'bar', '42')
        self.parser.set('__main__', 'baz', 42)
        self.parser.save()

        # test the changes were correctly saved
        data = open("%s/first.cfg" % folder).read()
        self.assertTrue('foo = 42' in data)
        self.assertFalse('bar = 42' in data)
        # new value goes into last read config file
        self.assertFalse('baz = 42' in data)
        data = open("%s/second.cfg" % folder).read()
        self.assertFalse('foo = 42' in data)
        self.assertTrue('bar = 42' in data)
        # new value goes into last read config file
        self.assertTrue('baz = 42' in data)

        # silently remove any created files
        try:
            shutil.rmtree(folder)
        except:
            pass

    def test_save_config_last_location_nested_includes(self):
        def setup_config():
            folder = tempfile.mkdtemp()

            f = open("%s/first.cfg" % folder, 'w')
            f.write("[__main__]\nfoo=1")
            f.close()

            f = open("%s/second.cfg" % folder, 'w')
            f.write("[__main__]\nbar=2\nincludes = third.cfg")
            f.close()

            f = open("%s/third.cfg" % folder, 'w')
            f.write("[__main__]\nfoo=3")
            f.close()

            files = ["%s/first.cfg" % folder, "%s/second.cfg" % folder]
            return files, folder

        class MySchema(Schema):
            foo = StringOption()
            bar = StringOption()
            baz = IntOption()

        self.parser = SchemaConfigParser(MySchema())

        files, folder = setup_config()
        self.parser.read(files)
        self.parser.set('__main__', 'foo', '42')
        self.parser.set('__main__', 'bar', '42')
        self.parser.set('__main__', 'baz', 42)
        self.parser.save()

        # test the changes were correctly saved
        data = open("%s/first.cfg" % folder).read()
        self.assertEqual(data.strip(), '[__main__]\nfoo=1')
        data = open("%s/third.cfg" % folder).read()
        self.assertEqual(data.strip(), '[__main__]\nfoo = 42')
        data = open("%s/second.cfg" % folder).read()
        self.assertTrue('bar = 42' in data)
        # new value goes into last read config file
        # not in the last included config file
        self.assertTrue('baz = 42' in data)

        # silently remove any created files
        try:
            shutil.rmtree(folder)
        except:
            pass