90

How can I convert the result of a ConfigParser.items('section') to a dictionary to format a string like here:

import ConfigParser

config = ConfigParser.ConfigParser()
config.read('conf.ini')

connection_string = ("dbname='%(dbname)s' user='%(dbuser)s' host='%(host)s' "
                     "password='%(password)s' port='%(port)s'")

print connection_string % config.items('db')
martineau
  • 119,623
  • 25
  • 170
  • 301
Szymon Lipiński
  • 27,098
  • 17
  • 75
  • 77
  • 5
    Be careful to use _sections: this variable is not documented, so it is not guaranteed to work in future versions of python – Bertera Mar 30 '11 at 09:26
  • 1
    This is valid for python 2, but if you're using python 3 you can just unpack the subscripted config into format(). "your {pattern}".format(**config['db']) – Hovis Biddle Mar 09 '15 at 23:13

9 Answers9

110

Have you tried

print connection_string % dict(config.items('db'))

?

Ian Clelland
  • 43,011
  • 8
  • 86
  • 87
  • right. this worked for me, while the approved one somehow didn't .. maybe it's a Python version thing .. – Ricky Levi Jan 28 '14 at 10:53
  • 10
    @Ricky, I guess the user is not supposed to access _sections. – Dacav Apr 30 '14 at 16:14
  • @Dacav yeah it's protected so you aren't supposed to, but it was a good contribution because it allows you to do it without dict comprehension (like this one) and maybe more people will learn about protected/private from this. – Hexiro Mar 26 '21 at 16:28
99

How I did it in just one line.

my_config_parser_dict = {s:dict(config.items(s)) for s in config.sections()}

No more than other answers but when it is not the real businesses of your method and you need it just in one place use less lines and take the power of dict comprehension could be useful.

martineau
  • 119,623
  • 25
  • 170
  • 301
Michele d'Amico
  • 22,111
  • 8
  • 69
  • 76
  • 4
    elegant and portable. – Jacob Lee Aug 10 '18 at 20:34
  • @DanielBraun: I believe James Kyle's [answer](https://stackoverflow.com/a/23944270/355230) is more correct. – martineau Aug 13 '18 at 00:02
  • love this answer. – SW_user2953243 Jan 29 '20 at 16:22
  • 2
    This really should be the accepted answer. This provides a very pythonic solution without accessing private attributes. – Aaron Ciuffo Nov 06 '20 at 08:52
  • The problem with using sections instead of keys is that it doesn't include the 'DEFAULT' section. you can entries that do not have a section which automatically get put into the 'DEFAULT' section. Using `{s:dict(config.items(s)) for s in config.keys()}` seems to be safer but it really all depends on the application. – Paul Randleman May 30 '22 at 13:25
77

This is actually already done for you in config._sections. Example:

$ cat test.ini
[First Section]
var = value
key = item

[Second Section]
othervar = othervalue
otherkey = otheritem

And then:

>>> from ConfigParser import ConfigParser
>>> config = ConfigParser()
>>> config.read('test.ini')
>>> config._sections
{'First Section': {'var': 'value', '__name__': 'First Section', 'key': 'item'}, 'Second Section': {'__name__': 'Second Section', 'otherkey': 'otheritem', 'othervar': 'othervalue'}}
>>> config._sections['First Section']
{'var': 'value', '__name__': 'First Section', 'key': 'item'}

Edit: My solution to the same problem was downvoted so I'll further illustrate how my answer does the same thing without having to pass the section thru dict(), because config._sections is provided by the module for you already.

Example test.ini:

[db]
dbname = testdb
dbuser = test_user
host   = localhost
password = abc123
port   = 3306

Magic happening:

>>> config.read('test.ini')
['test.ini']
>>> config._sections
{'db': {'dbname': 'testdb', 'host': 'localhost', 'dbuser': 'test_user', '__name__': 'db', 'password': 'abc123', 'port': '3306'}}
>>> connection_string = "dbname='%(dbname)s' user='%(dbuser)s' host='%(host)s' password='%(password)s' port='%(port)s'"
>>> connection_string % config._sections['db']
"dbname='testdb' user='test_user' host='localhost' password='abc123' port='3306'"

