• asyncio questions

    From Frank Millman@21:1/5 to All on Thu Jan 26 12:12:29 2023
    Hi all

    I have written a simple HTTP server using asyncio. It works, but I don't
    always understand how it works, so I was pleased that Python 3.11
    introduced some new high-level concepts that hide the gory details. I
    want to refactor my code to use these concepts, but I am not finding it
    easy.

    In simple terms my main loop looked like this -

        loop = asyncio.get_event_loop()
        server = loop.run_until_complete(
            asyncio.start_server(handle_client, host, port))
        loop.run_until_complete(setup_companies())
        session_check = asyncio.ensure_future(
            check_sessions())  # start background task
        print('Press Ctrl+C to stop')
        try:
            loop.run_forever()
        except KeyboardInterrupt:
            print()
        finally:
            session_check.cancel()  # tell session_check to stop running
            loop.run_until_complete(asyncio.wait([session_check]))
            server.close()
            loop.stop()

    Using 3.11 it now looks like this -

        with asyncio.Runner() as runner:
            server = runner.run(asyncio.start_server(
                handle_client, host, port)
            runner.run(setup_companies())
            session_check = asyncio.ensure_future(
                check_sessions())  # start background task
            print('Press Ctrl+C to stop')
            try:
                runner.run(server.serve_forever())
            except KeyboardInterrupt:
                print()
            finally:
                session_check.cancel()  # tell session_check to stop running
                runner.run(asyncio.wait([session_check]))
                server.close()

    It works, and I guess it looks a bit neater.

    Problem 1.

    The docs to 'asyncio.ensure_future' state 'See also the create_task()
    function which is the preferred way for creating new Tasks'

    If I change 'ensure_future' to 'create_task', I get
        RuntimeError: no running event loop

    I don't know how to fix this.

    Problem 2.

    The docs have a section on 'Handling Keyboard Interruption'

    https://docs.python.org/3.11/library/asyncio-runner.html#asyncio.Runner

    I have not figured out how to adapt my code to use this new approach.

    Any suggestions appreciated.

    Frank Millman

    P.S. Might it be better to ask these questions on the Async_SIG
    Discussion Forum?

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Grant Edwards@21:1/5 to Frank Millman on Thu Jan 26 08:06:57 2023
    On 2023-01-26, Frank Millman <frank@chagford.com> wrote:

    I have written a simple HTTP server using asyncio. It works, but I don't always understand how it works,

    I thought that was the rule with asyncio.

    ;)

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Dieter Maurer@21:1/5 to Frank Millman on Thu Jan 26 18:16:01 2023
    Frank Millman wrote at 2023-1-26 12:12 +0200:
    I have written a simple HTTP server using asyncio. It works, but I don't >always understand how it works, so I was pleased that Python 3.11
    introduced some new high-level concepts that hide the gory details. I
    want to refactor my code to use these concepts, but I am not finding it
    easy.

    In simple terms my main loop looked like this -

        loop = asyncio.get_event_loop()
        server = loop.run_until_complete(
            asyncio.start_server(handle_client, host, port))
        loop.run_until_complete(setup_companies())
        session_check = asyncio.ensure_future(
            check_sessions())  # start background task
        print('Press Ctrl+C to stop')
        try:
            loop.run_forever()
        except KeyboardInterrupt:
            print()
        finally:
            session_check.cancel()  # tell session_check to stop running
            loop.run_until_complete(asyncio.wait([session_check]))
            server.close()
            loop.stop()

    Why does your code uses several `loop.run*` calls?

    In fact, I would define a single coroutine and run that
    with `asyncio.run`.
    This way, the coroutine can use all `asyncio` features,
    including `loop.create_task`.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Frank Millman@21:1/5 to Dieter Maurer on Fri Jan 27 14:14:35 2023
    On 2023-01-26 7:16 PM, Dieter Maurer wrote:
    Frank Millman wrote at 2023-1-26 12:12 +0200:
    I have written a simple HTTP server using asyncio. It works, but I don't
    always understand how it works, so I was pleased that Python 3.11
    introduced some new high-level concepts that hide the gory details. I
    want to refactor my code to use these concepts, but I am not finding it
    easy.

    In simple terms my main loop looked like this -

        loop = asyncio.get_event_loop()
        server = loop.run_until_complete(
            asyncio.start_server(handle_client, host, port))
        loop.run_until_complete(setup_companies())
        session_check = asyncio.ensure_future(
            check_sessions())  # start background task
        print('Press Ctrl+C to stop')
        try:
            loop.run_forever()
        except KeyboardInterrupt:
            print()
        finally:
            session_check.cancel()  # tell session_check to stop running
            loop.run_until_complete(asyncio.wait([session_check]))
            server.close()
            loop.stop()

    Why does your code uses several `loop.run*` calls?

    In fact, I would define a single coroutine and run that
    with `asyncio.run`.
    This way, the coroutine can use all `asyncio` features,
    including `loop.create_task`.

    You are right, Dieter. The function that I showed above is a normal
    function, not an async one. There was no particular reason for this - I
    must have got it working like that at some point in the past, and 'if it
    ain't broke ...'

    I have changed it to async, which I call with 'asyncio.run'. It now
    looks like this -

    server = await asyncio.start_server(handle_client, host, port)
    await setup_companies()
    session_check = asyncio.create_task(
    check_sessions()) # start background task

    print('Press Ctrl+C to stop')

    try:
    await server.serve_forever()
    except asyncio.CancelledError:
    pass
    finally:
    session_check.cancel() # tell session_check to stop running
    await asyncio.wait([session_check])
    server.close()

    It works exactly the same as before, and it is now much neater.

    Thanks for the input.

    Frank

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Frank Millman@21:1/5 to Frank Millman on Fri Jan 27 14:24:18 2023
    On 2023-01-27 2:14 PM, Frank Millman wrote:

    I have changed it to async, which I call with 'asyncio.run'. It now
    looks like this -

        server = await asyncio.start_server(handle_client, host, port)
        await setup_companies()
        session_check = asyncio.create_task(
            check_sessions())  # start background task

        print('Press Ctrl+C to stop')

        try:
            await server.serve_forever()
        except asyncio.CancelledError:
            pass
        finally:
            session_check.cancel()  # tell session_check to stop running
            await asyncio.wait([session_check])
            server.close()


    I don't think I need the 'finally' clause - the cleanup can all happen
    in the 'except' block.

    Frank

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