Hi Developers! This is my first post. Please excuse me my poor English. If anyone is interested, I wrote a small introduction on my homepage. Link is at the bottom.
This post is about how to effectively implement the new asynchronous context manager in a typical network server. I would appreciate and welcome any confirmation or critics whether my thinking is right or wrong. Thanks in advance! So, a typical server main code I used to see around is like this: srv = loop.run_until_complete(create_server(handler, host, port)) try: loop.run_forever() except KeyboardInterrupt: pass finally: # other tear down code may be here srv.close() loop.run_until_complete(srv.wait_closed()) loop.close() Note that `create_server()` here is not necessary `BaseEventLoop.create_server()`. The above code is not prepared to handle `OSError`s or any other `Exception`s (including a `KeyboardInterrupt` by a rapid Ctr+C) when setting up the server, it just prints the traceback to the console which is not user friendly. Moreover, I would expect from a server to handle the SIGTERM signal as well and tell its clients that it stops serving when not force killed. How the main code should create server, maintain the serving, deal with errors and close properly both the connections and the event loop when exiting without letting pending tasks around is not trivial. There are many questions on SO and other places of the internet regarding of this problem. My idea was to provide a simple code which is robust in terms of these concerns by profiting from the new asynchronous context manager pattern. The code of the magic methods of a typical awaitable `CreateServer` object seems rather trivial: async def __aenter__(self): self.server = await self return self.server async def __aexit__(self, exc_type, exc_value, traceback): # other tear down code may be here self.server.close() await self.server.wait_closed() However, to make it work, a task has to be created: async def server_task(): async with CreateServer(handler, host, port) as srv: await asyncio.Future() # wait forever I write some remarks regarding the above code to the end of this post. Note that `srv` is unreachable from outside which could be a problem in some cases. What is unavoidable: this task has to get cancelled explicitely by the main code which should look like this: srvtsk = loop.create_task(server_task()) signal.signal(signal.SIGTERM, lambda si, fr: loop.call_soon(srvtsk.cancel)) while True: try: loop.run_until_complete(srvtsk) except KeyboardInterrupt: srvtsk.cancel() except asyncio.CancelledError: break except Exception as err: print(err) break loop.close() Note that when `CancelledError` gets raised, the tear down process is already done. Remarks: * It would be nice to have an `asyncio.wait_forever()` coroutine for dummy context bodies. * Moreover, I also imagined an `BaseEventLoop.create_context_task(awithable, body_coro_func=None)` method. The `body_coro_func` should default to `asyncio.wait_forever()`, otherwise it should get whatever is returned by `__aenter__` as a single argument. The returned Task object should also provide a reference to that object. Best regards, Ádám (http://szieberthadam.github.io/) _______________________________________________ Python-Dev mailing list Python-Dev@python.org https://mail.python.org/mailman/listinfo/python-dev Unsubscribe: https://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com