On 05/07/2010 07:56 PM, Steven D'Aprano wrote:
On Fri, 07 May 2010 15:05:53 -0500, Tim Chase wrote:
With a normal dictionary, I can specify a default fallback value in the
event the requested key isn't present:
[...]
However, with the ConfigParser object, there doesn't seem to be any way
to do a similar

Sounds like a nice feature to have. When you submit a patch (*grin*),

I'm game to create and provide a patch,

Attached is my initial patch against 2.6's ConfigParser.py to work in some defaults (though it should also apply fairly cleanly against 2.5).

It doesn't differentiate between missing sections and missing options, as both cases are the same in my use: the user didn't provide a setting, so I want to specify a default without lots of try/except wrapping.

For the type-specific get[int/float/boolean]() functions, it also provides an optional parameter to raise the ValueError or just return the default if it's provided-but-bogus (it may also yell at you if your default creates a ValueError).

My distributed ConfigParser.py didn't seem to have any tests, so I didn't include any; but would be glad to write a few if they'd be integrated into the standard core testing.

Is this the sort of things that should also be sent to python-dev, or are the powers-that-be lurking sufficiently on this list to see this patch fly?

-tkc



diff -r eea32b421d4c -r 79cb9fc14932 ConfigParser.py
--- a/ConfigParser.py   Sat May 08 07:15:52 2010 -0500
+++ b/ConfigParser.py   Sat May 08 07:55:48 2010 -0500
@@ -304,21 +304,30 @@
                 filename = '<???>'
         self._read(fp, filename)
 
-    def get(self, section, option):
+    def get(self, section, option, default=None):
         opt = self.optionxform(option)
         if section not in self._sections:
             if section != DEFAULTSECT:
-                raise NoSectionError(section)
+                if default is None:
+                    raise NoSectionError(section)
+                else:
+                    return default
             if opt in self._defaults:
                 return self._defaults[opt]
             else:
-                raise NoOptionError(option, section)
+                if default is None:
+                    raise NoOptionError(option, section)
+                else:
+                    return default
         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)
+            if default is None:
+                raise NoOptionError(option, section)
+            else:
+                return default
 
     def items(self, section):
         try:
@@ -333,22 +342,44 @@
             del d["__name__"]
         return d.items()
 
-    def _get(self, section, conv, option):
-        return conv(self.get(section, option))
+    def _get(self, section, conv, option, default=None, raise_on_bad=False):
+        v = self.get(section, option, default=default)
+        try:
+            return conv(v)
+        except ValueError:
+            if raise_on_bad:
+                raise
+            else:
+                return default
 
-    def getint(self, section, option):
-        return self._get(section, int, option)
+    def getint(self, section, option,
+            default=None,
+            raise_on_bad=False
+            ):
+        return self._get(section, int, option,
+            default=default,
+            raise_on_bad=raise_on_bad
+            )
 
-    def getfloat(self, section, option):
-        return self._get(section, float, option)
+    def getfloat(self, section, option,
+            default=None,
+            raise_on_bad=False
+            ):
+        return self._get(section, float, option,
+            default=default,
+            raise_on_bad=raise_on_bad
+            )
 
     _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)
+    def getboolean(self, section, option, default=None, raise_on_bad=False):
+        v = self.get(section, option, default=default)
         if v.lower() not in self._boolean_states:
-            raise ValueError, 'Not a boolean: %s' % v
+            if default is None or raise_on_bad:
+                raise ValueError, 'Not a boolean: %s' % v
+            else:
+                return default
         return self._boolean_states[v.lower()]
 
     def optionxform(self, optionstr):
@@ -512,7 +543,7 @@
 
 class ConfigParser(RawConfigParser):
 
-    def get(self, section, option, raw=False, vars=None):
+    def get(self, section, option, raw=False, vars=None, default=None):
         """Get an option value for a given section.
 
         All % interpolations are expanded in the return values, based on the
@@ -528,7 +559,10 @@
             d.update(self._sections[section])
         except KeyError:
             if section != DEFAULTSECT:
-                raise NoSectionError(section)
+                if default is None:
+                    raise NoSectionError(section)
+                else:
+                    return default
         # Update with the entry specific variables
         if vars:
             for key, value in vars.items():
@@ -537,14 +571,17 @@
         try:
             value = d[option]
         except KeyError:
-            raise NoOptionError(option, section)
+            if default is None:
+                raise NoOptionError(option, section)
+            else:
+                return default
 
         if raw:
             return value
         else:
-            return self._interpolate(section, option, value, d)
+            return self._interpolate(section, option, value, d, 
default=default)
 
-    def items(self, section, raw=False, vars=None):
+    def items(self, section, raw=False, vars=None, default=None):
         """Return a list of tuples with (name, value) for each option
         in the section.
 
@@ -573,10 +610,11 @@
             return [(option, d[option])
                     for option in options]
         else:
-            return [(option, self._interpolate(section, option, d[option], d))
+            return [(option, self._interpolate(section, option, d[option], d,
+                        default=default))
                     for option in options]
 
-    def _interpolate(self, section, option, rawval, vars):
+    def _interpolate(self, section, option, rawval, vars, default=None):
         # do the string interpolation
         value = rawval
         depth = MAX_INTERPOLATION_DEPTH
@@ -587,8 +625,12 @@
                 try:
                     value = value % vars
                 except KeyError, e:
-                    raise InterpolationMissingOptionError(
-                        option, section, rawval, e.args[0])
+                    if default is None:
+                        raise InterpolationMissingOptionError(
+                            option, section, rawval, e.args[0])
+                    else:
+                        return default
+
             else:
                 break
         if "%(" in value:
@@ -607,15 +649,17 @@
 
 class SafeConfigParser(ConfigParser):
 
-    def _interpolate(self, section, option, rawval, vars):
+    def _interpolate(self, section, option, rawval, vars, default=None):
         # do the string interpolation
         L = []
-        self._interpolate_some(option, L, rawval, section, vars, 1)
+        self._interpolate_some(option, L, rawval, section, vars, 1,
+            default=default)
         return ''.join(L)
 
     _interpvar_re = re.compile(r"%\(([^)]+)\)s")
 
-    def _interpolate_some(self, option, accum, rest, section, map, depth):
+    def _interpolate_some(self, option, accum, rest, section, map, depth,
+            default=None):
         if depth > MAX_INTERPOLATION_DEPTH:
             raise InterpolationDepthError(option, section, rest)
         while rest:
@@ -641,11 +685,15 @@
                 try:
                     v = map[var]
                 except KeyError:
-                    raise InterpolationMissingOptionError(
-                        option, section, rest, var)
+                    if default is None:
+                        raise InterpolationMissingOptionError(
+                            option, section, rest, var)
+                    else:
+                        return default
                 if "%" in v:
                     self._interpolate_some(option, accum, v,
-                                           section, map, depth + 1)
+                                           section, map, depth + 1,
+                                           default=default)
                 else:
                     accum.append(v)
             else:
-- 
http://mail.python.org/mailman/listinfo/python-list

Reply via email to