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