1

I'm doing a relatively big project for my final thesis and therefore I am using a .ini file for storing and retrieving settings. However, I am unable to find an elegant solution for how to convert the strings (well, actually the strings inside the dictionary) that Configparser returns to numbers (ints and floats) and/or lists.

Googling the issue, I came across this SO thread which only tackles the 'list' part of my problem but using the top rated solution (defining lists inside the .ini file like that: list=item1,item2) didn't do anything for me, as the 'list' still shows up as a string after parsing. Also, I do not want to change format.

So I decided I would try it myself and came up with this solution:

import configparser 

# create a new instance of a 'ConfigParser' class
config = configparser.ConfigParser()
# create mock-content for the config file
config["Test"] = {
"test_string":"string",
"test_int":"2",
"test_float":"3.0",
"test_list":"item1, item2"
}
# get the relevant settings
settings = config["Test"]
# pack the relevant settings into a dictionary
settings = dict(settings)
# iterate through all the key-value pairs and convert them, if possible
for key, value in settings.items():
    # try to convert to int
    try:
        settings[key] = int(value)
    # if the value can't be converted to int, it might be a float or a list
    except ValueError:
        # try converting it to a float
        try:
            settings[key] = float(value)
        # if the value can't be converted to float, try converting to list
        except ValueError:
            if "," in value:
                cont = value.split(",")
                settings[key] = [item.strip() for item in cont]
            else:
                settings[key] = value
print(type(settings["test_string"]))
print(settings)

However, this seems so very inelegant and is so heavily nested and the task itself seems so important that I cannot believe that there is no "more official" solution to this that I am simply unable to find.

So, could please someone help me out here and tell me if there really is no better, more straightforward way to achieve this!?

Viggar
  • 140
  • 1
  • 8

1 Answers1

2

Best I can do is this (though it's kinda hacky and probably dangerous too):

for key, value in settings.items():
    try: # will handle both ints and floats, even tuples with ints/floats
        settings[key] = eval(value)
    except NameError: # this means it's a string or a tuple with strings
        get_val = list(map(str.strip, value.split(",")))
        settings[key] = get_val if get_val[1:] else get_val[0]

This will work correctly for ints, floats as well as your comma separated values (it will evaluated it as a tuple, I guess that should be fine though I added a condition for that anyway).

Jarvis
  • 8,494
  • 3
  • 27
  • 58
  • Thanks for your solution! I'd upvote it but I don't have the 'reputation' yet... But in what way is your solution dangerous, could you please elaborate? And, since I am writing a software for an experiment on a satellite and stability is key here, would you recommend your solution over mine regarding stability or the other way around? – Viggar Dec 30 '20 at 11:38
  • Also, how is it possible that there is no "official" solution for this? I mean it must be an important part of config files to store all kinds of data types or am I doing something wrong here? – Viggar Dec 30 '20 at 11:39
  • Sure, in the try part, the `eval` basically evaluates your string in an interpreter, so if it's a valid type like int or float, it will pass, it's like when you type 1 or 1.0 in interpreter and you get the same thing back. On the other hand, if you write a string without quotes or with commas, it returns error, hence the except part. Here, it is assumed that the type is string or string with commas, so I split it and return first element (string with no commas split around it will return list of length 1) or the complete list in else case. – Jarvis Dec 30 '20 at 12:01
  • Regarding your second question, I think configparser is supposed to store only configuration data (like constants or properties) which you at most, just need to access in string format. For more complex data types, you can use a JSON or YAML instead. You can use this code in production iff you are sure that you only have ints, floats, strings, or comma-separated strings in your config. For even more cases, you will have to take a conditionals-based route. PS. You can upvote this answer once you accept it :) – Jarvis Dec 30 '20 at 12:02
  • 1
    By dangerous, I meant using `eval` which is certainly fine for your case. As to why it is dangerous, you can see [this](https://stackoverflow.com/questions/1832940/why-is-using-eval-a-bad-practice) or [this](https://softwareengineering.stackexchange.com/questions/311507/why-are-eval-like-features-considered-evil-in-contrast-to-other-possibly-harmfu). – Jarvis Dec 30 '20 at 12:10
  • 1
    Wow, that is a lot of information. Thank you very much for helping me out! I'm going to think about what kind of types I am going to need and then decide from there. You have a good... whatever it is you are celebrating next ;) – Viggar Dec 30 '20 at 12:15