r"""
    Formats an exception with relevant info

    Args:
        ex (Exception): exception to print
        msg (unicode):  a message to display to the user (default = u'[!?] Caught exception')
        keys (None): a list of strings denoting variables or expressions of interest (default = [])
        iswarning (bool): prints as a warning rather than an error if True (default = False)
        tb (bool): if True prints the traceback in the error message (default = False)
        prefix (None): (default = None)
        locals_ (None): (default = None)
        N (int): (default = 0)
        colored (None): (default = None)
        key_list (list): DEPRICATED use keys

    Returns:
        str: formated exception

    CommandLine:
        python -m utool.util_dbg --exec-formatex

    Example:
        >>> # ENABLE_DOCTET
        >>> from utool.util_dbg import *  # NOQA
        >>> import utool as ut
        >>> msg = 'Testing Exception'
        >>> prefix = '[test]'
        >>> key_list = ['N', 'foo', 'tb']
        >>> locals_ = None
        >>> iswarning = True
        >>> keys = None
        >>> colored = None
        >>> def failfunc():
        >>>     tb = True
        >>>     N = 0
        >>>     try:
        >>>         raise Exception('test exception. This is not an error')
        >>>     except Exception as ex:
        >>>         result = formatex(ex, msg, prefix, key_list, locals_,
        >>>                           iswarning, tb, N, keys, colored)
        >>>         return result
        >>> result = failfunc().replace('\n\n', '')
        >>> print(result)

        <!!! WARNING !!!>
        Traceback (most recent call last):
          File "<string>", line 15, in failfunc
        Exception: test exception. This is not an error[test] Testing Exception
        <class 'Exception'>: test exception. This is not an error
        [test] N = 0
        [test] foo = NameError (this likely due to a misformatted printex and is not related to the exception)
        [test] tb = True
        </!!! WARNING !!!>

def formatex(ex, msg='[!?] Caught exception',
             prefix=None, key_list=[], locals_=None, iswarning=False, tb=False,
             N=0, keys=None, colored=None):
    r"""
    Formats an exception with relevant info

    Args:
        ex (Exception): exception to print
        msg (unicode):  a message to display to the user (default = u'[!?] Caught exception')
        keys (None): a list of strings denoting variables or expressions of interest (default = [])
        iswarning (bool): prints as a warning rather than an error if True (default = False)
        tb (bool): if True prints the traceback in the error message (default = False)
        prefix (None): (default = None)
        locals_ (None): (default = None)
        N (int): (default = 0)
        colored (None): (default = None)
        key_list (list): DEPRICATED use keys

    Returns:
        str: formated exception

    CommandLine:
        python -m utool.util_dbg --exec-formatex

    Example:
        >>> # ENABLE_DOCTET
        >>> from utool.util_dbg import *  # NOQA
        >>> import utool as ut
        >>> msg = 'Testing Exception'
        >>> prefix = '[test]'
        >>> key_list = ['N', 'foo', 'tb']
        >>> locals_ = None
        >>> iswarning = True
        >>> keys = None
        >>> colored = None
        >>> def failfunc():
        >>>     tb = True
        >>>     N = 0
        >>>     try:
        >>>         raise Exception('test exception. This is not an error')
        >>>     except Exception as ex:
        >>>         result = formatex(ex, msg, prefix, key_list, locals_,
        >>>                           iswarning, tb, N, keys, colored)
        >>>         return result
        >>> result = failfunc().replace('\n\n', '')
        >>> print(result)

        <!!! WARNING !!!>
        Traceback (most recent call last):
          File "<string>", line 15, in failfunc
        Exception: test exception. This is not an error[test] Testing Exception
        <class 'Exception'>: test exception. This is not an error
        [test] N = 0
        [test] foo = NameError (this likely due to a misformatted printex and is not related to the exception)
        [test] tb = True
        </!!! WARNING !!!>

    """
    # Get error prefix and local info
    if prefix is None:
        prefix = get_caller_prefix(aserror=True, N=N)
    if locals_ is None:
        locals_ = get_parent_frame(N=N).f_locals
    if keys is not None:
        # shorthand for key_list
        key_list = keys
    # build exception message
    errstr_list = []  # list of exception strings
    ex_tag = 'WARNING' if iswarning else 'EXCEPTION'
    errstr_list.append('<!!! %s !!!>' % ex_tag)
    if tb or FORCE_TB:
        tbtext = traceback.format_exc()
        if colored or COLORED_EXCEPTIONS:
            from utool import util_str
            tbtext = util_str.highlight_text(tbtext, lexer_name='pytb', stripall=True)
        errstr_list.append(tbtext)
    errstr_list.append(prefix + ' ' + six.text_type(msg) + '\n%r: %s' % (type(ex), six.text_type(ex)))
    #errstr_list.append(prefix + ' ' + six.text_type(msg) + '\ntype(ex)=%r' % (type(ex),))
    parse_locals_keylist(locals_, key_list, errstr_list, prefix)
    errstr_list.append('</!!! %s !!!>' % ex_tag)
    return '\n'.join(errstr_list)