def find_funcs_called_with_kwargs(sourcecode, target_kwargs_name='kwargs'):
    r"""
    Finds functions that are called with the keyword `kwargs` variable

    CommandLine:
        python3 -m utool.util_inspect find_funcs_called_with_kwargs

    Example:
        >>> # ENABLE_DOCTEST
        >>> import utool as ut
        >>> sourcecode = ut.codeblock(
                '''
                x, y = list(zip(*ut.ichunks(data, 2)))
                somecall(arg1, arg2, arg3=4, **kwargs)
                import sys
                sys.badcall(**kwargs)
                def foo():
                    bar(**kwargs)
                    ut.holymoly(**kwargs)
                    baz()
                    def biz(**kwargs):
                        foo2(**kwargs)
                ''')
        >>> child_funcnamess = ut.find_funcs_called_with_kwargs(sourcecode)
        >>> print('child_funcnamess = %r' % (child_funcnamess,))
        >>> assert 'foo2' not in child_funcnamess, 'foo2 should not be found'
        >>> assert 'bar' in child_funcnamess, 'bar should be found'
    """
    import ast
    sourcecode = 'from __future__ import print_function\n' + sourcecode
    pt = ast.parse(sourcecode)
    child_funcnamess = []
    debug = False or VERYVERB_INSPECT

    if debug:
        print('\nInput:')
        print('target_kwargs_name = %r' % (target_kwargs_name,))
        print('\nSource:')
        print(sourcecode)
        import astor
        print('\nParse:')
        print(astor.dump(pt))

    class KwargParseVisitor(ast.NodeVisitor):
        """
        TODO: understand ut.update_existing and dict update
        ie, know when kwargs is passed to these functions and
        then look assume the object that was updated is a dictionary
        and check wherever that is passed to kwargs as well.
        """
        def visit_FunctionDef(self, node):
            if debug:
                print('\nVISIT FunctionDef node = %r' % (node,))
                print('node.args.kwarg = %r' % (node.args.kwarg,))
            if six.PY2:
                kwarg_name = node.args.kwarg
            else:
                if node.args.kwarg is None:
                    kwarg_name = None
                else:
                    kwarg_name = node.args.kwarg.arg
                #import utool as ut
                #ut.embed()
            if kwarg_name != target_kwargs_name:
                # target kwargs is still in scope
                ast.NodeVisitor.generic_visit(self, node)

        def visit_Call(self, node):
            if debug:
                print('\nVISIT Call node = %r' % (node,))
                #print(ut.repr4(node.__dict__,))
            if isinstance(node.func, ast.Attribute):
                try:
                    funcname = node.func.value.id + '.' + node.func.attr
                except AttributeError:
                    funcname = None
            elif isinstance(node.func, ast.Name):
                funcname = node.func.id
            else:
                raise NotImplementedError(
                    'do not know how to parse: node.func = %r' % (node.func,))
            if six.PY2:
                kwargs = node.kwargs
                kwargs_name = None if kwargs is None else kwargs.id
                if funcname is not None and kwargs_name == target_kwargs_name:
                    child_funcnamess.append(funcname)
                if debug:
                    print('funcname = %r' % (funcname,))
                    print('kwargs_name = %r' % (kwargs_name,))
            else:
                if node.keywords:
                    for kwargs in node.keywords:
                        if kwargs.arg is None:
                            if hasattr(kwargs.value, 'id'):
                                kwargs_name = kwargs.value.id
                                if funcname is not None and kwargs_name == target_kwargs_name:
                                    child_funcnamess.append(funcname)
                                if debug:
                                    print('funcname = %r' % (funcname,))
                                    print('kwargs_name = %r' % (kwargs_name,))
            ast.NodeVisitor.generic_visit(self, node)
    try:
        KwargParseVisitor().visit(pt)
    except Exception:
        raise
        pass
        #import utool as ut
        #if ut.SUPER_STRICT:
        #    raise
    return child_funcnamess
def find_funcs_called_with_kwargs ( sourcecode , target_kwargs_name = 'kwargs' ) : import ast sourcecode = 'from __future__ import print_function\n' + sourcecode pt = ast . parse ( sourcecode ) child_funcnamess = [ ] debug = False or VERYVERB_INSPECT if debug : print ( '\nInput:' ) print ( 'target_kwargs_name = %r' % ( target_kwargs_name , ) ) print ( '\nSource:' ) print ( sourcecode ) import astor print ( '\nParse:' ) print ( astor . dump ( pt ) ) class KwargParseVisitor ( ast . NodeVisitor ) : def visit_FunctionDef ( self , node ) : if debug : print ( '\nVISIT FunctionDef node = %r' % ( node , ) ) print ( 'node.args.kwarg = %r' % ( node . args . kwarg , ) ) if six . PY2 : kwarg_name = node . args . kwarg else : if node . args . kwarg is None : kwarg_name = None else : kwarg_name = node . args . kwarg . arg if kwarg_name != target_kwargs_name : ast . NodeVisitor . generic_visit ( self , node ) def visit_Call ( self , node ) : if debug : print ( '\nVISIT Call node = %r' % ( node , ) ) if isinstance ( node . func , ast . Attribute ) : try : funcname = node . func . value . id + '.' + node . func . attr except AttributeError : funcname = None elif isinstance ( node . func , ast . Name ) : funcname = node . func . id else : raise NotImplementedError ( 'do not know how to parse: node.func = %r' % ( node . func , ) ) if six . PY2 : kwargs = node . kwargs kwargs_name = None if kwargs is None else kwargs . id if funcname is not None and kwargs_name == target_kwargs_name : child_funcnamess . append ( funcname ) if debug : print ( 'funcname = %r' % ( funcname , ) ) print ( 'kwargs_name = %r' % ( kwargs_name , ) ) else : if node . keywords : for kwargs in node . keywords : if kwargs . arg is None : if hasattr ( kwargs . value , 'id' ) : kwargs_name = kwargs . value . id if funcname is not None and kwargs_name == target_kwargs_name : child_funcnamess . append ( funcname ) if debug : print ( 'funcname = %r' % ( funcname , ) ) print ( 'kwargs_name = %r' % ( kwargs_name , ) ) ast . NodeVisitor . generic_visit ( self , node ) try : KwargParseVisitor ( ) . visit ( pt ) except Exception : raise pass return child_funcnamess
r"""
    Finds functions that are called with the keyword `kwargs` variable

    CommandLine:
        python3 -m utool.util_inspect find_funcs_called_with_kwargs

    Example:
        >>> # ENABLE_DOCTEST
        >>> import utool as ut
        >>> sourcecode = ut.codeblock(
                '''
                x, y = list(zip(*ut.ichunks(data, 2)))
                somecall(arg1, arg2, arg3=4, **kwargs)
                import sys
                sys.badcall(**kwargs)
                def foo():
                    bar(**kwargs)
                    ut.holymoly(**kwargs)
                    baz()
                    def biz(**kwargs):
                        foo2(**kwargs)
                ''')
        >>> child_funcnamess = ut.find_funcs_called_with_kwargs(sourcecode)
        >>> print('child_funcnamess = %r' % (child_funcnamess,))
        >>> assert 'foo2' not in child_funcnamess, 'foo2 should not be found'
        >>> assert 'bar' in child_funcnamess, 'bar should be found'