Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Interval exception detect when modify the lower or the upper property on an exist interval instance #48

Open
wants to merge 12 commits into
base: master
Choose a base branch
from
Open
28 changes: 21 additions & 7 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -1,11 +1,25 @@
language: python
python:
- 2.7
- 3.3
- 3.4
- 3.5
- pypy
- pypy3
matrix:
include:
- python: 2.7
dist: trusty
sudo: false
- python: 3.3
dist: trusty
sudo: false
- python: 3.4
dist: trusty
sudo: false
- python: 3.5
dist: trusty
sudo: false
- python: pypy
dist: trusty
sudo: false
- python: pypy3
dist: trusty
sudo: false

install:
- pip install -e ".[test]"
script:
Expand Down
80 changes: 48 additions & 32 deletions intervals/interval.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,9 +40,9 @@ def py2round(value):
Python 3 also returns an int; Python 2 returns a float.
"""
if value > 0:
return float(floor(float(value)+0.5))
return float(floor(float(value) + 0.5))
else:
return float(ceil(float(value)-0.5))
return float(ceil(float(value) - 0.5))


def canonicalize_lower(interval, inc=True):
Expand Down Expand Up @@ -86,11 +86,11 @@ def canonicalize(interval, lower_inc=True, upper_inc=False):
def coerce_interval(func):
def wrapper(self, arg):
if (
isinstance(arg, list) or
isinstance(arg, tuple) or
isinstance(arg, self.type) or
isinstance(arg, type(self)) or
arg == inf or arg == -inf
isinstance(arg, list) or
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please remove this and all similar style changes where you add extra indents.

Copy link
Author

@Taktoshi Taktoshi Jan 23, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have revised all that changes.

isinstance(arg, tuple) or
isinstance(arg, self.type) or
isinstance(arg, type(self)) or
arg == inf or arg == -inf
):
try:
if arg is not None:
Expand All @@ -103,6 +103,7 @@ def wrapper(self, arg):
except (ValueError, TypeError):
pass
return func(self, arg)

return wrapper


Expand All @@ -112,11 +113,11 @@ class AbstractInterval(object):
parser = IntervalParser()

def __init__(
self,
bounds,
lower_inc=None,
upper_inc=None,
step=None
self,
bounds,
lower_inc=None,
upper_inc=None,
step=None
):
"""
Parse given args and assign lower and upper bound for this number
Expand Down Expand Up @@ -184,15 +185,19 @@ def __init__(
self.parser(bounds, lower_inc, upper_inc)
)

if self.lower > self.upper:
self.range_init_and_setter_exception_detect(self.lower, self.upper, self.lower_inc, self.upper_inc)

@classmethod
def range_init_and_setter_exception_detect(cls, lower, upper, lower_inc, upper_inc):
if lower > upper:
raise RangeBoundsException(
self.lower,
self.upper
lower,
upper
)
if (
self.lower == self.upper and
not self.lower_inc and
not self.upper_inc
lower == upper and
not lower_inc and
not upper_inc
):
raise IllegalArgument(
'The bounds may be equal only if at least one of the bounds '
Expand Down Expand Up @@ -320,8 +325,13 @@ def lower(self):
def lower(self, value):
value = self.coerce_value(value)
if value is None:
if hasattr(self, 'upper_inc'):
self.range_init_and_setter_exception_detect(-inf, self.upper, self.lower_inc, self.upper_inc)
self._lower = -inf
else:
if hasattr(self, 'upper_inc'):
self.range_init_and_setter_exception_detect(self.round_value_by_step(value), self.upper, self.lower_inc,
self.upper_inc)
self._lower = self.round_value_by_step(value)

@property
Expand All @@ -332,8 +342,13 @@ def upper(self):
def upper(self, value):
value = self.coerce_value(value)
if value is None:
if hasattr(self, 'upper_inc'):
self.range_init_and_setter_exception_detect(self.lower, inf, self.lower_inc, self.upper_inc)
self._upper = inf
else:
if hasattr(self, 'upper_inc'):
self.range_init_and_setter_exception_detect(self.lower, self.round_value_by_step(value), self.lower_inc,
self.upper_inc)
self._upper = self.round_value_by_step(value)

def round_value_by_step(self, value):
Expand Down Expand Up @@ -385,11 +400,11 @@ def __str__(self):

def equals(self, other):
return (
self.lower == other.lower and
self.upper == other.upper and
self.lower_inc == other.lower_inc and
self.upper_inc == other.upper_inc and
self.type == other.type
self.lower == other.lower and
self.upper == other.upper and
self.lower_inc == other.lower_inc and
self.upper_inc == other.upper_inc and
self.type == other.type
)

@coerce_interval
Expand Down Expand Up @@ -443,8 +458,8 @@ def __contains__(self, other):
else operator.gt
)
return (
lower_op(self.lower, other.lower) and
upper_op(self.upper, other.upper)
lower_op(self.lower, other.lower) and
upper_op(self.upper, other.upper)
)

@property
Expand Down Expand Up @@ -476,12 +491,12 @@ def degenerate(self):
def empty(self):
if self.discrete and not self.degenerate:
return (
self.upper - self.lower == self.step
and not (self.upper_inc or self.lower_inc)
self.upper - self.lower == self.step
and not (self.upper_inc or self.lower_inc)
)
return (
self.upper == self.lower
and not (self.lower_inc and self.upper_inc)
self.upper == self.lower
and not (self.lower_inc and self.upper_inc)
)

