From c289ff0e4d6fc8f893a715827e98111984a9d57e Mon Sep 17 00:00:00 2001 From: Tobias Betz Date: Fri, 20 Mar 2015 13:43:21 +0100 Subject: [PATCH] Allow 'allow_unknown' for sub-documents --- cerberus/cerberus.py | 17 +++++++++++++---- cerberus/tests/tests.py | 20 ++++++++++++++++++++ 2 files changed, 33 insertions(+), 4 deletions(-) diff --git a/cerberus/cerberus.py b/cerberus/cerberus.py index b2ca2e35..069289ba 100644 --- a/cerberus/cerberus.py +++ b/cerberus/cerberus.py @@ -115,7 +115,8 @@ class Validator(object): .. versionadded:: 0.0.2 Support for addition and validation of custom data types. """ - special_rules = "required", "nullable", "type", "dependencies", "readonly" + special_rules = "required", "nullable", "type", "dependencies", "readonly",\ + "allow_unknown", "schema" def __init__(self, schema=None, transparent_schema_rules=False, ignore_none_values=False, allow_unknown=False): @@ -226,6 +227,12 @@ def _validate(self, document, schema=None, update=False, context=None): if self.errors.get(field): continue + if 'schema' in definition: + self._validate_schema(definition['schema'], + field, + value, + definition.get('allow_unknown')) + definition_rules = [rule for rule in definition.keys() if rule not in self.special_rules] for rule in definition_rules: @@ -290,8 +297,6 @@ def validate_schema(self, schema): if not hasattr(self, '_validate_type_' + value): raise SchemaError( errors.ERROR_UNKNOWN_TYPE % value) - elif constraint in self.special_rules: - pass elif constraint == 'schema': constraint_type = constraints.get('type') if constraint_type == 'list': @@ -300,6 +305,8 @@ def validate_schema(self, schema): self.validate_schema(value) else: raise SchemaError(errors.ERROR_SCHEMA_TYPE % field) + elif constraint in self.special_rules: + pass elif constraint == 'items': if isinstance(value, Mapping): # list of dicts, deprecated @@ -426,7 +433,7 @@ def _validate_empty(self, empty, field, value): if isinstance(value, _str_type) and len(value) == 0 and not empty: self._error(field, errors.ERROR_EMPTY_NOT_ALLOWED) - def _validate_schema(self, schema, field, value): + def _validate_schema(self, schema, field, value, nested_allow_unknown): if isinstance(value, Sequence): list_errors = {} for i in range(len(value)): @@ -439,6 +446,8 @@ def _validate_schema(self, schema, field, value): elif isinstance(value, Mapping): validator = copy.copy(self) validator.schema = schema + if not validator.allow_unknown: + validator.allow_unknown = nested_allow_unknown validator.validate(value, context=self.document, update=self.update) if len(validator.errors): diff --git a/cerberus/tests/tests.py b/cerberus/tests/tests.py index 251216e4..cd6a6713 100644 --- a/cerberus/tests/tests.py +++ b/cerberus/tests/tests.py @@ -514,6 +514,26 @@ def _validate_type_foo(self, field, value): self.assertSuccess(document={"fred": "foo", "barney": "foo"}, validator=v) + def test_nested_unknown_keys(self): + schema = { + 'field1': { + 'type': 'dict', + 'allow_unknown': True, + 'schema': {'nested1': {'type': 'string'}} + } + } + document = { + 'field1': { + 'nested1': 'foo', + 'arb1': 'bar', + 'arb2': 42 + } + } + self.assertSuccess(document=document, schema=schema) + + schema['field1']['allow_unknown'] = {'type': 'string'} + self.assertFail(document=document, schema=schema) + def test_novalidate_noerrors(self): '''In v0.1.0 and below `self.errors` raised an exception if no validation had been performed yet.