How to read and write INI file with Python3?
Asked Answered
X

10

203

I need to read, write and create an INI file with Python3.

FILE.INI

default_path = "/path/name/"
default_file = "file.txt"

Python File:

#    Read file and and create if it not exists
config = iniFile( 'FILE.INI' )

#    Get "default_path"
config.default_path

#    Print (string)/path/name
print config.default_path

#    Create or Update
config.append( 'default_path', 'var/shared/' )
config.append( 'default_message', 'Hey! help me!!' )

UPDATED FILE.INI

default_path    = "var/shared/"
default_file    = "file.txt"
default_message = "Hey! help me!!"
Xl answered 16/1, 2012 at 17:54 Comment(4)
How about docs.python.org/library/configparser.html?Trailblazer
In fact, how about https://mcmap.net/q/129424/-read-all-the-contents-in-ini-file-into-dictionary-with-python?Tusche
a proper ini file needs a section heading like [foobar].Elsa
see also #19078670Ramshackle
S
257

This can be something to start with:

import configparser

config = configparser.ConfigParser()
config.read('FILE.INI')
print(config['DEFAULT']['path'])     # -> "/path/name/"
config['DEFAULT']['path'] = '/var/shared/'    # update
config['DEFAULT']['default_message'] = 'Hey! help me!!'   # create

with open('FILE.INI', 'w') as configfile:    # save
    config.write(configfile)

You can find more at the official configparser documentation.

Silberman answered 16/1, 2012 at 18:34 Comment(1)
Gives configparser.MissingSectionHeaderError when using provided example files without the proper section headers.Fallow
C
127

Here's a complete read, update and write example.

Input file, test.ini

[section_a]
string_val = hello
bool_val = false
int_val = 11
pi_val = 3.14

Working code.

try:
    from configparser import ConfigParser
except ImportError:
    from ConfigParser import ConfigParser  # ver. < 3.0

# instantiate
config = ConfigParser()

# parse existing file
config.read('test.ini')

# read values from a section
string_val = config.get('section_a', 'string_val')
bool_val = config.getboolean('section_a', 'bool_val')
int_val = config.getint('section_a', 'int_val')
float_val = config.getfloat('section_a', 'pi_val')

# update existing value
config.set('section_a', 'string_val', 'world')

# add a new section and some values
config.add_section('section_b')
config.set('section_b', 'meal_val', 'spam')
config.set('section_b', 'not_found_val', '404')

# save to a file
with open('test_update.ini', 'w') as configfile:
    config.write(configfile)

Output file, test_update.ini

[section_a]
string_val = world
bool_val = false
int_val = 11
pi_val = 3.14

[section_b]
meal_val = spam
not_found_val = 404

The original input file remains untouched.

Cannoneer answered 6/4, 2015 at 20:55 Comment(2)
On my Python 3.7 system, the line "config.set('section_b', 'not_found_val', 404)" had to be changed to "config.set('section_b', 'not_found_val', str(404))" because the parameters for "set" have to be strings. Excellent example, thanks!Mme
looks like the read method now returns a list of read files / file, but not the contentHagai
R
14

http://docs.python.org/library/configparser.html

Python's standard library might be helpful in this case.

Roncesvalles answered 16/1, 2012 at 18:3 Comment(0)
F
11

The standard ConfigParser normally requires access via config['section_name']['key'], which is no fun. A little modification can deliver attribute access:

class AttrDict(dict):
    def __init__(self, *args, **kwargs):
        super(AttrDict, self).__init__(*args, **kwargs)
        self.__dict__ = self

AttrDict is a class derived from dict which allows access via both dictionary keys and attribute access: that means a.x is a['x']

We can use this class in ConfigParser:

config = configparser.ConfigParser(dict_type=AttrDict)
config.read('application.ini')

and now we get application.ini with:

[general]
key = value

as

>>> config._sections.general.key
'value'
Forbis answered 28/4, 2015 at 16:56 Comment(1)
nice trick, but the users of this method should take care, that when accessing like config._sections.general.key = "3" this is not changing the internal value of the config option and therefore can only be used for read only access. If after the .read() command the config is extended or changed (add options,value pairs for some sections, -> which does interpolation which might be very important ) this access method should not be used! Also any access to config._sections["section"]["opt"] which is private circumvents interpolation and returns the raw values!Bim
S
8

contents in my backup_settings.ini file

[Settings]
year = 2020

python code for reading

import configparser
config = configparser.ConfigParser()
config.read('backup_settings.ini') #path of your .ini file
year = config.get("Settings","year") 
print(year)

for writing or updating

from pathlib import Path
import configparser
myfile = Path('backup_settings.ini')  #Path of your .ini file
config.read(myfile)
config.set('Settings', 'year','2050') #Updating existing entry 
config.set('Settings', 'day','sunday') #Writing new entry
config.write(myfile.open("w"))

output

[Settings]
year = 2050
day = sunday
Sonorous answered 18/4, 2020 at 10:35 Comment(0)
U
7

ConfigObj is a good alternative to ConfigParser which offers a lot more flexibility:

  • Nested sections (subsections), to any level
  • List values
  • Multiple line values
  • String interpolation (substitution)
  • Integrated with a powerful validation system including automatic type checking/conversion repeated sections and allowing default values
  • When writing out config files, ConfigObj preserves all comments and the order of members and sections
  • Many useful methods and options for working with configuration files (like the 'reload' method)
  • Full Unicode support

It has some draw backs:

  • You cannot set the delimiter, it has to be =… (pull request)
  • You cannot have empty values, well you can but they look liked: fuabr = instead of just fubar which looks weird and wrong.
