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.log

Reading 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/db

Default 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 overridden

Reading 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.

React to this post: