Skip to content

Commit

Permalink
Adds the 'contains' rule
Browse files Browse the repository at this point in the history
Closes pyeve#358
  • Loading branch information
funkyfuture committed Apr 21, 2018
1 parent fd65f47 commit 0d7114a
Show file tree
Hide file tree
Showing 5 changed files with 68 additions and 5 deletions.
12 changes: 7 additions & 5 deletions CHANGES.rst
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,10 @@ Here you can see the full list of changes between each Cerberus release.
In Development
--------------

- Update README link. Make it point to the new PYPI website (Frank
Sachsenheim).
- New: The ``contains`` rule. (Frank Sachsenheim)

- Update README link. Make it point to the new PyPI website. (Frank
Sachsenheim)

Version 1.2
-----------
Expand All @@ -23,8 +25,8 @@ Released on April 12, 2018.
(Frank Sachsenheim)
- New: Add errors.MAPPING_SCHEMA on errors within subdocuments.
(Frank Sachsenheim)
- New: Support for Types Definitions, which allow quick types check on the fly
(Frank Sachsenheim).
- New: Support for Types Definitions, which allow quick types check on the fly.
(Frank Sachsenheim)
- Fix: Simplify the tests with Docker by using a volume for tox environments.
(Frank Sachsenheim)
Expand All @@ -43,7 +45,7 @@ Released on April 12, 2018.
Closes :issue:`269`. (Frank Sachsenheim)
- Fix: A dependency is not considered satisfied if it has a null value.
Closes :issue:`305`. (Frank Sachsenheim)
- Override ``UnvalidatedSchema.copy`` (Peter Demin).
- Override ``UnvalidatedSchema.copy``. (Peter Demin)
- Fix: README link. (Gabriel Wainer)
- Fix: Regression: allow_unknown causes dictionary validation to fail with
a KeyError. Closes :issue:`302`. (Frank Sachsenheim)
Expand Down
2 changes: 2 additions & 0 deletions cerberus/errors.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@
UNALLOWED_VALUES = ErrorDefinition(0x45, 'allowed')
FORBIDDEN_VALUE = ErrorDefinition(0x46, 'forbidden')
FORBIDDEN_VALUES = ErrorDefinition(0x47, 'forbidden')
MISSING_MEMBERS = ErrorDefinition(0x48, 'contains')

# other
NORMALIZATION = ErrorDefinition(0x60, None)
Expand Down Expand Up @@ -461,6 +462,7 @@ class BasicErrorHandler(BaseErrorHandler):
0x45: "unallowed values {0}",
0x46: "unallowed value {value}",
0x47: "unallowed values {0}",
0x48: "missing members {0}",

0x61: "field '{field}' cannot be coerced: {0}",
0x62: "field '{field}' cannot be renamed: {0}",
Expand Down
18 changes: 18 additions & 0 deletions cerberus/tests/test_validation.py
Original file line number Diff line number Diff line change
Expand Up @@ -1577,3 +1577,21 @@ def test_allow_unknown_with_oneof_rules(validator):
# check that allow_unknown is actually applied
document = {'test': {'known': 's', 'unknown': 'asd'}}
assert_success(document, validator=validator)


@mark.parametrize('constraint',
(('Graham Chapman', 'Eric Idle'), 'Terry Gilliam'))
def test_contains(constraint):
validator = Validator({'actors': {'contains': constraint}})

document = {'actors': ('Graham Chapman', 'Eric Idle', 'Terry Gilliam')}
assert validator(document)

document = {
'actors': ('Eric idle', 'Terry Jones', 'John Cleese', 'Michael Palin')
}
assert not validator(document)
assert errors.MISSING_MEMBERS in validator.document_error_tree['actors']
missing_actors = \
validator.document_error_tree['actors'][errors.MISSING_MEMBERS].info[0]
assert any(x in missing_actors for x in ('Eric Idle', 'Terry Gilliam'))
16 changes: 16 additions & 0 deletions cerberus/validator.py
Original file line number Diff line number Diff line change
Expand Up @@ -964,6 +964,22 @@ def _validate_allowed(self, allowed_values, field, value):
if value not in allowed_values:
self._error(field, errors.UNALLOWED_VALUE, value)

def _validate_contains(self, expected_values, field, value):
# just to omit warnings about missing validation schema:
""" {'empty': True } """
if not isinstance(value, Iterable):
return

if (not isinstance(expected_values, Iterable) or
isinstance(expected_values, _str_type)):
expected_values = set((expected_values,))
else:
expected_values = set(expected_values)

missing_values = expected_values - set(value)
if missing_values:
self._error(field, errors.MISSING_MEMBERS, missing_values)

def _validate_dependencies(self, dependencies, field, value):
""" {'type': ('dict', 'hashable', 'list'),
'validator': 'dependencies'} """
Expand Down
25 changes: 25 additions & 0 deletions docs/validation-rules.rst
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,31 @@ Validates if *any* of the provided constraints validates the field. See `\*of-ru

.. versionadded:: 0.9

contains
--------
This rule validates the a container object contains all of the defined items.

.. doctest::

>>> document = {'states': ['peace', 'love', 'unity']}

>>> schema = {'states': {'contains': 'peace'}}
>>> v.validate(document, schema)
True

>>> schema = {'states': {'contains': 'greed'}}
>>> v.validate(document, schema)
False

>>> schema = {'states': {'contains': ['love', 'unity']}}
>>> v.validate(document, schema)
True

>>> schema = {'states': {'contains': ['love', 'laugh']}}
>>> v.validate(document, schema)
False


.. _dependencies:

dependencies
Expand Down

0 comments on commit 0d7114a

Please sign in to comment.