r"""
    Infers information for make_default_docstr
    # TODO: Interleave old documentation with new documentation

    Args:
        func (function): live python function

    CommandLine:
        python -m utool --tf infer_function_info:0
        python -m utool --tf infer_function_info:1 --funcname=ibeis_cnn.models.siam.ignore_hardest_cases

    Example0:
        >>> # ENABLE_DOCTEST
        >>> from utool.util_inspect import *  # NOQA
        >>> import utool as ut
        >>> func = ut.infer_function_info
        >>> #func = ut.Timer.tic
        >>> func = ut.make_default_docstr
        >>> funcinfo = infer_function_info(func)
        >>> result = ut.repr4(funcinfo.__dict__)
        >>> print(result)

    Example1:
        >>> # DISABLE_DOCTEST
        >>> # SCRIPT
        >>> from utool.util_inspect import *  # NOQA
        >>> import utool as ut
        >>> funcname = ut.get_argval('--funcname')
        >>> # Parse out custom function
        >>> modname = '.'.join(funcname.split('.')[0:-1])
        >>> script = 'import {modname}\nfunc = {funcname}'.format(
        >>>     modname=modname, funcname=funcname)
        >>> globals_, locals_ = {}, {}
        >>> exec(script, globals_, locals_)
        >>> func = locals_['func']
        >>> funcinfo = infer_function_info(func)
        >>> result = ut.repr4(funcinfo.__dict__)
        >>> print(result)

def infer_function_info(func):
    r"""
    Infers information for make_default_docstr
    # TODO: Interleave old documentation with new documentation

    Args:
        func (function): live python function

    CommandLine:
        python -m utool --tf infer_function_info:0
        python -m utool --tf infer_function_info:1 --funcname=ibeis_cnn.models.siam.ignore_hardest_cases

    Example0:
        >>> # ENABLE_DOCTEST
        >>> from utool.util_inspect import *  # NOQA
        >>> import utool as ut
        >>> func = ut.infer_function_info
        >>> #func = ut.Timer.tic
        >>> func = ut.make_default_docstr
        >>> funcinfo = infer_function_info(func)
        >>> result = ut.repr4(funcinfo.__dict__)
        >>> print(result)

    Example1:
        >>> # DISABLE_DOCTEST
        >>> # SCRIPT
        >>> from utool.util_inspect import *  # NOQA
        >>> import utool as ut
        >>> funcname = ut.get_argval('--funcname')
        >>> # Parse out custom function
        >>> modname = '.'.join(funcname.split('.')[0:-1])
        >>> script = 'import {modname}\nfunc = {funcname}'.format(
        >>>     modname=modname, funcname=funcname)
        >>> globals_, locals_ = {}, {}
        >>> exec(script, globals_, locals_)
        >>> func = locals_['func']
        >>> funcinfo = infer_function_info(func)
        >>> result = ut.repr4(funcinfo.__dict__)
        >>> print(result)
    """
    import utool as ut
    import re

    # TODO: allow a jedi argument
    if False:
        from jedi.evaluate import docstrings
        script = func.script
        argname_list = [p.name.value for p in func.params]
        argtype_list = [docstrings.follow_param(script._evaluator, p) for p in func.params]

    if isinstance(func, property):
        func = func.fget
    try:
        doc_shortdesc = ''
        doc_longdesc = ''

        known_arginfo = ut.ddict(dict)

        current_doc = inspect.getdoc(func)
        docstr_blocks = ut.parse_docblocks_from_docstr(current_doc)
        docblock_types = ut.take_column(docstr_blocks, 0)
        docblock_types = [re.sub('Example[0-9]', 'Example', type_)
                          for type_ in docblock_types]
        docblock_dict = ut.group_items(docstr_blocks, docblock_types)

        if '' in docblock_dict:
            docheaders = docblock_dict['']
            docheaders_lines = ut.take_column(docheaders, 1)
            docheaders_order = ut.take_column(docheaders, 2)
            docheaders_lines = ut.sortedby(docheaders_lines, docheaders_order)
            doc_shortdesc = '\n'.join(docheaders_lines)

        if 'Args' in docblock_dict:
            argblocks = docblock_dict['Args']
            if len(argblocks) != 1:
                print('Warning: should only be one args block')
            else:
                argblock = argblocks[0][1]

                assert argblock.startswith('Args:\n')
                argsblock_ = argblock[len('Args:\n'):]
                arglines = re.split(r'^    \b', argsblock_, flags=re.MULTILINE)
                arglines = [line for line in arglines if len(line) > 0]

                esc = re.escape

                def escparen(pat):
                    return esc('(')  + pat + esc(')')
                argname = ut.named_field('argname', ut.REGEX_VARNAME)
                argtype_ = ut.named_field('argtype', '.' + ut.REGEX_NONGREEDY)
                argtype = escparen(argtype_)
                argdesc = ut.named_field('argdesc', '.*')
                WS = ut.REGEX_WHITESPACE
                argpattern = (
                    WS + argname + WS + argtype + WS + ':' + WS + argdesc)

                for argline in arglines:
                    m = re.match(argpattern, argline, flags=re.MULTILINE | re.DOTALL)
                    try:
                        groupdict_ = m.groupdict()
                    except Exception as ex:
                        print('---')
                        print('argline = \n%s' % (argline,))
                        print('---')
                        raise Exception('Unable to parse argline=%s' % (argline,))
                    #print('groupdict_ = %s' % (ut.repr4(groupdict_),))
                    argname = groupdict_['argname']
                    known_arginfo[argname]['argdesc'] = groupdict_['argdesc'].rstrip('\n')
                    # TODO: record these in a file for future reference
                    # and potential guessing
                    if groupdict_['argtype'] != '?':
                        known_arginfo[argname]['argtype'] = groupdict_['argtype']

        is_class = isinstance(func, six.class_types)

        needs_surround = current_doc is None or len(current_doc) == 0

        if is_class:
            argfunc = func.__init__
        else:
            argfunc = func
        argspec = ut.get_func_argspec(argfunc)
        (argname_list, varargs, varkw, defaults) = argspec

        # See util_inspect
        tup = ut.infer_arg_types_and_descriptions(argname_list, defaults)
        argtype_list, argdesc_list, argdefault_list, hasdefault_list = tup
        # Put in user parsed info
        for index, argname in enumerate(argname_list):
            if argname in known_arginfo:
                arginfo = known_arginfo[argname]
                if 'argdesc' in arginfo:
                    argdesc_list[index] = arginfo['argdesc']
                if 'argtype' in arginfo:
                    argtype_list[index] = arginfo['argtype']

        if not is_class:
            # Move source down to base indentation, but remember original indentation
            sourcecode = get_func_sourcecode(func)
            #kwarg_keys = ut.parse_kwarg_keys(sourcecode)
            kwarg_items = ut.recursive_parse_kwargs(func)
            flags = ut.unique_flags(ut.take_column(kwarg_items, 0))
            kwarg_items = ut.compress(kwarg_items, flags)
            kwarg_keys = ut.take_column(kwarg_items, 0)
            #kwarg_keys = ut.unique_ordered(kwarg_keys)
            kwarg_keys = ut.setdiff_ordered(kwarg_keys, argname_list)
        else:
            sourcecode = None
            kwarg_keys = []

        if sourcecode is not None:
            num_indent = ut.get_indentation(sourcecode)
            sourcecode = ut.unindent(sourcecode)
            returninfo = ut.parse_return_type(sourcecode)
        else:
            num_indent = 0
            returninfo = None, None, None, ''
        return_type, return_name, return_header, return_desc = returninfo

        modname = func.__module__
        funcname = ut.get_funcname(func)
    except Exception as ex:
        #print('dealing with infer function error')
        #print('has utinfo? ' + str(hasattr(func, '_utinfo')))
        #sourcefile = inspect.getsourcefile(func)  # NOQA
        ut.printex(ex, 'Error Infering Function Info', keys=[
            'func',
            'sourcefile',
            'sourcecode',
            'argspec',
        ], tb=True)
        raise

    class FunctionInfo(object):
        def __init__(self):
            pass
    funcinfo = FunctionInfo()
    funcinfo.needs_surround = needs_surround
    funcinfo.argname_list = argname_list
    funcinfo.argtype_list = argtype_list
    funcinfo.argdesc_list = argdesc_list
    funcinfo.argdefault_list = argdefault_list
    funcinfo.hasdefault_list = hasdefault_list
    funcinfo.kwarg_keys = kwarg_keys
    # if new
    funcinfo.va_name = varargs
    funcinfo.kw_name = varkw
    funcinfo.kw_keys = kwarg_keys
    # else
    funcinfo.varargs = varargs
    funcinfo.varkw = varkw
    # fi
    funcinfo.defaults = defaults
    funcinfo.num_indent = num_indent
    funcinfo.return_type = return_type
    funcinfo.return_name = return_name
    funcinfo.return_header = return_header
    funcinfo.return_desc = return_desc
    funcinfo.modname = modname
    funcinfo.funcname = funcname
    funcinfo.doc_shortdesc = doc_shortdesc
    funcinfo.doc_longdesc = doc_longdesc
    funcinfo.ismethod = hasattr(func, 'im_class')
    return funcinfo