結論:TOMLの構文確認と設定内容の検証を分ける
設定ファイルをtomllibで読み込めたとしても、その値が処理に使えるとは限らない。構文が正しいこと、必要なキーがあること、型や値の範囲が適切なことを段階的に確かめる。例えば回数が0、しきい値がNaN、キー名の打ち間違いなどは、TOMLとして読めても実験条件としては不適切な場合がある。
tomllibはPython 3.11以降の標準ライブラリで、TOMLの読込を受け持つ。設定を書き出す機能や、業務上のスキーマ検証が自動で付いてくるわけではない。何を必須にして何を既定値にするか、未知の項目を許すかは利用するプログラムが決める。小さな設定なら明示的な検証関数を一つ作るだけでも、処理の途中で分かりにくいエラーになることを減らせる。
そのまま動かせる例
例は繰り返し回数repeats、しきい値threshold、集計方法methodを持つ設定を一時フォルダーへ作り、tomllib.loadで読む。ファイルはバイナリモードのrbで開く。文字列を読むtomllib.loadsとは受け取るものが違うので、名前が似ていても使い分けよう。標準ライブラリだけで動き、作ったファイルは終了時に削除される。
後半では構文不正、boolの回数、範囲外、非有限値、必須キー不足、未知キーを意図的に与え、全て拒否されることを確認する。いずれも想定した失敗例である。実際の設定ファイルを壊す操作ではなく、文字列から独立に試している。実行結果には例外の種類だけを出すので、利用者の設定値を丸ごとログへ漏らす構成にもしていない。
from pathlib import Path
from tempfile import TemporaryDirectory
import math
import tomllib
VALID = '[experiment]\nrepeats = 3\nthreshold = 0.25\nmethod = "mean"\n'
def validate(data):
if set(data) != {"experiment"} or not isinstance(data["experiment"], dict):
raise ValueError("experiment table required")
cfg = data["experiment"]
if set(cfg) != {"repeats", "threshold", "method"}:
raise ValueError("missing or unknown key")
if type(cfg["repeats"]) is not int or not 1 <= cfg["repeats"] <= 100:
raise ValueError("repeats must be an integer in 1..100")
threshold = cfg["threshold"]
if type(threshold) not in (int, float) or not math.isfinite(threshold) or not 0 <= threshold <= 1:
raise ValueError("threshold must be finite and in 0..1")
if cfg["method"] not in ("mean", "median"):
raise ValueError("unknown method")
return cfg
with TemporaryDirectory() as folder:
path = Path(folder) / "settings.toml"
path.write_text(VALID, encoding="utf-8")
with path.open("rb") as stream:
cfg = validate(tomllib.load(stream))
assert cfg == {"repeats": 3, "threshold": 0.25, "method": "mean"}
print("valid:", cfg)
invalid = {
"syntax": '[experiment\n',
"boolean": VALID.replace('repeats = 3', 'repeats = true'),
"range": VALID.replace('repeats = 3', 'repeats = 0'),
"nonfinite": VALID.replace('threshold = 0.25', 'threshold = nan'),
"missing": VALID.replace('method = "mean"\n', ''),
"unknown": VALID + 'extra = 1\n',
}
for name, text in invalid.items():
try:
validate(tomllib.loads(text))
except (tomllib.TOMLDecodeError, ValueError) as exc:
print(name, type(exc).__name__)
else:
raise AssertionError(name)
実行結果
valid: {'repeats': 3, 'threshold': 0.25, 'method': 'mean'}
syntax TOMLDecodeError
boolean ValueError
range ValueError
nonfinite ValueError
missing ValueError
unknown ValueError
型が合っていても値を調べる
TOMLの整数はPythonのint、真偽値はboolになる。Pythonではboolがintの派生なので、isinstance(value, int)だけだとTrueを回数として通す場合がある。ここでは回数へ真偽値を認めない方針なのでtypeを厳密に確認している。何でも厳密にすればよいわけではなく、受け入れる入力の契約を決めてから検証を書くことが大切である。
しきい値は整数と浮動小数点の両方を許し、0〜1の有限値に限定する。TOMLにはinfやnanも表現できるため、単純な型確認だけでは十分でない。文字列の数値を自動変換して受け入れるかも方針次第だが、設定の誤記を隠す変換を無条件に増やさない方が原因を追いやすい。エラーには項目名と許される条件を添えると修正しやすくなる。
未知キーをどう扱うか
この例はキー集合を厳密に比較し、余分なキーも拒否する。thresholdをthreshholdと誤記しても気付かず既定値で動く、といった事故を避けるためである。一方、設定を複数ツールで共有する場合は、他ツールの項目を許したいこともある。その場合も「未知キーを無視する」という仕様を明示し、単なる書き間違いを見つける方法を考えておきたい。
既定値を設ける項目は、欠けていてよい理由を明確にする。再現性に関わる重要な条件まで暗黙の既定値へ任せると、プログラムの更新で結果が変わる可能性がある。読み込んだ文字列だけでなく、既定値を適用して実際に使った条件を実行記録へ残すと、後から何が有効だったかを確認しやすい。
読込失敗と検証失敗を分けて案内する
存在しないファイルや読込権限の問題は、TOMLの構文エラーとは別である。ファイルを開く処理のOSError、解析時のTOMLDecodeError、内容検証のValueErrorを必要な範囲で分けて扱おう。全てを「設定がありません」とまとめると、構文や値の不正を見落としやすい。最新Pythonで追加された例外属性を、古い環境にもあるものとして使わない点にも注意したい。
検証を終えるまでは本処理や外部への書き込みを始めない方が、途中まで処理した状態を残しにくい。TOMLから得た辞書をdataclassなどへまとめる場合も、型注釈が実行時検証を代行するわけではない。まず入力を検証し、その後で扱いやすい構造へ変換する順番にすると、設定の入口と実験のロジックが分かれ、テストも書きやすくなる。
確認環境と参考資料
例はLinux・CPython 3.12.14で実行した。掲載した出力はこの環境での結果である。公式資料のstable版や最新版は更新されるため、手元のバージョンと対応する仕様も確認してほしい。
- Python公式:tomllib(2026年10月2日参照)
関連項目:argparseでヘルプ付きの使いやすいコマンドを作る / dataclassで実験条件をまとめる:default_factoryとfrozenの注意点 / 解析をやり直せる実行記録を残す:条件・入力ハッシュ・環境情報
