summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authormonopolis <epheon@gmail.com>2019-11-28 01:13:16 +0100
committerAlec Thomas <alec@swapoff.org>2019-11-28 11:13:16 +1100
commit858ceee119643bca099077e5a3ccbcb176292d1c (patch)
tree045f048921a97e1793a1472de10a5052534de4a9
parentf5e64dd7e26fb2de0ca4baec42c426c60cb061c2 (diff)
downloadvoluptuous-858ceee119643bca099077e5a3ccbcb176292d1c.tar.gz
Handle incomparable values in Range (#414)
In Python3 some values that are comparable in Python2 are no longer comparable. One instance of this is None, which in Python2 is always less than any other object. In Python3, however, a TypeError is raised if it is used in a comparison. This commit handles said TypeError and issues a RangeInvalid exception instead.
-rw-r--r--voluptuous/tests/tests.py13
-rw-r--r--voluptuous/validators.py41
2 files changed, 37 insertions, 17 deletions
diff --git a/voluptuous/tests/tests.py b/voluptuous/tests/tests.py
index 3ff71e4..acd6642 100644
--- a/voluptuous/tests/tests.py
+++ b/voluptuous/tests/tests.py
@@ -583,6 +583,19 @@ def test_range_exlcudes_nan():
assert_raises(MultipleInvalid, s, float('nan'))
+def test_range_excludes_none():
+ s = Schema(Range(min=0, max=10))
+ assert_raises(MultipleInvalid, s, None)
+
+
+def test_range_excludes_unordered_object():
+ class MyObject(object):
+ pass
+
+ s = Schema(Range(min=0, max=10))
+ assert_raises(MultipleInvalid, s, MyObject())
+
+
def test_equal():
s = Schema(Equal(1))
s(1)
diff --git a/voluptuous/validators.py b/voluptuous/validators.py
index 328e2bf..0e0e1fc 100644
--- a/voluptuous/validators.py
+++ b/voluptuous/validators.py
@@ -585,23 +585,30 @@ class Range(object):
self.msg = msg
def __call__(self, v):
- if self.min_included:
- if self.min is not None and not v >= self.min:
- raise RangeInvalid(
- self.msg or 'value must be at least %s' % self.min)
- else:
- if self.min is not None and not v > self.min:
- raise RangeInvalid(
- self.msg or 'value must be higher than %s' % self.min)
- if self.max_included:
- if self.max is not None and not v <= self.max:
- raise RangeInvalid(
- self.msg or 'value must be at most %s' % self.max)
- else:
- if self.max is not None and not v < self.max:
- raise RangeInvalid(
- self.msg or 'value must be lower than %s' % self.max)
- return v
+ try:
+ if self.min_included:
+ if self.min is not None and not v >= self.min:
+ raise RangeInvalid(
+ self.msg or 'value must be at least %s' % self.min)
+ else:
+ if self.min is not None and not v > self.min:
+ raise RangeInvalid(
+ self.msg or 'value must be higher than %s' % self.min)
+ if self.max_included:
+ if self.max is not None and not v <= self.max:
+ raise RangeInvalid(
+ self.msg or 'value must be at most %s' % self.max)
+ else:
+ if self.max is not None and not v < self.max:
+ raise RangeInvalid(
+ self.msg or 'value must be lower than %s' % self.max)
+
+ return v
+
+ # Objects that lack a partial ordering, e.g. None will raise TypeError
+ except TypeError:
+ raise RangeInvalid(
+ self.msg or 'value must have a partial ordering')
def __repr__(self):
return ('Range(min=%r, max=%r, min_included=%r,'