Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to pass additional parameters to handle_client coroutine?

The recommended way to use asyncio for a socket server is:

import asyncio

async def handle_client(reader, writer):
    request = (await reader.read(100)).decode()
    response = "Data received." 
    writer.write(response.encode())

async def main():
    loop.create_task(asyncio.start_server(handle_client, 'localhost', 15555))

loop = asyncio.get_event_loop()
loop.create_task(main())
loop.run_forever()

This works fine, but now I need to receive appropriate client request and then use aiohttp library to fetch data from a 3rd party restful API.

This requires creating a session variable as follows:

from aiohttp import ClientSession

session = ClientSession()

But this also should be inside a coroutine itself, so I'll put it inside main:

async def main():
    session = ClientSession()
    loop.create_task(asyncio.start_server(handle_client, '', 55555))

Now I need to pass the session variable to the aiohttp get coroutine to fetch the rest API data:

async with session.get(url, params=params) as r:
    try:
        return await r.json(content_type='application/json')
    except aiohttp.client_exceptions.ClientResponseError:
        ....

My question is how can I pass the session variable to handle_client coroutine, if it insists on only having reader,writer parameters, and globals don't help me because sessions must exist inside coroutines?

like image 854
sphere Avatar asked Jun 04 '18 10:06

sphere


Video Answer


1 Answers

You can use a temporary function or a lambda:

async def main():
    session = aiohttp.ClientSession()
    await asyncio.start_server(lambda r, w: handle_client(r, w, session),
                               '', 55555)

This works because even though the lambda is not technically a coroutine, it behaves just like one - when invoked it returns a coroutine object.

For larger programs you might prefer a class-based approach where a class encapsulates the state shared by multiple clients without having to pass it explicitly. For example:

class ClientContext:
    def __init__(self):
        self.session = aiohttp.ClientSession()
        # ... add anything else you will need "globally"

    async def handle_client(self, reader, writer):
        # ... here you get reader and writer, but also have
        # session etc as self.session ...

async def main():
    ctx = ClientContext()
    await asyncio.start_server(ctx.handle_client), '', 55555)
like image 157
user4815162342 Avatar answered Oct 04 '22 00:10

user4815162342