Improved NumberRange handling
This commit is contained in:
@@ -65,7 +65,8 @@ class NumberRange(object):
|
||||
def lower(self, value):
|
||||
if value is None:
|
||||
self._lower = -float('inf')
|
||||
self._lower = value
|
||||
else:
|
||||
self._lower = value
|
||||
|
||||
@property
|
||||
def upper(self):
|
||||
@@ -75,7 +76,22 @@ class NumberRange(object):
|
||||
def upper(self, value):
|
||||
if value is None:
|
||||
self._upper = float('inf')
|
||||
self._upper = value
|
||||
else:
|
||||
self._upper = value
|
||||
|
||||
@property
|
||||
def open(self):
|
||||
"""
|
||||
Returns whether or not this object is an open interval.
|
||||
"""
|
||||
return not self.lower_inc and not self.upper_inc
|
||||
|
||||
@property
|
||||
def closed(self):
|
||||
"""
|
||||
Returns whether or not this object is a closed interval.
|
||||
"""
|
||||
return self.lower_inc and self.upper_inc
|
||||
|
||||
def parse_object(self, obj):
|
||||
self.lower = obj.lower
|
||||
@@ -93,7 +109,10 @@ class NumberRange(object):
|
||||
lower, upper = seq
|
||||
self.lower = parse_number(lower)
|
||||
self.upper = parse_number(upper)
|
||||
self.lower_inc = self.upper_inc = True
|
||||
if isinstance(seq, tuple):
|
||||
self.lower_inc = self.upper_inc = False
|
||||
else:
|
||||
self.lower_inc = self.upper_inc = True
|
||||
|
||||
def parse_integer(self, value):
|
||||
self.lower = self.upper = value
|
||||
@@ -125,14 +144,8 @@ class NumberRange(object):
|
||||
except ValueError as e:
|
||||
raise NumberRangeException(e.message)
|
||||
|
||||
self.lower_inc = value[0] == '('
|
||||
if self.lower_inc:
|
||||
lower += 1
|
||||
|
||||
self.upper_inc = value[-1] == ')'
|
||||
if self.upper_inc:
|
||||
upper -= 1
|
||||
|
||||
self.lower_inc = value[0] == '['
|
||||
self.upper_inc = value[-1] == ']'
|
||||
self.lower = lower
|
||||
self.upper = upper
|
||||
|
||||
@@ -151,9 +164,11 @@ class NumberRange(object):
|
||||
|
||||
@property
|
||||
def normalized(self):
|
||||
return '[%s, %s]' % (
|
||||
self.lower if self.lower is not None else '',
|
||||
self.upper if self.upper is not None else ''
|
||||
return '%s%s, %s%s' % (
|
||||
'[' if self.lower_inc else '(',
|
||||
self.lower if self.lower != -float('inf') else '',
|
||||
self.upper if self.upper != float('inf') else '',
|
||||
']' if self.upper_inc else ')'
|
||||
)
|
||||
|
||||
def __eq__(self, other):
|
||||
|
@@ -30,6 +30,11 @@ class TestNumberRangeInit(object):
|
||||
assert num_range.lower == 1
|
||||
assert num_range.upper == 3
|
||||
|
||||
def test_empty_string_as_upper_bound(self):
|
||||
num_range = NumberRange('[1,)')
|
||||
assert num_range.lower == 1
|
||||
assert num_range.upper == float('inf')
|
||||
|
||||
def test_supports_exact_ranges_as_strings(self):
|
||||
num_range = NumberRange('3')
|
||||
assert num_range.lower == 3
|
||||
@@ -93,6 +98,36 @@ def test_raises_exception_for_badly_constructed_range(number_range):
|
||||
NumberRange(number_range)
|
||||
|
||||
|
||||
@mark.parametrize(('number_range', 'is_open'),
|
||||
(
|
||||
((2, 3), True),
|
||||
('(2, 5)', True),
|
||||
('[3, 4)', False),
|
||||
('(4, 5]', False),
|
||||
('3 - 4', False),
|
||||
([4, 5], False),
|
||||
('[4, 5]', False)
|
||||
)
|
||||
)
|
||||
def test_open(number_range, is_open):
|
||||
assert NumberRange(number_range).open == is_open
|
||||
|
||||
|
||||
@mark.parametrize(('number_range', 'is_closed'),
|
||||
(
|
||||
((2, 3), False),
|
||||
('(2, 5)', False),
|
||||
('[3, 4)', False),
|
||||
('(4, 5]', False),
|
||||
('3 - 4', True),
|
||||
([4, 5], True),
|
||||
('[4, 5]', True)
|
||||
)
|
||||
)
|
||||
def test_closed(number_range, is_closed):
|
||||
assert NumberRange(number_range).closed == is_closed
|
||||
|
||||
|
||||
class TestArithmeticOperators(object):
|
||||
def test_add_operator(self):
|
||||
assert NumberRange(1, 2) + NumberRange(1, 2) == NumberRange(2, 4)
|
||||
|
@@ -36,9 +36,10 @@ class TestNumberRangeType(TestCase):
|
||||
)
|
||||
self.session.add(building)
|
||||
self.session.commit()
|
||||
|
||||
building = self.session.query(self.Building).first()
|
||||
assert building.persons_at_night.lower == 1
|
||||
assert building.persons_at_night.upper is None
|
||||
assert building.persons_at_night.upper == float('inf')
|
||||
|
||||
def test_infinite_lower_bound(self):
|
||||
building = self.Building(
|
||||
@@ -47,8 +48,8 @@ class TestNumberRangeType(TestCase):
|
||||
self.session.add(building)
|
||||
self.session.commit()
|
||||
building = self.session.query(self.Building).first()
|
||||
assert building.persons_at_night.lower is None
|
||||
assert building.persons_at_night.upper is 1
|
||||
assert building.persons_at_night.lower == -float('inf')
|
||||
assert building.persons_at_night.upper == 1
|
||||
|
||||
def test_nullify_number_range(self):
|
||||
building = self.Building(
|
||||
|
Reference in New Issue
Block a user