Improved NumberRange handling
This commit is contained in:
@@ -65,7 +65,8 @@ class NumberRange(object):
|
|||||||
def lower(self, value):
|
def lower(self, value):
|
||||||
if value is None:
|
if value is None:
|
||||||
self._lower = -float('inf')
|
self._lower = -float('inf')
|
||||||
self._lower = value
|
else:
|
||||||
|
self._lower = value
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def upper(self):
|
def upper(self):
|
||||||
@@ -75,7 +76,22 @@ class NumberRange(object):
|
|||||||
def upper(self, value):
|
def upper(self, value):
|
||||||
if value is None:
|
if value is None:
|
||||||
self._upper = float('inf')
|
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):
|
def parse_object(self, obj):
|
||||||
self.lower = obj.lower
|
self.lower = obj.lower
|
||||||
@@ -93,7 +109,10 @@ class NumberRange(object):
|
|||||||
lower, upper = seq
|
lower, upper = seq
|
||||||
self.lower = parse_number(lower)
|
self.lower = parse_number(lower)
|
||||||
self.upper = parse_number(upper)
|
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):
|
def parse_integer(self, value):
|
||||||
self.lower = self.upper = value
|
self.lower = self.upper = value
|
||||||
@@ -125,14 +144,8 @@ class NumberRange(object):
|
|||||||
except ValueError as e:
|
except ValueError as e:
|
||||||
raise NumberRangeException(e.message)
|
raise NumberRangeException(e.message)
|
||||||
|
|
||||||
self.lower_inc = value[0] == '('
|
self.lower_inc = value[0] == '['
|
||||||
if self.lower_inc:
|
self.upper_inc = value[-1] == ']'
|
||||||
lower += 1
|
|
||||||
|
|
||||||
self.upper_inc = value[-1] == ')'
|
|
||||||
if self.upper_inc:
|
|
||||||
upper -= 1
|
|
||||||
|
|
||||||
self.lower = lower
|
self.lower = lower
|
||||||
self.upper = upper
|
self.upper = upper
|
||||||
|
|
||||||
@@ -151,9 +164,11 @@ class NumberRange(object):
|
|||||||
|
|
||||||
@property
|
@property
|
||||||
def normalized(self):
|
def normalized(self):
|
||||||
return '[%s, %s]' % (
|
return '%s%s, %s%s' % (
|
||||||
self.lower if self.lower is not None else '',
|
'[' if self.lower_inc else '(',
|
||||||
self.upper if self.upper is not None 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):
|
def __eq__(self, other):
|
||||||
|
@@ -30,6 +30,11 @@ class TestNumberRangeInit(object):
|
|||||||
assert num_range.lower == 1
|
assert num_range.lower == 1
|
||||||
assert num_range.upper == 3
|
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):
|
def test_supports_exact_ranges_as_strings(self):
|
||||||
num_range = NumberRange('3')
|
num_range = NumberRange('3')
|
||||||
assert num_range.lower == 3
|
assert num_range.lower == 3
|
||||||
@@ -93,6 +98,36 @@ def test_raises_exception_for_badly_constructed_range(number_range):
|
|||||||
NumberRange(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):
|
class TestArithmeticOperators(object):
|
||||||
def test_add_operator(self):
|
def test_add_operator(self):
|
||||||
assert NumberRange(1, 2) + NumberRange(1, 2) == NumberRange(2, 4)
|
assert NumberRange(1, 2) + NumberRange(1, 2) == NumberRange(2, 4)
|
||||||
|
@@ -36,9 +36,10 @@ class TestNumberRangeType(TestCase):
|
|||||||
)
|
)
|
||||||
self.session.add(building)
|
self.session.add(building)
|
||||||
self.session.commit()
|
self.session.commit()
|
||||||
|
|
||||||
building = self.session.query(self.Building).first()
|
building = self.session.query(self.Building).first()
|
||||||
assert building.persons_at_night.lower == 1
|
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):
|
def test_infinite_lower_bound(self):
|
||||||
building = self.Building(
|
building = self.Building(
|
||||||
@@ -47,8 +48,8 @@ class TestNumberRangeType(TestCase):
|
|||||||
self.session.add(building)
|
self.session.add(building)
|
||||||
self.session.commit()
|
self.session.commit()
|
||||||
building = self.session.query(self.Building).first()
|
building = self.session.query(self.Building).first()
|
||||||
assert building.persons_at_night.lower is None
|
assert building.persons_at_night.lower == -float('inf')
|
||||||
assert building.persons_at_night.upper is 1
|
assert building.persons_at_night.upper == 1
|
||||||
|
|
||||||
def test_nullify_number_range(self):
|
def test_nullify_number_range(self):
|
||||||
building = self.Building(
|
building = self.Building(
|
||||||
|
Reference in New Issue
Block a user