Skip to content

Commit

Permalink
Add a new validator Maybe(type) which can be used for values that are
Browse files Browse the repository at this point in the history
either of a certain type or None
  • Loading branch information
AndreaCrotti committed Oct 21, 2016
1 parent 0c5a292 commit fea2d4d
Show file tree
Hide file tree
Showing 2 changed files with 41 additions and 1 deletion.
13 changes: 12 additions & 1 deletion voluptuous/tests/tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
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
validate, ExactSequence, Equal, Unordered, Number, Maybe
)
from voluptuous.humanize import humanize_error
from voluptuous.util import to_utf8_py2, u
Expand Down Expand Up @@ -371,6 +371,7 @@ def test_repr():
max_included=False, msg='number not in range')
coerce_ = Coerce(int, msg="moo")
all_ = All('10', Coerce(int), msg='all msg')
maybe_int = Maybe(int)

assert_equal(repr(match), "Match('a pattern', msg='message')")
assert_equal(repr(replace), "Replace('you', 'I', msg='you and I')")
Expand All @@ -380,6 +381,7 @@ def test_repr():
)
assert_equal(repr(coerce_), "Coerce(int, msg='moo')")
assert_equal(repr(all_), "All('10', Coerce(int, msg=None), msg='all msg')")
assert_equal(repr(maybe_int), "Maybe(<type 'int'>)")


def test_list_validation_messages():
Expand Down Expand Up @@ -499,6 +501,15 @@ def test_unordered():
s([3, 2])


def test_maybe():
assert_raises(TypeError, Maybe, lambda x: x)

s = Schema(Maybe(int))
assert s(1) == 1

assert_raises(Invalid, s, 'foo')


def test_empty_list_as_exact():
s = Schema([])
assert_raises(Invalid, s, [1])
Expand Down
29 changes: 29 additions & 0 deletions voluptuous/validators.py
Original file line number Diff line number Diff line change
Expand Up @@ -459,6 +459,35 @@ def PathExists(v):
raise PathInvalid("Not a Path")


class Maybe(object):
"""Validate that the object is of a given type or is None.
:raises Invalid: if the value is not of the type declared or None
>>> s = Schema(Maybe(int))
>>> s(10)
10
>>> with raises(Invalid):
... s("string")
"""
def __init__(self, kind, msg=None):
if not isinstance(kind, type):
raise TypeError("kind has to be a type")

self.kind = kind
self.msg = msg

def __call__(self, v):
if v is not None and not isinstance(v, self.kind):
raise Invalid(self.msg or "%s must be None or of type %s" % (v, self.kind))

return v

def __repr__(self):
return 'Maybe(%s)' % str(self.kind)


class Range(object):
"""Limit a value to a range.
Expand Down

0 comments on commit fea2d4d

Please sign in to comment.