# Integer field in Python with extra constraints (Testable class)

I am trying to write an integer field in Python with the following attributes:

• Minimum
• Exclusive minimum
• Maximum
• Exclusive maximum
• Multiple of
• Value

Basically, given the constraints and the value, the class will have a validate method which will return the errors if they occur. If the minimum is not null, then the exclusive minimum is explicitly set to null to avoid redundancy. The same is done for maximum and exclusive maximum. All the constraints can be null. The constraints work as follows:

• The value must be greater than or equal to the minimum.
• The value must be greater than the exclusive minimum.
• The value must be less than or equal to the maximum.
• The value must be less than the exclusive maximum.
• The multiple of must be a factor of the value.

Note: All the constraints are assumed to be integers, so no type checks are done. This is because these values are coming from a database, so type will always be integer or null.

So far, I have written the following class:

def is_not_factor(dividend, divisor):
"""Checks if the given divisor is not a factor of the given dividend.

Notes:
- Since both the dividend and divisor are coming from the
database, it is assumed that both these parameters are numbers, and
the divisor is non-zero. Therefore, no type checks are done.
"""
return dividend % divisor != 0

class IntegerField:
def add_error(self, key, error):
self.errors.setdefault(key, [])
self.errors[key].append(error)

def __init__(self, minimum=None, exclusive_minimum=None,
maximum=None, exclusive_maximum=None, multiple_of=None):
self.minimum = minimum
self.exclusive_minimum = exclusive_minimum
self.maximum = maximum
self.exclusive_maximum = exclusive_maximum
self.multiple_of = multiple_of

if self.minimum:
self.exclusive_minimum = None
if self.maximum:
self.exclusive_maximum = None

self.value = None
self.errors = {}

if (self.minimum and self.maximum):
if self.minimum > self.maximum:
'minimum',
f'The minimum must be less than or equal to the maximum '
f'of {self.maximum}.'
)
'maximum',
f'The maximum must be greater than or equal to the '
f'minimum of {self.minimum}.'
)
elif (self.minimum and self.exclusive_maximum):
if self.minimum >= self.exclusive_maximum:
'minimum',
f'The minimum must be less than the exclusive maximum of '
f'{self.exclusive_maximum}.'
)
'exclusive_maximum',
f'The exclusive maximum must be greater than the '
f'minimum of {self.minimum}.'
)
elif (self.exclusive_minimum and self.maximum):
if self.exclusive_minimum >= self.maximum:
'exclusive_minimum',
f'The exclusive minimum must be less than the maximum of '
f'{self.maximum}.'
)
'maximum',
f'The maximum must be greater than the exclusive minimum '
f'of {self.exclusive_minimum}.'
)
elif (self.exclusive_minimum and self.exclusive_maximum):
if self.exclusive_minimum >= (self.exclusive_maximum - 1):
'exclusive_minimum',
f'The exclusive minimum must be less than one minus the '
f'exclusive maximum of {self.exclusive_maximum}. For '
f'example, if the exclusive maximum is set to 101, then '
f'the exclusive minimum must be less than 100, ie, 99 or '
f'less.'
)
'exclusive_maximum',
f'The exclusive maximum must be greater than one plus the '
f'exclusive minimum of {self.exclusive_minimum}. For '
f'example, if the exclusive minimum is set to 99, then '
f'the exclusive maximum must be greater than 100, ie, 101 '
f'or more.'
)

if self.multiple_of:
if self.multiple_of == 0:
'multiple_of', 'The multiple of must be non-zero'
)

if bool(self.errors):
print(f'The following errors were encountered: {self.errors}')
# Raise the errors

def set_value(self, value):
self.value = value

def validate(self):
if self.value:
print('Please set a value.')
else:
validation_errors = []

if self.multiple_of:
if is_not_factor(self.value, self.multiple_of):
validation_errors.append(
f'The value must be a multiple of {self.multiple_of}.'
)

if self.minimum:
if self.value < self.minimum:
validation_errors.append(
f'The value must be greater than or equal to the minimum '
f'of {self.minimum}.'
)
return
elif self.exclusive_minimum:
if self.value <= self.exclusive_minimum:
validation_errors.append(
f'The value must be greater than the exclusive minimum of '
f'{self.exclusive_minimum}.'
)
return

if self.maximum:
if self.value > self.maximum:
validation_errors.append(
f'The value must be less than or equal to the maximum of '
f'{self.maximum}.'
)
elif self.exclusive_maximum:
if self.value >= self.exclusive_maximum:
validation_errors.append(
f'The value must be less than the exclusive maximum of '
f'{self.exclusive_maximum}.'
)


Am I missing something in my class? Specifically, I would like to know if the two methods (init and validate) will work in every possible scenario or not. Moreover, is my current code checking for every single combination of constraints, or am I missing something?

• is_not_factor is missing. – vnp Jul 19 '19 at 0:03
• Please assume that its coming from another module. – darkhorse Jul 19 '19 at 11:05

### __init__()

There appears to be a typo in:

if self.multiple_of:
if self.multiple_of == 0:  <<<


### validate()

Should the first check be:

if self.value == None:
print('Please set a value.')


Some tests end with a return statement, other's don't. This kind of inconsistency could be an indication of an error.

Nothing gets done with validation_errors, it is just discarded when the validate() returns.

• Those are conditions which can never occur at the same time though. For example, if the minimum exists, then the exclusive minimum is set to null. So why bother checking 3 other conditions knowing they can never be true? – darkhorse Jul 20 '19 at 1:43
• @darkhorse the problem description doesn't say that the class can't be initialized with both a minimum and an exclusive_minimum, etc. If that it part of the interface, then it isn't being checked. – RootTwo Jul 20 '19 at 1:49
• It is clearly written in the second paragraph (after the list), and the check is done in the 8th line of the init method. – darkhorse Jul 20 '19 at 1:53
• I guess I read it differently. It appears to work as you intended, so I deleted that part of my answer. – RootTwo Jul 20 '19 at 2:24