def parse_function_call(expression):
    """
    Parse an expression looking for a function call
    return a dictionary with function name, args and kwargs
    e.g:
    {
        'func': 'display',
        'args': ['df'],
        'kwargs': {
            'aggregation': "'SUM'",
            'nostore_cw': "'1098'",
            'rowCount': "'500'"
        }
    }
    """
    class Walker(ast.NodeVisitor):
        "ast NodeVisition helper class"
        def __init__(self):
            self.results = {'func': None, 'args': [], 'kwargs': {}}

        def resolveName(self, node):
            if isinstance(node, ast.Name):
                self.results['func'] += node.id
            elif isinstance(node, ast.Attribute):
                self.resolveName(node.value)
                self.results['func'] += "." + node.attr

        def visit_Call(self, node):
            "Visit the first call function"
            if self.results['func'] is not None:
                return
            self.results['func'] = ""
            self.resolveName(node.func)
            self.results['args'] = [unparse(arg).strip() for arg in node.args]
            self.results['kwargs'] = {kw.arg:unparse(kw.value).strip().strip('\'"') for kw in node.keywords}

    walker = Walker()
    walker.visit(ast.parse(expression))
    return walker.results
Parse an expression looking for a function call
    return a dictionary with function name, args and kwargs
    e.g:
    {
        'func': 'display',
        'args': ['df'],
        'kwargs': {
            'aggregation': "'SUM'",
            'nostore_cw': "'1098'",
            'rowCount': "'500'"
        }
    }