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