0

Python 3.6 and 3.8.

I was using the following lines to start a server:

class MyServer:

    async def main(self, handler, host, port):
        self._server = await asyncio.start_server(handler, host=host, port=port)

        # Next line does not work with uvloop
        self._server._stop = False

where the _stop attribute would be added to access in the handler.

Then in the handler I would set the _stop attribute like so:

async def handler(reader, writer):
    writer._transport._server._stop = True

This works beautifully when not using uvloop. But when using uvloop, this no longer works.

When I try to set (!) the _stop attribute on the server object I immediately get this error:

AttributeError: 'uvloop.loop.Server' object has no attribute '_stop'

My question is how to "communicate" between the handler and the server ... ?

PS. When not using uvloop, this all works, because the StreamWriter._transport has an attribute _server.

Ytsen de Boer
  • 2,797
  • 2
  • 25
  • 36
  • Can you step back and explain what you were trying to do before you came up with the `_stop` hack? Do you need a way to stop the server? Would calling [`close()`](https://docs.python.org/3/library/asyncio-eventloop.html#asyncio.Server.close) on the server work just as well? – user4815162342 Feb 04 '21 at 22:08
  • Hi, yes, but only if a "stop" message was received by the handler first, which would then trigger the call to `close()`. And from within the handler, there is no access to the server. (When I have some time I'll try to work with a selector, prooooobably that is what one should do in any case when working with triggers from file descripors :) ). – Ytsen de Boer Feb 05 '21 at 11:05
  • 1
    *And from within the handler, there is no access to the server* - you can define the handler as a method on the class, pass `self.handler` as handler, and then inside it you'll have access to the server as `self._server`. (You could also use `lambda` or `functool.partial` to pass the server, but using `self` is the simplest and most idiomatic option.) Does that resolve your issue? – user4815162342 Feb 05 '21 at 11:15
  • Yes, actually, thanks for the tip! :D – Ytsen de Boer Feb 05 '21 at 14:24

1 Answers1

0

Thanks to @user4815162342, I could quite easily manage via simply passing a member function as call back.

This was not my first guess, because the member function takes three arguments, while the call back function takes two (search the web for "python bound method" for more on this and why the code below still works fine).

But the code below demonstrates how it works now:

import asyncio


class MyServerContext:

    async def handler(self, reader, writer):
        line = await reader.readline()
        if line == b'stop\n':
            setattr(self, '_stop', True)

    async def send_stop(self):

        _, writer = await asyncio.open_connection('localhost', 6369)
        writer.write(b'stop\n')
        await writer.drain()
        writer.close()
        await writer.wait_closed()

    async def start(self):

        server = await asyncio.start_server(
            self.handler,    # <---- Member function as call back
            'localhost',
            6369)

        while not getattr(self, '_stop', False):
            await self.send_stop()

        server.close()
        await server.wait_closed()


if __name__ == '__main__':
    server_context = MyServerContext()
    asyncio.get_event_loop().run_until_complete(server_context.start())
    exit(0)

Ytsen de Boer
  • 2,797
  • 2
  • 25
  • 36
  • 1
    Note that you don't even need the lambda, you can just pass `self.handler` (without parentheses) do `asyncio.start_server`. Also, you don't need to use `setattr` and `getattr` to access the `_stop` attribute, you can just assign or read `self._stop`. `getattr` and `setattr` are for when the attribute name is not known in advance. – user4815162342 Feb 05 '21 at 14:41
  • That surprises me, but it works fine indeed! I'm using the setattr and getattr here to have one line less in the minimal code above. – Ytsen de Boer Feb 05 '21 at 15:06
  • 1
    If you're curious, google python bound method. – user4815162342 Feb 05 '21 at 15:15