Files
deb-python-sqlalchemy-utils/sqlalchemy_utils/primitives/number_range.py

163 lines
4.6 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# -*- coding: utf-8 -*-
try:
from functools import total_ordering
except ImportError:
from total_ordering import total_ordering
import six
class NumberRangeException(Exception):
pass
class RangeBoundsException(NumberRangeException):
def __init__(self, min_value, max_value):
self.message = 'Min value %d is bigger than max value %d.' % (
min_value,
max_value
)
@total_ordering
class NumberRange(object):
def __init__(self, *args):
if len(args) > 2:
raise NumberRangeException(
'NumberRange takes at most two arguments'
)
elif len(args) == 2:
lower, upper = args
if lower > upper:
raise RangeBoundsException(lower, upper)
self.lower = lower
self.upper = upper
self.lower_inc = self.upper_inc = True
else:
if isinstance(args[0], six.integer_types):
self.lower = self.upper = args[0]
self.lower_inc = self.upper_inc = True
elif isinstance(args[0], six.string_types):
if ',' not in args[0]:
self.lower, self.upper = self.parse_range(args[0])
self.lower_inc = self.upper_inc = True
else:
self.from_range_with_bounds(args[0])
elif hasattr(args[0], 'lower') and hasattr(args[0], 'upper'):
self.lower = args[0].lower
self.upper = args[0].upper
if not args[0].lower_inc:
self.lower += 1
if not args[0].upper_inc:
self.upper -= 1
def from_range_with_bounds(self, value):
"""
Returns new NumberRange object from normalized number range format.
Example ::
range = NumberRange.from_normalized_str('[23, 45]')
range.lower = 23
range.upper = 45
range = NumberRange.from_normalized_str('(23, 45]')
range.lower = 24
range.upper = 45
range = NumberRange.from_normalized_str('(23, 45)')
range.lower = 24
range.upper = 44
"""
values = value[1:-1].split(',')
try:
lower, upper = map(
lambda a: int(a.strip()), values
)
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 = lower
self.upper = upper
def parse_range(self, value):
if value is not None:
values = value.split('-')
if len(values) == 1:
lower = upper = int(value.strip())
else:
try:
lower, upper = map(
lambda a: int(a.strip()), values
)
except ValueError as e:
raise NumberRangeException(str(e))
return lower, upper
@property
def normalized(self):
return '[%s, %s]' % (self.lower, self.upper)
def __eq__(self, other):
try:
return (
self.lower == other.lower and
self.upper == other.upper
)
except AttributeError:
return NotImplemented
def __ne__(self, other):
return not (self == other)
def __gt__(self, other):
try:
return self.lower > other.lower and self.upper > other.upper
except AttributeError:
return NotImplemented
def __repr__(self):
return 'NumberRange(%r, %r)' % (self.lower, self.upper)
def __str__(self):
if self.lower != self.upper:
return '%s - %s' % (self.lower, self.upper)
return str(self.lower)
def __add__(self, other):
"""
[a, b] + [c, d] = [a + c, b + d]
"""
try:
return NumberRange(
self.lower + other.lower,
self.upper + other.upper
)
except AttributeError:
return NotImplemented
def __sub__(self, other):
"""
Defines the substraction operator.
As defined in wikipedia:
[a, b] [c, d] = [a d, b c]
"""
try:
return NumberRange(
self.lower - other.upper,
self.upper - other.lower
)
except AttributeError:
return NotImplemented