Mailing List Archive

asyncio questions
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?
--
https://mail.python.org/mailman/listinfo/python-list
Re: asyncio questions [ In reply to ]
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.

;)

--
https://mail.python.org/mailman/listinfo/python-list
Re: asyncio questions [ In reply to ]
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`.
--
https://mail.python.org/mailman/listinfo/python-list
Re: asyncio questions [ In reply to ]
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

--
https://mail.python.org/mailman/listinfo/python-list
Re: asyncio questions [ In reply to ]
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

--
https://mail.python.org/mailman/listinfo/python-list