• Pythonic way to run the asyncio loop forever in python 3.10

    From David Jander@21:1/5 to All on Thu Oct 6 16:10:06 2022
    Hello,

    It is highly likely this has been discussed before, but I haven't been able to find the answer to my question, so if anyone knows, please send me to the
    right place. Thanks.

    I am migrating a lot of python software to 3.10, which has deprecated calling asyncio.get_event_loop() from outside the loop. While doing so, I have bumped against some common (I think) use-cases which seem to have no elegant solution anymore. At least I can't find one that seems "pythonic" or "correct".

    Problem: Before 3.10, asyncio.get_event_loop() was a convenient way to manage the loop singleton. In single-threaded applications, normally you want only a single instance of an event loop. The easiest way to make sure there was only one loop ever created, was to call asyncio.get_event_loop(). Since the only place this is crucially important to get right is _outside_ of the loop (because once inside the loop all damage is done already anyway), deprecating this use-case basically seems to destroy the usefulness of that method, and also many other methods of loop, like loop.run_forever() and loop.run_until_complete().

    Example: Suppose an application that does a bunch of (blocking and CPU intensive) initialization, while also setting up event handlers, that may call things like loop.add_reader() or loop.call_soon() in many different places. Then, after all initialization is done, loop.run_forever() is called to get the application running. Notice, that there may not even be a single coroutine involved.

    Someone else asked something similar here:

    https://stackoverflow.com/questions/65684730/what-is-the-pythonic-way-of-running-an-asyncio-event-loop-forever

    Unfortunately all proposed answers look like dirty hacks.

    In concrete, how do I do this nicely?

    async def main():
    loop = asyncio.get_running_loop()
    ...
    loop.add_reader(...)
    ...
    loop.call_later(...)
    ...
    ... etc...

    # HACK: Now wait forever to keep the loop running
    await asyncio.Future()

    if __name__ == "__main__":
    asyncio.run(main())


    It can't be seriously the intention that people do these kind of things,
    right?

    This example application might not even use coroutines at all, and be entirely event driven. Yet an initial coroutine is now required. No way to run the loop without one. The worst thing is that all the initialization and blocking code cannot be easily mixed with loop setup anymore, because loop setup before starting the loop has essentially been killed by python developers. Please correct me if I am wrong about any of this. What am I missing here?

    Unfortunately the world is now seemingly filled with horrible and sometimes broken workarounds for this change introduced in 3.10:

    https://github.com/aio-libs/aiohttp/blob/master/aiohttp/web.py#L434

    https://github.com/tornadoweb/tornado/blob/master/demos/tcpecho/server.py#L36

    etc...

    Where is the discussion about this, and where are the proposed solutions?

    Python 3.11 seems to mitigate this problem by introducing the asyncio.Runner() context-manager. But for the time being for 3.10, I see no good solution.

    Best regards,

    --
    David Jander

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)