Execute the given action and return a Future with the result.

    The ``forwards`` and/or ``backwards`` methods for the action may be
    synchronous or asynchronous. If asynchronous, that method must return a
    Future that will resolve to its result.

    See :py:func:`reversible.execute` for more details on the behavior of
    ``execute``.

    :param action:
        The action to execute.
    :param io_loop:
        IOLoop through which asynchronous operations will be executed. If
        omitted, the current IOLoop is used.
    :returns:
        A future containing the result of executing the action.

def execute(action, io_loop=None):
    """Execute the given action and return a Future with the result.

    The ``forwards`` and/or ``backwards`` methods for the action may be
    synchronous or asynchronous. If asynchronous, that method must return a
    Future that will resolve to its result.

    See :py:func:`reversible.execute` for more details on the behavior of
    ``execute``.

    :param action:
        The action to execute.
    :param io_loop:
        IOLoop through which asynchronous operations will be executed. If
        omitted, the current IOLoop is used.
    :returns:
        A future containing the result of executing the action.
    """

    if not io_loop:
        io_loop = IOLoop.current()

    output = Future()

    def call():
        try:
            result = _execute(_TornadoAction(action, io_loop))
        except Exception:
            output.set_exc_info(sys.exc_info())
        else:
            output.set_result(result)

    io_loop.add_callback(greenlet.greenlet(call).switch)
    return output