예제 #1
0
    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_no_config_same_files(self):
        class MySchema(Schema):
            foo = IntOption()

        parser = SchemaConfigParser(MySchema())
        parser.set('__main__', 'foo', 2)
        self.assertRaises(ValueError, parser.save)
예제 #3
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_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')
예제 #5
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
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