how to stop() the last loop among multiple nested asyncio loops? - python

I need at times more than one asyncio coroutine, where the routines then would be nested : coroutine B running in coroutine A, C in B and so on. The problem is with stopping a given loop. For example, using loop.stop() in the last top loop such as loop 'C' kills actually all asyncio coroutines - and not just this loop 'C'. I suspect that stop() actually kills coroutine A, and by doing so it annihilates all other dependent routines. The nested routines are call_soon_threadsafe, and all routines begin as run_forever.
I tried using specific loop names, or 'return', or 'break' (in the while loop inside the coroutine) but nothing exits the loop - except stop() which then kills non-specifically all loops at once.
My problem I described here is actually related to an earlier question of mine...
python daemon server crashes during HTML popup overlay callback using asyncio websocket coroutines
...which I thought I had solved - till running into this loop.stop() problem.
below now my example code for Python 3.4.3 where I try to stop() the coroutine_overlay_websocket_server loop as soon as it is done with the websocket job. As said, my code at the current state breaks all running loops. Thereafter the fmDaemon recreates a new asyncio loop that knows nothing of what was computed before :
import webbrowser
import websockets
import asyncio
class fmDaemon( Daemon):
# Daemon - see : http://www.jejik.com/articles/2007/02/a_simple_unix_linux_daemon_in_python/
# Daemon - see : http://www.jejik.com/files/examples/daemon3x.py
def __init__( self, me):
self.me = me
def run( self):
while True:
#asyncio.coroutine
def coroutine_daemon_websocket_server( websocket, path):
msg = yield from websocket.recv()
if msg != None:
msg_out = "{}".format( msg)
yield from websocket.send( msg_out)
self.me = Function1( self.me, msg)
loop = asyncio.get_event_loop()
loop.run_until_complete( websockets.serve( coroutine_daemon_websocket_server, self.me.IP, self.me.PORT))
loop.run_forever()
def Function1( me, msg):
# doing some stuff :
# creating HTML file,
# loading HTML webpage with a webbrowser call,
# awaiting HTML button press signal via websocket protocol :
#asyncio.coroutine
def coroutine_overlay_websocket_server( websocket, path):
while True:
msg = yield from websocket.recv()
msg_out = "{}".format( msg)
yield from websocket.send( msg_out)
if msg == 'my_expected_string':
me.flags['myStr'] = msg
break
loop.call_soon_threadsafe( loop.stop)
loop = asyncio.get_event_loop()
loop.call_soon_threadsafe( asyncio.async, websockets.serve( coroutine_overlay_websocket_server, me.IP, me.PORT_overlay))
loop.run_forever()
loop.call_soon_threadsafe( loop.close)
# program should continue here...
My two questions : 1) Is there a way to exit a given coroutine without killing a coroutine lower down? 2) Or, alternatively, do you know of a method for reading websocket calls that does not make use of asyncio ?

I'm still a little confused by what you're trying to do, but there's definitely no need to try to nest event loops - your program is single-threaded, so when you call asyncio.get_event_loop() multiple times, you're always going to get the same event loop back. So you're really not creating two different loops in your example; both fmDaemon.run and Function1 use the same one. That's why stopping loop inside Function1 also kills the coroutine you launched inside run.
That said, there's no reason to try to create two different event loops to begin with. Function1 is being called from a coroutine, and wants to call other coroutines, so why not make it a coroutine, too? Then you can just call yield from websockets.serve(...) directly, and use an asyncio.Event to wait for coroutine_overlay_websocket_server to complete:
import webbrowser
import websockets
import asyncio
class fmDaemon( Daemon):
def __init__( self, me):
self.me = me
def run( self):
#asyncio.coroutine
def coroutine_daemon_websocket_server(websocket, path):
msg = yield from websocket.recv()
if msg != None:
msg_out = "{}".format( msg)
yield from websocket.send( msg_out)
self.me = Function1( self.me, msg)
loop = asyncio.get_event_loop()
loop.run_until_complete(websockets.serve(coroutine_daemon_websocket_server,
self.me.IP,
self.me.PORT))
loop.run_forever()
#asyncio.coroutine
def Function1(me, msg):
#asyncio.coroutine
def coroutine_overlay_websocket_server(websocket, path):
while True:
msg = yield from websocket.recv()
msg_out = "{}".format( msg)
yield from websocket.send( msg_out)
if msg == 'my_expected_string':
me.flags['myStr'] = msg
break
event.set() # Tell the outer function it can exit.
event = asyncio.Event()
yield from websockets.serve(coroutine_overlay_websocket_server, me.IP, me.PORT_overlay))
yield from event.wait() # This will block until event.set() is called.

