def test_run_once_in_executor_handle(self): def cb(): pass self.assertRaises(AssertionError, self.loop.run_in_executor, None, asyncio.Handle(cb, (), self.loop), ('', )) self.assertRaises(AssertionError, self.loop.run_in_executor, None, asyncio.TimerHandle(10, cb, (), self.loop))
def _call_soon(self, callback, args): if (asyncio.iscoroutine(callback) or asyncio.iscoroutinefunction(callback)): raise TypeError("coroutines cannot be used with call_soon()") self._check_closed() handle = asyncio.Handle(callback, args, self) if handle._source_traceback: del handle._source_traceback[-1] self._ready.append(handle) return handle
def call_soon_threadsafe(self, callback, *args): handler = asyncio.Handle(callback, args, self) # We don't use _add_callback here because starting the Idle handle # is not threadsafe. Instead, we queue the callback and in the Async # handle callback (which is run in the loop thread) we start the # Idle handle if needed self._ready.append(handler) self._waker.send() return handler
def call_soon_threadsafe(self, callback, *args, **context): """asyncio's thread-safe defer-to-mainloop Note that the callback is a sync function. """ self._check_callback(callback, 'call_soon_threadsafe') self._check_closed() h = asyncio.Handle(callback, args, self, **context) self._token.run_sync_soon(self._q_send.send_nowait, h)
def test_run_once_in_executor_cancelled(self): def cb(): pass h = asyncio.Handle(cb, (), self.loop) h.cancel() f = self.loop.run_in_executor(None, h) self.assertIsInstance(f, asyncio.Future) self.assertTrue(f.done()) self.assertIsNone(f.result())
def call_later(self, delay, callback, *args, context=None): """Register callback to be invoked after a certain delay.""" if asyncio.iscoroutinefunction(callback): raise TypeError("coroutines cannot be used with call_later") if not callable(callback): raise TypeError( "callback must be callable: {}".format(type(callback).__name__) ) self.__log_debug( "Registering callback %s to be invoked with arguments %s after %s second(s)", callback, args, delay ) if sys.version_info >= (3, 7): return self._add_callback( asyncio.Handle(callback, args, self, context=context), delay ) return self._add_callback(asyncio.Handle(callback, args, self), delay)
def add_writer(self, fd, callback, *args): handler = asyncio.Handle(callback, args, self) try: poll_h = self._fd_map[fd] except KeyError: poll_h = self._create_poll_handle(fd) self._fd_map[fd] = poll_h poll_h.pevents |= pyuv.UV_WRITABLE poll_h.write_handler = handler poll_h.start(poll_h.pevents, self._poll_cb)
def _sync_await(awaitable: Awaitable[Any]) -> Any: """ _sync_await waits for the given future to complete by effectively yielding the current task and pumping the event loop. """ # Fetch the current event loop and ensure a future. loop = None try: loop = asyncio.get_event_loop() except RuntimeError: pass if loop is None: loop = asyncio.new_event_loop() asyncio.set_event_loop(loop) fut = asyncio.ensure_future(awaitable) # If the loop is not running, we can just use run_until_complete. Without this, we would need to duplicate a fair # amount of bookkeeping logic around loop startup and shutdown. if not loop.is_running(): return loop.run_until_complete(fut) # If we are executing inside a task, pretend we've returned from its current callback--effectively yielding to # the event loop--by calling _leave_task. task = _get_current_task(loop) if task is not None: _leave_task(loop, task) # Pump the event loop until the future is complete. This is the kernel of BaseEventLoop.run_forever, and may not # work with alternative event loop implementations. # # In order to make this reentrant with respect to _run_once, we keep track of the number of event handles on the # ready list and ensure that there are exactly that many handles on the list once we are finished. # # See https://github.com/python/cpython/blob/3.6/Lib/asyncio/base_events.py#L1428-L1452 for the details of the # _run_once kernel with which we need to cooperate. ntodo = len(loop._ready) # type: ignore while not fut.done() and not fut.cancelled(): loop._run_once() # type: ignore if loop._stopping: # type: ignore break # If we drained the ready list past what a calling _run_once would have expected, fix things up by pushing # cancelled handles onto the list. while len(loop._ready) < ntodo: # type: ignore handle = asyncio.Handle(lambda: None, [], loop) handle._cancelled = True loop._ready.append(handle) # type: ignore # If we were executing inside a task, restore its context and continue on. if task is not None: _enter_task(loop, task) # Return the result of the future. return fut.result()
def _call_asap( loop: Any, callback: Callable, *args: Any, context: Any = None ) -> asyncio.Handle: loop._check_closed() if loop._debug: loop._check_callback(callback, "call_soon_threadsafe") loop._call_soon(callback, args, context) if context is not None: handle = asyncio.Handle(callback, list(args), loop, context) # type: ignore else: handle = asyncio.Handle(callback, list(args), loop) if handle._source_traceback: # type: ignore del handle._source_traceback[-1] # type: ignore loop._ready.insert(0, handle) if handle._source_traceback: # type: ignore del handle._source_traceback[-1] # type: ignore loop._write_to_self() return handle
def add_signal_handler(self, sig, callback, *args): """asyncio's method to add a signal handler. """ self._check_closed() self._check_signal(sig) if sig == signal.SIGKILL: raise RuntimeError("SIGKILL cannot be caught") h = asyncio.Handle(callback, args, self) assert sig not in self._signal_handlers, \ "Signal %d is already being caught" % (sig,) self._orig_signals[sig] = signal.signal(sig, self._handle_sig) self._signal_handlers[sig] = h
def _call_asap(loop: Any, callback: Callable, *args: Any, context: Any = None) -> asyncio.Handle: loop._check_closed() if loop._debug: loop._check_callback(callback, 'call_soon_threadsafe') handle = loop._call_soon(callback, args, context) if context is not None: handle = asyncio.Handle(callback, args, loop, context) else: handle = asyncio.Handle(callback, args, loop) if handle._source_traceback: del handle._source_traceback[-1] loop._ready.insert(0, handle) if handle._source_traceback: del handle._source_traceback[-1] loop._write_to_self() return handle
def call_later(self, delay, callback, *args): """Register callback to be invoked after a certain delay.""" if asyncio.iscoroutinefunction(callback): raise TypeError("coroutines cannot be used with call_later") if not callable(callback): raise TypeError('callback must be callable: {}'.format( type(callback).__name__)) self._logger.debug( 'Registering callback {} to be invoked with arguments {} after {} second(s)' .format(callback, args, delay)) return self._add_callback(asyncio.Handle(callback, args, self), delay)
def add_signal_handler(self, sig, callback, *args): self._validate_signal(sig) signal_h = pyuv.Signal(self._loop) handler = asyncio.Handle(callback, args, self) signal_h.handler = handler try: signal_h.start(self._signal_cb, sig) except Exception as e: signal_h.close() raise RuntimeError(str(e)) else: self._signal_handlers[sig] = signal_h return handler
async def synchronize(self): """Suspend execution until all callbacks previously scheduled using ``call_soon()`` have been processed. This is a Trio-flavored async function. From asyncio, call ``await loop.run_trio(loop.synchronize)`` instead of ``await asyncio.sleep(0)`` if you need to process all queued callbacks. """ w = trio.Event() self._queue_handle(asyncio.Handle(w.set, (), self)) await w.wait()
def add_signal_handler(self, sig: int, callback: typing.Callable[..., typing.Any], *args: typing.Any): """Add a handler for a signal. UNIX only. Raise ValueError if the signal number is invalid or uncatchable. Raise RuntimeError if there is a problem setting up the handler. """ if asyncio.iscoroutine(callback) or asyncio.iscoroutinefunction( callback): raise TypeError("coroutines cannot be used " "with add_signal_handler()") if not callable(callback): raise TypeError(f"{callback!r} is not callable") self._check_signal(sig) self._check_closed() try: # set_wakeup_fd() raises ValueError if this is not the # main thread. By calling it early we ensure that an # event loop running in another thread cannot add a signal # handler. signal.set_wakeup_fd(self._csock.fileno()) except (ValueError, OSError) as exc: raise RuntimeError(str(exc)) handle = asyncio.Handle(callback, args, self, None) self._signal_handlers[sig] = handle try: # Register a dummy signal handler to ask Python to write the signal # number in the wakup file descriptor. _process_self_data() will # read signal numbers from this file descriptor to handle signals. signal.signal(sig, _sighandler_noop) # Set SA_RESTART to limit EINTR occurrences. signal.siginterrupt(sig, False) except OSError as exc: del self._signal_handlers[sig] if not self._signal_handlers: try: signal.set_wakeup_fd(-1) except (ValueError, OSError) as nexc: logger.info("set_wakeup_fd(-1) failed: %s", nexc) if exc.errno == errno.EINVAL: raise RuntimeError(f"sig {sig} cannot be caught") else: # pragma: nocover raise
def __init__(self, name, tag, *args, prefix=None, untagged_resp_name=None, loop=asyncio.get_event_loop(), timeout=None): self.name = name self.tag = tag self.args = args self.prefix = prefix + ' ' if prefix else None self.untagged_resp_name = untagged_resp_name or name self.response = None self._exception = None self._event = asyncio.Event(loop=loop) self._loop = loop self._timeout = timeout self._timer = asyncio.Handle(lambda: None, None, loop) # fake timer self._set_timer() self._literal_data = None self._expected_size = 0
def call_soon(self, callback, *args, context=None): def doit(hdl): if not hdl._cancelled: hdl._run() #end if return \ False # always one-shot #end doit #begin call_soon self._check_closed() hdl = asyncio.Handle(callback, args, self) GLib.idle_add(doit, hdl) self = None # avoid circular references return \ hdl
def test_run_once_in_executor_plain(self): def cb(): pass h = asyncio.Handle(cb, (), self.loop) f = asyncio.Future(loop=self.loop) executor = mock.Mock() executor.submit.return_value = f self.loop.set_default_executor(executor) res = self.loop.run_in_executor(None, h) self.assertIs(f, res) executor = mock.Mock() executor.submit.return_value = f res = self.loop.run_in_executor(executor, h) self.assertIs(f, res) self.assertTrue(executor.submit.called) f.cancel() # Don't complain about abandoned Future.
def stop(self): """Halt the main loop. Any callbacks queued before this point are processed before stopping. """ def do_stop(): self._stop_pending = False raise StopAsyncIteration # async def stop_me(): # def kick_(): # raise StopAsyncIteration # self._queue_handle(asyncio.Handle(kick_, (), self)) # await self._main_loop() # if threading.current_thread() != self._thread: # self.__run_in_thread(stop_me) # else: if self._thread_running and not self._stop_pending: self._stop_pending = True self._queue_handle(asyncio.Handle(do_stop, (), self))
def _patch_loop(loop): """ Patch loop to make it reentrent. """ def run_until_complete(self, future): if self.is_running(): self._check_closed() f = asyncio.ensure_future(future) if f is not future: f._log_destroy_pending = False while not f.done(): run_once(self) return f.result() else: return self._run_until_complete_orig(future) bogus_handle = asyncio.Handle(None, None, loop) bogus_handle.cancel() def run_once(self): ready = self._ready scheduled = self._scheduled # remove bogus handles to get more efficient timeout while ready and ready[0] is bogus_handle: ready.popleft() nready = len(ready) while scheduled and scheduled[0]._cancelled: self._timer_cancelled_count -= 1 handle = heapq.heappop(scheduled) handle._scheduled = False timeout = None if ready or self._stopping: timeout = 0 elif scheduled: when = scheduled[0]._when timeout = max(0, when - self.time()) event_list = self._selector.select(timeout) self._process_events(event_list) end_time = self.time() + self._clock_resolution while scheduled: handle = scheduled[0] if handle._when >= end_time: break handle = heapq.heappop(scheduled) handle._scheduled = False ready.append(handle) self._nesting_level += 1 ntodo = len(ready) for _ in range(ntodo): if not ready: break handle = ready.popleft() if handle._cancelled: continue handle._run() handle = None self._nesting_level -= 1 # add bogus handles to keep loop._run_once happy if nready and self._nesting_level == 0: ready.extendleft([bogus_handle] * nready) cls = loop.__class__ cls._run_until_complete_orig = cls.run_until_complete cls.run_until_complete = run_until_complete cls._nesting_level = 0
def test__add_callback_handle(self): h = asyncio.Handle(lambda: False, (), self.loop) self.loop._add_callback(h) self.assertFalse(self.loop._scheduled) self.assertIn(h, self.loop._ready)
def runTest(self): # add a dummy event handle = asyncio.Handle(lambda: None, (), self.loop) key = selectors.SelectorKey(1, 1, selectors.EVENT_READ, (handle, None)) mock.get_map.return_value = {1: key}
def call_soon(self, callback, *args, context=None): handle = asyncio.Handle(callback, args, self, context=context) window.setTimeout(handle._run) return handle
def call_soon(func, *args): func(*args) return asyncio.Handle(func, args, None)
def call_later(i, func, *args): if func.__name__ in dir(IrcBot): func(*args) return asyncio.Handle(func, args, None)
def call_soon(self, callback, *args, context=None): h = asyncio.Handle(callback, args, self) self._immediate.append(h) return h
def call_later(self, when, callback, *args, context=None): handle = asyncio.Handle(callback, args, self, context=context) window.setTimeout(handle._run, when * 1000.0) return handle
def _patch_loop(loop): """ Patch loop to make it reentrent. """ def run_forever_35_36(self): # from Python 3.5/3.6 asyncio.base_events self._check_closed() old_thread_id = self._thread_id old_running_loop = events._get_running_loop() self._set_coroutine_wrapper(self._debug) self._thread_id = threading.get_ident() if self._asyncgens is not None: old_agen_hooks = sys.get_asyncgen_hooks() sys.set_asyncgen_hooks(firstiter=self._asyncgen_firstiter_hook, finalizer=self._asyncgen_finalizer_hook) try: events._set_running_loop(self) while True: self._run_once() if self._stopping: break finally: self._stopping = False self._thread_id = old_thread_id events._set_running_loop(old_running_loop) self._set_coroutine_wrapper(False) if self._asyncgens is not None: sys.set_asyncgen_hooks(*old_agen_hooks) def run_forever_37(self): # from Python 3.7 asyncio.base_events self._check_closed() old_thread_id = self._thread_id old_running_loop = events._get_running_loop() self._set_coroutine_origin_tracking(self._debug) self._thread_id = threading.get_ident() old_agen_hooks = sys.get_asyncgen_hooks() sys.set_asyncgen_hooks(firstiter=self._asyncgen_firstiter_hook, finalizer=self._asyncgen_finalizer_hook) try: events._set_running_loop(self) while True: self._run_once() if self._stopping: break finally: self._stopping = False self._thread_id = old_thread_id events._set_running_loop(old_running_loop) self._set_coroutine_origin_tracking(False) sys.set_asyncgen_hooks(*old_agen_hooks) bogus_handle = asyncio.Handle(None, None, loop) bogus_handle.cancel() def run_once(self): ready = self._ready scheduled = self._scheduled # remove bogus handles to get more efficient timeout while ready and ready[0] is bogus_handle: ready.popleft() nready = len(ready) while scheduled and scheduled[0]._cancelled: self._timer_cancelled_count -= 1 handle = heapq.heappop(scheduled) handle._scheduled = False timeout = None if ready or self._stopping: timeout = 0 elif scheduled: when = scheduled[0]._when timeout = max(0, when - self.time()) event_list = self._selector.select(timeout) self._process_events(event_list) end_time = self.time() + self._clock_resolution while scheduled: handle = scheduled[0] if handle._when >= end_time: break handle = heapq.heappop(scheduled) handle._scheduled = False ready.append(handle) self._nesting_level += 1 ntodo = len(ready) for _ in range(ntodo): if not ready: break handle = ready.popleft() if handle._cancelled: continue handle._run() handle = None self._nesting_level -= 1 if nready and self._nesting_level == 0: # When the loop was patched while it was already running, # there is an unpatched loop._run_once enclosing us. # It expects to process 'nready' handles and will crash # if there are less. # So here we feed it 'nready' bogus handles. ready.extendleft([bogus_handle] * nready) cls = loop.__class__ cls._run_forever_orig = cls.run_forever if sys.version_info >= (3, 7, 0): cls.run_forever = run_forever_37 else: cls.run_forever = run_forever_35_36 cls._nesting_level = 0
def call_soon(self, callback, *args, context=None): """Call the given callback as soon as possible.""" h = asyncio.Handle(callback, args, self, context=context) self._todo.append(h) return h
def call_soon(self, callback, *args): handler = asyncio.Handle(callback, args, self) self._ready.append(handler) return handler