cerberusとは
CERBERUS, n. The watch-dog of Hades, whose duty it was to guard the entrance; everybody, sooner or later, had to go there, and nobody wanted to carry off the entrance. - Ambrose Bierce, The Devil’s Dictionary
ケルベロスはシンプルで軽量なデータ検証機能を提供するパッケージです。
公式曰く、拡張も容易なため、カスタム検証も可能です。
(以下、9割公式のドキュメントの翻訳です)
概要
検証用のスキーマを定義し、それを Validator クラスのインスタンスに渡します。
schma = {'name': {'type': 'string'}}
v = Validator(schema)
次に、validate()
を呼び出すことで、簡単にスキーマと辞書を検証できます。
検証が成功すると True が返されます。
document = {'name': 'john doe'}
v.validate(document) # True
インストール
安定バージョンはpip
から取得できます。pip install cerberus
使い方
基本的な使い方
# 概要の通り。スキーマを定義し、Validatorクラスに渡して検証する。
>>> from cerberus import Validator
>>> schema = {'name': {'type': 'string'}}
>>> v = Validator(schema)
>>> document = {'name': 'john doe'}
>>> v.validate(document)
True
# スキーマと辞書を同時に渡すこともできる。
>>> v = Validator()
>>> v.validate(document, schema)
True
# Validatorクラスとそのインスタンスは呼び出し可能なので、以下の簡略構文が使える。
>>> v(document)
True
ケルベロスは、検証に問題があった時点(最初のバリデーションエラーが起きた時点)では停止しません(!)。
文書全体が常に処理されてからFalse
を返します。その後、エラープロパティにアクセスすることで、起きた問題のリストを取得できます。
つまり検証したデータ内で起きたバリデーションエラーは全て取得できます。
バリデーションエラーはValidator.errors
に格納されます。
>>> schema = {'name': {'type': 'string'}, 'age': {'type': 'integer', 'min': 10}}
>>> document = {'name': 400, 'age': 5}
>>> v.validate(document, schema)
False
>>> v.errors
{'age': ['min value is 10'], 'name': ['must be of string type']}
未知のものを許可
デフォルトでは、スキーマで定義された以外のキーは許可されません。
ただし、Validator でallow_unknown = True
を設定することにより、スキーマに記述されていないキーを許可したり、検証をかけたりすることができます。
# デフォルトでは、スキーマで定義されたキーのみが許可される。
>>> schema = {'name': {'type': 'string', 'maxlength': 10}}
>>> v.validate({'name': 'john', 'sex': 'M'}, schema)
False
>>> v.errors
{'sex': ['unknown field']}
# allow_unknownをTrueに設定することで、不明なキーを許可できる。(検証はしない)
>>> v.schema = {}
>>> v.allow_unknown = True
>>> v.validate({'name': 'john', 'sex': 'M'})
True
# もしくは、検証スキーマに設定できる。この場合は未知のキーが検証される。
>>> v.schema = {}
>>> v.allow_unknown = {'type': 'string'}
>>> v.validate({'an_unknown_field': 1})
False
>>> v.errors
{'an_unknown_field': ['must be of string type']}
# 初期化時に設定することも可能。
>>> v = Validator({}, allow_unknown=True)
>>> v.validate({'name': 'john', 'sex': 'M'})
True
# スキーマのルールとして設定することもできる。ネストした先の辞書等にも使用可能。
>>> v = Validator()
>>> v.allow_unknown
False
>>> schema = {
... 'name': {'type': 'string'},
... 'a_dict': {
... 'type': 'dict',
... 'allow_unknown': True, #a_dict辞書の未知のキーを許可
... 'schema': {
... 'address': {'type': 'string'}
... }
... }
... }
>>> v.validate({'name': 'john',
... 'a_dict': {'an_unknown_field': 'is allowed'}},
... schema)
True
>>> v.validate({'name': 'john',
... 'an_unknown_field': 'is not allowed', # 親の辞書はv.allow_unknown == False
... 'a_dict':{'an_unknown_field': 'is allowed'}},
... schema)
False
>>> v.errors
{'an_unknown_field': ['unknown field']}
全てを要求する
デフォルトでは、スキーマで定義されたキーは不要です。
ただし、Validator でrequire_all = True
を設定することにより、スキーマに記述した全てのキーを要求できます。
>>> v = Validator()
>>> v.require_all
False
>>> schema = {
... 'name': {'type': 'string'},
... 'a_dict': {
... 'type': 'dict',
... 'require_all': True,
... 'schema': {
... 'address': {'type': 'string'}
... }
... }
... }
>>> v.validate({'name': 'foo', 'a_dict': {}}, schema)
False
>>> v.errors
{'a_dict': [{'address': ['required field']}]}
>>> v.validate({'a_dict': {'address': 'foobar'}}, schema)
True
処理済みドキュメントの取得
# 検証されたドキュメントはドキュメントプロパティに格納される
>>> v.schema = {'amount': {'type': 'integer', 'coerce': int}}
>>> v.validate({'amount': '1'})
True
>>> v.document
{'amount': 1}
# validated()
# 検証済みの値のみを返す
>>> schema = {'name': {'type': 'string', 'maxlength': 10}}
>>> v = Validator(schema)
>>> v.validated({'name': 'hoge'})
{'name': 'hoge'}
>>> v.validated({'name': 200})
# None
# normalized()
# 検証せずにDocumentの正規化されたコピーを返す
>>> n = v.normalized({'name': 200})
>>> type(n['name'])
# <class 'int'>
>>> n = v.normalized({'name': 'hoge'})
>>> type(n['name'])
# <class 'str'>
>>> n
{'name': 'hoge'}
検証スキーマ
前述の通りですが、デフォルトの設定では、スキーマに書かれている全てのキーはオプションとして扱われます。
極端な話、デフォルトではどんなスキーマ相手でもdocument = {}
で検証が通ります。
設定を変えるか、Validator.require_all
を True にして全てのスキーマキーを要求することが望ましいです。
2つのレジスタ
# schema_registry
>>> from cerberus import schema_registry
>>> schema_registry.add('non-system user',
... {'uid': {'min': 1000, 'max': 0xffff}})
>>> schema = {'sender': {'schema': 'non-system user',
... 'allow_unknown': True},
... 'receiver': {'schema': 'non-system user',
... 'allow_unknown': True}}
# rules_set_registry
>>> from cerberus import rules_set_registry
>>> rules_set_registry.extend((('boolean', {'type': 'boolean'}),
... ('booleans', {'valuesrules': 'boolean'})))
>>> schema = {'foo': 'booleans'}
レジストリを設定、及びダンプするには、extend()
及びall()
を使用します。
検証
スキーマ自体の検証は、決まったタイミングのみ行われます。
(when passed to the validator or a new set of rules is set for a document’s field)
>>> v = Validator({'foo': {'allowed': []}})
>>> v.schema['foo'] = {'allowed': 1}
Traceback (most recent call last):
File "<input>", line 1, in <module>
File "cerberus/schema.py", line 99, in __setitem__
self.validate({key: value})
File "cerberus/schema.py", line 126, in validate
self._validate(schema)
File "cerberus/schema.py", line 141, in _validate
raise SchemaError(self.schema_validator.errors)
SchemaError: {'foo': {'allowed': 'must be of container type'}}
>>> v.schema['foo']['allowed'] = 'strings are no valid constraint for allowed'
>>> v.schema.validate()
Traceback (most recent call last):
File "<input>", line 1, in <module>
File "cerberus/schema.py", line 126, in validate
self._validate(schema)
File "cerberus/schema.py", line 141, in _validate
raise SchemaError(self.schema_validator.errors)
SchemaError: {'foo': {'allowed': 'must be of container type'}}
Serialization
Cerberus スキーマは、Python の型(dict, list, string
など)で構成されています。
ユーザ定義の検証ルールも、スキーマ内ではstring
型としてその名前が呼び出されます。
これによって、PyYAML など、様々な方法でスキーマを定義することができます。
>>> import yaml
>>> schema_text = '''
... name:
... type: string
... age:
... type: integer
... min: 10
... '''
>>> schema = yaml.load(schema_text)
>>> document = {'name': 'Little Joe', 'age': 5}
>>> v.validate(document, schema)
False
>>> v.errors
{'age': ['min value is 10']}
YAML 以外にも、ネストされた辞書を生成できるデコーダーがあれば、どんなシリアライザーも利用できます。
JSON とか。
検証ルール
基本は 👇 にまとまってます
Validation Rules — Cerberus is a lightweight and extensible data validation library for Python
valuesrules
検証される全てのデータに適応されるルールを定義します。
>>> schema = {'numbers':
... {'type': 'dict',
... 'valuesrules': {'type': 'integer', 'min': 10}}
... }
>>> document = {'numbers': {'an integer': 10, 'another integer': 100}}
>>> v.validate(document, schema)
True
>>> document = {'numbers': {'an integer': 9}}
>>> v.validate(document, schema)
False
>>> v.errors
{'numbers': [{'an integer': ['min value is 10']}]}
エラー
ドキュメントが見つからない時、または無効な値の時
cerberus.validator.DocumentError
>>> document = ""
>>> v = Validator(schema)
>>> v(document)
cerberus.validator.DocumentError: '' is not a document, must be a dict
無効な検証スキーマを検出した場合
cerberus.schema.SchemaError
>>> schema = {"name": {"type": "hoge"}}
>>> v = Validator(schema)
cerberus.schema.SchemaError: {'name': [{'type': ['Unsupported types: hoge']}]}
参考
Python のバリデーションライブラリ「Cerberus」のよく使うバリデーションルールをまとめてみた | Developers.IO
Cerberus 入門!ばりばりバリデーションしよう! - hogehoge diary