The configparser module handles INI-style configuration files—the [section] and key = value format you see in many applications.
Reading Config Files
Given a config file config.ini:
[database]
host = localhost
port = 5432
name = myapp
[logging]
level = INFO
file = /var/log/app.logRead it:
import configparser
config = configparser.ConfigParser()
config.read('config.ini')
# Access values
host = config['database']['host']
port = config.getint('database', 'port')
log_level = config.get('logging', 'level')
print(host) # localhost
print(port) # 5432 (as int)
print(log_level) # INFOType Conversion
All values are strings by default. Use getters for types:
# Typed getters
port = config.getint('database', 'port')
debug = config.getboolean('app', 'debug')
ratio = config.getfloat('app', 'ratio')
# Boolean values: yes/no, on/off, true/false, 1/0
# All work with getboolean()Config file:
[app]
debug = yes
ratio = 0.75Default Values
# Fallback if key doesn't exist
timeout = config.getint('database', 'timeout', fallback=30)
# Check existence
if config.has_option('database', 'ssl'):
ssl = config.getboolean('database', 'ssl')Writing Config Files
config = configparser.ConfigParser()
# Add sections and values
config['database'] = {
'host': 'localhost',
'port': '5432',
'name': 'myapp'
}
config['logging'] = {}
config['logging']['level'] = 'DEBUG'
config['logging']['file'] = '/var/log/app.log'
# Write to file
with open('config.ini', 'w') as f:
config.write(f)Interpolation
Reference other values within the config:
# config.ini
[paths]
base = /opt/app
data = %(base)s/data
logs = %(base)s/logsconfig = configparser.ConfigParser()
config.read('config.ini')
print(config['paths']['data']) # /opt/app/data
print(config['paths']['logs']) # /opt/app/logsExtended interpolation for cross-section references:
config = configparser.ConfigParser(
interpolation=configparser.ExtendedInterpolation()
)[common]
base_dir = /opt/app
[database]
path = ${common:base_dir}/dbDEFAULT Section
Values in [DEFAULT] apply to all sections:
[DEFAULT]
timeout = 30
retry = 3
[database]
host = localhost
# timeout and retry are inherited
[cache]
host = redis
# timeout and retry are inherited# Both sections have timeout=30
db_timeout = config.getint('database', 'timeout')
cache_timeout = config.getint('cache', 'timeout')Iteration
# List sections
for section in config.sections():
print(section)
# List keys in section
for key in config['database']:
print(key, config['database'][key])
# Get items as list of tuples
items = config.items('database')Practical Example: App Configuration
import configparser
from pathlib import Path
from dataclasses import dataclass
@dataclass
class DatabaseConfig:
host: str
port: int
name: str
user: str
password: str
@dataclass
class AppConfig:
debug: bool
log_level: str
database: DatabaseConfig
def load_config(path: str) -> AppConfig:
config = configparser.ConfigParser()
config.read(path)
return AppConfig(
debug=config.getboolean('app', 'debug', fallback=False),
log_level=config.get('app', 'log_level', fallback='INFO'),
database=DatabaseConfig(
host=config.get('database', 'host'),
port=config.getint('database', 'port'),
name=config.get('database', 'name'),
user=config.get('database', 'user'),
password=config.get('database', 'password'),
)
)
# Usage
cfg = load_config('config.ini')
print(cfg.database.host)Multiple Config Files
config = configparser.ConfigParser()
# Read multiple files (later files override earlier)
config.read(['defaults.ini', 'local.ini', 'secrets.ini'])
# Read from string
config.read_string("""
[app]
debug = true
""")
# Read from dict
config.read_dict({'app': {'name': 'myapp'}})Customization
# Case-sensitive keys (default is case-insensitive)
config = configparser.RawConfigParser()
config.optionxform = str
# Custom delimiters
config = configparser.ConfigParser(delimiters=('=', ':'))
# Allow no value (flags)
config = configparser.ConfigParser(allow_no_value=True)Quick Reference
| Method | Purpose |
|---|---|
read(filename) | Load from file |
read_string(string) | Load from string |
write(file) | Save to file |
sections() | List section names |
get(section, key) | Get string value |
getint(section, key) | Get as integer |
getboolean(section, key) | Get as boolean |
getfloat(section, key) | Get as float |
has_section(name) | Check section exists |
has_option(section, key) | Check key exists |
INI files aren't as flexible as YAML or TOML, but they're simple, readable, and configparser is in the stdlib.
React to this post: