docs: def gen(function):
      """
      Allows using a generator to chain together reversible actions.
  
      This decorator may be added to a generator that yields reversible actions
      (any object with a ``.forwards()`` and ``.backwards()`` method). These may
      be constructed manually or via :py:func:`reversible.action`. The decorated
      function, when called, will return another reversible action that runs all
      yielded actions and if any of them fails, rolls back all actions that had
      been executed *in the reverse order*.
  
      Values can be returned by raising the :py:class:`reversible.Return`
      exception, or if using Python 3.3 or newer, by simply using the ``return``
      statement.
  
      For example,
  
      .. code-block:: python
  
          @reversible.gen
          def submit_order(order):
              # CreateOrder is a class that declares a forwards() and
              # backwards() method. The forwards() method returns the
              # order_id. It is propagated back to the yield point.
              order_id = yield CreateOrder(order.cart)
  
              # If get_payment_info throws an exception, the order will
              # be deleted and the exeception will be re-raised to the
              # caller.
              payment_info = PaymentStore.get_payment_info(order.user_id)
  
              try:
                  # charge_order is a function that returns an action.
                  # It is easy to create such a function by using
                  # reversible.action as a decorator.
                  total = yield charge_order(payment_info, order_id)
              except InsufficientFundsException:
                  # Exceptions thrown by a dependency's forwards()
                  # method are propagated at the yield point. It's
                  # possible to handle them and prevent rollback for
                  # everything else.
                  send_insufficient_funds_email(order_id, order.user_id)
              else:
                  yield update_receipt(order_id, total)
                  send_receipt(order_id)
  
              # The order ID is the result of this action.
              raise reversible.Return(order_id)
  
          order_id = reversible.execute(submit_order(order))
  
          # If another action based on reversible.gen calls
          # submit_order, it can simply do:
          #
          #    order_id = yield submit_order(order_details)
  
      When an action fails, its ``backwards`` method and the ``backwards``
      methods of all actions executed so far will be called in reverse of the
      order in which the ``forwards`` methods were called.
  
      If any of the ``backwards`` methods fail, rollback will be aborted.
  
      :param function:
          The generator function. This generator must yield action objects.
      :returns:
          A function that, when called, produces an action object that executes
          actions and functions as yielded by the generator.
      """
  
      @functools.wraps(function)  # TODO: use wrapt instead?
      def new_function(*args, **kwargs):
          try:
              value = function(*args, **kwargs)
          except Return as result:
              return SimpleAction(
                  lambda ctx: ctx.value,
                  lambda _: None,
                  result,
              )
          else:
              if isinstance(value, types.GeneratorType):
                  return _GeneratorAction(value)
              else:
                  return SimpleAction(
                      lambda _: value,
                      lambda _: None,
                      None,
                  )
  
      return new_function