コンフィグレーションファイルを書き間違える。そのようなことは誰しも経験するに違いない。人間であるからミスは付きまとうものだ。INIファイルの場合、どのような間違いが起こり得るだろうか。
INIファイルの配置を忘れる
INIファイル名を誤る
セクション名を誤る
キーの名称を誤る
キーに定義する値を誤る
キーが不足する
不要なキーが混入する
1と2はファイルの存在確認やディレクトリではないことの確認で対処する。3はキーやバリューを読み込むためにセクション名を指定するからチェックとしては問題ない。4から7はCerberusでバリデーションしてみよう。
開発環境:
Python 3.9.2
参考情報:
まずはSchemaを準備しよう。CerberusでどのようなValidatonを行うのかこのSchemaで定義する。この投稿で使用したINIファイルのバリデーションを行うため下記のSchemaを用意した。YAML形式でvalidate.yamlとして保存した。
GM:
type: ipv4address
USER:
# USERは1-64文字の文字列で「¥」は含まない
type: string
minlength: 1
maxlength: 64
regex: '^(?!.*¥).*$'
PASS:
# PASSは4-64文字の文字列で「¥」は含まない
type: string
minlength: 4
maxlength: 64
regex: '^(?!.*¥).*$'
VER:
type: string
regex: '^v\d{1,2}\.\d{1,2}\.\d{1,2}'
IPv4アドレスの判定を行いたいためregexで実装しようとしたが、Cerberusの機能拡張の学習を兼ねて参考情報をそのまま流用した。命名規則に沿って行えば簡易に拡張可能だ。Extending Cerberusにも目を通すことをお勧めする。
require_all=TrueとすることでSchemaで定義した項目は全て必須項目になる。また、規定値でallow_unknown=Falseになっているため、不要なキーが含まれているとバリデーションに失敗する。
# バリデーション用関数
def validate_ini(items, file):
result = False
# Schemaに定義された項目は全て必須項目
v = CustomValidator(require_all=True)
try:
with open(file, 'r') as f:
schema = yaml.safe_load(f)
result = v.validate(items, schema)
except FileNotFoundError as e:
print('File not Found!', e)
except Exception as e:
print(e)
msg = v.errors
return result, msg # msgはバリデーション失敗した際のエラーメッセージ
未知のフィールドが含まれている際のエラーメッセージ例:
{'HOGE': ['unknown field']}
エラーなく正常終了した際の出力:
configuration: {'GM': '192.168.3.51', 'USER': 'admin', 'PASS': 'infoblox', 'VER': 'v2.11.2', 'API': 'https://192.168.3.51/wapi/v2.11.2/'} validation: success
以前の投稿を参考にロギングを是非とも実装しよう。
もしも、FOOというキーが1以外の値になり得ないのであればINIファイルにFOOを含む必要はない。INIファイルは使用される環境や使い方によって可変値になり得る要素を保存するためのファイルと考えているからだ。不変な値を持つFOOの情報を外部に保存するならば、一般ユーザが読み書き不可能なディレクトリやデータベースへ分離しておくべきだと考えている。同様に管理者にしか開示したくない情報もあるだろう。今回の例では、パスワード文字列である「PASS」が平文のままだ。このままでは好奇心旺盛な一般ユーザにシステム上重要なサービスへの特権(に近しい)アクセスを許してしまいかねない。パスワードの保管はどのようにすべきだろうか。
関連ファイル:
Cerberusのバージョン:
Package Version
---------- ---------
Cerberus 1.3.3
まとめ:
スキーマの定義さえできればそれなりのバリデーションを実装できる
バリデーション対象のフィールドが増えてもすっきりしたコードにしやすい