Helper method to check that an object has certain properties:
    * not none
    * a certain type
    * in some accepted values
    * in some accepted range

    :param var: the object to check
    :param var_types: the type(s) to enforce. If None, type will not be enforced
    :param var_name: the name of the varioable to be used in error messages
    :param enforce_not_none: boolean, default True. Whether to enforce that var is not None.
    :param allowed_values: an optional set of allowed values
    :param min_value: an optional minimum value
    :param min_strict: if True, only values strictly greater than the minimum value will be accepted
    :param max_value: an optional maximum value
    :param max_strict: if True, only values strictly lesser than the minimum value will be accepted
    :return:

def check_var(var, var_types:Union[type, List[type]] =None, var_name=None, enforce_not_none:bool = True,
              allowed_values:Set = None, min_value = None, min_strict:bool = False,
              max_value = None, max_strict:bool = False, min_len:int = None, min_len_strict:bool = False,
              max_len:int = None, max_len_strict:bool = False):
    """
    Helper method to check that an object has certain properties:
    * not none
    * a certain type
    * in some accepted values
    * in some accepted range

    :param var: the object to check
    :param var_types: the type(s) to enforce. If None, type will not be enforced
    :param var_name: the name of the varioable to be used in error messages
    :param enforce_not_none: boolean, default True. Whether to enforce that var is not None.
    :param allowed_values: an optional set of allowed values
    :param min_value: an optional minimum value
    :param min_strict: if True, only values strictly greater than the minimum value will be accepted
    :param max_value: an optional maximum value
    :param max_strict: if True, only values strictly lesser than the minimum value will be accepted
    :return:
    """

    var_name = var_name or 'object'

    if enforce_not_none and (var is None):
        # enforce not none
        raise MissingMandatoryParameterException('Error, ' + var_name + '" is mandatory, it should be non-None')

    if not (var is None) and not (var_types is None):
        # enforce type
        if not isinstance(var_types, list):
            var_types = [var_types]

        match = False
        for var_type in var_types:
            # just in case, even though users should use FunctionType or MethodType which is the true type
            if var_type is Callable:
                if callable(var):
                    match = True
                    break
            else:
                if isinstance(var, var_type):
                    match = True
                    break

        if not match:
            raise TypeError('Error, ' + var_name + '" should be one of type(s) ' + str(var_types) + ', found: ' + str(type(var)))

    if var is not None:
        if allowed_values is not None:
            # enforce allowed values
            if var not in allowed_values:
                raise TypeError('Error, ' + var_name + '" should be one of "' + str(allowed_values) + '", found: ' + str(var))

        if min_value is not None:
            # enforce min value
            if min_strict:
                if not (var > min_value):
                    raise TypeError(
                        'Error, ' + var_name + '" should be strictly greater than "' + str(min_value) + '", found: ' + str(var))
            else:
                if not (var >= min_value):
                    raise TypeError(
                        'Error, ' + var_name + '" should be greater than "' + str(min_value) + '", found: ' + str(var))

        if max_value is not None:
            # enforce max value
            if max_strict:
                if not (var < max_value):
                    raise TypeError(
                        'Error, ' + var_name + '" should be strictly lesser than "' + str(max_value) + '", found: ' + str(var))
            else:
                if not (var <= max_value):
                    raise TypeError(
                        'Error, ' + var_name + '" should be lesser than "' + str(max_value) + '", found: ' + str(var))

        if min_len is not None:
            # enforce min length
            if min_len_strict:
                if not (len(var) > min_len):
                    raise TypeError(
                        'Error, ' + var_name + '" length should be strictly greater than "' + str(min_len) + '", found: ' + str(len(var)))
            else:
                if not (len(var) >= min_len):
                    raise TypeError(
                        'Error, ' + var_name + '" length should be greater than "' + str(min_len) + '", found: ' + str(len(var)))

        if max_len is not None:
            # enforce max length
            if max_len_strict:
                if not (len(var) < max_len):
                    raise TypeError(
                        'Error, ' + var_name + '" length should be strictly lesser than "' + str(max_len) + '", found: ' + str(len(var)))
            else:
                if not (len(var) <= max_len):
                    raise TypeError(
                        'Error, ' + var_name + '" length should be lesser than "' + str(max_len) + '", found: ' + str(len(var)))