diff --git a/voluptuous/error.py b/voluptuous/error.py index 064aae1..aa87500 100644 --- a/voluptuous/error.py +++ b/voluptuous/error.py @@ -101,6 +101,10 @@ class ValueInvalid(Invalid): """The value was found invalid by evaluation function.""" +class ContainsInvalid(Invalid): + """List does not contain item""" + + class ScalarInvalid(Invalid): """Scalars did not match.""" diff --git a/voluptuous/tests/tests.py b/voluptuous/tests/tests.py index 6b320ef..1f9b7ff 100644 --- a/voluptuous/tests/tests.py +++ b/voluptuous/tests/tests.py @@ -6,7 +6,8 @@ Schema, Required, Optional, Extra, Invalid, In, Remove, Literal, Url, MultipleInvalid, LiteralInvalid, NotIn, Match, Email, Replace, Range, Coerce, All, Any, Length, FqdnUrl, ALLOW_EXTRA, PREVENT_EXTRA, - validate, ExactSequence, Equal, Unordered, Number, Maybe, Datetime, Date + validate, ExactSequence, Equal, Unordered, Number, Maybe, Datetime, Date, + Contains ) from voluptuous.humanize import humanize_error from voluptuous.util import to_utf8_py2, u @@ -73,6 +74,17 @@ def test_not_in(): assert False, "Did not raise NotInInvalid" +def test_contains(): + """Verify contains validation method.""" + schema = Schema({'color': Contains('red')}) + schema({'color': ['blue', 'red', 'yellow']}) + try: + schema({'color': ['blue', 'yellow']}) + except Invalid as e: + assert_equal(str(e), + "value is not allowed for dictionary value @ data['color']") + + def test_remove(): """Verify that Remove works.""" # remove dict keys diff --git a/voluptuous/validators.py b/voluptuous/validators.py index 08fb0bf..8756a26 100644 --- a/voluptuous/validators.py +++ b/voluptuous/validators.py @@ -10,13 +10,13 @@ from error import (MultipleInvalid, CoerceInvalid, TrueInvalid, FalseInvalid, BooleanInvalid, Invalid, AnyInvalid, AllInvalid, MatchInvalid, UrlInvalid, EmailInvalid, FileInvalid, DirInvalid, RangeInvalid, PathInvalid, ExactSequenceInvalid, LengthInvalid, DatetimeInvalid, DateInvalid, InInvalid, - TypeInvalid, NotInInvalid) + TypeInvalid, NotInInvalid, ContainsInvalid) except ImportError: from .schema_builder import Schema, raises, message from .error import (MultipleInvalid, CoerceInvalid, TrueInvalid, FalseInvalid, BooleanInvalid, Invalid, AnyInvalid, AllInvalid, MatchInvalid, UrlInvalid, EmailInvalid, FileInvalid, DirInvalid, RangeInvalid, PathInvalid, ExactSequenceInvalid, LengthInvalid, DatetimeInvalid, DateInvalid, InInvalid, - TypeInvalid, NotInInvalid) + TypeInvalid, NotInInvalid, ContainsInvalid) if sys.version_info >= (3,): @@ -680,6 +680,26 @@ def __repr__(self): return 'NotIn(%s)' % (self.container,) +class Contains(object): + """Validate that a value is in a collection.""" + + def __init__(self, item, msg=None): + self.item = item + self.msg = msg + + def __call__(self, v): + try: + check = self.item not in v + except TypeError: + check = True + if check: + raise ContainsInvalid(self.msg or 'value is not allowed') + return v + + def __repr__(self): + return 'Contains(%s)' % (self.item, ) + + class ExactSequence(object): """Matches each element in a sequence against the corresponding element in the validators.