Python configparser getting and setting without exceptions

Nick Humrich picture Nick Humrich · Jul 18, 2014 · Viewed 19.5k times · Source

Everytime you try to get or set to a section using configparser in Python it throws a NoSectionError if the section does not exist. Is there anyway to avoid this? Also, can I also avoid the NoOptionError when getting an option?

For example, using a dictionary, there is the setdefault option: instead of throwing a KeyError when the key does not exist, the dictionary adds the key, sets the key's value to the default value, and returns the default value.

I am currently doing the following for getting attributes:

def read_config(section):
    config = configparser.ConfigParser()
    config.read(location)
    try:
        apple = config.get(section, 'apple')
    except NoSectionError, NoOptionError:
        apple = None
    try:
        pear = config.get(section, 'pear')
    except NoSectionError, NoOptionError:
        pear = None
    try:
        banana = config(section, 'banana')
    except NoSectionError, NoOptionError:
        banana = None
    return apple, pear, banana

And the following for setting them:

def save_to_config(section, apple, pear, banana):
    config = configparser.ConfigParser()
    if not os.path.exists(folder_location):
        os.makedirs(folder_location)

    config.read(location)
    if section not in config.sections():
        config.add_section(section)

    config.set(section, 'apple', apple)
    config.set(section, 'pear', pear)
    config.set(section, 'banana', banana)

Setting isn't too bad because they all have the same section, but getting is well... terrible. There has got to be a better way.

Is there perhaps some one liner where I can reduce this:

try:
    apple = config.get(section, 'apple')
except NoSectionError, NoOptionError:
    apple = None

to this:

apple = config.get_with_default(section, 'apple', None)

-- EDIT --

I have tried to make the following changes per lego's suggestion:

def read_config(section):
    defaults = { section : {'apple': None,
                            'pear': None,
                            'banana': None }} 
    config = configparser.ConfigParser(defaults = defaults)
    config.read(location)

    apple = config.get(section, 'apple')
    pear = config.get(section, 'pear')
    banana = config(section, 'banana')

    return apple, pear, banana

But this still raises a NoSectionError if the section doesn't exist

Note: I have also tried it where defaults = just {'apple': None, 'pear': None, 'banana': None } (no section)

Answer

user764357 picture user764357 · Jul 23, 2014

There a few ways to handle this depending on how complex you want to get.

The simplest way is probably just chaining logic together. ConfigParser defines has_option to safely check if an option exists for a section.

apple = config.has_option(section,'apple') and config.get(section,'apple') or None

Alternatively, if you know ahead of time which options should have values you can set the defaults dictionary when instantiating the parser. This has the advantage of retaining and raising any errors for sections you don't know about.

 myDefaults = {'apple':None,'banana':None,'pear':None}
 config = configparser.ConfigParser(defaults=myDefaults)

As stated by Wogan you can create a wrapper function, but you can easily just again use has_option like so:

def get_with_default(config,section,name,default)
    if config.has_option(section,name):
        return config.get(section,name)
    else:
        return default