Perform a Globus Transfer and monitor for success.

    Arguments:
        transfer_client (TransferClient): An authenticated Transfer client.
        source_ep (str): The source Globus Endpoint ID.
        dest_ep (str): The destination Globus Endpoint ID.
        path_list (list of tuple of 2 str): A list of tuples containing the paths to transfer as
                ``(source, destination)``.

                **Example**::

                    [("/source/files/file.dat", "/dest/mydocs/doc.dat"),
                     ("/source/all_reports/", "/dest/reports/")]

        interval (int): Number of seconds to wait before polling Transfer status.
                Minimum ``1``.**Default**: ``DEFAULT_INTERVAL``.
        retries (int): The number of errors to tolerate before cancelling the task.
                Globus Transfer makes no distinction between hard errors
                (e.g. "permission denied") and soft errors
                (e.g. "endpoint [temporarily] too busy") so requiring retries is
                not uncommon for large Transfers.
                ``-1`` for infinite tries (Transfer still fails after a period of no activity).
                ``None`` is synonymous with ``0``.
                **Default**: ``10``.
        notify (bool): When ``True``, trigger a notification email from Globus to the user when
                the Transfer succeeds or fails. When ``False``, disable the notification.
                **Default**: ``True``.

    Returns:
        str: ID of the Globus Transfer.

def quick_transfer(transfer_client, source_ep, dest_ep, path_list, interval=None, retries=10,
                   notify=True):
    """Perform a Globus Transfer and monitor for success.

    Arguments:
        transfer_client (TransferClient): An authenticated Transfer client.
        source_ep (str): The source Globus Endpoint ID.
        dest_ep (str): The destination Globus Endpoint ID.
        path_list (list of tuple of 2 str): A list of tuples containing the paths to transfer as
                ``(source, destination)``.

                **Example**::

                    [("/source/files/file.dat", "/dest/mydocs/doc.dat"),
                     ("/source/all_reports/", "/dest/reports/")]

        interval (int): Number of seconds to wait before polling Transfer status.
                Minimum ``1``.**Default**: ``DEFAULT_INTERVAL``.
        retries (int): The number of errors to tolerate before cancelling the task.
                Globus Transfer makes no distinction between hard errors
                (e.g. "permission denied") and soft errors
                (e.g. "endpoint [temporarily] too busy") so requiring retries is
                not uncommon for large Transfers.
                ``-1`` for infinite tries (Transfer still fails after a period of no activity).
                ``None`` is synonymous with ``0``.
                **Default**: ``10``.
        notify (bool): When ``True``, trigger a notification email from Globus to the user when
                the Transfer succeeds or fails. When ``False``, disable the notification.
                **Default**: ``True``.

    Returns:
        str: ID of the Globus Transfer.
    """
    if retries is None:
        retries = 0
    iterations = 0

    transfer = custom_transfer(transfer_client, source_ep, dest_ep, path_list, notify=notify)
    res = next(transfer)
    try:
        # Loop ends on StopIteration from generator exhaustion
        while True:
            if iterations < retries or retries == -1:
                res = transfer.send(True)
                iterations += 1
            else:
                res = transfer.send(False)
    except StopIteration:
        pass
    if res["success"]:
        error = "No error"
    else:
        error = "{}: {}".format(res.get("fatal_error", {}).get("code", "Error"),
                                res.get("fatal_error", {}).get("description", "Unknown"))
    return {
        "success": res["success"],
        "task_id": res["task_id"],
        "error": error
    }