r"""
    Args:
        modname (str):  module name
        funcname (str):  function name
        verbose (bool):  verbosity flag(Defaults to True)
        moddir (None): (Defaults to None)

    CommandLine:
        python -m utool.util_autogen load_func_from_module

    Example:
        >>> # DISABLE_DOCTEST
        >>> # UNSTABLE_DOCTEST
        >>> from utool.util_autogen import *  # NOQA
        >>> import utool as ut
        >>> #modname = 'plottool.plots'
        >>> #funcname = 'multi_plot'
        >>> modname = 'utool.util_path'
        >>> funcname = 'checkpath'
        >>> verbose = True
        >>> moddir = None
        >>> func, module, error_str = load_func_from_module(modname, funcname, verbose, moddir)
        >>> source = ut.get_func_sourcecode(func, strip_docstr=True, strip_comments=True)
        >>> keyname = ut.named_field('keyname', ut.REGEX_VARNAME)
        >>> default = ut.named_field('default', '[\'\"A-Za-z_][A-Za-z0-9_\'\"]*')
        >>> pattern = re.escape('kwargs.get(\'') + keyname + re.escape('\',')
        >>> kwarg_keys = [match.groupdict()['keyname'] for match in re.finditer(pattern, source)]

def load_func_from_module(modname, funcname, verbose=True, moddir=None, modpath=None):
    r"""
    Args:
        modname (str):  module name
        funcname (str):  function name
        verbose (bool):  verbosity flag(Defaults to True)
        moddir (None): (Defaults to None)

    CommandLine:
        python -m utool.util_autogen load_func_from_module

    Example:
        >>> # DISABLE_DOCTEST
        >>> # UNSTABLE_DOCTEST
        >>> from utool.util_autogen import *  # NOQA
        >>> import utool as ut
        >>> #modname = 'plottool.plots'
        >>> #funcname = 'multi_plot'
        >>> modname = 'utool.util_path'
        >>> funcname = 'checkpath'
        >>> verbose = True
        >>> moddir = None
        >>> func, module, error_str = load_func_from_module(modname, funcname, verbose, moddir)
        >>> source = ut.get_func_sourcecode(func, strip_docstr=True, strip_comments=True)
        >>> keyname = ut.named_field('keyname', ut.REGEX_VARNAME)
        >>> default = ut.named_field('default', '[\'\"A-Za-z_][A-Za-z0-9_\'\"]*')
        >>> pattern = re.escape('kwargs.get(\'') + keyname + re.escape('\',')
        >>> kwarg_keys = [match.groupdict()['keyname'] for match in re.finditer(pattern, source)]
    """
    import utool as ut
    from os.path import join
    import imp
    func = None
    module = None
    error_str = None
    print('modname = %r' % (modname,))
    print('funcname = %r' % (funcname,))
    print('moddir = %r' % (moddir,))

    try:
        from xdoctest import static_analysis as static
        from xdoctest import utils
        print('modpath = {!r}'.format(modpath))
    except ImportError:
        modpath = None
    else:
        if modpath is None:
            modpath = static.modname_to_modpath(modname)
            if modpath is None:
                modname = modname.split('.')[-1]
                if moddir is not None:
                    modpath = join(moddir, modname + '.py')
                    if not exists(modpath):
                        modpath = None
                if modpath is None:
                    raise Exception('Cannot find modname={} in moddir={}'.format(modname, moddir))
        print('modpath = {!r}'.format(modpath))
        module = utils.import_module_from_path(modpath)
        print('module = {!r}'.format(module))
        try:
            func = eval('module.{}'.format(funcname))
        except AttributeError:
            imp.reload(module)
            func = eval('module.{}'.format(funcname))
        print('func = {!r}'.format(func))
        return func, module, error_str

    if not isinstance(modname, six.string_types):
        error_str = 'modname=%r is not a string. bad input' % (modname,)
    else:
        if False:
            # TODO: static analysis
            import jedi  # NOQA
            modpath = ut.util_import.get_modpath_from_modname(modname)
            script = jedi.Script(path=modpath)
            mod = script._get_module()
            # monkeypatch
            func.script = script
            func = None
            for name in mod.names_dict[funcname]:
                if name.parent.type == 'funcdef':
                    func = name.parent
                    break
            return func, mod, error_str
            # ut.get_modpath_from_modname(modname)
        if module is None:
            try:
                module = __import__(modname)
            except ImportError:
                if moddir is not None:
                    #parts =
                    # There can be a weird double import error thing happening here
                    # Rectify the dots in the filename
                    module = ut.import_module_from_fpath(join(moddir, modname.split('.')[-1] + '.py'))
                else:
                    raise
            #import inspect
            # try:
            #     imp.reload(module)
            # except Exception as ex:
            #     pass
        # if False:
        #     # Try removing pyc if it exists
        #     if module.__file__.endswith('.pyc'):
        #         ut.delete(module.__file__, verbose=False)
        #         try:
        #             module = __import__(modname)
        #         except ImportError:
        #             if moddir is not None:
        #                 module = ut.import_module_from_fpath(join(moddir, modname.split('.')[-1] + '.py'))
        #             else:
        #                 raise
        try:
            imp.reload(module)
        except Exception as ex:
            pass
        try:
            # FIXME: PYTHON 3
            execstr = ut.codeblock(
                '''
                try:
                    import {modname}
                    module = {modname}
                    #print('Trying to reload module=%r' % (module,))
                    imp.reload(module)
                except Exception:
                    # If it fails maybe the module is not in the path
                    if moddir is not None:
                        try:
                            import imp
                            import os
                            orig_dir = os.getcwd()
                            os.chdir(moddir)
                            modname_str = '{modname}'
                            modinfo = imp.find_module(modname_str, [moddir])
                            module = imp.load_module(modname_str, *modinfo)
                            #print('loaded module=%r' % (module,))
                        except Exception as ex:
                            ut.printex(ex, 'failed to imp.load_module')
                            pass
                        finally:
                            os.chdir(orig_dir)
                import imp
                import utool as ut
                imp.reload(ut.util_autogen)
                imp.reload(ut.util_inspect)
                try:
                    func = module.{funcname}
                except AttributeError:
                    docstr = 'Could not find attribute funcname={funcname} in modname={modname} This might be a reloading issue'
                    imp.reload(module)
                '''
            ).format(**locals())
            exec_locals = locals()
            exec_globals = globals()
            exec(execstr, exec_globals, exec_locals)
            func = exec_locals.get('func', None)
            module = exec_locals.get('module', None)
        except Exception as ex2:
            docstr = 'error ' + str(ex2)
            if verbose:
                import utool as ut
                #ut.printex(ex1, 'ex1')
                ut.printex(ex2, 'ex2', tb=True)
            testcmd = 'python -c "import utool; print(utool.auto_docstr(\'%s\', \'%s\'))"' % (modname, funcname)
            error_str = ut.formatex(ex2, 'ex2', tb=True, keys=['modname', 'funcname', 'testcmd'])
            error_str += '---' + execstr
    return func, module, error_str