So this solution is not wrong, and it actually requires one less step. Thanks for stopping by!

jathanism
  • 33,067
  • 9
  • 68
  • 86
  • 2
    This is wrong ways, since it doesn't account defaults and variables are not interpolated. – Denis Otkidach Nov 21 '09 at 16:59
  • 3
    I'm not saying it is wrong, but I can't use this, because I tested it with the config parser's built in replacement %(syntax)s For the unaware this allows use of config values in other config values. These are not expanded in the _sections member, but are via the items() function. – AlwaysTraining Apr 05 '13 at 17:55
  • 2
    The answer by @jathanism is correct if and only if you don't need the defaults-and-interpolation behavior provided by `ConfigParser`. In other words, that answer is equivalent to `ConfigParser` when its *raw* options are used; and not equivalent to the non-raw options. The follow-up ("Magic happening") comment shows how to work around the interpolation issue, but not the defaults issue. Bottom line: config._sections works well if and only if the config file has the flat / final version of each value specified; otherwise does not. – Chris Johnson Jun 30 '13 at 15:30
  • 72
    Using _sections is dangerous. Not exposed in the API, not future proof. – johnharris85 Aug 17 '13 at 13:08
  • 49
    This is a private API and terrible advice. – acdx May 22 '15 at 11:43
  • 15
    anyone following this advice is making life so much harder for maintainers of ConfigParser if they decide to change how the internals work, it makes me sad that people recommend to use private APIs – Aprillion Aug 19 '15 at 08:14
  • 9
    Everybody calm down. It's been six years and the API has yet to change. Now if and when the public API actually IMPROVES and is more user friendly I'll update this answer. – jathanism Aug 19 '15 at 16:09
  • 5
    @jathanism the public API is harder to improve while people are mis-using `_sections` as you suggest. – groovecoder Nov 15 '16 at 20:28
  • 2
    The point being is that you shouldn't access these outside the module itself. It is like private variable. – Peter Majko Jul 26 '17 at 18:31
  • 1
    Another reason this solution is not ideal: When reading an ini file, any values under a `[DEFAULT]` section will be missed from the `_sections` field. Michele d'Amico's solution however, will include them as expected. – Daniel Braun Mar 20 '18 at 12:27
  • What `ConfigParser`'s maintainers should do seeing this answer is provide a public api – Jay Jun 13 '20 at 07:48
  • Looks like the public API has changed. `dict(self) | Return a deepcopy of self as a dictionary. | | All members that are ``Section`` instances are recursively turned to | ordinary dictionaries - by calling their ``dict`` method.` – Kevin Südmersen Apr 28 '21 at 09:01
  • A word of warning to all: regardless of whether this answer or other (https://stackoverflow.com/a/28990982/7530580) approaches are used, if the sections part of the configparser is used, all the keys of sections (2nd level) will be converted to all lower case. – krg Apr 28 '21 at 14:48
  • Any members using `_` are intended to be private. That's what the `_` means. It's for internal use only. It's not only about future-proofing the API, it's also about thread safety, and so on. You're violating the contract between yourself and the developers, if you ignore the `_` and use it anyway. – Edward Ned Harvey Aug 11 '21 at 18:50
27

I know this was asked a long time ago and a solution chosen, but the solution selected does not take into account defaults and variable substitution. Since it's the first hit when searching for creating dicts from parsers, thought I'd post my solution which does include default and variable substitutions by using ConfigParser.items().

from ConfigParser import SafeConfigParser
defaults = {'kone': 'oneval', 'ktwo': 'twoval'}
parser = SafeConfigParser(defaults=defaults)
parser.set('section1', 'kone', 'new-val-one')
parser.add_section('section1')
parser.set('section1', 'kone', 'new-val-one')
parser.get('section1', 'ktwo')
parser.add_section('section2')
parser.get('section2', 'kone')
parser.set('section2', 'kthree', 'threeval')
parser.items('section2')
thedict = {}
for section in parser.sections():
    thedict[section] = {}
    for key, val in parser.items(section):
        thedict[section][key] = val
thedict
{'section2': {'ktwo': 'twoval', 'kthree': 'threeval', 'kone': 'oneval'}, 'section1': {'ktwo': 'twoval', 'kone': 'new-val-one'}}

A convenience function to do this might look something like:

def as_dict(config):
    """
    Converts a ConfigParser object into a dictionary.

    The resulting dictionary has sections as keys which point to a dict of the
    sections options as key => value pairs.
    """
    the_dict = {}
    for section in config.sections():
        the_dict[section] = {}
        for key, val in config.items(section):
            the_dict[section][key] = val
    return the_dict
James Kyle
  • 464
  • 5
  • 12
14

For an individual section, e.g. "general", you can do:

dict(parser['general'])
Keith Hughitt
  • 4,860
  • 5
  • 49
  • 54
  • It is the simplest solution for reading a config-based database configuration. Which is what [I believe] Szymon Lipiński wanted to achieve. – WHS May 27 '20 at 09:04
  • This answer should be the correct one. Simple, correct and doesn't use protected fields – Alessandro Muzzi Mar 02 '23 at 10:57
7

In Python +3.6 you could do this

file.ini

[SECTION1]
one = 1
two = 2

[SECTION2]
foo = Hello
bar = World

[SECTION3]
param1 = parameter one
param2 = parameter two

file.py

import configparser

cfg = configparser.ConfigParser()
cfg.read('file.ini')
# Get one section in a dict
numbers = {k:v for k, v in cfg['SECTION1'].items()}

If you need all sections listed you should use cfg.sections()

crisconru
  • 170
  • 2
  • 7
6

Another alternative would be:

config.ini

[DEFAULT]
potato=3

[foo]
foor_property=y
potato=4


[bar]
bar_property=y

parser.py

import configparser
from typing import Dict


def to_dict(config: configparser.ConfigParser) -> Dict[str, Dict[str, str]]:
    """
    function converts a ConfigParser structure into a nested dict
    Each section name is a first level key in the the dict, and the key values of the section
    becomes the dict in the second level
    {
        'section_name': {
            'key': 'value'
        }
    }
    :param config:  the ConfigParser with the file already loaded
    :return: a nested dict
    """
    return {section_name: dict(config[section_name]) for section_name in config.sections()}

main.py

import configparser

from parser import to_dict


def main():
    config = configparser.ConfigParser()
    # By default section names are parsed to lower case, optionxform = str sets to no conversion.
    # For more information: https://docs.python.org/3/library/configparser.html#configparser-objects
    # config.optionxform = str
    config.read('config.ini')
    print(f'Config read: {to_dict(config)}')
    print(f'Defaults read: {config.defaults()}')


if __name__ == '__main__':
    main()
4

Combining Michele d'Amico and Kyle's answer (no dict), produces a less readable but somehow compelling:

{i: {i[0]: i[1] for i in config.items(i)} for i in config.sections()}
fzzylogic
  • 2,183
  • 1
  • 19
  • 25
2

Here is another approach using Python 3.7 with configparser and ast.literal_eval:

game.ini

[assets]
tileset = {0:(32, 446, 48, 48), 
           1:(96, 446, 16, 48)}

game.py

import configparser
from ast import literal_eval

config = configparser.ConfigParser()
config.read('game.ini')

# convert a string to dict
tileset = literal_eval(config['assets']['tileset'])

print('tileset:', tileset)
print('type(tileset):', type(tileset))

output

tileset: {0: (32, 446, 48, 48), 1: (96, 446, 16, 48)}
type(tileset): <class 'dict'>
rbento
  • 9,919
  • 3
  • 61
  • 61