Skip to content

Commit

Permalink
Handle incomparable values in Range (#414)
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
monopolis authored and alecthomas committed Nov 28, 2019
1 parent f5e64dd commit 858ceee
Show file tree
Hide file tree
Showing 2 changed files with 37 additions and 17 deletions.
13 changes: 13 additions & 0 deletions voluptuous/tests/tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
41 changes: 24 additions & 17 deletions voluptuous/validators.py
Original file line number Diff line number Diff line change
Expand Up @@ -585,23 +585,30 @@ def __init__(self, min=None, max=None, min_included=True,
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,'
Expand Down

0 comments on commit 858ceee

Please sign in to comment.