The configparser module reads and writes INI-style configuration files. It's been around forever and handles the format that tools like git config and countless other applications use.
Basic INI Format
INI files have sections and key-value pairs:
[database]
host = localhost
port = 5432
name = myapp
[logging]
level = INFO
file = /var/log/app.logReading Configuration
Read a config file and access values:
import configparser
config = configparser.ConfigParser()
config.read('settings.ini')
# Access values
host = config['database']['host']
port = config['database']['port']Default Values
Handle missing keys gracefully:
import configparser
config = configparser.ConfigParser()
config.read('settings.ini')
# With fallback
debug = config.get('app', 'debug', fallback='false')
# Or check existence
if config.has_option('app', 'debug'):
debug = config['app']['debug']Type Conversion
Values are strings by default. Use typed getters:
import configparser
config = configparser.ConfigParser()
config.read('settings.ini')
# Integer
port = config.getint('database', 'port')
# Float
timeout = config.getfloat('server', 'timeout')
# Boolean (handles yes/no, on/off, true/false, 1/0)
debug = config.getboolean('app', 'debug')Writing Configuration
Create and write config files:
import configparser
config = configparser.ConfigParser()
config['database'] = {
'host': 'localhost',
'port': '5432',
'name': 'myapp'
}
config['logging'] = {}
config['logging']['level'] = 'INFO'
config['logging']['file'] = '/var/log/app.log'
with open('settings.ini', 'w') as f:
config.write(f)Interpolation
Reference other values within the config:
# config.ini
# [paths]
# base = /opt/app
# logs = %(base)s/logs
# data = %(base)s/data
import configparser
config = configparser.ConfigParser()
config.read('config.ini')
# Automatically expands to /opt/app/logs
print(config['paths']['logs'])Extended Interpolation
Use ${section:key} syntax for cross-section references:
import configparser
config = configparser.ConfigParser(
interpolation=configparser.ExtendedInterpolation()
)
config.read_string("""
[common]
base_dir = /opt/app
[database]
path = ${common:base_dir}/db
""")
print(config['database']['path']) # /opt/app/dbDefault Section
The [DEFAULT] section provides fallbacks:
[DEFAULT]
timeout = 30
retries = 3
[database]
host = localhost
# timeout and retries inherited from DEFAULT
[cache]
host = redis
timeout = 5
# retries inherited, timeout overriddenReading from Strings
Parse config from a string:
import configparser
config_string = """
[server]
host = localhost
port = 8080
"""
config = configparser.ConfigParser()
config.read_string(config_string)Multiple Config Files
Layer configs with later files overriding earlier:
import configparser
config = configparser.ConfigParser()
config.read(['defaults.ini', 'local.ini', 'override.ini'])Case Sensitivity
Keys are lowercased by default. Preserve case:
import configparser
config = configparser.ConfigParser()
config.optionxform = str # Preserve case
config.read('settings.ini')Checking Structure
Inspect the config:
import configparser
config = configparser.ConfigParser()
config.read('settings.ini')
# List all sections
print(config.sections())
# Check if section exists
if 'database' in config:
print('Database configured')
# List keys in a section
print(list(config['database'].keys()))Environment Override Pattern
Common pattern: env vars override config file:
import configparser
import os
config = configparser.ConfigParser()
config.read('settings.ini')
# Env var takes precedence
db_host = os.environ.get('DB_HOST') or config.get('database', 'host')When to Use configparser
Use configparser when:
- You need human-editable config files
- Working with existing INI files
- Simple key-value configuration
- No dependencies desired
Consider alternatives for:
- Complex nested structures (use TOML or YAML)
- Schema validation (use pydantic-settings)
- Environment-first config (use python-dotenv)
The INI format is simple and widely understood. For straightforward configuration needs, configparser does the job with zero dependencies.