| 
									
										
										
										
											2013-10-17 13:40:50 -07:00
										 |  |  | """Support for tasks, coroutines and the scheduler.""" | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-12-10 18:36:12 -05:00
										 |  |  | __all__ = ( | 
					
						
							| 
									
										
										
										
											2017-12-15 07:04:38 +02:00
										 |  |  |     'Task', 'create_task', | 
					
						
							| 
									
										
										
										
											2017-12-10 18:36:12 -05:00
										 |  |  |     'FIRST_COMPLETED', 'FIRST_EXCEPTION', 'ALL_COMPLETED', | 
					
						
							| 
									
										
										
										
											2017-12-11 10:03:48 -05:00
										 |  |  |     'wait', 'wait_for', 'as_completed', 'sleep', | 
					
						
							| 
									
										
										
										
											2017-12-10 18:36:12 -05:00
										 |  |  |     'gather', 'shield', 'ensure_future', 'run_coroutine_threadsafe', | 
					
						
							| 
									
										
										
										
											2017-12-16 21:58:38 +02:00
										 |  |  |     'current_task', 'all_tasks', | 
					
						
							|  |  |  |     '_register_task', '_unregister_task', '_enter_task', '_leave_task', | 
					
						
							| 
									
										
										
										
											2017-12-10 18:36:12 -05:00
										 |  |  | ) | 
					
						
							| 
									
										
										
										
											2013-10-17 13:40:50 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  | import concurrent.futures | 
					
						
							| 
									
										
										
										
											2018-01-22 19:11:18 -05:00
										 |  |  | import contextvars | 
					
						
							| 
									
										
										
										
											2013-10-17 13:40:50 -07:00
										 |  |  | import functools | 
					
						
							|  |  |  | import inspect | 
					
						
							| 
									
										
										
										
											2018-08-09 00:06:47 +03:00
										 |  |  | import itertools | 
					
						
							| 
									
										
										
										
											2017-12-09 00:23:48 +02:00
										 |  |  | import types | 
					
						
							| 
									
										
										
										
											2015-05-11 14:48:38 -04:00
										 |  |  | import warnings | 
					
						
							| 
									
										
										
										
											2013-10-17 13:40:50 -07:00
										 |  |  | import weakref | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-10-28 12:52:37 -04:00
										 |  |  | from . import base_tasks | 
					
						
							| 
									
										
										
										
											2014-06-29 00:46:45 +02:00
										 |  |  | from . import coroutines | 
					
						
							| 
									
										
										
										
											2013-10-17 13:40:50 -07:00
										 |  |  | from . import events | 
					
						
							| 
									
										
										
										
											2018-09-11 10:13:04 -07:00
										 |  |  | from . import exceptions | 
					
						
							| 
									
										
										
										
											2013-10-17 13:40:50 -07:00
										 |  |  | from . import futures | 
					
						
							| 
									
										
										
										
											2019-05-16 17:52:10 +03:00
										 |  |  | from .coroutines import _is_coroutine | 
					
						
							| 
									
										
										
										
											2013-10-17 13:40:50 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-08-09 00:06:47 +03:00
										 |  |  | # Helper to generate new task names | 
					
						
							|  |  |  | # This uses itertools.count() instead of a "+= 1" operation because the latter | 
					
						
							|  |  |  | # is not thread safe. See bpo-11866 for a longer explanation. | 
					
						
							|  |  |  | _task_name_counter = itertools.count(1).__next__ | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-10-17 13:40:50 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-12-16 21:58:38 +02:00
										 |  |  | def current_task(loop=None): | 
					
						
							|  |  |  |     """Return a currently executed task.""" | 
					
						
							|  |  |  |     if loop is None: | 
					
						
							|  |  |  |         loop = events.get_running_loop() | 
					
						
							|  |  |  |     return _current_tasks.get(loop) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | def all_tasks(loop=None): | 
					
						
							|  |  |  |     """Return a set of all tasks for the loop.""" | 
					
						
							| 
									
										
										
										
											2018-05-28 17:54:02 -04:00
										 |  |  |     if loop is None: | 
					
						
							|  |  |  |         loop = events.get_running_loop() | 
					
						
							| 
									
										
										
										
											2019-06-11 18:27:30 +03:00
										 |  |  |     # Looping over a WeakSet (_all_tasks) isn't safe as it can be updated from another | 
					
						
							|  |  |  |     # thread while we do so. Therefore we cast it to list prior to filtering. The list | 
					
						
							|  |  |  |     # cast itself requires iteration, so we repeat it several times ignoring | 
					
						
							|  |  |  |     # RuntimeErrors (which are not very likely to occur). See issues 34970 and 36607 for | 
					
						
							|  |  |  |     # details. | 
					
						
							|  |  |  |     i = 0 | 
					
						
							|  |  |  |     while True: | 
					
						
							|  |  |  |         try: | 
					
						
							|  |  |  |             tasks = list(_all_tasks) | 
					
						
							|  |  |  |         except RuntimeError: | 
					
						
							|  |  |  |             i += 1 | 
					
						
							|  |  |  |             if i >= 1000: | 
					
						
							|  |  |  |                 raise | 
					
						
							|  |  |  |         else: | 
					
						
							|  |  |  |             break | 
					
						
							|  |  |  |     return {t for t in tasks | 
					
						
							| 
									
										
										
										
											2018-05-28 17:54:02 -04:00
										 |  |  |             if futures._get_loop(t) is loop and not t.done()} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | def _all_tasks_compat(loop=None): | 
					
						
							|  |  |  |     # Different from "all_task()" by returning *all* Tasks, including | 
					
						
							|  |  |  |     # the completed ones.  Used to implement deprecated "Tasks.all_task()" | 
					
						
							|  |  |  |     # method. | 
					
						
							| 
									
										
										
										
											2017-12-16 21:58:38 +02:00
										 |  |  |     if loop is None: | 
					
						
							|  |  |  |         loop = events.get_event_loop() | 
					
						
							| 
									
										
										
										
											2019-06-11 18:27:30 +03:00
										 |  |  |     # Looping over a WeakSet (_all_tasks) isn't safe as it can be updated from another | 
					
						
							|  |  |  |     # thread while we do so. Therefore we cast it to list prior to filtering. The list | 
					
						
							|  |  |  |     # cast itself requires iteration, so we repeat it several times ignoring | 
					
						
							|  |  |  |     # RuntimeErrors (which are not very likely to occur). See issues 34970 and 36607 for | 
					
						
							|  |  |  |     # details. | 
					
						
							|  |  |  |     i = 0 | 
					
						
							|  |  |  |     while True: | 
					
						
							|  |  |  |         try: | 
					
						
							|  |  |  |             tasks = list(_all_tasks) | 
					
						
							|  |  |  |         except RuntimeError: | 
					
						
							|  |  |  |             i += 1 | 
					
						
							|  |  |  |             if i >= 1000: | 
					
						
							|  |  |  |                 raise | 
					
						
							|  |  |  |         else: | 
					
						
							|  |  |  |             break | 
					
						
							|  |  |  |     return {t for t in tasks if futures._get_loop(t) is loop} | 
					
						
							| 
									
										
										
										
											2017-12-16 21:58:38 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-08-09 00:06:47 +03:00
										 |  |  | def _set_task_name(task, name): | 
					
						
							|  |  |  |     if name is not None: | 
					
						
							|  |  |  |         try: | 
					
						
							|  |  |  |             set_name = task.set_name | 
					
						
							|  |  |  |         except AttributeError: | 
					
						
							|  |  |  |             pass | 
					
						
							|  |  |  |         else: | 
					
						
							|  |  |  |             set_name(name) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-12-25 10:48:15 -05:00
										 |  |  | class Task(futures._PyFuture):  # Inherit Python Task implementation | 
					
						
							|  |  |  |                                 # from a Python Future implementation. | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-10-17 13:40:50 -07:00
										 |  |  |     """A coroutine wrapped in a Future.""" | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     # An important invariant maintained while a Task not done: | 
					
						
							|  |  |  |     # | 
					
						
							|  |  |  |     # - Either _fut_waiter is None, and _step() is scheduled; | 
					
						
							|  |  |  |     # - or _fut_waiter is some Future, and _step() is *not* scheduled. | 
					
						
							|  |  |  |     # | 
					
						
							|  |  |  |     # The only transition from the latter to the former is through | 
					
						
							|  |  |  |     # _wakeup().  When _fut_waiter is not None, one of its callbacks | 
					
						
							|  |  |  |     # must be _wakeup(). | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-12-04 23:00:13 +01:00
										 |  |  |     # If False, don't log a message if the task is destroyed whereas its | 
					
						
							|  |  |  |     # status is still pending | 
					
						
							|  |  |  |     _log_destroy_pending = True | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-08-09 00:06:47 +03:00
										 |  |  |     def __init__(self, coro, *, loop=None, name=None): | 
					
						
							| 
									
										
										
										
											2013-10-17 13:40:50 -07:00
										 |  |  |         super().__init__(loop=loop) | 
					
						
							| 
									
										
										
										
											2014-06-27 13:52:20 +02:00
										 |  |  |         if self._source_traceback: | 
					
						
							|  |  |  |             del self._source_traceback[-1] | 
					
						
							| 
									
										
										
										
											2017-12-15 07:04:38 +02:00
										 |  |  |         if not coroutines.iscoroutine(coro): | 
					
						
							|  |  |  |             # raise after Future.__init__(), attrs are required for __del__ | 
					
						
							|  |  |  |             # prevent logging for pending task in __del__ | 
					
						
							|  |  |  |             self._log_destroy_pending = False | 
					
						
							|  |  |  |             raise TypeError(f"a coroutine was expected, got {coro!r}") | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-08-09 00:06:47 +03:00
										 |  |  |         if name is None: | 
					
						
							|  |  |  |             self._name = f'Task-{_task_name_counter()}' | 
					
						
							|  |  |  |         else: | 
					
						
							|  |  |  |             self._name = str(name) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-10-17 13:40:50 -07:00
										 |  |  |         self._must_cancel = False | 
					
						
							| 
									
										
										
										
											2017-12-15 07:04:38 +02:00
										 |  |  |         self._fut_waiter = None | 
					
						
							|  |  |  |         self._coro = coro | 
					
						
							| 
									
										
										
										
											2018-01-22 19:11:18 -05:00
										 |  |  |         self._context = contextvars.copy_context() | 
					
						
							| 
									
										
										
										
											2017-12-15 07:04:38 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-01-24 11:31:01 -05:00
										 |  |  |         self._loop.call_soon(self.__step, context=self._context) | 
					
						
							| 
									
										
										
										
											2017-12-23 15:04:15 -05:00
										 |  |  |         _register_task(self) | 
					
						
							| 
									
										
										
										
											2013-10-17 13:40:50 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-04-25 10:57:18 +09:00
										 |  |  |     def __del__(self): | 
					
						
							|  |  |  |         if self._state == futures._PENDING and self._log_destroy_pending: | 
					
						
							|  |  |  |             context = { | 
					
						
							|  |  |  |                 'task': self, | 
					
						
							|  |  |  |                 'message': 'Task was destroyed but it is pending!', | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |             if self._source_traceback: | 
					
						
							|  |  |  |                 context['source_traceback'] = self._source_traceback | 
					
						
							|  |  |  |             self._loop.call_exception_handler(context) | 
					
						
							| 
									
										
										
										
											2017-12-25 10:48:15 -05:00
										 |  |  |         super().__del__() | 
					
						
							| 
									
										
										
										
											2014-06-24 22:37:53 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-12-07 14:05:07 +03:00
										 |  |  |     def __class_getitem__(cls, type): | 
					
						
							|  |  |  |         return cls | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-07-29 12:58:23 +02:00
										 |  |  |     def _repr_info(self): | 
					
						
							| 
									
										
										
										
											2016-10-28 12:52:37 -04:00
										 |  |  |         return base_tasks._task_repr_info(self) | 
					
						
							| 
									
										
										
										
											2013-10-17 13:40:50 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-05-30 18:30:09 +03:00
										 |  |  |     def get_coro(self): | 
					
						
							|  |  |  |         return self._coro | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-08-09 00:06:47 +03:00
										 |  |  |     def get_name(self): | 
					
						
							|  |  |  |         return self._name | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def set_name(self, value): | 
					
						
							|  |  |  |         self._name = str(value) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-12-25 10:48:15 -05:00
										 |  |  |     def set_result(self, result): | 
					
						
							|  |  |  |         raise RuntimeError('Task does not support set_result operation') | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def set_exception(self, exception): | 
					
						
							|  |  |  |         raise RuntimeError('Task does not support set_exception operation') | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-10-17 13:40:50 -07:00
										 |  |  |     def get_stack(self, *, limit=None): | 
					
						
							|  |  |  |         """Return the list of stack frames for this task's coroutine.
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-12-02 17:57:04 +01:00
										 |  |  |         If the coroutine is not done, this returns the stack where it is | 
					
						
							| 
									
										
										
										
											2013-10-17 13:40:50 -07:00
										 |  |  |         suspended.  If the coroutine has completed successfully or was | 
					
						
							|  |  |  |         cancelled, this returns an empty list.  If the coroutine was | 
					
						
							|  |  |  |         terminated by an exception, this returns the list of traceback | 
					
						
							|  |  |  |         frames. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         The frames are always ordered from oldest to newest. | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-02-18 22:27:48 -05:00
										 |  |  |         The optional limit gives the maximum number of frames to | 
					
						
							| 
									
										
										
										
											2013-10-17 13:40:50 -07:00
										 |  |  |         return; by default all available frames are returned.  Its | 
					
						
							|  |  |  |         meaning differs depending on whether a stack or a traceback is | 
					
						
							|  |  |  |         returned: the newest frames of a stack are returned, but the | 
					
						
							|  |  |  |         oldest frames of a traceback are returned.  (This matches the | 
					
						
							|  |  |  |         behavior of the traceback module.) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         For reasons beyond our control, only one stack frame is | 
					
						
							|  |  |  |         returned for a suspended coroutine. | 
					
						
							|  |  |  |         """
 | 
					
						
							| 
									
										
										
										
											2016-10-28 12:52:37 -04:00
										 |  |  |         return base_tasks._task_get_stack(self, limit) | 
					
						
							| 
									
										
										
										
											2013-10-17 13:40:50 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  |     def print_stack(self, *, limit=None, file=None): | 
					
						
							|  |  |  |         """Print the stack or traceback for this task's coroutine.
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         This produces output similar to that of the traceback module, | 
					
						
							|  |  |  |         for the frames retrieved by get_stack().  The limit argument | 
					
						
							|  |  |  |         is passed to get_stack().  The file argument is an I/O stream | 
					
						
							| 
									
										
										
										
											2014-09-24 13:13:45 -04:00
										 |  |  |         to which the output is written; by default output is written | 
					
						
							|  |  |  |         to sys.stderr. | 
					
						
							| 
									
										
										
										
											2013-10-17 13:40:50 -07:00
										 |  |  |         """
 | 
					
						
							| 
									
										
										
										
											2016-10-28 12:52:37 -04:00
										 |  |  |         return base_tasks._task_print_stack(self, limit, file) | 
					
						
							| 
									
										
										
										
											2013-10-17 13:40:50 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-05-15 16:55:50 -07:00
										 |  |  |     def cancel(self, msg=None): | 
					
						
							| 
									
										
										
										
											2014-09-24 13:13:45 -04:00
										 |  |  |         """Request that this task cancel itself.
 | 
					
						
							| 
									
										
										
										
											2014-04-07 11:18:06 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-06-02 23:06:46 +02:00
										 |  |  |         This arranges for a CancelledError to be thrown into the | 
					
						
							| 
									
										
										
										
											2014-04-07 11:18:06 +02:00
										 |  |  |         wrapped coroutine on the next cycle through the event loop. | 
					
						
							|  |  |  |         The coroutine then has a chance to clean up or even deny | 
					
						
							|  |  |  |         the request using try/except/finally. | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-09-24 13:13:45 -04:00
										 |  |  |         Unlike Future.cancel, this does not guarantee that the | 
					
						
							| 
									
										
										
										
											2014-04-07 11:18:06 +02:00
										 |  |  |         task will be cancelled: the exception might be caught and | 
					
						
							| 
									
										
										
										
											2014-09-24 13:13:45 -04:00
										 |  |  |         acted upon, delaying cancellation of the task or preventing | 
					
						
							|  |  |  |         cancellation completely.  The task may also return a value or | 
					
						
							|  |  |  |         raise a different exception. | 
					
						
							| 
									
										
										
										
											2014-04-07 11:18:06 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |         Immediately after this method is called, Task.cancelled() will | 
					
						
							|  |  |  |         not return True (unless the task was already cancelled).  A | 
					
						
							|  |  |  |         task will be marked as cancelled when the wrapped coroutine | 
					
						
							|  |  |  |         terminates with a CancelledError exception (even if cancel() | 
					
						
							|  |  |  |         was not called). | 
					
						
							|  |  |  |         """
 | 
					
						
							| 
									
										
										
										
											2017-06-11 13:49:18 +00:00
										 |  |  |         self._log_traceback = False | 
					
						
							| 
									
										
										
										
											2013-10-17 13:40:50 -07:00
										 |  |  |         if self.done(): | 
					
						
							|  |  |  |             return False | 
					
						
							|  |  |  |         if self._fut_waiter is not None: | 
					
						
							| 
									
										
										
										
											2020-05-15 16:55:50 -07:00
										 |  |  |             if self._fut_waiter.cancel(msg=msg): | 
					
						
							| 
									
										
										
										
											2013-10-17 13:40:50 -07:00
										 |  |  |                 # Leave self._fut_waiter; it may be a Task that | 
					
						
							|  |  |  |                 # catches and ignores the cancellation so we may have | 
					
						
							|  |  |  |                 # to cancel it again later. | 
					
						
							|  |  |  |                 return True | 
					
						
							| 
									
										
										
										
											2018-01-24 11:31:01 -05:00
										 |  |  |         # It must be the case that self.__step is already scheduled. | 
					
						
							| 
									
										
										
										
											2013-10-17 13:40:50 -07:00
										 |  |  |         self._must_cancel = True | 
					
						
							| 
									
										
										
										
											2020-05-15 16:55:50 -07:00
										 |  |  |         self._cancel_message = msg | 
					
						
							| 
									
										
										
										
											2013-10-17 13:40:50 -07:00
										 |  |  |         return True | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-01-24 11:31:01 -05:00
										 |  |  |     def __step(self, exc=None): | 
					
						
							| 
									
										
										
										
											2017-12-25 10:48:15 -05:00
										 |  |  |         if self.done(): | 
					
						
							| 
									
										
										
										
											2018-09-11 10:13:04 -07:00
										 |  |  |             raise exceptions.InvalidStateError( | 
					
						
							| 
									
										
										
										
											2017-12-25 10:48:15 -05:00
										 |  |  |                 f'_step(): already done: {self!r}, {exc!r}') | 
					
						
							| 
									
										
										
										
											2013-10-17 13:40:50 -07:00
										 |  |  |         if self._must_cancel: | 
					
						
							| 
									
										
										
										
											2018-09-11 10:13:04 -07:00
										 |  |  |             if not isinstance(exc, exceptions.CancelledError): | 
					
						
							| 
									
										
										
										
											2020-05-17 22:47:31 -07:00
										 |  |  |                 exc = self._make_cancelled_error() | 
					
						
							| 
									
										
										
										
											2013-10-17 13:40:50 -07:00
										 |  |  |             self._must_cancel = False | 
					
						
							|  |  |  |         coro = self._coro | 
					
						
							|  |  |  |         self._fut_waiter = None | 
					
						
							| 
									
										
										
										
											2013-12-06 12:57:40 -08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-12-16 21:58:38 +02:00
										 |  |  |         _enter_task(self._loop, self) | 
					
						
							| 
									
										
										
										
											2015-11-20 12:41:03 -05:00
										 |  |  |         # Call either coro.throw(exc) or coro.send(None). | 
					
						
							| 
									
										
										
										
											2013-10-17 13:40:50 -07:00
										 |  |  |         try: | 
					
						
							| 
									
										
										
										
											2015-11-20 12:41:03 -05:00
										 |  |  |             if exc is None: | 
					
						
							|  |  |  |                 # We use the `send` method directly, because coroutines | 
					
						
							|  |  |  |                 # don't have `__iter__` and `__next__` methods. | 
					
						
							|  |  |  |                 result = coro.send(None) | 
					
						
							| 
									
										
										
										
											2013-10-17 13:40:50 -07:00
										 |  |  |             else: | 
					
						
							| 
									
										
										
										
											2015-11-20 12:41:03 -05:00
										 |  |  |                 result = coro.throw(exc) | 
					
						
							| 
									
										
										
										
											2013-10-17 13:40:50 -07:00
										 |  |  |         except StopIteration as exc: | 
					
						
							| 
									
										
										
										
											2017-05-11 21:18:38 +09:00
										 |  |  |             if self._must_cancel: | 
					
						
							|  |  |  |                 # Task is cancelled right before coro stops. | 
					
						
							|  |  |  |                 self._must_cancel = False | 
					
						
							| 
									
										
										
										
											2020-05-15 16:55:50 -07:00
										 |  |  |                 super().cancel(msg=self._cancel_message) | 
					
						
							| 
									
										
										
										
											2017-05-11 21:18:38 +09:00
										 |  |  |             else: | 
					
						
							| 
									
										
										
										
											2017-12-25 10:48:15 -05:00
										 |  |  |                 super().set_result(exc.value) | 
					
						
							| 
									
										
										
										
											2020-05-15 16:55:50 -07:00
										 |  |  |         except exceptions.CancelledError as exc: | 
					
						
							| 
									
										
										
										
											2020-05-17 22:47:31 -07:00
										 |  |  |             # Save the original exception so we can chain it later. | 
					
						
							|  |  |  |             self._cancelled_exc = exc | 
					
						
							|  |  |  |             super().cancel()  # I.e., Future.cancel(self). | 
					
						
							| 
									
										
										
										
											2019-05-27 14:45:12 +02:00
										 |  |  |         except (KeyboardInterrupt, SystemExit) as exc: | 
					
						
							| 
									
										
										
										
											2017-12-25 10:48:15 -05:00
										 |  |  |             super().set_exception(exc) | 
					
						
							| 
									
										
										
										
											2019-05-27 14:45:12 +02:00
										 |  |  |             raise | 
					
						
							| 
									
										
										
										
											2013-10-17 13:40:50 -07:00
										 |  |  |         except BaseException as exc: | 
					
						
							| 
									
										
										
										
											2017-12-25 10:48:15 -05:00
										 |  |  |             super().set_exception(exc) | 
					
						
							| 
									
										
										
										
											2013-10-17 13:40:50 -07:00
										 |  |  |         else: | 
					
						
							| 
									
										
										
										
											2016-09-09 12:54:54 -07:00
										 |  |  |             blocking = getattr(result, '_asyncio_future_blocking', None) | 
					
						
							|  |  |  |             if blocking is not None: | 
					
						
							| 
									
										
										
										
											2013-10-17 13:40:50 -07:00
										 |  |  |                 # Yielded Future must come from Future.__iter__(). | 
					
						
							| 
									
										
										
										
											2017-12-23 15:04:15 -05:00
										 |  |  |                 if futures._get_loop(result) is not self._loop: | 
					
						
							| 
									
										
										
										
											2017-12-10 18:36:12 -05:00
										 |  |  |                     new_exc = RuntimeError( | 
					
						
							|  |  |  |                         f'Task {self!r} got Future ' | 
					
						
							|  |  |  |                         f'{result!r} attached to a different loop') | 
					
						
							| 
									
										
										
										
											2018-01-22 19:11:18 -05:00
										 |  |  |                     self._loop.call_soon( | 
					
						
							| 
									
										
										
										
											2018-01-24 11:31:01 -05:00
										 |  |  |                         self.__step, new_exc, context=self._context) | 
					
						
							| 
									
										
										
										
											2016-09-09 12:54:54 -07:00
										 |  |  |                 elif blocking: | 
					
						
							| 
									
										
										
										
											2016-10-09 12:19:12 -04:00
										 |  |  |                     if result is self: | 
					
						
							| 
									
										
										
										
											2017-12-10 18:36:12 -05:00
										 |  |  |                         new_exc = RuntimeError( | 
					
						
							|  |  |  |                             f'Task cannot await on itself: {self!r}') | 
					
						
							| 
									
										
										
										
											2018-01-22 19:11:18 -05:00
										 |  |  |                         self._loop.call_soon( | 
					
						
							| 
									
										
										
										
											2018-01-24 11:31:01 -05:00
										 |  |  |                             self.__step, new_exc, context=self._context) | 
					
						
							| 
									
										
										
										
											2016-10-09 12:19:12 -04:00
										 |  |  |                     else: | 
					
						
							|  |  |  |                         result._asyncio_future_blocking = False | 
					
						
							| 
									
										
										
										
											2018-01-22 19:11:18 -05:00
										 |  |  |                         result.add_done_callback( | 
					
						
							| 
									
										
										
										
											2018-01-24 11:31:01 -05:00
										 |  |  |                             self.__wakeup, context=self._context) | 
					
						
							| 
									
										
										
										
											2016-10-09 12:19:12 -04:00
										 |  |  |                         self._fut_waiter = result | 
					
						
							|  |  |  |                         if self._must_cancel: | 
					
						
							| 
									
										
										
										
											2020-05-15 16:55:50 -07:00
										 |  |  |                             if self._fut_waiter.cancel( | 
					
						
							|  |  |  |                                     msg=self._cancel_message): | 
					
						
							| 
									
										
										
										
											2016-10-09 12:19:12 -04:00
										 |  |  |                                 self._must_cancel = False | 
					
						
							| 
									
										
										
										
											2013-10-17 13:40:50 -07:00
										 |  |  |                 else: | 
					
						
							| 
									
										
										
										
											2017-12-10 18:36:12 -05:00
										 |  |  |                     new_exc = RuntimeError( | 
					
						
							|  |  |  |                         f'yield was used instead of yield from ' | 
					
						
							|  |  |  |                         f'in task {self!r} with {result!r}') | 
					
						
							| 
									
										
										
										
											2018-01-22 19:11:18 -05:00
										 |  |  |                     self._loop.call_soon( | 
					
						
							| 
									
										
										
										
											2018-01-24 11:31:01 -05:00
										 |  |  |                         self.__step, new_exc, context=self._context) | 
					
						
							| 
									
										
										
										
											2017-12-10 18:36:12 -05:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-10-17 13:40:50 -07:00
										 |  |  |             elif result is None: | 
					
						
							|  |  |  |                 # Bare yield relinquishes control for one event loop iteration. | 
					
						
							| 
									
										
										
										
											2018-01-24 11:31:01 -05:00
										 |  |  |                 self._loop.call_soon(self.__step, context=self._context) | 
					
						
							| 
									
										
										
										
											2013-10-17 13:40:50 -07:00
										 |  |  |             elif inspect.isgenerator(result): | 
					
						
							|  |  |  |                 # Yielding a generator is just wrong. | 
					
						
							| 
									
										
										
										
											2017-12-10 18:36:12 -05:00
										 |  |  |                 new_exc = RuntimeError( | 
					
						
							|  |  |  |                     f'yield was used instead of yield from for ' | 
					
						
							| 
									
										
										
										
											2018-05-20 16:30:31 +03:00
										 |  |  |                     f'generator in task {self!r} with {result!r}') | 
					
						
							| 
									
										
										
										
											2018-01-22 19:11:18 -05:00
										 |  |  |                 self._loop.call_soon( | 
					
						
							| 
									
										
										
										
											2018-01-24 11:31:01 -05:00
										 |  |  |                     self.__step, new_exc, context=self._context) | 
					
						
							| 
									
										
										
										
											2013-10-17 13:40:50 -07:00
										 |  |  |             else: | 
					
						
							|  |  |  |                 # Yielding something else is an error. | 
					
						
							| 
									
										
										
										
											2017-12-10 18:36:12 -05:00
										 |  |  |                 new_exc = RuntimeError(f'Task got bad yield: {result!r}') | 
					
						
							| 
									
										
										
										
											2018-01-22 19:11:18 -05:00
										 |  |  |                 self._loop.call_soon( | 
					
						
							| 
									
										
										
										
											2018-01-24 11:31:01 -05:00
										 |  |  |                     self.__step, new_exc, context=self._context) | 
					
						
							| 
									
										
										
										
											2013-12-06 12:57:40 -08:00
										 |  |  |         finally: | 
					
						
							| 
									
										
										
										
											2017-12-16 21:58:38 +02:00
										 |  |  |             _leave_task(self._loop, self) | 
					
						
							| 
									
										
										
										
											2014-03-04 23:07:08 +01:00
										 |  |  |             self = None  # Needed to break cycles when an exception occurs. | 
					
						
							| 
									
										
										
										
											2013-10-17 13:40:50 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-01-24 11:31:01 -05:00
										 |  |  |     def __wakeup(self, future): | 
					
						
							| 
									
										
										
										
											2013-10-17 13:40:50 -07:00
										 |  |  |         try: | 
					
						
							| 
									
										
										
										
											2015-11-16 15:12:10 -05:00
										 |  |  |             future.result() | 
					
						
							| 
									
										
										
										
											2019-05-27 14:45:12 +02:00
										 |  |  |         except BaseException as exc: | 
					
						
							| 
									
										
										
										
											2013-10-17 13:40:50 -07:00
										 |  |  |             # This may also be a cancellation. | 
					
						
							| 
									
										
										
										
											2018-01-24 11:31:01 -05:00
										 |  |  |             self.__step(exc) | 
					
						
							| 
									
										
										
										
											2013-10-17 13:40:50 -07:00
										 |  |  |         else: | 
					
						
							| 
									
										
										
										
											2015-11-16 15:12:10 -05:00
										 |  |  |             # Don't pass the value of `future.result()` explicitly, | 
					
						
							|  |  |  |             # as `Future.__iter__` and `Future.__await__` don't need it. | 
					
						
							|  |  |  |             # If we call `_step(value, None)` instead of `_step()`, | 
					
						
							|  |  |  |             # Python eval loop would use `.send(value)` method call, | 
					
						
							|  |  |  |             # instead of `__next__()`, which is slower for futures | 
					
						
							|  |  |  |             # that return non-generator iterators from their `__iter__`. | 
					
						
							| 
									
										
										
										
											2018-01-24 11:31:01 -05:00
										 |  |  |             self.__step() | 
					
						
							| 
									
										
										
										
											2013-10-17 13:40:50 -07:00
										 |  |  |         self = None  # Needed to break cycles when an exception occurs. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-10-28 12:52:37 -04:00
										 |  |  | _PyTask = Task | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | try: | 
					
						
							|  |  |  |     import _asyncio | 
					
						
							|  |  |  | except ImportError: | 
					
						
							|  |  |  |     pass | 
					
						
							|  |  |  | else: | 
					
						
							|  |  |  |     # _CTask is needed for tests. | 
					
						
							|  |  |  |     Task = _CTask = _asyncio.Task | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-08-09 00:06:47 +03:00
										 |  |  | def create_task(coro, *, name=None): | 
					
						
							| 
									
										
										
										
											2017-12-15 07:04:38 +02:00
										 |  |  |     """Schedule the execution of a coroutine object in a spawn task.
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     Return a Task object. | 
					
						
							|  |  |  |     """
 | 
					
						
							|  |  |  |     loop = events.get_running_loop() | 
					
						
							| 
									
										
										
										
											2018-08-09 00:06:47 +03:00
										 |  |  |     task = loop.create_task(coro) | 
					
						
							|  |  |  |     _set_task_name(task, name) | 
					
						
							|  |  |  |     return task | 
					
						
							| 
									
										
										
										
											2017-12-15 07:04:38 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-10-17 13:40:50 -07:00
										 |  |  | # wait() and as_completed() similar to those in PEP 3148. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | FIRST_COMPLETED = concurrent.futures.FIRST_COMPLETED | 
					
						
							|  |  |  | FIRST_EXCEPTION = concurrent.futures.FIRST_EXCEPTION | 
					
						
							|  |  |  | ALL_COMPLETED = concurrent.futures.ALL_COMPLETED | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-12-09 00:23:48 +02:00
										 |  |  | async def wait(fs, *, loop=None, timeout=None, return_when=ALL_COMPLETED): | 
					
						
							| 
									
										
										
										
											2013-10-17 13:40:50 -07:00
										 |  |  |     """Wait for the Futures and coroutines given by fs to complete.
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-11-02 03:19:45 -08:00
										 |  |  |     The fs iterable must not be empty. | 
					
						
							| 
									
										
										
										
											2014-06-10 11:16:05 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-10-17 13:40:50 -07:00
										 |  |  |     Coroutines will be wrapped in Tasks. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     Returns two sets of Future: (done, pending). | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     Usage: | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-12-09 00:23:48 +02:00
										 |  |  |         done, pending = await asyncio.wait(fs) | 
					
						
							| 
									
										
										
										
											2013-10-17 13:40:50 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  |     Note: This does not raise TimeoutError! Futures that aren't done | 
					
						
							|  |  |  |     when the timeout occurs are returned in the second set. | 
					
						
							|  |  |  |     """
 | 
					
						
							| 
									
										
										
										
											2016-09-09 14:26:31 -07:00
										 |  |  |     if futures.isfuture(fs) or coroutines.iscoroutine(fs): | 
					
						
							| 
									
										
										
										
											2017-12-10 18:36:12 -05:00
										 |  |  |         raise TypeError(f"expect a list of futures, not {type(fs).__name__}") | 
					
						
							| 
									
										
										
										
											2013-10-17 13:40:50 -07:00
										 |  |  |     if not fs: | 
					
						
							|  |  |  |         raise ValueError('Set of coroutines/Futures is empty.') | 
					
						
							| 
									
										
										
										
											2014-07-16 18:50:39 +02:00
										 |  |  |     if return_when not in (FIRST_COMPLETED, FIRST_EXCEPTION, ALL_COMPLETED): | 
					
						
							| 
									
										
										
										
											2017-12-10 18:36:12 -05:00
										 |  |  |         raise ValueError(f'Invalid return_when value: {return_when}') | 
					
						
							| 
									
										
										
										
											2013-10-17 13:40:50 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  |     if loop is None: | 
					
						
							| 
									
										
										
										
											2018-09-24 06:51:22 -03:00
										 |  |  |         loop = events.get_running_loop() | 
					
						
							|  |  |  |     else: | 
					
						
							| 
									
										
										
										
											2019-05-20 23:20:10 -07:00
										 |  |  |         warnings.warn("The loop argument is deprecated since Python 3.8, " | 
					
						
							|  |  |  |                       "and scheduled for removal in Python 3.10.", | 
					
						
							| 
									
										
										
										
											2018-09-24 06:51:22 -03:00
										 |  |  |                       DeprecationWarning, stacklevel=2) | 
					
						
							| 
									
										
										
										
											2013-10-17 13:40:50 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-11-10 15:10:57 -08:00
										 |  |  |     fs = set(fs) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if any(coroutines.iscoroutine(f) for f in fs): | 
					
						
							| 
									
										
										
										
											2019-12-30 06:50:19 -05:00
										 |  |  |         warnings.warn("The explicit passing of coroutine objects to " | 
					
						
							|  |  |  |                       "asyncio.wait() is deprecated since Python 3.8, and " | 
					
						
							|  |  |  |                       "scheduled for removal in Python 3.11.", | 
					
						
							|  |  |  |                       DeprecationWarning, stacklevel=2) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-11-10 15:10:57 -08:00
										 |  |  |     fs = {ensure_future(f, loop=loop) for f in fs} | 
					
						
							| 
									
										
										
										
											2013-10-17 13:40:50 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-12-09 00:23:48 +02:00
										 |  |  |     return await _wait(fs, timeout, return_when, loop) | 
					
						
							| 
									
										
										
										
											2013-10-17 13:40:50 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-08-28 11:19:25 +02:00
										 |  |  | def _release_waiter(waiter, *args): | 
					
						
							| 
									
										
										
										
											2013-10-17 13:40:50 -07:00
										 |  |  |     if not waiter.done(): | 
					
						
							| 
									
										
										
										
											2014-08-28 11:19:25 +02:00
										 |  |  |         waiter.set_result(None) | 
					
						
							| 
									
										
										
										
											2013-10-17 13:40:50 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-12-09 00:23:48 +02:00
										 |  |  | async def wait_for(fut, timeout, *, loop=None): | 
					
						
							| 
									
										
										
										
											2013-10-17 13:40:50 -07:00
										 |  |  |     """Wait for the single Future or coroutine to complete, with timeout.
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     Coroutine will be wrapped in Task. | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-01-23 17:40:59 +01:00
										 |  |  |     Returns result of the Future or coroutine.  When a timeout occurs, | 
					
						
							|  |  |  |     it cancels the task and raises TimeoutError.  To avoid the task | 
					
						
							|  |  |  |     cancellation, wrap it in shield(). | 
					
						
							| 
									
										
										
										
											2013-10-17 13:40:50 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-01-15 16:29:10 +01:00
										 |  |  |     If the wait is cancelled, the task is also cancelled. | 
					
						
							| 
									
										
										
										
											2013-10-17 13:40:50 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-01-15 16:29:10 +01:00
										 |  |  |     This function is a coroutine. | 
					
						
							| 
									
										
										
										
											2013-10-17 13:40:50 -07:00
										 |  |  |     """
 | 
					
						
							|  |  |  |     if loop is None: | 
					
						
							| 
									
										
										
										
											2018-09-24 06:51:22 -03:00
										 |  |  |         loop = events.get_running_loop() | 
					
						
							|  |  |  |     else: | 
					
						
							| 
									
										
										
										
											2019-05-20 23:20:10 -07:00
										 |  |  |         warnings.warn("The loop argument is deprecated since Python 3.8, " | 
					
						
							|  |  |  |                       "and scheduled for removal in Python 3.10.", | 
					
						
							| 
									
										
										
										
											2018-09-24 06:51:22 -03:00
										 |  |  |                       DeprecationWarning, stacklevel=2) | 
					
						
							| 
									
										
										
										
											2013-10-17 13:40:50 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-01-29 14:30:38 -08:00
										 |  |  |     if timeout is None: | 
					
						
							| 
									
										
										
										
											2017-12-09 00:23:48 +02:00
										 |  |  |         return await fut | 
					
						
							| 
									
										
										
										
											2014-01-29 14:30:38 -08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-10-05 19:04:39 +03:00
										 |  |  |     if timeout <= 0: | 
					
						
							|  |  |  |         fut = ensure_future(fut, loop=loop) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         if fut.done(): | 
					
						
							|  |  |  |             return fut.result() | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-08-26 10:14:59 -07:00
										 |  |  |         await _cancel_and_wait(fut, loop=loop) | 
					
						
							|  |  |  |         try: | 
					
						
							| 
									
										
										
										
											2021-11-30 05:39:13 -08:00
										 |  |  |             return fut.result() | 
					
						
							| 
									
										
										
										
											2020-08-26 10:14:59 -07:00
										 |  |  |         except exceptions.CancelledError as exc: | 
					
						
							|  |  |  |             raise exceptions.TimeoutError() from exc | 
					
						
							| 
									
										
										
										
											2017-10-05 19:04:39 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-05-16 15:38:39 -04:00
										 |  |  |     waiter = loop.create_future() | 
					
						
							| 
									
										
										
										
											2014-08-28 11:19:25 +02:00
										 |  |  |     timeout_handle = loop.call_later(timeout, _release_waiter, waiter) | 
					
						
							|  |  |  |     cb = functools.partial(_release_waiter, waiter) | 
					
						
							| 
									
										
										
										
											2013-10-17 13:40:50 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-05-11 14:48:38 -04:00
										 |  |  |     fut = ensure_future(fut, loop=loop) | 
					
						
							| 
									
										
										
										
											2013-10-17 13:40:50 -07:00
										 |  |  |     fut.add_done_callback(cb) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     try: | 
					
						
							| 
									
										
										
										
											2014-08-28 11:19:25 +02:00
										 |  |  |         # wait until the future completes or the timeout | 
					
						
							| 
									
										
										
										
											2015-01-15 16:29:10 +01:00
										 |  |  |         try: | 
					
						
							| 
									
										
										
										
											2017-12-09 00:23:48 +02:00
										 |  |  |             await waiter | 
					
						
							| 
									
										
										
										
											2018-09-11 10:13:04 -07:00
										 |  |  |         except exceptions.CancelledError: | 
					
						
							| 
									
										
										
										
											2020-08-26 10:15:35 -07:00
										 |  |  |             if fut.done(): | 
					
						
							|  |  |  |                 return fut.result() | 
					
						
							|  |  |  |             else: | 
					
						
							|  |  |  |                 fut.remove_done_callback(cb) | 
					
						
							| 
									
										
										
										
											2020-12-18 11:19:10 -08:00
										 |  |  |                 # We must ensure that the task is not running | 
					
						
							|  |  |  |                 # after wait_for() returns. | 
					
						
							|  |  |  |                 # See https://bugs.python.org/issue32751 | 
					
						
							|  |  |  |                 await _cancel_and_wait(fut, loop=loop) | 
					
						
							| 
									
										
										
										
											2020-08-26 10:15:35 -07:00
										 |  |  |                 raise | 
					
						
							| 
									
										
										
										
											2014-08-28 11:19:25 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |         if fut.done(): | 
					
						
							| 
									
										
										
										
											2013-10-17 13:40:50 -07:00
										 |  |  |             return fut.result() | 
					
						
							|  |  |  |         else: | 
					
						
							|  |  |  |             fut.remove_done_callback(cb) | 
					
						
							| 
									
										
										
										
											2018-05-29 17:31:01 -04:00
										 |  |  |             # We must ensure that the task is not running | 
					
						
							|  |  |  |             # after wait_for() returns. | 
					
						
							|  |  |  |             # See https://bugs.python.org/issue32751 | 
					
						
							|  |  |  |             await _cancel_and_wait(fut, loop=loop) | 
					
						
							| 
									
										
										
										
											2020-05-15 23:12:05 +03:00
										 |  |  |             # In case task cancellation failed with some | 
					
						
							|  |  |  |             # exception, we should re-raise it | 
					
						
							|  |  |  |             # See https://bugs.python.org/issue40607 | 
					
						
							|  |  |  |             try: | 
					
						
							| 
									
										
										
										
											2021-11-30 05:39:13 -08:00
										 |  |  |                 return fut.result() | 
					
						
							| 
									
										
										
										
											2020-05-15 23:12:05 +03:00
										 |  |  |             except exceptions.CancelledError as exc: | 
					
						
							|  |  |  |                 raise exceptions.TimeoutError() from exc | 
					
						
							| 
									
										
										
										
											2013-10-17 13:40:50 -07:00
										 |  |  |     finally: | 
					
						
							|  |  |  |         timeout_handle.cancel() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-12-09 00:23:48 +02:00
										 |  |  | async def _wait(fs, timeout, return_when, loop): | 
					
						
							| 
									
										
										
										
											2018-05-29 17:31:01 -04:00
										 |  |  |     """Internal helper for wait().
 | 
					
						
							| 
									
										
										
										
											2013-10-17 13:40:50 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  |     The fs argument must be a collection of Futures. | 
					
						
							|  |  |  |     """
 | 
					
						
							|  |  |  |     assert fs, 'Set of Futures is empty.' | 
					
						
							| 
									
										
										
										
											2016-05-16 15:38:39 -04:00
										 |  |  |     waiter = loop.create_future() | 
					
						
							| 
									
										
										
										
											2013-10-17 13:40:50 -07:00
										 |  |  |     timeout_handle = None | 
					
						
							|  |  |  |     if timeout is not None: | 
					
						
							|  |  |  |         timeout_handle = loop.call_later(timeout, _release_waiter, waiter) | 
					
						
							|  |  |  |     counter = len(fs) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def _on_completion(f): | 
					
						
							|  |  |  |         nonlocal counter | 
					
						
							|  |  |  |         counter -= 1 | 
					
						
							|  |  |  |         if (counter <= 0 or | 
					
						
							|  |  |  |             return_when == FIRST_COMPLETED or | 
					
						
							|  |  |  |             return_when == FIRST_EXCEPTION and (not f.cancelled() and | 
					
						
							|  |  |  |                                                 f.exception() is not None)): | 
					
						
							|  |  |  |             if timeout_handle is not None: | 
					
						
							|  |  |  |                 timeout_handle.cancel() | 
					
						
							|  |  |  |             if not waiter.done(): | 
					
						
							| 
									
										
										
										
											2014-08-28 11:19:25 +02:00
										 |  |  |                 waiter.set_result(None) | 
					
						
							| 
									
										
										
										
											2013-10-17 13:40:50 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  |     for f in fs: | 
					
						
							|  |  |  |         f.add_done_callback(_on_completion) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     try: | 
					
						
							| 
									
										
										
										
											2017-12-09 00:23:48 +02:00
										 |  |  |         await waiter | 
					
						
							| 
									
										
										
										
											2013-10-17 13:40:50 -07:00
										 |  |  |     finally: | 
					
						
							|  |  |  |         if timeout_handle is not None: | 
					
						
							|  |  |  |             timeout_handle.cancel() | 
					
						
							| 
									
										
										
										
											2019-05-03 18:18:02 +03:00
										 |  |  |         for f in fs: | 
					
						
							|  |  |  |             f.remove_done_callback(_on_completion) | 
					
						
							| 
									
										
										
										
											2013-10-17 13:40:50 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  |     done, pending = set(), set() | 
					
						
							|  |  |  |     for f in fs: | 
					
						
							|  |  |  |         if f.done(): | 
					
						
							|  |  |  |             done.add(f) | 
					
						
							|  |  |  |         else: | 
					
						
							|  |  |  |             pending.add(f) | 
					
						
							|  |  |  |     return done, pending | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-05-29 17:31:01 -04:00
										 |  |  | async def _cancel_and_wait(fut, loop): | 
					
						
							|  |  |  |     """Cancel the *fut* future or task and wait until it completes.""" | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     waiter = loop.create_future() | 
					
						
							|  |  |  |     cb = functools.partial(_release_waiter, waiter) | 
					
						
							|  |  |  |     fut.add_done_callback(cb) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     try: | 
					
						
							|  |  |  |         fut.cancel() | 
					
						
							|  |  |  |         # We cannot wait on *fut* directly to make | 
					
						
							|  |  |  |         # sure _cancel_and_wait itself is reliably cancellable. | 
					
						
							|  |  |  |         await waiter | 
					
						
							|  |  |  |     finally: | 
					
						
							|  |  |  |         fut.remove_done_callback(cb) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-10-17 13:40:50 -07:00
										 |  |  | # This is *not* a @coroutine!  It is just an iterator (yielding Futures). | 
					
						
							|  |  |  | def as_completed(fs, *, loop=None, timeout=None): | 
					
						
							| 
									
										
										
										
											2014-02-12 17:58:19 -08:00
										 |  |  |     """Return an iterator whose values are coroutines.
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     When waiting for the yielded coroutines you'll get the results (or | 
					
						
							|  |  |  |     exceptions!) of the original Futures (or coroutines), in the order | 
					
						
							|  |  |  |     in which and as soon as they complete. | 
					
						
							| 
									
										
										
										
											2013-10-17 13:40:50 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  |     This differs from PEP 3148; the proper way to use this is: | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         for f in as_completed(fs): | 
					
						
							| 
									
										
										
										
											2017-12-09 00:23:48 +02:00
										 |  |  |             result = await f  # The 'await' may raise. | 
					
						
							| 
									
										
										
										
											2013-10-17 13:40:50 -07:00
										 |  |  |             # Use result. | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-12-09 00:23:48 +02:00
										 |  |  |     If a timeout is specified, the 'await' will raise | 
					
						
							| 
									
										
										
										
											2014-02-12 17:58:19 -08:00
										 |  |  |     TimeoutError when the timeout occurs before all Futures are done. | 
					
						
							| 
									
										
										
										
											2013-10-17 13:40:50 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  |     Note: The futures 'f' are not necessarily members of fs. | 
					
						
							|  |  |  |     """
 | 
					
						
							| 
									
										
										
										
											2016-09-09 14:26:31 -07:00
										 |  |  |     if futures.isfuture(fs) or coroutines.iscoroutine(fs): | 
					
						
							| 
									
										
										
										
											2020-11-02 03:19:45 -08:00
										 |  |  |         raise TypeError(f"expect an iterable of futures, not {type(fs).__name__}") | 
					
						
							| 
									
										
										
										
											2019-09-12 15:40:40 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-09-04 20:54:50 +03:00
										 |  |  |     if loop is not None: | 
					
						
							|  |  |  |         warnings.warn("The loop argument is deprecated since Python 3.8, " | 
					
						
							|  |  |  |                       "and scheduled for removal in Python 3.10.", | 
					
						
							|  |  |  |                       DeprecationWarning, stacklevel=2) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-02-12 17:58:19 -08:00
										 |  |  |     from .queues import Queue  # Import here to avoid circular import problem. | 
					
						
							|  |  |  |     done = Queue(loop=loop) | 
					
						
							| 
									
										
										
										
											2019-09-12 15:40:40 +03:00
										 |  |  | 
 | 
					
						
							|  |  |  |     if loop is None: | 
					
						
							|  |  |  |         loop = events.get_event_loop() | 
					
						
							|  |  |  |     todo = {ensure_future(f, loop=loop) for f in set(fs)} | 
					
						
							| 
									
										
										
										
											2014-02-12 17:58:19 -08:00
										 |  |  |     timeout_handle = None | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def _on_timeout(): | 
					
						
							|  |  |  |         for f in todo: | 
					
						
							|  |  |  |             f.remove_done_callback(_on_completion) | 
					
						
							|  |  |  |             done.put_nowait(None)  # Queue a dummy value for _wait_for_one(). | 
					
						
							|  |  |  |         todo.clear()  # Can't do todo.remove(f) in the loop. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def _on_completion(f): | 
					
						
							|  |  |  |         if not todo: | 
					
						
							|  |  |  |             return  # _on_timeout() was here first. | 
					
						
							|  |  |  |         todo.remove(f) | 
					
						
							|  |  |  |         done.put_nowait(f) | 
					
						
							|  |  |  |         if not todo and timeout_handle is not None: | 
					
						
							|  |  |  |             timeout_handle.cancel() | 
					
						
							| 
									
										
										
										
											2013-10-17 13:40:50 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-12-09 00:23:48 +02:00
										 |  |  |     async def _wait_for_one(): | 
					
						
							|  |  |  |         f = await done.get() | 
					
						
							| 
									
										
										
										
											2014-02-12 17:58:19 -08:00
										 |  |  |         if f is None: | 
					
						
							|  |  |  |             # Dummy value from _on_timeout(). | 
					
						
							| 
									
										
										
										
											2018-09-11 10:13:04 -07:00
										 |  |  |             raise exceptions.TimeoutError | 
					
						
							| 
									
										
										
										
											2014-02-12 17:58:19 -08:00
										 |  |  |         return f.result()  # May raise f.exception(). | 
					
						
							| 
									
										
										
										
											2013-10-17 13:40:50 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-02-12 17:58:19 -08:00
										 |  |  |     for f in todo: | 
					
						
							|  |  |  |         f.add_done_callback(_on_completion) | 
					
						
							|  |  |  |     if todo and timeout is not None: | 
					
						
							|  |  |  |         timeout_handle = loop.call_later(timeout, _on_timeout) | 
					
						
							| 
									
										
										
										
											2013-10-17 13:40:50 -07:00
										 |  |  |     for _ in range(len(todo)): | 
					
						
							|  |  |  |         yield _wait_for_one() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-12-09 00:23:48 +02:00
										 |  |  | @types.coroutine | 
					
						
							|  |  |  | def __sleep0(): | 
					
						
							|  |  |  |     """Skip one event loop run cycle.
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     This is a private helper for 'asyncio.sleep()', used | 
					
						
							|  |  |  |     when the 'delay' is set to 0.  It uses a bare 'yield' | 
					
						
							| 
									
										
										
										
											2018-01-24 11:31:01 -05:00
										 |  |  |     expression (which Task.__step knows how to handle) | 
					
						
							| 
									
										
										
										
											2017-12-09 00:23:48 +02:00
										 |  |  |     instead of creating a Future object. | 
					
						
							|  |  |  |     """
 | 
					
						
							|  |  |  |     yield | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | async def sleep(delay, result=None, *, loop=None): | 
					
						
							| 
									
										
										
										
											2013-10-17 13:40:50 -07:00
										 |  |  |     """Coroutine that completes after a given time (in seconds).""" | 
					
						
							| 
									
										
										
										
											2021-08-18 19:47:55 +01:00
										 |  |  |     if loop is not None: | 
					
						
							|  |  |  |         warnings.warn("The loop argument is deprecated since Python 3.8, " | 
					
						
							|  |  |  |                       "and scheduled for removal in Python 3.10.", | 
					
						
							|  |  |  |                       DeprecationWarning, stacklevel=2) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-12-17 16:41:30 +02:00
										 |  |  |     if delay <= 0: | 
					
						
							| 
									
										
										
										
											2017-12-09 00:23:48 +02:00
										 |  |  |         await __sleep0() | 
					
						
							| 
									
										
										
										
											2015-11-05 14:29:04 -05:00
										 |  |  |         return result | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-05-16 15:38:39 -04:00
										 |  |  |     if loop is None: | 
					
						
							| 
									
										
										
										
											2018-09-24 06:51:22 -03:00
										 |  |  |         loop = events.get_running_loop() | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-05-16 15:38:39 -04:00
										 |  |  |     future = loop.create_future() | 
					
						
							| 
									
										
										
										
											2017-12-23 15:04:15 -05:00
										 |  |  |     h = loop.call_later(delay, | 
					
						
							|  |  |  |                         futures._set_result_unless_cancelled, | 
					
						
							|  |  |  |                         future, result) | 
					
						
							| 
									
										
										
										
											2013-10-17 13:40:50 -07:00
										 |  |  |     try: | 
					
						
							| 
									
										
										
										
											2017-12-09 00:23:48 +02:00
										 |  |  |         return await future | 
					
						
							| 
									
										
										
										
											2013-10-17 13:40:50 -07:00
										 |  |  |     finally: | 
					
						
							|  |  |  |         h.cancel() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-05-11 14:48:38 -04:00
										 |  |  | def ensure_future(coro_or_future, *, loop=None): | 
					
						
							| 
									
										
										
										
											2015-10-02 15:00:19 -04:00
										 |  |  |     """Wrap a coroutine or an awaitable in a future.
 | 
					
						
							| 
									
										
										
										
											2015-05-11 14:48:38 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-10-17 13:40:50 -07:00
										 |  |  |     If the argument is a Future, it is returned directly. | 
					
						
							|  |  |  |     """
 | 
					
						
							| 
									
										
										
										
											2018-05-28 06:42:05 -10:00
										 |  |  |     if coroutines.iscoroutine(coro_or_future): | 
					
						
							| 
									
										
										
										
											2014-07-08 11:29:25 +02:00
										 |  |  |         if loop is None: | 
					
						
							|  |  |  |             loop = events.get_event_loop() | 
					
						
							|  |  |  |         task = loop.create_task(coro_or_future) | 
					
						
							| 
									
										
										
										
											2014-06-27 13:52:20 +02:00
										 |  |  |         if task._source_traceback: | 
					
						
							|  |  |  |             del task._source_traceback[-1] | 
					
						
							|  |  |  |         return task | 
					
						
							| 
									
										
										
										
											2018-05-28 06:42:05 -10:00
										 |  |  |     elif futures.isfuture(coro_or_future): | 
					
						
							|  |  |  |         if loop is not None and loop is not futures._get_loop(coro_or_future): | 
					
						
							| 
									
										
										
										
											2019-05-03 09:35:26 -06:00
										 |  |  |             raise ValueError('The future belongs to a different loop than ' | 
					
						
							|  |  |  |                              'the one specified as the loop argument') | 
					
						
							| 
									
										
										
										
											2018-05-28 06:42:05 -10:00
										 |  |  |         return coro_or_future | 
					
						
							| 
									
										
										
										
											2017-11-28 14:43:52 +01:00
										 |  |  |     elif inspect.isawaitable(coro_or_future): | 
					
						
							| 
									
										
										
										
											2015-10-02 15:00:19 -04:00
										 |  |  |         return ensure_future(_wrap_awaitable(coro_or_future), loop=loop) | 
					
						
							| 
									
										
										
										
											2013-10-17 13:40:50 -07:00
										 |  |  |     else: | 
					
						
							| 
									
										
										
										
											2017-04-21 16:49:48 -04:00
										 |  |  |         raise TypeError('An asyncio.Future, a coroutine or an awaitable is ' | 
					
						
							|  |  |  |                         'required') | 
					
						
							| 
									
										
										
										
											2015-10-02 15:00:19 -04:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-05-16 17:52:10 +03:00
										 |  |  | @types.coroutine | 
					
						
							| 
									
										
										
										
											2015-10-02 15:00:19 -04:00
										 |  |  | def _wrap_awaitable(awaitable): | 
					
						
							|  |  |  |     """Helper for asyncio.ensure_future().
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     Wraps awaitable (an object with __await__) into a coroutine | 
					
						
							|  |  |  |     that will later be wrapped in a Task by ensure_future(). | 
					
						
							|  |  |  |     """
 | 
					
						
							|  |  |  |     return (yield from awaitable.__await__()) | 
					
						
							| 
									
										
										
										
											2013-10-17 13:40:50 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-05-16 17:52:10 +03:00
										 |  |  | _wrap_awaitable._is_coroutine = _is_coroutine | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-10-17 13:40:50 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  | class _GatheringFuture(futures.Future): | 
					
						
							|  |  |  |     """Helper for gather().
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     This overrides cancel() to cancel all the children and act more | 
					
						
							|  |  |  |     like Task.cancel(), which doesn't immediately mark itself as | 
					
						
							|  |  |  |     cancelled. | 
					
						
							|  |  |  |     """
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def __init__(self, children, *, loop=None): | 
					
						
							|  |  |  |         super().__init__(loop=loop) | 
					
						
							|  |  |  |         self._children = children | 
					
						
							| 
									
										
										
										
											2018-05-29 17:20:02 -04:00
										 |  |  |         self._cancel_requested = False | 
					
						
							| 
									
										
										
										
											2013-10-17 13:40:50 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-05-15 16:55:50 -07:00
										 |  |  |     def cancel(self, msg=None): | 
					
						
							| 
									
										
										
										
											2013-10-17 13:40:50 -07:00
										 |  |  |         if self.done(): | 
					
						
							|  |  |  |             return False | 
					
						
							| 
									
										
										
										
											2016-10-21 17:22:17 -04:00
										 |  |  |         ret = False | 
					
						
							| 
									
										
										
										
											2013-10-17 13:40:50 -07:00
										 |  |  |         for child in self._children: | 
					
						
							| 
									
										
										
										
											2020-05-15 16:55:50 -07:00
										 |  |  |             if child.cancel(msg=msg): | 
					
						
							| 
									
										
										
										
											2016-10-21 17:22:17 -04:00
										 |  |  |                 ret = True | 
					
						
							| 
									
										
										
										
											2018-05-29 17:20:02 -04:00
										 |  |  |         if ret: | 
					
						
							|  |  |  |             # If any child tasks were actually cancelled, we should | 
					
						
							|  |  |  |             # propagate the cancellation request regardless of | 
					
						
							|  |  |  |             # *return_exceptions* argument.  See issue 32684. | 
					
						
							|  |  |  |             self._cancel_requested = True | 
					
						
							| 
									
										
										
										
											2016-10-21 17:22:17 -04:00
										 |  |  |         return ret | 
					
						
							| 
									
										
										
										
											2013-10-17 13:40:50 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | def gather(*coros_or_futures, loop=None, return_exceptions=False): | 
					
						
							| 
									
										
										
										
											2017-12-19 07:19:53 -05:00
										 |  |  |     """Return a future aggregating results from the given coroutines/futures.
 | 
					
						
							| 
									
										
										
										
											2013-10-17 13:40:50 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-09-30 08:17:15 -07:00
										 |  |  |     Coroutines will be wrapped in a future and scheduled in the event | 
					
						
							|  |  |  |     loop. They will not necessarily be scheduled in the same order as | 
					
						
							|  |  |  |     passed in. | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-10-17 13:40:50 -07:00
										 |  |  |     All futures must share the same event loop.  If all the tasks are | 
					
						
							|  |  |  |     done successfully, the returned future's result is the list of | 
					
						
							|  |  |  |     results (in the order of the original sequence, not necessarily | 
					
						
							| 
									
										
										
										
											2014-02-06 12:03:53 -05:00
										 |  |  |     the order of results arrival).  If *return_exceptions* is True, | 
					
						
							| 
									
										
										
										
											2013-10-17 13:40:50 -07:00
										 |  |  |     exceptions in the tasks are treated the same as successful | 
					
						
							|  |  |  |     results, and gathered in the result list; otherwise, the first | 
					
						
							|  |  |  |     raised exception will be immediately propagated to the returned | 
					
						
							|  |  |  |     future. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     Cancellation: if the outer Future is cancelled, all children (that | 
					
						
							|  |  |  |     have not completed yet) are also cancelled.  If any child is | 
					
						
							|  |  |  |     cancelled, this is treated as if it raised CancelledError -- | 
					
						
							|  |  |  |     the outer Future is *not* cancelled in this case.  (This is to | 
					
						
							|  |  |  |     prevent the cancellation of one child to cause other children to | 
					
						
							|  |  |  |     be cancelled.) | 
					
						
							| 
									
										
										
										
											2020-07-20 02:01:39 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  |     If *return_exceptions* is False, cancelling gather() after it | 
					
						
							|  |  |  |     has been marked done won't cancel any submitted awaitables. | 
					
						
							|  |  |  |     For instance, gather can be marked done after propagating an | 
					
						
							|  |  |  |     exception to the caller, therefore, calling ``gather.cancel()`` | 
					
						
							|  |  |  |     after catching an exception (raised by one of the awaitables) from | 
					
						
							|  |  |  |     gather won't cancel any other awaitables. | 
					
						
							| 
									
										
										
										
											2013-10-17 13:40:50 -07:00
										 |  |  |     """
 | 
					
						
							| 
									
										
										
										
											2021-08-18 19:47:55 +01:00
										 |  |  |     if loop is not None: | 
					
						
							|  |  |  |         warnings.warn("The loop argument is deprecated since Python 3.8, " | 
					
						
							|  |  |  |                       "and scheduled for removal in Python 3.10.", | 
					
						
							|  |  |  |                       DeprecationWarning, stacklevel=2) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-09-04 20:54:50 +03:00
										 |  |  |     return _gather(*coros_or_futures, loop=loop, return_exceptions=return_exceptions) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | def _gather(*coros_or_futures, loop=None, return_exceptions=False): | 
					
						
							| 
									
										
										
										
											2014-07-16 18:36:24 +02:00
										 |  |  |     if not coros_or_futures: | 
					
						
							| 
									
										
										
										
											2016-05-16 15:38:39 -04:00
										 |  |  |         if loop is None: | 
					
						
							|  |  |  |             loop = events.get_event_loop() | 
					
						
							|  |  |  |         outer = loop.create_future() | 
					
						
							| 
									
										
										
										
											2013-10-17 13:40:50 -07:00
										 |  |  |         outer.set_result([]) | 
					
						
							|  |  |  |         return outer | 
					
						
							| 
									
										
										
										
											2014-07-16 18:36:24 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-12-19 07:19:53 -05:00
										 |  |  |     def _done_callback(fut): | 
					
						
							| 
									
										
										
										
											2013-10-17 13:40:50 -07:00
										 |  |  |         nonlocal nfinished | 
					
						
							| 
									
										
										
										
											2017-12-19 07:19:53 -05:00
										 |  |  |         nfinished += 1 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-01-09 01:42:52 +01:00
										 |  |  |         if outer.done(): | 
					
						
							|  |  |  |             if not fut.cancelled(): | 
					
						
							| 
									
										
										
										
											2013-10-17 13:40:50 -07:00
										 |  |  |                 # Mark exception retrieved. | 
					
						
							|  |  |  |                 fut.exception() | 
					
						
							|  |  |  |             return | 
					
						
							| 
									
										
										
										
											2015-01-09 01:42:52 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-12-19 07:19:53 -05:00
										 |  |  |         if not return_exceptions: | 
					
						
							|  |  |  |             if fut.cancelled(): | 
					
						
							|  |  |  |                 # Check if 'fut' is cancelled first, as | 
					
						
							|  |  |  |                 # 'fut.exception()' will *raise* a CancelledError | 
					
						
							|  |  |  |                 # instead of returning it. | 
					
						
							| 
									
										
										
										
											2020-05-17 22:47:31 -07:00
										 |  |  |                 exc = fut._make_cancelled_error() | 
					
						
							| 
									
										
										
										
											2017-12-19 07:19:53 -05:00
										 |  |  |                 outer.set_exception(exc) | 
					
						
							| 
									
										
										
										
											2013-10-17 13:40:50 -07:00
										 |  |  |                 return | 
					
						
							| 
									
										
										
										
											2017-12-19 07:19:53 -05:00
										 |  |  |             else: | 
					
						
							|  |  |  |                 exc = fut.exception() | 
					
						
							|  |  |  |                 if exc is not None: | 
					
						
							|  |  |  |                     outer.set_exception(exc) | 
					
						
							|  |  |  |                     return | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         if nfinished == nfuts: | 
					
						
							|  |  |  |             # All futures are done; create a list of results | 
					
						
							|  |  |  |             # and set it to the 'outer' future. | 
					
						
							|  |  |  |             results = [] | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             for fut in children: | 
					
						
							|  |  |  |                 if fut.cancelled(): | 
					
						
							| 
									
										
										
										
											2020-05-17 22:47:31 -07:00
										 |  |  |                     # Check if 'fut' is cancelled first, as 'fut.exception()' | 
					
						
							|  |  |  |                     # will *raise* a CancelledError instead of returning it. | 
					
						
							|  |  |  |                     # Also, since we're adding the exception return value | 
					
						
							|  |  |  |                     # to 'results' instead of raising it, don't bother | 
					
						
							|  |  |  |                     # setting __context__.  This also lets us preserve | 
					
						
							|  |  |  |                     # calling '_make_cancelled_error()' at most once. | 
					
						
							| 
									
										
										
										
											2020-05-15 16:55:50 -07:00
										 |  |  |                     res = exceptions.CancelledError( | 
					
						
							|  |  |  |                         '' if fut._cancel_message is None else | 
					
						
							|  |  |  |                         fut._cancel_message) | 
					
						
							| 
									
										
										
										
											2017-12-19 07:19:53 -05:00
										 |  |  |                 else: | 
					
						
							|  |  |  |                     res = fut.exception() | 
					
						
							|  |  |  |                     if res is None: | 
					
						
							|  |  |  |                         res = fut.result() | 
					
						
							|  |  |  |                 results.append(res) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-05-29 17:20:02 -04:00
										 |  |  |             if outer._cancel_requested: | 
					
						
							|  |  |  |                 # If gather is being cancelled we must propagate the | 
					
						
							|  |  |  |                 # cancellation regardless of *return_exceptions* argument. | 
					
						
							|  |  |  |                 # See issue 32684. | 
					
						
							| 
									
										
										
										
											2020-05-17 22:47:31 -07:00
										 |  |  |                 exc = fut._make_cancelled_error() | 
					
						
							| 
									
										
										
										
											2020-05-15 16:55:50 -07:00
										 |  |  |                 outer.set_exception(exc) | 
					
						
							| 
									
										
										
										
											2018-05-29 17:20:02 -04:00
										 |  |  |             else: | 
					
						
							|  |  |  |                 outer.set_result(results) | 
					
						
							| 
									
										
										
										
											2013-10-17 13:40:50 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-12-19 07:19:53 -05:00
										 |  |  |     arg_to_fut = {} | 
					
						
							|  |  |  |     children = [] | 
					
						
							|  |  |  |     nfuts = 0 | 
					
						
							|  |  |  |     nfinished = 0 | 
					
						
							|  |  |  |     for arg in coros_or_futures: | 
					
						
							|  |  |  |         if arg not in arg_to_fut: | 
					
						
							|  |  |  |             fut = ensure_future(arg, loop=loop) | 
					
						
							|  |  |  |             if loop is None: | 
					
						
							| 
									
										
										
										
											2017-12-23 15:04:15 -05:00
										 |  |  |                 loop = futures._get_loop(fut) | 
					
						
							| 
									
										
										
										
											2017-12-19 07:19:53 -05:00
										 |  |  |             if fut is not arg: | 
					
						
							|  |  |  |                 # 'arg' was not a Future, therefore, 'fut' is a new | 
					
						
							|  |  |  |                 # Future created specifically for 'arg'.  Since the caller | 
					
						
							|  |  |  |                 # can't control it, disable the "destroy pending task" | 
					
						
							|  |  |  |                 # warning. | 
					
						
							|  |  |  |                 fut._log_destroy_pending = False | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             nfuts += 1 | 
					
						
							|  |  |  |             arg_to_fut[arg] = fut | 
					
						
							|  |  |  |             fut.add_done_callback(_done_callback) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         else: | 
					
						
							|  |  |  |             # There's a duplicate Future object in coros_or_futures. | 
					
						
							|  |  |  |             fut = arg_to_fut[arg] | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         children.append(fut) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     outer = _GatheringFuture(children, loop=loop) | 
					
						
							| 
									
										
										
										
											2013-10-17 13:40:50 -07:00
										 |  |  |     return outer | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | def shield(arg, *, loop=None): | 
					
						
							|  |  |  |     """Wait for a future, shielding it from cancellation.
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     The statement | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-12-09 00:23:48 +02:00
										 |  |  |         res = await shield(something()) | 
					
						
							| 
									
										
										
										
											2013-10-17 13:40:50 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  |     is exactly equivalent to the statement | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-12-09 00:23:48 +02:00
										 |  |  |         res = await something() | 
					
						
							| 
									
										
										
										
											2013-10-17 13:40:50 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  |     *except* that if the coroutine containing it is cancelled, the | 
					
						
							|  |  |  |     task running in something() is not cancelled.  From the POV of | 
					
						
							|  |  |  |     something(), the cancellation did not happen.  But its caller is | 
					
						
							|  |  |  |     still cancelled, so the yield-from expression still raises | 
					
						
							|  |  |  |     CancelledError.  Note: If something() is cancelled by other means | 
					
						
							|  |  |  |     this will still cancel shield(). | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     If you want to completely ignore cancellation (not recommended) | 
					
						
							|  |  |  |     you can combine shield() with a try/except clause, as follows: | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         try: | 
					
						
							| 
									
										
										
										
											2017-12-09 00:23:48 +02:00
										 |  |  |             res = await shield(something()) | 
					
						
							| 
									
										
										
										
											2013-10-17 13:40:50 -07:00
										 |  |  |         except CancelledError: | 
					
						
							|  |  |  |             res = None | 
					
						
							|  |  |  |     """
 | 
					
						
							| 
									
										
										
										
											2019-09-12 15:40:40 +03:00
										 |  |  |     if loop is not None: | 
					
						
							|  |  |  |         warnings.warn("The loop argument is deprecated since Python 3.8, " | 
					
						
							|  |  |  |                       "and scheduled for removal in Python 3.10.", | 
					
						
							|  |  |  |                       DeprecationWarning, stacklevel=2) | 
					
						
							| 
									
										
										
										
											2015-05-11 14:48:38 -04:00
										 |  |  |     inner = ensure_future(arg, loop=loop) | 
					
						
							| 
									
										
										
										
											2013-10-17 13:40:50 -07:00
										 |  |  |     if inner.done(): | 
					
						
							|  |  |  |         # Shortcut. | 
					
						
							|  |  |  |         return inner | 
					
						
							| 
									
										
										
										
											2017-12-23 15:04:15 -05:00
										 |  |  |     loop = futures._get_loop(inner) | 
					
						
							| 
									
										
										
										
											2016-05-16 15:38:39 -04:00
										 |  |  |     outer = loop.create_future() | 
					
						
							| 
									
										
										
										
											2013-10-17 13:40:50 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-05-07 20:58:24 +02:00
										 |  |  |     def _inner_done_callback(inner): | 
					
						
							| 
									
										
										
										
											2013-10-17 13:40:50 -07:00
										 |  |  |         if outer.cancelled(): | 
					
						
							| 
									
										
										
										
											2015-01-09 01:42:52 +01:00
										 |  |  |             if not inner.cancelled(): | 
					
						
							|  |  |  |                 # Mark inner's result as retrieved. | 
					
						
							|  |  |  |                 inner.exception() | 
					
						
							| 
									
										
										
										
											2013-10-17 13:40:50 -07:00
										 |  |  |             return | 
					
						
							| 
									
										
										
										
											2015-01-09 01:42:52 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-10-17 13:40:50 -07:00
										 |  |  |         if inner.cancelled(): | 
					
						
							|  |  |  |             outer.cancel() | 
					
						
							|  |  |  |         else: | 
					
						
							|  |  |  |             exc = inner.exception() | 
					
						
							|  |  |  |             if exc is not None: | 
					
						
							|  |  |  |                 outer.set_exception(exc) | 
					
						
							|  |  |  |             else: | 
					
						
							|  |  |  |                 outer.set_result(inner.result()) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-05-07 20:58:24 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |     def _outer_done_callback(outer): | 
					
						
							|  |  |  |         if not inner.done(): | 
					
						
							|  |  |  |             inner.remove_done_callback(_inner_done_callback) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     inner.add_done_callback(_inner_done_callback) | 
					
						
							|  |  |  |     outer.add_done_callback(_outer_done_callback) | 
					
						
							| 
									
										
										
										
											2013-10-17 13:40:50 -07:00
										 |  |  |     return outer | 
					
						
							| 
									
										
										
										
											2015-10-03 08:31:42 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | def run_coroutine_threadsafe(coro, loop): | 
					
						
							|  |  |  |     """Submit a coroutine object to a given event loop.
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     Return a concurrent.futures.Future to access the result. | 
					
						
							|  |  |  |     """
 | 
					
						
							|  |  |  |     if not coroutines.iscoroutine(coro): | 
					
						
							|  |  |  |         raise TypeError('A coroutine object is required') | 
					
						
							|  |  |  |     future = concurrent.futures.Future() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def callback(): | 
					
						
							| 
									
										
										
										
											2015-10-05 16:20:00 -07:00
										 |  |  |         try: | 
					
						
							|  |  |  |             futures._chain_future(ensure_future(coro, loop=loop), future) | 
					
						
							| 
									
										
										
										
											2019-05-27 14:45:12 +02:00
										 |  |  |         except (SystemExit, KeyboardInterrupt): | 
					
						
							|  |  |  |             raise | 
					
						
							|  |  |  |         except BaseException as exc: | 
					
						
							| 
									
										
										
										
											2015-10-05 16:20:00 -07:00
										 |  |  |             if future.set_running_or_notify_cancel(): | 
					
						
							|  |  |  |                 future.set_exception(exc) | 
					
						
							|  |  |  |             raise | 
					
						
							| 
									
										
										
										
											2015-10-03 08:31:42 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  |     loop.call_soon_threadsafe(callback) | 
					
						
							|  |  |  |     return future | 
					
						
							| 
									
										
										
										
											2017-12-16 21:58:38 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-12-23 15:04:15 -05:00
										 |  |  | # WeakSet containing all alive tasks. | 
					
						
							|  |  |  | _all_tasks = weakref.WeakSet() | 
					
						
							| 
									
										
										
										
											2017-12-16 21:58:38 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | # Dictionary containing tasks that are currently active in | 
					
						
							|  |  |  | # all running event loops.  {EventLoop: Task} | 
					
						
							|  |  |  | _current_tasks = {} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-12-23 15:04:15 -05:00
										 |  |  | def _register_task(task): | 
					
						
							|  |  |  |     """Register a new task in asyncio as executed by loop.""" | 
					
						
							|  |  |  |     _all_tasks.add(task) | 
					
						
							| 
									
										
										
										
											2017-12-16 21:58:38 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | def _enter_task(loop, task): | 
					
						
							|  |  |  |     current_task = _current_tasks.get(loop) | 
					
						
							|  |  |  |     if current_task is not None: | 
					
						
							|  |  |  |         raise RuntimeError(f"Cannot enter into task {task!r} while another " | 
					
						
							|  |  |  |                            f"task {current_task!r} is being executed.") | 
					
						
							|  |  |  |     _current_tasks[loop] = task | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | def _leave_task(loop, task): | 
					
						
							|  |  |  |     current_task = _current_tasks.get(loop) | 
					
						
							|  |  |  |     if current_task is not task: | 
					
						
							|  |  |  |         raise RuntimeError(f"Leaving task {task!r} does not match " | 
					
						
							|  |  |  |                            f"the current task {current_task!r}.") | 
					
						
							|  |  |  |     del _current_tasks[loop] | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-12-23 15:04:15 -05:00
										 |  |  | def _unregister_task(task): | 
					
						
							|  |  |  |     """Unregister a task.""" | 
					
						
							|  |  |  |     _all_tasks.discard(task) | 
					
						
							| 
									
										
										
										
											2017-12-16 21:58:38 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | _py_register_task = _register_task | 
					
						
							|  |  |  | _py_unregister_task = _unregister_task | 
					
						
							|  |  |  | _py_enter_task = _enter_task | 
					
						
							|  |  |  | _py_leave_task = _leave_task | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | try: | 
					
						
							|  |  |  |     from _asyncio import (_register_task, _unregister_task, | 
					
						
							|  |  |  |                           _enter_task, _leave_task, | 
					
						
							|  |  |  |                           _all_tasks, _current_tasks) | 
					
						
							|  |  |  | except ImportError: | 
					
						
							|  |  |  |     pass | 
					
						
							|  |  |  | else: | 
					
						
							|  |  |  |     _c_register_task = _register_task | 
					
						
							|  |  |  |     _c_unregister_task = _unregister_task | 
					
						
							|  |  |  |     _c_enter_task = _enter_task | 
					
						
							|  |  |  |     _c_leave_task = _leave_task |