def _chain_future(source: Union[(concurrent.futures.Future, Future)], destination: Union[(concurrent.futures.Future, Future)]) -> None:
    'Chain two futures so that when one completes, so does the other.\n\n    The result (or exception) of source will be copied to destination.\n    If destination is cancelled, source gets cancelled too.\n    Compatible with both asyncio.Future and concurrent.futures.Future.\n    '
    if (not isinstance(source, (Future, concurrent.futures.Future))):
        raise TypeError('A future is required for source argument')
    if (not isinstance(destination, (Future, concurrent.futures.Future))):
        raise TypeError('A future is required for destination argument')
    if isinstance(source, Future):
        source_loop = source._loop
    else:
        source_loop = None
    if isinstance(destination, Future):
        dest_loop = destination._loop
    else:
        dest_loop = None

    def _set_state(future: Union[(concurrent.futures.Future, Future)], other: Union[(concurrent.futures.Future, Future)]) -> None:
        if isinstance(future, Future):
            _copy_future_state(other, future)
        else:
            _set_concurrent_future_state(future, other)

    def _call_check_cancel(destination: Union[(concurrent.futures.Future, Future)]) -> None:
        if destination.cancelled():
            if ((source_loop is None) or (source_loop is dest_loop)):
                source.cancel()
            else:
                source_loop.call_soon_threadsafe(source.cancel)

    def _call_set_state(source: Union[(concurrent.futures.Future, Future)]) -> None:
        if ((dest_loop is None) or (dest_loop is source_loop)):
            _set_state(destination, source)
        else:
            dest_loop.call_soon_threadsafe(_set_state, destination, source)
    destination.add_done_callback(_call_check_cancel)
    source.add_done_callback(_call_set_state)
Chain two futures so that when one completes, so does the other.

The result (or exception) of source will be copied to destination.
If destination is cancelled, source gets cancelled too.
Compatible with both asyncio.Future and concurrent.futures.Future.
def _chain_future(source: Union[(concurrent.futures.Future, Future)], destination: Union[(concurrent.futures.Future, Future)]) -> None:
    'Chain two futures so that when one completes, so does the other.\n\n    The result (or exception) of source will be copied to destination.\n    If destination is cancelled, source gets cancelled too.\n    Compatible with both asyncio.Future and concurrent.futures.Future.\n    '
    if (not isinstance(source, (Future, concurrent.futures.Future))):
        raise TypeError('A future is required for source argument')
    if (not isinstance(destination, (Future, concurrent.futures.Future))):
        raise TypeError('A future is required for destination argument')
    if isinstance(source, Future):
        source_loop = source._loop
    else:
        source_loop = None
    if isinstance(destination, Future):
        dest_loop = destination._loop
    else:
        dest_loop = None

    def _set_state(future: Union[(concurrent.futures.Future, Future)], other: Union[(concurrent.futures.Future, Future)]) -> None:
        if isinstance(future, Future):
            _copy_future_state(other, future)
        else:
            _set_concurrent_future_state(future, other)

    def _call_check_cancel(destination: Union[(concurrent.futures.Future, Future)]) -> None:
        if destination.cancelled():
            if ((source_loop is None) or (source_loop is dest_loop)):
                source.cancel()
            else:
                source_loop.call_soon_threadsafe(source.cancel)

    def _call_set_state(source: Union[(concurrent.futures.Future, Future)]) -> None:
        if ((dest_loop is None) or (dest_loop is source_loop)):
            _set_state(destination, source)
        else:
            dest_loop.call_soon_threadsafe(_set_state, destination, source)
    destination.add_done_callback(_call_check_cancel)
    source.add_done_callback(_call_set_state)