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)
An alternative approach:
ConfigParser.get offers a vars parameter that can be passed in, which is used as the primary lookup if it's provided, however, it ignores whether there exists a value for the option already on the section.
We can, therefore, use vars via ducktyping, but we'll change the behavior of .items() to the following:
- If the config object has the option we're already looking for, we'll take that.
- Otherwise, we'll return the default from
vars.
Here's a very naive implementation:
class DefaultOption(dict):
def __init__(self, config, section, **kv):
self._config = config
self._section = section
dict.__init__(self, **kv)
def items(self):
_items = []
for option in self:
if not self._config.has_option(self._section, option):
_items.append((option, self[option]))
else:
value_in_config = self._config.get(self._section, option)
_items.append((option, value_in_config))
return _items
In usage:
def read_config(section, location):
config = configparser.ConfigParser()
config.read(location)
apple = config.get(section, 'apple',
vars=DefaultOption(config, section, apple=None))
pear = config.get(section, 'pear',
vars=DefaultOption(config, section, pear=None))
banana = config.get(section, 'banana',
vars=DefaultOption(config, section, banana=None))
return apple, pear, banana
def save_to_config(section, location, apple, pear, banana):
config = configparser.ConfigParser()
config.read(location)
if section not in config.sections():
config.add_section(section)
config.set(section, 'apple', apple)
config.set(section, 'pear', pear)
with open(location, 'wb') as cf:
config.write(cf)
That being said, this is a little indirect, but perfectly valid.
Note, that this will still raise NoSectionError.
If you're trying to handle that as well, ConfigParser.ConfigParser takes a dict_type parameter, so you just instantiate the class with a defaultdict.
So, change configparser.ConfigParser() to configparser.ConfigParser(dict_type=lambda: defaultdict(list))
For all intents and purposes though, I'd probably use Lego's suggestions.
Update for original question edit
If you want to use the defaults keyword into ConfigParser, it might help to look at how the implementation is defined. Here's the ConfigParser.__init__() code for how defaults are initialized. You'll see that defaults are used completely differently than sections. To dive a bit deeper about the role they play during get(), look at the code for ConfigParser.get(). Basically, if the section isn't DEFAULTSECT, then a NoSectionError is thrown.
You have two ways to overcome this:
- Use the
defaultdict idea I proposed above
- Modify your
read_config function slightly
def read_config(section):
defaults = {'apple': None,
'pear': None,
'banana': None }
config = configparser.ConfigParser(defaults = defaults)
config.read(location)
if not config.has_section(section):
config.add_section(section)
apple = config.get(section,'apple')
pear = config.get(section, 'pear')
banana = config.get(section, 'banana')
return apple, pear, banana
But I say, since this is not a one-liner, it opens up a lot more paths like the DefaultOption class I offered. We can make it even a bit less verbose.
所有评论(0)