add consistent support for the vars and default arguments on all
configuration parser classes
(http://bugs.python.org/issue9421)
diff --git a/Lib/configparser.py b/Lib/configparser.py
index fb39ac3..7f1514f 100644
--- a/Lib/configparser.py
+++ b/Lib/configparser.py
@@ -24,9 +24,9 @@
 
     methods:
 
-    __init__(defaults=None, dict_type=_default_dict,
-             delimiters=('=', ':'), comment_prefixes=('#', ';'),
-             strict=False, empty_lines_in_values=True, allow_no_value=False):
+    __init__(defaults=None, dict_type=_default_dict, allow_no_value=False,
+             delimiters=('=', ':'), comment_prefixes=_COMPATIBLE,
+             strict=False, empty_lines_in_values=True):
         Create the parser. When `defaults' is given, it is initialized into the
         dictionary or intrinsic defaults. The keys must be strings, the values
         must be appropriate for %()s string interpolation. Note that `__name__'
@@ -82,22 +82,24 @@
         Read configuration from a dictionary. Keys are section names,
         values are dictionaries with keys and values that should be present
         in the section. If the used dictionary type preserves order, sections
-        and their keys will be added in order.
+        and their keys will be added in order. Values are automatically
+        converted to strings.
 
-    get(section, option, raw=False, vars=None)
+    get(section, option, raw=False, vars=None, default=_UNSET)
         Return a string value for the named option.  All % interpolations are
         expanded in the return values, based on the defaults passed into the
         constructor and the DEFAULT section.  Additional substitutions may be
         provided using the `vars' argument, which must be a dictionary whose
-        contents override any pre-existing defaults.
+        contents override any pre-existing defaults. If `option' is a key in
+        `vars', the value from `vars' is used.
 
-    getint(section, options)
+    getint(section, options, raw=False, vars=None, default=_UNSET)
         Like get(), but convert value to an integer.
 
-    getfloat(section, options)
+    getfloat(section, options, raw=False, vars=None, default=_UNSET)
         Like get(), but convert value to a float.
 
-    getboolean(section, options)
+    getboolean(section, options, raw=False, vars=None, default=_UNSET)
         Like get(), but convert value to a boolean (currently case
         insensitively defined as 0, false, no, off for False, and 1, true,
         yes, on for True).  Returns False or True.
@@ -353,6 +355,17 @@
         self.args = (filename, lineno, line)
 
 
+# Used in parsers to denote selecting a backwards-compatible inline comment
+# character behavior (; and # are comments at the start of a line, but ; only
+# inline)
+_COMPATIBLE = object()
+
+# Used in parser getters to indicate the default behaviour when a specific
+# option is not found it to raise an exception. Created to enable `None' as
+# a valid fallback value.
+_UNSET = object()
+
+
 class RawConfigParser:
     """ConfigParser that does not do interpolation."""
 
@@ -389,9 +402,9 @@
     OPTCRE_NV = re.compile(_OPT_NV_TMPL.format(delim="=|:"), re.VERBOSE)
     # Compiled regular expression for matching leading whitespace in a line
     NONSPACECRE = re.compile(r"\S")
-    # Select backwards-compatible inline comment character behavior
-    # (; and # are comments at the start of a line, but ; only inline)
-    _COMPATIBLE = object()
+    # Possible boolean values in the configuration.
+    BOOLEAN_STATES = {'1': True, 'yes': True, 'true': True, 'on': True,
+                      '0': False, 'no': False, 'false': False, 'off': False}
 
     def __init__(self, defaults=None, dict_type=_default_dict,
                  allow_no_value=False, *, delimiters=('=', ':'),
@@ -414,7 +427,7 @@
             else:
                 self._optcre = re.compile(self._OPT_TMPL.format(delim=d),
                                           re.VERBOSE)
-        if comment_prefixes is self._COMPATIBLE:
+        if comment_prefixes is _COMPATIBLE:
             self._startonly_comment_prefixes = ('#',)
             self._comment_prefixes = (';',)
         else:
@@ -528,6 +541,8 @@
                 elements_added.add(section)
             for key, value in keys.items():
                 key = self.optionxform(key)
+                if value is not None:
+                    value = str(value)
                 if self._strict and (section, key) in elements_added:
                     raise DuplicateOptionError(section, key, source)
                 elements_added.add((section, key))
@@ -542,21 +557,29 @@
         )
         self.read_file(fp, source=filename)
 
-    def get(self, section, option):
-        opt = self.optionxform(option)
-        if section not in self._sections:
-            if section != DEFAULTSECT:
-                raise NoSectionError(section)
-            if opt in self._defaults:
-                return self._defaults[opt]
+    def get(self, section, option, vars=None, default=_UNSET):
+        """Get an option value for a given section.
+
+        If `vars' is provided, it must be a dictionary. The option is looked up
+        in `vars' (if provided), `section', and in `DEFAULTSECT' in that order.
+        If the key is not found and `default' is provided, it is used as
+        a fallback value. `None' can be provided as a `default' value.
+        """
+        try:
+            d = self._unify_values(section, vars)
+        except NoSectionError:
+            if default is _UNSET:
+                raise
             else:
+                return default
+        option = self.optionxform(option)
+        try:
+            return d[option]
+        except KeyError:
+            if default is _UNSET:
                 raise NoOptionError(option, section)
-        elif opt in self._sections[section]:
-            return self._sections[section][opt]
-        elif opt in self._defaults:
-            return self._defaults[opt]
-        else:
-            raise NoOptionError(option, section)
+            else:
+                return default
 
     def items(self, section):
         try:
@@ -571,23 +594,35 @@
             del d["__name__"]
         return d.items()
 
-    def _get(self, section, conv, option):
-        return conv(self.get(section, option))
+    def _get(self, section, conv, option, *args, **kwargs):
+        return conv(self.get(section, option, *args, **kwargs))
 
-    def getint(self, section, option):
-        return self._get(section, int, option)
+    def getint(self, section, option, vars=None, default=_UNSET):
+        try:
+            return self._get(section, int, option, vars)
+        except (NoSectionError, NoOptionError):
+            if default is _UNSET:
+                raise
+            else:
+                return default
 
-    def getfloat(self, section, option):
-        return self._get(section, float, option)
+    def getfloat(self, section, option, vars=None, default=_UNSET):
+        try:
+            return self._get(section, float, option, vars)
+        except (NoSectionError, NoOptionError):
+            if default is _UNSET:
+                raise
+            else:
+                return default
 
-    _boolean_states = {'1': True, 'yes': True, 'true': True, 'on': True,
-                       '0': False, 'no': False, 'false': False, 'off': False}
-
-    def getboolean(self, section, option):
-        v = self.get(section, option)
-        if v.lower() not in self._boolean_states:
-            raise ValueError('Not a boolean: %s' % v)
-        return self._boolean_states[v.lower()]
+    def getboolean(self, section, option, vars=None, default=_UNSET):
+        try:
+            return self._get(section, self._convert_to_boolean, option, vars)
+        except (NoSectionError, NoOptionError):
+            if default is _UNSET:
+                raise
+            else:
+                return default
 
     def optionxform(self, optionstr):
         return optionstr.lower()
@@ -797,21 +832,10 @@
         exc.append(lineno, repr(line))
         return exc
 
-
-class ConfigParser(RawConfigParser):
-    """ConfigParser implementing interpolation."""
-
-    def get(self, section, option, raw=False, vars=None):
-        """Get an option value for a given section.
-
-        If `vars' is provided, it must be a dictionary. The option is looked up
-        in `vars' (if provided), `section', and in `defaults' in that order.
-
-        All % interpolations are expanded in the return values, unless the
-        optional argument `raw' is true.  Values for interpolation keys are
-        looked up in the same manner as the option.
-
-        The section DEFAULT is special.
+    def _unify_values(self, section, vars):
+        """Create a copy of the DEFAULTSECT with values from a specific
+        `section' and the `vars' dictionary. If provided, values in `vars'
+        take precendence.
         """
         d = self._defaults.copy()
         try:
@@ -822,18 +846,86 @@
         # Update with the entry specific variables
         if vars:
             for key, value in vars.items():
+                if value is not None:
+                    value = str(value)
                 d[self.optionxform(key)] = value
+        return d
+
+    def _convert_to_boolean(self, value):
+        """Return a boolean value translating from other types if necessary.
+        """
+        if value.lower() not in self.BOOLEAN_STATES:
+            raise ValueError('Not a boolean: %s' % value)
+        return self.BOOLEAN_STATES[value.lower()]
+
+
+class ConfigParser(RawConfigParser):
+    """ConfigParser implementing interpolation."""
+
+    def get(self, section, option, raw=False, vars=None, default=_UNSET):
+        """Get an option value for a given section.
+
+        If `vars' is provided, it must be a dictionary. The option is looked up
+        in `vars' (if provided), `section', and in `DEFAULTSECT' in that order.
+        If the key is not found and `default' is provided, it is used as
+        a fallback value. `None' can be provided as a `default' value.
+
+        All % interpolations are expanded in the return values, unless the
+        optional argument `raw' is true.  Values for interpolation keys are
+        looked up in the same manner as the option.
+
+        The section DEFAULT is special.
+        """
+        try:
+            d = self._unify_values(section, vars)
+        except NoSectionError:
+            if default is _UNSET:
+                raise
+            else:
+                return default
         option = self.optionxform(option)
         try:
             value = d[option]
         except KeyError:
-            raise NoOptionError(option, section)
+            if default is _UNSET:
+                raise NoOptionError(option, section)
+            else:
+                return default
 
         if raw or value is None:
             return value
         else:
             return self._interpolate(section, option, value, d)
 
+    def getint(self, section, option, raw=False, vars=None, default=_UNSET):
+        try:
+            return self._get(section, int, option, raw, vars)
+        except (NoSectionError, NoOptionError):
+            if default is _UNSET:
+                raise
+            else:
+                return default
+
+    def getfloat(self, section, option, raw=False, vars=None, default=_UNSET):
+        try:
+            return self._get(section, float, option, raw, vars)
+        except (NoSectionError, NoOptionError):
+            if default is _UNSET:
+                raise
+            else:
+                return default
+
+    def getboolean(self, section, option, raw=False, vars=None,
+                   default=_UNSET):
+        try:
+            return self._get(section, self._convert_to_boolean, option, raw,
+                             vars)
+        except (NoSectionError, NoOptionError):
+            if default is _UNSET:
+                raise
+            else:
+                return default
+
     def items(self, section, raw=False, vars=None):
         """Return a list of (name, value) tuples for each option in a section.
 
diff --git a/Lib/test/test_cfgparser.py b/Lib/test/test_cfgparser.py
index a20678d..3079cfb 100644
--- a/Lib/test/test_cfgparser.py
+++ b/Lib/test/test_cfgparser.py
@@ -62,9 +62,10 @@
              'Spaces',
              'Spacey Bar',
              'Spacey Bar From The Beginning',
+             'Types',
              ]
         if self.allow_no_value:
-            E.append(r'NoValue')
+            E.append('NoValue')
         E.sort()
         eq = self.assertEqual
         eq(L, E)
@@ -80,9 +81,43 @@
         eq(cf.get('Commented Bar', 'baz'), 'qwe')
         eq(cf.get('Spaces', 'key with spaces'), 'value')
         eq(cf.get('Spaces', 'another with spaces'), 'splat!')
+        eq(cf.getint('Types', 'int'), 42)
+        eq(cf.get('Types', 'int'), "42")
+        self.assertAlmostEqual(cf.getfloat('Types', 'float'), 0.44)
+        eq(cf.get('Types', 'float'), "0.44")
+        eq(cf.getboolean('Types', 'boolean'), False)
         if self.allow_no_value:
             eq(cf.get('NoValue', 'option-without-value'), None)
 
+        # test vars= and default=
+        eq(cf.get('Foo Bar', 'foo', default='baz'), 'bar')
+        eq(cf.get('Foo Bar', 'foo', vars={'foo': 'baz'}), 'baz')
+        with self.assertRaises(configparser.NoSectionError):
+            cf.get('No Such Foo Bar', 'foo')
+        with self.assertRaises(configparser.NoOptionError):
+            cf.get('Foo Bar', 'no-such-foo')
+        eq(cf.get('No Such Foo Bar', 'foo', default='baz'), 'baz')
+        eq(cf.get('Foo Bar', 'no-such-foo', default='baz'), 'baz')
+        eq(cf.get('Spacey Bar', 'foo', default=None), 'bar')
+        eq(cf.get('No Such Spacey Bar', 'foo', default=None), None)
+        eq(cf.getint('Types', 'int', default=18), 42)
+        eq(cf.getint('Types', 'no-such-int', default=18), 18)
+        eq(cf.getint('Types', 'no-such-int', default="18"), "18") # sic!
+        self.assertAlmostEqual(cf.getfloat('Types', 'float',
+                                           default=0.0), 0.44)
+        self.assertAlmostEqual(cf.getfloat('Types', 'no-such-float',
+                                           default=0.0), 0.0)
+        eq(cf.getfloat('Types', 'no-such-float', default="0.0"), "0.0") # sic!
+        eq(cf.getboolean('Types', 'boolean', default=True), False)
+        eq(cf.getboolean('Types', 'no-such-boolean', default="yes"),
+           "yes") # sic!
+        eq(cf.getboolean('Types', 'no-such-boolean', default=True), True)
+        eq(cf.getboolean('No Such Types', 'boolean', default=True), True)
+        if self.allow_no_value:
+            eq(cf.get('NoValue', 'option-without-value', default=False), None)
+            eq(cf.get('NoValue', 'no-such-option-without-value',
+                      default=False), False)
+
         self.assertNotIn('__name__', cf.options("Foo Bar"),
                          '__name__ "option" should not be exposed by the API!')
 
@@ -127,6 +162,10 @@
 [Spaces]
 key with spaces {0[1]} value
 another with spaces {0[0]} splat!
+[Types]
+int {0[1]} 42
+float {0[0]} 0.44
+boolean {0[0]} NO
 """.format(self.delimiters, self.comment_prefixes)
         if self.allow_no_value:
             config_string += (
@@ -194,7 +233,12 @@
             "Spaces": {
                 "key with spaces": "value",
                 "another with spaces": "splat!",
-            }
+            },
+            "Types": {
+                "int": 42,
+                "float": 0.44,
+                "boolean": False,
+            },
         }
         if self.allow_no_value:
             config.update({
@@ -732,8 +776,11 @@
                                          'no values here',
                                          'tricky interpolation',
                                          'more interpolation'])
-        #self.assertEqual(cf.getint('DEFAULT', 'go', vars={'interpolate': '-1'}),
-        #                 -1)
+        self.assertEqual(cf.getint('DEFAULT', 'go',
+                                   vars={'interpolate': '-1'}), -1)
+        with self.assertRaises(ValueError):
+            # no interpolation will happen
+            cf.getint('DEFAULT', 'go', raw=True, vars={'interpolate': '-1'})
         self.assertEqual(len(cf.get('strange', 'other').split('\n')), 4)
         self.assertEqual(len(cf.get('corruption', 'value').split('\n')), 10)
         longname = 'yeah, sections can be indented as well'
@@ -808,7 +855,7 @@
 
 class CompatibleTestCase(CfgParserTestCaseClass):
     config_class = configparser.RawConfigParser
-    comment_prefixes = configparser.RawConfigParser._COMPATIBLE
+    comment_prefixes = configparser._COMPATIBLE
 
     def test_comment_handling(self):
         config_string = textwrap.dedent("""\