Rewrite a function so that any if/else branches are intercepted and their behavior can be overridden. This is
    accomplished using 3 steps:

    1. Get the source of the function and then rewrite the AST using _IfTransformer
    2. Do some small fixups to the tree to make sure
        a) the function doesn't have the same name, and
        b) the decorator isn't called recursively on the transformed function as well
    3. Bring the variables from the call site back into scope

    :param f: Function to rewrite
    :return: Rewritten function

def _rewrite_function(f):
    """
    Rewrite a function so that any if/else branches are intercepted and their behavior can be overridden. This is
    accomplished using 3 steps:

    1. Get the source of the function and then rewrite the AST using _IfTransformer
    2. Do some small fixups to the tree to make sure
        a) the function doesn't have the same name, and
        b) the decorator isn't called recursively on the transformed function as well
    3. Bring the variables from the call site back into scope

    :param f: Function to rewrite
    :return: Rewritten function
    """
    source = inspect.getsource(f)
    tree = ast.parse(source)
    _IfTransformer().visit(tree)

    ast.fix_missing_locations(tree)
    tree.body[0].name = f.__name__ + '_patched'
    tree.body[0].decorator_list = []

    compiled = compile(tree, filename='<ast>', mode='exec')
    # The first f_back here gets to the body of magicquil() and the second f_back gets to the user's call site which
    # is what we want. If we didn't add these manually to the globals it wouldn't be possible to call other @magicquil
    # functions from within a @magicquil function.
    prev_globals = inspect.currentframe().f_back.f_back.f_globals
    # For reasons I don't quite understand it's critical to add locals() here otherwise the function will disappear and
    # we won't be able to return it below
    exec(compiled, {**prev_globals, **globals()}, locals())
    return locals()[f.__name__ + '_patched']