docs: 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