1*cda5da8dSAndroid Build Coastguard Worker__all__ = ('Runner', 'run') 2*cda5da8dSAndroid Build Coastguard Worker 3*cda5da8dSAndroid Build Coastguard Workerimport contextvars 4*cda5da8dSAndroid Build Coastguard Workerimport enum 5*cda5da8dSAndroid Build Coastguard Workerimport functools 6*cda5da8dSAndroid Build Coastguard Workerimport threading 7*cda5da8dSAndroid Build Coastguard Workerimport signal 8*cda5da8dSAndroid Build Coastguard Workerimport sys 9*cda5da8dSAndroid Build Coastguard Workerfrom . import coroutines 10*cda5da8dSAndroid Build Coastguard Workerfrom . import events 11*cda5da8dSAndroid Build Coastguard Workerfrom . import exceptions 12*cda5da8dSAndroid Build Coastguard Workerfrom . import tasks 13*cda5da8dSAndroid Build Coastguard Worker 14*cda5da8dSAndroid Build Coastguard Worker 15*cda5da8dSAndroid Build Coastguard Workerclass _State(enum.Enum): 16*cda5da8dSAndroid Build Coastguard Worker CREATED = "created" 17*cda5da8dSAndroid Build Coastguard Worker INITIALIZED = "initialized" 18*cda5da8dSAndroid Build Coastguard Worker CLOSED = "closed" 19*cda5da8dSAndroid Build Coastguard Worker 20*cda5da8dSAndroid Build Coastguard Worker 21*cda5da8dSAndroid Build Coastguard Workerclass Runner: 22*cda5da8dSAndroid Build Coastguard Worker """A context manager that controls event loop life cycle. 23*cda5da8dSAndroid Build Coastguard Worker 24*cda5da8dSAndroid Build Coastguard Worker The context manager always creates a new event loop, 25*cda5da8dSAndroid Build Coastguard Worker allows to run async functions inside it, 26*cda5da8dSAndroid Build Coastguard Worker and properly finalizes the loop at the context manager exit. 27*cda5da8dSAndroid Build Coastguard Worker 28*cda5da8dSAndroid Build Coastguard Worker If debug is True, the event loop will be run in debug mode. 29*cda5da8dSAndroid Build Coastguard Worker If loop_factory is passed, it is used for new event loop creation. 30*cda5da8dSAndroid Build Coastguard Worker 31*cda5da8dSAndroid Build Coastguard Worker asyncio.run(main(), debug=True) 32*cda5da8dSAndroid Build Coastguard Worker 33*cda5da8dSAndroid Build Coastguard Worker is a shortcut for 34*cda5da8dSAndroid Build Coastguard Worker 35*cda5da8dSAndroid Build Coastguard Worker with asyncio.Runner(debug=True) as runner: 36*cda5da8dSAndroid Build Coastguard Worker runner.run(main()) 37*cda5da8dSAndroid Build Coastguard Worker 38*cda5da8dSAndroid Build Coastguard Worker The run() method can be called multiple times within the runner's context. 39*cda5da8dSAndroid Build Coastguard Worker 40*cda5da8dSAndroid Build Coastguard Worker This can be useful for interactive console (e.g. IPython), 41*cda5da8dSAndroid Build Coastguard Worker unittest runners, console tools, -- everywhere when async code 42*cda5da8dSAndroid Build Coastguard Worker is called from existing sync framework and where the preferred single 43*cda5da8dSAndroid Build Coastguard Worker asyncio.run() call doesn't work. 44*cda5da8dSAndroid Build Coastguard Worker 45*cda5da8dSAndroid Build Coastguard Worker """ 46*cda5da8dSAndroid Build Coastguard Worker 47*cda5da8dSAndroid Build Coastguard Worker # Note: the class is final, it is not intended for inheritance. 48*cda5da8dSAndroid Build Coastguard Worker 49*cda5da8dSAndroid Build Coastguard Worker def __init__(self, *, debug=None, loop_factory=None): 50*cda5da8dSAndroid Build Coastguard Worker self._state = _State.CREATED 51*cda5da8dSAndroid Build Coastguard Worker self._debug = debug 52*cda5da8dSAndroid Build Coastguard Worker self._loop_factory = loop_factory 53*cda5da8dSAndroid Build Coastguard Worker self._loop = None 54*cda5da8dSAndroid Build Coastguard Worker self._context = None 55*cda5da8dSAndroid Build Coastguard Worker self._interrupt_count = 0 56*cda5da8dSAndroid Build Coastguard Worker self._set_event_loop = False 57*cda5da8dSAndroid Build Coastguard Worker 58*cda5da8dSAndroid Build Coastguard Worker def __enter__(self): 59*cda5da8dSAndroid Build Coastguard Worker self._lazy_init() 60*cda5da8dSAndroid Build Coastguard Worker return self 61*cda5da8dSAndroid Build Coastguard Worker 62*cda5da8dSAndroid Build Coastguard Worker def __exit__(self, exc_type, exc_val, exc_tb): 63*cda5da8dSAndroid Build Coastguard Worker self.close() 64*cda5da8dSAndroid Build Coastguard Worker 65*cda5da8dSAndroid Build Coastguard Worker def close(self): 66*cda5da8dSAndroid Build Coastguard Worker """Shutdown and close event loop.""" 67*cda5da8dSAndroid Build Coastguard Worker if self._state is not _State.INITIALIZED: 68*cda5da8dSAndroid Build Coastguard Worker return 69*cda5da8dSAndroid Build Coastguard Worker try: 70*cda5da8dSAndroid Build Coastguard Worker loop = self._loop 71*cda5da8dSAndroid Build Coastguard Worker _cancel_all_tasks(loop) 72*cda5da8dSAndroid Build Coastguard Worker loop.run_until_complete(loop.shutdown_asyncgens()) 73*cda5da8dSAndroid Build Coastguard Worker loop.run_until_complete(loop.shutdown_default_executor()) 74*cda5da8dSAndroid Build Coastguard Worker finally: 75*cda5da8dSAndroid Build Coastguard Worker if self._set_event_loop: 76*cda5da8dSAndroid Build Coastguard Worker events.set_event_loop(None) 77*cda5da8dSAndroid Build Coastguard Worker loop.close() 78*cda5da8dSAndroid Build Coastguard Worker self._loop = None 79*cda5da8dSAndroid Build Coastguard Worker self._state = _State.CLOSED 80*cda5da8dSAndroid Build Coastguard Worker 81*cda5da8dSAndroid Build Coastguard Worker def get_loop(self): 82*cda5da8dSAndroid Build Coastguard Worker """Return embedded event loop.""" 83*cda5da8dSAndroid Build Coastguard Worker self._lazy_init() 84*cda5da8dSAndroid Build Coastguard Worker return self._loop 85*cda5da8dSAndroid Build Coastguard Worker 86*cda5da8dSAndroid Build Coastguard Worker def run(self, coro, *, context=None): 87*cda5da8dSAndroid Build Coastguard Worker """Run a coroutine inside the embedded event loop.""" 88*cda5da8dSAndroid Build Coastguard Worker if not coroutines.iscoroutine(coro): 89*cda5da8dSAndroid Build Coastguard Worker raise ValueError("a coroutine was expected, got {!r}".format(coro)) 90*cda5da8dSAndroid Build Coastguard Worker 91*cda5da8dSAndroid Build Coastguard Worker if events._get_running_loop() is not None: 92*cda5da8dSAndroid Build Coastguard Worker # fail fast with short traceback 93*cda5da8dSAndroid Build Coastguard Worker raise RuntimeError( 94*cda5da8dSAndroid Build Coastguard Worker "Runner.run() cannot be called from a running event loop") 95*cda5da8dSAndroid Build Coastguard Worker 96*cda5da8dSAndroid Build Coastguard Worker self._lazy_init() 97*cda5da8dSAndroid Build Coastguard Worker 98*cda5da8dSAndroid Build Coastguard Worker if context is None: 99*cda5da8dSAndroid Build Coastguard Worker context = self._context 100*cda5da8dSAndroid Build Coastguard Worker task = self._loop.create_task(coro, context=context) 101*cda5da8dSAndroid Build Coastguard Worker 102*cda5da8dSAndroid Build Coastguard Worker if (threading.current_thread() is threading.main_thread() 103*cda5da8dSAndroid Build Coastguard Worker and signal.getsignal(signal.SIGINT) is signal.default_int_handler 104*cda5da8dSAndroid Build Coastguard Worker ): 105*cda5da8dSAndroid Build Coastguard Worker sigint_handler = functools.partial(self._on_sigint, main_task=task) 106*cda5da8dSAndroid Build Coastguard Worker try: 107*cda5da8dSAndroid Build Coastguard Worker signal.signal(signal.SIGINT, sigint_handler) 108*cda5da8dSAndroid Build Coastguard Worker except ValueError: 109*cda5da8dSAndroid Build Coastguard Worker # `signal.signal` may throw if `threading.main_thread` does 110*cda5da8dSAndroid Build Coastguard Worker # not support signals (e.g. embedded interpreter with signals 111*cda5da8dSAndroid Build Coastguard Worker # not registered - see gh-91880) 112*cda5da8dSAndroid Build Coastguard Worker sigint_handler = None 113*cda5da8dSAndroid Build Coastguard Worker else: 114*cda5da8dSAndroid Build Coastguard Worker sigint_handler = None 115*cda5da8dSAndroid Build Coastguard Worker 116*cda5da8dSAndroid Build Coastguard Worker self._interrupt_count = 0 117*cda5da8dSAndroid Build Coastguard Worker try: 118*cda5da8dSAndroid Build Coastguard Worker return self._loop.run_until_complete(task) 119*cda5da8dSAndroid Build Coastguard Worker except exceptions.CancelledError: 120*cda5da8dSAndroid Build Coastguard Worker if self._interrupt_count > 0: 121*cda5da8dSAndroid Build Coastguard Worker uncancel = getattr(task, "uncancel", None) 122*cda5da8dSAndroid Build Coastguard Worker if uncancel is not None and uncancel() == 0: 123*cda5da8dSAndroid Build Coastguard Worker raise KeyboardInterrupt() 124*cda5da8dSAndroid Build Coastguard Worker raise # CancelledError 125*cda5da8dSAndroid Build Coastguard Worker finally: 126*cda5da8dSAndroid Build Coastguard Worker if (sigint_handler is not None 127*cda5da8dSAndroid Build Coastguard Worker and signal.getsignal(signal.SIGINT) is sigint_handler 128*cda5da8dSAndroid Build Coastguard Worker ): 129*cda5da8dSAndroid Build Coastguard Worker signal.signal(signal.SIGINT, signal.default_int_handler) 130*cda5da8dSAndroid Build Coastguard Worker 131*cda5da8dSAndroid Build Coastguard Worker def _lazy_init(self): 132*cda5da8dSAndroid Build Coastguard Worker if self._state is _State.CLOSED: 133*cda5da8dSAndroid Build Coastguard Worker raise RuntimeError("Runner is closed") 134*cda5da8dSAndroid Build Coastguard Worker if self._state is _State.INITIALIZED: 135*cda5da8dSAndroid Build Coastguard Worker return 136*cda5da8dSAndroid Build Coastguard Worker if self._loop_factory is None: 137*cda5da8dSAndroid Build Coastguard Worker self._loop = events.new_event_loop() 138*cda5da8dSAndroid Build Coastguard Worker if not self._set_event_loop: 139*cda5da8dSAndroid Build Coastguard Worker # Call set_event_loop only once to avoid calling 140*cda5da8dSAndroid Build Coastguard Worker # attach_loop multiple times on child watchers 141*cda5da8dSAndroid Build Coastguard Worker events.set_event_loop(self._loop) 142*cda5da8dSAndroid Build Coastguard Worker self._set_event_loop = True 143*cda5da8dSAndroid Build Coastguard Worker else: 144*cda5da8dSAndroid Build Coastguard Worker self._loop = self._loop_factory() 145*cda5da8dSAndroid Build Coastguard Worker if self._debug is not None: 146*cda5da8dSAndroid Build Coastguard Worker self._loop.set_debug(self._debug) 147*cda5da8dSAndroid Build Coastguard Worker self._context = contextvars.copy_context() 148*cda5da8dSAndroid Build Coastguard Worker self._state = _State.INITIALIZED 149*cda5da8dSAndroid Build Coastguard Worker 150*cda5da8dSAndroid Build Coastguard Worker def _on_sigint(self, signum, frame, main_task): 151*cda5da8dSAndroid Build Coastguard Worker self._interrupt_count += 1 152*cda5da8dSAndroid Build Coastguard Worker if self._interrupt_count == 1 and not main_task.done(): 153*cda5da8dSAndroid Build Coastguard Worker main_task.cancel() 154*cda5da8dSAndroid Build Coastguard Worker # wakeup loop if it is blocked by select() with long timeout 155*cda5da8dSAndroid Build Coastguard Worker self._loop.call_soon_threadsafe(lambda: None) 156*cda5da8dSAndroid Build Coastguard Worker return 157*cda5da8dSAndroid Build Coastguard Worker raise KeyboardInterrupt() 158*cda5da8dSAndroid Build Coastguard Worker 159*cda5da8dSAndroid Build Coastguard Worker 160*cda5da8dSAndroid Build Coastguard Workerdef run(main, *, debug=None): 161*cda5da8dSAndroid Build Coastguard Worker """Execute the coroutine and return the result. 162*cda5da8dSAndroid Build Coastguard Worker 163*cda5da8dSAndroid Build Coastguard Worker This function runs the passed coroutine, taking care of 164*cda5da8dSAndroid Build Coastguard Worker managing the asyncio event loop and finalizing asynchronous 165*cda5da8dSAndroid Build Coastguard Worker generators. 166*cda5da8dSAndroid Build Coastguard Worker 167*cda5da8dSAndroid Build Coastguard Worker This function cannot be called when another asyncio event loop is 168*cda5da8dSAndroid Build Coastguard Worker running in the same thread. 169*cda5da8dSAndroid Build Coastguard Worker 170*cda5da8dSAndroid Build Coastguard Worker If debug is True, the event loop will be run in debug mode. 171*cda5da8dSAndroid Build Coastguard Worker 172*cda5da8dSAndroid Build Coastguard Worker This function always creates a new event loop and closes it at the end. 173*cda5da8dSAndroid Build Coastguard Worker It should be used as a main entry point for asyncio programs, and should 174*cda5da8dSAndroid Build Coastguard Worker ideally only be called once. 175*cda5da8dSAndroid Build Coastguard Worker 176*cda5da8dSAndroid Build Coastguard Worker Example: 177*cda5da8dSAndroid Build Coastguard Worker 178*cda5da8dSAndroid Build Coastguard Worker async def main(): 179*cda5da8dSAndroid Build Coastguard Worker await asyncio.sleep(1) 180*cda5da8dSAndroid Build Coastguard Worker print('hello') 181*cda5da8dSAndroid Build Coastguard Worker 182*cda5da8dSAndroid Build Coastguard Worker asyncio.run(main()) 183*cda5da8dSAndroid Build Coastguard Worker """ 184*cda5da8dSAndroid Build Coastguard Worker if events._get_running_loop() is not None: 185*cda5da8dSAndroid Build Coastguard Worker # fail fast with short traceback 186*cda5da8dSAndroid Build Coastguard Worker raise RuntimeError( 187*cda5da8dSAndroid Build Coastguard Worker "asyncio.run() cannot be called from a running event loop") 188*cda5da8dSAndroid Build Coastguard Worker 189*cda5da8dSAndroid Build Coastguard Worker with Runner(debug=debug) as runner: 190*cda5da8dSAndroid Build Coastguard Worker return runner.run(main) 191*cda5da8dSAndroid Build Coastguard Worker 192*cda5da8dSAndroid Build Coastguard Worker 193*cda5da8dSAndroid Build Coastguard Workerdef _cancel_all_tasks(loop): 194*cda5da8dSAndroid Build Coastguard Worker to_cancel = tasks.all_tasks(loop) 195*cda5da8dSAndroid Build Coastguard Worker if not to_cancel: 196*cda5da8dSAndroid Build Coastguard Worker return 197*cda5da8dSAndroid Build Coastguard Worker 198*cda5da8dSAndroid Build Coastguard Worker for task in to_cancel: 199*cda5da8dSAndroid Build Coastguard Worker task.cancel() 200*cda5da8dSAndroid Build Coastguard Worker 201*cda5da8dSAndroid Build Coastguard Worker loop.run_until_complete(tasks.gather(*to_cancel, return_exceptions=True)) 202*cda5da8dSAndroid Build Coastguard Worker 203*cda5da8dSAndroid Build Coastguard Worker for task in to_cancel: 204*cda5da8dSAndroid Build Coastguard Worker if task.cancelled(): 205*cda5da8dSAndroid Build Coastguard Worker continue 206*cda5da8dSAndroid Build Coastguard Worker if task.exception() is not None: 207*cda5da8dSAndroid Build Coastguard Worker loop.call_exception_handler({ 208*cda5da8dSAndroid Build Coastguard Worker 'message': 'unhandled exception during asyncio.run() shutdown', 209*cda5da8dSAndroid Build Coastguard Worker 'exception': task.exception(), 210*cda5da8dSAndroid Build Coastguard Worker 'task': task, 211*cda5da8dSAndroid Build Coastguard Worker }) 212