Related

Pyserial and asyncio

Trying to use pyserial with asyncio on a windows machine.
Inspired by https://stackoverflow.com/a/27927704/1629704 my code is constantly watching a serial port for incoming data.
# This coroutine is added as a task to the event loop.
#asyncio.coroutine
def get_from_serial_port(self):
while 1:
serial_data = yield from self.get_byte_async()
<doing other stuff with serial_data>
# The method which gets executed in the executor
def get_byte(self):
data = self.s.read(1)
time.sleep(0.5)
tst = self.s.read(self.s.inWaiting())
data += tst
return data
# Runs blocking function in executor, yielding the result
#asyncio.coroutine
def get_byte_async(self):
with concurrent.futures.ThreadPoolExecutor(max_workers=1) as executor:
res = yield from self.loop.run_in_executor(executor, self.get_byte)
return res
After serial data has been returned. the coroutine get_byte_async is called inside the while loop creating a new executor. I always learned creating a new thread is expensive so I feel I should take another approach, but I am not sure how to do that.
I've been reading this article https://hackernoon.com/threaded-asynchronous-magic-and-how-to-wield-it-bba9ed602c32#.964j4a5s7
And I guess I need to do the reading of the serial port in another thread. But how to get the serial data back to the "main" loop ?
You can either use the default executor and lock the access to get_byte with an asyncio lock:
async def get_byte_async(self):
async with self.lock:
return await self.loop.run_in_executor(None, self.get_byte)
Or simply create your own executor once:
async def get_byte_async(self):
if self.executor is None:
self.executor = concurrent.futures.ThreadPoolExecutor(max_workers=1)
return await self.loop.run_in_executor(self.executor, self.get_byte)

How to call an asyncio coroutine from Celery [duplicate]

I am using asyncio for a network framework.
In below code(low_level is our low level function, main block is our program entry, user_func is user-defined function):
import asyncio
loop = asyncio.get_event_loop()
""":type :asyncio.AbstractEventLoop"""
def low_level():
yield from asyncio.sleep(2)
def user_func():
yield from low_level()
if __name__ == '__main__':
co = user_func()
loop.run_until_complete(co)
I want wrap the low_level as normal function rather than coroutine(for compatibility etc.), but low_level is in event loop. How can wrap it as a normal function?
Because low_level is a coroutine, it can only be used by running an asyncio event loop. If you want to be able to call it from synchronous code that isn't running an event loop, you have to provide a wrapper that actually launches an event loop and runs the coroutine until completion:
def sync_low_level():
loop = asyncio.get_event_loop()
loop.run_until_complete(low_level())
If you want to be able to call low_level() from a function that is part of the running event loop, have it block for two seconds, but not have to use yield from, the answer is that you can't. The event loop is single-threaded; whenever execution is inside one of your functions, the event loop is blocked. No other events or callbacks can be processed. The only ways for a function running in the event loop to give control back to the event loop are to 1) return 2) use yield from. The asyncio.sleep call in low_level will never be able to complete unless you do one those two things.
Now, I suppose you could create an entirely new event loop, and use that to run the sleep synchronously from a coroutine running as part of the default event loop:
import asyncio
loop = asyncio.get_event_loop()
#asyncio.coroutine
def low_level(loop=None):
yield from asyncio.sleep(2, loop=loop)
def sync_low_level():
new_loop = asyncio.new_event_loop()
new_loop.run_until_complete(low_level(loop=new_loop))
#asyncio.coroutine
def user_func():
sync_low_level()
if __name__ == "__main__":
loop.run_until_complete(user_func())
But I'm really not sure why you'd want to do that.
If you just want to be able to make low_level act like a method returning a Future, so you can attach callbacks, etc. to it, just wrap it in asyncio.async():
loop = asyncio.get_event_loop()
def sleep_done(fut):
print("Done sleeping")
loop.stop()
#asyncio.coroutine
def low_level(loop=None):
yield from asyncio.sleep(2, loop=loop)
def user_func():
fut = asyncio.async(low_level())
fut.add_done_callback(sleep_done)
if __name__ == "__main__":
loop.call_soon(user_func)
loop.run_forever()
Output:
<2 second delay>
"Done sleeping"
Also, in your example code, you should use the #asyncio.coroutine decorator for both low_level and user_func, as stated in the asyncio docs:
A coroutine is a generator that follows certain conventions. For
documentation purposes, all coroutines should be decorated with
#asyncio.coroutine, but this cannot be strictly enforced.
Edit:
Here's how a user from a synchronous web framework could call into your application without blocking other requests:
#asyncio.coroutine
def low_level(loop=None):
yield from asyncio.sleep(2, loop=loop)
def thr_low_level():
loop = asyncio.new_event_loop()
t = threading.Thread(target=loop.run_until_complete, args(low_level(loop=loop),))
t.start()
t.join()
If a request being handled by Flask calls thr_low_level, it will block until the request is done, but the GIL should be released for all of the asynchronous I/O going on in low_level, allowing other requests to be handled in separate threads.

