I agree with Andrew that there are too many different scenarios and requirements to make this a useful library function. Some notes on the actual code you posted:
- Instead of calling signal.signal() yourself, you should use loop.add_signal_handler(). It makes sure your signal handler doesn't run while another handler is already running. - If you add a handler for SIGINT you can control what happens when the user hits ^C (again, ensuring the handler already running isn't interrupted halfway through). - I'm unclear on why you want a wait_forever() instead of using loop.run_forever(). Can you clarify? - In theory, instead of waiting for a Future that is cancelled by a handler, you should be able to use asyncio.sleep() with a very large number (e.g. a million seconds). Your handler could then just call loop.stop(). However, I just tested this and it raises "RuntimeError: Event loop stopped before Future completed." so ignore this until we've fixed it. :-) On Fri, Dec 18, 2015 at 5:58 AM, Szieberth Ádám <szieba...@gmail.com> wrote: > 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/guido%40python.org > -- --Guido van Rossum (python.org/~guido)
_______________________________________________ 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