recursive kwargs parser
    TODO: rectify with others
    FIXME: if docstr indentation is off, this fails

    SeeAlso:
        argparse_funckw
        recursive_parse_kwargs
        parse_kwarg_keys
        parse_func_kwarg_keys
        get_func_kwargs

    Args:
        root_func (function):  live python function
        path_ (None): (default = None)

    Returns:
        list:

    CommandLine:
        python -m utool.util_inspect recursive_parse_kwargs:0
        python -m utool.util_inspect recursive_parse_kwargs:0 --verbinspect
        python -m utool.util_inspect recursive_parse_kwargs:1
        python -m utool.util_inspect recursive_parse_kwargs:2 --mod plottool --func draw_histogram

        python -m utool.util_inspect recursive_parse_kwargs:2 --mod vtool --func ScoreNormalizer.visualize

        python -m utool.util_inspect recursive_parse_kwargs:2 --mod ibeis.viz.viz_matches --func show_name_matches --verbinspect
        python -m utool.util_inspect recursive_parse_kwargs:2 --mod ibeis.expt.experiment_drawing --func draw_rank_cmc --verbinspect

    Example:
        >>> # ENABLE_DOCTEST
        >>> from utool.util_inspect import *  # NOQA
        >>> import utool as ut
        >>> root_func = iter_module_doctestable
        >>> path_ = None
        >>> result = ut.repr2(recursive_parse_kwargs(root_func), nl=1)
        >>> print(result)
        [
            ('include_funcs', True),
            ('include_classes', True),
            ('include_methods', True),
            ('include_builtin', True),
            ('include_inherited', False),
            ('debug_key', None),
        ]

    Example:
        >>> # DISABLE_DOCTEST
        >>> from utool.util_inspect import *  # NOQA
        >>> from ibeis.algo.hots import chip_match
        >>> import utool as ut
        >>> root_func = chip_match.ChipMatch.show_ranked_matches
        >>> path_ = None
        >>> result = ut.repr2(recursive_parse_kwargs(root_func))
        >>> print(result)

    Example:
        >>> # DISABLE_DOCTEST
        >>> from utool.util_inspect import *  # NOQA
        >>> import utool as ut
        >>> modname = ut.get_argval('--mod', type_=str, default='plottool')
        >>> funcname = ut.get_argval('--func', type_=str, default='draw_histogram')
        >>> mod = ut.import_modname(modname)
        >>> root_func = lookup_attribute_chain(funcname, mod.__dict__)
        >>> path_ = None
        >>> parsed = recursive_parse_kwargs(root_func)
        >>> flags = ut.unique_flags(ut.take_column(parsed, 0))
        >>> unique = ut.compress(parsed, flags)
        >>> print('parsed = %s' % (ut.repr4(parsed),))
        >>> print('unique = %s' % (ut.repr4(unique),))

