Python で INI / 設定ファイルを読み書きする(configparser)

INI ファイルは Windows の設定ファイルとして広まったフォーマットで、セクションとキー・バリューのシンプルな構造を持つ。Python では標準ライブラリの configparser モジュールを使って読み書きできるため、外部ライブラリのインストールが不要だ。

INI ファイルの基本構造

INI ファイルは [セクション名] でセクションを区切り、その中に キー = 値 のペアを並べる。

[database]
host = localhost
port = 5432
name = myapp

[logging]
level = DEBUG
file = app.log

値はすべて文字列として扱われる。YAML や TOML と異なり、型の自動変換は行われない。数値や真偽値が必要な場合は、読み込み後に明示的に変換する必要がある。

INI ファイルを読み込む

configparser.ConfigParser を使って INI ファイルを読み込む基本パターンを見てみよう。

import configparser

config = configparser.ConfigParser()
config.read("config.ini", encoding="utf-8")

# セクションの一覧
print(config.sections())  # ['database', 'logging']

# 値の取得
host = config["database"]["host"]
port = config["database"]["port"]
print(f"{host}:{port}")  # localhost:5432

config.read() はファイルが存在しない場合でもエラーを出さず、空の設定として扱われる。ファイルの存在を保証したい場合は、事前に os.path.exists() で確認するか、read_file() を使って明示的にファイルオブジェクトを渡すとよい。

import configparser

config = configparser.ConfigParser()

# ファイルオブジェクトを渡す方法(ファイルがなければ例外が出る)
with open("config.ini", encoding="utf-8") as f:
    config.read_file(f)

型変換メソッド

configparser の値はすべて文字列だが、型変換用のメソッドが用意されている。

import configparser

config = configparser.ConfigParser()
config.read_string("""
[server]
host = 0.0.0.0
port = 8080
debug = true
timeout = 30.5
""")

# 文字列(デフォルト)
host = config.get("server", "host")

# 整数
port = config.getint("server", "port")

# 真偽値
debug = config.getboolean("server", "debug")

# 浮動小数点
timeout = config.getfloat("server", "timeout")

print(f"{host}:{port}, debug={debug}, timeout={timeout}")
# 0.0.0.0:8080, debug=True, timeout=30.5

getboolean() が真と判定する値は 1, yes, true, on で、偽と判定するのは 0, no, false, off だ。大文字小文字は区別されない。

メソッド戻り値の型用途
get()str文字列として取得
getint()int整数として取得
getfloat()float浮動小数点として取得
getboolean()bool真偽値として取得

デフォルト値とフォールバック

キーが存在しない場合に備え、fallback パラメータでデフォルト値を指定できる。

import configparser

config = configparser.ConfigParser()
config.read_string("""
[server]
host = localhost
""")

# キーがなければ fallback が返る
port = config.getint("server", "port", fallback=3000)
debug = config.getboolean("server", "debug", fallback=False)

print(port)   # 3000
print(debug)  # False

また、ConfigParser には DEFAULT セクションという特殊なセクションがあり、ここに書いた値はすべてのセクションで共有される。

import configparser

config = configparser.ConfigParser()
config.read_string("""
[DEFAULT]
timeout = 30
retry = 3

[database]
host = localhost

[cache]
host = redis-server
""")

# DEFAULT の値がどちらのセクションでも取得できる
print(config.getint("database", "timeout"))  # 30
print(config.getint("cache", "retry"))       # 3

DEFAULT セクションの値はあくまでフォールバックであり、各セクションで同じキーを定義すれば上書きされる。設定ファイル全体で共通のデフォルト値を定義したいときに有効な仕組みだ。

INI ファイルに書き込む

ConfigParser で構築した設定をファイルに書き出すには write() メソッドを使う。

import configparser

config = configparser.ConfigParser()

config["server"] = {
    "host": "0.0.0.0",
    "port": "8080",
    "debug": "false",
}

config["database"] = {
    "host": "localhost",
    "port": "5432",
    "name": "myapp",
}

with open("output.ini", "w", encoding="utf-8") as f:
    config.write(f)

出力結果は以下のようになる。

[server]
host = 0.0.0.0
port = 8080
debug = false

[database]
host = localhost
port = 5432
name = myapp

値はすべて文字列として渡す点に注意しよう。数値や真偽値も文字列に変換してからセットする必要がある。

設定の変更と保存

既存の INI ファイルを読み込み、一部を変更して書き戻すパターンもよく使われる。

import configparser

config = configparser.ConfigParser()
config.read("config.ini", encoding="utf-8")

# 値の変更
config["server"]["port"] = "9090"

# セクションの追加
config["newfeature"] = {
    "enabled": "true",
    "version": "2",
}

# キーの削除
config.remove_option("logging", "file")

# セクションの削除
# config.remove_section("obsolete")

with open("config.ini", "w", encoding="utf-8") as f:
    config.write(f)

読み込みと書き込みで同じファイルを指定する場合、書き込み時にファイル全体が上書きされる。コメントは保持されないため、コメント付きの INI ファイルを編集する用途には向いていない。

configparser の制限

configparser はシンプルで使いやすい反面、いくつかの制限がある。

ネストが 1 階層のみ

セクション内にサブセクションを作れない。深い構造の設定には YAML や TOML が適している。

値はすべて文字列

型変換メソッドはあるが、リストや辞書を直接格納する方法がない。カンマ区切りの文字列を split() で分割するといった工夫が必要になる。

コメントが保持されない

読み込み時にコメントは破棄されるため、write() で書き戻すとコメントが消える。

リスト的な値を扱いたい場合は、カンマ区切りで工夫する方法が現実的だ。

import configparser

config = configparser.ConfigParser()
config.read_string("""
[app]
allowed_hosts = localhost, 127.0.0.1, example.com
""")

# カンマ区切りをリストに変換
hosts = [h.strip() for h in config.get("app", "allowed_hosts").split(",")]
print(hosts)  # ['localhost', '127.0.0.1', 'example.com']

どのフォーマットを選ぶべきか

INI、YAML、TOML にはそれぞれ異なる特徴がある。プロジェクトの要件に応じて選択しよう。

INI(configparser)

標準ライブラリだけで完結し、構造がフラットでわかりやすい。小規模なアプリケーションやレガシーシステムの設定に向いている

YAML / TOML

ネスト構造やリスト、型付きの値を自然に表現できる。モダンなプロジェクトではこちらを選ぶケースが増えている

既存のプロジェクトで INI ファイルが使われている場合は configparser を使えば十分だ。新規プロジェクトで設定ファイルのフォーマットを選べるなら、型の明確さやエコシステムとの統合の観点から TOML を検討する価値がある。ただし、外部依存なしで動作する configparser の手軽さは、小さなスクリプトやユーティリティでは今でも大きなメリットになる。