diff -pruN 1.0-1/configglue/app/base.py 1.1.2-0ubuntu3/configglue/app/base.py
--- 1.0-1/configglue/app/base.py	2011-08-04 21:55:29.000000000 +0000
+++ 1.1.2-0ubuntu3/configglue/app/base.py	2013-07-11 16:22:48.000000000 +0000
@@ -4,7 +4,7 @@
 #
 # A library for simple, DRY configuration of applications
 #
-# (C) 2009--2011 by Canonical Ltd.
+# (C) 2009--2013 by Canonical Ltd.
 # by John R. Lenton <john.lenton@canonical.com>
 # and Ricardo Kirkner <ricardo.kirkner@canonical.com>
 #
diff -pruN 1.0-1/configglue/app/__init__.py 1.1.2-0ubuntu3/configglue/app/__init__.py
--- 1.0-1/configglue/app/__init__.py	2011-08-04 21:55:29.000000000 +0000
+++ 1.1.2-0ubuntu3/configglue/app/__init__.py	2013-07-11 16:22:50.000000000 +0000
@@ -4,7 +4,7 @@
 #
 # A library for simple, DRY configuration of applications
 #
-# (C) 2009--2011 by Canonical Ltd.
+# (C) 2009--2013 by Canonical Ltd.
 # by John R. Lenton <john.lenton@canonical.com>
 # and Ricardo Kirkner <ricardo.kirkner@canonical.com>
 #
diff -pruN 1.0-1/configglue/app/plugin.py 1.1.2-0ubuntu3/configglue/app/plugin.py
--- 1.0-1/configglue/app/plugin.py	2011-08-04 21:55:29.000000000 +0000
+++ 1.1.2-0ubuntu3/configglue/app/plugin.py	2013-07-11 16:22:52.000000000 +0000
@@ -4,7 +4,7 @@
 #
 # A library for simple, DRY configuration of applications
 #
-# (C) 2009--2011 by Canonical Ltd.
+# (C) 2009--2013 by Canonical Ltd.
 # by John R. Lenton <john.lenton@canonical.com>
 # and Ricardo Kirkner <ricardo.kirkner@canonical.com>
 #
diff -pruN 1.0-1/configglue/_compat.py 1.1.2-0ubuntu3/configglue/_compat.py
--- 1.0-1/configglue/_compat.py	1970-01-01 00:00:00.000000000 +0000
+++ 1.1.2-0ubuntu3/configglue/_compat.py	2013-07-11 19:05:58.000000000 +0000
@@ -0,0 +1,137 @@
+# -*- coding: utf-8 -*-
+###############################################################################
+#
+# configglue -- glue for your apps' configuration
+#
+# A library for simple, DRY configuration of applications
+#
+# (C) 2013 by Canonical Ltd.
+# by John R. Lenton <john.lenton@canonical.com>
+# and Ricardo Kirkner <ricardo.kirkner@canonical.com>
+# (C) Python Software Foundation (“PSF”)
+#
+# Released under the PSF License Agreement (see the file LICENSE.PSF)
+#
+# For bug reports, support, and new releases: http://launchpad.net/configglue
+#
+###############################################################################
+import re
+import sys
+
+
+PY2 = sys.version_info[0] == 2
+
+if PY2:
+    import __builtin__ as builtins
+    import ConfigParser as configparser
+    from ConfigParser import (
+        DEFAULTSECT,
+        InterpolationDepthError,
+        InterpolationMissingOptionError,
+        InterpolationSyntaxError,
+        NoOptionError,
+        NoSectionError,
+        RawConfigParser,
+    )
+
+    class BasicInterpolation(object):
+        """Interpolation as implemented in the classic ConfigParser.
+
+        The option values can contain format strings which refer to other
+        values in the same section, or values in the special default section.
+
+        For example:
+
+            something: %(dir)s/whatever
+
+        would resolve the "%(dir)s" to the value of dir.  All reference
+        expansions are done late, on demand. If a user needs to use a
+        bare % in a configuration file, she can escape it by writing %%.
+        Other % usage is considered a user error and
+        raises `InterpolationSyntaxError'."""
+
+        _KEYCRE = re.compile(r"%\(([^)]+)\)s")
+
+        def before_get(self, parser, section, option, value, defaults):
+            L = []
+            self._interpolate_some(parser, option, L, value, section,
+                                defaults, 1)
+            return ''.join(L)
+
+        def before_set(self, parser, section, option, value):
+            tmp_value = value.replace('%%', '') # escaped percent signs
+            tmp_value = self._KEYCRE.sub('', tmp_value) # valid syntax
+            if '%' in tmp_value:
+                raise ValueError("invalid interpolation syntax in %r at "
+                                "position %d" % (value, tmp_value.find('%')))
+            return value
+
+        def _interpolate_some(self, parser, option, accum, rest, section, map,
+                            depth):
+            if depth > configparser.MAX_INTERPOLATION_DEPTH:
+                raise configparser.InterpolationDepthError(option, section,
+                                                        rest)
+            while rest:
+                p = rest.find("%")
+                if p < 0:
+                    accum.append(rest)
+                    return
+                if p > 0:
+                    accum.append(rest[:p])
+                    rest = rest[p:]
+                # p is no longer used
+                c = rest[1:2]
+                if c == "%":
+                    accum.append("%")
+                    rest = rest[2:]
+                elif c == "(":
+                    m = self._KEYCRE.match(rest)
+                    if m is None:
+                        raise configparser.InterpolationSyntaxError(option,
+                                                                    section,
+                            "bad interpolation variable reference %r" % rest)
+                    var = parser.optionxform(m.group(1))
+                    rest = rest[m.end():]
+                    try:
+                        v = map[var]
+                    except KeyError:
+                        raise configparser.InterpolationMissingOptionError(
+                            option, section, rest, var)
+                    if "%" in v:
+                        self._interpolate_some(parser, option, accum, v,
+                                            section, map, depth + 1)
+                    else:
+                        accum.append(v)
+                else:
+                    raise configparser.InterpolationSyntaxError(
+                        option, section,
+                        "'%%' must be followed by '%%' or '(', "
+                        "found: %r" % (rest,))
+
+    class BaseConfigParser(configparser.SafeConfigParser):
+        def __init__(self, *args, **kwargs):
+            configparser.SafeConfigParser.__init__(self, *args, **kwargs)
+
+            self._interpolation = BasicInterpolation()
+
+    text_type = unicode
+    string_types = (str, unicode)
+    iteritems = lambda d: d.iteritems()
+
+else:
+    import builtins
+    import configparser
+    from configparser import (
+        DEFAULTSECT,
+        InterpolationDepthError,
+        InterpolationMissingOptionError,
+        InterpolationSyntaxError,
+        NoOptionError,
+        NoSectionError,
+        RawConfigParser,
+    )
+
+    BaseConfigParser = configparser.SafeConfigParser
+    text_type = str
+    string_types = (str,)
+    iteritems = lambda d: iter(d.items())
diff -pruN 1.0-1/configglue/contrib/__init__.py 1.1.2-0ubuntu3/configglue/contrib/__init__.py
--- 1.0-1/configglue/contrib/__init__.py	1970-01-01 00:00:00.000000000 +0000
+++ 1.1.2-0ubuntu3/configglue/contrib/__init__.py	2013-07-11 16:23:06.000000000 +0000
@@ -0,0 +1,18 @@
+###############################################################################
+#
+# configglue -- glue for your apps' configuration
+#
+# A library for simple, DRY configuration of applications
+#
+# (C) 2009--2013 by Canonical Ltd.
+# by John R. Lenton <john.lenton@canonical.com>
+# and Ricardo Kirkner <ricardo.kirkner@canonical.com>
+#
+# Released under the BSD License (see the file LICENSE)
+#
+# For bug reports, support, and new releases: http://launchpad.net/configglue
+#
+###############################################################################
+
+# import all schemas
+from .schema import *
diff -pruN 1.0-1/configglue/contrib/schema/devserver.py 1.1.2-0ubuntu3/configglue/contrib/schema/devserver.py
--- 1.0-1/configglue/contrib/schema/devserver.py	1970-01-01 00:00:00.000000000 +0000
+++ 1.1.2-0ubuntu3/configglue/contrib/schema/devserver.py	2013-07-11 16:23:34.000000000 +0000
@@ -0,0 +1,76 @@
+###############################################################################
+#
+# configglue -- glue for your apps' configuration
+#
+# A library for simple, DRY configuration of applications
+#
+# (C) 2009--2013 by Canonical Ltd.
+# by John R. Lenton <john.lenton@canonical.com>
+# and Ricardo Kirkner <ricardo.kirkner@canonical.com>
+#
+# Released under the BSD License (see the file LICENSE)
+#
+# For bug reports, support, and new releases: http://launchpad.net/configglue
+#
+###############################################################################
+from configglue.schema import (
+    BoolOption,
+    IntOption,
+    ListOption,
+    Section,
+    Schema,
+    StringOption,
+    )
+
+
+class DevServerSchema(Schema):
+    """Configglue schema for django-devserver."""
+
+    __version__ = '0.3.1'
+
+    class devserver(Section):
+        devserver_args = ListOption(
+            item=StringOption(),
+            default=[],
+            help='Additional command line arguments to pass to the runserver command (as defaults).')
+        devserver_default_addr = StringOption(
+            default='127.0.0.1',
+            help='The default address to bind to.')
+        devserver_default_port = StringOption(
+            default='8000',
+            help='The default port to bind to.')
+        devserver_wsgi_middleware = ListOption(
+            item=StringOption(),
+            default=[],
+            help='A list of additional WSGI middleware to apply to the runserver command.')
+        devserver_modules = ListOption(
+            item=StringOption(),
+            default=[
+                'devserver.modules.sql.SQLRealTimeModule',
+                ],
+            help='List of devserver modules to enable')
+        devserver_ignored_prefixes = ListOption(
+            item=StringOption(),
+            default=['/media', '/uploads'],
+            help='List of prefixes to supress and skip process on. By default, '
+                'ADMIN_MEDIA_PREFIX, MEDIA_URL and STATIC_URL '
+                '(for Django >= 1.3) will be ignored (assuming MEDIA_URL and '
+                'STATIC_URL is relative).')
+        devserver_truncate_sql = BoolOption(
+            default=True,
+            help='Truncate SQL queries output by SQLRealTimeModule.')
+        devserver_truncate_aggregates = BoolOption(
+            default=False)
+        devserver_active = BoolOption(
+            default=False)
+        devserver_ajax_content_length = IntOption(
+            default=300,
+            help='Maximum response length to dump.')
+        devserver_ajax_pretty_print = BoolOption(
+            default=False)
+        devserver_sql_min_duration = IntOption(
+            default=None,
+            help='Minimum time a query must execute to be shown, value is in ms.')
+        devserver_auto_profile = BoolOption(
+            default=False,
+            help='Automatically profile all view functions.')
diff -pruN 1.0-1/configglue/contrib/schema/django_jenkins.py 1.1.2-0ubuntu3/configglue/contrib/schema/django_jenkins.py
--- 1.0-1/configglue/contrib/schema/django_jenkins.py	1970-01-01 00:00:00.000000000 +0000
+++ 1.1.2-0ubuntu3/configglue/contrib/schema/django_jenkins.py	2013-07-11 16:23:37.000000000 +0000
@@ -0,0 +1,47 @@
+###############################################################################
+#
+# configglue -- glue for your apps' configuration
+#
+# A library for simple, DRY configuration of applications
+#
+# (C) 2009--2013 by Canonical Ltd.
+# by John R. Lenton <john.lenton@canonical.com>
+# and Ricardo Kirkner <ricardo.kirkner@canonical.com>
+#
+# Released under the BSD License (see the file LICENSE)
+#
+# For bug reports, support, and new releases: http://launchpad.net/configglue
+#
+###############################################################################
+from configglue.schema import (
+    ListOption,
+    Schema,
+    Section,
+    StringOption,
+    TupleOption,
+    )
+
+
+class DjangoJenkinsSchema(Schema):
+    """Configglue schema for django-jenkins."""
+
+    __version__ = '0.12.1'
+
+    class django_jenkins(Section):
+        project_apps = ListOption(
+            item=StringOption(),
+            default=[],
+            help='List of of django apps for Jenkins to run.')
+        jenkins_tasks = TupleOption(
+            default=(
+                'django_jenkins.tasks.run_pylint',
+                'django_jenkins.tasks.with_coverage',
+                'django_jenkins.tasks.django_tests',
+            ),
+            help='List of Jenkins tasks executed by ./manage.py jenkins '
+                'command.')
+        jenkins_test_runner = StringOption(
+            default='',
+            help='The name of the class to use for starting the test suite for '
+                'jenkins and jtest commands. Class should be inherited from '
+                'django_jenkins.runner.CITestSuiteRunner.')
diff -pruN 1.0-1/configglue/contrib/schema/django_openid_auth.py 1.1.2-0ubuntu3/configglue/contrib/schema/django_openid_auth.py
--- 1.0-1/configglue/contrib/schema/django_openid_auth.py	1970-01-01 00:00:00.000000000 +0000
+++ 1.1.2-0ubuntu3/configglue/contrib/schema/django_openid_auth.py	2013-07-11 16:23:39.000000000 +0000
@@ -0,0 +1,65 @@
+###############################################################################
+#
+# configglue -- glue for your apps' configuration
+#
+# A library for simple, DRY configuration of applications
+#
+# (C) 2009--2013 by Canonical Ltd.
+# by John R. Lenton <john.lenton@canonical.com>
+# and Ricardo Kirkner <ricardo.kirkner@canonical.com>
+#
+# Released under the BSD License (see the file LICENSE)
+#
+# For bug reports, support, and new releases: http://launchpad.net/configglue
+#
+###############################################################################
+from configglue.schema import (
+    BoolOption,
+    DictOption,
+    ListOption,
+    Schema,
+    Section,
+    StringOption,
+    )
+
+
+class DjangoOpenIdAuthSchema(Schema):
+    """Configglue schema for django-openid-auth."""
+
+    __version__ = '0.5'
+
+    class openid(Section):
+        openid_use_as_admin_login = BoolOption(
+            default=False)
+        openid_create_users = BoolOption(
+            default=False)
+        openid_update_details_from_sreg = BoolOption(
+            default=False)
+        openid_physical_multifactor_required = BoolOption(
+            default=False)
+        openid_strict_usernames = BoolOption(
+            default=False)
+        openid_sreg_required_fields = ListOption(
+            item=StringOption())
+        openid_sreg_extra_fields = ListOption(
+            item=StringOption())
+        openid_follow_renames = BoolOption(
+            default=False)
+        openid_launchpad_teams_mapping_auto = BoolOption(
+            default=False)
+        openid_launchpad_teams_mapping_auto_blacklist = ListOption(
+            item=StringOption())
+        openid_launchpad_teams_mapping = DictOption()
+        openid_launchpad_staff_teams = ListOption(
+            item=StringOption())
+        openid_launchpad_teams_required = ListOption(
+            item=StringOption())
+        openid_disallow_inames = BoolOption(
+            default=False)
+        allowed_external_openid_redirect_domains = ListOption(
+            item=StringOption())
+        openid_trust_root = StringOption()
+        openid_sso_server_url = StringOption(
+            null=True)
+        openid_email_whitelist_regexp_list = ListOption(
+            item=StringOption())
diff -pruN 1.0-1/configglue/contrib/schema/__init__.py 1.1.2-0ubuntu3/configglue/contrib/schema/__init__.py
--- 1.0-1/configglue/contrib/schema/__init__.py	1970-01-01 00:00:00.000000000 +0000
+++ 1.1.2-0ubuntu3/configglue/contrib/schema/__init__.py	2013-07-11 16:23:44.000000000 +0000
@@ -0,0 +1,36 @@
+###############################################################################
+#
+# configglue -- glue for your apps' configuration
+#
+# A library for simple, DRY configuration of applications
+#
+# (C) 2009--2013 by Canonical Ltd.
+# by John R. Lenton <john.lenton@canonical.com>
+# and Ricardo Kirkner <ricardo.kirkner@canonical.com>
+#
+# Released under the BSD License (see the file LICENSE)
+#
+# For bug reports, support, and new releases: http://launchpad.net/configglue
+#
+###############################################################################
+
+from .devserver import DevServerSchema
+from .django_jenkins import DjangoJenkinsSchema
+from .nexus import NexusSchema
+from .django_openid_auth import DjangoOpenIdAuthSchema
+from .preflight import PreflightSchema
+from .saml2idp import Saml2IdpSchema
+from .raven import RavenSchema
+from .pystatsd import PyStatsdSchema
+
+
+__all__ = [
+    'DevServerSchema',
+    'DjangoJenkinsSchema',
+    'NexusSchema',
+    'DjangoOpenIdAuthSchema',
+    'PreflightSchema',
+    'Saml2IdpSchema',
+    'RavenSchema',
+    'PyStatsdSchema',
+    ]
diff -pruN 1.0-1/configglue/contrib/schema/nexus.py 1.1.2-0ubuntu3/configglue/contrib/schema/nexus.py
--- 1.0-1/configglue/contrib/schema/nexus.py	1970-01-01 00:00:00.000000000 +0000
+++ 1.1.2-0ubuntu3/configglue/contrib/schema/nexus.py	2013-07-11 16:23:47.000000000 +0000
@@ -0,0 +1,28 @@
+###############################################################################
+#
+# configglue -- glue for your apps' configuration
+#
+# A library for simple, DRY configuration of applications
+#
+# (C) 2009--2013 by Canonical Ltd.
+# by John R. Lenton <john.lenton@canonical.com>
+# and Ricardo Kirkner <ricardo.kirkner@canonical.com>
+#
+# Released under the BSD License (see the file LICENSE)
+#
+# For bug reports, support, and new releases: http://launchpad.net/configglue
+#
+###############################################################################
+from configglue.schema import BoolOption, Schema, Section, StringOption
+
+
+class NexusSchema(Schema):
+    """Configglue schema for nexus."""
+
+    __version__ = '0.2.3'
+
+    class nexus(Section):
+        nexus_media_prefix = StringOption(
+            default='/nexus/media/')
+        nexus_use_django_media_url = BoolOption(
+            default=False)
diff -pruN 1.0-1/configglue/contrib/schema/preflight.py 1.1.2-0ubuntu3/configglue/contrib/schema/preflight.py
--- 1.0-1/configglue/contrib/schema/preflight.py	1970-01-01 00:00:00.000000000 +0000
+++ 1.1.2-0ubuntu3/configglue/contrib/schema/preflight.py	2013-07-11 16:23:50.000000000 +0000
@@ -0,0 +1,28 @@
+###############################################################################
+#
+# configglue -- glue for your apps' configuration
+#
+# A library for simple, DRY configuration of applications
+#
+# (C) 2009--2013 by Canonical Ltd.
+# by John R. Lenton <john.lenton@canonical.com>
+# and Ricardo Kirkner <ricardo.kirkner@canonical.com>
+#
+# Released under the BSD License (see the file LICENSE)
+#
+# For bug reports, support, and new releases: http://launchpad.net/configglue
+#
+###############################################################################
+from configglue.schema import Section, Schema, StringOption
+
+
+class PreflightSchema(Schema):
+    """Configglue schema for django-preflight."""
+
+    __version__ = '0.1'
+
+    class preflight(Section):
+        preflight_base_template = StringOption(
+            default='index.1col.html')
+        preflight_table_class = StringOption(
+            default='listing')
diff -pruN 1.0-1/configglue/contrib/schema/pystatsd.py 1.1.2-0ubuntu3/configglue/contrib/schema/pystatsd.py
--- 1.0-1/configglue/contrib/schema/pystatsd.py	1970-01-01 00:00:00.000000000 +0000
+++ 1.1.2-0ubuntu3/configglue/contrib/schema/pystatsd.py	2013-07-11 16:23:51.000000000 +0000
@@ -0,0 +1,28 @@
+###############################################################################
+#
+# configglue -- glue for your apps' configuration
+#
+# A library for simple, DRY configuration of applications
+#
+# (C) 2009--2013 by Canonical Ltd.
+# by John R. Lenton <john.lenton@canonical.com>
+# and Ricardo Kirkner <ricardo.kirkner@canonical.com>
+#
+# Released under the BSD License (see the file LICENSE)
+#
+# For bug reports, support, and new releases: http://launchpad.net/configglue
+#
+###############################################################################
+from configglue.schema import IntOption, Schema, Section, StringOption
+
+
+class PyStatsdSchema(Schema):
+    """Configglue schema for pystatsd."""
+
+    __version__ = '0.1.6'
+
+    class statsd(Section):
+        statsd_host = StringOption(
+            default='localhost')
+        statsd_port = IntOption(
+            default=8125)
diff -pruN 1.0-1/configglue/contrib/schema/raven.py 1.1.2-0ubuntu3/configglue/contrib/schema/raven.py
--- 1.0-1/configglue/contrib/schema/raven.py	1970-01-01 00:00:00.000000000 +0000
+++ 1.1.2-0ubuntu3/configglue/contrib/schema/raven.py	2013-07-11 16:23:54.000000000 +0000
@@ -0,0 +1,84 @@
+###############################################################################
+#
+# configglue -- glue for your apps' configuration
+#
+# A library for simple, DRY configuration of applications
+#
+# (C) 2009--2013 by Canonical Ltd.
+# by John R. Lenton <john.lenton@canonical.com>
+# and Ricardo Kirkner <ricardo.kirkner@canonical.com>
+#
+# Released under the BSD License (see the file LICENSE)
+#
+# For bug reports, support, and new releases: http://launchpad.net/configglue
+#
+###############################################################################
+from configglue.schema import (
+    BoolOption,
+    IntOption,
+    ListOption,
+    Schema,
+    Section,
+    StringOption,
+    )
+
+
+class RavenSchema(Schema):
+    """Configglue schema for raven."""
+
+    __version__ = '1.6.1'
+
+    class raven(Section):
+        sentry_servers = ListOption()
+        sentry_include_paths = ListOption(
+            item=StringOption())
+        sentry_exclude_paths = ListOption(
+            item=StringOption(),
+            help='Ignore module prefixes when attempting to discover which '
+                'function an error comes from.')
+        sentry_timeout = IntOption(
+            default=5,
+            help='Timeout value for sending messages to remote.')
+        sentry_name = StringOption(
+            null=True,
+            help='This will override the server_name value for this installation.')
+        sentry_auto_log_stacks = BoolOption(
+            default=False,
+            help='Should raven automatically log frame stacks (including '
+                'locals) all calls as it would for exceptions.')
+        sentry_key = StringOption(
+            null=True)
+        sentry_max_length_string = IntOption(
+            default=200,
+            help='The maximum characters of a string that should be stored.')
+        sentry_max_length_list = IntOption(
+            default=50,
+            help='The maximum number of items a list-like container should store.')
+        sentry_site = StringOption(
+            null=True,
+            help='An optional, arbitrary string to identify this client '
+                'installation.')
+        sentry_public_key = StringOption(
+            null=True,
+            help='Public key of the project member which will authenticate as '
+                'the client.')
+        sentry_private_key = StringOption(
+            null=True,
+            help='Private key of the project member which will authenticate as '
+                'the client.')
+        sentry_project = IntOption(
+            default=1,
+            help='Sentry project ID. The default value for installations is 1.')
+        sentry_processors = ListOption(
+            item=StringOption(),
+            default=[
+                'raven.processors.SanitizePasswordsProcessor',
+                ],
+            help='List of processors to apply to events before sending them '
+                'to the Sentry server.')
+        sentry_dsn = StringOption(
+            help='A sentry compatible DSN.')
+        sentry_client = StringOption(
+            default='raven.contrib.django.DjangoClient')
+        sentry_debug = BoolOption(
+            default=False)
diff -pruN 1.0-1/configglue/contrib/schema/saml2idp.py 1.1.2-0ubuntu3/configglue/contrib/schema/saml2idp.py
--- 1.0-1/configglue/contrib/schema/saml2idp.py	1970-01-01 00:00:00.000000000 +0000
+++ 1.1.2-0ubuntu3/configglue/contrib/schema/saml2idp.py	2013-07-11 16:23:55.000000000 +0000
@@ -0,0 +1,53 @@
+###############################################################################
+#
+# configglue -- glue for your apps' configuration
+#
+# A library for simple, DRY configuration of applications
+#
+# (C) 2009--2013 by Canonical Ltd.
+# by John R. Lenton <john.lenton@canonical.com>
+# and Ricardo Kirkner <ricardo.kirkner@canonical.com>
+#
+# Released under the BSD License (see the file LICENSE)
+#
+# For bug reports, support, and new releases: http://launchpad.net/configglue
+#
+###############################################################################
+from configglue.schema import (
+    BoolOption,
+    ListOption,
+    Section,
+    Schema,
+    StringOption,
+    )
+
+
+class Saml2IdpSchema(Schema):
+    """Configglue schema for saml2idp."""
+
+    __version__ = '0.14'
+
+    class saml2(Section):
+        saml2idp_autosubmit = BoolOption(
+            default=True)
+        saml2idp_issuer = StringOption(
+            default='http://127.0.0.1:8000')
+        saml2idp_certificate_file = StringOption(
+            default='keys/certificate.pem')
+        saml2idp_private_key_file = StringOption(
+            default='keys/private-key.pem')
+        saml2idp_signing = BoolOption(
+            default=True)
+        saml2idp_valid_acs = ListOption(
+            item=StringOption(),
+            default=[
+                'https://login.salesforce.com',
+                ],
+            help="List of ACS URLs accepted by /+saml login")
+        saml2idp_processor_classes = ListOption(
+            item=StringOption(),
+            default=[
+                'saml2idp.salesforce.Processor',
+                'saml2idp.google_apps.Processor',
+                ],
+            help="List of SAML 2.0 AuthnRequest processors")
diff -pruN 1.0-1/configglue/glue.py 1.1.2-0ubuntu3/configglue/glue.py
--- 1.0-1/configglue/glue.py	2011-08-04 21:55:29.000000000 +0000
+++ 1.1.2-0ubuntu3/configglue/glue.py	2013-07-11 16:41:08.000000000 +0000
@@ -4,7 +4,7 @@
 #
 # A library for simple, DRY configuration of applications
 #
