| 
									
										
										
										
											2013-10-17 13:40:50 -07:00
										 |  |  | """A Future class similar to the one in PEP 3148.""" | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-12-10 18:36:12 -05:00
										 |  |  | __all__ = ( | 
					
						
							|  |  |  |     'CancelledError', 'TimeoutError', 'InvalidStateError', | 
					
						
							|  |  |  |     'Future', 'wrap_future', 'isfuture', | 
					
						
							|  |  |  | ) | 
					
						
							| 
									
										
										
										
											2013-10-17 13:40:50 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-10-28 12:52:37 -04:00
										 |  |  | import concurrent.futures | 
					
						
							| 
									
										
										
										
											2018-01-22 19:11:18 -05:00
										 |  |  | import contextvars | 
					
						
							| 
									
										
										
										
											2013-10-17 13:40:50 -07:00
										 |  |  | import logging | 
					
						
							| 
									
										
										
										
											2013-12-19 22:42:40 +01:00
										 |  |  | import sys | 
					
						
							| 
									
										
										
										
											2013-10-17 13:40:50 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-10-28 12:52:37 -04:00
										 |  |  | from . import base_futures | 
					
						
							| 
									
										
										
										
											2013-10-17 13:40:50 -07:00
										 |  |  | from . import events | 
					
						
							| 
									
										
										
										
											2017-12-15 07:04:38 +02:00
										 |  |  | from . import format_helpers | 
					
						
							| 
									
										
										
										
											2013-10-17 13:40:50 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-10-28 12:52:37 -04:00
										 |  |  | CancelledError = base_futures.CancelledError | 
					
						
							|  |  |  | InvalidStateError = base_futures.InvalidStateError | 
					
						
							|  |  |  | TimeoutError = base_futures.TimeoutError | 
					
						
							|  |  |  | isfuture = base_futures.isfuture | 
					
						
							| 
									
										
										
										
											2013-10-17 13:40:50 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-10-28 12:52:37 -04:00
										 |  |  | 
 | 
					
						
							|  |  |  | _PENDING = base_futures._PENDING | 
					
						
							|  |  |  | _CANCELLED = base_futures._CANCELLED | 
					
						
							|  |  |  | _FINISHED = base_futures._FINISHED | 
					
						
							| 
									
										
										
										
											2013-10-17 13:40:50 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-10-28 12:52:37 -04:00
										 |  |  | STACK_DEBUG = logging.DEBUG - 1  # heavy-duty debugging | 
					
						
							| 
									
										
										
										
											2013-10-17 13:40:50 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class Future: | 
					
						
							|  |  |  |     """This class is *almost* compatible with concurrent.futures.Future.
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     Differences: | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-11-07 17:03:28 +01:00
										 |  |  |     - This class is not thread-safe. | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-10-17 13:40:50 -07:00
										 |  |  |     - result() and exception() do not take a timeout argument and | 
					
						
							|  |  |  |       raise an exception when the future isn't done yet. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     - Callbacks registered with add_done_callback() are always called | 
					
						
							| 
									
										
										
										
											2017-11-07 17:03:28 +01:00
										 |  |  |       via the event loop's call_soon(). | 
					
						
							| 
									
										
										
										
											2013-10-17 13:40:50 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  |     - This class is not compatible with the wait() and as_completed() | 
					
						
							|  |  |  |       methods in the concurrent.futures package. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     (In Python 3.4 or later we may be able to unify the implementations.) | 
					
						
							|  |  |  |     """
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     # Class variables serving as defaults for instance variables. | 
					
						
							|  |  |  |     _state = _PENDING | 
					
						
							|  |  |  |     _result = None | 
					
						
							|  |  |  |     _exception = None | 
					
						
							|  |  |  |     _loop = None | 
					
						
							| 
									
										
										
										
											2014-12-04 23:00:13 +01:00
										 |  |  |     _source_traceback = None | 
					
						
							| 
									
										
										
										
											2013-10-17 13:40:50 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-09-09 12:54:54 -07:00
										 |  |  |     # This field is used for a dual purpose: | 
					
						
							|  |  |  |     # - Its presence is a marker to declare that a class implements | 
					
						
							|  |  |  |     #   the Future protocol (i.e. is intended to be duck-type compatible). | 
					
						
							|  |  |  |     #   The value must also be not-None, to enable a subclass to declare | 
					
						
							|  |  |  |     #   that it is not compatible by setting this to None. | 
					
						
							|  |  |  |     # - It is set by __iter__() below so that Task._step() can tell | 
					
						
							| 
									
										
										
										
											2017-12-11 17:35:49 +02:00
										 |  |  |     #   the difference between | 
					
						
							|  |  |  |     #   `await Future()` or`yield from Future()` (correct) vs. | 
					
						
							| 
									
										
										
										
											2016-09-09 12:54:54 -07:00
										 |  |  |     #   `yield Future()` (incorrect). | 
					
						
							|  |  |  |     _asyncio_future_blocking = False | 
					
						
							| 
									
										
										
										
											2013-10-17 13:40:50 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-12-25 16:16:10 -05:00
										 |  |  |     __log_traceback = False | 
					
						
							| 
									
										
										
										
											2013-10-17 13:40:50 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  |     def __init__(self, *, loop=None): | 
					
						
							|  |  |  |         """Initialize the future.
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-02-10 05:44:01 +00:00
										 |  |  |         The optional event_loop argument allows explicitly setting the event | 
					
						
							| 
									
										
										
										
											2013-10-17 13:40:50 -07:00
										 |  |  |         loop object used by the future. If it's not provided, the future uses | 
					
						
							|  |  |  |         the default event loop. | 
					
						
							|  |  |  |         """
 | 
					
						
							|  |  |  |         if loop is None: | 
					
						
							|  |  |  |             self._loop = events.get_event_loop() | 
					
						
							|  |  |  |         else: | 
					
						
							|  |  |  |             self._loop = loop | 
					
						
							|  |  |  |         self._callbacks = [] | 
					
						
							| 
									
										
										
										
											2014-06-27 13:52:20 +02:00
										 |  |  |         if self._loop.get_debug(): | 
					
						
							| 
									
										
										
										
											2017-12-15 07:04:38 +02:00
										 |  |  |             self._source_traceback = format_helpers.extract_stack( | 
					
						
							|  |  |  |                 sys._getframe(1)) | 
					
						
							| 
									
										
										
										
											2013-10-17 13:40:50 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-10-28 12:52:37 -04:00
										 |  |  |     _repr_info = base_futures._future_repr_info | 
					
						
							| 
									
										
										
										
											2014-07-29 12:58:23 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |     def __repr__(self): | 
					
						
							| 
									
										
										
										
											2017-12-10 18:36:12 -05:00
										 |  |  |         return '<{} {}>'.format(self.__class__.__name__, | 
					
						
							|  |  |  |                                 ' '.join(self._repr_info())) | 
					
						
							| 
									
										
										
										
											2013-10-17 13:40:50 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-04-25 10:57:18 +09:00
										 |  |  |     def __del__(self): | 
					
						
							| 
									
										
										
										
											2017-12-25 16:16:10 -05:00
										 |  |  |         if not self.__log_traceback: | 
					
						
							| 
									
										
										
										
											2017-04-25 10:57:18 +09:00
										 |  |  |             # set_exception() was not called, or result() or exception() | 
					
						
							|  |  |  |             # has consumed the exception | 
					
						
							|  |  |  |             return | 
					
						
							|  |  |  |         exc = self._exception | 
					
						
							|  |  |  |         context = { | 
					
						
							| 
									
										
										
										
											2017-12-10 18:36:12 -05:00
										 |  |  |             'message': | 
					
						
							|  |  |  |                 f'{self.__class__.__name__} exception was never retrieved', | 
					
						
							| 
									
										
										
										
											2017-04-25 10:57:18 +09:00
										 |  |  |             'exception': exc, | 
					
						
							|  |  |  |             'future': self, | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         if self._source_traceback: | 
					
						
							|  |  |  |             context['source_traceback'] = self._source_traceback | 
					
						
							|  |  |  |         self._loop.call_exception_handler(context) | 
					
						
							| 
									
										
										
										
											2013-12-19 22:42:40 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-12-25 16:16:10 -05:00
										 |  |  |     @property | 
					
						
							|  |  |  |     def _log_traceback(self): | 
					
						
							|  |  |  |         return self.__log_traceback | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     @_log_traceback.setter | 
					
						
							|  |  |  |     def _log_traceback(self, val): | 
					
						
							|  |  |  |         if bool(val): | 
					
						
							|  |  |  |             raise ValueError('_log_traceback can only be set to False') | 
					
						
							|  |  |  |         self.__log_traceback = False | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-12-23 15:04:15 -05:00
										 |  |  |     def get_loop(self): | 
					
						
							|  |  |  |         """Return the event loop the Future is bound to.""" | 
					
						
							|  |  |  |         return self._loop | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-10-17 13:40:50 -07:00
										 |  |  |     def cancel(self): | 
					
						
							|  |  |  |         """Cancel the future and schedule callbacks.
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         If the future is already done or cancelled, return False.  Otherwise, | 
					
						
							|  |  |  |         change the future's state to cancelled, schedule the callbacks and | 
					
						
							|  |  |  |         return True. | 
					
						
							|  |  |  |         """
 | 
					
						
							| 
									
										
										
										
											2017-12-25 16:16:10 -05:00
										 |  |  |         self.__log_traceback = False | 
					
						
							| 
									
										
										
										
											2013-10-17 13:40:50 -07:00
										 |  |  |         if self._state != _PENDING: | 
					
						
							|  |  |  |             return False | 
					
						
							|  |  |  |         self._state = _CANCELLED | 
					
						
							| 
									
										
										
										
											2018-01-24 11:31:01 -05:00
										 |  |  |         self.__schedule_callbacks() | 
					
						
							| 
									
										
										
										
											2013-10-17 13:40:50 -07:00
										 |  |  |         return True | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-01-24 11:31:01 -05:00
										 |  |  |     def __schedule_callbacks(self): | 
					
						
							| 
									
										
										
										
											2013-10-17 13:40:50 -07:00
										 |  |  |         """Internal: Ask the event loop to call all callbacks.
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         The callbacks are scheduled to be called as soon as possible. Also | 
					
						
							|  |  |  |         clears the callback list. | 
					
						
							|  |  |  |         """
 | 
					
						
							|  |  |  |         callbacks = self._callbacks[:] | 
					
						
							|  |  |  |         if not callbacks: | 
					
						
							|  |  |  |             return | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         self._callbacks[:] = [] | 
					
						
							| 
									
										
										
										
											2018-01-22 19:11:18 -05:00
										 |  |  |         for callback, ctx in callbacks: | 
					
						
							|  |  |  |             self._loop.call_soon(callback, self, context=ctx) | 
					
						
							| 
									
										
										
										
											2013-10-17 13:40:50 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  |     def cancelled(self): | 
					
						
							|  |  |  |         """Return True if the future was cancelled.""" | 
					
						
							|  |  |  |         return self._state == _CANCELLED | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     # Don't implement running(); see http://bugs.python.org/issue18699 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def done(self): | 
					
						
							|  |  |  |         """Return True if the future is done.
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         Done means either that a result / exception are available, or that the | 
					
						
							|  |  |  |         future was cancelled. | 
					
						
							|  |  |  |         """
 | 
					
						
							|  |  |  |         return self._state != _PENDING | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def result(self): | 
					
						
							|  |  |  |         """Return the result this future represents.
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         If the future has been cancelled, raises CancelledError.  If the | 
					
						
							|  |  |  |         future's result isn't yet available, raises InvalidStateError.  If | 
					
						
							|  |  |  |         the future is done and has an exception set, this exception is raised. | 
					
						
							|  |  |  |         """
 | 
					
						
							|  |  |  |         if self._state == _CANCELLED: | 
					
						
							|  |  |  |             raise CancelledError | 
					
						
							|  |  |  |         if self._state != _FINISHED: | 
					
						
							|  |  |  |             raise InvalidStateError('Result is not ready.') | 
					
						
							| 
									
										
										
										
											2017-12-25 16:16:10 -05:00
										 |  |  |         self.__log_traceback = False | 
					
						
							| 
									
										
										
										
											2013-10-17 13:40:50 -07:00
										 |  |  |         if self._exception is not None: | 
					
						
							|  |  |  |             raise self._exception | 
					
						
							|  |  |  |         return self._result | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def exception(self): | 
					
						
							|  |  |  |         """Return the exception that was set on this future.
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         The exception (or None if no exception was set) is returned only if | 
					
						
							|  |  |  |         the future is done.  If the future has been cancelled, raises | 
					
						
							|  |  |  |         CancelledError.  If the future isn't done yet, raises | 
					
						
							|  |  |  |         InvalidStateError. | 
					
						
							|  |  |  |         """
 | 
					
						
							|  |  |  |         if self._state == _CANCELLED: | 
					
						
							|  |  |  |             raise CancelledError | 
					
						
							|  |  |  |         if self._state != _FINISHED: | 
					
						
							|  |  |  |             raise InvalidStateError('Exception is not set.') | 
					
						
							| 
									
										
										
										
											2017-12-25 16:16:10 -05:00
										 |  |  |         self.__log_traceback = False | 
					
						
							| 
									
										
										
										
											2013-10-17 13:40:50 -07:00
										 |  |  |         return self._exception | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-01-22 19:11:18 -05:00
										 |  |  |     def add_done_callback(self, fn, *, context=None): | 
					
						
							| 
									
										
										
										
											2013-10-17 13:40:50 -07:00
										 |  |  |         """Add a callback to be run when the future becomes done.
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         The callback is called with a single argument - the future object. If | 
					
						
							|  |  |  |         the future is already done when this is called, the callback is | 
					
						
							|  |  |  |         scheduled with call_soon. | 
					
						
							|  |  |  |         """
 | 
					
						
							|  |  |  |         if self._state != _PENDING: | 
					
						
							| 
									
										
										
										
											2018-01-22 19:11:18 -05:00
										 |  |  |             self._loop.call_soon(fn, self, context=context) | 
					
						
							| 
									
										
										
										
											2013-10-17 13:40:50 -07:00
										 |  |  |         else: | 
					
						
							| 
									
										
										
										
											2018-01-22 19:11:18 -05:00
										 |  |  |             if context is None: | 
					
						
							|  |  |  |                 context = contextvars.copy_context() | 
					
						
							|  |  |  |             self._callbacks.append((fn, context)) | 
					
						
							| 
									
										
										
										
											2013-10-17 13:40:50 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  |     # New method not in PEP 3148. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def remove_done_callback(self, fn): | 
					
						
							|  |  |  |         """Remove all instances of a callback from the "call when done" list.
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         Returns the number of callbacks removed. | 
					
						
							|  |  |  |         """
 | 
					
						
							| 
									
										
										
										
											2018-01-22 19:11:18 -05:00
										 |  |  |         filtered_callbacks = [(f, ctx) | 
					
						
							|  |  |  |                               for (f, ctx) in self._callbacks | 
					
						
							|  |  |  |                               if f != fn] | 
					
						
							| 
									
										
										
										
											2013-10-17 13:40:50 -07:00
										 |  |  |         removed_count = len(self._callbacks) - len(filtered_callbacks) | 
					
						
							|  |  |  |         if removed_count: | 
					
						
							|  |  |  |             self._callbacks[:] = filtered_callbacks | 
					
						
							|  |  |  |         return removed_count | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     # So-called internal methods (note: no set_running_or_notify_cancel()). | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def set_result(self, result): | 
					
						
							|  |  |  |         """Mark the future done and set its result.
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         If the future is already done when this method is called, raises | 
					
						
							|  |  |  |         InvalidStateError. | 
					
						
							|  |  |  |         """
 | 
					
						
							|  |  |  |         if self._state != _PENDING: | 
					
						
							|  |  |  |             raise InvalidStateError('{}: {!r}'.format(self._state, self)) | 
					
						
							|  |  |  |         self._result = result | 
					
						
							|  |  |  |         self._state = _FINISHED | 
					
						
							| 
									
										
										
										
											2018-01-24 11:31:01 -05:00
										 |  |  |         self.__schedule_callbacks() | 
					
						
							| 
									
										
										
										
											2013-10-17 13:40:50 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  |     def set_exception(self, exception): | 
					
						
							|  |  |  |         """Mark the future done and set an exception.
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         If the future is already done when this method is called, raises | 
					
						
							|  |  |  |         InvalidStateError. | 
					
						
							|  |  |  |         """
 | 
					
						
							|  |  |  |         if self._state != _PENDING: | 
					
						
							|  |  |  |             raise InvalidStateError('{}: {!r}'.format(self._state, self)) | 
					
						
							| 
									
										
										
										
											2014-01-30 16:01:54 -08:00
										 |  |  |         if isinstance(exception, type): | 
					
						
							|  |  |  |             exception = exception() | 
					
						
							| 
									
										
										
										
											2016-03-02 11:03:28 -05:00
										 |  |  |         if type(exception) is StopIteration: | 
					
						
							|  |  |  |             raise TypeError("StopIteration interacts badly with generators " | 
					
						
							|  |  |  |                             "and cannot be raised into a Future") | 
					
						
							| 
									
										
										
										
											2013-10-17 13:40:50 -07:00
										 |  |  |         self._exception = exception | 
					
						
							|  |  |  |         self._state = _FINISHED | 
					
						
							| 
									
										
										
										
											2018-01-24 11:31:01 -05:00
										 |  |  |         self.__schedule_callbacks() | 
					
						
							| 
									
										
										
										
											2017-12-25 16:16:10 -05:00
										 |  |  |         self.__log_traceback = True | 
					
						
							| 
									
										
										
										
											2013-10-17 13:40:50 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-12-25 10:48:15 -05:00
										 |  |  |     def __await__(self): | 
					
						
							| 
									
										
										
										
											2013-10-17 13:40:50 -07:00
										 |  |  |         if not self.done(): | 
					
						
							| 
									
										
										
										
											2016-09-09 12:54:54 -07:00
										 |  |  |             self._asyncio_future_blocking = True | 
					
						
							| 
									
										
										
										
											2013-10-17 13:40:50 -07:00
										 |  |  |             yield self  # This tells Task to wait for completion. | 
					
						
							| 
									
										
										
										
											2017-12-25 10:48:15 -05:00
										 |  |  |         if not self.done(): | 
					
						
							|  |  |  |             raise RuntimeError("await wasn't used with future") | 
					
						
							| 
									
										
										
										
											2013-10-17 13:40:50 -07:00
										 |  |  |         return self.result()  # May raise too. | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-12-25 10:48:15 -05:00
										 |  |  |     __iter__ = __await__  # make compatible with 'yield from'. | 
					
						
							| 
									
										
										
										
											2015-05-11 22:27:25 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-10-17 13:40:50 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-10-23 22:34:35 -04:00
										 |  |  | # Needed for testing purposes. | 
					
						
							|  |  |  | _PyFuture = Future | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-12-23 15:04:15 -05:00
										 |  |  | def _get_loop(fut): | 
					
						
							|  |  |  |     # Tries to call Future.get_loop() if it's available. | 
					
						
							|  |  |  |     # Otherwise fallbacks to using the old '_loop' property. | 
					
						
							|  |  |  |     try: | 
					
						
							|  |  |  |         get_loop = fut.get_loop | 
					
						
							|  |  |  |     except AttributeError: | 
					
						
							|  |  |  |         pass | 
					
						
							|  |  |  |     else: | 
					
						
							|  |  |  |         return get_loop() | 
					
						
							|  |  |  |     return fut._loop | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-11-17 12:19:41 -05:00
										 |  |  | def _set_result_unless_cancelled(fut, result): | 
					
						
							|  |  |  |     """Helper setting the result only if the future was not cancelled.""" | 
					
						
							|  |  |  |     if fut.cancelled(): | 
					
						
							|  |  |  |         return | 
					
						
							|  |  |  |     fut.set_result(result) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-10-03 08:31:42 -07:00
										 |  |  | def _set_concurrent_future_state(concurrent, source): | 
					
						
							|  |  |  |     """Copy state from a future to a concurrent.futures.Future.""" | 
					
						
							|  |  |  |     assert source.done() | 
					
						
							|  |  |  |     if source.cancelled(): | 
					
						
							|  |  |  |         concurrent.cancel() | 
					
						
							|  |  |  |     if not concurrent.set_running_or_notify_cancel(): | 
					
						
							|  |  |  |         return | 
					
						
							|  |  |  |     exception = source.exception() | 
					
						
							|  |  |  |     if exception is not None: | 
					
						
							|  |  |  |         concurrent.set_exception(exception) | 
					
						
							|  |  |  |     else: | 
					
						
							|  |  |  |         result = source.result() | 
					
						
							|  |  |  |         concurrent.set_result(result) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-11-17 12:19:41 -05:00
										 |  |  | def _copy_future_state(source, dest): | 
					
						
							|  |  |  |     """Internal helper to copy state from another Future.
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     The other Future may be a concurrent.futures.Future. | 
					
						
							|  |  |  |     """
 | 
					
						
							|  |  |  |     assert source.done() | 
					
						
							|  |  |  |     if dest.cancelled(): | 
					
						
							|  |  |  |         return | 
					
						
							|  |  |  |     assert not dest.done() | 
					
						
							|  |  |  |     if source.cancelled(): | 
					
						
							|  |  |  |         dest.cancel() | 
					
						
							|  |  |  |     else: | 
					
						
							|  |  |  |         exception = source.exception() | 
					
						
							|  |  |  |         if exception is not None: | 
					
						
							|  |  |  |             dest.set_exception(exception) | 
					
						
							|  |  |  |         else: | 
					
						
							|  |  |  |             result = source.result() | 
					
						
							|  |  |  |             dest.set_result(result) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-10-03 08:31:42 -07:00
										 |  |  | def _chain_future(source, destination): | 
					
						
							|  |  |  |     """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. | 
					
						
							|  |  |  |     """
 | 
					
						
							| 
									
										
										
										
											2016-09-09 14:26:31 -07:00
										 |  |  |     if not isfuture(source) and not isinstance(source, | 
					
						
							|  |  |  |                                                concurrent.futures.Future): | 
					
						
							| 
									
										
										
										
											2015-10-03 08:31:42 -07:00
										 |  |  |         raise TypeError('A future is required for source argument') | 
					
						
							| 
									
										
										
										
											2016-09-09 14:26:31 -07:00
										 |  |  |     if not isfuture(destination) and not isinstance(destination, | 
					
						
							|  |  |  |                                                     concurrent.futures.Future): | 
					
						
							| 
									
										
										
										
											2015-10-03 08:31:42 -07:00
										 |  |  |         raise TypeError('A future is required for destination argument') | 
					
						
							| 
									
										
										
										
											2017-12-23 15:04:15 -05:00
										 |  |  |     source_loop = _get_loop(source) if isfuture(source) else None | 
					
						
							|  |  |  |     dest_loop = _get_loop(destination) if isfuture(destination) else None | 
					
						
							| 
									
										
										
										
											2015-10-03 08:31:42 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  |     def _set_state(future, other): | 
					
						
							| 
									
										
										
										
											2016-09-09 14:26:31 -07:00
										 |  |  |         if isfuture(future): | 
					
						
							| 
									
										
										
										
											2015-11-17 12:19:41 -05:00
										 |  |  |             _copy_future_state(other, future) | 
					
						
							| 
									
										
										
										
											2015-10-03 08:31:42 -07:00
										 |  |  |         else: | 
					
						
							|  |  |  |             _set_concurrent_future_state(future, other) | 
					
						
							| 
									
										
										
										
											2013-11-22 11:47:22 -08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-10-03 08:31:42 -07:00
										 |  |  |     def _call_check_cancel(destination): | 
					
						
							|  |  |  |         if destination.cancelled(): | 
					
						
							|  |  |  |             if source_loop is None or source_loop is dest_loop: | 
					
						
							|  |  |  |                 source.cancel() | 
					
						
							|  |  |  |             else: | 
					
						
							|  |  |  |                 source_loop.call_soon_threadsafe(source.cancel) | 
					
						
							| 
									
										
										
										
											2013-11-22 11:47:22 -08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-10-03 08:31:42 -07:00
										 |  |  |     def _call_set_state(source): | 
					
						
							|  |  |  |         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) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | def wrap_future(future, *, loop=None): | 
					
						
							|  |  |  |     """Wrap concurrent.futures.Future object.""" | 
					
						
							| 
									
										
										
										
											2016-09-09 14:26:31 -07:00
										 |  |  |     if isfuture(future): | 
					
						
							| 
									
										
										
										
											2015-10-03 08:31:42 -07:00
										 |  |  |         return future | 
					
						
							|  |  |  |     assert isinstance(future, concurrent.futures.Future), \ | 
					
						
							| 
									
										
										
										
											2017-12-10 18:36:12 -05:00
										 |  |  |         f'concurrent.futures.Future is expected, got {future!r}' | 
					
						
							| 
									
										
										
										
											2016-05-16 15:38:39 -04:00
										 |  |  |     if loop is None: | 
					
						
							|  |  |  |         loop = events.get_event_loop() | 
					
						
							|  |  |  |     new_future = loop.create_future() | 
					
						
							| 
									
										
										
										
											2015-10-03 08:31:42 -07:00
										 |  |  |     _chain_future(future, new_future) | 
					
						
							| 
									
										
										
										
											2013-10-17 13:40:50 -07:00
										 |  |  |     return new_future | 
					
						
							| 
									
										
										
										
											2016-10-18 11:48:14 +09:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | try: | 
					
						
							|  |  |  |     import _asyncio | 
					
						
							|  |  |  | except ImportError: | 
					
						
							|  |  |  |     pass | 
					
						
							|  |  |  | else: | 
					
						
							| 
									
										
										
										
											2016-10-23 22:34:35 -04:00
										 |  |  |     # _CFuture is needed for tests. | 
					
						
							|  |  |  |     Future = _CFuture = _asyncio.Future |