Pyjo.Reactor.Asyncio - Low-level event reactor on asyncio¶
from Pyjo.Reactor.Asyncio import Asyncio
# Watch if handle becomes readable or writable
reactor = Asyncio()
reactor.io(first, lambda reactor, writable: print(
'First handle is writable' if writable else 'First handle is readable'
))
# Or import the module, as in Google Python Style Guide
import Pyjo.Reactor.Asyncio
# Watch if handle becomes readable or writable
reactor = Pyjo.Reactor.Asyncio.new()
reactor.io(first, lambda reactor, writable: print(
'First handle is writable' if writable else 'First handle is readable'
))
# Change to watching only if handle becomes writable
reactor.watch(first, False, True)
# Watch a file descriptor if it becomes readable
reactor.io(fd, lambda reactor, writable: print(
'Second handle is writable' if writable else 'Second handle is readable'
)).watch(fd, True, False)
# Add a timer
def timeout(reactor):
reactor.remove(first)
reactor.remove(fd)
print('Timeout!')
reactor.timer(15, timeout)
# Start reactor if necessary
if not reactor.is_running:
reactor.start()
Pyjo.Reactor.Asyncio is a low-level event reactor based on
asyncio. It watches handles and runs timers in an asyncio event loop,
so coroutines, tasks and other users of the loop run along with it.
A reactor uses the event loop it is given, or else the loop that is running,
as inside asyncio.run(), or a new event loop of its own, and moves its
handles and timers to the loop that is running. In a loop that runs
already, is_running is true, start() and one_tick()
return right away, as asyncio does not nest event loops, and the handles and
timers are watched by that loop.
The loop watches handles with add_reader and add_writer, which the
default event loop of asyncio on Windows, asyncio.ProactorEventLoop,
does not have. There, a loop of its own is an
asyncio.SelectorEventLoop, and a loop that the reactor is given or
that is running has to be one too, as of
asyncio.run(main(), loop_factory=asyncio.SelectorEventLoop). The
asyncio.SelectorEventLoop of Windows watches sockets only, not pipes
or files, up to 512 of them, with select.select().
In the debug mode of asyncio, as of PYTHONDEVMODE, a loop logs a callback
that takes longer than its slow_callback_duration, 0.1 seconds. The
PYJO_ASYNCIO_SLOW_CALLBACK environment variable sets it in seconds for a
loop that the reactor makes, as on a machine that is busy, where TLS and an
application of a server take longer.
PYTHONDEVMODE=1 PYJO_ASYNCIO_SLOW_CALLBACK=5 python myapp.py daemon
The end of a with block closes the event loop that the reactor made.
See also¶
Pyjoyment, https://mojolicious.org.
Ported from Mojo::Reactor::Poll of Mojolicious 9.49.
- class Pyjo.Reactor.Asyncio.Pyjo_Reactor_Asyncio(loop: AbstractEventLoop | None = None)[source]¶
Pyjo.Reactor.Asyncioinherits all methods fromPyjo.Reactorand implements the following new ones.- again(id: str, after: float | None = None) None[source]¶
reactor.again(id) reactor.again(id, 0.5)
Restart timer and optionally change the invocation time. Note that this method requires an active timer.
- awaitable(promise: Any) Awaitable[None][source]¶
future = reactor.awaitable(promise)
Return a future of
loopthat is done when the promise is fulfilled or rejected, so thatawait promiseworks in any task of the loop.
- close() None[source]¶
reactor.close()
Close the event loop that the reactor made for itself, as the end of a
withblock does, after the tasks that are left in it are cancelled and have ended, asasyncio.run()does, and as the end of the program does; an event loop that it was given, or that was running, is left to its owner.
- event_loop_modules = ('asyncio', 'selectors')¶
modules = reactor.event_loop_modules
The names of the modules of the event loop that the reactor runs on, as
('trio',), whose code a finalizer of the garbage collector may interrupt. The cleanup of a stream, a server or a user agent that is freed in a frame of such a module, or of a module of a class of the reactor, waits for the next tick of the reactor, so that it does not change the structures that the event loop is changing. Defaults to().class Pyjo_Reactor_MyLoop(Pyjo.Reactor.Poll.object): event_loop_modules = ('myloop',)
- future() Future[Any][source]¶
future = reactor.future()
Return a new
asyncio.Futureofloop, whichPyjo.Promise.Pyjo_Promise.to_future()turns a promise into.
- io(handle: Handle, cb: IOCallback) Self[source]¶
reactor = reactor.io(handle, cb)
Watch handle for I/O events, invoking the callback whenever handle becomes readable or writable.
# Callback will be executed twice if handle becomes readable and writable reactor.io(handle, lambda reactor, writable: print('Handle is writable' if writable else 'Handle is readable'))
- property is_running: bool[source]¶
boolean = reactor.is_running
Check if reactor is running, also in a loop that runs already, as inside
asyncio.run(), which watches its handles and timers.
- property loop: AbstractEventLoop[source]¶
loop = reactor.loop
The
asyncioevent loop of the reactor: the one it is given, or else the loop that is running, as insideasyncio.run(), or a loop of its own. A reactor that is not given a loop follows the loop that is running, and moves its handles and timers to it, unless it has tasks in another loop, which cannot move. In the child of a fork, it moves to a new loop of its own, as the selector of the loop of the parent is not the child’s.
- next_tick(cb: TimerCallback) None[source]¶
none = reactor.next_tick(cb)
Execute callback as soon as possible, but not before returning or other callbacks that have been registered with this method, always returns
None.
- one_tick() None[source]¶
reactor.one_tick()
Run reactor until an event occurs or no events are being watched anymore.
# Don't block longer than 0.5 seconds id = reactor.timer(0.5, lambda reactor: None) reactor.one_tick() reactor.remove(id)
- recurring(after: float, cb: TimerCallback) str[source]¶
id = reactor.recurring(0.25, cb)
Create a new recurring timer, invoking the callback repeatedly after a given amount of time in seconds.
- remove(remove: Handle | str) bool[source]¶
boolean = reactor.remove(handle) boolean = reactor.remove(id)
Remove handle or timer.
- run_coroutine(coro: Coroutine[Any, Any, Any], resolve: Callable[[Any], Any], reject: Callable[[Any], Any]) None[source]¶
reactor.run_coroutine(coro, resolve, reject)
Run a coroutine as a task of
loop, where it can await anything of the loop, and callresolvewith what it returns orrejectwith the exception it raises. A task that is not done is something to watch, so thatstart()does not stop before it, and each step of the coroutine is an event, which endsone_tick().
- start() None[source]¶
reactor.start()
Start watching for I/O and timer events, this will block until
stop()is called or no events are being watched anymore.# Start reactor only if it is not running already if not reactor.is_running: reactor.start()
- timer(after: float, cb: TimerCallback) str[source]¶
id = reactor.timer(0.5, cb)
Create a new timer, invoking the callback after a given amount of time in seconds.
- watch(handle: Handle, read: bool, write: bool) Self[source]¶
reactor = reactor.watch(handle, readable, writable)
Change I/O events to watch handle for with true and false values. Note that this method requires an active I/O watcher.
# Watch only for readable events reactor.watch(handle, True, False) # Watch only for writable events reactor.watch(handle, False, True) # Watch for readable and writable events reactor.watch(handle, True, True) # Pause watching for events reactor.watch(handle, False, False)