-# (C) 2009--2011 by Canonical Ltd.
+# (C) 2009--2013 by Canonical Ltd.
 # by John R. Lenton <john.lenton@canonical.com>
 # and Ricardo Kirkner <ricardo.kirkner@canonical.com>
 #
@@ -16,13 +16,10 @@
 
 import os
 import sys
-from ConfigParser import (
-    NoOptionError,
-    NoSectionError,
-)
 from optparse import OptionParser
 from collections import namedtuple
 
+from ._compat import NoSectionError, NoOptionError
 from .parser import SchemaConfigParser
 
 
diff -pruN 1.0-1/configglue/inischema/attributed.py 1.1.2-0ubuntu3/configglue/inischema/attributed.py
--- 1.0-1/configglue/inischema/attributed.py	2011-08-04 21:55:29.000000000 +0000
+++ 1.1.2-0ubuntu3/configglue/inischema/attributed.py	2013-07-11 17:00:48.000000000 +0000
@@ -4,7 +4,7 @@
 #
 # A library for simple, DRY configuration of applications
 #
-# (C) 2009--2011 by Canonical Ltd.
+# (C) 2009--2013 by Canonical Ltd.
 # by John R. Lenton <john.lenton@canonical.com>
 # and Ricardo Kirkner <ricardo.kirkner@canonical.com>
 #
@@ -18,7 +18,8 @@
 AttributtedConfigParser lives here.
 """
 import re
-from ConfigParser import RawConfigParser
+
+from configglue._compat import RawConfigParser
 
 
 marker = object()
diff -pruN 1.0-1/configglue/inischema/glue.py 1.1.2-0ubuntu3/configglue/inischema/glue.py
--- 1.0-1/configglue/inischema/glue.py	2011-08-04 21:55:29.000000000 +0000
+++ 1.1.2-0ubuntu3/configglue/inischema/glue.py	2013-07-11 16:24:13.000000000 +0000
@@ -4,7 +4,7 @@
 #
 # A library for simple, DRY configuration of applications
 #
-# (C) 2009--2011 by Canonical Ltd.
+# (C) 2009--2013 by Canonical Ltd.
 # by John R. Lenton <john.lenton@canonical.com>
 # and Ricardo Kirkner <ricardo.kirkner@canonical.com>
 #
@@ -18,9 +18,9 @@
 """
 from __future__ import absolute_import
 
-import __builtin__
 from collections import namedtuple
 
+from configglue._compat import builtins
 from configglue.inischema import parsers
 from configglue.inischema.attributed import AttributedConfigParser
 from configglue.glue import schemaconfigglue
@@ -78,7 +78,7 @@ def ini2schema(fd, p=None):
             parser_args = option.attrs.pop('parser.args', '').split()
             parser_fun = getattr(parsers, parser, None)
             if parser_fun is None:
-                parser_fun = getattr(__builtin__, parser, None)
+                parser_fun = getattr(builtins, parser, None)
             if parser_fun is None:
                 parser_fun = lambda x: x
 
diff -pruN 1.0-1/configglue/inischema/__init__.py 1.1.2-0ubuntu3/configglue/inischema/__init__.py
--- 1.0-1/configglue/inischema/__init__.py	2011-08-04 21:55:29.000000000 +0000
+++ 1.1.2-0ubuntu3/configglue/inischema/__init__.py	2013-07-11 16:24:15.000000000 +0000
@@ -4,7 +4,7 @@
 #
 # A library for simple, DRY configuration of applications
 #
-# (C) 2009--2011 by Canonical Ltd.
+# (C) 2009--2013 by Canonical Ltd.
 # by John R. Lenton <john.lenton@canonical.com>
 # and Ricardo Kirkner <ricardo.kirkner@canonical.com>
 #
diff -pruN 1.0-1/configglue/inischema/parsers.py 1.1.2-0ubuntu3/configglue/inischema/parsers.py
--- 1.0-1/configglue/inischema/parsers.py	2011-08-04 21:55:29.000000000 +0000
+++ 1.1.2-0ubuntu3/configglue/inischema/parsers.py	2013-07-11 16:24:17.000000000 +0000
@@ -4,7 +4,7 @@
 #
 # A library for simple, DRY configuration of applications
 #
-# (C) 2009--2011 by Canonical Ltd.
+# (C) 2009--2013 by Canonical Ltd.
 # by John R. Lenton <john.lenton@canonical.com>
 # and Ricardo Kirkner <ricardo.kirkner@canonical.com>
 #
diff -pruN 1.0-1/configglue/inischema/typed.py 1.1.2-0ubuntu3/configglue/inischema/typed.py
--- 1.0-1/configglue/inischema/typed.py	2011-08-04 21:55:29.000000000 +0000
+++ 1.1.2-0ubuntu3/configglue/inischema/typed.py	2013-07-11 16:24:20.000000000 +0000
@@ -4,7 +4,7 @@
 #
 # A library for simple, DRY configuration of applications
 #
-# (C) 2009--2011 by Canonical Ltd.
+# (C) 2009--2013 by Canonical Ltd.
 # by John R. Lenton <john.lenton@canonical.com>
 # and Ricardo Kirkner <ricardo.kirkner@canonical.com>
 #
@@ -15,10 +15,11 @@
 ###############################################################################
 
 """ TypedConfigParser lives here """
-from __future__ import absolute_import
+from __future__ import absolute_import, unicode_literals
 
 import os
 
+from configglue._compat import text_type
 from . import parsers
 from .attributed import AttributedConfigParser
 
@@ -34,7 +35,7 @@ class TypedConfigParser(AttributedConfig
                         'float': float,
                         'int': int,
                         'lines': parsers.lines,
-                        'unicode': unicode,
+                        'unicode': text_type,
                         'getenv': os.getenv,
                         None: lambda x: x}
 
diff -pruN 1.0-1/configglue/__init__.py 1.1.2-0ubuntu3/configglue/__init__.py
--- 1.0-1/configglue/__init__.py	2011-08-04 21:55:29.000000000 +0000
+++ 1.1.2-0ubuntu3/configglue/__init__.py	2013-07-11 19:38:47.000000000 +0000
@@ -4,7 +4,7 @@
 #
 # A library for simple, DRY configuration of applications
 #
-# (C) 2009--2011 by Canonical Ltd.
+# (C) 2009--2013 by Canonical Ltd.
 # by John R. Lenton <john.lenton@canonical.com>
 # and Ricardo Kirkner <ricardo.kirkner@canonical.com>
 #
@@ -14,4 +14,4 @@
 #
 ###############################################################################
 
-__version__ = '1.0'
+__version__ = '1.1.2'
diff -pruN 1.0-1/configglue/parser.py 1.1.2-0ubuntu3/configglue/parser.py
--- 1.0-1/configglue/parser.py	2011-08-04 21:55:29.000000000 +0000
+++ 1.1.2-0ubuntu3/configglue/parser.py	2013-07-11 16:51:06.000000000 +0000
@@ -4,7 +4,7 @@
 #
 # A library for simple, DRY configuration of applications
 #
-# (C) 2009--2011 by Canonical Ltd.
+# (C) 2009--2013 by Canonical Ltd.
 # by John R. Lenton <john.lenton@canonical.com>
 # and Ricardo Kirkner <ricardo.kirkner@canonical.com>
 #
@@ -20,18 +20,17 @@ import copy
 import logging
 import os
 import re
-import string
 
