Python asyncio fires event loop once?

I am trying to understand an asynchronous library, especially using sockets. I wrote the code in an attempt to get an understanding,

I wanted to start sender and receive sockets asynchronously. I got to the point that I got all the data sent to the last, but then I need to start another loop. Considering how to do this, I found the https://stackoverflow.com/a/166648/ which is executed below, but what happens here? Is there a better / smarter way to do this than calling stop and then run_forever ?

Documentation for stop() in the event loop:

Stop the execution of the event loop.

Each callback scheduled before the stop () function is called is triggered. Callbacks scheduled after calling the stop () function will not be executed. However, these callbacks will be executed if run_forever () is called again later.

And run_forever() documentation:

Run before stop () is called.

Questions:

  • Why is run_forever only way run_once in the world? It doesn't even make sense.
  • Is there a better way to do this?
  • Is my code looking like a reasonable way to program using the asyncio library?
  • Is there a better way to add tasks to the event loop other than asyncio.async() ? loop.create_task gives an error on my Linux system.

https://gist.github.com/cloudformdesign/b30e0860497f19bd6596

+7
python sockets python-asyncio asyncsocket
source share
1 answer

Three stop(); run_forever() stop(); run_forever() works due to the implementation of stop :

 def stop(self): """Stop running the event loop. Every callback scheduled before stop() is called will run. Callback scheduled after stop() is called won't. However, those callbacks will run if run() is called again later. """ self.call_soon(_raise_stop_error) def _raise_stop_error(*args): raise _StopError 

So, the next time the event loop _raise_stop_error and executes pending callbacks, it will raise _raise_stop_error , which raises _StopError . The run_forever will only break into this particular exception:

 def run_forever(self): """Run until stop() is called.""" if self._running: raise RuntimeError('Event loop is running.') self._running = True try: while True: try: self._run_once() except _StopError: break finally: self._running = False 

So, by planning a stop() and then calling run_forever , you start one iteration of the event loop and then stop as soon as it accesses the _raise_stop_error return message. You may also have noticed that _run_once is defined and run_forever is run_forever . You can call it directly, but sometimes it can block if there are no callbacks ready to run, which may be undesirable. I don’t think there is a cleaner way to do it now - this answer was provided by Andrei Svetlov, who is a contributor to asyncio ; he would probably know if there is a better option. :)

All in all, your code looks reasonable, although I think you should not use this run_once approach to start. It is not deterministic; if you had a longer list or a slower system, printing it all might require more than two additional iterations. Instead, you should just send a watchdog that tells the receiver to complete the work, and then wait for the send and receive commands to complete:

 import sys import time import socket import asyncio addr = ('127.0.0.1', 1064) SENTINEL = b"_DONE_" # ... (This stuff is the same) @asyncio.coroutine def sending(addr, dataiter): loop = asyncio.get_event_loop() for d in dataiter: print("Sending:", d) sock = socket.socket() yield from send_close(loop, sock, addr, str(d).encode()) # Send a sentinel sock = socket.socket() yield from send_close(loop, sock, addr, SENTINEL) @asyncio.coroutine def receiving(addr): loop = asyncio.get_event_loop() sock = socket.socket() try: sock.setblocking(False) sock.bind(addr) sock.listen(5) while True: data = yield from accept_recv(loop, sock) if data == SENTINEL: # Got a sentinel return print("Recevied:", data) finally: sock.close() def main(): loop = asyncio.get_event_loop() # add these items to the event loop recv = asyncio.async(receiving(addr), loop=loop) send = asyncio.async(sending(addr, range(10)), loop=loop) loop.run_until_complete(asyncio.wait([recv, send])) main() 

Finally, asyncio.async is the right way to add tasks to the event loop. create_task was added in Python 3.4.2, so if you have an earlier version, it will not exist.

+10
source share

All Articles