Running the loop¶
zuvloop is a loop factory. Everything below is a different way to hand that factory to something that starts a loop.
Directly¶
zuvloop.run() takes the same arguments as asyncio.run() — debug and
loop_factory included — and is exactly asyncio.run() with zuvloop's factory as
the default.
Through asyncio¶
Prefer this when something else already owns the call to asyncio.run().
asyncio.Runner takes the same argument, which is what you want if you need the
loop to outlive a single coroutine:
with asyncio.Runner(loop_factory=zuvloop.new_event_loop) as runner:
runner.run(setup())
runner.run(main())
Through a framework¶
Most frameworks accept a loop factory somewhere. A few examples:
Where zuvloop_loop.py is:
Creating a loop by hand¶
import zuvloop
loop = zuvloop.new_event_loop()
try:
loop.run_until_complete(main())
finally:
loop.close()
zuvloop.new_event_loop() returns a zuvloop.EventLoop, which is an
asyncio.AbstractEventLoop. Close it when you are done: the loop owns a libuv
loop, a self-pipe and any transports still open, and closing is what releases
them.
One loop per thread
A loop belongs to the thread that runs it, as in asyncio. call_soon_threadsafe
is the only method other threads may call, and signal handlers may only be
registered from the main thread.
Debug mode¶
Debug mode times every callback and reports the slow ones through
call_exception_handler, the same as asyncio. zuvloop also turns each into an
OpenTelemetry span carrying the awaiting call graph — see
Instrumentation.
The timing is done in Zig with uv_hrtime(), so enabling debug mode costs one
clock read per callback rather than a Python-level wrapper.