How to raise exception instead of returning None in Python "ConfigParser" section?

Viewed 257

I have a problem using the configparser module.

import configparser

config = configparser.ConfigParser()
config.read_dict({"foo": {}})

foo = config["foo"]
foo.getboolean("missing_field")

I would like my code to raise an exception if the parsed configuration is missing a required field. However, getboolean() returns None in this case instead of raising a KeyError as expected.

I could possibly use foo["missing_field"] which does raise an exception. However, in such case, I loose the boolean conversion.

I could explicitly test for if res is None: and throw the exception manually but I have a lot of config fields so that would be cumbersome.

Does Python provide an elegant way to force strict config parsing?

2 Answers

You can use getboolean on the config object directly:

config.getboolean("foo", "missing_field")

which will raise a NoOptionError if missing_field doesn't exist:

configparser.NoOptionError: No option 'missing_field' in section: 'foo'

The different behavior for getboolean on the proxy is because it calls the relevant getter with a default fallback=None. The problem is that the "regular" get uses a special _UNSET object as the default fallback and then does:

if fallback is _UNSET:
    raise NoOptionError(option, section)
else:
    return fallback

So, as an alternative (which came up in the discussion for @Tomerikoo's answer and @chepner suggested in a comment to this answer), you can pass in _UNSET as the fallback value when using the section proxy. As per your original code:

foo.getboolean("missing_field", fallback=configparser._UNSET)

Well... @Kemp found a tweaky behaviour that solves this easily, but as I already went through the following trouble, I might as well just post it...


The default behavior doesn't raise an exception, but why not "fix" it according to your needs? You can create a sub-class of ConfigParser that only wraps getboolean with a check if the option actually exists and then returns the original getboolean value. I think the nice thing about this option is that you don't need to change your code at all - just change the initial config = ... line to use the new class:

import configparser

class MyConfig(configparser.ConfigParser):
    def getboolean(self, section, option, *, raw=False, vars=None,
                   fallback=configparser_UNSET, **kwargs):
        if self.get(section, option, raw=raw, vars=vars, fallback=fallback) == fallback:
            raise configparser.NoOptionError(option, section)

        return super().getboolean(section, option, raw=raw, vars=vars, fallback=fallback)

Now doing:

config = MyConfig()
config.read_dict({"foo": {'existing_field': '1'}})

foo = config["foo"]
print(foo.getboolean("existing_field"))
print(foo.getboolean("missing_field"))

Will give (truncated):

True
Traceback (most recent call last):
...
configparser.NoOptionError: No option 'missing_field' in section: 'foo'

As opposed to:

True
None

With the regular configparser.ConfigParser.

Related