68

I need to store configurations (key/value) for a Python application and I am searching for the best way to store these configurations in a file.

I run into Python's ConfigParser and I wondered if the INI file format is really still appropriate nowadays?! Does there exist a more up-to-date format or is INI still the recommended way to go? (XML, JSON, ...)

Please share your opinions/recommendations...

gecco
  • 17,969
  • 11
  • 51
  • 68
  • 7
    I think INI is fine if you're just storing a list of key-value pairs! – Katriel Nov 22 '11 at 11:24
  • There is also the dict object type with a shelve. May be over kill though. – Matt Nov 22 '11 at 11:27
  • @Matt: I think it is easy to use, probably easier than using the `ConfigParser` – Constantinius Nov 22 '11 at 11:30
  • 1
    @Matt: Can you please lay out in an answer what you mean? – gecco Nov 22 '11 at 11:41
  • @gecco sure, ill post the python docs too. – Matt Nov 22 '11 at 11:45
  • 1
    Just to reiterate what others have said: there are alternatives, but INI style config is probably at least as good as any of them. It's readable, it doesn't need any more dependencies, your code can modify it... Don't ignore it just because it's old. – Thomas K Nov 22 '11 at 13:18
  • possible duplicate of [What's the best practice using a settings file in Python?](http://stackoverflow.com/questions/5055042/whats-the-best-practice-using-a-settings-file-in-python) – Ciro Santilli OurBigBook.com Mar 26 '14 at 04:06

8 Answers8

66

Consider using plain Python files as configuration files.

An example (config.py):

# use normal python comments

value1 = 32
value2 = "A string value"

value3 = ["lists", "are", "handy"]
value4 = {"and": "so", "are": "dictionaries"}

In your program, load the config file using exec (docs):

from pathlib import Path

if __name__ == "__main__":
    config = {}
    exec(Path("config.py").read_text(encoding="utf8"), {}, config)
    
    print(config["value1"])
    print(config["value4"])

I like this approach, for the following reasons:

  • In the simple case, the format is as easy to author as an INI-style config file. It also shares an important characteristic with INI files: it is very suitable for version control (this is less true for XML and maybe also for JSON)
  • I like the flexibility that comes with having the config file in an actual programming language.

The approach is widely used, a few examples:

  • A Django site's settings lives inside settings.py. Django does not use execfile, it uses import to read/execute settings.py AFAIK, but the end result is the same: the code inside the settings file is executed.
  • The bash shell reads and executes ~/.bashrc on startup.
  • The Python interpreter imports site.py on startup.
codeape
  • 97,830
  • 24
  • 159
  • 188
  • Not a security expert here, but I would guess that if the program runs with root privileges, but the config file lives in user space, a user could easily get dangerous code executed as root. – mac Nov 22 '11 at 11:53
  • 10
    If the program runs with root privileges, the config file should have root-only write permissions. – codeape Nov 22 '11 at 11:57
  • 2
    I don't really like this. The `setup.py` thing in distutils was changed into a declarative format to prevent it from being confusingly complex. This opens up potential for abuse (if statements etc.). However, if you were to parse the file using the `ast` module and then allow only simple assignments, it might work. – Noufal Ibrahim Nov 22 '11 at 12:27
  • @NoufalIbrahim: I disagree. The possibility of using (for instance) if statements is one of the reasons I like this approach (flexibility). – codeape Nov 22 '11 at 12:38
  • 1
    To each her own. I find configuration files written in a declarative style easier to understand and use. Once it includes statements etc., it becomes part of the code rather than the configuration file and should be treated like that. – Noufal Ibrahim Nov 22 '11 at 12:54
  • 19
    This also makes it tricky if you later want to be able to change the settings from a GUI, or set them programmatically in any way. Modifying a declarative context file is much simpler than modifying a script. – Thomas K Nov 22 '11 at 13:13
  • @codeape - "If the program runs with root privileges, the config file should have root-only write permissions." Not necessarily. It could be a system-wide service that users can configure... – mac Nov 22 '11 at 13:19
  • 2
    Kugel: And it's a pain to manage. It doesn't even read/exec the file. It imports it which makes it necessary to configure the sys.path manually and all other sorts of hassles. A true nightmare from an ops perspective. It's probably the point about Django which I like the least. – Noufal Ibrahim Nov 22 '11 at 16:20
  • +1, Sample config file: `DEBUG = False`, `CONF1 = 'something' if Debug else 'something else'`, etc, etc... – Izkata Nov 22 '11 at 16:21
  • 4
    The more I consider this solution the better I like it – gecco Dec 01 '11 at 20:42
  • it works great when I run the code in debug mode , but when I call it from the console I get this exception : IOError : [Errno2] No such file or directory : 'Settings.conf' – user1863359 Apr 24 '14 at 10:02
  • Perhaps the current working directory is different in the two cases? – codeape Apr 24 '14 at 12:55
  • 2
    +1 good solution, however you might want to do the following: config = {} discard = {} execfile("example.conf", discard, config) otherwise you're config dict will be polluted with globals like the python copyright. – andrew Sep 19 '14 at 18:29
  • 1
    I prefer this. While you should avoid logic in configuration files (otherwise, it's not really a config file), most large programs end up with them eventually. Using an actual programming language will be easier than using something that started as a declarative config format onto which logic was added. If you never need complicated logic, then don't use it. – Kevin Mar 21 '16 at 18:49
  • @andrew I added your suggestion to the answer – codeape Oct 16 '20 at 10:31
  • Note, there is the [Python-like (Python dialect/subset) Starlark language](https://github.com/bazelbuild/starlark), which is intended for this use case of config files (and used by Bazel). The main feature is hermetic execution, i.e. execution cannot access the file system, network, system clock. It is safe to execute untrusted code. – Albert Mar 03 '21 at 13:00
35

INI is till totally OK and as other said, the format of your config file really depends from how you are going to use it.

Personally I am a fan of YAML: concise, readable, flexible.

Google seems to share my enthusiasm, as they use it too in the Google App Engine. The python parser is here.

mac
  • 42,153
  • 26
  • 121
  • 131
9

Dictionaries are pretty popular as well. Basically a hash table.

{"one": 1, "two": 2} is an example, kind of looks like json.

Then you can call it up like mydict["one"], which would return 1.

Then you can use shelve to save the dictionary to a file:

mydict = shelve.open(filename)
# then you can call it from there, like
mydict["one"]

So, it's somewhat easier then an ini file. You can add stuff just like a list or change options pretty easily and then once you close it, it will write it back out.

Heres a simple example of what i mean:

import shelve

def main():
    mydict = shelve.open("testfile")
    mydict["newKey"] = value("some comment", 5)
    print(mydict["newKey"].value)
    print(mydict["newKey"].comment)
    mydict.close()


class value():
    def __init__(self, comment, value):
        self.comment = comment
        self.value = value



if __name__ == '__main__':
    main()
Matt
  • 7,049
  • 7
  • 50
  • 77
  • I guess the comment made for json is here valid as well: No comments possible?! – gecco Nov 22 '11 at 12:12
  • @gecoo not entirely. You can store an object into a dictionary. So, you can always store the value as an object and have a comment in there. So it's not exactly like json. – Matt Nov 22 '11 at 12:13
  • @geeco i updated my answer with some simple sample code. This isn't going to be in plaintext, so if you wanted to look at it, it won't be easy to read. Really just depends what you are looking for. – Matt Nov 22 '11 at 12:26
  • 2
    I do not like having configuration settings in a binary format. For two reasons: 1. You need a custom program to view and edit the settings (vs. a text editor for text-based formats) 2. The binary configuration file can not be easily version-controlled or compared with other configuration files. – codeape Nov 22 '11 at 12:44
  • @codeape i agree, but it really depends what you are trying to do. This is just another way to store key/value pairs. If he wants a simple a config file I think he should just use ConfigParser and stick with an ini or use the registry – Matt Nov 22 '11 at 12:50
  • For me, binary config files are not interesting. Nevertheless I judge your answer useful. – gecco Nov 22 '11 at 13:03
  • 11
    "Registry"? Gah! Get the garlic and holy water! – PaulMcG Nov 22 '11 at 13:04
8

This entirely depends on your requirements. If (as you say) all you need is key/value pairs, ini files (or other "plain" config files) will perfectly suit you. No, they are not outdated, as they are still in use.

XML/JSON is perfect if you have hierarchical structures and also want to use more sophisticated methods (e.g: XML file validation, namespaces, etc.).

Constantinius
  • 34,183
  • 8
  • 77
  • 85
  • 13
    I would recommend against using JSON, since (at least with Python's json parser) you cannot have comments inside JSON. – codeape Nov 22 '11 at 11:28
7

Check out ConfigObj, its the slickest method I've found so far, and definitely more flexible than ConfigParser. Personally I'm not a fan of YAML based because its 'flexibility' makes it difficult to use with tools like Augeas.

gregswift
  • 4,568
  • 1
  • 20
  • 8
  • 1
    Since i answered with this one of my co-workers published a nice wrapper around configparser, argparse, and ENV variables called [crumbs](https://pypi.python.org/pypi/crumbs). Because really, shouldn't your config be able to come from any of these 3 places without you having to wire together the different libraries on your own? – gregswift Dec 01 '14 at 22:20
5

It depends on how the config file will be used.

One of the advantages of INI files is that they are really easy to read and understand. It's much easier to make a mistake in JSON or XML file if you edit config by hand. PHP still uses INI files.

However, if you your config is not meant to be edited by hand, go with any format you like, because INI is not the easiest one to parse.

Silver Light
  • 44,202
  • 36
  • 123
  • 164
1

For completeness, you can also use a shell-style configuration format with the help of the "shlex" module. If you have a fixed set of configuration parameters then you can combine it with the "optparse" module.

from optparse import OptionParser
_o = OptionParser("%prog [options] configfiles...")
_o.add_option("--hostname", metavar="HOSTNAME", default="10.0.0.1")
_o.add_option("--username", metavar="USERNAME", default="admin")
_o.add_option("--password", metavar="PASSWORD", default="admin")

import shlex
def parse(filename, defaults):
    opt, args = _o.parse_args(shlex.split(open(filename).read()), defaults)
    return opt

if __name__ == "__main__":
    import sys
    values, args = _o.parse_args()
    for arg in args:
       values = parse(arg, values)
    values, args = _o.parse_args(values = values)
    for name in _o.defaults:
        print name, "=", getattr(values, name)

The example shows how you can chain ini files for having a default set of values and user-defined redefinitions. So assume you have two files containing

file1.ini:

--hostname 10.2.3.4
--password admin-sc

file2.ini:

--username "foo bar"
--password "special key"

Then you can run ./configtest.py file1.ini file2.ini --password other and the resulting values will have hostname as 10.2.3.4 and username as "foo bar" and password as "other". This variation for configuration settings comes in handy if you do already have an optparse-definition for your program parameters -> just reuse it and you can chain the values from the command line with the values from a config file and possibly some global config settings.

As an incentive, your configuration parameters are always documented and mistyped configuration parameters will come up early as an error, just as you can use the optparse-instance to precheck your default settings file (schema check). As a downside, there are no comments allowed in the ini and configuration elements are not easily substructured.Still your parser is essentially a one-liner.

Guido U. Draheim
  • 3,038
  • 1
  • 20
  • 19
  • You should probably use argparse instead of optionparse -- optionparse has been deprecated. – k107 Feb 27 '16 at 01:00
0

I ran into same problem more than once. I love the idea of python file(s) as settings files, it's just simple, elegant and all fellow python devs like it. Same time not really convinced with execfile idea.

So I went ahead and created Converge.

It supports some advanced options but at it's heart it simple python module as settings file.

It's as simple as

  • creating default_settings.py, prod_settings.py
  • pip install converge
  • import settings
Shekhar
  • 7,095
  • 4
  • 40
  • 45