Unseat answered 2/9, 2016 at 7:44 Comment(2)
Sardathrion is right, ConfigObj is the way to go if you want to keep the comments in the file and the section order as in the original file. ConfigParser will just clear your comments and will also scramble the order at some point.Corso
cant find support for multi-line lists eg long file namesNoguchi
C
4

You could use python-benedict, it's a dict subclass that provides normalized I/O support for most common formats, including ini.

from benedict import benedict

# path can be a ini string, a filepath or a remote url
path = 'path/to/config.ini'

d = benedict.from_ini(path)

# do stuff with your dict
# ...

# write it back to disk
d.to_ini(filepath=path)

It's well tested and documented, check the README to see all the features:

Documentation: https://github.com/fabiocaccamo/python-benedict

Installation: pip install python-benedict

Note: I am the author of this project

Copyholder answered 4/5, 2021 at 21:59 Comment(0)
T
3

There are some problems I found when used configparser such as - I got an error when I tryed to get value from param:

destination=\my-server\backup$%USERNAME%

It was because parser can't get this value with special character '%'. And then I wrote a parser for reading ini files based on 're' module:

import re

# read from ini file.
def ini_read(ini_file, key):
    value = None
    with open(ini_file, 'r') as f:
        for line in f:
            match = re.match(r'^ *' + key + ' *= *.*$', line, re.M | re.I)
            if match:
                value = match.group()
                value = re.sub(r'^ *' + key + ' *= *', '', value)
                break
    return value


# read value for a key 'destination' from 'c:/myconfig.ini'
my_value_1 = ini_read('c:/myconfig.ini', 'destination')

# read value for a key 'create_destination_folder' from 'c:/myconfig.ini'
my_value_2 = ini_read('c:/myconfig.ini', 'create_destination_folder')


# write to an ini file.
def ini_write(ini_file, key, value, add_new=False):
    line_number = 0
    match_found = False
    with open(ini_file, 'r') as f:
        lines = f.read().splitlines()
    for line in lines:
        if re.match(r'^ *' + key + ' *= *.*$', line, re.M | re.I):
            match_found = True
            break
        line_number += 1
    if match_found:
        lines[line_number] = key + ' = ' + value
        with open(ini_file, 'w') as f:
            for line in lines:
                f.write(line + '\n')
        return True
    elif add_new:
        with open(ini_file, 'a') as f:
            f.write(key + ' = ' + value)
        return True
    return False


# change a value for a key 'destination'.
ini_write('my_config.ini', 'destination', '//server/backups$/%USERNAME%')

# change a value for a key 'create_destination_folder'
ini_write('my_config.ini', 'create_destination_folder', 'True')

# to add a new key, we need to use 'add_new=True' option.
ini_write('my_config.ini', 'extra_new_param', 'True', True)
Toed answered 15/10, 2020 at 2:24 Comment(0)
G
1

Use nested dictionaries. Take a look:

INI File: example.ini

[Section]
Key = Value

Code:

class IniOpen:
    def __init__(self, file):
        self.parse = {}
        self.file = file
        self.open = open(file, "r")
        self.f_read = self.open.read()
        split_content = self.f_read.split("\n")

        section = ""
        pairs = ""

        for i in range(len(split_content)):
            if split_content[i].find("[") != -1:
                section = split_content[i]
                section = string_between(section, "[", "]")  # define your own function
                self.parse.update({section: {}})
            elif split_content[i].find("[") == -1 and split_content[i].find("="):
                pairs = split_content[i]
                split_pairs = pairs.split("=")
                key = split_pairs[0].trim()
                value = split_pairs[1].trim()
                self.parse[section].update({key: value})

    def read(self, section, key):
        try:
            return self.parse[section][key]
        except KeyError:
            return "Sepcified Key Not Found!"

    def write(self, section, key, value):
        if self.parse.get(section) is  None:
            self.parse.update({section: {}})
        elif self.parse.get(section) is not None:
            if self.parse[section].get(key) is None:
                self.parse[section].update({key: value})
            elif self.parse[section].get(key) is not None:
                return "Content Already Exists"

Apply code like so:

ini_file = IniOpen("example.ini")
print(ini_file.parse) # prints the entire nested dictionary
print(ini_file.read("Section", "Key") # >> Returns Value
ini_file.write("NewSection", "NewKey", "New Value"
Gigantism answered 21/12, 2020 at 20:53 Comment(0)
A
0

If—like in the question—you neither have section headers nor have any nesting, then this simple one liner will create a dict for you:

with open("/etc/os-release", "rt") as f:
    conf = dict((lambda l: (l[0], l[2][:-1].strip('"')))(line.partition("="))
                for line in f)
{
    "SUPPORT_URL": "https://help.ubuntu.com/", 
    "UBUNTU_CODENAME": "noble", 
    "PRIVACY_POLICY_URL":
        "https://www.ubuntu.com/legal/terms-and-policies/privacy-policy", 
    "NAME": "Ubuntu", 
    "VERSION_CODENAME": "noble", 
    "ID_LIKE": "debian", 
    "VERSION_ID": "24.04", 
    "BUG_REPORT_URL": "https://bugs.launchpad.net/ubuntu/", 
    "PRETTY_NAME": "Ubuntu Noble Numbat (development branch)", 
    "VERSION": "24.04 (Noble Numbat)", 
    "LOGO": "ubuntu-logo", 
    "HOME_URL": "https://www.ubuntu.com/", 
    "ID": "ubuntu"
}

(if you want conf.ID rather than conf["ID"]; you can construct a class using type; or try a namedtuple or—if you restrict support to Python 3.7+—dataclasses)

Abrahamabrahams answered 2/4 at 0:58 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.