-from ConfigParser import (
+from functools import reduce
+
+from ._compat import BaseConfigParser, text_type, string_types
+from ._compat import (
     DEFAULTSECT,
-    SafeConfigParser as BaseConfigParser,
     InterpolationMissingOptionError,
     NoOptionError,
     NoSectionError,
 )
 
-from configglue.schema import DictOption
-
 
 __all__ = [
     'SchemaValidationError',
@@ -40,6 +39,14 @@ __all__ = [
 
 CONFIG_FILE_ENCODING = 'utf-8'
 
+class NullHandler(logging.Handler):
+    def emit(self, record):
+        pass
+
+
+logger = logging.getLogger(__name__)
+logger.addHandler(NullHandler())
+
 
 class SchemaValidationError(Exception):
     """Exception class raised for any schema validation error."""
@@ -149,8 +156,8 @@ class SchemaConfigParser(BaseConfigParse
             # structure validates, validate content
             self.parse_all()
 
-        except Exception, e:
-            errors.append(str(e))
+        except Exception as e:
+            errors.append(text_type(e))
             valid = False
 
         if report:
@@ -195,7 +202,7 @@ class SchemaConfigParser(BaseConfigParse
             for option in options:
                 try:
                     value = self._interpolate(section, option, d[option], d)
-                except InterpolationMissingOptionError, e:
+                except InterpolationMissingOptionError as e:
                     # interpolation failed, because key was not found in
                     # section. try other sections before bailing out
                     value = self._interpolate_value(section, option)
@@ -237,7 +244,7 @@ class SchemaConfigParser(BaseConfigParse
         """Like ConfigParser.read, but consider files we've already read."""
         if already_read is None:
             already_read = set()
-        if isinstance(filenames, basestring):
+        if isinstance(filenames, string_types):
             filenames = [filenames]
         read_ok = []
         for filename in filenames:
@@ -245,9 +252,9 @@ class SchemaConfigParser(BaseConfigParse
             if path in already_read:
                 continue
             try:
-                fp = codecs.open(path, 'r', CONFIG_FILE_ENCODING)
+                fp = codecs.open(path, 'r', encoding=CONFIG_FILE_ENCODING)
             except IOError:
-                logging.warn(
+                logger.warn(
                     'File {0} could not be read. Skipping.'.format(path))
                 continue
             self._read(fp, path, already_read=already_read)
@@ -274,7 +281,7 @@ class SchemaConfigParser(BaseConfigParse
             old_basedir, self._basedir = self._basedir, os.path.dirname(
                 fpname)
             includes = self.get('__main__', 'includes')
-            filenames = map(string.strip, includes)
+            filenames = [text_type.strip(x) for x in includes]
             self.read(filenames, already_read=already_read)
             self._basedir = old_basedir
 
@@ -294,7 +301,7 @@ class SchemaConfigParser(BaseConfigParse
 
     def _update_location(self, old_sections, filename):
         # keep list of valid options to include locations for
-        option_names = map(lambda x: x.name, self.schema.options())
+        option_names = [x.name for x in self.schema.options()]
 
         # new values
         sections = self._sections
@@ -342,7 +349,7 @@ class SchemaConfigParser(BaseConfigParse
 
             try:
                 value = option_obj.parse(value, **kwargs)
-            except ValueError, e:
+            except ValueError as e:
                 raise ValueError("Invalid value '%s' for %s '%s' in"
                     " section '%s'. Original exception was: %s" %
                     (value, option_obj.__class__.__name__, option,
@@ -373,10 +380,10 @@ class SchemaConfigParser(BaseConfigParse
 
     def _extract_interpolation_keys(self, item):
         if isinstance(item, (list, tuple)):
-            keys = map(self._extract_interpolation_keys, item)
+            keys = [self._extract_interpolation_keys(x) for x in item]
             keys = reduce(set.union, keys, set())
         else:
-            keys = set(self._KEYCRE.findall(item))
+            keys = set(self._interpolation._KEYCRE.findall(item))
         # remove invalid key
         if '' in keys:
             keys.remove('')
@@ -384,7 +391,7 @@ class SchemaConfigParser(BaseConfigParse
 
     def _get_interpolation_keys(self, section, option):
 
-        rawval = super(SchemaConfigParser, self).get(section, option, True)
+        rawval = super(SchemaConfigParser, self).get(section, option, raw=True)
         try:
             opt = self.schema.section(section).option(option)
             value = opt.parse(rawval, raw=True)
@@ -394,6 +401,10 @@ class SchemaConfigParser(BaseConfigParse
         keys = self._extract_interpolation_keys(value)
         return rawval, keys
 
+    def _interpolate(self, *args, **kwargs):
+        """Helper method for transition to configparser."""
+        return self._interpolation.before_get(self, *args, **kwargs)
+
     def _interpolate_value(self, section, option):
         rawval, keys = self._get_interpolation_keys(section, option)
         if not keys:
@@ -423,24 +434,49 @@ class SchemaConfigParser(BaseConfigParse
         # replace holders with values
         result = rawval % values
 
-        assert isinstance(result, basestring)
+        assert isinstance(result, string_types)
         return result
 
     def interpolate_environment(self, rawval, raw=False):
+        """Interpolate environment variables"""
         if raw:
             return rawval
 
-        # interpolate environment variables
+        # this allows both nested and mutliple environment variable
+        # interpolation in a single value
         pattern = re.sub(r'\${([A-Z_]+)}', r'%(\1)s', rawval)
         pattern = re.sub(r'\$([A-Z_]+)', r'%(\1)s', pattern)
 
+        # counter to protect against infinite loops
+        num_interpolations = 0
+        # save simple result in case we need it
+        simple_pattern = pattern
+
+        # handle complex case of env vars with defaults
+        env = os.environ.copy()
+        env_re = re.compile(r'\${(?P<name>[A-Z_]+):-(?P<default>.*?)}')
+        match = env_re.search(pattern)
+        while match and num_interpolations < 50:
+            groups = match.groupdict()
+            name = groups['name']
+            pattern = pattern.replace(match.group(), '%%(%s)s' % name)
+            if name not in env and groups['default'] is not None:
+                # interpolate defaults as well to allow ${FOO:-$BAR}
+                env[name] = groups['default'] % os.environ
+
+            num_interpolations += 1
+            match = env_re.search(pattern)
+
+        if num_interpolations >= 50:
+            # blown loop, restore earlier simple interpolation
+            pattern = simple_pattern
+
         keys = self._extract_interpolation_keys(pattern)
         if not keys:
             # interpolation keys are not valid
             return rawval
 
-        interpolated = pattern % os.environ
-        return interpolated
+        return pattern % env
 
     def _get_default(self, section, option):
         # cater for 'special' sections
@@ -477,20 +513,20 @@ class SchemaConfigParser(BaseConfigParse
                 pass
             # value is defined entirely in current section
             value = super(SchemaConfigParser, self).get(section, option,
-                                                        raw, vars)
-        except InterpolationMissingOptionError, e:
+                                                        raw=raw, vars=vars)
+        except InterpolationMissingOptionError as e:
             # interpolation key not in same section
             value = self._interpolate_value(section, option)
             if value is None:
                 # this should be a string, so None indicates an error
                 raise e
-        except (NoSectionError, NoOptionError), e:
+        except (NoSectionError, NoOptionError) as e:
             # option not found in config, try to get its default value from
             # schema
             value = self._get_default(section, option)
 
         # interpolate environment variables
-        if isinstance(value, basestring):
+        if isinstance(value, string_types):
             try:
                 value = self.interpolate_environment(value, raw=raw)
                 if parse:
@@ -551,11 +587,9 @@ class SchemaConfigParser(BaseConfigParse
 
         """
         if fp is not None:
-            if isinstance(fp, basestring):
-                fp = open(fp, 'w')
-            # write to a specific file
-            encoded_fp = codecs.getwriter(CONFIG_FILE_ENCODING)(fp)
-            self.write(encoded_fp)
+            if isinstance(fp, string_types):
+                fp = codecs.open(fp, 'w', encoding=CONFIG_FILE_ENCODING)
+            self.write(fp)
         else:
             # write to the original files
             for filename, sections in self._dirty.items():
@@ -580,7 +614,8 @@ class SchemaConfigParser(BaseConfigParse
                         parser.set(section, option, value)
 
                 # write to new file
-                parser.write(open("%s.new" % filename, 'w'))
+                parser.write(codecs.open("%s.new" % filename, 'w',
+                                         encoding=CONFIG_FILE_ENCODING))
                 # rename old file
                 if os.path.exists(filename):
                     os.rename(filename, "%s.old" % filename)
diff -pruN 1.0-1/configglue/schema.py 1.1.2-0ubuntu3/configglue/schema.py
--- 1.0-1/configglue/schema.py	2011-08-04 21:55:29.000000000 +0000
+++ 1.1.2-0ubuntu3/configglue/schema.py	2013-07-11 16:41:40.000000000 +0000
@@ -4,7 +4,7 @@
 #
 # A library for simple, DRY configuration of applications
 #
-# (C) 2009--2011 by Canonical Ltd.
+# (C) 2009--2013 by Canonical Ltd.
 # by John R. Lenton <john.lenton@canonical.com>
 # and Ricardo Kirkner <ricardo.kirkner@canonical.com>
 #
@@ -13,15 +13,16 @@
 # For bug reports, support, and new releases: http://launchpad.net/configglue
 #
 ###############################################################################
+from __future__ import unicode_literals
 
 import json
-from ConfigParser import (
-    NoSectionError,
-    NoOptionError,
-)
 from copy import deepcopy
 from inspect import getmembers
 
+from ._compat import text_type, string_types
+from ._compat import NoSectionError, NoOptionError
+
+
 
 __all__ = [
     'BoolOption',
@@ -156,6 +157,9 @@ class Schema(object):
     def __ne__(self, other):
         return not self.__eq__(other)
 
+    def __hash__(self):
+        return id(self)
+
     def is_valid(self):
         """Return whether the schema has a valid structure."""
         explicit_default_section = isinstance(getattr(self, '__main__', None),
@@ -186,7 +190,7 @@ class Schema(object):
         To get options from the default section, specify section='__main__'
 
         """
-        if isinstance(section, basestring):
+        if isinstance(section, string_types):
             section = self.section(section)
         if section is None:
             options = []
@@ -222,6 +226,9 @@ class Section(object):
     def __ne__(self, other):
         return not self.__eq__(other)
 
+    def __hash__(self):
+        return id(self)
+
     def __repr__(self):
         if self.name:
             name = " %s" % self.name
@@ -316,6 +323,9 @@ class Option(object):
     def __ne__(self, other):
         return not self.__eq__(other)
 
+    def __hash__(self):
+        return id(self)
+
     def __repr__(self):
         extra = ' raw' if self.raw else ''
         extra += ' fatal' if self.fatal else ''
@@ -332,7 +342,7 @@ class Option(object):
     def _get_default(self):
         return None
 
-    def parse(self, value):
+    def parse(self, value, raw=False):
         """Parse the given value."""
         raise NotImplementedError()
 
@@ -341,7 +351,7 @@ class Option(object):
 
     def to_string(self, value):
         """Return a string representation of the value."""
-        return str(value)
+        return text_type(value)
 
 
 class BoolOption(Option):
@@ -432,6 +442,9 @@ class ListOption(Option):
 
         return equal
 
+    def __hash__(self):
+        return id(self)
+
     def _get_default(self):
         return []
 
@@ -501,6 +514,9 @@ class StringOption(Option):
 
         return equal
 
+    def __hash__(self):
+        return id(self)
+
     def _get_default(self):
         return '' if not self.null else None
 
@@ -514,7 +530,7 @@ class StringOption(Option):
             result = value
         elif self.null:
             result = None if value in (None, 'None') else value
-        elif isinstance(value, basestring):
+        elif isinstance(value, string_types):
             result = value
         else:
             result = repr(value)
@@ -526,7 +542,7 @@ class StringOption(Option):
         return value
 
     def validate(self, value):
-        return (self.null and value is None) or isinstance(value, basestring)
+        return (self.null and value is None) or isinstance(value, string_types)
 
 
 class TupleOption(Option):
@@ -552,6 +568,9 @@ class TupleOption(Option):
 
         return equal
 
+    def __hash__(self):
+        return id(self)
+
     def _get_default(self):
         return ()
 
@@ -620,6 +639,9 @@ class DictOption(Option):
 
         return equal
 
+    def __hash__(self):
+        return id(self)
+
     def _get_default(self):
         default = {}
         for key, value in self.spec.items():
@@ -663,12 +685,13 @@ class DictOption(Option):
                 # parse it using the default item parser
                 option = self.item
 
-            # parse option
-            kwargs = {}
-            if option.require_parser:
-                kwargs['parser'] = parser
-            if not raw:
-                value = option.parse(value, **kwargs)
+            if not option.validate(value):
+                # parse option
+                kwargs = {}
+                if option.require_parser:
+                    kwargs['parser'] = parser
+                if not raw:
+                    value = option.parse(value, **kwargs)
             result[key] = value
 
         # fill in missing items with default values
@@ -682,7 +705,7 @@ class DictOption(Option):
                     if not raw:
                         value = option.default
                     else:
-                        value = unicode(option.default)
+                        value = text_type(option.default)
                     result[key] = value
         return result
 
diff -pruN 1.0-1/configglue/tests/app/test_base.py 1.1.2-0ubuntu3/configglue/tests/app/test_base.py
--- 1.0-1/configglue/tests/app/test_base.py	2011-08-04 21:55:29.000000000 +0000
+++ 1.1.2-0ubuntu3/configglue/tests/app/test_base.py	2013-07-11 16:25:37.000000000 +0000
@@ -4,7 +4,7 @@
 #
 # A library for simple, DRY configuration of applications
 #
-# (C) 2009--2011 by Canonical Ltd.
+# (C) 2009--2013 by Canonical Ltd.
 # by John R. Lenton <john.lenton@canonical.com>
 # and Ricardo Kirkner <ricardo.kirkner@canonical.com>
 #
@@ -14,7 +14,6 @@
 #
 ###############################################################################
 import os
-import user
 from optparse import OptionParser
 from unittest import TestCase
 
@@ -68,8 +67,8 @@ def make_config(app=None):
 class ConfigTestCase(TestCase):
     def get_xdg_config_dirs(self):
         xdg_config_home = os.environ.get('XDG_CONFIG_HOME',
-            os.path.join(user.home, '.config'))
-        xdg_config_dirs = ([xdg_config_home] + 
+            os.path.join(os.path.expanduser('~'), '.config'))
+        xdg_config_dirs = ([xdg_config_home] +
             os.environ.get('XDG_CONFIG_DIRS', '/etc/xdg').split(':'))
         return xdg_config_dirs
 
diff -pruN 1.0-1/configglue/tests/app/test_plugin.py 1.1.2-0ubuntu3/configglue/tests/app/test_plugin.py
--- 1.0-1/configglue/tests/app/test_plugin.py	2011-08-04 21:55:29.000000000 +0000
+++ 1.1.2-0ubuntu3/configglue/tests/app/test_plugin.py	2013-07-11 16:25:39.000000000 +0000
@@ -4,7 +4,7 @@
 #
 # A library for simple, DRY configuration of applications
 #
-# (C) 2009--2011 by Canonical Ltd.
+# (C) 2009--2013 by Canonical Ltd.
 # by John R. Lenton <john.lenton@canonical.com>
 # and Ricardo Kirkner <ricardo.kirkner@canonical.com>
 #
diff -pruN 1.0-1/configglue/tests/inischema/__init__.py 1.1.2-0ubuntu3/configglue/tests/inischema/__init__.py
--- 1.0-1/configglue/tests/inischema/__init__.py	2011-08-04 21:55:29.000000000 +0000
+++ 1.1.2-0ubuntu3/configglue/tests/inischema/__init__.py	2013-07-11 16:25:51.000000000 +0000
@@ -4,7 +4,7 @@
 #
 # A library for simple, DRY configuration of applications
 #
-# (C) 2009--2011 by Canonical Ltd.
+# (C) 2009--2013 by Canonical Ltd.
 # by John R. Lenton <john.lenton@canonical.com>
 # and Ricardo Kirkner <ricardo.kirkner@canonical.com>
 #
diff -pruN 1.0-1/configglue/tests/inischema/test_attributed.py 1.1.2-0ubuntu3/configglue/tests/inischema/test_attributed.py
--- 1.0-1/configglue/tests/inischema/test_attributed.py	2011-08-04 21:55:29.000000000 +0000
+++ 1.1.2-0ubuntu3/configglue/tests/inischema/test_attributed.py	2013-07-11 17:00:57.000000000 +0000
@@ -4,7 +4,7 @@
 #
 # A library for simple, DRY configuration of applications
 #
-# (C) 2009--2011 by Canonical Ltd.
+# (C) 2009--2013 by Canonical Ltd.
 # by John R. Lenton <john.lenton@canonical.com>
 # and Ricardo Kirkner <ricardo.kirkner@canonical.com>
 #
@@ -13,14 +13,15 @@
 # For bug reports, support, and new releases: http://launchpad.net/configglue
 #
 ###############################################################################
+from __future__ import unicode_literals
 
 # in testfiles, putting docstrings on methods messes up with the
 # runner's output, so pylint: disable-msg=C0111
 
 import unittest
-from ConfigParser import RawConfigParser
-from StringIO import StringIO
+from io import StringIO
 
+from configglue._compat import RawConfigParser
 from configglue.inischema.attributed import AttributedConfigParser
 
 
diff -pruN 1.0-1/configglue/tests/inischema/test_glue2glue.py 1.1.2-0ubuntu3/configglue/tests/inischema/test_glue2glue.py
--- 1.0-1/configglue/tests/inischema/test_glue2glue.py	2011-08-04 21:55:29.000000000 +0000
+++ 1.1.2-0ubuntu3/configglue/tests/inischema/test_glue2glue.py	2013-07-11 16:25:56.000000000 +0000
@@ -5,7 +5,7 @@
 #
 # A library for simple, DRY configuration of applications
 #
-# (C) 2009--2011 by Canonical Ltd.
+# (C) 2009--2013 by Canonical Ltd.
 # by John R. Lenton <john.lenton@canonical.com>
 # and Ricardo Kirkner <ricardo.kirkner@canonical.com>
 #
@@ -14,11 +14,12 @@
 # For bug reports, support, and new releases: http://launchpad.net/configglue
 #
 ###############################################################################
+from __future__ import unicode_literals
 
 import sys
 import textwrap
 import unittest
-from StringIO import StringIO
+from io import StringIO
 
 from configglue.inischema.glue import (
     configglue,
@@ -67,8 +68,6 @@ class TestGlueConvertor(unittest.TestCas
         s = textwrap.dedent("""
             [__main__]
             bar = zátrapa
-            bar.parser = unicode
-            bar.parser.args = utf-8
         """)
         _, cg, _ = configglue(StringIO(s))
         _, sg, _ = schemaconfigglue(ini2schema(StringIO(s)))
diff -pruN 1.0-1/configglue/tests/inischema/test_glue.py 1.1.2-0ubuntu3/configglue/tests/inischema/test_glue.py
--- 1.0-1/configglue/tests/inischema/test_glue.py	2011-08-04 21:55:29.000000000 +0000
+++ 1.1.2-0ubuntu3/configglue/tests/inischema/test_glue.py	2013-07-11 16:25:58.000000000 +0000
@@ -4,7 +4,7 @@
 #
 # A library for simple, DRY configuration of applications
 #
-# (C) 2009--2011 by Canonical Ltd.
+# (C) 2009--2013 by Canonical Ltd.
 # by John R. Lenton <john.lenton@canonical.com>
 # and Ricardo Kirkner <ricardo.kirkner@canonical.com>
 #
@@ -13,21 +13,25 @@
 # For bug reports, support, and new releases: http://launchpad.net/configglue
 #
 ###############################################################################
+from __future__ import unicode_literals
 
 # in testfiles, putting docstrings on methods messes up with the
 # runner's output, so pylint: disable-msg=C0111
 
 import sys
 import unittest
-from StringIO import StringIO
+from io import BytesIO, StringIO, TextIOWrapper
 
+from mock import patch
+
+from configglue._compat import PY2
 from configglue.inischema.glue import configglue
 
 
 class TestBase(unittest.TestCase):
     """ Base class to keep common set-up """
     def setUp(self):
-        self.file = StringIO(self.ini)
+        self.file = TextIOWrapper(BytesIO(self.ini))
         self.old_sys_argv = sys.argv
         sys.argv = ['']
 
@@ -36,7 +40,7 @@ class TestBase(unittest.TestCase):
 
 
 class TestGlue(TestBase):
-    ini = '''
+    ini = b'''
 [blah]
 foo.help = yadda yadda yadda
          yadda
@@ -60,17 +64,20 @@ foo = 2
                          {self.opt: '5'})
 
     def test_help_is_displayed(self):
-        sys.stdout = StringIO()
-        try:
-            configglue(self.file, args=['', '--help'])
-        except SystemExit:
-            output = sys.stdout.getvalue()
-            sys.stdout = sys.__stdout__
+        new_callable = StringIO
+        if PY2:
+            new_callable = BytesIO
+
+        with patch('sys.stdout', new_callable=new_callable) as mock_stdout:
+            try:
+                configglue(self.file, args=['', '--help'])
+            except SystemExit:
+                output = mock_stdout.getvalue()
         self.assertTrue('yadda yadda yadda yadda' in output)
 
 
 class TestCrazyGlue(TestGlue):
-    ini = '''
+    ini = b'''
 [bl-ah]
 foo.default = 3
 foo.help = yadda yadda yadda
@@ -84,7 +91,7 @@ foo = 2
 
 
 class TestNoValue(TestGlue):
-    ini = '''
+    ini = b'''
 [blah]
 foo.help = yadda yadda yadda
          yadda
@@ -96,7 +103,7 @@ foo = 3
 
 
 class TestGlue2(TestBase):
-    ini = '[__main__]\na=1\n'
+    ini = b'[__main__]\na=1\n'
 
     def test_main(self):
         parser, options, args = configglue(self.file)
@@ -104,7 +111,7 @@ class TestGlue2(TestBase):
 
 
 class TestGlue3(TestBase):
-    ini = '[x]\na.help=hi\n'
+    ini = b'[x]\na.help=hi\n'
 
     def test_empty(self):
         parser, options, args = configglue(self.file)
@@ -117,7 +124,7 @@ class TestGlue3(TestBase):
 
 
 class TestGlueBool(TestBase):
-    ini = '''[__main__]
+    ini = b'''[__main__]
 foo.parser=bool
 foo.action=store_true
 
@@ -136,7 +143,7 @@ bar.action = store_false
 
 
 class TestGlueLines(TestBase):
-    ini = '''[__main__]
+    ini = b'''[__main__]
 foo.parser = lines
 foo.action = append
 
diff -pruN 1.0-1/configglue/tests/inischema/test_parsers.py 1.1.2-0ubuntu3/configglue/tests/inischema/test_parsers.py
--- 1.0-1/configglue/tests/inischema/test_parsers.py	2011-08-04 21:55:29.000000000 +0000
+++ 1.1.2-0ubuntu3/configglue/tests/inischema/test_parsers.py	2013-07-11 16:26:01.000000000 +0000
@@ -4,7 +4,7 @@
 #
 # A library for simple, DRY configuration of applications
 #
-# (C) 2009--2011 by Canonical Ltd.
+# (C) 2009--2013 by Canonical Ltd.
 # by John R. Lenton <john.lenton@canonical.com>
 # and Ricardo Kirkner <ricardo.kirkner@canonical.com>
 #
diff -pruN 1.0-1/configglue/tests/inischema/test_typed.py 1.1.2-0ubuntu3/configglue/tests/inischema/test_typed.py
--- 1.0-1/configglue/tests/inischema/test_typed.py	2011-08-04 21:55:29.000000000 +0000
+++ 1.1.2-0ubuntu3/configglue/tests/inischema/test_typed.py	2013-07-11 17:00:53.000000000 +0000
@@ -1,10 +1,11 @@
+# -*- encoding: utf-8 -*-
 ###############################################################################
 #
 # configglue -- glue for your apps' configuration
 #
 # A library for simple, DRY configuration of applications
 #
-# (C) 2009--2011 by Canonical Ltd.
+# (C) 2009--2013 by Canonical Ltd.
 # by John R. Lenton <john.lenton@canonical.com>
 # and Ricardo Kirkner <ricardo.kirkner@canonical.com>
 #
@@ -13,14 +14,15 @@
 # For bug reports, support, and new releases: http://launchpad.net/configglue
 #
 ###############################################################################
+from __future__ import unicode_literals
 
 # in testfiles, putting docstrings on methods messes up with the
 # runner's output, so pylint: disable-msg=C0111
 
 import unittest
-from StringIO import StringIO
-from ConfigParser import RawConfigParser
+from io import StringIO
 
+from configglue._compat import RawConfigParser
 from configglue.inischema.typed import TypedConfigParser
 
 
@@ -52,14 +54,8 @@ baz = marker
 baz2.parser = more.parser
 baz2 = -1
 
-meep = \xe1rbol
+meep = árbol
 meep.parser = unicode
-meep.parser.args = latin1
-
-quux = \xe1rbol
-quux.parser = unicode
-quux.parser.args = utf-8
-                   replace
 
 thud.help = this is the help for thud
 
@@ -119,9 +115,7 @@ class TestParserd(BaseTest):
                                      ('baz', marker),
                                      ('baz2', None),
                                      ('foo', 1j),
-                                     ('meep', u'\xe1rbol'),
-                                     ('quux', unicode('\xe1rbol', 'utf-8',
-                                                      'replace')),
+                                     ('meep', '\xe1rbol'),
                                      ('thud', None),
                                      ('woof', True),
                                      ])])
diff -pruN 1.0-1/configglue/tests/__init__.py 1.1.2-0ubuntu3/configglue/tests/__init__.py
--- 1.0-1/configglue/tests/__init__.py	2011-08-04 21:55:29.000000000 +0000
+++ 1.1.2-0ubuntu3/configglue/tests/__init__.py	2013-07-11 16:24:45.000000000 +0000
@@ -4,7 +4,7 @@
 #
 # A library for simple, DRY configuration of applications
 #
-# (C) 2009--2011 by Canonical Ltd.
+# (C) 2009--2013 by Canonical Ltd.
 # by John R. Lenton <john.lenton@canonical.com>
 # and Ricardo Kirkner <ricardo.kirkner@canonical.com>
 #
diff -pruN 1.0-1/configglue/tests/test_contrib_schema.py 1.1.2-0ubuntu3/configglue/tests/test_contrib_schema.py
--- 1.0-1/configglue/tests/test_contrib_schema.py	1970-01-01 00:00:00.000000000 +0000
+++ 1.1.2-0ubuntu3/configglue/tests/test_contrib_schema.py	2013-07-11 16:24:55.000000000 +0000
@@ -0,0 +1,34 @@
+###############################################################################
+#
+# configglue -- glue for your apps' configuration
+#
+# A library for simple, DRY configuration of applications
+#
+# (C) 2009--2013 by Canonical Ltd.
+# by John R. Lenton <john.lenton@canonical.com>
+# and Ricardo Kirkner <ricardo.kirkner@canonical.com>
+#
+# Released under the BSD License (see the file LICENSE)
+#
+# For bug reports, support, and new releases: http://launchpad.net/configglue
+#
+###############################################################################
+from unittest import TestCase
+
+from configglue.schema import ListOption, StringOption
+from configglue.contrib.schema import DjangoOpenIdAuthSchema
+
+
+class DjangoOpenIdAuthSchemaTestCase(TestCase):
+
+    def test_openid_launchpad_teams_required_option(self):
+        schema = DjangoOpenIdAuthSchema()
+        option = schema.openid.openid_launchpad_teams_required
+        self.assertTrue(isinstance(option, ListOption))
+        self.assertTrue(isinstance(option.item, StringOption))
+
+    def test_openid_email_whitelist_regexp_list_option(self):
+        schema = DjangoOpenIdAuthSchema()
+        option = schema.openid.openid_email_whitelist_regexp_list
+        self.assertTrue(isinstance(option, ListOption))
+        self.assertTrue(isinstance(option.item, StringOption))
diff -pruN 1.0-1/configglue/tests/test_parser.py 1.1.2-0ubuntu3/configglue/tests/test_parser.py
--- 1.0-1/configglue/tests/test_parser.py	2011-08-04 21:55:29.000000000 +0000
+++ 1.1.2-0ubuntu3/configglue/tests/test_parser.py	2013-07-11 16:36:46.000000000 +0000
@@ -5,7 +5,7 @@
 #
 # A library for simple, DRY configuration of applications
 #
-# (C) 2009--2011 by Canonical Ltd.
+# (C) 2009--2013 by Canonical Ltd.
 # by John R. Lenton <john.lenton@canonical.com>
 # and Ricardo Kirkner <ricardo.kirkner@canonical.com>
 #
@@ -14,30 +14,33 @@
 # For bug reports, support, and new releases: http://launchpad.net/configglue
 #
 ###############################################################################
+from __future__ import unicode_literals
 
-import logging
+import codecs
 import os
 import shutil
 import tempfile
 import textwrap
 import unittest
-from ConfigParser import (
-    DEFAULTSECT,
-    InterpolationDepthError,
-    InterpolationMissingOptionError,
-    InterpolationSyntaxError,
-    NoSectionError,
-)
-from StringIO import StringIO
+from io import BytesIO
 
 from mock import (
+    MagicMock,
     Mock,
     patch,
 )
 
+from configglue._compat import iteritems
+from configglue._compat import (
+    DEFAULTSECT,
+    InterpolationDepthError,
+    InterpolationMissingOptionError,
+    InterpolationSyntaxError,
+    NoOptionError,
+    NoSectionError,
+)
 from configglue.parser import (
     CONFIG_FILE_ENCODING,
-    NoOptionError,
     SchemaConfigParser,
     SchemaValidationError,
 )
@@ -66,27 +69,22 @@ class TestIncludes(unittest.TestCase):
             foo = StringOption()
         self.schema = MySchema()
         fd, self.name = tempfile.mkstemp(suffix='.cfg')
-        os.write(fd, '[__main__]\nfoo=bar\n')
+        os.write(fd, b'[__main__]\nfoo=bar\n')
         os.close(fd)
 
-        # disable logging output during test
-        self.level = logging.getLogger().level
-        logging.disable('ERROR')
-
     def tearDown(self):
         os.remove(self.name)
 
-        # re-enable original logging level
-        logging.getLogger().setLevel(self.level)
-
     def test_basic_include(self):
-        config = StringIO('[__main__]\nincludes=%s' % self.name)
+        config = '[__main__]\nincludes=%s' % self.name
+        config = BytesIO(config.encode(CONFIG_FILE_ENCODING))
         parser = SchemaConfigParser(self.schema)
         parser.readfp(config, 'my.cfg')
         self.assertEquals({'__main__': {'foo': 'bar'}}, parser.values())
 
     def test_locate(self):
-        config = StringIO("[__main__]\nincludes=%s" % self.name)
+        config = "[__main__]\nincludes=%s" % self.name
+        config = BytesIO(config.encode(CONFIG_FILE_ENCODING))
         parser = SchemaConfigParser(self.schema)
         parser.readfp(config, 'my.cfg')
 
@@ -94,7 +92,7 @@ class TestIncludes(unittest.TestCase):
         expected_location = self.name
         self.assertEqual(expected_location, location)
 
-    @patch('configglue.parser.logging.warn')
+    @patch('configglue.parser.logger.warn')
     @patch('configglue.parser.codecs.open')
     def test_read_ioerror(self, mock_open, mock_warn):
         mock_open.side_effect = IOError
@@ -112,24 +110,29 @@ class TestIncludes(unittest.TestCase):
         def setup_config():
             folder = tempfile.mkdtemp()
 
-            f = open("%s/first.cfg" % folder, 'w')
+            f = codecs.open("%s/first.cfg" % folder, 'w',
+                            encoding=CONFIG_FILE_ENCODING)
             f.write("[__main__]\nfoo=1\nincludes=second.cfg")
             f.close()
 
-            f = open("%s/second.cfg" % folder, 'w')
+            f = codecs.open("%s/second.cfg" % folder, 'w',
+                            encoding=CONFIG_FILE_ENCODING)
             f.write("[__main__]\nbar=2\nincludes=sub/third.cfg")
             f.close()
 
             os.mkdir("%s/sub" % folder)
-            f = open("%s/sub/third.cfg" % folder, 'w')
+            f = codecs.open("%s/sub/third.cfg" % folder, 'w',
+                            encoding=CONFIG_FILE_ENCODING)
             f.write("[__main__]\nincludes=../fourth.cfg")
             f.close()
 
-            f = open("%s/fourth.cfg" % folder, 'w')
+            f = codecs.open("%s/fourth.cfg" % folder, 'w',
+                            encoding=CONFIG_FILE_ENCODING)
             f.write("[__main__]\nbaz=3")
             f.close()
 
-            config = StringIO("[__main__]\nincludes=%s/first.cfg" % folder)
+            config = "[__main__]\nincludes=%s/first.cfg" % folder
+            config = BytesIO(config.encode(CONFIG_FILE_ENCODING))
             return config, folder
 
         class MySchema(Schema):
@@ -158,16 +161,18 @@ class TestIncludes(unittest.TestCase):
         def setup_config():
             folder = tempfile.mkdtemp()
 
-            f = open("%s/first.cfg" % folder, 'w')
+            f = codecs.open("%s/first.cfg" % folder, 'w',
+                            encoding=CONFIG_FILE_ENCODING)
             f.write("[__main__]\nfoo=1\nbar=2\nincludes=second.cfg")
             f.close()
 
-            f = open("%s/second.cfg" % folder, 'w')
+            f = codecs.open("%s/second.cfg" % folder, 'w',
+                            encoding=CONFIG_FILE_ENCODING)
             f.write("[__main__]\nbaz=3")
             f.close()
 
-            config = StringIO(
-                "[__main__]\nfoo=4\nincludes=%s/first.cfg" % folder)
+            config = "[__main__]\nfoo=4\nincludes=%s/first.cfg" % folder
+            config = BytesIO(config.encode(CONFIG_FILE_ENCODING))
             return config, folder
 
         class MySchema(Schema):
@@ -198,7 +203,7 @@ class TestInterpolation(unittest.TestCas
         class MySchema(Schema):
             foo = StringOption()
             bar = BoolOption()
-        config = StringIO('[__main__]\nbar=%(foo)s\nfoo=True')
+        config = BytesIO(b'[__main__]\nbar=%(foo)s\nfoo=True')
         parser = SchemaConfigParser(MySchema())
         parser.readfp(config, 'my.cfg')
         self.assertEquals({'__main__': {'foo': 'True', 'bar': True}},
@@ -243,8 +248,8 @@ class TestInterpolation(unittest.TestCas
         rawval = '%(bar)'
         vars = {'foo': '%(bar)s', 'bar': 'pepe'}
         parser = SchemaConfigParser(MySchema())
-        self.assertRaises(InterpolationSyntaxError, parser._interpolate,
-            section, option, rawval, vars)
+        self.assertRaises(InterpolationSyntaxError,
+                          parser._interpolate, section, option, rawval, vars)
 
     def test_interpolate_across_sections(self):
         """Test interpolation across sections."""
@@ -255,7 +260,7 @@ class TestInterpolation(unittest.TestCas
             class baz(Section):
                 wham = IntOption()
 
-        config = StringIO("[foo]\nbar=%(wham)s\n[baz]\nwham=42")
+        config = BytesIO(b"[foo]\nbar=%(wham)s\n[baz]\nwham=42")
         parser = SchemaConfigParser(MySchema())
         parser.readfp(config)
         self.assertRaises(InterpolationMissingOptionError,
@@ -270,11 +275,11 @@ class TestInterpolation(unittest.TestCas
             class baz(Section):
                 wham = IntOption()
 
-        config = StringIO("[foo]\nbar=%(wham)s\n[baz]\nwham=42")
+        config = BytesIO(b"[foo]\nbar=%(wham)s\n[baz]\nwham=42")
         parser = SchemaConfigParser(MySchema())
         parser.readfp(config)
-        self.assertRaises(InterpolationMissingOptionError, parser.get,
-                          'foo', 'bar')
+        self.assertRaises(InterpolationMissingOptionError,
+                          parser.get, 'foo', 'bar')
 
     @patch('configglue.parser.os')
     def test_interpolate_environment_basic_syntax(self, mock_os):
@@ -291,6 +296,69 @@ class TestInterpolation(unittest.TestCas
         self.assertEqual(result, 'foo')
 
     @patch('configglue.parser.os')
+    def test_interpolate_environment_with_default_uses_env(self, mock_os):
+        mock_os.environ = {'PATH': 'foo'}
+        parser = SchemaConfigParser(Schema())
+        result = parser.interpolate_environment("X${PATH:-bar}X")
+        self.assertEqual(result, 'XfooX')
+
+    @patch('configglue.parser.os')
+    def test_interpolate_environment_with_default_uses_default(self, mock_os):
+        mock_os.environ = {}
+        parser = SchemaConfigParser(Schema())
+        result = parser.interpolate_environment("X${PATH:-bar}X")
+        self.assertEqual(result, 'XbarX')
+
+    @patch('configglue.parser.os')
+    def test_interpolate_environment_with_empty_default(self, mock_os):
+        mock_os.environ = {}
+        parser = SchemaConfigParser(Schema())
+        result = parser.interpolate_environment("X${PATH:-}X")
+        self.assertEqual(result, 'XX')
+
+    @patch('configglue.parser.os')
+    def test_interpolate_environment_multiple(self, mock_os):
+        mock_os.environ = {'FOO': 'foo', 'BAR': 'bar', 'BAZ': 'baz'}
+        parser = SchemaConfigParser(Schema())
+        result = parser.interpolate_environment(
+            "$FOO, ${BAR}, ${BAZ:-default}")
+        self.assertEqual(result, 'foo, bar, baz')
+
+    @patch('configglue.parser.os')
+    def test_interpolate_environment_multiple_with_default(self, mock_os):
+        mock_os.environ = {'FOO': 'foo', 'BAR': 'bar'}
+        parser = SchemaConfigParser(Schema())
+        result = parser.interpolate_environment(
+            "$FOO, ${BAR}, ${BAZ:-default}")
+        self.assertEqual(result, 'foo, bar, default')
+
+    def test_interpolate_environment_multiple_defaults(self):
+        "Only the first default is used"
+        parser = SchemaConfigParser(Schema())
+        result = parser.interpolate_environment(
+                "${FOO:-default1}, ${FOO:-default2}")
+        self.assertEqual(result, 'default1, default1')
+
+    @patch('configglue.parser.os')
+    def test_interpolate_environment_defaults_nested(self, mock_os):
+        mock_os.environ = {'BAR': 'bar'}
+        parser = SchemaConfigParser(Schema())
+        result = parser.interpolate_environment("${FOO:-$BAR}")
+        self.assertEqual(result, 'bar')
+
+    @patch('configglue.parser.os')
+    @patch('configglue.parser.re.compile')
+    def test_interpolate_environment_default_loop(self, mock_compile, mock_os):
+        mock_os.environ = {'FOO': 'foo'}
+        parser = SchemaConfigParser(Schema())
+        mock_match = MagicMock()
+        mock_match.group.return_value = "FOO"
+        mock_compile.return_value.search.return_value = mock_match
+        result = parser.interpolate_environment("${FOO:-bar}")
+        # should be uninterpolated result
+        self.assertEqual(result, '${FOO:-bar}')
+
+    @patch('configglue.parser.os')
     def test_interpolate_environment_in_config(self, mock_os):
         mock_os.environ = {'PYTHONPATH': 'foo', 'PATH': 'bar'}
 
@@ -298,7 +366,7 @@ class TestInterpolation(unittest.TestCas
             pythonpath = StringOption()
             path = StringOption()
 
-        config = StringIO("[__main__]\npythonpath=${PYTHONPATH}\npath=$PATH")
+        config = BytesIO(b"[__main__]\npythonpath=${PYTHONPATH}\npath=$PATH")
         parser = SchemaConfigParser(MySchema())
         parser.readfp(config)
         self.assertEqual(parser.values('__main__'),
@@ -316,7 +384,7 @@ class TestInterpolation(unittest.TestCas
         class MySchema(Schema):
             foo = IntOption()
 
-        config = StringIO("[__main__]\nfoo=$FOO")
+        config = BytesIO(b"[__main__]\nfoo=$FOO")
         parser = SchemaConfigParser(MySchema())
         parser.readfp(config)
         self.assertEqual(parser.get('__main__', 'foo'), 42)
@@ -325,7 +393,7 @@ class TestInterpolation(unittest.TestCas
         class MySchema(Schema):
             foo = IntOption()
 
-        config = StringIO("[__main__]\nfoo=$FOO")
+        config = BytesIO(b"[__main__]\nfoo=$FOO")
         parser = SchemaConfigParser(MySchema())
         parser.readfp(config)
         self.assertEqual(parser.get('__main__', 'foo'), 0)
@@ -334,7 +402,7 @@ class TestInterpolation(unittest.TestCas
         """Test get_interpolation_keys for a string."""
         class MySchema(Schema):
             foo = StringOption()
-        config = StringIO("[__main__]\nfoo=%(bar)s")
+        config = BytesIO(b"[__main__]\nfoo=%(bar)s")
         expected = ('%(bar)s', set(['bar']))
 
         parser = SchemaConfigParser(MySchema())
@@ -346,7 +414,7 @@ class TestInterpolation(unittest.TestCas
         """Test get_interpolation_keys for an integer."""
         class MySchema(Schema):
             foo = IntOption()
-        config = StringIO("[__main__]\nfoo=%(bar)s")
+        config = BytesIO(b"[__main__]\nfoo=%(bar)s")
         expected = ('%(bar)s', set(['bar']))
 
         parser = SchemaConfigParser(MySchema())
@@ -358,7 +426,7 @@ class TestInterpolation(unittest.TestCas
         """Test get_interpolation_keys for a boolean."""
         class MySchema(Schema):
             foo = BoolOption()
-        config = StringIO("[__main__]\nfoo=%(bar)s")
+        config = BytesIO(b"[__main__]\nfoo=%(bar)s")
         expected = ('%(bar)s', set(['bar']))
 
         parser = SchemaConfigParser(MySchema())
@@ -370,7 +438,7 @@ class TestInterpolation(unittest.TestCas
         """Test get_interpolation_keys for a tuple."""
         class MySchema(Schema):
             foo = TupleOption(2)
-        config = StringIO("[__main__]\nfoo=%(bar)s,%(baz)s")
+        config = BytesIO(b"[__main__]\nfoo=%(bar)s,%(baz)s")
         expected = ('%(bar)s,%(baz)s', set(['bar', 'baz']))
 
         parser = SchemaConfigParser(MySchema())
@@ -382,7 +450,7 @@ class TestInterpolation(unittest.TestCas
         """Test get_interpolation_keys for a list."""
         class MySchema(Schema):
             foo = ListOption(item=StringOption())
-        config = StringIO("[__main__]\nfoo=%(bar)s\n    %(baz)s")
+        config = BytesIO(b"[__main__]\nfoo=%(bar)s\n    %(baz)s")
         expected = ('%(bar)s\n%(baz)s', set(['bar', 'baz']))
 
         parser = SchemaConfigParser(MySchema())
@@ -394,8 +462,8 @@ class TestInterpolation(unittest.TestCas
         """Test get_interpolation_keys for a list of tuples."""
         class MySchema(Schema):
             foo = ListOption(item=TupleOption(2))
-        config = StringIO(
-            "[__main__]\nfoo=%(bar)s,%(bar)s\n    %(baz)s,%(baz)s")
+        config = BytesIO(
+            b"[__main__]\nfoo=%(bar)s,%(bar)s\n    %(baz)s,%(baz)s")
         expected = ('%(bar)s,%(bar)s\n%(baz)s,%(baz)s',
                     set(['bar', 'baz']))
 
@@ -408,14 +476,14 @@ class TestInterpolation(unittest.TestCas
         """Test get_interpolation_keys for a dict."""
         class MySchema(Schema):
             foo = DictOption(spec={'a': IntOption()})
-        config = StringIO(textwrap.dedent("""
+        config = BytesIO(textwrap.dedent("""
             [__noschema__]
             bar=4
             [__main__]
             foo=mydict
             [mydict]
             a=%(bar)s
-            """))
+            """).encode(CONFIG_FILE_ENCODING))
         expected = ('mydict', set([]))
 
         parser = SchemaConfigParser(MySchema())
@@ -427,8 +495,8 @@ class TestInterpolation(unittest.TestCas
         """Test interpolate_value for a duplicate key."""
         class MySchema(Schema):
             foo = TupleOption(2)
-        config = StringIO(
-            "[__noschema__]\nbar=4\n[__main__]\nfoo=%(bar)s,%(bar)s")
+        config = BytesIO(
+            b"[__noschema__]\nbar=4\n[__main__]\nfoo=%(bar)s,%(bar)s")
         expected_value = '4,4'
 
         parser = SchemaConfigParser(MySchema())
@@ -440,7 +508,7 @@ class TestInterpolation(unittest.TestCas
         """Test interpolate_value with an invalid key."""
         class MySchema(Schema):
             foo = TupleOption(2)
-        config = StringIO("[other]\nbar=4\n[__main__]\nfoo=%(bar)s,%(bar)s")
+        config = BytesIO(b"[other]\nbar=4\n[__main__]\nfoo=%(bar)s,%(bar)s")
         expected_value = None
 
         parser = SchemaConfigParser(MySchema())
@@ -452,7 +520,7 @@ class TestInterpolation(unittest.TestCas
         """Test interpolate_value with no keys."""
         class MySchema(Schema):
             foo = TupleOption(2)
-        config = StringIO("[__main__]\nfoo=%(bar)s,%(bar)s")
+        config = BytesIO(b"[__main__]\nfoo=%(bar)s,%(bar)s")
 
         mock_get_interpolation_keys = Mock(return_value=('%(bar)s', None))
 
@@ -468,7 +536,7 @@ class TestInterpolation(unittest.TestCas
         """Test get using a raw value."""
         class MySchema(Schema):
             foo = StringOption(raw=True)
-        config = StringIO('[__main__]\nfoo=blah%(asd)##$@@dddf2kjhkjs')
+        config = BytesIO(b'[__main__]\nfoo=blah%(asd)##$@@dddf2kjhkjs')
         expected_value = 'blah%(asd)##$@@dddf2kjhkjs'
 
         parser = SchemaConfigParser(MySchema())
@@ -480,14 +548,14 @@ class TestInterpolation(unittest.TestCas
         """Test interpolation while parsing a dict."""
         class MySchema(Schema):
             foo = DictOption(spec={'a': IntOption()})
-        config = StringIO(textwrap.dedent("""
+        config = BytesIO(textwrap.dedent("""
             [__noschema__]
             bar=4
             [__main__]
             foo=mydict
             [mydict]
             a=%(bar)s
-            """))
+            """).encode(CONFIG_FILE_ENCODING))
         expected = {'__main__': {'foo': {'a': 4}}}
 
         parser = SchemaConfigParser(MySchema())
@@ -502,7 +570,7 @@ class TestSchemaConfigParser(unittest.Te
             foo = StringOption()
         self.schema = MySchema()
         self.parser = SchemaConfigParser(self.schema)
-        self.config = StringIO("[__main__]\nfoo = bar")
+        self.config = BytesIO(b"[__main__]\nfoo = bar")
 
     def test_init_no_args(self):
         self.assertRaises(TypeError, SchemaConfigParser)
@@ -524,16 +592,17 @@ class TestSchemaConfigParser(unittest.Te
         self.assertEqual(set(items), set([('foo', 'bar')]))
 
     def test_items_no_section(self):
-        self.assertRaises(NoSectionError, self.parser.items, '__main__')
+        self.assertRaises(NoSectionError, self.parser.items,
+                          '__main__')
 
     def test_items_raw(self):
-        config = StringIO('[__main__]\nfoo=%(baz)s')
+        config = BytesIO(b'[__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')
+        config = BytesIO(b'[__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')]))
@@ -547,17 +616,17 @@ class TestSchemaConfigParser(unittest.Te
                 bar = StringOption()
 
         parser = SchemaConfigParser(MySchema())
-        config = StringIO('[__main__]\nfoo=%(bar)s\n[baz]\nbar=42')
+        config = BytesIO(b'[__main__]\nfoo=%(bar)s\n[baz]\nbar=42')
         parser.readfp(config)
         # test interpolate
         items = parser.items('baz')
-        self.assertEqual(items, {'bar': '42'}.items())
+        self.assertEqual(items, list(iteritems({'bar': '42'})))
 
     def test_items_interpolate_error(self):
-        config = StringIO('[__main__]\nfoo=%(bar)s')
+        config = BytesIO(b'[__main__]\nfoo=%(bar)s')
         self.parser.readfp(config)
-        self.assertRaises(InterpolationMissingOptionError, self.parser.items,
-                          '__main__')
+        self.assertRaises(InterpolationMissingOptionError,
+                          self.parser.items, '__main__')
 
     def test_values_empty_parser(self):
         values = self.parser.values()
@@ -581,7 +650,7 @@ class TestSchemaConfigParser(unittest.Te
             class baz(Section):
                 bar = IntOption()
 
-        config = StringIO("[foo]\nbar=3\n[baz]\nbar=4")
+        config = BytesIO(b"[foo]\nbar=3\n[baz]\nbar=4")
         expected_values = {'foo': {'bar': 3}, 'baz': {'bar': 4}}
 
         schema = MySchema()
@@ -599,7 +668,7 @@ class TestSchemaConfigParser(unittest.Te
             class bar(Section):
                 baz = IntOption()
 
-        config = StringIO("[foo]\nbar=3\n[bar]\nbaz=4")
+        config = BytesIO(b"[foo]\nbar=3\n[bar]\nbaz=4")
         expected_values = {'foo': {'bar': 3}, 'bar': {'baz': 4}}
 
         schema = MySchema()
@@ -615,7 +684,7 @@ class TestSchemaConfigParser(unittest.Te
                 bar = StringOption()
 
         expected_value = 'baz'
-        config = StringIO("[foo]\nbar = baz")
+        config = BytesIO(b"[foo]\nbar = baz")
         parser = SchemaConfigParser(MyOtherSchema())
         parser.readfp(config)
         value = parser.get('foo', 'bar')
@@ -635,14 +704,14 @@ class TestSchemaConfigParser(unittest.Te
                 bla = StringOption(default='hello')
 
         schema = MySchema()
-        config = StringIO("[bar]\nbaz=123")
+        config = BytesIO(b"[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")
+        config = BytesIO(b"[bar]\nbla=123")
         expected = {
             '__main__': {'foo': True},
             'bar': {'baz': 0, 'bla': '123'}}
@@ -656,13 +725,13 @@ class TestSchemaConfigParser(unittest.Te
             foo = IntOption(fatal=True)
             bar = IntOption()
         schema = MySchema()
-        config = StringIO("[__main__]\nfoo=123")
+        config = BytesIO(b"[__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")
+        config = BytesIO(b"[__main__]\nbar=123")
         parser = SchemaConfigParser(schema)
         parser.readfp(config)
         self.assertRaises(NoOptionError, parser.values)
@@ -672,7 +741,7 @@ class TestSchemaConfigParser(unittest.Te
         class MySchema(Schema):
             foo = DictOption(spec={'bar': IntOption()})
 
-        config = StringIO("[__main__]\nfoo=mydict\n[mydict]\nbar=1")
+        config = BytesIO(b"[__main__]\nfoo=mydict\n[mydict]\nbar=1")
         parser = SchemaConfigParser(MySchema())
         parser.readfp(config)
         parser.parse_all()
@@ -698,10 +767,10 @@ class TestSchemaConfigParser(unittest.Te
         class MySchema(Schema):
             foo = DictOption()
 
-        config = StringIO(textwrap.dedent("""
+        config = BytesIO(textwrap.dedent("""
             [__main__]
             foo = dict1
-            """))
+            """).encode(CONFIG_FILE_ENCODING))
         parser = SchemaConfigParser(MySchema())
         parser.readfp(config)
         parser.parse_all()
@@ -714,8 +783,8 @@ class TestSchemaConfigParser(unittest.Te
             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')
+        config = BytesIO(b'[__main__]\nfoo=d1\n    d2\n    d3\n'
+                         b'[d1]\nbar=1\n[d2]\nbar=2\n[d3]\nbar=3')
         parser = SchemaConfigParser(MySchema())
         parser.readfp(config)
         parser.parse_all()
@@ -725,14 +794,14 @@ class TestSchemaConfigParser(unittest.Te
         self.assertEqual(expected_sections, extra_sections)
 
     def test_get_default(self):
-        config = StringIO("[__main__]\n")
+        config = BytesIO(b"[__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")
+        config = BytesIO(b"[__noschema__]\nbar=1\n[__main__]\n")
         expected = '1'
         self.parser.readfp(config)
         default = self.parser._get_default('__noschema__', 'bar')
@@ -743,7 +812,7 @@ class TestSchemaConfigParser(unittest.Te
         class MySchema(Schema):
             class foo(Section):
                 bar = IntOption()
-        config = StringIO("[__main__]\n")
+        config = BytesIO(b"[__main__]\n")
         expected = 0
 
         parser = SchemaConfigParser(MySchema())
@@ -752,12 +821,12 @@ class TestSchemaConfigParser(unittest.Te
         self.assertEqual(default, expected)
 
     def test_get_default_no_option(self):
-        self.assertRaises(NoOptionError, self.parser._get_default,
-            '__main__', 'bar')
+        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')
+        self.assertRaises(NoSectionError,
+                          self.parser._get_default, 'foo', 'bar')
 
     def test_multi_file_dict_config(self):
         """Test parsing a dict option spanning multiple files."""
@@ -766,8 +835,8 @@ class TestSchemaConfigParser(unittest.Te
                 'bar': IntOption(),
                 'baz': IntOption(),
             }, strict=True)
-        config1 = StringIO('[__main__]\nfoo=mydict\n[mydict]\nbar=1\nbaz=1')
-        config2 = StringIO('[mydict]\nbaz=2')
+        config1 = BytesIO(b'[__main__]\nfoo=mydict\n[mydict]\nbar=1\nbaz=1')
+        config2 = BytesIO(b'[mydict]\nbaz=2')
         expected_values = {'__main__': {'foo': {'bar': 1, 'baz': 2}}}
 
         parser = SchemaConfigParser(MySchema())
@@ -784,7 +853,7 @@ class TestSchemaConfigParser(unittest.Te
                     'baz': IntOption(),
                 }, strict=True))
 
-        config1 = StringIO('[__main__]\nfoo=mydict\n[mydict]\nbar=1\nbaz=1')
+        config1 = BytesIO(b'[__main__]\nfoo=mydict\n[mydict]\nbar=1\nbaz=1')
         expected_values = {'__main__': {'foo': [{'bar': 1, 'baz': 1}]}}
 
         parser = SchemaConfigParser(MySchema())
@@ -792,19 +861,19 @@ class TestSchemaConfigParser(unittest.Te
         self.assertEqual(parser.values(), expected_values)
 
         # override used dictionaries
-        config2 = StringIO('[__main__]\nfoo=otherdict\n[otherdict]\nbar=2')
+        config2 = BytesIO(b'[__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')
+        config3 = BytesIO(b'[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')
+        config4 = BytesIO(b'[__main__]\nfoo=mydict\n    otherdict')
         expected_values = {'__main__': {'foo': [{'bar': 1, 'baz': 1},
                                                {'bar': 2, 'baz': 3}]}}
         parser.readfp(config4)
@@ -814,11 +883,13 @@ class TestSchemaConfigParser(unittest.Te
         def setup_config():
             folder = tempfile.mkdtemp()
 
-            f = open("%s/first.cfg" % folder, 'w')
+            f = codecs.open("%s/first.cfg" % folder, 'w',
+                            encoding=CONFIG_FILE_ENCODING)
             f.write("[__main__]\nfoo=foo")
             f.close()
 
-            f = open("%s/second.cfg" % folder, 'w')
+            f = codecs.open("%s/second.cfg" % folder, 'w',
+                            encoding=CONFIG_FILE_ENCODING)
             f.write("[__main__]\nfoo=bar")
             f.close()
 
@@ -840,25 +911,26 @@ class TestSchemaConfigParser(unittest.Te
         fp, filename = tempfile.mkstemp()
 
         try:
-            f = open(filename, 'w')
-            f.write(u'[__main__]\nfoo=€'.encode(CONFIG_FILE_ENCODING))
+            f = codecs.open(filename, 'w',
+                            encoding=CONFIG_FILE_ENCODING)
+            f.write('[__main__]\nfoo=€')
             f.close()
 
             self.parser.read(filename)
             self.assertEqual(self.parser.values(),
-                {'__main__': {'foo': u'€'}})
+                {'__main__': {'foo': '€'}})
         finally:
             # destroy config file
             os.remove(filename)
 
     def test_readfp_with_utf8_encoded_text(self):
-        config = StringIO(u'[__main__]\nfoo=€'.encode(CONFIG_FILE_ENCODING))
+        config = BytesIO('[__main__]\nfoo=€'.encode(CONFIG_FILE_ENCODING))
         self.parser.readfp(config)
-        self.assertEqual(self.parser.values(), {'__main__': {'foo': u'€'}})
+        self.assertEqual(self.parser.values(), {'__main__': {'foo': '€'}})
 
     def test_set(self):
         with tempfile.NamedTemporaryFile() as f:
-            f.write('[__main__]\nfoo=1')
+            f.write(b'[__main__]\nfoo=1')
             f.flush()
 
             self.parser.read(f.name)
@@ -897,16 +969,18 @@ class TestSchemaConfigParser(unittest.Te
                 pass
 
         parser = SchemaConfigParser(MySchema())
-        expected = u"[{0}]\nbaz = 2\n\n[__main__]\nfoo = bar".format(
+        expected = "[{0}]\nbaz = 2\n\n[__main__]\nfoo = bar".format(
             DEFAULTSECT)
-        config = StringIO(expected)
+        config = BytesIO(expected.encode(CONFIG_FILE_ENCODING))
         parser.readfp(config)
 
         # create config file
         fp, filename = tempfile.mkstemp()
         try:
-            parser.write(open(filename, 'w'))
-            result = open(filename, 'r').read().strip()
+            parser.write(codecs.open(filename, 'w',
+                                     encoding=CONFIG_FILE_ENCODING))
+            result = codecs.open(filename, 'r',
+                                 encoding=CONFIG_FILE_ENCODING).read().strip()
             self.assertEqual(result, expected)
         finally:
             # remove the file
@@ -918,48 +992,53 @@ class TestSchemaConfigParser(unittest.Te
             foo = IntOption()
 
         parser = SchemaConfigParser(MySchema())
-        expected = u"[__main__]\nfoo = 0"
+        expected = "[__main__]\nfoo = 0"
 
         # create config file
         fp, filename = tempfile.mkstemp()
         try:
-            parser.write(open(filename, 'w'))
-            result = open(filename, 'r').read().strip()
+            parser.write(codecs.open(filename, 'w',
+                                     encoding=CONFIG_FILE_ENCODING))
+            result = codecs.open(filename, 'r',
+                                 encoding=CONFIG_FILE_ENCODING).read().strip()
             self.assertEqual(result, expected)
         finally:
             # remove the file
             os.unlink(filename)
 
     def test_save_config(self):
-        expected = u'[__main__]\nfoo = 42'
+        expected = '[__main__]\nfoo = 42'
         self._check_save_file(expected)
 
     def test_save_config_non_ascii(self):
-        expected = u'[__main__]\nfoo = fóobâr'
+        expected = '[__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))
+        config = BytesIO(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(codecs.open(filename, 'w',
+                                         encoding=CONFIG_FILE_ENCODING))
+            result = codecs.open(filename, 'r',
+                                 encoding=CONFIG_FILE_ENCODING).read().strip()
+            self.assertEqual(result, expected)
 
             self.parser.save(filename)
-            result = open(filename, 'r').read().strip()
-            self.assertEqual(result.decode(CONFIG_FILE_ENCODING), expected)
+            result = codecs.open(filename, 'r',
+                                 encoding=CONFIG_FILE_ENCODING).read().strip()
+            self.assertEqual(result, 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 ='
+        expected = '[__main__]\nfoo ='
         self._check_save_file(expected, read_config=False)
 
     def test_save_no_config_same_files(self):
@@ -975,11 +1054,13 @@ class TestSchemaConfigParser(unittest.Te
         def setup_config():
             folder = tempfile.mkdtemp()
 
-            f = open("%s/first.cfg" % folder, 'w')
+            f = codecs.open("%s/first.cfg" % folder, 'w',
+                            encoding=CONFIG_FILE_ENCODING)
             f.write("[__main__]\nfoo=1")
             f.close()
 
-            f = open("%s/second.cfg" % folder, 'w')
+            f = codecs.open("%s/second.cfg" % folder, 'w',
+                            encoding=CONFIG_FILE_ENCODING)
             f.write("[__main__]\nbar=2")
             f.close()
 
@@ -1001,12 +1082,14 @@ class TestSchemaConfigParser(unittest.Te
         self.parser.save()
 
         # test the changes were correctly saved
-        data = open("%s/first.cfg" % folder).read()
+        data = codecs.open("%s/first.cfg" % folder,
+                           encoding=CONFIG_FILE_ENCODING).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()
+        data = codecs.open("%s/second.cfg" % folder,
+                           encoding=CONFIG_FILE_ENCODING).read()
         self.assertFalse('foo = 42' in data)
         self.assertTrue('bar = 42' in data)
         # new value goes into last read config file
@@ -1022,15 +1105,18 @@ class TestSchemaConfigParser(unittest.Te
         def setup_config():
             folder = tempfile.mkdtemp()
 
-            f = open("%s/first.cfg" % folder, 'w')
+            f = codecs.open("%s/first.cfg" % folder, 'w',
+                            encoding=CONFIG_FILE_ENCODING)
             f.write("[__main__]\nfoo=1")
             f.close()
 
-            f = open("%s/second.cfg" % folder, 'w')
+            f = codecs.open("%s/second.cfg" % folder, 'w',
+                            encoding=CONFIG_FILE_ENCODING)
             f.write("[__main__]\nbar=2\nincludes = third.cfg")
             f.close()
 
-            f = open("%s/third.cfg" % folder, 'w')
+            f = codecs.open("%s/third.cfg" % folder, 'w',
+                            encoding=CONFIG_FILE_ENCODING)
             f.write("[__main__]\nfoo=3")
             f.close()
 
@@ -1052,11 +1138,14 @@ class TestSchemaConfigParser(unittest.Te
         self.parser.save()
 
         # test the changes were correctly saved
-        data = open("%s/first.cfg" % folder).read()
+        data = codecs.open("%s/first.cfg" % folder,
+                           encoding=CONFIG_FILE_ENCODING).read()
         self.assertEqual(data.strip(), '[__main__]\nfoo=1')
-        data = open("%s/third.cfg" % folder).read()
+        data = codecs.open("%s/third.cfg" % folder,
+                           encoding=CONFIG_FILE_ENCODING).read()
         self.assertEqual(data.strip(), '[__main__]\nfoo = 42')
-        data = open("%s/second.cfg" % folder).read()
+        data = codecs.open("%s/second.cfg" % folder,
+                           encoding=CONFIG_FILE_ENCODING).read()
         self.assertTrue('bar = 42' in data)
         # new value goes into last read config file
         # not in the last included config file
@@ -1075,7 +1164,7 @@ class TestParserIsValid(unittest.TestCas
             foo = StringOption()
         self.schema = MySchema()
         self.parser = SchemaConfigParser(self.schema)
-        self.config = StringIO("[__main__]\nfoo = bar")
+        self.config = BytesIO(b"[__main__]\nfoo = bar")
 
     def test_basic_is_valid(self):
         """Test basic validation without error reporting."""
@@ -1083,7 +1172,7 @@ class TestParserIsValid(unittest.TestCas
             foo = IntOption()
 
         schema = MySchema()
-        config = StringIO("[__main__]\nfoo = 5")
+        config = BytesIO(b"[__main__]\nfoo = 5")
         parser = SchemaConfigParser(schema)
         parser.readfp(config)
 
@@ -1094,7 +1183,7 @@ class TestParserIsValid(unittest.TestCas
         class MySchema(Schema):
             foo = IntOption()
 
-        config = StringIO("[__main__]\nfoo=5")
+        config = BytesIO(b"[__main__]\nfoo=5")
         expected = (True, [])
         parser = SchemaConfigParser(MySchema())
         parser.readfp(config)
@@ -1107,7 +1196,7 @@ class TestParserIsValid(unittest.TestCas
             foo = IntOption()
 
         schema = MySchema()
-        config = StringIO("[__main__]\nfoo = 5\nbar = 6")
+        config = BytesIO(b"[__main__]\nfoo = 5\nbar = 6")
         parser = SchemaConfigParser(schema)
         parser.readfp(config)
 
@@ -1118,7 +1207,7 @@ class TestParserIsValid(unittest.TestCas
         class MySchema(Schema):
             foo = IntOption()
 
-        config = StringIO("[__main__]\nfoo=5\nbar=6")
+        config = BytesIO(b"[__main__]\nfoo=5\nbar=6")
         errors = ["Configuration includes invalid options for "
                   "section '__main__': bar"]
         expected = (False, errors)
@@ -1137,7 +1226,7 @@ class TestParserIsValid(unittest.TestCas
             assert False
 
         schema = MySchema()
-        config = StringIO("[__main__]\nfoo = 5")
+        config = BytesIO(b"[__main__]\nfoo = 5")
         parser = SchemaConfigParser(schema)
         parser.parse_all = mock_parse_all
         parser.readfp(config)
@@ -1145,22 +1234,22 @@ class TestParserIsValid(unittest.TestCas
         self.assertFalse(parser.is_valid())
 
     def test_parse_invalid_section(self):
-        config = StringIO("[bar]\nbaz=foo")
+        config = BytesIO(b"[bar]\nbaz=foo")
         self.parser.readfp(config)
 
         self.assertFalse(self.parser.is_valid())
 
     def test_parse_invalid_section_with_report(self):
-        config = StringIO("[bar]\nbaz=foo")
+        config = BytesIO(b"[bar]\nbaz=foo")
         self.parser.readfp(config)
 
         valid, errors = self.parser.is_valid(report=True)
         self.assertFalse(valid)
         self.assertEqual(errors[0],
-            u'Sections in configuration are missing from schema: bar')
+            'Sections in configuration are missing from schema: bar')
 
     def test_different_sections(self):
-        config = StringIO("[__main__]\nfoo=1\n[bar]\nbaz=2")
+        config = BytesIO(b"[__main__]\nfoo=1\n[bar]\nbaz=2")
         self.parser.readfp(config)
 
         self.assertFalse(self.parser.is_valid())
@@ -1171,7 +1260,7 @@ class TestParserIsValid(unittest.TestCas
             foo = IntOption()
             bar = IntOption(fatal=True)
 
-        config = StringIO("[__main__]\nfoo=1")
+        config = BytesIO(b"[__main__]\nfoo=1")
         parser = SchemaConfigParser(MySchema())
         parser.readfp(config)
 
@@ -1183,7 +1272,7 @@ class TestParserIsValid(unittest.TestCas
             foo = IntOption()
             bar = IntOption(fatal=True)
 
-        config = StringIO("[__main__]\nbar=2")
+        config = BytesIO(b"[__main__]\nbar=2")
         parser = SchemaConfigParser(MySchema())
         parser.readfp(config)
 
@@ -1194,7 +1283,7 @@ class TestParserIsValid(unittest.TestCas
         class MySchema(Schema):
             foo = DictOption(spec={'bar': IntOption()})
 
-        config = StringIO("[__main__]\nfoo=mydict\n[mydict]\nbar=1")
+        config = BytesIO(b"[__main__]\nfoo=mydict\n[mydict]\nbar=1")
         parser = SchemaConfigParser(MySchema())
         parser.readfp(config)
         parser.parse_all()
@@ -1206,7 +1295,7 @@ class TestParserIsValid(unittest.TestCas
         class MySchema(Schema):
             foo = DictOption(item=DictOption())
 
-        config = StringIO("""
+        config = BytesIO(b"""
 [__main__]
 foo=dict1
 [dict1]
@@ -1227,7 +1316,7 @@ baz=42
         class MySchema(Schema):
             foo = DictOption(spec={'bar': DictOption()}, strict=True)
 
-        config = StringIO("""
+        config = BytesIO(b"""
 [__main__]
 foo=dict1
 [dict1]
@@ -1249,7 +1338,7 @@ baz=42
             foo = ListOption(
                 item=DictOption(item=DictOption()))
 
-        config = StringIO("""
+        config = BytesIO(b"""
 [__main__]
 foo = dict1
       dict2
@@ -1278,7 +1367,7 @@ whaz = 2
             foo = DictOption(
                 item=ListOption(item=DictOption()))
 
-        config = StringIO("""
+        config = BytesIO(b"""
 [__main__]
 foo = dict1
 [dict1]
@@ -1304,7 +1393,7 @@ wham = 2
                 item=DictOption(
                     item=ListOption(item=DictOption())))
 
-        config = StringIO("""
+        config = BytesIO(b"""
 [__main__]
 foo = dict1
       dict2
@@ -1338,10 +1427,10 @@ swoosh = 4
         class MySchema(Schema):
             foo = DictOption()
 
-        config = StringIO(textwrap.dedent("""
+        config = BytesIO(textwrap.dedent("""
             [__main__]
             foo = dict1
-            """))
+            """).encode(CONFIG_FILE_ENCODING))
         parser = SchemaConfigParser(MySchema())
         parser.readfp(config)
         parser.parse_all()
@@ -1356,8 +1445,8 @@ swoosh = 4
             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')
+        config = BytesIO(b'[__main__]\nfoo=d1\n    d2\n    d3\n'
+                         b'[d1]\nbar=1\n[d2]\nbar=2\n[d3]\nbar=3')
         parser = SchemaConfigParser(MySchema())
         parser.readfp(config)
         parser.parse_all()
@@ -1365,8 +1454,8 @@ swoosh = 4
         self.assertTrue(parser.is_valid())
 
     def test_noschema_section(self):
-        config = StringIO(
-            "[__main__]\nfoo=%(bar)s\n[__noschema__]\nbar=hello")
+        config = BytesIO(
+            b"[__main__]\nfoo=%(bar)s\n[__noschema__]\nbar=hello")
         parser = SchemaConfigParser(self.schema)
         parser.readfp(config)
         parser.parse_all()
@@ -1381,10 +1470,10 @@ swoosh = 4
             class bar(Section):
                 baz = BoolOption()
 
-        config = StringIO(textwrap.dedent("""
+        config = BytesIO(textwrap.dedent("""
             [foo]
             bar = 3
-            """))
+            """).encode(CONFIG_FILE_ENCODING))
         parser = SchemaConfigParser(MySchema())
         parser.readfp(config)
         parser.parse_all()
diff -pruN 1.0-1/configglue/tests/test_schemaconfig.py 1.1.2-0ubuntu3/configglue/tests/test_schemaconfig.py
--- 1.0-1/configglue/tests/test_schemaconfig.py	2011-08-04 21:55:29.000000000 +0000
+++ 1.1.2-0ubuntu3/configglue/tests/test_schemaconfig.py	2013-07-11 16:40:23.000000000 +0000
@@ -5,7 +5,7 @@
 #
 # A library for simple, DRY configuration of applications
 #
-# (C) 2009--2011 by Canonical Ltd.
+# (C) 2009--2013 by Canonical Ltd.
 # by John R. Lenton <john.lenton@canonical.com>
 # and Ricardo Kirkner <ricardo.kirkner@canonical.com>
 #
@@ -14,11 +14,12 @@
 # For bug reports, support, and new releases: http://launchpad.net/configglue
 #
 ###############################################################################
+from __future__ import unicode_literals
 
 import unittest
 import os
 import sys
-from StringIO import StringIO
+from io import BytesIO, StringIO
 from optparse import (
     OptionConflictError,
     OptionParser,
@@ -29,14 +30,13 @@ from mock import (
     patch,
 )
 
+from configglue._compat import PY2
+from configglue._compat import NoSectionError
 from configglue.glue import (
     configglue,
     schemaconfigglue,
 )
-from configglue.parser import (
-    NoSectionError,
-    SchemaConfigParser,
-)
+from configglue.parser import SchemaConfigParser
 from configglue.schema import (
     DictOption,
     IntOption,
@@ -146,7 +146,7 @@ class TestSchemaConfigGlue(unittest.Test
 
     def test_glue_no_op(self):
         """Test schemaconfigglue with the default OptionParser value."""
-        config = StringIO("[__main__]\nbaz=1")
+        config = BytesIO(b"[__main__]\nbaz=1")
         self.parser.readfp(config)
         self.assertEqual(self.parser.values(),
             {'foo': {'bar': 0}, '__main__': {'baz': 1}})
@@ -157,7 +157,7 @@ class TestSchemaConfigGlue(unittest.Test
 
     def test_glue_no_argv(self):
         """Test schemaconfigglue with the default argv value."""
-        config = StringIO("[__main__]\nbaz=1")
+        config = BytesIO(b"[__main__]\nbaz=1")
         self.parser.readfp(config)
         self.assertEqual(self.parser.values(),
             {'foo': {'bar': 0}, '__main__': {'baz': 1}})
@@ -172,7 +172,7 @@ class TestSchemaConfigGlue(unittest.Test
 
     def test_glue_section_option(self):
         """Test schemaconfigglue overriding one option."""
-        config = StringIO("[foo]\nbar=1")
+        config = BytesIO(b"[foo]\nbar=1")
         self.parser.readfp(config)
         self.assertEqual(self.parser.values(),
             {'foo': {'bar': 1}, '__main__': {'baz': 0}})
@@ -187,7 +187,7 @@ class TestSchemaConfigGlue(unittest.Test
         class MySchema(Schema):
             foo = DictOption()
 
-        config = StringIO("[__main__]\nfoo = bar")
+        config = BytesIO(b"[__main__]\nfoo = bar")
         parser = SchemaConfigParser(MySchema())
         parser.readfp(config)
 
@@ -216,7 +216,7 @@ class TestSchemaConfigGlue(unittest.Test
     @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")
+        config = BytesIO(b"[foo]\nbar=1")
         self.parser.readfp(config)
 
         _argv, sys.argv = sys.argv, ['prognam']
@@ -230,7 +230,7 @@ class TestSchemaConfigGlue(unittest.Test
     @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")
+        config = BytesIO(b"[foo]\nbar=1")
         self.parser.readfp(config)
 
         _argv, sys.argv = sys.argv, ['prognam']
@@ -245,7 +245,7 @@ class TestSchemaConfigGlue(unittest.Test
         with patch.object(os, 'environ',
             {'CONFIGGLUE_FOO_BAR': '42', 'BAR': '1'}):
 
-            config = StringIO("[foo]\nbar=$BAR")
+            config = BytesIO(b"[foo]\nbar=$BAR")
             self.parser.readfp(config)
 
             _argv, sys.argv = sys.argv, ['prognam']
@@ -306,7 +306,7 @@ class TestSchemaConfigGlue(unittest.Test
             class bar(Section):
                 baz = IntOption()
 
-        config = StringIO("[foo]\nbaz=1")
+        config = BytesIO(b"[foo]\nbaz=1")
         parser = SchemaConfigParser(MySchema())
         parser.readfp(config)
         self.assertEqual(parser.values('foo'), {'baz': 1})
@@ -319,24 +319,22 @@ class TestSchemaConfigGlue(unittest.Test
 
     def test_help(self):
         """Test schemaconfigglue with --help."""
-        config = StringIO("[foo]\nbar=1")
+        config = BytesIO(b"[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
+        new_callable = StringIO
+        if PY2:
+            new_callable = BytesIO
+        with patch('sys.stdout', new_callable=new_callable) as mock_stdout:
+            # call the method and assert its value
+            self.assertRaises(SystemExit, schemaconfigglue, self.parser,
+                argv=['--help'])
 
         # assert the value of stdout is correct
-        stdout.seek(0)
-        output = stdout.read()
+        output = mock_stdout.getvalue()
         self.assertTrue(output.startswith('Usage:'))
 
     def test_help_with_fatal(self):
@@ -347,18 +345,16 @@ class TestSchemaConfigGlue(unittest.Test
         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
+        new_callable = StringIO
+        if PY2:
+            new_callable = BytesIO
+        with patch('sys.stdout', new_callable=new_callable) as mock_stdout:
+            # call the method and assert its value
+            self.assertRaises(SystemExit, schemaconfigglue, self.parser,
+                argv=['--help'])
 
         # assert the value of stdout is correct
-        stdout.seek(0)
-        output = stdout.read()
+        output = mock_stdout.getvalue()
         self.assertTrue(output.startswith('Usage:'))
 
     def test_parser_set_with_encoding(self):
@@ -410,7 +406,7 @@ class TestSchemaConfigGlue(unittest.Test
         class MySchema(Schema):
             foo = IntOption(fatal=True)
 
-        config = StringIO("[__main__]\nfoo=1")
+        config = BytesIO(b"[__main__]\nfoo=1")
         parser = SchemaConfigParser(MySchema())
         parser.readfp(config)
 
diff -pruN 1.0-1/configglue/tests/test_schema.py 1.1.2-0ubuntu3/configglue/tests/test_schema.py
--- 1.0-1/configglue/tests/test_schema.py	2011-08-04 21:55:29.000000000 +0000
+++ 1.1.2-0ubuntu3/configglue/tests/test_schema.py	2013-07-11 16:33:40.000000000 +0000
@@ -5,7 +5,7 @@
 #
 # A library for simple, DRY configuration of applications
 #
-# (C) 2009--2011 by Canonical Ltd.
+# (C) 2009--2013 by Canonical Ltd.
 # by John R. Lenton <john.lenton@canonical.com>
 # and Ricardo Kirkner <ricardo.kirkner@canonical.com>
 #
@@ -14,15 +14,14 @@
 # For bug reports, support, and new releases: http://launchpad.net/configglue
 #
 ###############################################################################
+from __future__ import unicode_literals
 
 import textwrap
 import unittest
-from ConfigParser import (
-    NoOptionError,
-    NoSectionError,
-)
-from StringIO import StringIO
+from io import BytesIO
 
+from configglue._compat import text_type
+from configglue._compat import NoOptionError, NoSectionError
 from configglue.parser import (
     SchemaConfigParser,
     SchemaValidationError,
@@ -389,19 +388,19 @@ class TestIntOption(unittest.TestCase):
         class MySchema(Schema):
             foo = self.cls()
 
-        config = StringIO("[__main__]\nfoo = 42")
+        config = BytesIO(b"[__main__]\nfoo = 42")
         expected_values = {'__main__': {'foo': 42}}
         schema = MySchema()
         parser = SchemaConfigParser(schema)
         parser.readfp(config)
         self.assertEqual(parser.values(), expected_values)
 
-        config = StringIO("[__main__]\nfoo =")
+        config = BytesIO(b"[__main__]\nfoo =")
         parser = SchemaConfigParser(schema)
         parser.readfp(config)
         self.assertRaises(ValueError, parser.values)
 
-        config = StringIO("[__main__]\nfoo = bla")
+        config = BytesIO(b"[__main__]\nfoo = bla")
         parser = SchemaConfigParser(schema)
         parser.readfp(config)
         self.assertRaises(ValueError, parser.values)
@@ -435,24 +434,24 @@ class TestBoolOption(unittest.TestCase):
         class MySchema(Schema):
             foo = self.cls()
 
-        config = StringIO("[__main__]\nfoo = Yes")
+        config = BytesIO(b"[__main__]\nfoo = Yes")
         expected_values = {'__main__': {'foo': True}}
         schema = MySchema()
         parser = SchemaConfigParser(schema)
         parser.readfp(config)
         self.assertEqual(parser.values(), expected_values)
 
-        config = StringIO("[__main__]\nfoo = tRuE")
+        config = BytesIO(b"[__main__]\nfoo = tRuE")
         parser = SchemaConfigParser(schema)
         parser.readfp(config)
         self.assertEqual(parser.values(), expected_values)
 
-        config = StringIO("[__main__]\nfoo =")
+        config = BytesIO(b"[__main__]\nfoo =")
         parser = SchemaConfigParser(schema)
         parser.readfp(config)
         self.assertRaises(ValueError, parser.values)
 
-        config = StringIO("[__main__]\nfoo = bla")
+        config = BytesIO(b"[__main__]\nfoo = bla")
         parser = SchemaConfigParser(schema)
         parser.readfp(config)
         self.assertRaises(ValueError, parser.values)
@@ -486,7 +485,7 @@ class TestListOption(unittest.TestCase):
         class MySchema(Schema):
             foo = self.cls(item=IntOption())
 
-        config = StringIO("[__main__]\nfoo = 42\n 43\n 44")
+        config = BytesIO(b"[__main__]\nfoo = 42\n 43\n 44")
         expected_values = {'__main__': {'foo': [42, 43, 44]}}
         schema = MySchema()
         parser = SchemaConfigParser(schema)
@@ -497,7 +496,7 @@ class TestListOption(unittest.TestCase):
         class MySchema(Schema):
             foo = self.cls(item=IntOption(), parse_json=False)
 
-        config = StringIO("[__main__]\nfoo = 42\n 43\n 44")
+        config = BytesIO(b"[__main__]\nfoo = 42\n 43\n 44")
         expected_values = {'__main__': {'foo': [42, 43, 44]}}
         schema = MySchema()
         parser = SchemaConfigParser(schema)
@@ -508,7 +507,7 @@ class TestListOption(unittest.TestCase):
         class MySchema(Schema):
             foo = self.cls(item=IntOption(), parse_json=False)
 
-        config = StringIO("[__main__]\nfoo = [42, 43, 44]")
+        config = BytesIO(b"[__main__]\nfoo = [42, 43, 44]")
         schema = MySchema()
         parser = SchemaConfigParser(schema)
         parser.readfp(config)
@@ -518,7 +517,7 @@ class TestListOption(unittest.TestCase):
         class MySchema(Schema):
             foo = self.cls(item=IntOption())
 
-        config = StringIO("[__main__]\nfoo = [42, 43, 44]")
+        config = BytesIO(b"[__main__]\nfoo = [42, 43, 44]")
         expected_values = {'__main__': {'foo': [42, 43, 44]}}
         schema = MySchema()
         parser = SchemaConfigParser(schema)
@@ -529,7 +528,7 @@ class TestListOption(unittest.TestCase):
         class MySchema(Schema):
             foo = self.cls(item=IntOption())
 
-        config = StringIO('[__main__]\nfoo = 1, 2, 3')
+        config = BytesIO(b'[__main__]\nfoo = 1, 2, 3')
         schema = MySchema()
         parser = SchemaConfigParser(schema)
         parser.readfp(config)
@@ -539,7 +538,7 @@ class TestListOption(unittest.TestCase):
         class MySchema(Schema):
             foo = self.cls(item=IntOption())
 
-        config = StringIO('[__main__]\nfoo = {"foo": "bar"}')
+        config = BytesIO(b'[__main__]\nfoo = {"foo": "bar"}')
         schema = MySchema()
         parser = SchemaConfigParser(schema)
         parser.readfp(config)
@@ -551,7 +550,7 @@ class TestListOption(unittest.TestCase):
             foo = self.cls(item=BoolOption())
 
         schema = MySchema()
-        config = StringIO("[__main__]\nfoo = tRuE\n No\n 0\n 1")
+        config = BytesIO(b"[__main__]\nfoo = tRuE\n No\n 0\n 1")
         expected_values = {'__main__': {'foo': [True, False, False, True]}}
         parser = SchemaConfigParser(schema)
         parser.readfp(config)
@@ -563,7 +562,7 @@ class TestListOption(unittest.TestCase):
             foo = self.cls(item=BoolOption())
 
         schema = MySchema()
-        config = StringIO("[__main__]\nfoo =")
+        config = BytesIO(b"[__main__]\nfoo =")
         parser = SchemaConfigParser(schema)
         parser.readfp(config)
         expected_values = {'__main__': {'foo': []}}
@@ -575,12 +574,12 @@ class TestListOption(unittest.TestCase):
             foo = self.cls(item=BoolOption())
 
         schema = MySchema()
-        config = StringIO("[__main__]\nfoo = bla")
+        config = BytesIO(b"[__main__]\nfoo = bla")
         parser = SchemaConfigParser(schema)
         parser.readfp(config)
         self.assertRaises(ValueError, parser.values)
 
-        config = StringIO("[__main__]\nfoo = True\n bla")
+        config = BytesIO(b"[__main__]\nfoo = True\n bla")
         parser = SchemaConfigParser(schema)
         parser.readfp(config)
         self.assertRaises(ValueError, parser.values)
@@ -596,7 +595,7 @@ class TestListOption(unittest.TestCase):
             foo = self.cls(item=StringOption(), remove_duplicates=True)
 
         schema = MySchema()
-        config = StringIO("[__main__]\nfoo = bla\n blah\n bla")
+        config = BytesIO(b"[__main__]\nfoo = bla\n blah\n bla")
         parser = SchemaConfigParser(schema)
         parser.readfp(config)
         self.assertEquals({'__main__': {'foo': ['bla', 'blah']}},
@@ -608,7 +607,7 @@ class TestListOption(unittest.TestCase):
             foo = self.cls(item=DictOption(), remove_duplicates=True)
 
         schema = MyOtherSchema()
-        config = StringIO("[__main__]\nfoo = bla\n bla\n[bla]\nbar = baz")
+        config = BytesIO(b"[__main__]\nfoo = bla\n bla\n[bla]\nbar = baz")
         parser = SchemaConfigParser(schema)
         parser.readfp(config)
         self.assertEquals({'__main__': {'foo': [{'bar': 'baz'}]}},
@@ -661,7 +660,8 @@ class TestListOption(unittest.TestCase):
     def test_to_string_when_no_json(self):
         option = ListOption(parse_json=False)
         result = option.to_string(['1', '2', '3'])
-        self.assertEqual(result, "['1', '2', '3']")
+        expected = [text_type(x) for x in [1, 2, 3]]
+        self.assertEqual(result, text_type(expected))
 
 
 class TestTupleOption(unittest.TestCase):
@@ -683,7 +683,7 @@ class TestTupleOption(unittest.TestCase)
         class MySchema(Schema):
             foo = self.cls()
 
-        config = StringIO('[__main__]\nfoo=1,2,3,4')
+        config = BytesIO(b'[__main__]\nfoo=1,2,3,4')
         expected_values = {'__main__': {'foo': ('1', '2', '3', '4')}}
         parser = SchemaConfigParser(MySchema())
         parser.readfp(config)
@@ -694,19 +694,19 @@ class TestTupleOption(unittest.TestCase)
         class MySchema(Schema):
             foo = self.cls(length=4)
 
-        config = StringIO('[__main__]\nfoo = 1, 2, 3, 4')
+        config = BytesIO(b'[__main__]\nfoo = 1, 2, 3, 4')
         expected_values = {'__main__': {'foo': ('1', '2', '3', '4')}}
         schema = MySchema()
         parser = SchemaConfigParser(schema)
         parser.readfp(config)
         self.assertEqual(parser.values(), expected_values)
 
-        config = StringIO('[__main__]\nfoo = 1, 2, 3')
+        config = BytesIO(b'[__main__]\nfoo = 1, 2, 3')
         parser = SchemaConfigParser(schema)
         parser.readfp(config)
         self.assertRaises(ValueError, parser.values)
 
-        config = StringIO('[__main__]\nfoo = ')
+        config = BytesIO(b'[__main__]\nfoo = ')
         parser = SchemaConfigParser(schema)
         parser.readfp(config)
         self.assertRaises(ValueError, parser.values)
@@ -763,7 +763,7 @@ class TestDictOption(unittest.TestCase):
         class MySchema(Schema):
             foo = self.cls(item=self.cls())
 
-        config = StringIO("""
+        config = BytesIO(b"""
 [__main__]
 foo=dict1
 [dict1]
@@ -788,7 +788,7 @@ baz=42
                 'bla': BoolOption(),
             })
 
-        config = StringIO("""[__main__]
+        config = BytesIO(b"""[__main__]
 foo = mydict
 [mydict]
 bar=baz
@@ -813,7 +813,7 @@ bla=Yes
                 'bla': BoolOption(),
             }, parse_json=False)
 
-        config = StringIO("""[__main__]
+        config = BytesIO(b"""[__main__]
 foo = mydict
 [mydict]
 bar=baz
@@ -838,13 +838,13 @@ bla=Yes
                 'bla': BoolOption(),
             })
 
-        config = StringIO(textwrap.dedent("""
+        config = BytesIO(textwrap.dedent("""
             [__main__]
             foo = {
                 "bar": "baz",
                 "baz": "42",
                 "bla": "Yes"}
-            """))
+            """).encode('utf-8'))
         expected_values = {
             '__main__': {
                 'foo': {'bar': 'baz', 'baz': 42, 'bla': True}}}
@@ -863,10 +863,10 @@ bla=Yes
                 'bla': BoolOption(),
             })
 
-        config = StringIO(textwrap.dedent("""
+        config = BytesIO(textwrap.dedent("""
             [__main__]
             foo = {'bar': 23}
-            """))
+            """).encode('utf-8'))
 
         schema = MySchema()
         parser = SchemaConfigParser(schema)
@@ -882,10 +882,10 @@ bla=Yes
                 'bla': BoolOption(),
             })
 
-        config = StringIO(textwrap.dedent("""
+        config = BytesIO(textwrap.dedent("""
             [__main__]
             foo = [1, 2, 3]
-            """))
+            """).encode('utf-8'))
 
         schema = MySchema()
         parser = SchemaConfigParser(schema)
@@ -901,13 +901,13 @@ bla=Yes
                 'bla': BoolOption(),
             }, parse_json=False)
 
-        config = StringIO(textwrap.dedent("""
+        config = BytesIO(textwrap.dedent("""
             [__main__]
             foo = {
                 "bar": "baz",
                 "baz": "42",
                 "bla": "Yes"}
-            """))
+            """).encode('utf-8'))
 
         schema = MySchema()
         parser = SchemaConfigParser(schema)
@@ -923,7 +923,7 @@ bla=Yes
                 'bla': BoolOption(),
             })
 
-        config = StringIO("""[__main__]
+        config = BytesIO(b"""[__main__]
 foo = mydict
 [mydict]
 baz=42
@@ -936,12 +936,54 @@ baz=42
         parsed = schema.foo.parse('mydict', parser, True)
         self.assertEqual(parsed, expected)
 
+    def test_parse_json_raw_with_interpolation_marks(self):
+        """Test DictOption parse json using raw=True when data has interpolation marks."""
+        class MySchema(Schema):
+            class logging(Section):
+                formatters = self.cls(raw=True, item=self.cls())
+
+        config = BytesIO(textwrap.dedent("""
+            [logging]
+            formatters = {"sample": {"format": "%(name)s"}}
+            """).encode('utf-8'))
+        expected = {'sample': {'format': '%(name)s'}}
+
+        schema = MySchema()
+        parser = SchemaConfigParser(schema)
+        parser.readfp(config)
+        parsed = parser.values('logging')['formatters']
+        self.assertEqual(parsed, expected)
+
+    def test_parse_no_json_raw_with_interpolation_marks(self):
+        """Test DictOption parse non-json using raw=True when data has interpolation marks."""
+        class MySchema(Schema):
+            class logging(Section):
+                formatters = self.cls(raw=True, item=self.cls())
+
+        config = BytesIO(textwrap.dedent("""
+            [logging]
+            formatters = logging_formatters
+
+            [logging_formatters]
+            sample = sample_formatter
+
+            [sample_formatter]
+            format = %%(name)s
+            """).encode('utf-8'))
+        expected = {'sample': {'format': '%(name)s'}}
+
+        schema = MySchema()
+        parser = SchemaConfigParser(schema)
+        parser.readfp(config)
+        parsed = parser.values('logging')['formatters']
+        self.assertEqual(parsed, expected)
+
     def test_parse_invalid_key_in_parsed(self):
         """Test DictOption parse with an invalid key in the config."""
         class MySchema(Schema):
             foo = self.cls(spec={'bar': IntOption()})
 
-        config = StringIO("[__main__]\nfoo=mydict\n[mydict]\nbaz=2")
+        config = BytesIO(b"[__main__]\nfoo=mydict\n[mydict]\nbaz=2")
         expected_values = {'__main__': {'foo': {'bar': 0, 'baz': '2'}}}
         parser = SchemaConfigParser(MySchema())
         parser.readfp(config)
@@ -954,7 +996,7 @@ baz=42
                 'bar': IntOption(),
                 'baz': IntOption(fatal=True)})
 
-        config = StringIO("[__main__]\nfoo=mydict\n[mydict]\nbar=2")
+        config = BytesIO(b"[__main__]\nfoo=mydict\n[mydict]\nbar=2")
         parser = SchemaConfigParser(MySchema())
         parser.readfp(config)
         self.assertRaises(ValueError, parser.parse_all)
@@ -968,7 +1010,7 @@ baz=42
         class MySchema(Schema):
             foo = self.cls(spec={'bar': IntOption()})
 
-        config = StringIO("[__main__]\nfoo=mydict\n[mydict]")
+        config = BytesIO(b"[__main__]\nfoo=mydict\n[mydict]")
         expected_values = {'__main__': {'foo': {'bar': 0}}}
         parser = SchemaConfigParser(MySchema())
         parser.readfp(config)
@@ -978,7 +1020,7 @@ baz=42
         class MySchema(Schema):
             foo = self.cls()
 
-        config = StringIO("[__main__]\nfoo=mydict\n[mydict]\nbar=2")
+        config = BytesIO(b"[__main__]\nfoo=mydict\n[mydict]\nbar=2")
         expected_values = {'__main__': {'foo': {'bar': '2'}}}
         parser = SchemaConfigParser(MySchema())
         parser.readfp(config)
@@ -990,7 +1032,7 @@ baz=42
             foo = self.cls(
                       item=self.cls(
                           item=IntOption()))
-        config = StringIO("""
+        config = BytesIO(b"""
 [__main__]
 foo = mydict
 [mydict]
@@ -1009,7 +1051,7 @@ wham=42
             spec = {'bar': IntOption()}
             foo = self.cls(spec=spec, strict=True)
 
-        config = StringIO("[__main__]\nfoo=mydict\n[mydict]\nbar=2")
+        config = BytesIO(b"[__main__]\nfoo=mydict\n[mydict]\nbar=2")
         expected_values = {'__main__': {'foo': {'bar': 2}}}
         parser = SchemaConfigParser(MySchema())
         parser.readfp(config)
@@ -1022,7 +1064,7 @@ wham=42
                     'baz': IntOption()}
             foo = self.cls(spec=spec, strict=True)
 
-        config = StringIO("[__main__]\nfoo=mydict\n[mydict]\nbar=2")
+        config = BytesIO(b"[__main__]\nfoo=mydict\n[mydict]\nbar=2")
         expected_values = {'__main__': {'foo': {'bar': 2, 'baz': 0}}}
         parser = SchemaConfigParser(MySchema())
         parser.readfp(config)
@@ -1034,7 +1076,7 @@ wham=42
             spec = {'bar': IntOption()}
             foo = self.cls(spec=spec, strict=True)
 
-        config = StringIO("[__main__]\nfoo=mydict\n[mydict]\nbar=2\nbaz=3")
+        config = BytesIO(b"[__main__]\nfoo=mydict\n[mydict]\nbar=2\nbaz=3")
         parser = SchemaConfigParser(MySchema())
         parser.readfp(config)
         self.assertRaises(ValueError, parser.parse_all)
@@ -1074,7 +1116,7 @@ wham=42
     def test_to_string_when_no_json(self):
         option = DictOption(parse_json=False)
         result = option.to_string({'foo': '1'})
-        self.assertEqual(result, str({'foo': '1'}))
+        self.assertEqual(result, text_type({'foo': '1'}))
 
 
 class TestListOfDictOption(unittest.TestCase):
@@ -1088,7 +1130,7 @@ class TestListOfDictOption(unittest.Test
                     'bla': BoolOption(),
                 }))
 
-        config = StringIO("""[__main__]
+        config = BytesIO(b"""[__main__]
 foo = mylist0
       mylist1
 [mylist0]
@@ -1125,7 +1167,7 @@ class TestDictWithDicts(unittest.TestCas
         class MySchema(Schema):
             foo = DictOption(spec=spec)
 
-        config = StringIO("""[__main__]
+        config = BytesIO(b"""[__main__]
 foo = outerdict
 [outerdict]
 options = innerdict
@@ -1152,19 +1194,19 @@ class TestListOfTuples(unittest.TestCase
         self.parser = SchemaConfigParser(schema)
 
     def test_parse_list_of_tuples(self):
-        config = StringIO('[__main__]\nfoo = a, b, c\n      d, e, f')
+        config = BytesIO(b'[__main__]\nfoo = a, b, c\n      d, e, f')
         expected_values = {
             '__main__': {'foo': [('a', 'b', 'c'), ('d', 'e', 'f')]}}
         self.parser.readfp(config)
         self.assertEqual(self.parser.values(), expected_values)
 
     def test_parse_wrong_tuple_size(self):
-        config = StringIO('[__main__]\nfoo = a, b, c\n      d, e')
+        config = BytesIO(b'[__main__]\nfoo = a, b, c\n      d, e')
         self.parser.readfp(config)
         self.assertRaises(ValueError, self.parser.values)
 
     def test_parse_empty_tuple(self):
-        config = StringIO('[__main__]\nfoo=()')
+        config = BytesIO(b'[__main__]\nfoo=()')
         expected_values = {'__main__': {'foo': [()]}}
         self.parser.readfp(config)
         self.assertEqual(self.parser.values(), expected_values)
@@ -1289,6 +1331,6 @@ class MultiSchemaTestCase(unittest.TestC
         try:
             merge(SchemaA, SchemaB)
             self.fail('SchemaValidationError not raised.')
-        except SchemaValidationError, e:
-            self.assertEqual(str(e),
+        except SchemaValidationError as e:
+            self.assertEqual(text_type(e),
                 "Conflicting option '__main__.foo' while merging schemas.")
diff -pruN 1.0-1/configglue.egg-info/PKG-INFO 1.1.2-0ubuntu3/configglue.egg-info/PKG-INFO
--- 1.0-1/configglue.egg-info/PKG-INFO	2011-08-04 22:35:57.000000000 +0000
+++ 1.1.2-0ubuntu3/configglue.egg-info/PKG-INFO	2013-07-11 19:41:36.000000000 +0000
@@ -1,6 +1,6 @@
-Metadata-Version: 1.0
+Metadata-Version: 1.1
 Name: configglue
-Version: 1.0
+Version: 1.1.2
 Summary: Glue to stick OptionParser and ConfigParser together
 Home-page: https://launchpad.net/configglue
 Author: John R. Lenton, Ricardo Kirkner
@@ -16,3 +16,9 @@ Platform: UNKNOWN
 Classifier: License :: OSI Approved :: BSD License
 Classifier: Topic :: Software Development :: Libraries :: Python Modules
 Classifier: Programming Language :: Python
+Classifier: Programming Language :: Python :: 2
+Classifier: Programming Language :: Python :: 2.6
+Classifier: Programming Language :: Python :: 2.7
+Classifier: Programming Language :: Python :: 3
+Classifier: Programming Language :: Python :: 3.2
+Classifier: Programming Language :: Python :: 3.3
diff -pruN 1.0-1/configglue.egg-info/SOURCES.txt 1.1.2-0ubuntu3/configglue.egg-info/SOURCES.txt
--- 1.0-1/configglue.egg-info/SOURCES.txt	2011-08-04 22:35:57.000000000 +0000
+++ 1.1.2-0ubuntu3/configglue.egg-info/SOURCES.txt	2013-07-11 19:41:37.000000000 +0000
@@ -4,6 +4,7 @@ README
 setup.cfg
 setup.py
 configglue/__init__.py
+configglue/_compat.py
 configglue/glue.py
 configglue/parser.py
 configglue/schema.py
@@ -16,12 +17,23 @@ configglue.egg-info/zip-safe
 configglue/app/__init__.py
 configglue/app/base.py
 configglue/app/plugin.py
+configglue/contrib/__init__.py
+configglue/contrib/schema/__init__.py
+configglue/contrib/schema/devserver.py
+configglue/contrib/schema/django_jenkins.py
+configglue/contrib/schema/django_openid_auth.py
+configglue/contrib/schema/nexus.py
+configglue/contrib/schema/preflight.py
+configglue/contrib/schema/pystatsd.py
+configglue/contrib/schema/raven.py
+configglue/contrib/schema/saml2idp.py
 configglue/inischema/__init__.py
 configglue/inischema/attributed.py
 configglue/inischema/glue.py
 configglue/inischema/parsers.py
 configglue/inischema/typed.py
 configglue/tests/__init__.py
+configglue/tests/test_contrib_schema.py
 configglue/tests/test_parser.py
 configglue/tests/test_schema.py
 configglue/tests/test_schemaconfig.py
diff -pruN 1.0-1/debian/changelog 1.1.2-0ubuntu3/debian/changelog
--- 1.0-1/debian/changelog	2016-04-14 22:36:53.000000000 +0000
+++ 1.1.2-0ubuntu3/debian/changelog	2016-04-14 11:33:06.000000000 +0000
@@ -1,3 +1,64 @@
+python-configglue (1.1.2-0ubuntu3) xenial; urgency=medium
+
+  * Remove python3-configglue binary. It does not work with Python 3.5,
+    neither does the latest upstream version, and it has zero reverse
+    dependencies. (LP: #1504288)
+
+ -- Martin Pitt <martin.pitt@ubuntu.com>  Thu, 14 Apr 2016 13:32:13 +0200
+
+python-configglue (1.1.2-0ubuntu2) trusty; urgency=medium
+
+  * d/control: Build-Depend on python3-all.
+
+ -- Barry Warsaw <barry@ubuntu.com>  Thu, 16 Jan 2014 10:35:02 -0500
+
+python-configglue (1.1.2-0ubuntu1) saucy; urgency=low
+
+  * New upstream release.
+  * Un-revert previous revert.
+    - debian/control: Remove Build-Dep on python-configparser.
+
+ -- Barry Warsaw <barry@ubuntu.com>  Thu, 11 Jul 2013 17:39:10 -0400
+
+python-configglue (1.1.1.is.really.1.0-0ubuntu1) saucy; urgency=low
+
+  * Revert to upstream version 1.0 to address bug #1198480 et al.
+
+ -- Steve Langasek <steve.langasek@ubuntu.com>  Wed, 10 Jul 2013 17:59:50 -0700
+
+python-configglue (1.1.1-0ubuntu1) saucy; urgency=low
+
+  * New upstream release.
+    - debian/patches/disable-lp1196754.patch: Bug was fixed upstream,
+      so this patch is no longer necessary.
+
+ -- Barry Warsaw <barry@ubuntu.com>  Mon, 08 Jul 2013 17:35:44 -0400
+
+python-configglue (1.1.0-0ubuntu1) saucy; urgency=low
+
+  * New upstream release.
+  * Add Python 3 packaging.
+    - debian/compat: bump to 8
+    - debian/control:
+      + Add required Python 2 and Python 3 Build-Depends
+      + Add X-Python3-Version header
+      + Standards-Version bumped to 3.9.4
+      + Added python3-configglue binary package.
+    - debian/patches/disable-lp1196754.patch: Required due to test suite
+      failure when run in Python 3 under non-UTF-8 locales.
+    - debian/pycompat: Obsolete, removed.
+    - debian/python{,3}-configglue.install: Added
+    - debian/rules: Add rules to build and test for Python 3.
+    - debian/source/format: Added.
+
+ -- Barry Warsaw <barry@ubuntu.com>  Mon, 01 Jul 2013 19:52:35 -0400
+
+python-configglue (1.0-1build1) precise; urgency=low
+
+  * Rebuild to drop python2.6 dependencies.
+
+ -- Matthias Klose <doko@ubuntu.com>  Sat, 31 Dec 2011 02:09:20 +0000
+
 python-configglue (1.0-1) unstable; urgency=low
 
   * New upstream release
diff -pruN 1.0-1/debian/compat 1.1.2-0ubuntu3/debian/compat
--- 1.0-1/debian/compat	2016-04-14 22:36:53.000000000 +0000
+++ 1.1.2-0ubuntu3/debian/compat	2013-07-11 21:22:35.000000000 +0000
@@ -1 +1 @@
-5
+8
diff -pruN 1.0-1/debian/control 1.1.2-0ubuntu3/debian/control
--- 1.0-1/debian/control	2016-04-14 22:36:53.000000000 +0000
+++ 1.1.2-0ubuntu3/debian/control	2016-04-14 11:31:48.000000000 +0000
@@ -1,19 +1,23 @@
 Source: python-configglue
 Section: python
 Priority: optional
-Maintainer: Andrew Mitchell <ajmitch@debian.org>
-Build-Depends: 
+Maintainer: Ubuntu Developers <ubuntu-devel-discuss@lists.ubuntu.com>
+XSBC-Original-Maintainer: Andrew Mitchell <ajmitch@debian.org>
+Build-Depends:
  python (>= 2.6.6-3~),
  python-setuptools,
+ python-xdg,
+ python-mock,
  debhelper (>= 7.0.50)
-X-Python-Version: >=2.6
-Standards-Version: 3.9.2
+X-Python-Version: >= 2.6
+Standards-Version: 3.9.4
 
 Package: python-configglue
 Architecture: all
-XB-Python-Version: ${python:Versions}
 Depends: ${misc:Depends}, ${python:Depends}
 Description: Glues together optparse.OptionParser and ConfigParser.ConfigParser
- Configglue is a library that glues together python's optparse.OptionParser 
+ Configglue is a library that glues together python's optparse.OptionParser
  and ConfigParser.ConfigParser, so that the same options can be exported to a
  configuration file and a commandline interface.
+ .
+ This is the Python 2 package.
diff -pruN 1.0-1/debian/pycompat 1.1.2-0ubuntu3/debian/pycompat
--- 1.0-1/debian/pycompat	2016-04-14 22:36:53.000000000 +0000
+++ 1.1.2-0ubuntu3/debian/pycompat	1970-01-01 00:00:00.000000000 +0000
@@ -1 +0,0 @@
-2
diff -pruN 1.0-1/debian/python3-configglue.install 1.1.2-0ubuntu3/debian/python3-configglue.install
--- 1.0-1/debian/python3-configglue.install	1970-01-01 00:00:00.000000000 +0000
+++ 1.1.2-0ubuntu3/debian/python3-configglue.install	2013-07-11 21:22:35.000000000 +0000
@@ -0,0 +1 @@
+usr/lib/python3
diff -pruN 1.0-1/debian/python-configglue.install 1.1.2-0ubuntu3/debian/python-configglue.install
--- 1.0-1/debian/python-configglue.install	1970-01-01 00:00:00.000000000 +0000
+++ 1.1.2-0ubuntu3/debian/python-configglue.install	2013-07-11 21:22:35.000000000 +0000
@@ -0,0 +1 @@
+usr/lib/python2*
diff -pruN 1.0-1/debian/rules 1.1.2-0ubuntu3/debian/rules
--- 1.0-1/debian/rules	2016-04-14 22:36:53.000000000 +0000
+++ 1.1.2-0ubuntu3/debian/rules	2016-04-14 11:32:12.000000000 +0000
@@ -1,4 +1,40 @@
 #!/usr/bin/make -f
 
+PYTHON2=$(shell pyversions -vr)
+
+# Prevent setuptools/distribute from accessing the internet.
+export http_proxy = http://127.0.9.1:9
+
 %:
 	dh $@ --with python2
+
+ifeq (,$(filter nocheck,$(DEB_BUILD_OPTIONS)))
+test-python%:
+	python$* setup.py test
+
+override_dh_auto_test: $(PYTHON2:%=test-python%)
+endif
+
+build-python%:
+	python$* setup.py build
+
+override_dh_auto_build:
+	dh_auto_build
+
+install-python%:
+	python$* setup.py install --root=$(CURDIR)/debian/tmp --install-layout=deb
+
+override_dh_auto_install:
+	dh_auto_install --destdir=debian/tmp
+
+# override_dh_installchangelogs:
+#         dh_installchangelogs -k foo/NEWS.rst
+
+# override_dh_installdocs:
+#         python setup.py build_sphinx
+#         dh_installdocs build/sphinx/html
+
+override_dh_auto_clean:
+	dh_auto_clean
+	rm -rf build
+	rm -rf *.egg-info
diff -pruN 1.0-1/debian/source/format 1.1.2-0ubuntu3/debian/source/format
--- 1.0-1/debian/source/format	1970-01-01 00:00:00.000000000 +0000
+++ 1.1.2-0ubuntu3/debian/source/format	2013-07-11 21:22:35.000000000 +0000
@@ -0,0 +1 @@
+3.0 (quilt)
diff -pruN 1.0-1/LICENSE 1.1.2-0ubuntu3/LICENSE
--- 1.0-1/LICENSE	2010-10-22 12:00:34.000000000 +0000
+++ 1.1.2-0ubuntu3/LICENSE	2013-07-11 19:00:57.000000000 +0000
@@ -1,4 +1,4 @@
-Copyright 2009, 2010 Canonical Ltd. All rights reserved.
+Copyright 2009--2013 Canonical Ltd. All rights reserved.
 
 Redistribution and use in source and binary forms, with or without
 modification, are permitted provided that the following conditions are
diff -pruN 1.0-1/.pc/.quilt_patches 1.1.2-0ubuntu3/.pc/.quilt_patches
--- 1.0-1/.pc/.quilt_patches	1970-01-01 00:00:00.000000000 +0000
+++ 1.1.2-0ubuntu3/.pc/.quilt_patches	2016-04-14 22:36:53.466929564 +0000
@@ -0,0 +1 @@
+debian/patches
diff -pruN 1.0-1/.pc/.quilt_series 1.1.2-0ubuntu3/.pc/.quilt_series
--- 1.0-1/.pc/.quilt_series	1970-01-01 00:00:00.000000000 +0000
+++ 1.1.2-0ubuntu3/.pc/.quilt_series	2016-04-14 22:36:53.470929675 +0000
@@ -0,0 +1 @@
+series
diff -pruN 1.0-1/.pc/.version 1.1.2-0ubuntu3/.pc/.version
--- 1.0-1/.pc/.version	1970-01-01 00:00:00.000000000 +0000
+++ 1.1.2-0ubuntu3/.pc/.version	2016-04-14 22:36:53.466929564 +0000
@@ -0,0 +1 @@
+2
diff -pruN 1.0-1/PKG-INFO 1.1.2-0ubuntu3/PKG-INFO
--- 1.0-1/PKG-INFO	2011-08-04 22:35:57.000000000 +0000
+++ 1.1.2-0ubuntu3/PKG-INFO	2013-07-11 19:41:37.000000000 +0000
@@ -1,6 +1,6 @@
-Metadata-Version: 1.0
+Metadata-Version: 1.1
 Name: configglue
-Version: 1.0
+Version: 1.1.2
 Summary: Glue to stick OptionParser and ConfigParser together
 Home-page: https://launchpad.net/configglue
 Author: John R. Lenton, Ricardo Kirkner
@@ -16,3 +16,9 @@ Platform: UNKNOWN
 Classifier: License :: OSI Approved :: BSD License
 Classifier: Topic :: Software Development :: Libraries :: Python Modules
 Classifier: Programming Language :: Python
+Classifier: Programming Language :: Python :: 2
+Classifier: Programming Language :: Python :: 2.6
+Classifier: Programming Language :: Python :: 2.7
+Classifier: Programming Language :: Python :: 3
+Classifier: Programming Language :: Python :: 3.2
+Classifier: Programming Language :: Python :: 3.3
diff -pruN 1.0-1/setup.py 1.1.2-0ubuntu3/setup.py
--- 1.0-1/setup.py	2011-08-04 21:55:29.000000000 +0000
+++ 1.1.2-0ubuntu3/setup.py	2013-07-09 19:59:05.000000000 +0000
@@ -23,6 +23,9 @@ from setuptools import (
 import configglue
 
 
+install_requires = ['pyxdg']
+
+
 setup(name='configglue',
       version=configglue.__version__,
       description="Glue to stick OptionParser and ConfigParser together",
@@ -36,12 +39,18 @@ interface.
         'License :: OSI Approved :: BSD License',
         'Topic :: Software Development :: Libraries :: Python Modules',
         'Programming Language :: Python',
+        'Programming Language :: Python :: 2',
+        'Programming Language :: Python :: 2.6',
+        'Programming Language :: Python :: 2.7',
+        'Programming Language :: Python :: 3',
+        'Programming Language :: Python :: 3.2',
+        'Programming Language :: Python :: 3.3',
         ],
       author='John R. Lenton, Ricardo Kirkner',
       author_email='john.lenton@canonical.com, ricardo.kirkner@canonical.com',
       url='https://launchpad.net/configglue',
       license='BSD License',
-      install_requires=['pyxdg'],
+      install_requires=install_requires,
       dependency_links=['http://www.freedesktop.org/wiki/Software/pyxdg'],
       packages=find_packages(),
       include_package_data=True,
