def user_input(prompt="", choices=None, default=None, choices_str="",
               required=False):
    """
    Python2/3-compatible input method handling choices and default value.
    
    :param prompt:   prompt message
    :param choices:  list of possible choices or lambda function
    :param default:  default value
    :param required: make non-null user input mandatory
    :return:         handled user input
    """
    if type(choices) in [list, tuple, set]:
        choices = list(map(str, choices))
        choices_str = " {%s}" % (choices_str or \
                                 '|'.join(list(map(str, choices))))
        # consider choices of the form ["(Y)es", "(N)o"] ;
        #  in this case, we want the choices to be ['y', 'n'] for the sake of
        #  simplicity for the user
        m = list(map(lambda x: CHOICE_REGEX.match(x), choices))
        choices = [x.group(1).lower() if x else c for x, c in zip(m, choices)]
        # this way, if using ["Yes", "No"], choices will remain so
        _check = lambda v: v in choices
    elif is_lambda(choices):
        _check = choices
    else:
        _check = lambda v: True
    prompt += "{}{}\n".format(choices_str, [" [{}]".format(default), ""]\
                                           [default is None and required])
    user_input, first = None, True
    while not user_input:
        user_input = std_input(["", prompt][first] + " >> ")
        first = False
        if type(choices) in [list, tuple, set]:
            choices = list(map(lambda x: x.lower(), choices))
            user_input = user_input.lower()
        if user_input == "" and default is not None and _check(default):
            return str(default)
        if user_input != "" and _check(user_input):
            return user_input
        if not required:
            return