def __bool__(self):
Expand Down Expand Up @@ -672,10 +687,10 @@ def is_connected(self, other):
* [1, 3) and (3, 5) are not connected
"""
return self.upper > other.lower and other.upper > self.lower or (
self.upper == other.lower and (self.upper_inc or other.lower_inc)
self.upper == other.lower and (self.upper_inc or other.lower_inc)
) or (
self.lower == other.upper and (self.lower_inc or other.upper_inc)
)
self.lower == other.upper and (self.lower_inc or other.upper_inc)
)


class NumberInterval(AbstractInterval):
Expand Down Expand Up @@ -792,4 +807,5 @@ def from_string(self, value):
'Could not initialize interval.'
)


Interval = IntervalFactory()
100 changes: 72 additions & 28 deletions tests/interval/test_initialization.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,18 +20,18 @@ def test_string_as_constructor_param(self):
with raises(TypeError) as e:
FloatInterval('(0.2, 0.5)')
assert (
'First argument should be a list or tuple. If you wish to '
'initialize an interval from string, use from_string factory '
'method.'
) in str(e)
'First argument should be a list or tuple. If you wish to '
'initialize an interval from string, use from_string factory '
'method.'
) in str(e)

def test_invalid_argument(self):
with raises(IllegalArgument) as e:
FloatInterval((0, 0))
assert (
'The bounds may be equal only if at least one of the bounds is '
'closed.'
) in str(e)
'The bounds may be equal only if at least one of the bounds is '
'closed.'
) in str(e)

def test_floats(self):
interval = FloatInterval((0.2, 0.5))
Expand Down Expand Up @@ -155,11 +155,11 @@ def test_empty_string_as_lower_bound_for_char_interval(self):
@mark.parametrize(
('number_range', 'lower', 'upper'),
(
('-2-2', -2, 2),
('-3--2', -3, -2),
('2-3', 2, 3),
('2-', 2, inf),
('-5', -5, -5)
('-2-2', -2, 2),
('-3--2', -3, -2),
('2-3', 2, 3),
('2-', 2, inf),
('-5', -5, -5)
)
)
def test_hyphen_format(self, number_range, lower, upper):
Expand All @@ -170,18 +170,18 @@ def test_hyphen_format(self, number_range, lower, upper):
@mark.parametrize(
('constructor', 'number_range'),
(
(IntInterval, (3, 2)),
(IntInterval, [4, 2]),
(IntInterval, (float('inf'), 2)),
(CharacterInterval, ('c', 'b')),
(CharacterInterval, ('d', 'b')),
(CharacterInterval, (inf, 'b')),
(IntInterval, (3, 2)),
(IntInterval, [4, 2]),
(IntInterval, (float('inf'), 2)),
(CharacterInterval, ('c', 'b')),
(CharacterInterval, ('d', 'b')),
(CharacterInterval, (inf, 'b')),
)
)
def test_raises_exception_for_badly_constructed_range(
self,
constructor,
number_range
self,
constructor,
number_range
):
with raises(RangeBoundsException):
constructor(number_range)
Expand All @@ -191,14 +191,58 @@ class TestTypeGuessing(object):
@mark.parametrize(
('number_range', 'type'),
(
((2, 3), int),
([-6, 8], int),
(8.5, float),
([Decimal(2), 9], int),
([Decimal('0.5'), 9], float),
([date(2000, 1, 1), inf], date),
(('a', 'e'), str),
((2, 3), int),
([-6, 8], int),
(8.5, float),
([Decimal(2), 9], int),
([Decimal('0.5'), 9], float),
([date(2000, 1, 1), inf], date),
(('a', 'e'), str),
)
)
def test_guesses_types(self, number_range, type):
assert Interval(number_range).type == type


class TestIntervalChanging(object):
@mark.parametrize(
('constructor', 'number_range', 'bad_lower'),
(
(IntInterval, (1, 2), 3),
(IntInterval, [1, 2], 3),
(IntInterval, (1, 2), float('inf')),
(CharacterInterval, ('a', 'b'), 'c'),
(CharacterInterval, ('a', 'b'), 'd'),
(CharacterInterval, ('a', 'b'), inf),
)
)
def test_raises_exception_for_badly_lower_changing(
self,
constructor,
number_range,
bad_lower
):
with raises(RangeBoundsException):
interval = constructor(number_range)
interval.lower = bad_lower

@mark.parametrize(
('constructor', 'number_range', 'bad_upper'),
(
(IntInterval, (1, 2), 0),
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use the indentation of 4 spaces not 8.

Copy link
Author

@Taktoshi Taktoshi Jan 23, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The indentation has been revised to 4.

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There are still lines with indentation of 8 not 4

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK, I will check again and change them.

(IntInterval, [1, 2], 0),
(IntInterval, (1, 2), float('-inf')),
(CharacterInterval, ('b', 'c'), 'a'),
(CharacterInterval, ('b', 'd'), 'a'),
(CharacterInterval, ('b', 'c'), -inf),
)
)
def test_raises_exception_for_badly_upper_changing(
self,
constructor,
number_range,
bad_upper
):
with raises(RangeBoundsException):
interval = constructor(number_range)
interval.upper = bad_upper