diff --git a/cerberus/tests/test_normalization.py b/cerberus/tests/test_normalization.py index 6e06f553..c7d0e147 100644 --- a/cerberus/tests/test_normalization.py +++ b/cerberus/tests/test_normalization.py @@ -30,6 +30,15 @@ def test_coerce_in_listschema(): assert_normalized(document, expected, schema) +def test_coerce_in_listitems(): + schema = {'things': {'type': 'list', + 'items': [{'coerce': int}, + {'coerce': str}]}} + document = {'things': ['1', 2]} + expected = {'things': [1, '2']} + assert_normalized(document, expected, schema) + + def test_coerce_in_dictschema_in_listschema(): item_schema = {'type': 'dict', 'schema': {'amount': {'coerce': int}}} schema = {'things': {'type': 'list', 'schema': item_schema}} @@ -56,6 +65,16 @@ def test_coerce_catches_ValueError(): errors.COERCION_FAILED, int) +def test_coerce_in_listitems_catches_ValueError(): + schema = {'things': {'type': 'list', + 'items': [{'coerce': int}, {'coerce': str}]}} + document = {'things': ['not_a_number', 2]} + _errors = assert_fail(document, schema) + _errors[0].info = () # ignore exception message here + assert_has_error(_errors, ('things', 0), ('things', 'items', 'coerce'), + errors.COERCION_FAILED, int) + + def test_coerce_catches_TypeError(): schema = {'name': {'coerce': str.lower}} _errors = assert_fail({'name': 1234}, schema) @@ -64,6 +83,16 @@ def test_coerce_catches_TypeError(): errors.COERCION_FAILED, str.lower) +def test_coerce_in_listitems_catches_TypeError(): + schema = {'things': {'type': 'list', + 'items': [{'coerce': int}, {'coerce': str.lower}]}} + document = {'things': ['1', 2]} + _errors = assert_fail(document, schema) + _errors[0].info = () # ignore exception message here + assert_has_error(_errors, ('things', 1), ('things', 'items', 'coerce'), + errors.COERCION_FAILED, str.lower) + + def test_coerce_unknown(): schema = {'foo': {'schema': {}, 'allow_unknown': {'coerce': int}}} document = {'foo': {'bar': '0'}} diff --git a/cerberus/tests/test_schema.py b/cerberus/tests/test_schema.py index 240303cf..709f5831 100644 --- a/cerberus/tests/test_schema.py +++ b/cerberus/tests/test_schema.py @@ -88,7 +88,7 @@ def test_validated_schema_cache(): v = Validator({'foozifix': {'coerce': int}}) assert len(v._valid_schemas) == cache_size - max_cache_size = 145 + max_cache_size = 149 assert cache_size <= max_cache_size, \ "There's an unexpected high amount (%s) of cached valid " \ "definition schemas. Unless you added further tests, " \ diff --git a/cerberus/validator.py b/cerberus/validator.py index d0cc6b09..b561600e 100644 --- a/cerberus/validator.py +++ b/cerberus/validator.py @@ -682,9 +682,11 @@ def __normalize_containers(self, mapping, schema): pass elif isinstance(mapping[field], _str_type): continue - elif isinstance(mapping[field], Sequence) and \ - 'schema' in schema[field]: - self.__normalize_sequence(field, mapping, schema) + elif isinstance(mapping[field], Sequence): + if 'schema' in schema[field]: + self.__normalize_sequence(field, mapping, schema) + elif 'items' in schema[field]: + self.__normalize_items(field, mapping, schema) def __normalize_mapping_per_keyschema(self, field, mapping, property_rules): schema = dict(((k, property_rules) for k in mapping[field])) @@ -747,6 +749,19 @@ def __normalize_sequence(self, field, mapping, schema): self._drop_nodes_from_errorpaths(validator._errors, [], [2]) self._error(validator._errors) + def __normalize_items(self, field, mapping, schema): + schema = dict(((k, v) for k, v in enumerate(schema[field]['items']))) + document = dict((k, v) for k, v in enumerate(mapping[field])) + validator = self._get_child_validator( + document_crumb=field, schema_crumb=(field, 'items'), + schema=schema) + value_type = type(mapping[field]) + result = validator.normalized(document, always_return_document=True) + mapping[field] = value_type(result.values()) + if validator._errors: + self._drop_nodes_from_errorpaths(validator._errors, [], [2]) + self._error(validator._errors) + @staticmethod def _normalize_purge_unknown(mapping, schema): """ {'type': 'boolean'} """