I am using Python's configparser to read an ini file.
After parsing, I would like to print a list of entries that have not been used by the parser to inform the user that they have added entries to their ini file that will be ignored.
Consider an ini file like this:
[DEFAULT]
param1 = 1
param2 = 2
param3 = 3
param4 = 4
What I would like to have is this:
parser = configparser.ConfigParser()
parser.read(config_path)
p1 = parser["DEFAULT"]["param1"]
p2 = parser["DEFAULT"]["param2"]
unused = parser["DEFAULT"].get_unused_keys()
The last line is made up. I would like the variable 'unused' to contain
["param3", "param4"]
I haven't found any mention of such a feature in the manual, but I would find it very helpful. I could inherit from ConfigParser and extend all access functions to have a flag that keeps track of whether a specific item was accessed, but I was hoping there is a simpler way.
I have written a solution. It is not too elegant but it works. You need to create a child class from RawConfigParser
class and create a dict variable in your class and extend the get, _get
methods. As you can see below when you use the get, getint, getfloat, getboolean
methods to get a value of variable then the related method appends the section and variable to your dict. When you call the get_unused_keys
method, it will filter the all available options in a section with the already used options and gives the unused options.
Complete code:
try:
import ConfigParser as Cp
except ImportError:
import configparser as Cp
class ConfigParser(Cp.RawConfigParser):
"""
ConfigParser class contains the all ConfigParser related implementations.
"""
used_vars = {}
def get_unused_keys(self, section):
all_sections = self.options(section)
unused_options = [x for x in all_sections if x not in self.used_vars[section]]
return unused_options
def get(self, section, option, *, raw=False, vars=None, fallback=Cp._UNSET):
if section not in self.used_vars:
self.used_vars[section] = [option]
else:
self.used_vars[section].append(option)
return super().get(section, option, raw=raw, vars=vars, fallback=fallback)
def _get(self, section, conv, option, **kwargs):
if section not in self.used_vars:
self.used_vars[section] = [option]
else:
self.used_vars[section].append(option)
return super()._get(section, conv, option, **kwargs)
parser = ConfigParser()
parser.read("test.ini")
p1 = parser.getint(section="TEST", option="param1")
print("TEST section - param1 = {}".format(p1))
p2 = parser.getboolean(section="TEST", option="param2")
print("TEST section - param2 = {}".format(p2))
print("Unused options in 'TEST' section: {}".format(parser.get_unused_keys("TEST")))
print("")
par2 = parser.get(section="TEST_SEC", option="param2")
print("TEST_SEC section - param2 = {}".format(par2))
print("Unused options in 'TEST_SEC' section: {}".format(parser.get_unused_keys("TEST_SEC")))
Used ini file:
[TEST]
param1 = 1
param2 = True
param3 = 3
param4 = False
[TEST_SEC]
param1 = 89
param2 = Hello World
param3 = 655
OUTPUT:
>>> python3 test.py
TEST section - param1 = 1
TEST section - param2 = True
Unused options in 'TEST' section: ['param3', 'param4']
TEST_SEC section - param2 = Hello World
Unused options in 'TEST_SEC' section: ['param1', 'param3']
FYI:
You shouldn't use DEFAULT
as name of section because it is a special section and you can get unexpected behavior. I have added _get
method to my implementations. If you check the original implementation of ConfigParser
, you can see all type specific getter use this method so it is enough to change. It means now the implementation supports getint, getfloat, getboolean
methods as well.
I hope my answer help you!