Is it possible to run the asyncio.Server instance while the event loop is already running

I'm trying to understand, is it possible to run the asyncio.Server instance while the event loop is already running by run_forever method (from a separate thread, of course).
As I understand, the server can be started either by loop.run_until_complete(asyncio.start_server(...)) or by
await asyncio.start_server(...), if the loop is already running.
The first way is not acceptable for me, since the loop is already running by run_forever method. But I also can't use the await expression, since I'm going to start it from outside the "loop area" (e.g. from the main method, which can't be marked as async, right?)
def loop_thread(loop):
asyncio.set_event_loop(loop)
try:
loop.run_forever()
finally:
loop.close()
print("loop clesed")
class SchedulerTestManager:
def __init__(self):
...
self.loop = asyncio.get_event_loop()
self.servers_loop_thread = threading.Thread(
target=loop_thread, args=(self.loop, ))
...
def start_test(self):
self.servers_loop_thread.start()
return self.servers_loop_thread
def add_router(self, router):
r = self.endpoint.add_router(router)
host = router.ConnectionParameters.Host
port = router.ConnectionParameters.Port
srv = TcpServer(host, port)
server_coro = asyncio.start_server(
self.handle_connection, self.host, self.port)
# does not work since add_router is not async
# self.server = await server_coro
# does not work, since the loop is already running
# self.server = self.loop.run_until_complete(server_coro)
return r
def maind():
st_manager = SchedulerTestManager()
thread = st_manager.start_test()
router = st_manager.add_router(router)
Of cource, the simplest solution is to add all routers (servers) before starting the test (running the loop). But I want try to implement it, so it would be possible to add a router when a test is already running. I thought the loop.call_soon (call_soon_threadsafe) methods can help me, but it seems the can't shedule a coroutine, but just a simple function.
Hope that my explanation is not very confusing. Thanks in advance!
For communicating between event loop executed in one thread and conventional old good threaded code executed in other thread you might use janus library.
It's a queue with two interfaces: async and thread-safe sync one.
This is usage example:
import asyncio
import janus
loop = asyncio.get_event_loop()
queue = janus.Queue(loop=loop)
def threaded(sync_q):
for i in range(100):
sync_q.put(i)
sync_q.join()
#asyncio.coroutine
def async_coro(async_q):
for i in range(100):
val = yield from async_q.get()
assert val == i
async_q.task_done()
fut = loop.run_in_executor(None, threaded, queue.sync_q)
loop.run_until_complete(async_coro(queue.async_q))
loop.run_until_complete(fut)
You may create a task waiting new messages from the queue in a loop and starting new servers on request. Other thread may push new message into the queue asking for a new server.

What should I decorate with #asyncio.coroutine for async operations?

I want to run my code asnychronously. What should I decorate with #asyncio.coroutine and what should I call with yield from for async operations?
In my case, I have some example code without decorator. (Simple chat bot look-like IRC)
import asyncio
class ChatBot:
def __init__(self, loop):
conn = asyncio.open_connection(HOST, PORT, loop=loop)
self.reader, self.writer = yield from conn
def send(self, msg):
self.writer.write(msg)
def read(self):
msg = yield from self.reader.readline()
return msg
def run(self):
while True:
msg = self.read()
self.parse(msg)
def parse(self, msg):
if msg.startswith('PING'):
self.some_work(msg)
elif msg.startswith('ERROR'):
self.some_error()
else:
self.server_log(msg)
def some_work(self, msg):
# some work. It can call asynchronous function like I/O or long operation. It can use self.send().
def some_error(self, msg):
# some work. It can call asynchronous function like I/O or long operation. It can use self.send().
def server_log(self, msg):
# some work. It can call asynchronous function like I/O or long operation. It can use self.send().
loop = asyncio.get_event_loop()
bot = ChatBot(loop)
loop.run_until_complete(???)
loop.close()
I think ??? is bot.run() and ChatBot.run must be decorated with #asyncio.coroutine. Then, how about other methods? I can't understand when use #asyncio.coroutine decorator and call method with yield from or asyncio.async. (I already read PEP-3156 for understanding asnycio. But I can't understand fully.)
When to use the #asyncio.coroutine decorator
If you have a function that needs to use yield from to call a coroutine, you should decorate it with asyncio.coroutine. Also note that coroutines are often (not always) "viral". As soon as you add yield from to a function it becomes a coroutine, and additionally any function that calls that coroutine usually (though not always) needs to be come a coroutine, too.
When to use asyncio.async
Why are coroutines not always viral? Because you actually don't always need to use yield from to call a coroutine. You only need to use yield from if you want to call a coroutine and wait for it to finish. If you just want to kick off a coroutine in the background, you can just do this:
asyncio.async(coroutine())
This will schedule coroutine to run as soon as control returns to the event loop; it won't wait for coroutine to finish before moving on to the next line. An ordinary function can use this to schedule a coroutine to run without also having to become a coroutine itself.
You can also use this approach to run multiple coroutines concurrently. So, imagine you have these two coroutines:
#asyncio.coroutine
def coro1():
yield from asyncio.sleep(1)
print("coro1")
#asyncio.coroutine
def coro2():
yield from asyncio.sleep(2)
print("coro2")
If you had this:
#asyncio.coroutine
def main():
yield from coro1()
yield from coro2()
yield from asyncio.sleep(5)
asyncio.get_event_loop().run_until_complete(main())
After 1 second, "coro1" would be printed. Then, after two more seconds (so three seconds total), "coro2" would be printed, and five seconds later the program would exit, making for 8 seconds of total runtime. Alternatively, if you used asyncio.async:
#asyncio.coroutine
def main():
asyncio.async(coro1())
asyncio.async(coro2())
yield from asyncio.sleep(5)
asyncio.get_event_loop().run_until_complete(main())
This will print "coro1" after one second, "coro2" one second later, and the program would exit 3 seconds later, for a total of 5 seconds of runtime.
How does this affect your code?
So following those rules, your code needs to look like this:
import asyncio
class ChatBot:
def __init__(self, reader, writer):
# __init__ shouldn't be a coroutine, otherwise you won't be able
# to instantiate ChatBot properly. So I've removed the code that
# used yield from, and moved it outside of __init__.
#conn = asyncio.open_connection(HOST, PORT, loop=loop)
#self.reader, self.writer = yield from conn
self.reader, self.writer = reader, writer
def send(self, msg):
# writer.write is not a coroutine, so you
# don't use 'yield from', and send itself doesn't
# need to be a coroutine.
self.writer.write(msg)
#asyncio.coroutine
def read(self):
msg = yield from self.reader.readline()
return msg
#asyncio.coroutine
def run(self):
while True:
msg = yield from self.read()
yield from self.parse(msg)
#asyncio.coroutine
def parse(self, msg):
if msg.startswith('PING'):
yield from self.some_work(msg)
elif msg.startswith('ERROR'):
yield from self.some_error()
else:
yield from self.server_log(msg)
#asyncio.coroutine
def some_work(self, msg):
# some work. It can call asynchronous function like I/O or long operation. It can use self.send().
#asyncio.coroutine
def some_error(self, msg):
# some work. It can call asynchronous function like I/O or long operation. It can use self.send().
#asyncio.coroutine
def server_log(self, msg):
# some work. It can call asynchronous function like I/O or long operation. It can use self.send()
#asyncio.coroutine
def main(host, port):
reader, writer = yield from asyncio.open_connection(HOST, PORT, loop=loop)
bot = ChatBot(reader, writer)
yield from bot.run()
loop = asyncio.get_event_loop()
loop.run_until_complete(main())
loop.close()
One other thing to keep in mind - adding yield from in front of a function doesn't magically make that call non-blocking. Neither does adding the #asyncio.coroutine decorator. Functions are only non-blocking if they're actually directly or indirectly calling native asyncio coroutines, which use non-blocking I/O and are integrated with the asyncio event loop. You mentioned making REST API calls, for example. In order for those REST API calls to not block the event loop, you'd need to use the aiohttp library, or asyncio.open_connection. Using something like requests or urllib will block the loop, because they're not integrated with `asyncio.
You should decorate everything that uses yield from, because the decorator asyncio.coroutine will take your function as a generator and do all the callback/async job while you're yielding.
In your case, run needs to be rewritten as the following:
#asyncio.coroutine
def run(self):
while True:
msg = yield from self.read()
yield from self.parse(msg)
Then, read and parse must be coroutine as well.
You should read about how asynchronous work before use it, it will help you a lot.
#asyncio.coroutine
def read(self):
msg = yield from self.reader.readline()
return msg
#asyncio.coroutine
def run(loop):
while True:
msg = yield from read()
yield from parse(msg)
loop = asyncio.get_event_loop()
loop.run_until_complete(run(loop))
loop.close()

How can I package a coroutine as normal function in event loop?

I am using asyncio for a network framework.
In below code(low_level is our low level function, main block is our program entry, user_func is user-defined function):
import asyncio
loop = asyncio.get_event_loop()
""":type :asyncio.AbstractEventLoop"""
def low_level():
yield from asyncio.sleep(2)
def user_func():
yield from low_level()
if __name__ == '__main__':
co = user_func()
loop.run_until_complete(co)
I want wrap the low_level as normal function rather than coroutine(for compatibility etc.), but low_level is in event loop. How can wrap it as a normal function?
Because low_level is a coroutine, it can only be used by running an asyncio event loop. If you want to be able to call it from synchronous code that isn't running an event loop, you have to provide a wrapper that actually launches an event loop and runs the coroutine until completion:
def sync_low_level():
loop = asyncio.get_event_loop()
loop.run_until_complete(low_level())
If you want to be able to call low_level() from a function that is part of the running event loop, have it block for two seconds, but not have to use yield from, the answer is that you can't. The event loop is single-threaded; whenever execution is inside one of your functions, the event loop is blocked. No other events or callbacks can be processed. The only ways for a function running in the event loop to give control back to the event loop are to 1) return 2) use yield from. The asyncio.sleep call in low_level will never be able to complete unless you do one those two things.
Now, I suppose you could create an entirely new event loop, and use that to run the sleep synchronously from a coroutine running as part of the default event loop:
import asyncio
loop = asyncio.get_event_loop()
#asyncio.coroutine
def low_level(loop=None):
yield from asyncio.sleep(2, loop=loop)
def sync_low_level():
new_loop = asyncio.new_event_loop()
new_loop.run_until_complete(low_level(loop=new_loop))
#asyncio.coroutine
def user_func():
sync_low_level()
if __name__ == "__main__":
loop.run_until_complete(user_func())
But I'm really not sure why you'd want to do that.
If you just want to be able to make low_level act like a method returning a Future, so you can attach callbacks, etc. to it, just wrap it in asyncio.async():
loop = asyncio.get_event_loop()
def sleep_done(fut):
print("Done sleeping")
loop.stop()
#asyncio.coroutine
def low_level(loop=None):
yield from asyncio.sleep(2, loop=loop)
def user_func():
fut = asyncio.async(low_level())
fut.add_done_callback(sleep_done)
if __name__ == "__main__":
loop.call_soon(user_func)
loop.run_forever()
Output:
<2 second delay>
"Done sleeping"
Also, in your example code, you should use the #asyncio.coroutine decorator for both low_level and user_func, as stated in the asyncio docs:
A coroutine is a generator that follows certain conventions. For
documentation purposes, all coroutines should be decorated with
#asyncio.coroutine, but this cannot be strictly enforced.
Edit:
Here's how a user from a synchronous web framework could call into your application without blocking other requests:
#asyncio.coroutine
def low_level(loop=None):
yield from asyncio.sleep(2, loop=loop)
def thr_low_level():
loop = asyncio.new_event_loop()
t = threading.Thread(target=loop.run_until_complete, args(low_level(loop=loop),))
t.start()
t.join()
If a request being handled by Flask calls thr_low_level, it will block until the request is done, but the GIL should be released for all of the asynchronous I/O going on in low_level, allowing other requests to be handled in separate threads.

Categories

Resources