@@ -390,22 +390,64 @@ def __iter__(self):
390390 __await__ = __iter__ # make compatible with 'await' expression
391391
392392
393- def wrap_future (fut , * , loop = None ):
394- """Wrap concurrent.futures.Future object."""
395- if isinstance (fut , Future ):
396- return fut
397- assert isinstance (fut , concurrent .futures .Future ), \
398- 'concurrent.futures.Future is expected, got {!r}' .format (fut )
399- if loop is None :
400- loop = events .get_event_loop ()
401- new_future = Future (loop = loop )
393+ def _set_concurrent_future_state (concurrent , source ):
394+ """Copy state from a future to a concurrent.futures.Future."""
395+ assert source .done ()
396+ if source .cancelled ():
397+ concurrent .cancel ()
398+ if not concurrent .set_running_or_notify_cancel ():
399+ return
400+ exception = source .exception ()
401+ if exception is not None :
402+ concurrent .set_exception (exception )
403+ else :
404+ result = source .result ()
405+ concurrent .set_result (result )
406+
407+
408+ def _chain_future (source , destination ):
409+ """Chain two futures so that when one completes, so does the other.
410+
411+ The result (or exception) of source will be copied to destination.
412+ If destination is cancelled, source gets cancelled too.
413+ Compatible with both asyncio.Future and concurrent.futures.Future.
414+ """
415+ if not isinstance (source , (Future , concurrent .futures .Future )):
416+ raise TypeError ('A future is required for source argument' )
417+ if not isinstance (destination , (Future , concurrent .futures .Future )):
418+ raise TypeError ('A future is required for destination argument' )
419+ source_loop = source ._loop if isinstance (source , Future ) else None
420+ dest_loop = destination ._loop if isinstance (destination , Future ) else None
421+
422+ def _set_state (future , other ):
423+ if isinstance (future , Future ):
424+ future ._copy_state (other )
425+ else :
426+ _set_concurrent_future_state (future , other )
402427
403- def _check_cancel_other (f ):
404- if f .cancelled ():
405- fut .cancel ()
428+ def _call_check_cancel (destination ):
429+ if destination .cancelled ():
430+ if source_loop is None or source_loop is dest_loop :
431+ source .cancel ()
432+ else :
433+ source_loop .call_soon_threadsafe (source .cancel )
406434
407- new_future .add_done_callback (_check_cancel_other )
408- fut .add_done_callback (
409- lambda future : loop .call_soon_threadsafe (
410- new_future ._copy_state , future ))
435+ def _call_set_state (source ):
436+ if dest_loop is None or dest_loop is source_loop :
437+ _set_state (destination , source )
438+ else :
439+ dest_loop .call_soon_threadsafe (_set_state , destination , source )
440+
441+ destination .add_done_callback (_call_check_cancel )
442+ source .add_done_callback (_call_set_state )
443+
444+
445+ def wrap_future (future , * , loop = None ):
446+ """Wrap concurrent.futures.Future object."""
447+ if isinstance (future , Future ):
448+ return future
449+ assert isinstance (future , concurrent .futures .Future ), \
450+ 'concurrent.futures.Future is expected, got {!r}' .format (future )
451+ new_future = Future (loop = loop )
452+ _chain_future (future , new_future )
411453 return new_future
0 commit comments