🔥

python3 configparser でセクション名のないiniファイルを扱う

2023/12/30に公開

iniファイルは普通はセクションを書くわけだが、実はセクションがなければ dot-env ファイルに非常に似てる。

https://www.npmjs.com/package/dotenv

この形式の何が嬉しいかというと、なんとシェルスクリプトでそのまま読み込めてしまう。
環境変数への代入分に酷似してるためだ。

だが、python configparserではこれを受け付けない。

色々なひとが七転八倒している。

https://stackoverflow.com/questions/2885190/using-configparser-to-read-a-file-without-section-name

多くの場合は、セクション文字列を捏造して、read_stringで読んでいる。間違ってはいない。だが小生は思った。ダサいと。

pythonはオブジェクト指向軽量言語であり、configparserはpure pythonで書いてあることがわかってる。configparserを継承して改造すればよいだろう。

例によってリバースエンジニアリングしてみる。

https://github.com/python/cpython/blob/3.12/Lib/configparser.py#L665-L686

毎度お世話になる read() は結局のところ RawConfigParser._read を呼んでいる。

https://github.com/python/cpython/blob/3.12/Lib/configparser.py#L974-L998

さらに _read() を呼んで、中では引数 fp に対して enumerate() で回している。 つまり fp は iterableであればなんでもよいことになる。

であれば、_read() の引数 fp に介入するだけで済みそうだ。

python3.6以降は、サブジェネレータ委譲があるため、イテレータ絡みの小細工が非常に読みやすくなる。

import configparser

class envparser(configparser.ConfigParser):    
    def _read(self,fp,fpname):
        def addsection(fp):
            yield "[DEFAULT]"
            yield from fp
        super(envparser,self)._read(addsection(fp),fpname)

これで全ての値が"DEFAULT"セクションに入る。

この _read 関数についてのオフィシャルドキュメントには存在しないのだが、githubの履歴をみる限り、2002年にリファクタリングされたっきりなので、今後も修正される可能性は非常に低いと思われる。

https://github.com/python/cpython/commit/fce6557c6b7176eb599c6b7e449936e98faf197f#diff-be0852781756e56ef7db3d4e5e2eb8655fe04580e12496cdc4aab4796ac7bad6L429-R379

外部依存パッケージにこだわりがなければ、既存のライブラリは幾つか存在する。

https://pypi.org/search/?q=dotenv

Discussion