ConfigParser と json の MIX

なるほどね、ConfigParser と json の MIX

前置き

設定ファイルというものは、ユーザフレンドリと開発者フレンドリの間の中間管理職である。

「設定ファイル」などというものがなぜに存在するかといえば、無論「ソフトウェアの振る舞いの変更をエンドユーザに委ねられるようにしたい」からであって。

SI屋なんて商売やってると、なかにはとんでもない誤解をしてしまう開発者もいて、「マジックナンバーはプログラムに埋め込んではならない」「定数は一箇所にまとめるべきだ」のと完全に同義だと思ってしまうのもいて侮れない(そもそも後者はその「べき論」自体に異論あるけどな)。

違うぞ。例えば「PI = 3.14」を設定ファイルに書く馬鹿いるか。「んなやついるか!」と思うでしょう? そうでもないぞ。java の「*.properties」に「定数」をガンガン書き込む開発者、何人みたか知れない。

で。本題に戻ります。

「エンドユーザに制御を委ねたい変数等置き場」というものは、今でも時々悩むのである、というおはなし。

普段使いの Python には Python そのものでいい

Python を知っている「自分向け」とか、Python を知っている人々向け、なら、config.py みたいなものそのものが「設定ファイルだ!」と主張すれば良いし、このアプローチは当然一般的。

些細な欠点が2つばかりあって、一つは python モジュール検索パスの影響を受けること。この制御は、初学者には厳しいかもしれないね。もうひとつはあるいは致命傷かもしれなくて、「pythonで出来ること全てが出来てしまう」ことね。つまりは、欠点のうちの一つは、まさにメリットでもある。

あと、欠点ではない欠点があって。「pythonを設定ファイルにしてしまえば?」と言うだけで「お客様がわかる はずがない」と過剰反応してしまう人も多い、ってこと。そりゃ馬鹿にし過ぎです、実際には:

config.py的なもの
1 dir = 'c:/Windows/System32'

という記述をみて「わかりません、出来ません、出来るわけがありません」というお客様がいたら逆に教えて欲しい。こんなものは「Python です」と言わなければ良いだけだ、ということなのだけれど、頑なな人もいて。

Pythonそのものでは困る場合のいくつもの選択肢

csv, ini, xml, yaml, json。

csv はユーザフレンドリかもしれないが、自由度がなさ過ぎるし、(仕様化されてるとは言えない)しょうもない方言もあったりするから、見かけの手軽さよりもずっと開発者フレンドリとは言えない。

xml は…ユーザフレンドリでも開発者フレンドリでもないのだが、異論多いだろう。「XML を書く」ことに苦痛を感じない人が…いるのかねぇ? というのはワタシの意見。冗長過ぎて一瞥しにくいのが理由の1つ目。2つ目はやはり仕様が膨大過ぎることですわ。DTD、XML Schema、namespace、うん、美しいね、はいそうですか。で、「mixed content」って何の役に立つの。データ記述なのかドキュメント記述なのかどっちつかずだから、こうまで水膨れ仕様になる。データの記述には XML は最適ではない。ユーザにとって。酷いのがあるぞ:

1 <aaaList>
2 1
3 2
4 3
5 4
6 5
7 </aaaList>

何が滅茶苦茶かって、「改行コード区切り」…。あのー、何のための XML? そもそもデータ量削減のために小細工したくなるのが XML の宿命だってことだろ。なんかもう、好きにしてくれ、って感じ。

xml が開発者フレンドリでないのは、python も ruby も同じだが「デフォルトの」XML パーサがexpat しか提供されない」ことによる。expat だけであっても、英語圏ではまぁ良い。プログラムは多少苦痛でも、扱えないわけではないから。けどな、「Shift-JIS じゃないなんて自己中だ」と言い出す「自称 技術者」までいる日本語圏では特に面倒なことになる。こういう滅茶苦茶な技術者がいると「utf-8で書いてね」という当たり前のお願いが通じない。そして expat はこれを扱えない。めでたしめでたし。

yaml は、仕様はスッキリしているし、書きやすく読みやすいのだが、ruby 界隈とは違って、python における yaml がおかれている状況は厳しい。というのも、「PyYAML というほぼ唯一の選択肢が、かなり致命的なバグを多数残したまま開発が止まってしまっている」から。使えないわけではないのだけれど、そんな状態だと、不安になるわな。(あとね、一括読み込みしか出来ないのが困る。小刻みにメモリを節約しながら読む、とか出来ない。)

json は。正直言って、そのものではユーザフレンドリとは「あまり言えない」。致命的なのはコメントが書けないこと。

ini ファイル「のようなもの」は、これはユーザフレンドリではあり、開発者フレンドリでもある。けれども、自由度が高くないのが問題だ。

あぁ、これは良いバランスかも | ini + json

ConfigParser を使おうとして、で、「値にリスト設定出来ないかなぁ?」と思ってね。

StackOverflow にアイディアがありました。

ConfigParserが扱える形式の値に json を使う。言われてみりゃそうだね。

myconfig.cfg
1 [build]
2 ;include_dirs: ["./include"]
3 include_dirs: ["./include",
4     "/opt/local/include/site-local"]
myconf.cfgを使うひと
 1 # -*- coding: utf-8 -*-
 2 try:
 3     from configparser import ConfigParser  # Python 3.x
 4 except ImportError:
 5     from ConfigParser import ConfigParser  # Python 2.x
 6 import json
 7 
 8 config = ConfigParser()
 9 config.read(["myconf.cfg"])
10 
11 include_dirs = json.loads(config.get("build", "include_dirs"))
12 
13 print(include_dirs)

INIファイルの欠点(自由度が低い)とjsonの欠点(コメントを書けない・コメントアウト出来ない)をうまく補っている、結構いいバランスだね。jsonなので型が維持されるのもいいよね。(XMLが最善だと信じている技術者は多くて、「XMLSchemaで型問題はパーフェクトだ!」と言うかもしれない。けどね、その開発コスト・ランタイムのオーバヘッドについて、果たして考えたことはあるのだろうか?)

json なので、シングルコーテーションはダメ、とか、まぁ多少の難点はあるけれどね、このアプローチ、個人的に凄く気に入った。

追記:PyYAMLが息を吹き返してた

あれまいつのまに? 間が悪かったなぁ、見切りつけたの、2013年だったからさ。そのあと興味もなくしてたわ。

YAML、いいと思うぞ…ってのは Ruby メインの人たちには釈迦に説法ね。

今なら、の YAML 話題を書いたよ

一応今の pyyaml (+ libyaml) はちゃんとアクティブなので、以前感じた不安は解消してる。最初の印象は最悪だったんだけどね、今はそんなでもない。でこれ

2020-11-04追記:with …

内緒

いまだにこれを読む人が多いみたいなんで、一応ね。

2022-12-20追記:今でもここに辿り着いてしまう人は、toml にも気付いておいてね

python 3.11 という、2022年時点での最新からの話なのだから、今でも configarser と json の mix という話題に辿り着いて喜ぶ人がいるのは問題ないのだけれど、せっかくの標準の流れなので、 tomllib には気付いてあげてほしい。

toml は json と同じように組み込み型を持っているので、これが使えるとなれば configparser はきっと用なしになっていくんじゃないかと思うよ。