Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Asynchronously receive output from long running shell commands with asyncio (Python 3.4+)?

I'm trying to figure out how to simply start a number of long running shell commands in a non-blocking way, and asynchronously handle their output when they finish, in the order they finish, even if that is another order than they started, using the asyncio python library available in Python 3.4 and forward.

I couldn't find a simple example of doing this, even in the asyncio documentation itself, which also seems to be quite low-level.

like image 566
Samuel Lampa Avatar asked Dec 01 '15 12:12

Samuel Lampa


People also ask

How can I run an external command asynchronously from Python?

To run an external command asynchronously from Python, we can use the asyncio. create_subprocess_exec method. to run a process asynchronously with asyncio.

How do I call a function asynchronously in Python?

In asyncio Coroutine can be created by using async keyword before def. To run an async function (coroutine) you have to call it using an Event Loop. Event Loops: You can think of Event Loop as functions to run asynchronous tasks and callbacks, perform network IO operations, and run subprocesses.

Is Popen asynchronous?

Popen. Run subprocesses asynchronously using the subprocess module.

How many times should Asyncio run () be called?

It should be used as a main entry point for asyncio programs, and should ideally only be called once. New in version 3.7.


1 Answers

Use get_lines() coroutines, to get shell commands output asynchronously and pass the coroutines to asyncio.as_completed(), to get the results in the order they finish:

#!/usr/bin/env python3.5
import asyncio
import sys
from asyncio.subprocess import PIPE, STDOUT

async def get_lines(shell_command):
    p = await asyncio.create_subprocess_shell(shell_command,
            stdin=PIPE, stdout=PIPE, stderr=STDOUT)
    return (await p.communicate())[0].splitlines()

async def main():
    # get commands output concurrently
    coros = [get_lines('"{e}" -c "print({i:d}); import time; time.sleep({i:d})"'
                       .format(i=i, e=sys.executable))
             for i in reversed(range(5))]
    for f in asyncio.as_completed(coros): # print in the order they finish
        print(await f)


if sys.platform.startswith('win'):
    loop = asyncio.ProactorEventLoop() # for subprocess' pipes on Windows
    asyncio.set_event_loop(loop)
else:
    loop = asyncio.get_event_loop()
loop.run_until_complete(main())
loop.close()
like image 124
jfs Avatar answered Oct 05 '22 13:10

jfs