1
\$\begingroup\$

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:
                self.add_error(
                    'minimum',
                    f'The minimum must be less than or equal to the maximum '
                    f'of {self.maximum}.'
                )
                self.add_error(
                    '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:
                self.add_error(
                    'minimum',
                    f'The minimum must be less than the exclusive maximum of '
                    f'{self.exclusive_maximum}.'
                )
                self.add_error(
                    '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:
                self.add_error(
                    'exclusive_minimum',
                    f'The exclusive minimum must be less than the maximum of '
                    f'{self.maximum}.'
                )
                self.add_error(
                    '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):
                self.add_error(
                    '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.'
                )
                self.add_error(
                    '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:
                self.add_error(
                    '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?

\$\endgroup\$
  • \$\begingroup\$ is_not_factor is missing. \$\endgroup\$ – vnp Jul 19 at 0:03
  • \$\begingroup\$ Please assume that its coming from another module. \$\endgroup\$ – darkhorse Jul 19 at 11:05
-1
\$\begingroup\$

__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.

\$\endgroup\$
  • \$\begingroup\$ 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? \$\endgroup\$ – darkhorse Jul 20 at 1:43
  • \$\begingroup\$ @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. \$\endgroup\$ – RootTwo Jul 20 at 1:49
  • \$\begingroup\$ It is clearly written in the second paragraph (after the list), and the check is done in the 8th line of the init method. \$\endgroup\$ – darkhorse Jul 20 at 1:53
  • \$\begingroup\$ I guess I read it differently. It appears to work as you intended, so I deleted that part of my answer. \$\endgroup\$ – RootTwo Jul 20 at 2:24

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.