diff --git a/voluptuous/tests/tests.py b/voluptuous/tests/tests.py index aaa260d..3bf52b4 100644 --- a/voluptuous/tests/tests.py +++ b/voluptuous/tests/tests.py @@ -11,7 +11,7 @@ Replace, Range, Coerce, All, Any, Length, FqdnUrl, ALLOW_EXTRA, PREVENT_EXTRA, validate, ExactSequence, Equal, Unordered, Number, Maybe, Datetime, Date, Contains, Marker, IsDir, IsFile, PathExists, SomeOf, TooManyValid, Self, - raises, Union) + raises, Union, Clamp) from voluptuous.humanize import humanize_error from voluptuous.util import u, Capitalize, Lower, Strip, Title, Upper @@ -585,7 +585,23 @@ def test_fix_157(): assert_raises(MultipleInvalid, s, ['four']) -def test_range_exlcudes_nan(): +def test_range_inside(): + s = Schema(Range(min=0, max=10)) + assert_equal(5, s(5)) + + +def test_range_outside(): + s = Schema(Range(min=0, max=10)) + assert_raises(MultipleInvalid, s, 12) + assert_raises(MultipleInvalid, s, -1) + + +def test_range_no_upper_limit(): + s = Schema(Range(min=0)) + assert_equal(123, s(123)) + + +def test_range_excludes_nan(): s = Schema(Range(min=0, max=10)) assert_raises(MultipleInvalid, s, float('nan')) @@ -595,6 +611,11 @@ def test_range_excludes_none(): assert_raises(MultipleInvalid, s, None) +def test_range_excludes_string(): + s = Schema(Range(min=0, max=10)) + assert_raises(MultipleInvalid, s, "abc") + + def test_range_excludes_unordered_object(): class MyObject(object): pass @@ -603,6 +624,58 @@ class MyObject(object): assert_raises(MultipleInvalid, s, MyObject()) +def test_clamp_inside(): + s = Schema(Clamp(min=1, max=10)) + assert_equal(5, s(5)) + + +def test_clamp_above(): + s = Schema(Clamp(min=1, max=10)) + assert_equal(10, s(12)) + + +def test_clamp_below(): + s = Schema(Clamp(min=1, max=10)) + assert_equal(1, s(-3)) + + +def test_clamp_invalid(): + s = Schema(Clamp(min=1, max=10)) + if sys.version_info.major >= 3: + assert_raises(MultipleInvalid, s, None) + assert_raises(MultipleInvalid, s, "abc") + else: + assert_equal(1, s(None)) + + +def test_length_ok(): + v1 = ['a', 'b', 'c'] + s = Schema(Length(min=1, max=10)) + assert_equal(v1, s(v1)) + v2 = "abcde" + assert_equal(v2, s(v2)) + + +def test_length_too_short(): + v1 = [] + s = Schema(Length(min=1, max=10)) + assert_raises(MultipleInvalid, s, v1) + v2 = '' + assert_raises(MultipleInvalid, s, v2) + + +def test_length_too_long(): + v = ['a', 'b', 'c'] + s = Schema(Length(min=0, max=2)) + assert_raises(MultipleInvalid, s, v) + + +def test_length_invalid(): + v = None + s = Schema(Length(min=0, max=2)) + assert_raises(MultipleInvalid, s, v) + + def test_equal(): s = Schema(Equal(1)) s(1) diff --git a/voluptuous/validators.py b/voluptuous/validators.py index 9b0e030..ce1567e 100644 --- a/voluptuous/validators.py +++ b/voluptuous/validators.py @@ -607,10 +607,10 @@ def __call__(self, v): return v - # Objects that lack a partial ordering, e.g. None will raise TypeError + # Objects that lack a partial ordering, e.g. None or strings will raise TypeError except TypeError: raise RangeInvalid( - self.msg or 'value must have a partial ordering') + self.msg or 'invalid value or type (must have a partial ordering)') def __repr__(self): return ('Range(min=%r, max=%r, min_included=%r,' @@ -640,11 +640,17 @@ def __init__(self, min=None, max=None, msg=None): self.msg = msg def __call__(self, v): - if self.min is not None and v < self.min: - v = self.min - if self.max is not None and v > self.max: - v = self.max - return v + try: + if self.min is not None and v < self.min: + v = self.min + if self.max is not None and v > self.max: + v = self.max + return v + + # Objects that lack a partial ordering, e.g. None or strings will raise TypeError + except TypeError: + raise RangeInvalid( + self.msg or 'invalid value or type (must have a partial ordering)') def __repr__(self): return 'Clamp(min=%s, max=%s)' % (self.min, self.max) @@ -659,13 +665,19 @@ def __init__(self, min=None, max=None, msg=None): self.msg = msg def __call__(self, v): - if self.min is not None and len(v) < self.min: - raise LengthInvalid( - self.msg or 'length of value must be at least %s' % self.min) - if self.max is not None and len(v) > self.max: - raise LengthInvalid( - self.msg or 'length of value must be at most %s' % self.max) - return v + try: + if self.min is not None and len(v) < self.min: + raise LengthInvalid( + self.msg or 'length of value must be at least %s' % self.min) + if self.max is not None and len(v) > self.max: + raise LengthInvalid( + self.msg or 'length of value must be at most %s' % self.max) + return v + + # Objects that havbe no length e.g. None or strings will raise TypeError + except TypeError: + raise RangeInvalid( + self.msg or 'invalid value or type') def __repr__(self): return 'Length(min=%s, max=%s)' % (self.min, self.max)