GeezerVisonThe World Wide Web, restored to 800 pixels of glory. |
| Home|About|Gaming cabinet|Archives|How it works|Starter sites |
GeezerVison is showing zetcode.com — Python ConfigParser - working with configuration files Python ConfigParserlast modified October 30, 2026 Python 3.15
In this article we show how to work with configuration files in Python with
Python ConfigParser
A configuration file consists of sections followed by key/value pairs. Section
names are written in
Reading a fileIn the first example, we read configuration data from a file. db.ini
; database connections [mysql] host = localhost user = user7 passwd = s$cret db = ydb [postgresql] host = localhost port = 5432 user = user8 passwd = mypwd$7 db = testdb We have two sections of configuration data and a comment. reading_from_file.py
import configparser
config = configparser.ConfigParser()
found = config.read('db.ini')
print(found)
mysql = config['mysql']
print('MySQL configuration:')
print(f"Host: {mysql['host']}")
print(f"User: {mysql['user']}")
print(f"Database: {mysql['db']}")
pg = config['postgresql']
print('PostgreSQL configuration:')
print(f"Host: {pg['host']}")
print(f"Port: {pg['port']}")
print(f"Database: {pg['db']}")
print(config.read('missing.ini'))
The example reads configuration data for MySQL and PostgreSQL. config = configparser.ConfigParser()
found = config.read('db.ini')
We create a mysql = config['mysql']
print(f"Host: {mysql['host']}")
The parser behaves like a dictionary of sections, and each section like a dictionary of options. print(config.read('missing.ini'))
A missing file is not an error; $ python reading_from_file.py ['db.ini'] MySQL configuration: Host: localhost User: user7 Database: ydb PostgreSQL configuration: Host: localhost Port: 5432 Database: testdb [] Sections and options
The sections.py
import configparser
config = configparser.ConfigParser()
config.read('db.ini')
print(config.sections())
for name in ['mysql', 'postgresql', 'sqlite']:
print(name, config.has_section(name), name in config)
for key, value in config['postgresql'].items():
print(f'{key} = {value}')
print(config.has_option('mysql', 'port'))
print(config.options('mysql'))
We inspect the structure of the print(name, config.has_section(name), name in config)
The for key, value in config['postgresql'].items(): We iterate over the options of a section. print(config.has_option('mysql', 'port'))
print(config.options('mysql'))
The $ python sections.py ['mysql', 'postgresql'] mysql True True postgresql True True sqlite False False host = localhost port = 5432 user = user8 passwd = mypwd$7 db = testdb False ['host', 'user', 'passwd', 'db'] Typed values
Since all values are strings, the parser provides app.ini
[server] Port = 8080 timeout = 2.5 debug = yes workers = 4 [cache] enabled = off
The file contains numbers and boolean flags. The typed_values.py
import configparser
config = configparser.ConfigParser()
config.read('app.ini')
server = config['server']
port = server.getint('port')
timeout = server.getfloat('timeout')
debug = server.getboolean('debug')
print(port, type(port).__name__)
print(timeout, type(timeout).__name__)
print(debug, type(debug).__name__)
print(config.getboolean('cache', 'enabled'))
print(server['PORT'], type(server['port']).__name__)
print(server.getint('max_clients', fallback=100))
print(server.get('host', 'localhost'))
try:
config.getint('server', 'debug')
except ValueError as e:
print(e)
We read the values with the proper types. debug = server.getboolean('debug')
...
print(config.getboolean('cache', 'enabled'))
The print(server['PORT'], type(server['port']).__name__)
Keys are case-insensitive; print(server.getint('max_clients', fallback=100))
print(server.get('host', 'localhost'))
The try:
config.getint('server', 'debug')
except ValueError as e:
print(e)
A value that cannot be converted raises a $ python typed_values.py 8080 int 2.5 float True bool False 8080 str 100 localhost invalid literal for int() with base 10: 'yes' The DEFAULT section
The special defaults.ini
[DEFAULT] host = localhost timeout = 30 [mysql] user = user7 [postgresql] host = db.example.com user = user8
Both sections inherit default_section.py
import configparser
config = configparser.ConfigParser()
config.read('defaults.ini')
print(config.sections())
for name in config.sections():
sec = config[name]
print(f"{name}: {sec['user']}@{sec['host']} timeout={sec['timeout']}")
print(dict(config.defaults()))
We print the effective values of each section. print(config.sections())
The print(dict(config.defaults()))
The $ python default_section.py ['mysql', 'postgresql'] mysql: user7@localhost timeout=30 postgresql: [email protected] timeout=30 {'host': 'localhost', 'timeout': '30'} Reading strings and dictionaries
The read_from_string.py
import configparser
cfg_data = '''
[mysql]
host = localhost
user = user7
db = ydb
'''
config = configparser.ConfigParser()
config.read_string(cfg_data)
print(dict(config['mysql']))
config.read_dict({
'mysql': {'port': 3306},
'redis': {'host': 'localhost', 'port': 6379},
})
print(config.sections())
print(dict(config['mysql']))
print(config['redis']['port'], type(config['redis']['port']).__name__)
We combine both sources in one parser. config.read_dict({
'mysql': {'port': 3306},
'redis': {'host': 'localhost', 'port': 6379},
})
Keys of the outer dictionary are section names; the inner dictionaries hold
the options. New options are added to existing sections. Values are converted
to strings, so the port of Redis comes back as $ python read_from_string.py
{'host': 'localhost', 'user': 'user7', 'db': 'ydb'}
['mysql', 'redis']
{'host': 'localhost', 'user': 'user7', 'db': 'ydb', 'port': '3306'}
6379 str
Writing configuration
The writing.py
import configparser
config = configparser.ConfigParser()
config['mysql'] = {'host': 'localhost', 'user': 'user7', 'db': 'ydb'}
config.add_section('redis')
config.set('redis', 'host', 'localhost')
config['redis']['port'] = '6379'
config['mysql']['port'] = '3306'
config.remove_option('mysql', 'db')
with open('generated.ini', 'w') as f:
config.write(f)
We create a configuration in code and save it. config['mysql'] = {'host': 'localhost', 'user': 'user7', 'db': 'ydb'}
Assigning a dictionary creates a whole section. config.add_section('redis')
config.set('redis', 'host', 'localhost')
config['redis']['port'] = '6379'
Alternatively, we add an empty section and set the options one by one. The
config.remove_option('mysql', 'db')
Options and sections are deleted with $ python writing.py $ cat generated.ini [mysql] host = localhost user = user7 port = 3306 [redis] host = localhost port = 6379 Interpolation
By default, cfg.ini
[info] users_dir = C:\Users name = Jano home_dir = %(users_dir)s\%(name)s
We build the interpolation.py
import configparser
config = configparser.ConfigParser()
config.read('cfg.ini')
info = config['info']
print(info['users_dir'])
print(info['name'])
print(info['home_dir'])
print(info.get('home_dir', raw=True))
The example reads the values and prints them. print(info.get('home_dir', raw=True))
With $ python interpolation.py C:\Users Jano C:\Users\Jano %(users_dir)s\%(name)s The percent sign
With basic interpolation, a literal percent sign must be written as
percent.py
import configparser
data = '''
[report]
title = Sales up 50%
note = 100%% done
'''
config = configparser.ConfigParser()
config.read_string(data)
try:
print(config['report']['title'])
except configparser.InterpolationSyntaxError as e:
print(e)
print(config['report']['note'])
raw = configparser.ConfigParser(interpolation=None)
raw.read_string(data)
print(raw['report']['title'])
print(raw['report']['note'])
We read values with a percent sign. try:
print(config['report']['title'])
except configparser.InterpolationSyntaxError as e:
print(e)
The value raw = configparser.ConfigParser(interpolation=None)
If a file never uses interpolation, it is simplest to turn it off. Then every
value is returned exactly as written, including $ python percent.py
'%' must be followed by '%' or '(', found: '%'
100% done
Sales up 50%
100%% done
Extended interpolation
paths.ini
[common]
base = /opt/myapp
[paths]
logs = ${common:base}/logs
data = ${common:base}/data
archive = ${data}/archive
The paths are built from a base directory defined in another section. extended.py
import configparser
config = configparser.ConfigParser(
interpolation=configparser.ExtendedInterpolation())
config.read('paths.ini')
for key, value in config['paths'].items():
print(f'{key:8} {value}')
We pass an archive = ${data}/archive
References are resolved recursively; $ python extended.py logs /opt/myapp/logs data /opt/myapp/data archive /opt/myapp/data/archive Special valuesSeveral constructor options relax the format: keys without values, values spanning several lines, inline comments, and options before the first section. features.ini
version = 2
[mysqld]
user = mysql
skip-networking
# comment line
hosts =
web1.example.com
web2.example.com
web3.example.com
pool = 10 ; inline comment
The file looks like a MySQL configuration file. special.py
import configparser
config = configparser.ConfigParser(
allow_no_value=True,
allow_unnamed_section=True,
inline_comment_prefixes=(';',),
)
config.read('features.ini')
print(config.sections())
print(config[configparser.UNNAMED_SECTION]['version'])
mysqld = config['mysqld']
print(mysqld['skip-networking'])
print('skip-networking' in mysqld)
print(mysqld['hosts'].split())
print(repr(mysqld['pool']))
We enable the options needed to read the file. config = configparser.ConfigParser(
allow_no_value=True,
allow_unnamed_section=True,
inline_comment_prefixes=(';',),
)
The print(mysqld['hosts'].split())
Indented lines continue the previous value. The value of $ python special.py [<UNNAMED_SECTION>, 'mysqld'] 2 None True ['web1.example.com', 'web2.example.com', 'web3.example.com'] '10' Custom converters
The converters.py
import configparser
data = '''
[web]
origins = localhost, example.com, test.example.com
ports = 80, 443, 8080
'''
def to_list(value):
return [item.strip() for item in value.split(',')]
config = configparser.ConfigParser(converters={'list': to_list})
config.read_string(data)
web = config['web']
print(web.getlist('origins'))
print([int(p) for p in config.getlist('web', 'ports')])
We add a config = configparser.ConfigParser(converters={'list': to_list})
The key $ python converters.py ['localhost', 'example.com', 'test.example.com'] [80, 443, 8080] Layered configuration
The base.ini
[server] host = 0.0.0.0 port = 8080 debug = no The base configuration is shipped with the application. local.ini
[server] debug = yes
The local file overrides only the layered.py
import configparser config = configparser.ConfigParser() loaded = config.read(['base.ini', 'local.ini', 'missing.ini']) print(loaded) print(dict(config['server'])) We read the base file, the local file, and a file that does not exist. loaded = config.read(['base.ini', 'local.ini', 'missing.ini']) Missing files are skipped, so optional configuration files need no special handling. $ python layered.py
['base.ini', 'local.ini']
{'host': '0.0.0.0', 'port': '8080', 'debug': 'yes'}
SourcePython configparser documentation
In this article we have used AuthorMy name is Jan Bodnar, and I am a passionate programmer with extensive programming experience. I have been writing programming articles since 2007. To date, I have authored over 1,400 articles and 8 e-books. I possess more than ten years of experience in teaching programming. List all Python tutorials. |
| You are visitor 01402520 — GeezerVison does not store the pages you look up. Best viewed with Netscape Navigator 4.7 or Internet Explorer 6 at 800×600. No cookies required. |