def recursive_parse_kwargs(root_func, path_=None, verbose=None):
    """
    recursive kwargs parser
    TODO: rectify with others
    FIXME: if docstr indentation is off, this fails

    SeeAlso:
        argparse_funckw
        recursive_parse_kwargs
        parse_kwarg_keys
        parse_func_kwarg_keys
        get_func_kwargs

    Args:
        root_func (function):  live python function
        path_ (None): (default = None)

    Returns:
        list:

    CommandLine:
        python -m utool.util_inspect recursive_parse_kwargs:0
        python -m utool.util_inspect recursive_parse_kwargs:0 --verbinspect
        python -m utool.util_inspect recursive_parse_kwargs:1
        python -m utool.util_inspect recursive_parse_kwargs:2 --mod plottool --func draw_histogram

        python -m utool.util_inspect recursive_parse_kwargs:2 --mod vtool --func ScoreNormalizer.visualize

        python -m utool.util_inspect recursive_parse_kwargs:2 --mod ibeis.viz.viz_matches --func show_name_matches --verbinspect
        python -m utool.util_inspect recursive_parse_kwargs:2 --mod ibeis.expt.experiment_drawing --func draw_rank_cmc --verbinspect

    Example:
        >>> # ENABLE_DOCTEST
        >>> from utool.util_inspect import *  # NOQA
        >>> import utool as ut
        >>> root_func = iter_module_doctestable
        >>> path_ = None
        >>> result = ut.repr2(recursive_parse_kwargs(root_func), nl=1)
        >>> print(result)
        [
            ('include_funcs', True),
            ('include_classes', True),
            ('include_methods', True),
            ('include_builtin', True),
            ('include_inherited', False),
            ('debug_key', None),
        ]

    Example:
        >>> # DISABLE_DOCTEST
        >>> from utool.util_inspect import *  # NOQA
        >>> from ibeis.algo.hots import chip_match
        >>> import utool as ut
        >>> root_func = chip_match.ChipMatch.show_ranked_matches
        >>> path_ = None
        >>> result = ut.repr2(recursive_parse_kwargs(root_func))
        >>> print(result)

    Example:
        >>> # DISABLE_DOCTEST
        >>> from utool.util_inspect import *  # NOQA
        >>> import utool as ut
        >>> modname = ut.get_argval('--mod', type_=str, default='plottool')
        >>> funcname = ut.get_argval('--func', type_=str, default='draw_histogram')
        >>> mod = ut.import_modname(modname)
        >>> root_func = lookup_attribute_chain(funcname, mod.__dict__)
        >>> path_ = None
        >>> parsed = recursive_parse_kwargs(root_func)
        >>> flags = ut.unique_flags(ut.take_column(parsed, 0))
        >>> unique = ut.compress(parsed, flags)
        >>> print('parsed = %s' % (ut.repr4(parsed),))
        >>> print('unique = %s' % (ut.repr4(unique),))
    """
    if verbose is None:
        verbose = VERBOSE_INSPECT
    if verbose:
        print('[inspect] recursive parse kwargs root_func = %r ' % (root_func,))

    import utool as ut
    if path_ is None:
        path_ = []
    if root_func in path_:
        if verbose:
            print('[inspect] Encountered cycle. returning')
        return []
    path_.append(root_func)
    spec = ut.get_func_argspec(root_func)
    # ADD MORE
    kwargs_list = []
    found_explicit = list(ut.get_kwdefaults(root_func, parse_source=False).items())
    if verbose:
        print('[inspect] * Found explicit %r' % (found_explicit,))

    #kwargs_list = [(kw,) for kw in  ut.get_kwargs(root_func)[0]]
    sourcecode = ut.get_func_sourcecode(root_func, strip_docstr=True,
                                        stripdef=True)
    sourcecode1 = ut.get_func_sourcecode(root_func, strip_docstr=True,
                                         stripdef=False)
    found_implicit = ut.parse_kwarg_keys(sourcecode1, spec.keywords,
                                         with_vals=True)
    if verbose:
        print('[inspect] * Found found_implicit %r' % (found_implicit,))
    kwargs_list = found_explicit + found_implicit

    def hack_lookup_mod_attrs(attr):
        # HACKS TODO: have find_funcs_called_with_kwargs infer an attribute is a
        # module / function / type. In the module case, we can import it and
        # look it up.  Maybe args, or returns can help infer type.  Maybe just
        # register some known varnames.  Maybe jedi has some better way to do
        # this.
        if attr == 'ut':
            subdict = ut.__dict__
        elif attr == 'pt':
            import plottool as pt
            subdict = pt.__dict__
        else:
            subdict = None
        return subdict

    def resolve_attr_subfunc(subfunc_name):
        # look up attriute chain
        #subdict = root_func.func_globals
        subdict = meta_util_six.get_funcglobals(root_func)
        subtup = subfunc_name.split('.')
        try:
            subdict = lookup_attribute_chain(subfunc_name, subdict)
            if ut.is_func_or_method(subdict):
                # Was subdict supposed to be named something else here?
                subfunc = subdict
                return subfunc
        except (KeyError, TypeError):
            for attr in subtup[:-1]:
                try:
                    subdict = subdict[attr].__dict__
                except (KeyError, TypeError):
                    # limited support for class lookup
                    if ut.is_method(root_func) and spec.args[0] == attr:
                        subdict = root_func.im_class.__dict__
                    else:
                        # FIXME TODO lookup_attribute_chain
                        subdict = hack_lookup_mod_attrs(attr)
                        if subdict is None:
                            print('Unable to find attribute of attr=%r' % (attr,))
                            if ut.SUPER_STRICT:
                                raise
        if subdict is not None:
            attr_name = subtup[-1]
            subfunc = subdict[attr_name]
        else:
            subfunc = None
        return subfunc

    def check_subfunc_name(subfunc_name):
        if isinstance(subfunc_name, tuple) or '.' in subfunc_name:
            subfunc = resolve_attr_subfunc(subfunc_name)
        else:
            # try to directly take func from globals
            func_globals = meta_util_six.get_funcglobals(root_func)
            try:
                subfunc = func_globals[subfunc_name]
            except KeyError:
                print('Unable to find function definition subfunc_name=%r' %
                      (subfunc_name,))
                if ut.SUPER_STRICT:
                    raise
                subfunc = None
        if subfunc is not None:
            subkw_list = recursive_parse_kwargs(subfunc, path_, verbose=verbose)
            new_subkw = subkw_list
            # have_keys = set(ut.take_column(kwargs_list, 0))
            # new_subkw = [item for item in subkw_list
            #              if item[0] not in have_keys]
        else:
            new_subkw = []
        return new_subkw

    if spec.keywords is not None:
        if verbose:
            print('[inspect] Checking spec.keywords=%r' % (spec.keywords,))
        subfunc_name_list = ut.find_funcs_called_with_kwargs(sourcecode, spec.keywords)
        if verbose:
            print('[inspect] Checking subfunc_name_list=%r' % (subfunc_name_list,))
        for subfunc_name in subfunc_name_list:
            try:
                new_subkw = check_subfunc_name(subfunc_name)
                if verbose:
                    print('[inspect] * Found %r' % (new_subkw,))
                kwargs_list.extend(new_subkw)
            except TypeError:
                print('warning: unable to recursivley parse type of : %r' % (subfunc_name,))
    return kwargs_list