I am trying to use ConfigParser
in the following situation. I am running some code after which i have an object with several attributes. I pick out some of these attributes and write them to a .ini
file with configparser
. This works fine and my .ini
file looks something like this.
[Section]
path = "D:\\"
number = 10.0
boolean = False
Then with some other script I want to read the file and add the items as attributes to another object (self) using.
parser.read('file.ini')
self.__dict__.update(dict(parser.items("Section")))
This fails because all values are read as strings by configparser and now all attributes are strings. parser.items("Section")
looks like this:
[('path', '"D:\\"'), ('number', '10.0'), ('boolean', 'False')]
Now I could go and specify the floats, integers, and booleans by their keys and use the corresponding methods parser.getfloat
, getint
, and getboolean
to get the right python types out. However, that means making an extra list of keys and a loop to get them for each data type, which i don't want. Furthermore, even the path is now double quoted and i need to start removing quotes.
This behavior makes ConfigParser
almost completely worthless to me and I am doubting if I am using it correctly an if ConfigParser
is the best option for my goal, which is simply to write object attributes to a file and at a later time read the file and add the parameters to a new object as attributes. Can I use ConfigParser
for this effectively? Or should I start looking for a different method?
INI is not JSON. There are no data types. There are sections and key/value pairs. Stuff before the =
is the key, stuff after it is the value. Everything is text.
There are no "strings", which means there is no need to double quote anything. The same goes for "escaped" backslashes. The concept of escaping does not exist in the INI file format.
So, first off, your file should be looking like this:
[Section]
path = D:\
number = 10.0
boolean = False
Next, I consider this a dangerous operation
parser.read('file.ini')
self.__dict__.update(dict(parser.items("Section")))
because it potentially trashes your class instance with arbitrary values from a text file that might contain garbage, but when you can swear that the file will always be fine, well, do it if you must.
I would write a more explicit way of reading and validating config data. I sense your reason not to do that is laziness or a false sense of simplicity; both of these reasons are not very good.
If you want a semi-automated way of type conversion, tag the keys with a data type.
[Section]
s_path = D:\
f_number = 10.0
b_boolean = False
and write a function that knows how to handle them and throws when something is not right.
def type_convert(items):
result = []
for (key, value) in items:
type_tag = key[:2]
if type_tag == "s_":
result.append((key[2:], value))
elif type_tag == "f_":
result.append((key[2:], float(value)))
elif type_tag == "b_":
result.append((key[2:], bool(value)))
else:
raise ValueError('Invalid type tag "%s" found in ini file.' % type_tag)
# alternatively: "everything else defaults to string"
return result
which you can use to make the operation somewhat more convenient:
self.__dict__.update(dict(type_convert(parser.items("Section"))))
Of course you still run the risk of trashing your class instance by overriding keys that should not be overridden.