Raised the same exception given, with an additional message.

    :param Exception exception:
        Original exception being raised with additional messages

    :param unicode message:
        Message to be added to the given exception

    :param unicode separator:
        String separating `message` from the `exception`'s original message.

    e.g.
        try:
            raise RuntimeError('original message')
        except Exception, e:
            Reraise(e, 'message')

        > RuntimeError:
        > message
        > original message

        try:
            raise RuntimeError('original message')
        except Exception, e:
            Reraise(e, '[message]', separator=' ')

        > RuntimeError:
        > [message] original message

def reraise(exception, message, separator='\n'):
    """
    Raised the same exception given, with an additional message.

    :param Exception exception:
        Original exception being raised with additional messages

    :param unicode message:
        Message to be added to the given exception

    :param unicode separator:
        String separating `message` from the `exception`'s original message.

    e.g.
        try:
            raise RuntimeError('original message')
        except Exception, e:
            Reraise(e, 'message')

        > RuntimeError:
        > message
        > original message

        try:
            raise RuntimeError('original message')
        except Exception, e:
            Reraise(e, '[message]', separator=' ')

        > RuntimeError:
        > [message] original message
    """
    import sys

    # IMPORTANT: Do NOT use try/except mechanisms in this method or the
    # sys.exc_info()[-1] will be invalid

    if hasattr(exception, 'reraised_message'):
        current_message = exception.reraised_message
    else:
        current_message = exception_to_unicode(exception)

    # Build the new message
    if not current_message.startswith(separator):
        current_message = separator + current_message
    message = '\n' + message + current_message

    if exception.__class__ in _SPECIAL_EXCEPTION_MAP:
        # Handling for special case, some exceptions have different behaviors.
        exception = _SPECIAL_EXCEPTION_MAP[exception.__class__](
            *exception.args
        )

    elif exception.__class__ not in _SPECIAL_EXCEPTION_MAP.values():
        # In Python 2.5 overriding the exception "__str__" has no effect in
        # "unicode()". Instead, we must change the "args" attribute which is
        # used to build the string representation. Even though the
        # documentation says "args" will be deprecated, it uses its first
        # argument in unicode() implementation and not "message".
        exception.args = (message,)

    exception.message = message
    # keep the already decoded message in the object in case this exception is
    # reraised again
    exception.reraised_message = message

    # Reraise the exception with the EXTRA message information
    if six.PY2:
        six.reraise(exception, None, sys.exc_info()[-1])
    else:
        raise exception.with_traceback(sys.exc_info()[-1])