Launch an operation on a thread and get a handle to its future result.

    >>> from time import sleep
    >>> def print_sleep_print(duration):
    ...     sleep(duration)
    ...     print('hello from background thread')
    ...     sleep(duration)
    ...     print('goodbye from background thread')
    ...     return 'return value'
    ...
    >>> future = go(print_sleep_print, 0.1)
    >>> sleep(0.15)
    hello from background thread
    >>> print('main thread')
    main thread
    >>> result = future()
    goodbye from background thread
    >>> result
    'return value'

def go(fn, *args, **kwargs):
    """Launch an operation on a thread and get a handle to its future result.

    >>> from time import sleep
    >>> def print_sleep_print(duration):
    ...     sleep(duration)
    ...     print('hello from background thread')
    ...     sleep(duration)
    ...     print('goodbye from background thread')
    ...     return 'return value'
    ...
    >>> future = go(print_sleep_print, 0.1)
    >>> sleep(0.15)
    hello from background thread
    >>> print('main thread')
    main thread
    >>> result = future()
    goodbye from background thread
    >>> result
    'return value'
    """
    if not callable(fn):
        raise TypeError('go() requires a function, not %r' % (fn,))
    result = [None]
    error = []

    def target():
        try:
            result[0] = fn(*args, **kwargs)
        except Exception:
            # Are we in interpreter shutdown?
            if sys:
                error.extend(sys.exc_info())

    t = threading.Thread(target=target)
    t.daemon = True
    t.start()

    def get_result(timeout=10):
        t.join(timeout)
        if t.is_alive():
            raise AssertionError('timed out waiting for %r' % fn)
        if error:
            reraise(*error)
        return result[0]

    return get_result