ハードコーディングはもう卒業:YAML、TOML、INIでPythonの設定管理をプロ級に

Python tutorial - IT technology blog
Python tutorial - IT technology blog

午前2時、激しく震える電話と忘れられない教訓

監視システムからアラートを受け取りました。本番サーバーが完全にダウンしています。Grafanaでログを確認すると、明らかなエラーが表示されていました:ConnectionTimeout: Cannot connect to database at 192.168.1.50:5432

実は、インフラチームがデータベースクラスターのIP帯域を変更したばかりでした。私が古いIPアドレスをコードの奥深くにハードコーディングしてしまっていたため、システムが即座に麻痺してしまったのです。夜通しで20以上のPythonファイルを探し出し、一行ずつ手動で修正してDockerイメージをビルドし直す羽目になりました。

その事故の後、私は鉄の掟を心に刻みました。IPアドレス、ポート、APIキーなどのパラメータをコード内に直接記述してはいけません。すべてを設定ファイル(Config File)に切り出すべきです。安眠を手に入れたいなら、私と一緒にYAML、TOML、INIをマスターしましょう。

なぜ設定を分離する必要があるのか?

設定をソースコードから分離することは、単にコードを綺麗にするだけではありません。システムをスケールさせるための死活問題です。

  • 即時の変更: データベースのポートを5432から6432に変更しますか?テキストファイルを1行修正するだけで済み、コードを再ビルドする必要はありません。
  • セキュリティ: 設定ファイルを.gitignoreに追加することで、GitHubにシークレットキーやトークンが漏洩するのを防げます。
  • マルチ環境: 開発環境(Dev)や本番環境(Prod)でのスクリプト実行も、対応する設定ファイルを指定するだけで切り替えられます。

Pythonのエコシステムでは、主にINI(クラシック)、YAML(DevOps標準)、TOML(モダン)の3つが使われます。

1. INI – シンプルでインストール不要

INI形式は、Pythonが標準ライブラリconfigparserをサポートしているため、非常に一般的です。追加のpip installは一切必要ありません。

config.iniファイルの例:

[database]
host = 192.168.1.100
port = 5432

[server]
debug = true

Pythonでの処理方法:

import configparser

config = configparser.ConfigParser()
config.read('config.ini')

# 値を読み込み、データ型を自動変換する
db_host = config['database']['host']
db_port = config.getint('database', 'port') 
is_debug = config.getboolean('server', 'debug')

print(f"{db_host}:{db_port} に接続中")

弱点: INIはフラットな構成にのみ適しています。複雑な入れ子構造(リストなど)がある場合、INIは非常に扱いにくくなります。

2. YAML – 柔軟性におけるナンバーワンの選択肢

DockerやKubernetesを扱うなら、YAMLは必須知識です。階層構造のサポートが非常に優れており、可読性も高いです。まずはPyYAMLをインストールしましょう。

pip install pyyaml

config.yamlファイルは、ノードのリストを管理するのによく使われます:

database:
  nodes:
    - {host: "db-1.local", port: 5432}
    - {host: "db-2.local", port: 5432}
  timeout: 30

YAMLを読み込むためのPythonコード:

import yaml

with open('config.yaml', 'r') as file:
    # 悪意のあるコード実行攻撃を防ぐため、常にSafeLoaderを使用する
    config = yaml.load(file, Loader=yaml.SafeLoader)

print(f"ノードに接続中: {config['database']['nodes'][0]['host']}")

ヒント: 信頼できないソースからの設定ファイルに対して、決してFullLoaderを使用しないでください。ハッカーに悪用され、マシン上で不正なPythonコードを実行される恐れがあります。

3. TOML – Python界の新しい標準

TOML (Tom’s Obvious, Minimal Language) は、その明快さから多くのPythonプロジェクトでYAMLに取って代わりつつあります。YAMLのように空白(インデント)に敏感すぎないため、初歩的な構文エラーを減らすことができます。

Python 3.11からは、標準ライブラリにtomllibが組み込まれました。それ以前のバージョンでは、tomliをインストールする必要があります。

import tomllib

# TOMLはバイナリモード(rb)でファイルを開く必要があります
with open("config.toml", "rb") as f:
    config = tomllib.load(f)

print(f"最大接続数: {config['database']['connection_max']}")

実践的なアドバイス:どれを選ぶべきか?

実際のプロジェクトでは、私は以下の基準で選んでいます:

  1. INIを使用: 外部ライブラリをインストールせずに、あらゆる環境ですぐに実行する必要がある小規模なスクリプトの場合。
  2. YAMLを使用: 大規模なシステム、CI/CD、または多階層(Nested)の構造が必要な場合。
  3. TOMLを使用: モダンな標準(pyproject.tomlなど)に従いたい場合や、明快さを優先する場合。

もう一つのテクニックは、設定ファイルをSettingsクラスでラップすることです。config['db']['port']を使う代わりに、settings.DB_PORTを呼び出します。これにより、IDEのコード補完(IntelliSense)が効くようになり、キー名のタイポを防ぐことができます。より高度な管理を目指すなら、DynaconfでPythonの設定管理を「プロ仕様」にアップグレードするのも一つの手です。

適切な設定管理は、シニアデベロッパーへの第一歩です。今のプロジェクトにぜひ取り入れてみてください。システムのメンテナンスが格段に楽になるはずです。

Share: