classification
Title: Allow ConfigParser.get*() to take a default value
Type: enhancement Stage: test needed
Components: Library (Lib) Versions: Python 3.2
process
Status: closed Resolution: duplicate
Dependencies: Superseder: Default return value in ConfigParser
View: 6751
Assigned To: Nosy List: Gumnos, amaury.forgeotdarc, eric.araujo, lukasz.langa, meatballhat, r.david.murray
Priority: normal Keywords: patch

Created on 2010-05-08 19:56 by Gumnos, last changed 2010-08-09 13:12 by lukasz.langa. This issue is now closed.

Files
File name Uploaded Description Edit
ConfigParser.diff Gumnos, 2010-05-08 20:08 The patch
configparser.diff Gumnos, 2010-05-10 00:51 configparser patch against py3k branch instead of py2.6
Messages (9)
msg105333 - (view) Author: Tim Chase (Gumnos) * Date: 2010-05-08 19:56
Patch to update ConfigParser.py so that the .get* methods can take an optional parameter rather than raising exceptions.  Usage:

  cp = ConfigParser(...)
  # ...
  value = cp.get('MySection', 'MyOption', default='some default')
  i = cp.getint('MySecton', 'MyInt', default=42)
  f = cp.getfloat('MySection', 'MyF', default=3.14)
msg105334 - (view) Author: Tim Chase (Gumnos) * Date: 2010-05-08 20:02
For some reason, the diff didn't attach to the previous message.
msg105335 - (view) Author: Tim Chase (Gumnos) * Date: 2010-05-08 20:08
Trying a 3rd time to attach the diff (this time from Safari instead of FF)
msg105339 - (view) Author: Éric Araujo (eric.araujo) * (Python committer) Date: 2010-05-08 22:04
I don’t know if this feature request should be discussed on python-ideas first. Do you have real use cases for this, is it just a nice-to-have thing?

If it’s accepted, I have some remarks about the patch:
- 2.7 is feature-frozen, so you’ll need to target the py3k branch. (Do we need to put a big warning somewhere?) Practically, this means that “ValueError, thing” should be “ValueError(thing)”.
- Lone parens or “):” on a line are not pretty.
- Some indentation after a linebreak do not comply with PEP 8 (e.g., do
def function(argument, argument,
             indent after parens column, end):

Thanks for your submission!
msg105423 - (view) Author: Tim Chase (Gumnos) * Date: 2010-05-10 00:51
Yes, the use-case is the same as a dict.get(key, default) which I frequently use -- so it can be used in things like

  result = some_function(
     cp.get('MySection', 'MyValue', default='hello'),
     cp.getint('MySection', 'MyInt', default=42)
     )

To write something similar with the current ConfigParser requires a lot more code or an inelegant wrapper (either passing the config-parser object each call, or capturing the parser object in a closure which makes the "def" something repeated):

  try:
    mv = cp.get('MySection', 'MyValue')
  except (NoSectionError, NoOptionError), e:
    mv = 'hello'
  try:
    mi = cp.getint('MySection', 'MyInt')
  except (NoSectionError, NoOptionError), e:
    mi = 42
  result = some_function(mv, mi)
  del mv, mi  # leaving the namespace as it was before

The above can be done in a scattering of wrapper functions, but the addition in stock ConfigParser prevents a lot of duplicate code.  This is the fourth project in which I've used the ConfigParser and have reached for a default value in every one of them, to be frustrated by the lack.

The old-style "raise ValueError" you mention was the original code (just indented), but I've changed it to the preferred format.

For the dangling close-paren or "):" on its own line, I tend to do it for the same reason a trailing comma is allowed/encouraged in lists/tuples/dicts/param lists, so it's easy to add further comma-separated entries without giving cluttered diffs.  The source against which I'm patching has several dangling close-parens already (search for "^\s*)" to find the calls to re.compile).

I pulled down the branches/py3k and patched against it. (attached)
msg105446 - (view) Author: Amaury Forgeot d'Arc (amaury.forgeotdarc) * (Python committer) Date: 2010-05-10 16:21
what is this "raise_on_bad" additional argument?
msg105453 - (view) Author: Tim Chase (Gumnos) * Date: 2010-05-10 18:16
The "raise_on_bad" (I'm ambivalent on the name, but if you come up with a better name, I'd be fine with changing it) allows you to differentiate between an option that isn't provided, and one that is provided, but can't be converted to the specified int/float/boolean type.  E.g.

  [MySection]
  myFloat = hello

if you issue

  f = cp.getfloat("MySection", "myFloat", default=3.14, raise_on_bad=True)

it will raise a ValueError because "hello" can't be converted to a float.  However there are other times you want (well, other times *I've* wanted...most cases, in fact) to be able to specify that if there's ANY problem, just return the default:

  f = cp.getfloat("MySection", "myFloat", default=3.14, raise_on_bad=False)

returns f=3.14 (the default).  The only crazy side-exception I saw in the code is if you make a "dumb programmer" mistake of 

  f = cp.getfloat("MySection", "myFloat", default="foo", raise_on_bad=False)

it may still give you an error or unexpected non-float results because the default isn't what you asked for.

The ability to get a valid result back (regardless of section or option presence; or data errors) is most helpful when all you want is the answer to the question "did the user specify a valid value I can use? otherwise, just use the darn default".
msg105456 - (view) Author: R. David Murray (r.david.murray) * (Python committer) Date: 2010-05-10 19:25
Rather than a raise_on_bad option, it seems to me it would be better to code a try/except clause in cases where you want the default even if there is an error converting the data in the file.  I would expect such cases to be rare, except for cases where you want to issue a warning message and then proceed with the default anyway, in which case you'd need the try/except anyway in order to issue the warning message.
msg113414 - (view) Author: Łukasz Langa (lukasz.langa) * (Python committer) Date: 2010-08-09 13:12
Superseded by #6751 in terms of age. A new patch for the functionality is going to be prepared.
History
Date User Action Args
2010-08-09 13:12:05lukasz.langasetstatus: open -> closed

nosy: + lukasz.langa
messages: + msg113414

superseder: Default return value in ConfigParser
resolution: duplicate
2010-05-10 19:26:00r.david.murraysetnosy: + r.david.murray
messages: + msg105456
2010-05-10 18:16:37Gumnossetmessages: + msg105453
2010-05-10 16:21:07amaury.forgeotdarcsetnosy: + amaury.forgeotdarc
messages: + msg105446
2010-05-10 00:51:24Gumnossetfiles: + configparser.diff

messages: + msg105423
2010-05-09 00:20:10brian.curtinsetstage: test needed
2010-05-08 23:14:41meatballhatsetnosy: + meatballhat
2010-05-08 22:04:05eric.araujosetnosy: + eric.araujo

messages: + msg105339
stage: test needed -> (no value)
2010-05-08 21:07:08brian.curtinsetstage: test needed
versions: + Python 3.2, - Python 2.6
2010-05-08 20:08:23Gumnossetfiles: + ConfigParser.diff
keywords: + patch
messages: + msg105335
2010-05-08 20:02:07Gumnossetmessages: + msg105334
2010-05-08 19:56:33Gumnoscreate