'start_server() method in python asyncio module

This question is really for the different coroutines in base_events.py and streams.py that deal with Network Connections, Network Servers and their higher API equivalents under Streams but since its not really clear how to group these functions I am going to attempt to use start_server() to explain what I don't understand about these coroutines and haven't found online (unless I missed something obvious).

When running the following code, I am able to create a server that is able to handle incoming messages from a client and I also periodically print out the number of tasks that the EventLoop is handling to see how the tasks work. What I'm surprised about is that after creating a server, the task is in the finished state not too long after the program starts. I expected that a task in the finished state was a completed task that no longer does anything other than pass back the results or exception.

However, of course this is not true, the EventLoop is still running and handling incoming messages from clients and the application is still running. Monitor however shows that all tasks are completed and no new task is dispatched to handle a new incoming message.

So my question is this:

  • What is going on underneath asyncio that I am missing that explains the behavior I am seeing? For example, I would have expected a task (or tasks created for each message) that is handling incoming messages in the pending state.
  • Why is the asyncio.Task.all_tasks() passing back finished tasks. I would have thought that once a task has completed it is garbage collected (so long as no other references are to it).

I have seen similar behavior with the other asyncio functions like using create_connection() with a websocket from a site. I know at the end of these coroutines, their result is usually a tuple such as (reader, writer) or (transport, protocol) but I don't understand how it all ties together or what other documentation/code to read to give me more insight. Any help is appreciated.

import asyncio
from pprint import pprint

async def echo_message(self, reader, writer):
    data = await reader.read(1000)
    message = data.decode()
    addr = writer.get_extra_info('peername')
    print('Received %r from %r' % (message, addr))

    print('Send: %r' % message)
    writer.write(message.encode())
    await writer.drain()

    print('Close the client socket')
    writer.close()


async def monitor():
    while True:
        tasks = asyncio.Task.all_tasks()
        pprint(tasks)
        await asyncio.sleep(60)

if __name__ == '__main__': 
    loop = asyncio.get_event_loop()
    loop.create_task(monitor())
    loop.create_task(asyncio.start_server(echo_message, 'localhost', 7777, loop))
    loop.run_forever()

Outputs:

###
# Soon after starting the application, monitor prints out: 
###
{<Task pending coro=<start_server() running ...>,
 <Task pending coro=<monitor() running ...>, 
 <Task pending coro=<BaseEventLoop._create_server_getaddrinfo() running ...>}

###
# After, things initialized and the server has started and the next print out is:
###
{<Task finished coro=<start_server() done ...>,
 <Task pending coro=<monitor() running ...>, 
 <Task finished coro=<BaseEventLoop._create_server_getaddrinfo() done ...>}


Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source