Python comes with a handy module called ConfigParser. It’s good for creating and reading configuration files (aka INI files). However, Michael Foord (author of IronPython in Action) and Nicola Larosa decided to write their own configuration module called ConfigObj. In many ways, it is an improvement over the standard library’s module. When I first looked at ConfigObj’s home page, I thought it was well documented, but it didn’t seem to have any fully functional snippets. Since I learn from docs plus examples, I found it harder to get started using ConfigObj when the examples were unavailable. When I started writing this article, I was unaware that Michael Foord has already written his own tutorial on the subject; but I had made a promise that I would write my own, so that is what you will get to read today!

Getting Started

First of all you will need to download ConfigObj. Once you have that downloaded and installed, we can continue. Got it? Then let’s see what it can do!

To start off, open a text editor and create a file with some contents like this:


product = Sony PS3
accessories = controller, eye, memory stick
# This is a comment that will be ignored
retail_price = $400

Save it where ever you like. I’m going to call mine “config.ini”. Now let’s see how ConfigObj can be used to extract that information:


>>> from configobj import ConfigObj
>>> config = ConfigObj(r"path to config.ini")
>>> config["product"]
'Sony PS3'
>>> config["accessories"]
['controller', 'eye', 'memory stick']
>>> type(config["accessories"])
<type 'list'>

As you can see, ConfigObj uses Python’s dict API to access the information it has extracted. All you had to do to get ConfigObj to parse the file was to pass the file’s path to ConfigObj. Now, if the information had been under a section (i.e. [Sony]), then you would have had to do pre-pend everything with ["Sony"], like this: config["Sony"]["product"]. Also take note that the “accessories” section was returned as a list of strings. ConfigObj will take any valid line with a comma-separated list and return it as a Python list. You can also create multi-line strings in the config file as long as you enclose them with triple single or double quotes.

If you need to create a sub-section in the file, then use extra square brackets. For example, [Sony] is the top section, [[Playstation]] is the sub-section and [[[PS3]]] is the sub-section of the sub-section. You can create sub-sections up to any depth. For more information on the formatting of the file, I recommend the documentation linked to above.

Now we’ll do the reverse and create the config file programmatically.

import configobj
 
def createConfig(path):
    config = configobj.ConfigObj()
    config.filename = path
    config["Sony"] = {}
    config["Sony"]["product"] = "Sony PS3"
    config["Sony"]["accessories"] = ['controller', 'eye', 'memory stick']
    config["Sony"]["retail price"] = "$400"
    config.write()

As you can see, all it took was 8 lines of code. In the code above, we create a function and pass it the path for our config file. Then we create a ConfigObj object and set its filename property. To create the section, we create an empty dict with the name “Sony”. Then we pre-pend each line of the sections contents in the same way. Finally, we call our config object’s write method to write the data to the file.

Using a configspec

ConfigObj also provides a way to validate your configuration files using a configspec. When I mentioned that I was going to write this article, Steven Sproat (creator of Whyteboard) volunteered his configspec code as an example. I took his specification and used it to create a default config file. In this example, we use Foord’s validate module to do the validation. I don’t think it’s included in your ConfigObj download, so you may need to download it as well. Now, let’s take a look at the code:

import configobj, validate
 
cfg = """
bmp_select_transparent = boolean(default=False)
canvas_border = integer(min=10, max=35, default=15)
colour1 = list(min=3, max=3, default=list('280', '0', '0'))
colour2 = list(min=3, max=3, default=list('255', '255', '0'))
colour3 = list(min=3, max=3, default=list('0', '255', '0'))
colour4 = list(min=3, max=3, default=list('255', '0', '0'))
colour5 = list(min=3, max=3, default=list('0', '0', '255'))
colour6 = list(min=3, max=3, default=list('160', '32', '240'))
colour7 = list(min=3, max=3, default=list('0', '255', '255'))
colour8 = list(min=3, max=3, default=list('255', '165', '0'))
colour9 = list(min=3, max=3, default=list('211', '211', '211'))
convert_quality = option('highest', 'high', 'normal', default='normal')
default_font = string
default_width = integer(min=1, max=12000, default=640)
default_height = integer(min=1, max=12000, default=480)
imagemagick_path = string
handle_size = integer(min=3, max=15, default=6)
language = option('English', 'English (United Kingdom)', 'Russian', 'Hindi', default='English')
print_title = boolean(default=True)
statusbar = boolean(default=True)
toolbar = boolean(default=True)
toolbox = option('icon', 'text', default='icon')
undo_sheets = integer(min=5, max=50, default=10)
"""
 
def createConfig(path):
    """
    Create a config file using a configspec
    and validate it against a Validator object
    """
    spec = cfg.split("\n")
    config = configobj.ConfigObj(path, configspec=spec)
    validator = validate.Validator()
    config.validate(validator, copy=True)
    config.filename = path
    config.write()
 
if __name__ == "__main__":
    createConfig("config.ini")

If you go and look at Steven’s original configspec, you’ll notice I shortened his list of languages quite a bit. I did this to make the code easier to read. Anyway, the configspec allows the programmer the ability to specify what types are returned for each line in the configuration file. It also can be used to set a default value and a min and max values (among other things). If you run the code above, you will see a “config.ini” file generated in the current working directory that has just the default values. If the programmer didn’t specify a default, then that line isn’t even added to the configuration.

Let’s take a closer look at what’s going on just to make sure you understand. In the createConfig function, we create a ConfigObj instance by passing in the file path and setting the configspec. Note that the configspec can also be a normal text file or a python file rather than the string that is in this example. Next, we create a Validator object. Normal usage is to just call config.validate(validator), but in this code I set the copy argument to True so that I could create a file. Otherwise, all it would do is validate that the file I passed in fit the configspec’s rules. Finally I set the config’s filename and write the data out.

Wrapping Up

Now you know just enough to get you started on the ins and outs of ConfigObj. I hope you’ll find it as helpful as I have. There’s lots more to learn, so be sure to check out some of the links below.

Note: All code tested on Windows XP with Python 2.5, ConfigObj 4.6.0, and Validate 1.0.0.

Further Reading

Download the Source

Print Friendly